Skip to content

Commit

Permalink
Voltage and Current Sensors CLI (sonic-net#2941)
Browse files Browse the repository at this point in the history
Added support for voltage and current sensor monitoring CLIs as mentioned in the feature HLD for PMON Voltage/Current Sensor Monitoring Enhancement.
sonic-net/SONiC#1394
* Addressed review comments

* Fix review comment

* UT file

* Fixed dependency for running unit test on platform common changes

* Fixed standalone unit test failure

* Addressed review comment
  • Loading branch information
bmridul authored and JunhongMao committed Oct 4, 2023
1 parent 32f5607 commit 9bd7d88
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 0 deletions.
88 changes: 88 additions & 0 deletions scripts/sensorshow
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/python3

'''
Script to show Voltage and Current Sensor status.
'''
from tabulate import tabulate
from natsort import natsorted
import argparse
import os
import sys

# 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

from swsscommon.swsscommon import SonicV2Connector

header = ['Sensor', '', 'High TH', 'Low TH', 'Crit High TH', 'Crit Low TH', 'Warning', 'Timestamp']

TIMESTAMP_FIELD_NAME = 'timestamp'
UNIT_FIELD_NAME = 'unit'
HIGH_THRESH_FIELD_NAME = 'high_threshold'
LOW_THRESH_FIELD_NAME = 'low_threshold'
CRIT_HIGH_THRESH_FIELD_NAME = 'critical_high_threshold'
CRIT_LOW_THRESH_FIELD_NAME = 'critical_low_threshold'
WARNING_STATUS_FIELD_NAME = 'warning_status'
VOLTAGE_INFO_TABLE_NAME = 'VOLTAGE_INFO'
CURRENT_INFO_TABLE_NAME = 'CURRENT_INFO'


class SensorShow(object):
def __init__(self, type):
self.db = SonicV2Connector(use_unix_socket_path=True)
self.db.connect(self.db.STATE_DB)
self.field_name = type
header[1] = type.capitalize()

if type == "voltage":
self.table_name = VOLTAGE_INFO_TABLE_NAME
else:
self.table_name = CURRENT_INFO_TABLE_NAME

def show(self):
keys = self.db.keys(self.db.STATE_DB, self.table_name + '*')
if not keys:
print('Sensor not detected')
return

table = []
for key in natsorted(keys):
key_list = key.split('|')
if len(key_list) != 2: # error data in DB, log it and ignore
print('Warn: Invalid key in table {}: {}'.format(self.table_name, key))
continue

name = key_list[1]
data_dict = self.db.get_all(self.db.STATE_DB, key)
#print(name, data_dict)
table.append((name,
"{} {}".format(data_dict[self.field_name], data_dict[UNIT_FIELD_NAME]),
data_dict[HIGH_THRESH_FIELD_NAME],
data_dict[LOW_THRESH_FIELD_NAME],
data_dict[CRIT_HIGH_THRESH_FIELD_NAME],
data_dict[CRIT_LOW_THRESH_FIELD_NAME],
data_dict[WARNING_STATUS_FIELD_NAME],
data_dict[TIMESTAMP_FIELD_NAME]
))

if table:
print(tabulate(table, header, tablefmt='simple', stralign='right'))
else:
print('No sensor data available')


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--type", help="sensor type", required=True, choices=['voltage', 'current'])
args = parser.parse_args()

sensor_show = SensorShow(args.type)
sensor_show.show()
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
'scripts/tempershow',
'scripts/tunnelstat',
'scripts/update_json.py',
'scripts/sensorshow',
'scripts/voqutil',
'scripts/warm-reboot',
'scripts/watermarkstat',
Expand Down
17 changes: 17 additions & 0 deletions show/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,23 @@ def temperature():
cmd = ['tempershow']
clicommon.run_command(cmd)


# 'voltage' subcommand ("show platform voltage")
@platform.command()
def voltage():
"""Show device voltage information"""
cmd = ["sensorshow", "-t", "voltage"]
clicommon.run_command(cmd)


# 'current' subcommand ("show platform current")
@platform.command()
def current():
"""Show device current information"""
cmd = ["sensorshow", "-t", "current"]
clicommon.run_command(cmd)


# 'firmware' subcommand ("show platform firmware")
@platform.command(
context_settings=dict(
Expand Down
52 changes: 52 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -1620,5 +1620,57 @@
},
"ACL_RULE_TABLE|DATAACL_5|RULE_1" : {
"status": "Active"
},
"VOLTAGE_INFO|VSENSOR0": {
"critical_high_threshold": "872",
"critical_low_threshold": "664",
"high_threshold": "852",
"is_replaceable": "False",
"low_threshold": "684",
"maximum_voltage": "760",
"minimum_voltage": "759",
"timestamp": "20230704 17:38:04",
"voltage": "760",
"unit": "mV",
"warning_status": "False"
},
"VOLTAGE_INFO|VSENSOR1": {
"critical_high_threshold": "872",
"critical_low_threshold": "664",
"high_threshold": "852",
"is_replaceable": "False",
"low_threshold": "684",
"maximum_voltage": "760",
"minimum_voltage": "759",
"timestamp": "20230704 17:38:04",
"voltage": "759",
"unit": "mV",
"warning_status": "False"
},
"CURRENT_INFO|ISENSOR0": {
"critical_high_threshold": "460",
"critical_low_threshold": "300",
"current": "410",
"unit": "mA",
"high_threshold": "440",
"is_replaceable": "False",
"low_threshold": "320",
"maximum_current": "431",
"minimum_current": "402",
"timestamp": "20230704 17:38:04",
"warning_status": "False"
},
"CURRENT_INFO|ISENSOR1": {
"critical_high_threshold": "460",
"critical_low_threshold": "300",
"current": "360",
"unit": "mA",
"high_threshold": "440",
"is_replaceable": "False",
"low_threshold": "320",
"maximum_current": "367",
"minimum_current": "339",
"timestamp": "20230704 17:38:04",
"warning_status": "False"
}
}
50 changes: 50 additions & 0 deletions tests/sensor_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import sys
import os
from click.testing import CliRunner

test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
scripts_path = os.path.join(modules_path, "scripts")
sys.path.insert(0, modules_path)

import show.main as show

class TestVoltage(object):
@classmethod
def setup_class(cls):
print("SETUP")
os.environ["PATH"] += os.pathsep + scripts_path
os.environ["UTILITIES_UNIT_TESTING"] = "1"

def test_show_platform_voltage(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["platform"].commands["voltage"])
print(result.output)
expected = """\
Sensor Voltage High TH Low TH Crit High TH Crit Low TH Warning Timestamp
-------- --------- --------- -------- -------------- ------------- --------- -----------------
VSENSOR0 760 mV 852 684 872 664 False 20230704 17:38:04
VSENSOR1 759 mV 852 684 872 664 False 20230704 17:38:04
"""

assert result.output == expected

def test_show_platform_current(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["platform"].commands["current"])
print(result.output)
expected = """\
Sensor Current High TH Low TH Crit High TH Crit Low TH Warning Timestamp
-------- --------- --------- -------- -------------- ------------- --------- -----------------
ISENSOR0 410 mA 440 320 460 300 False 20230704 17:38:04
ISENSOR1 360 mA 440 320 460 300 False 20230704 17:38:04
"""

assert result.output == expected

@classmethod
def teardown_class(cls):
print("TEARDOWN")
os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1])
os.environ["UTILITIES_UNIT_TESTING"] = "0"

0 comments on commit 9bd7d88

Please sign in to comment.