From 065414aae809ae06b7b3ca7e69c4d5a5dad00b35 Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Tue, 17 Sep 2019 19:30:50 +0300 Subject: [PATCH] Add SSD Health CLI utility (#587) * Add SSD Health CLI utility Signed-off-by: Andriy Moroz * Add SSD Health command reference * Rename ssdutility entrypoint to ssdutil Signed-off-by: Andriy Moroz * Fix - do not add units if value is N/A Signed-off-by: Andriy Moroz --- doc/Command-Reference.md | 12 ++++ setup.py | 2 + show/main.py | 14 +++++ ssdutil/__init__.py | 0 ssdutil/main.py | 130 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100755 ssdutil/__init__.py create mode 100755 ssdutil/main.py diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index dfdc25f6c8fe..314717abbbe3 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -598,7 +598,19 @@ Couple of example outputs are given below. (checksum valid) ``` +**show platform ssdhealth** +This command displays health parameters of the device's SSD +- Usage: + show platform ssdhealth [--verbose, --vendor] + +- Example: + ``` + root@arc-switch1029:/home/admin# show platform ssdhealth + Device Model : M.2 (S42) 3IE3 + Health : 99.665% + Temperature : 30C + ``` **show platform psustatus** This command displays the status of the device's power supply units diff --git a/setup.py b/setup.py index a4bdf7089ef1..79ee605532b7 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ 'debug', 'pfcwd', 'sfputil', + 'ssdutil', 'pfc', 'psuutil', 'show', @@ -100,6 +101,7 @@ 'debug = debug.main:cli', 'pfcwd = pfcwd.main:cli', 'sfputil = sfputil.main:cli', + 'ssdutil = ssdutil.main:ssdutil', 'pfc = pfc.main:cli', 'psuutil = psuutil.main:cli', 'show = show.main:cli', diff --git a/show/main.py b/show/main.py index 5a4ba17cdef1..3d2d225fd2ca 100755 --- a/show/main.py +++ b/show/main.py @@ -1258,6 +1258,20 @@ def psustatus(index, verbose): run_command(cmd, display_cmd=verbose) +# 'ssdhealth' subcommand ("show platform ssdhealth [--verbose/--vendor]") +@platform.command() +@click.argument('device', required=False) +@click.option('--verbose', is_flag=True, help="Enable verbose output") +@click.option('--vendor', is_flag=True, help="Enable vendor specific output") +def ssdhealth(device, verbose, vendor): + """Show SSD Health information""" + if not device: + device = os.popen("lsblk -o NAME,TYPE -p | grep disk").readline().strip().split()[0] + cmd = "ssdutil -d " + device + options = " -v" if verbose else "" + options += " -e" if vendor else "" + run_command(cmd + options, display_cmd=verbose) + # # 'logging' command ("show logging") # diff --git a/ssdutil/__init__.py b/ssdutil/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/ssdutil/main.py b/ssdutil/main.py new file mode 100755 index 000000000000..c73fa147dc3a --- /dev/null +++ b/ssdutil/main.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# main.py +# +# Command-line utility to check SSD health and parameters +# + +try: + import sys + import os + import subprocess + import argparse + import syslog +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + +DEFAULT_DEVICE="/dev/sda" +SYSLOG_IDENTIFIER = "ssdutil" + +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' +HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku' +PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform' + +def syslog_msg(severity, msg, stdout=False): + """ + Prints to syslog (and stdout if needed) message with specified severity + + Args: + severity : message severity + msg : message + stdout : also primt message to stdout + + """ + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(severity, msg) + syslog.closelog() + + if stdout: + print msg + +def get_platform_and_hwsku(): + """ + Retrieves current platform name and hwsku + Raises an OSError exception when failed to fetch + + Returns: + tuple of strings platform and hwsku + e.g. ("x86_64-mlnx_msn2700-r0", "ACS-MSN2700") + """ + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + platform = stdout.rstrip('\n') + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + hwsku = stdout.rstrip('\n') + except OSError, e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + +def import_ssd_api(diskdev): + """ + Loads platform specific or generic ssd_util module from source + Raises an ImportError exception if none of above available + + Returns: + Instance of the class with SSD API implementation (vendor or generic) + """ + + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # try to load platform specific module + try: + hwsku_plugins_path = "/".join([PLATFORM_ROOT_PATH, platform, "plugins"]) + sys.path.append(os.path.abspath(hwsku_plugins_path)) + from ssd_util import SsdUtil + except ImportError as e: + syslog_msg(syslog.LOG_WARNING, "Platform specific SsdUtil module not found. Falling down to the generic implementation") + try: + from sonic_platform_base.sonic_ssd.ssd_generic import SsdUtil + except ImportError as e: + syslog_msg(syslog.LOG_ERR, "Failed to import default SsdUtil. Error: {}".format(str(e)), True) + raise e + + return SsdUtil(diskdev) + +def is_number(s): + try: + float(s) + return True + except ValueError: + return False + +# ==================== Entry point ==================== +def ssdutil(): + if os.geteuid() != 0: + print "Root privileges are required for this operation" + sys.exit(1) + + parser = argparse.ArgumentParser() + parser.add_argument("-d", "--device", help="Device name to show health info", default=DEFAULT_DEVICE) + parser.add_argument("-v", "--verbose", action="store_true", default=False, help="Show verbose output (some additional parameters)") + parser.add_argument("-e", "--vendor", action="store_true", default=False, help="Show vendor output (extended output if provided by platform vendor)") + args = parser.parse_args() + + ssd = import_ssd_api(args.device) + + print "Device Model : {}".format(ssd.get_model()) + if args.verbose: + print "Firmware : {}".format(ssd.get_firmware()) + print "Serial : {}".format(ssd.get_serial()) + print "Health : {}{}".format(ssd.get_health(), "%" if is_number(ssd.get_health()) else "") + print "Temperature : {}{}".format(ssd.get_temperature(), "C" if is_number(ssd.get_temperature()) else "") + if args.vendor: + print ssd.get_vendor_output() + +if __name__ == '__main__': + ssdutil()