From 2c9dfbc8bde06a856ccb347c8e138e794f37fb69 Mon Sep 17 00:00:00 2001 From: "Yuanzhe, Liu" Date: Sun, 17 Aug 2025 08:59:51 +0300 Subject: [PATCH] add cli to show liquid cooling leakage status Signed-off-by: Yuanzhe, Liu --- scripts/leakageshow | 62 +++++++++++++++++++++++++++++++++ setup.py | 1 + show/platform.py | 14 ++++++++ tests/leakage_status_test.py | 37 ++++++++++++++++++++ tests/mock_tables/state_db.json | 9 +++++ 5 files changed, 123 insertions(+) create mode 100755 scripts/leakageshow create mode 100644 tests/leakage_status_test.py diff --git a/scripts/leakageshow b/scripts/leakageshow new file mode 100755 index 0000000000..649489cf54 --- /dev/null +++ b/scripts/leakageshow @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +""" + Script to show leakage status. +""" + +import os +import sys +from tabulate import tabulate +from natsort import natsorted + +# 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 = ['Name', 'Leak'] + +LIQUID_COOLING_TABLE_NAME = 'LIQUID_COOLING_INFO' +STATUS_FIELD_NAME = 'leak_status' + +class LeakageShow(object): + def __init__(self): + self.db = SonicV2Connector(host="127.0.0.1") + self.db.connect(self.db.STATE_DB) + + def show(self): + keys = self.db.keys(self.db.STATE_DB, LIQUID_COOLING_TABLE_NAME + '*') + if not keys: + print('Leakage sensor Not detected\n') + 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 LIQUID_COOLING_INFO: {}'.format(key)) + continue + + name = key_list[1] + data_dict = self.db.get_all(self.db.STATE_DB, key) + status = data_dict[STATUS_FIELD_NAME] + + table.append((name, status)) + + if table: + print(tabulate(table, header, tablefmt='simple', stralign='right')) + else: + print('No leakage status data available\n') + + +if __name__ == "__main__": + leakageShow = LeakageShow() + leakageShow.show() diff --git a/setup.py b/setup.py index 6f88ff83ec..31be48f41f 100644 --- a/setup.py +++ b/setup.py @@ -144,6 +144,7 @@ 'scripts/intfstat', 'scripts/ipintutil', 'scripts/lag_keepalive.py', + 'scripts/leakageshow', 'scripts/lldpshow', 'scripts/log_ssd_health', 'scripts/mellanox_buffer_migrator.py', diff --git a/show/platform.py b/show/platform.py index 20f9f20a8a..f5ac0de922 100644 --- a/show/platform.py +++ b/show/platform.py @@ -198,3 +198,17 @@ def firmware(args): subprocess.check_call(cmd) except subprocess.CalledProcessError as e: sys.exit(e.returncode) + + +# 'leakage' subcommand ("show platform leakage status") +@platform.group() +def leakage(): + """Show platform leakage information""" + pass + + +@leakage.command() +def status(): + """Show platform leakage status""" + cmd = ["leakageshow"] + clicommon.run_command(cmd) diff --git a/tests/leakage_status_test.py b/tests/leakage_status_test.py new file mode 100644 index 0000000000..2d46bf7daa --- /dev/null +++ b/tests/leakage_status_test.py @@ -0,0 +1,37 @@ +import sys +import os +from click.testing import CliRunner +import show.main as show + +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) + + +class TestFan(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_leakage_status(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["platform"].commands["leakage"].commands["status"]) + print(result.output) + expected = """\ + Name Leak +-------- ------ +leakage1 No +leakage2 No +leakage3 Yes +""" + + 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" diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 3de0451292..44ce8b892e 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -1249,6 +1249,15 @@ "14": "200:200:200:200::5@Vlan1000", "15": "200:200:200:200::5@Vlan1000" }, + "LIQUID_COOLING_INFO|leakage1": { + "leak_status": "No" + }, + "LIQUID_COOLING_INFO|leakage2": { + "leak_status": "No" + }, + "LIQUID_COOLING_INFO|leakage3": { + "leak_status": "Yes" + }, "FG_ROUTE_TABLE|100.50.25.12/32": { "0": "200.200.200.4@Vlan1000", "1": "200.200.200.4@Vlan1000",