bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/netbackup.go (about) 1 package collectors 2 3 import ( 4 "encoding/csv" 5 "fmt" 6 "reflect" 7 "strconv" 8 "strings" 9 "time" 10 11 "bosun.org/metadata" 12 "bosun.org/opentsdb" 13 "bosun.org/util" 14 ) 15 16 func init() { 17 collectors = append(collectors, &IntervalCollector{F: c_netbackup_jobs}) 18 collectors = append(collectors, &IntervalCollector{F: c_netbackup_frequency}) 19 } 20 21 //jobtype 22 // 0=backup, 1=archive, 2=restore, 3=verify, 4=duplicate, 5=import, 6=catalog backup, 7=vault, 8=label 23 // 9=erase, 10=tape request, 11=tape clean, 12=format tape, 13=physical inventory, 14=qualification 24 // 15=database recovery, 16=media contents, 17=image delete, 18=LiveUpdate 25 26 //state 27 // 0=queued, 1=active, 2=wait for retry, 3=done, 4=suspended, 5=incomplete 28 29 //NOTE!! 30 // This depends on the retention of the job log being greater than the jobs, 31 // else things are going to go unknown. See 32 // http://www.symantec.com/connect/forums/netbackup-75-activity-monitor-job-logs 33 // In my case I created the two registry entries mentioned in that link 34 // (KEEP_JOB_HOURS) and (KEEP_JOBS_SUCCESSFUL_HOURS). I also changed the 35 // rentention under "Clean-up" under the master server properties via the Java 36 // Admin Console. One of those seems to have worked. This *is* netbackup, so I 37 // wish you the best of luck ;-). 38 39 type nbJob struct { 40 Jobid string 41 Jobtype int 42 State int 43 Status string 44 Class string 45 Schedule string 46 Client string 47 Server string 48 Started time.Time 49 Elapsed string 50 Ended time.Time 51 Stunit string 52 Try string 53 Operation string 54 Kbytes int 55 Files int 56 Pathlastwritten string 57 Percent string 58 Jobpid string 59 Owner string 60 Subtype string 61 Classtype string 62 Schedule_Type string 63 Priority string 64 Group string 65 Masterserver string 66 Retentionunits string 67 Retentionperiod string 68 Compression string 69 Kbyteslastwritten string 70 Fileslastwritten string 71 } 72 73 var timeType = reflect.TypeOf(time.Time{}) 74 75 func nbUnmarhsall(reader *csv.Reader, v interface{}) error { 76 record, err := reader.Read() 77 if err != nil { 78 return err 79 } 80 if len(record) < 32 { 81 return fmt.Errorf("record too short, expected at least 32 fields, got %v", len(record)) 82 } 83 s := reflect.ValueOf(v).Elem() 84 for i := 0; i < s.NumField(); i++ { 85 f := s.Field(i) 86 switch f.Kind() { 87 case reflect.String: 88 f.SetString(record[i]) 89 case reflect.Int: 90 var ival int64 91 if record[i] == "" { 92 continue 93 } 94 ival, err = strconv.ParseInt(record[i], 10, 64) 95 if err != nil { 96 return err 97 } 98 f.SetInt(ival) 99 case reflect.Struct: 100 switch f.Type() { 101 case timeType: 102 ival, err := strconv.ParseInt(record[i], 10, 64) 103 if err != nil { 104 return err 105 } 106 t := time.Unix(ival, 0) 107 f.Set(reflect.ValueOf(t)) 108 default: 109 return fmt.Errorf("unsupported type: %s", f.Type()) 110 } 111 default: 112 return fmt.Errorf("unsupported type: %s", f.Type()) 113 } 114 } 115 return nil 116 } 117 118 func c_netbackup_jobs() (opentsdb.MultiDataPoint, error) { 119 var md opentsdb.MultiDataPoint 120 latest := make(map[string]nbJob) 121 if err := util.ReadCommand(func(line string) error { 122 if len(line) < 32 { 123 return nil 124 } 125 var r nbJob 126 reader := csv.NewReader(strings.NewReader(line)) 127 if err := nbUnmarhsall(reader, &r); err != nil { 128 return err 129 } 130 if !(r.Jobtype == 0 || r.Jobtype == 6) { 131 return nil 132 } 133 if r.State != 3 && r.State != 5 { 134 return nil 135 } 136 key := r.Class + r.Schedule + r.Client 137 if existing, ok := latest[key]; !ok { 138 latest[key] = r 139 } else if r.Started.After(existing.Started) { 140 latest[key] = r 141 } 142 return nil 143 }, "bpdbjobs", "-report", "-all_columns"); err == util.ErrPath { 144 return nil, nil 145 } else if err != nil { 146 return nil, err 147 } 148 now := time.Now() 149 for _, r := range latest { 150 tags := opentsdb.TagSet{"class": r.Class, "client": r.Client, "schedule": r.Schedule} 151 Add(&md, "netbackup.backup.status", r.Status, tags, metadata.Gauge, metadata.StatusCode, "") 152 Add(&md, "netbackup.backup.duration", r.Elapsed, tags, metadata.Gauge, metadata.Second, "") 153 Add(&md, "netbackup.backup.attempt_age", now.Sub(r.Ended).Seconds(), tags, metadata.Gauge, metadata.Second, "") 154 Add(&md, "netbackup.backup.duration", r.Elapsed, tags, metadata.Gauge, metadata.Second, "") 155 Add(&md, "netbackup.backup.no_files", r.Files, tags, metadata.Gauge, metadata.Count, "") 156 Add(&md, "netbackup.backup.kbytes", r.Kbytes, tags, metadata.Gauge, metadata.KBytes, "") 157 } 158 return md, nil 159 } 160 161 func c_netbackup_frequency() (opentsdb.MultiDataPoint, error) { 162 var md opentsdb.MultiDataPoint 163 var class, schedule string 164 var clients []string 165 if err := util.ReadCommand(func(line string) error { 166 if strings.HasPrefix(line, "Policy Name:") { 167 clients = nil 168 f := strings.Fields(line) 169 if len(f) == 3 { 170 class = f[2] 171 return nil 172 } 173 return fmt.Errorf("error parsing policy: %v", line) 174 } 175 if strings.HasPrefix(line, "Client/HW/OS/Pri/DMI/CIT:") { 176 f := strings.Fields(line) 177 if len(f) == 9 { 178 clients = append(clients, f[1]) 179 return nil 180 } 181 return fmt.Errorf("error parsing client") 182 } 183 if strings.HasPrefix(line, "Schedule:") { 184 f := strings.Fields(line) 185 if len(f) > 1 { 186 schedule = f[1] 187 return nil 188 } 189 return fmt.Errorf("error parsing client: %v", line) 190 } 191 if strings.HasPrefix(strings.TrimSpace(line), "Frequency:") { 192 f := strings.Fields(line) 193 if len(f) == 5 { 194 freq := strings.TrimLeft(f[3], "(") 195 for _, client := range clients { 196 tags := opentsdb.TagSet{"class": class, "client": client, "schedule": schedule} 197 Add(&md, "netbackup.backup.frequency", freq, tags, metadata.Gauge, metadata.Second, "") 198 } 199 return nil 200 } 201 return fmt.Errorf("error parsing frequency: %v", line) 202 } 203 return nil 204 }, "bppllist", "-L", "-allpolicies"); err == util.ErrPath { 205 return nil, nil 206 } else if err != nil { 207 return nil, err 208 } 209 return md, nil 210 }