github.com/netdata/go.d.plugin@v0.58.1/modules/nvme/exec.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package nvme 4 5 import ( 6 "bytes" 7 "context" 8 "encoding/json" 9 "os/exec" 10 "time" 11 ) 12 13 type nvmeDeviceList struct { 14 Devices []struct { 15 DevicePath string `json:"DevicePath"` 16 UsedBytes nvmeNumber `json:"UsedBytes"` 17 PhysicalSize nvmeNumber `json:"PhysicalSize"` 18 SectorSize nvmeNumber `json:"SectorSize"` 19 } 20 } 21 22 // See "Health Information Log Page" in the Current Specification Version 23 // https://nvmexpress.org/developers/nvme-specification/ 24 type nvmeDeviceSmartLog struct { 25 CriticalWarning nvmeNumber `json:"critical_warning"` 26 Temperature nvmeNumber `json:"temperature"` 27 AvailSpare nvmeNumber `json:"avail_spare"` 28 SpareThresh nvmeNumber `json:"spare_thresh"` 29 PercentUsed nvmeNumber `json:"percent_used"` 30 DataUnitsRead nvmeNumber `json:"data_units_read"` 31 DataUnitsWritten nvmeNumber `json:"data_units_written"` 32 HostReadCommands nvmeNumber `json:"host_read_commands"` 33 HostWriteCommands nvmeNumber `json:"host_write_commands"` 34 ControllerBusyTime nvmeNumber `json:"controller_busy_time"` 35 PowerCycles nvmeNumber `json:"power_cycles"` 36 PowerOnHours nvmeNumber `json:"power_on_hours"` 37 UnsafeShutdowns nvmeNumber `json:"unsafe_shutdowns"` 38 MediaErrors nvmeNumber `json:"media_errors"` 39 NumErrLogEntries nvmeNumber `json:"num_err_log_entries"` 40 WarningTempTime nvmeNumber `json:"warning_temp_time"` 41 CriticalCompTime nvmeNumber `json:"critical_comp_time"` 42 ThmTemp1TransCount nvmeNumber `json:"thm_temp1_trans_count"` 43 ThmTemp2TransCount nvmeNumber `json:"thm_temp2_trans_count"` 44 ThmTemp1TotalTime nvmeNumber `json:"thm_temp1_total_time"` 45 ThmTemp2TotalTime nvmeNumber `json:"thm_temp2_total_time"` 46 } 47 48 // nvme-cli 2.1.1 exposes some values as strings 49 type nvmeNumber string 50 51 func (n *nvmeNumber) UnmarshalJSON(b []byte) error { 52 *n = nvmeNumber(bytes.Trim(b, "\"")) 53 return nil 54 } 55 56 type nvmeCLIExec struct { 57 sudoPath string 58 nvmePath string 59 ndsudoPath string 60 timeout time.Duration 61 } 62 63 func (n *nvmeCLIExec) list() (*nvmeDeviceList, error) { 64 var data []byte 65 var err error 66 67 if n.ndsudoPath != "" { 68 data, err = n.executeNdSudo("nvme-list") 69 } else { 70 data, err = n.execute("list", "--output-format=json") 71 } 72 if err != nil { 73 return nil, err 74 } 75 76 var v nvmeDeviceList 77 if err := json.Unmarshal(data, &v); err != nil { 78 return nil, err 79 } 80 81 return &v, nil 82 } 83 84 func (n *nvmeCLIExec) smartLog(devicePath string) (*nvmeDeviceSmartLog, error) { 85 var data []byte 86 var err error 87 88 if n.ndsudoPath != "" { 89 data, err = n.executeNdSudo("nvme-smart-log", "--device", devicePath) 90 } else { 91 data, err = n.execute("smart-log", devicePath, "--output-format=json") 92 } 93 if err != nil { 94 return nil, err 95 } 96 97 var v nvmeDeviceSmartLog 98 if err := json.Unmarshal(data, &v); err != nil { 99 return nil, err 100 } 101 102 return &v, nil 103 } 104 105 func (n *nvmeCLIExec) execute(arg ...string) ([]byte, error) { 106 ctx, cancel := context.WithTimeout(context.Background(), n.timeout) 107 defer cancel() 108 109 if n.sudoPath != "" { 110 args := append([]string{"-n", n.nvmePath}, arg...) 111 return exec.CommandContext(ctx, n.sudoPath, args...).Output() 112 } 113 114 return exec.CommandContext(ctx, n.nvmePath, arg...).Output() 115 } 116 117 func (n *nvmeCLIExec) executeNdSudo(arg ...string) ([]byte, error) { 118 ctx, cancel := context.WithTimeout(context.Background(), n.timeout) 119 defer cancel() 120 121 return exec.CommandContext(ctx, n.ndsudoPath, arg...).Output() 122 }