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 )