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  }