bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/dell_hw.go (about)

     1  package collectors
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  
    10  	"bosun.org/metadata"
    11  	"bosun.org/opentsdb"
    12  	"bosun.org/slog"
    13  	"bosun.org/util"
    14  )
    15  
    16  func init() {
    17  	const interval = time.Minute * 5
    18  	collectors = append(collectors,
    19  		&IntervalCollector{F: c_omreport_chassis, Interval: interval},
    20  		&IntervalCollector{F: c_omreport_fans, Interval: interval},
    21  		&IntervalCollector{F: c_omreport_memory, Interval: interval},
    22  		&IntervalCollector{F: c_omreport_processors, Interval: interval},
    23  		&IntervalCollector{F: c_omreport_ps, Interval: interval},
    24  		&IntervalCollector{F: c_omreport_ps_amps_sysboard_pwr, Interval: interval},
    25  		&IntervalCollector{F: c_omreport_storage_battery, Interval: interval},
    26  		&IntervalCollector{F: c_omreport_storage_controller, Interval: interval},
    27  		&IntervalCollector{F: c_omreport_storage_enclosure, Interval: interval},
    28  		&IntervalCollector{F: c_omreport_storage_vdisk, Interval: interval},
    29  		&IntervalCollector{F: c_omreport_system, Interval: interval},
    30  		&IntervalCollector{F: c_omreport_temps, Interval: interval},
    31  		&IntervalCollector{F: c_omreport_volts, Interval: interval},
    32  	)
    33  }
    34  
    35  func c_omreport_chassis() (opentsdb.MultiDataPoint, error) {
    36  	var md opentsdb.MultiDataPoint
    37  	readOmreport(func(fields []string) {
    38  		if len(fields) != 2 || fields[0] == "SEVERITY" {
    39  			return
    40  		}
    41  		component := strings.Replace(fields[1], " ", "_", -1)
    42  		Add(&md, "hw.chassis", severity(fields[0]), opentsdb.TagSet{"component": component}, metadata.Gauge, metadata.Ok, descDellHWChassis)
    43  	}, "chassis")
    44  	return md, nil
    45  }
    46  
    47  func c_omreport_system() (opentsdb.MultiDataPoint, error) {
    48  	var md opentsdb.MultiDataPoint
    49  	readOmreport(func(fields []string) {
    50  		if len(fields) != 2 || fields[0] == "SEVERITY" {
    51  			return
    52  		}
    53  		component := strings.Replace(fields[1], " ", "_", -1)
    54  		Add(&md, "hw.system", severity(fields[0]), opentsdb.TagSet{"component": component}, metadata.Gauge, metadata.Ok, descDellHWSystem)
    55  	}, "system")
    56  	return md, nil
    57  }
    58  
    59  func c_omreport_storage_enclosure() (opentsdb.MultiDataPoint, error) {
    60  	var md opentsdb.MultiDataPoint
    61  	readOmreport(func(fields []string) {
    62  		if len(fields) < 3 || fields[0] == "ID" {
    63  			return
    64  		}
    65  		id := strings.Replace(fields[0], ":", "_", -1)
    66  		Add(&md, "hw.storage.enclosure", severity(fields[1]), opentsdb.TagSet{"id": id}, metadata.Gauge, metadata.Ok, descDellHWStorageEnc)
    67  	}, "storage", "enclosure")
    68  	return md, nil
    69  }
    70  
    71  func c_omreport_storage_vdisk() (opentsdb.MultiDataPoint, error) {
    72  	var md opentsdb.MultiDataPoint
    73  	readOmreport(func(fields []string) {
    74  		if len(fields) < 3 || fields[0] == "ID" {
    75  			return
    76  		}
    77  		id := strings.Replace(fields[0], ":", "_", -1)
    78  		Add(&md, "hw.storage.vdisk", severity(fields[1]), opentsdb.TagSet{"id": id}, metadata.Gauge, metadata.Ok, descDellHWVDisk)
    79  	}, "storage", "vdisk")
    80  	return md, nil
    81  }
    82  
    83  func c_omreport_ps() (opentsdb.MultiDataPoint, error) {
    84  	var md opentsdb.MultiDataPoint
    85  	readOmreport(func(fields []string) {
    86  		if len(fields) < 3 || fields[0] == "Index" {
    87  			return
    88  		}
    89  		id := strings.Replace(fields[0], ":", "_", -1)
    90  		ts := opentsdb.TagSet{"id": id}
    91  		Add(&md, "hw.ps", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWPS)
    92  		pm := &metadata.HWPowerSupplyMeta{}
    93  		if len(fields) < 6 {
    94  			return
    95  		}
    96  		if fields[4] != "" {
    97  			pm.RatedInputWattage = fields[4]
    98  		}
    99  		if fields[5] != "" {
   100  			pm.RatedOutputWattage = fields[5]
   101  		}
   102  		if j, err := json.Marshal(&pm); err == nil {
   103  			metadata.AddMeta("", ts, "psMeta", string(j), true)
   104  		} else {
   105  			slog.Error(err)
   106  		}
   107  	}, "chassis", "pwrsupplies")
   108  	return md, nil
   109  }
   110  
   111  func c_omreport_ps_amps_sysboard_pwr() (opentsdb.MultiDataPoint, error) {
   112  	var md opentsdb.MultiDataPoint
   113  	readOmreport(func(fields []string) {
   114  		if len(fields) == 2 && strings.Contains(fields[0], "Current") {
   115  			i_fields := strings.Split(fields[0], "Current")
   116  			v_fields := strings.Fields(fields[1])
   117  			if len(i_fields) < 2 && len(v_fields) < 2 {
   118  				return
   119  			}
   120  			id := strings.Replace(i_fields[0], " ", "", -1)
   121  			Add(&md, "hw.chassis.current.reading", v_fields[0], opentsdb.TagSet{"id": id}, metadata.Gauge, metadata.A, descDellHWCurrent)
   122  		} else if len(fields) == 6 && (fields[2] == "System Board Pwr Consumption" || fields[2] == "System Board System Level") {
   123  			v_fields := strings.Fields(fields[3])
   124  			warn_fields := strings.Fields(fields[4])
   125  			fail_fields := strings.Fields(fields[5])
   126  			if len(v_fields) < 2 || len(warn_fields) < 2 || len(fail_fields) < 2 {
   127  				return
   128  			}
   129  			Add(&md, "hw.chassis.power.reading", v_fields[0], nil, metadata.Gauge, metadata.Watt, descDellHWPower)
   130  			Add(&md, "hw.chassis.power.warn_level", warn_fields[0], nil, metadata.Gauge, metadata.Watt, descDellHWPowerThreshold)
   131  			Add(&md, "hw.chassis.power.fail_level", fail_fields[0], nil, metadata.Gauge, metadata.Watt, descDellHWPowerThreshold)
   132  		}
   133  	}, "chassis", "pwrmonitoring")
   134  	return md, nil
   135  }
   136  
   137  func c_omreport_storage_battery() (opentsdb.MultiDataPoint, error) {
   138  	var md opentsdb.MultiDataPoint
   139  	readOmreport(func(fields []string) {
   140  		if len(fields) < 3 || fields[0] == "ID" {
   141  			return
   142  		}
   143  		id := strings.Replace(fields[0], ":", "_", -1)
   144  		Add(&md, "hw.storage.battery", severity(fields[1]), opentsdb.TagSet{"id": id}, metadata.Gauge, metadata.Ok, descDellHWStorageBattery)
   145  	}, "storage", "battery")
   146  	return md, nil
   147  }
   148  
   149  func c_omreport_storage_controller() (opentsdb.MultiDataPoint, error) {
   150  	var md opentsdb.MultiDataPoint
   151  	readOmreport(func(fields []string) {
   152  		if len(fields) < 3 || fields[0] == "ID" {
   153  			return
   154  		}
   155  		c_omreport_storage_pdisk(fields[0], &md)
   156  		id := strings.Replace(fields[0], ":", "_", -1)
   157  		ts := opentsdb.TagSet{"id": id}
   158  		Add(&md, "hw.storage.controller", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWStorageCtl)
   159  		cm := &metadata.HWControllerMeta{}
   160  		if len(fields) < 8 {
   161  			return
   162  		}
   163  		if fields[2] != "" {
   164  			cm.Name = fields[2]
   165  		}
   166  		if fields[3] != "" {
   167  			cm.SlotId = fields[3]
   168  		}
   169  		if fields[4] != "" {
   170  			cm.State = fields[4]
   171  		}
   172  		if fields[5] != "" {
   173  			cm.FirmwareVersion = fields[5]
   174  		}
   175  		if fields[7] != "" {
   176  			cm.DriverVersion = fields[7]
   177  		}
   178  		if j, err := json.Marshal(&cm); err == nil {
   179  			metadata.AddMeta("", ts, "controllerMeta", string(j), true)
   180  		} else {
   181  			slog.Error(err)
   182  		}
   183  	}, "storage", "controller")
   184  	return md, nil
   185  }
   186  
   187  // c_omreport_storage_pdisk is called from the controller func, since it needs the encapsulating id.
   188  func c_omreport_storage_pdisk(id string, md *opentsdb.MultiDataPoint) {
   189  	readOmreport(func(fields []string) {
   190  		if len(fields) < 3 || fields[0] == "ID" {
   191  			return
   192  		}
   193  		//Need to find out what the various ID formats might be
   194  		id := strings.Replace(fields[0], ":", "_", -1)
   195  		ts := opentsdb.TagSet{"id": id}
   196  		Add(md, "hw.storage.pdisk", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWPDisk)
   197  		if len(fields) < 32 {
   198  			return
   199  		}
   200  		dm := &metadata.HWDiskMeta{}
   201  		if fields[2] != "" {
   202  			dm.Name = fields[2]
   203  		}
   204  		if fields[6] != "" {
   205  			dm.Media = fields[6]
   206  		}
   207  		if fields[19] != "" {
   208  			dm.Capacity = fields[19]
   209  		}
   210  		if fields[23] != "" {
   211  			dm.VendorId = fields[23]
   212  		}
   213  		if fields[24] != "" {
   214  			dm.ProductId = fields[24]
   215  		}
   216  		if fields[25] != "" {
   217  			dm.Serial = fields[25]
   218  		}
   219  		if fields[26] != "" {
   220  			dm.Part = fields[26]
   221  		}
   222  		if fields[27] != "" {
   223  			dm.NegotatiedSpeed = fields[27]
   224  		}
   225  		if fields[28] != "" {
   226  			dm.CapableSpeed = fields[28]
   227  		}
   228  		if fields[31] != "" {
   229  			dm.SectorSize = fields[31]
   230  
   231  		}
   232  		if j, err := json.Marshal(&dm); err == nil {
   233  			metadata.AddMeta("", ts, "physicalDiskMeta", string(j), true)
   234  		} else {
   235  			slog.Error(err)
   236  		}
   237  	}, "storage", "pdisk", "controller="+id)
   238  }
   239  
   240  func c_omreport_processors() (opentsdb.MultiDataPoint, error) {
   241  	var md opentsdb.MultiDataPoint
   242  	readOmreport(func(fields []string) {
   243  		if len(fields) != 8 {
   244  			return
   245  		}
   246  		if _, err := strconv.Atoi(fields[0]); err != nil {
   247  			return
   248  		}
   249  		ts := opentsdb.TagSet{"name": replace(fields[2])}
   250  		Add(&md, "hw.chassis.processor", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWCPU)
   251  		metadata.AddMeta("", ts, "processor", clean(fields[3], fields[4]), true)
   252  	}, "chassis", "processors")
   253  	return md, nil
   254  }
   255  
   256  func c_omreport_fans() (opentsdb.MultiDataPoint, error) {
   257  	var md opentsdb.MultiDataPoint
   258  	readOmreport(func(fields []string) {
   259  		if len(fields) != 8 {
   260  			return
   261  		}
   262  		if _, err := strconv.Atoi(fields[0]); err != nil {
   263  			return
   264  		}
   265  		ts := opentsdb.TagSet{"name": replace(fields[2])}
   266  		Add(&md, "hw.chassis.fan", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWFan)
   267  		fs := strings.Fields(fields[3])
   268  		if len(fs) == 2 && fs[1] == "RPM" {
   269  			i, err := strconv.Atoi(fs[0])
   270  			if err == nil {
   271  				Add(&md, "hw.chassis.fan.reading", i, ts, metadata.Gauge, metadata.RPM, descDellHWFanSpeed)
   272  			}
   273  		}
   274  	}, "chassis", "fans")
   275  	return md, nil
   276  }
   277  
   278  func c_omreport_memory() (opentsdb.MultiDataPoint, error) {
   279  	var md opentsdb.MultiDataPoint
   280  	readOmreport(func(fields []string) {
   281  		if len(fields) != 5 {
   282  			return
   283  		}
   284  		if _, err := strconv.Atoi(fields[0]); err != nil {
   285  			return
   286  		}
   287  		ts := opentsdb.TagSet{"name": replace(fields[2])}
   288  		Add(&md, "hw.chassis.memory", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWMemory)
   289  		metadata.AddMeta("", ts, "memory", clean(fields[4]), true)
   290  	}, "chassis", "memory")
   291  	return md, nil
   292  }
   293  
   294  func c_omreport_temps() (opentsdb.MultiDataPoint, error) {
   295  	var md opentsdb.MultiDataPoint
   296  	readOmreport(func(fields []string) {
   297  		if len(fields) != 8 {
   298  			return
   299  		}
   300  		if _, err := strconv.Atoi(fields[0]); err != nil {
   301  			return
   302  		}
   303  		ts := opentsdb.TagSet{"name": replace(fields[2])}
   304  		Add(&md, "hw.chassis.temps", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWTemp)
   305  		fs := strings.Fields(fields[3])
   306  		if len(fs) == 2 && fs[1] == "C" {
   307  			i, err := strconv.ParseFloat(fs[0], 64)
   308  			if err == nil {
   309  				Add(&md, "hw.chassis.temps.reading", i, ts, metadata.Gauge, metadata.C, descDellHWTempReadings)
   310  			}
   311  		}
   312  	}, "chassis", "temps")
   313  	return md, nil
   314  }
   315  
   316  func c_omreport_volts() (opentsdb.MultiDataPoint, error) {
   317  	var md opentsdb.MultiDataPoint
   318  	readOmreport(func(fields []string) {
   319  		if len(fields) != 8 {
   320  			return
   321  		}
   322  		if _, err := strconv.Atoi(fields[0]); err != nil {
   323  			return
   324  		}
   325  		ts := opentsdb.TagSet{"name": replace(fields[2])}
   326  		Add(&md, "hw.chassis.volts", severity(fields[1]), ts, metadata.Gauge, metadata.Ok, descDellHWVolt)
   327  		if i, err := extract(fields[3], "V"); err == nil {
   328  			Add(&md, "hw.chassis.volts.reading", i, ts, metadata.Gauge, metadata.V, descDellHWVoltReadings)
   329  		}
   330  	}, "chassis", "volts")
   331  	return md, nil
   332  }
   333  
   334  // extract tries to return a parsed number from s with given suffix. A space may
   335  // be present between number ond suffix.
   336  func extract(s, suffix string) (float64, error) {
   337  	if !strings.HasSuffix(s, suffix) {
   338  		return 0, fmt.Errorf("extract: suffix not found")
   339  	}
   340  	s = s[:len(s)-len(suffix)]
   341  	return strconv.ParseFloat(strings.TrimSpace(s), 64)
   342  }
   343  
   344  // severity returns 0 if s is not "Ok" or "Non-Critical", else 1.
   345  func severity(s string) int {
   346  	if s != "Ok" && s != "Non-Critical" {
   347  		return 1
   348  	}
   349  	return 0
   350  }
   351  
   352  func readOmreport(f func([]string), args ...string) {
   353  	args = append(args, "-fmt", "ssv")
   354  	_ = util.ReadCommand(func(line string) error {
   355  		sp := strings.Split(line, ";")
   356  		for i, s := range sp {
   357  			sp[i] = clean(s)
   358  		}
   359  		f(sp)
   360  		return nil
   361  	}, "omreport", args...)
   362  }
   363  
   364  // clean concatenates arguments with a space and removes extra whitespace.
   365  func clean(ss ...string) string {
   366  	v := strings.Join(ss, " ")
   367  	fs := strings.Fields(v)
   368  	return strings.Join(fs, " ")
   369  }
   370  
   371  func replace(name string) string {
   372  	r, _ := opentsdb.Replace(name, "_")
   373  	return r
   374  }
   375  
   376  const (
   377  	descDellHWChassis        = "Overall status of chassis components."
   378  	descDellHWSystem         = "Overall status of system components."
   379  	descDellHWStorageEnc     = "Overall status of storage enclosures."
   380  	descDellHWVDisk          = "Overall status of virtual disks."
   381  	descDellHWPS             = "Overall status of power supplies."
   382  	descDellHWCurrent        = "Amps used per power supply."
   383  	descDellHWPower          = "System board power usage."
   384  	descDellHWPowerThreshold = "The warning and failure levels set on the device for system board power usage."
   385  	descDellHWStorageBattery = "Status of storage controller backup batteries."
   386  	descDellHWStorageCtl     = "Overall status of storage controllers."
   387  	descDellHWPDisk          = "Overall status of physical disks."
   388  	descDellHWCPU            = "Overall status of CPUs."
   389  	descDellHWFan            = "Overall status of system fans."
   390  	descDellHWFanSpeed       = "System fan speed."
   391  	descDellHWMemory         = "System RAM DIMM status."
   392  	descDellHWTemp           = "Overall status of system temperature readings."
   393  	descDellHWTempReadings   = "System temperature readings."
   394  	descDellHWVolt           = "Overall status of power supply volt readings."
   395  	descDellHWVoltReadings   = "Volts used per power supply."
   396  )