From 454b1f36e56a251ffaff5b0c0954dae35726a679 Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Wed, 24 Jul 2019 10:50:55 +0000 Subject: [PATCH 1/4] Add SSD Health CLI utility Signed-off-by: Andriy Moroz --- setup.py | 2 + show/main.py | 14 +++++ ssdutil/__init__.py | 0 ssdutil/main.py | 123 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100755 ssdutil/__init__.py create mode 100755 ssdutil/main.py diff --git a/setup.py b/setup.py index d12d9880b8..800da65457 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ 'debug', 'pfcwd', 'sfputil', + 'ssdutil', 'pfc', 'psuutil', 'show', @@ -99,6 +100,7 @@ 'debug = debug.main:cli', 'pfcwd = pfcwd.main:cli', 'sfputil = sfputil.main:cli', + 'ssdutil = ssdutil.main:cli', 'pfc = pfc.main:cli', 'psuutil = psuutil.main:cli', 'show = show.main:cli', diff --git a/show/main.py b/show/main.py index e8a2617bf3..15a1e113b7 100755 --- a/show/main.py +++ b/show/main.py @@ -1185,6 +1185,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 0000000000..e69de29bb2 diff --git a/ssdutil/main.py b/ssdutil/main.py new file mode 100755 index 0000000000..49650bd3f5 --- /dev/null +++ b/ssdutil/main.py @@ -0,0 +1,123 @@ +#!/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) + +# ==================== Entry point ==================== +def cli(): + 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()) + print "Temperature : {}C".format(ssd.get_temperature()) + if args.vendor: + print ssd.get_vendor_output() + +if __name__ == '__main__': + cli() From dd02351b618328026e55cc68ecdaed9a2d94caff Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Mon, 19 Aug 2019 10:02:56 +0000 Subject: [PATCH 2/4] Add SSD Health command reference --- doc/Command-Reference.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index a37be1df64..17f6c4ba47 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -583,7 +583,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 From ea024e08ad84d0e83c74a0a41d5c494e84341827 Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Tue, 20 Aug 2019 16:18:45 +0300 Subject: [PATCH 3/4] Rename ssdutility entrypoint to ssdutil Signed-off-by: Andriy Moroz --- setup.py | 2 +- ssdutil/main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 800da65457..c69bffd592 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ 'debug = debug.main:cli', 'pfcwd = pfcwd.main:cli', 'sfputil = sfputil.main:cli', - 'ssdutil = ssdutil.main:cli', + 'ssdutil = ssdutil.main:ssdutil', 'pfc = pfc.main:cli', 'psuutil = psuutil.main:cli', 'show = show.main:cli', diff --git a/ssdutil/main.py b/ssdutil/main.py index 49650bd3f5..e9db742b95 100755 --- a/ssdutil/main.py +++ b/ssdutil/main.py @@ -97,7 +97,7 @@ def import_ssd_api(diskdev): return SsdUtil(diskdev) # ==================== Entry point ==================== -def cli(): +def ssdutil(): if os.geteuid() != 0: print "Root privileges are required for this operation" sys.exit(1) @@ -120,4 +120,4 @@ def cli(): print ssd.get_vendor_output() if __name__ == '__main__': - cli() + ssdutil() From f59e51fb1a3b3b2f19102f96009daa279f6b92f3 Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Mon, 16 Sep 2019 17:17:00 +0300 Subject: [PATCH 4/4] Fix - do not add units if value is N/A Signed-off-by: Andriy Moroz --- ssdutil/main.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ssdutil/main.py b/ssdutil/main.py index e9db742b95..c73fa147dc 100755 --- a/ssdutil/main.py +++ b/ssdutil/main.py @@ -96,6 +96,13 @@ def import_ssd_api(diskdev): return SsdUtil(diskdev) +def is_number(s): + try: + float(s) + return True + except ValueError: + return False + # ==================== Entry point ==================== def ssdutil(): if os.geteuid() != 0: @@ -114,8 +121,8 @@ def ssdutil(): if args.verbose: print "Firmware : {}".format(ssd.get_firmware()) print "Serial : {}".format(ssd.get_serial()) - print "Health : {}%".format(ssd.get_health()) - print "Temperature : {}C".format(ssd.get_temperature()) + 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()