in plugins/inputs/smart/smart.go [755:897]
func (m *Smart) gatherDisk(acc telegraf.Accumulator, device string, wg *sync.WaitGroup) {
defer wg.Done()
// smartctl 5.41 & 5.42 have are broken regarding handling of --nocheck/-n
args := []string{"--info", "--health", "--attributes", "--tolerance=verypermissive", "-n", m.Nocheck, "--format=brief"}
args = append(args, strings.Split(device, " ")...)
out, e := runCmd(m.Timeout, m.UseSudo, m.PathSmartctl, args...)
outStr := string(out)
// Ignore all exit statuses except if it is a command line parse error
exitStatus, er := exitStatus(e)
if er != nil {
acc.AddError(fmt.Errorf("failed to run command '%s %s': %s - %s", m.PathSmartctl, strings.Join(args, " "), e, outStr))
return
}
deviceTags := map[string]string{}
deviceNode := strings.Split(device, " ")[0]
deviceTags["device"] = path.Base(deviceNode)
deviceFields := make(map[string]interface{})
deviceFields["exit_status"] = exitStatus
scanner := bufio.NewScanner(strings.NewReader(outStr))
for scanner.Scan() {
line := scanner.Text()
model := modelInfo.FindStringSubmatch(line)
if len(model) > 2 {
deviceTags["model"] = model[2]
}
serial := serialInfo.FindStringSubmatch(line)
if len(serial) > 1 {
deviceTags["serial_no"] = serial[1]
}
wwn := wwnInfo.FindStringSubmatch(line)
if len(wwn) > 1 {
deviceTags["wwn"] = strings.Replace(wwn[1], " ", "", -1)
}
capacity := userCapacityInfo.FindStringSubmatch(line)
if len(capacity) > 1 {
deviceTags["capacity"] = strings.Replace(capacity[1], ",", "", -1)
}
enabled := smartEnabledInfo.FindStringSubmatch(line)
if len(enabled) > 1 {
deviceTags["enabled"] = enabled[1]
}
health := smartOverallHealth.FindStringSubmatch(line)
if len(health) > 2 {
deviceFields["health_ok"] = health[2] == "PASSED" || health[2] == "OK"
}
// checks to see if there is a power mode to print to user
// if not look for Device is in STANDBY which happens when
// nocheck is set to standby (will exit to not spin up the disk)
// otherwise nothing is found so nothing is printed (NVMe does not show power)
if power := powermodeInfo.FindStringSubmatch(line); len(power) > 1 {
deviceTags["power"] = power[1]
} else {
if power := standbyInfo.FindStringSubmatch(line); len(power) > 1 {
deviceTags["power"] = power[1]
}
}
tags := map[string]string{}
fields := make(map[string]interface{})
if m.Attributes {
//add power mode
keys := [...]string{"device", "model", "serial_no", "wwn", "capacity", "enabled", "power"}
for _, key := range keys {
if value, ok := deviceTags[key]; ok {
tags[key] = value
}
}
}
attr := attribute.FindStringSubmatch(line)
if len(attr) > 1 {
// attribute has been found, add it only if m.Attributes is true
if m.Attributes {
tags["id"] = attr[1]
tags["name"] = attr[2]
tags["flags"] = attr[3]
fields["exit_status"] = exitStatus
if i, err := strconv.ParseInt(attr[4], 10, 64); err == nil {
fields["value"] = i
}
if i, err := strconv.ParseInt(attr[5], 10, 64); err == nil {
fields["worst"] = i
}
if i, err := strconv.ParseInt(attr[6], 10, 64); err == nil {
fields["threshold"] = i
}
tags["fail"] = attr[7]
if val, err := parseRawValue(attr[8]); err == nil {
fields["raw_value"] = val
}
acc.AddFields("smart_attribute", fields, tags)
}
// If the attribute matches on the one in deviceFieldIds
// save the raw value to a field.
if field, ok := deviceFieldIds[attr[1]]; ok {
if val, err := parseRawValue(attr[8]); err == nil {
deviceFields[field] = val
}
}
} else {
// what was found is not a vendor attribute
if matches := sasNVMeAttr.FindStringSubmatch(line); len(matches) > 2 {
if attr, ok := sasNVMeAttributes[matches[1]]; ok {
tags["name"] = attr.Name
if attr.ID != "" {
tags["id"] = attr.ID
}
parse := parseCommaSeparatedInt
if attr.Parse != nil {
parse = attr.Parse
}
if err := parse(fields, deviceFields, matches[2]); err != nil {
continue
}
// if the field is classified as an attribute, only add it
// if m.Attributes is true
if m.Attributes {
acc.AddFields("smart_attribute", fields, tags)
}
}
}
}
}
acc.AddFields("smart_device", deviceFields, deviceTags)
}