Skip to content

Commit

Permalink
[psushow] Add more output columns; Add option to output in JSON format (
Browse files Browse the repository at this point in the history
sonic-net#1416)

- Enhance `psushow -s` output, adding columns to display PSU Model, Serial, Voltage, Current and Power
- Add `-j/--json` option to display output in JSON format
- Update mock State DB PSU_INFO table to provide values which we can ensure floats are padded to two decimal places in tabular display mode
- Add `--json` option to `show platform psustatus` which in turn adds the `-j` flag to the underlying `psushow` call
- Add unit tests for `psushow` in psushow_test.py
- Add more unit tests for `show platform psustatus` and move to show_platform_test.py
  • Loading branch information
jleveque authored Feb 25, 2021
1 parent 79ccd03 commit 1fa4b94
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 124 deletions.
147 changes: 104 additions & 43 deletions scripts/psushow
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
#!/usr/bin/env python3

import argparse
import json
import sys
import os

from swsscommon.swsscommon import SonicV2Connector
from tabulate import tabulate

# mock the redis for unit test purposes #
try:
if os.environ["UTILITIES_UNIT_TESTING"] == "1":
modules_path = os.path.join(os.path.dirname(__file__), "..")
test_path = os.path.join(modules_path, "tests")
sys.path.insert(0, modules_path)
sys.path.insert(0, test_path)
import mock_tables.dbconnector
except KeyError:
pass
VERSION = '1.0'

from swsscommon.swsscommon import SonicV2Connector

def psu_status_show(index):

def get_psu_status_list():
psu_status_list = []

db = SonicV2Connector(host="127.0.0.1")
db.connect(db.STATE_DB)

Expand All @@ -27,59 +22,125 @@ def psu_status_show(index):
chassis_name = "chassis {}".format(chassis_num)
num_psus = db.get(db.STATE_DB, 'CHASSIS_INFO|{}'.format(chassis_name), 'psu_num')
if not num_psus:
print("Error! Failed to get the number of PSUs!")
return -1
print('Error: Failed to get the number of PSUs')
return None

supported_psu = range(1, int(num_psus) + 1)
if (index < 0):
psu_ids = supported_psu
else:
psu_ids = [index]
for psu_idx in range(1, int(num_psus) + 1):
psu_status = {}

header = ['PSU', 'Status', 'LED']
status_table = []
psu_status['index'] = str(psu_idx)

psu_name = 'PSU {}'.format(psu_idx)
psu_status['name'] = psu_name

for psu in psu_ids:
msg = ""
psu_name = "PSU {}".format(psu)
if psu not in supported_psu:
print("Error! The {} is not available on the platform.\n"
"Number of supported PSU - {}.".format(psu_name, num_psus))
continue
presence = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'presence')
psu_status['presence'] = presence

if presence == 'true':
oper_status = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'status')
msg = 'OK' if oper_status == 'true' else "NOT OK"
status = 'OK' if oper_status == 'true' else "NOT OK"
else:
msg = 'NOT PRESENT'
led_status = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'led_status')
status_table.append([psu_name, msg, led_status])
status = 'NOT PRESENT'
psu_status['status'] = status

psu_status['led_status'] = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'led_status')

psu_status['model'] = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'model') if presence else 'N/A'
psu_status['serial'] = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'serial') if presence else 'N/A'
psu_status['voltage'] = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'voltage') if presence else 'N/A'
psu_status['current'] = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'current') if presence else 'N/A'
psu_status['power'] = db.get(db.STATE_DB, 'PSU_INFO|{}'.format(psu_name), 'power') if presence else 'N/A'

psu_status_list.append(psu_status)

return psu_status_list


def psu_status_show_table(index):
psu_status_list = get_psu_status_list()

if not psu_status_list:
print('Error: Failed to get PSU status')
return None

header = ['PSU', 'Model', 'Serial', 'Voltage (V)', 'Current (A)', 'Power (W)', 'Status', 'LED']
status_table = []

if index > 0:
if index > len(psu_status_list):
print("Error: PSU {} is not available. Number of supported PSUs: {}".format(index, len(psu_status_list)))
return -1

# Trim the list down to contain only the requested PSU
psu_status_list = [psu_status_list[index-1]]

for psu_status in psu_status_list:
status_table.append([psu_status['name'],
psu_status['model'],
psu_status['serial'],
psu_status['voltage'],
psu_status['current'],
psu_status['power'],
psu_status['status'],
psu_status['led_status']])

if status_table:
print(tabulate(status_table, header, tablefmt="simple"))
print(tabulate(status_table, header, tablefmt="simple", floatfmt='.2f'))

return 0


def psu_status_show_json(index):
psu_status_list = get_psu_status_list()

if not psu_status_list:
print('Error: Failed to get PSU status')
return None

if index > 0:
if index > len(psu_status_list):
print("Error: PSU {} is not available. Number of supported PSUs: {}".format(index, len(psu_status_list)))
return -1

# Trim the list down to contain only the requested PSU
psu_status_list = [psu_status_list[index-1]]

print(json.dumps(psu_status_list, indent=4))
return 0


def main():
parser = argparse.ArgumentParser(description='Display the psu status information',
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Examples:
psushow -s
psushow -s -j
psushow -s -i 1
""")

parser.add_argument('-s', '--status', action='store_true', help='show the status information')
parser.add_argument('-i', '--index', type=int, default=-1, help='the index of psu')
parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0')
parser.add_argument('-s', '--status', action='store_true', help='Show PSU status information')
parser.add_argument('-i', '--index', type=int, default=-1, help='The index of PSU to display')
parser.add_argument('-j', '--json', action='store_true', help='Display output in JSON format')
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(VERSION))
args = parser.parse_args()

status_show = args.status
psu_index = args.index
output_json = args.json

if status_show:
err = psu_status_show(psu_index)
if err:
print("Error: fail to get psu status from state DB")
sys.exit(1)
if output_json:
ret = psu_status_show_json(psu_index)
else:
ret = psu_status_show_table(psu_index)

if __name__ == "__main__":
main()
if ret != 0:
print("Error: failed to get PSU status from state DB")
return 1

return 0


if __name__ == "__main__":
sys.exit(main())
8 changes: 6 additions & 2 deletions show/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def platform():

# 'summary' subcommand ("show platform summary")
@platform.command()
@click.option('--json', is_flag=True, help="JSON output")
@click.option('--json', is_flag=True, help="Output in JSON format")
def summary(json):
"""Show hardware platform information"""

Expand Down Expand Up @@ -69,14 +69,18 @@ def syseeprom(verbose):
# 'psustatus' subcommand ("show platform psustatus")
@platform.command()
@click.option('-i', '--index', default=-1, type=int, help="the index of PSU")
@click.option('--json', is_flag=True, help="Output in JSON format")
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def psustatus(index, verbose):
def psustatus(index, json, verbose):
"""Show PSU status information"""
cmd = "psushow -s"

if index >= 0:
cmd += " -i {}".format(index)

if json:
cmd += " -j"

clicommon.run_command(cmd, display_cmd=verbose)


Expand Down
20 changes: 20 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,31 @@
"PSU_INFO|PSU 1": {
"presence": "true",
"status": "true",
"model": "0J6J4K",
"serial": "CN-0J6J4K-17972-5AF-0086-A00",
"temp": "None",
"temp_threshold": "None",
"voltage": "12.19",
"voltage_min_threshold": "None",
"voltage_max_threshold": "None",
"current": "8.37",
"power": "102.7",
"is_replaceable": "False",
"led_status": "green"
},
"PSU_INFO|PSU 2": {
"presence": "true",
"status": "true",
"model": "0J6J4K",
"serial": "CN-0J6J4K-17972-5AF-008M-A00",
"temp": "None",
"temp_threshold": "None",
"voltage": "12.18",
"voltage_min_threshold": "None",
"voltage_max_threshold": "None",
"current": "10.07",
"power": "122.0",
"is_replaceable": "False",
"led_status": "green"
},
"EEPROM_INFO|TlvHeader": {
Expand Down
51 changes: 0 additions & 51 deletions tests/psu_test.py

This file was deleted.

Loading

0 comments on commit 1fa4b94

Please sign in to comment.