Add sample for partial GO implementation
This commit is contained in:
parent
49270d4d28
commit
352e8cf9b1
|
|
@ -0,0 +1,251 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SmartDev struct {
|
||||
DeviceFile string
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// Lower case for zabbix-expected Json
|
||||
type SmartDiscoEntry struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Model *string `json:"model"`
|
||||
SN *string `json:"sn"`
|
||||
Rotations *int32 `json:"rotations"`
|
||||
Lbs *int32 `json:"lbs"`
|
||||
IsExternal int32 `json:"isExternal"`
|
||||
IsSSD int32 `json:"isSSD"`
|
||||
}
|
||||
|
||||
type SmartInfoEntry struct {
|
||||
Name string
|
||||
DeviceFile string
|
||||
Health string
|
||||
PowerOnHours int32
|
||||
PowerCycleCount int32
|
||||
Temperature float32
|
||||
}
|
||||
|
||||
type smartctlJson struct {
|
||||
ModelName string `json:"model_name"`
|
||||
SN string `json:"serial_number"`
|
||||
Rotations int32 `json:"rotation_rate"`
|
||||
Lbs int32 `json:"logical_block_size"`
|
||||
}
|
||||
|
||||
func ExecSmartCtl(resAsSingleString bool, args ...string) ([]string, error) {
|
||||
|
||||
path, err := exec.LookPath("smartctl")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coudn't find smartctl %v", err)
|
||||
}
|
||||
|
||||
out, err := exec.Command(path, args...).Output()
|
||||
if err != nil {
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) && exitErr.ExitCode() == 2 {
|
||||
return nil, fmt.Errorf("smartctl failed, not running root?: %v", exitErr)
|
||||
} else if errors.As(err, &exitErr) && exitErr.ExitCode() == 4 {
|
||||
// ignore, no or partial smart support
|
||||
} else {
|
||||
return nil, fmt.Errorf("smartctl failed: %v", exitErr)
|
||||
}
|
||||
}
|
||||
|
||||
if !resAsSingleString {
|
||||
var lns []string
|
||||
s := bufio.NewScanner(strings.NewReader(string(out)))
|
||||
for s.Scan() {
|
||||
lns = append(lns, s.Text())
|
||||
}
|
||||
return lns, nil
|
||||
} else {
|
||||
var lns []string
|
||||
lns = append(lns, string(out))
|
||||
return lns, nil
|
||||
}
|
||||
}
|
||||
|
||||
func IsUSBDrive(dev string) (bool, error) {
|
||||
path, err := exec.LookPath("lsblk")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't find lsblk: %v", err)
|
||||
}
|
||||
|
||||
out, err := exec.Command(path, "-d", dev, "-o", "SUBSYSTEMS").Output()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("calling lsblk failed: %v", err)
|
||||
}
|
||||
|
||||
return strings.Index(string(out), "usb") >= 0, nil
|
||||
}
|
||||
|
||||
func GetSmartDevsFromScan() ([]SmartDev, error) {
|
||||
var r []SmartDev
|
||||
|
||||
lns, err := ExecSmartCtl(false, "-n", "standby", "--scan")
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("Scanning for devices: %w", err)
|
||||
}
|
||||
|
||||
for _, ln := range lns {
|
||||
if len(ln) > 0 {
|
||||
//fmt.Println("x" + ln)
|
||||
splt := strings.Split(ln, " ")
|
||||
var sd SmartDev
|
||||
sd.DeviceFile = splt[0]
|
||||
sd.Name = strings.Split(splt[0], "/")[2]
|
||||
sd.Type = strings.Split(strings.Split(ln, ",")[1], " ")[1]
|
||||
r = append(r, sd)
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func GetSmartDisco() ([]SmartDiscoEntry, error) {
|
||||
var r []SmartDiscoEntry
|
||||
|
||||
devs, err := GetSmartDevsFromScan()
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
for _, dev := range devs {
|
||||
isUSB, err := IsUSBDrive(dev.DeviceFile)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("failed get disco: %v", err)
|
||||
}
|
||||
|
||||
var entry SmartDiscoEntry
|
||||
|
||||
if !isUSB {
|
||||
//fmt.Println(dev.DeviceFile)
|
||||
lns, err := ExecSmartCtl(true, "-n", "standby", "-a", dev.DeviceFile, "--json")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "? skipped device %v: %v\n", dev.Name, err)
|
||||
continue
|
||||
}
|
||||
//j := lns[0]
|
||||
var sc smartctlJson
|
||||
// Prevent collision with bash-call
|
||||
err = json.Unmarshal([]byte(strings.ReplaceAll(lns[0], "'", "")), &sc)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("failed parsing json: %v", err)
|
||||
}
|
||||
entry.Model = &sc.ModelName
|
||||
entry.SN = &sc.SN
|
||||
entry.Rotations = &sc.Rotations
|
||||
entry.Lbs = &sc.Lbs
|
||||
|
||||
} else {
|
||||
entry.Model = nil
|
||||
entry.SN = nil
|
||||
entry.Rotations = nil
|
||||
entry.Lbs = nil
|
||||
}
|
||||
entry.Name = dev.Name
|
||||
entry.Type = dev.Type
|
||||
entry.IsExternal = func() int32 {
|
||||
if isUSB {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}()
|
||||
entry.IsSSD = 0 // TODO
|
||||
r = append(r, entry)
|
||||
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func SendSmartDiscoToZabbix(testMode bool) error {
|
||||
disco, err := GetSmartDisco()
|
||||
if err != nil {
|
||||
return fmt.Errorf("SendSmartDiscoToZabbix failed: %w", err)
|
||||
}
|
||||
|
||||
j, err := json.Marshal(disco)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SendSmartDiscoToZabbix failed: %v", err)
|
||||
}
|
||||
|
||||
if testMode {
|
||||
fmt.Printf("Testmode: Would send %v\n", string(j))
|
||||
|
||||
} else {
|
||||
path, err := exec.LookPath("bash")
|
||||
if err != nil {
|
||||
return fmt.Errorf("coudn't find bash %v", err)
|
||||
}
|
||||
|
||||
_, err = exec.Command(path, "-c", ""+
|
||||
"zabbix_sender -vv -k 8o_smartcheck.disco_devs -o '"+
|
||||
string(j)+
|
||||
"' -c /etc/zabbix/zabbix_agentd.conf"+
|
||||
" | logger -t zbs_smart").Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("zabbix_sender exec failed: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
doDiscoPtr := flag.Bool("smart_disco", false, "Do discovery")
|
||||
doCheckPtr := flag.Bool("smart_check", false, "Do check")
|
||||
testModePtr := flag.Bool("test", false, "TestMode")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
var err error
|
||||
|
||||
if *testModePtr {
|
||||
fmt.Println("----- Testing GetSmartDevsFromScan")
|
||||
s, _ := GetSmartDevsFromScan()
|
||||
j, _ := json.Marshal(s)
|
||||
fmt.Println(string(j))
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("----- Testing IsUsbDrive")
|
||||
for _, dev := range s {
|
||||
isUSB, err := IsUSBDrive(dev.DeviceFile)
|
||||
fmt.Printf("- %s %v %+v\n", dev.Name, isUSB, err)
|
||||
}
|
||||
|
||||
fmt.Println("----- SendSmartDiscoToZabbix")
|
||||
err = SendSmartDiscoToZabbix(*testModePtr)
|
||||
}
|
||||
|
||||
if *doDiscoPtr {
|
||||
fmt.Println("Doing SMART device discovery")
|
||||
err = SendSmartDiscoToZabbix(*testModePtr)
|
||||
}
|
||||
|
||||
if err == nil && *doCheckPtr {
|
||||
fmt.Println("Doig SMART checks")
|
||||
// TODO
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "! %+v\n", err)
|
||||
}
|
||||
|
||||
fmt.Println("Done")
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue