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  }