From fa0beb48059ddff84771ff1ff4b28f346196acab Mon Sep 17 00:00:00 2001 From: mprabhu Date: Tue, 20 Oct 2020 01:20:21 -0700 Subject: [PATCH 1/3] PSUd changes to calculate power requirements PSUd will introduce power requirements calculations. Platform APIs are introduced to provide consumers and total consumed power. Number of PSUs will help provide total supplied power --- sonic-psud/scripts/psud | 98 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud index 0bc7526ce..f8beea727 100644 --- a/sonic-psud/scripts/psud +++ b/sonic-psud/scripts/psud @@ -14,8 +14,10 @@ try: import sys import threading - from sonic_py_common import daemon_base + from sonic_py_common import daemon_base, logger from swsscommon import swsscommon + from sonic_platform_base.device_base import DeviceBase + except ImportError as e: raise ImportError (str(e) + " - required module not found") @@ -32,6 +34,10 @@ CHASSIS_INFO_TABLE = 'CHASSIS_INFO' CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}' CHASSIS_INFO_PSU_NUM_FIELD = 'psu_num' +CHASSIS_INFO_POWER_CONSUMER_FIELD = '{} consumed_power' +CHASSIS_INFO_POWER_SUPPLIER_FIELD = '{} supplied_power' +CHASSIS_INFO_POWER_KEY_TEMPLATE = 'chassis_power_budget {}' + PSU_INFO_TABLE = 'PSU_INFO' PSU_INFO_KEY_TEMPLATE = 'PSU {}' PSU_INFO_PRESENCE_FIELD = 'presence' @@ -133,8 +139,68 @@ def log_on_status_changed(logger, normal_status, normal_log, abnormal_log): else: logger.log_warning(abnormal_log) - # +# PSU Chassis Info ========================================================== +# +class PsuChassisInfo(object): + def __init__(self): + self.logger = logger.Logger(SYSLOG_IDENTIFIER) + self.master_status_good = False + self.total_consumed_power = 0.0 + self.total_supplied_power = 0.0 + + def run_power_budget(self, chassis_tbl): + self.total_supplied_power = 0.0 + self.total_consumed_power = 0.0 + + supplier_list = ['PSU'] + consumer_list = platform_chassis.get_all_power_consumers() + + fvs = swsscommon.FieldValuePairs(len(supplier_list) + len(consumer_list)) + + for index, psu in enumerate(platform_chassis.get_all_psus()): + presence = _wrapper_get_psus_presence(index + 1) + if not presence: + continue + + power_good = _wrapper_get_psus_status(index + 1) + if not power_good: + continue + + supplied_power = try_get(psu.get_maximum_supplied_power) + self.total_supplied_power = self.total_supplied_power + supplied_power + + for index, power_consumer in enumerate(consumer_list): + total_consumer_power = platform_chassis.get_total_consumer_power(power_consumer) + self.total_consumed_power = self.total_consumed_power + total_consumer_power + + fvs[index] = (CHASSIS_INFO_POWER_CONSUMER_FIELD.format(str(power_consumer)), str(total_consumer_power)) + + #Record in state DB in chassis table + fvs[index + 1] = (CHASSIS_INFO_POWER_SUPPLIER_FIELD.format('PSU'), str(self.total_supplied_power)) + chassis_tbl.set(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1), fvs) + + def update_master_status(self): + if not self.total_supplied_power or not self.total_consumed_power: + if self.master_status_good is not True: + self.master_status_good = True + return False + + master_status_good = (self.total_consumed_power < self.total_supplied_power) + if master_status_good == self.master_status_good: + return False + + self.master_status_good = master_status_good + + return True + + def _set_psu_master_led(self, master_status): + try: + color = platform_chassis.STATUS_LED_COLOR_GREEN if master_status else platform_chassis.STATUS_LED_COLOR_RED + platform_chassis.set_status_master_led(DeviceBase.DEVICE_TYPE_PSU, color) + except NotImplementedError as e: + pass + # PSU status =================================================================== # @@ -216,6 +282,7 @@ class DaemonPsud(daemon_base.DaemonBase): self.stop = threading.Event() self.psu_status_dict = {} self.fan_tbl = None + self.psu_chassis_info = None # Signal handler def signal_handler(self, sig, frame): @@ -263,6 +330,7 @@ class DaemonPsud(daemon_base.DaemonBase): fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_PSU_NUM_FIELD, str(psu_num))]) chassis_tbl.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) + # Start main loop self.log_info("Start daemon main loop") @@ -271,6 +339,9 @@ class DaemonPsud(daemon_base.DaemonBase): self.update_psu_data(psu_tbl) self._update_led_color(psu_tbl) + self.update_psu_chassis_info(chassis_tbl) + self.update_master_led_color(chassis_tbl) + self.log_info("Stop daemon main loop") # Delete all the information from DB and then exit @@ -278,6 +349,7 @@ class DaemonPsud(daemon_base.DaemonBase): psu_tbl._del(PSU_INFO_KEY_TEMPLATE.format(psu_index)) chassis_tbl._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + chassis_tbl._del(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) self.log_info("Shutting down...") @@ -427,6 +499,28 @@ class DaemonPsud(daemon_base.DaemonBase): ]) self.fan_tbl.set(fan_name, fvs) + def update_psu_chassis_info(self, chassis_tbl): + if not platform_chassis: + return + + if not self.psu_chassis_info: + self.psu_chassis_info = PsuChassisInfo() + + self.psu_chassis_info.run_power_budget(chassis_tbl) + + def update_master_led_color(self, chassis_tbl): + if not platform_chassis or not self.psu_chassis_info: + return + + psu_chassis_info = self.psu_chassis_info + if psu_chassis_info.update_master_status(): + log_on_status_changed(self, psu_chassis_info.master_status_good, + 'PSU supplied power warning cleared: supplied power is back to normal.', + 'PSU supplied power warning: {}W supplied-power less than {}W consumed-power'.format( + psu_chassis_info.total_supplied_power, psu_chassis_info.total_consumed_power) + ) + psu_chassis_info._set_psu_master_led(psu_chassis_info.master_status_good) + # # Main ========================================================================= From 515a13c5b6c6481620b0927c81f718312335b01a Mon Sep 17 00:00:00 2001 From: mprabhu Date: Mon, 26 Oct 2020 17:53:58 -0700 Subject: [PATCH 2/3] Add UT and Cleanup APIs for power-budget Addressed review-comments to move get_maxiumum_consumed_power(), get_status_master_led() and set_status_master_led() to PSU etc. This can in future be added to Fan and other peripherals Added UT to test the power-budget feature --- sonic-psud/pytest.ini | 3 + sonic-psud/scripts/psud | 104 +++++++++++++---- sonic-psud/setup.cfg | 2 + sonic-psud/setup.py | 9 ++ sonic-psud/tests/__init__.py | 0 sonic-psud/tests/mock_device_base.py | 11 ++ sonic-psud/tests/mock_platform.py | 116 +++++++++++++++++++ sonic-psud/tests/mock_swsscommon.py | 27 +++++ sonic-psud/tests/test_psud.py | 167 +++++++++++++++++++++++++++ 9 files changed, 414 insertions(+), 25 deletions(-) create mode 100644 sonic-psud/pytest.ini create mode 100644 sonic-psud/setup.cfg create mode 100644 sonic-psud/tests/__init__.py create mode 100644 sonic-psud/tests/mock_device_base.py create mode 100644 sonic-psud/tests/mock_platform.py create mode 100644 sonic-psud/tests/mock_swsscommon.py create mode 100644 sonic-psud/tests/test_psud.py diff --git a/sonic-psud/pytest.ini b/sonic-psud/pytest.ini new file mode 100644 index 000000000..c24fe5bb9 --- /dev/null +++ b/sonic-psud/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +filterwarnings = + ignore::DeprecationWarning diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud index f8beea727..4a2132440 100644 --- a/sonic-psud/scripts/psud +++ b/sonic-psud/scripts/psud @@ -15,12 +15,20 @@ try: import threading from sonic_py_common import daemon_base, logger - from swsscommon import swsscommon - from sonic_platform_base.device_base import DeviceBase except ImportError as e: raise ImportError (str(e) + " - required module not found") +try: + from swsscommon import swsscommon +except ImportError as e: + from tests import mock_swsscommon as swsscommon + +try: + from sonic_platform_base.device_base import DeviceBase +except ImportError, e: + from tests.mock_device_base import DeviceBase + # # Constants ==================================================================== # @@ -34,8 +42,10 @@ CHASSIS_INFO_TABLE = 'CHASSIS_INFO' CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}' CHASSIS_INFO_PSU_NUM_FIELD = 'psu_num' -CHASSIS_INFO_POWER_CONSUMER_FIELD = '{} consumed_power' -CHASSIS_INFO_POWER_SUPPLIER_FIELD = '{} supplied_power' +CHASSIS_INFO_POWER_CONSUMER_FIELD = 'Consumed Power {}' +CHASSIS_INFO_POWER_SUPPLIER_FIELD = 'Supplied Power {}' +CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD = 'Total Consumed Power' +CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD = 'Total Supplied Power' CHASSIS_INFO_POWER_KEY_TEMPLATE = 'chassis_power_budget {}' PSU_INFO_TABLE = 'PSU_INFO' @@ -142,42 +152,80 @@ def log_on_status_changed(logger, normal_status, normal_log, abnormal_log): # # PSU Chassis Info ========================================================== # -class PsuChassisInfo(object): - def __init__(self): - self.logger = logger.Logger(SYSLOG_IDENTIFIER) - self.master_status_good = False +class PsuChassisInfo(logger.Logger): + + def __init__(self, log_identifier, chassis): + """ + Constructor for PsuChassisInfo + :param chassis: Object representing a platform chassis + """ + super(PsuChassisInfo, self).__init__(log_identifier) + + self.chassis = chassis + self.master_status_good = True self.total_consumed_power = 0.0 self.total_supplied_power = 0.0 def run_power_budget(self, chassis_tbl): self.total_supplied_power = 0.0 self.total_consumed_power = 0.0 + total_supplied_power = 0.0 + total_fan_consumed_power = 0.0 + total_module_consumed_power = 0.0 - supplier_list = ['PSU'] - consumer_list = platform_chassis.get_all_power_consumers() + dict_index = 0 + total_entries_len = 2 #For total supplied and consumed + dict_len = self.chassis.get_num_psus() +\ + self.chassis.get_num_fan_drawers() +\ + self.chassis.get_num_modules() + \ + total_entries_len - fvs = swsscommon.FieldValuePairs(len(supplier_list) + len(consumer_list)) + fvs = swsscommon.FieldValuePairs(dict_len) - for index, psu in enumerate(platform_chassis.get_all_psus()): - presence = _wrapper_get_psus_presence(index + 1) + for index, psu in enumerate(self.chassis.get_all_psus()): + presence = try_get(psu.get_presence) if not presence: continue - power_good = _wrapper_get_psus_status(index + 1) + power_good = try_get(psu.get_powergood_status) if not power_good: continue + name = try_get(psu.get_name, 'PSU {}'.format(index + 1)) supplied_power = try_get(psu.get_maximum_supplied_power) - self.total_supplied_power = self.total_supplied_power + supplied_power + total_supplied_power = total_supplied_power + supplied_power + fvs[dict_index] = (CHASSIS_INFO_POWER_SUPPLIER_FIELD.format(name), str(supplied_power)) + dict_index += 1 - for index, power_consumer in enumerate(consumer_list): - total_consumer_power = platform_chassis.get_total_consumer_power(power_consumer) - self.total_consumed_power = self.total_consumed_power + total_consumer_power + for index, power_consumer in enumerate(self.chassis.get_all_fan_drawers()): + presence = try_get(power_consumer.get_presence) + if not presence: + continue - fvs[index] = (CHASSIS_INFO_POWER_CONSUMER_FIELD.format(str(power_consumer)), str(total_consumer_power)) + name = try_get(power_consumer.get_name, 'FAN-DRAWER {}'.format(index)) + fan_drawer_power = try_get(power_consumer.get_maximum_consumed_power, 0) + total_fan_consumed_power = total_fan_consumed_power + fan_drawer_power + fvs[dict_index] = (CHASSIS_INFO_POWER_CONSUMER_FIELD.format(name), str(fan_drawer_power)) + dict_index += 1 + + for index, power_consumer in enumerate(self.chassis.get_all_modules()): + presence = try_get(power_consumer.get_presence) + if not presence: + continue + + name = try_get(power_consumer.get_name, 'MODULE {}'.format(index)) + module_power = try_get(power_consumer.get_maximum_consumed_power, 0) + total_module_consumed_power = total_module_consumed_power + module_power + fvs[dict_index] = (CHASSIS_INFO_POWER_CONSUMER_FIELD.format(name), str(module_power)) + dict_index += 1 + + #Record total supplied and consumed power + self.total_supplied_power = total_supplied_power + self.total_consumed_power = total_fan_consumed_power + total_module_consumed_power #Record in state DB in chassis table - fvs[index + 1] = (CHASSIS_INFO_POWER_SUPPLIER_FIELD.format('PSU'), str(self.total_supplied_power)) + fvs[dict_index] = (CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD, str(self.total_supplied_power)) + fvs[dict_index + 1] = (CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD, str(self.total_consumed_power)) chassis_tbl.set(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1), fvs) def update_master_status(self): @@ -196,8 +244,13 @@ class PsuChassisInfo(object): def _set_psu_master_led(self, master_status): try: - color = platform_chassis.STATUS_LED_COLOR_GREEN if master_status else platform_chassis.STATUS_LED_COLOR_RED - platform_chassis.set_status_master_led(DeviceBase.DEVICE_TYPE_PSU, color) + try: + from sonic_platform.psu import Psu + except ImportError, e: + from tests.mock_platform import MockPsu as Psu + + color = DeviceBase.STATUS_LED_COLOR_GREEN if master_status else DeviceBase.STATUS_LED_COLOR_RED + Psu.set_status_master_led(color) except NotImplementedError as e: pass @@ -339,8 +392,9 @@ class DaemonPsud(daemon_base.DaemonBase): self.update_psu_data(psu_tbl) self._update_led_color(psu_tbl) - self.update_psu_chassis_info(chassis_tbl) - self.update_master_led_color(chassis_tbl) + if platform_chassis is not None and platform_chassis.is_modular_chassis(): + self.update_psu_chassis_info(chassis_tbl) + self.update_master_led_color(chassis_tbl) self.log_info("Stop daemon main loop") @@ -504,7 +558,7 @@ class DaemonPsud(daemon_base.DaemonBase): return if not self.psu_chassis_info: - self.psu_chassis_info = PsuChassisInfo() + self.psu_chassis_info = PsuChassisInfo(SYSLOG_IDENTIFIER, platform_chassis) self.psu_chassis_info.run_power_budget(chassis_tbl) diff --git a/sonic-psud/setup.cfg b/sonic-psud/setup.cfg new file mode 100644 index 000000000..b7e478982 --- /dev/null +++ b/sonic-psud/setup.cfg @@ -0,0 +1,2 @@ +[aliases] +test=pytest diff --git a/sonic-psud/setup.py b/sonic-psud/setup.py index 3a465b901..59ef04894 100644 --- a/sonic-psud/setup.py +++ b/sonic-psud/setup.py @@ -10,12 +10,20 @@ url='https://github.com/Azure/sonic-platform-daemons', maintainer='Kevin Wang', maintainer_email='kevinw@mellanox.com', + packages=[ + 'tests' + ], scripts=[ 'scripts/psud', ], setup_requires= [ + 'pytest-runner', 'wheel' ], + tests_require = [ + 'pytest', + 'mock>=2.0.0' + ], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: No Input/Output (Daemon)', @@ -29,4 +37,5 @@ 'Topic :: System :: Hardware', ], keywords='sonic SONiC psu PSU daemon psud PSUD', + test_suite='setup.get_test_suite' ) diff --git a/sonic-psud/tests/__init__.py b/sonic-psud/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sonic-psud/tests/mock_device_base.py b/sonic-psud/tests/mock_device_base.py new file mode 100644 index 000000000..272a99482 --- /dev/null +++ b/sonic-psud/tests/mock_device_base.py @@ -0,0 +1,11 @@ +class DeviceBase(): + #Device-types + DEVICE_TYPE_PSU = "PSU" + DEVICE_TYPE_FAN = "FAN" + DEVICE_TYPE_FANDRAWER = "FAN-DRAWER" + + #LED colors + STATUS_LED_COLOR_GREEN = "green" + STATUS_LED_COLOR_AMBER = "amber" + STATUS_LED_COLOR_RED = "red" + STATUS_LED_COLOR_OFF = "off" diff --git a/sonic-psud/tests/mock_platform.py b/sonic-psud/tests/mock_platform.py new file mode 100644 index 000000000..701bb1b70 --- /dev/null +++ b/sonic-psud/tests/mock_platform.py @@ -0,0 +1,116 @@ +from .mock_device_base import DeviceBase + +class MockDevice: + def __init__(self): + self.name = None + self.presence = True + self.model = 'Module Model' + self.serial = 'Module Serial' + + def get_name(self): + return self.name + + def set_presence(self, presence): + self.presence = presence + + def get_presence(self): + return self.presence + + def get_model(self): + return self.model + + def get_serial(self): + return self.serial + +class MockPsu(MockDevice): + + psu_master_led_color = DeviceBase.STATUS_LED_COLOR_OFF + + def __init__(self, psu_presence, psu_status, psu_name): + self.name = psu_name + self.presence = True + self.psu_status = psu_status + + def get_powergood_status(self): + return self.psu_status + + def set_status(self, status): + self.psu_status = status + + def set_maximum_supplied_power(self, supplied_power): + self.max_supplied_power = supplied_power + + def get_maximum_supplied_power(self): + return self.max_supplied_power + + @classmethod + def set_status_master_led(cls, color): + cls.psu_master_led_color = color + + @classmethod + def get_status_master_led(cls): + return cls.psu_master_led_color + +class MockFanDrawer(MockDevice): + def __init__(self, fan_drawer_presence, fan_drawer_status, fan_drawer_name): + self.name = fan_drawer_name + self.presence = True + self.fan_drawer_status = fan_drawer_status + + def get_status(self): + return self.fan_drawer_status + + def set_status(self, status): + self.fan_drawer_status = status + + def set_maximum_consumed_power(self, consumed_power): + self.max_consumed_power = consumed_power + + def get_maximum_consumed_power(self): + return self.max_consumed_power + +class MockModule(MockDevice): + def __init__(self, module_presence, module_status, module_name): + self.name = module_name + self.presence = True + self.module_status = module_status + + def get_status(self): + return self.module_status + + def set_status(self, status): + self.module_status = status + + def set_maximum_consumed_power(self, consumed_power): + self.max_consumed_power = consumed_power + + def get_maximum_consumed_power(self): + return self.max_consumed_power + +class MockChassis: + + def __init__(self): + self.psu_list = [] + self.fan_drawer_list = [] + self.module_list = [] + + def get_num_psus(self): + return len(self.psu_list) + + def get_all_psus(self): + return self.psu_list + + def get_psu(self, index): + return self.psu_list[index] + + def get_num_fan_drawers(self): + return len(self.fan_drawer_list) + + def get_all_fan_drawers(self): + return self.fan_drawer_list + + def get_num_modules(self): + return len(self.module_list) + + def get_all_modules(self): + return self.module_list diff --git a/sonic-psud/tests/mock_swsscommon.py b/sonic-psud/tests/mock_swsscommon.py new file mode 100644 index 000000000..bd16d7f7e --- /dev/null +++ b/sonic-psud/tests/mock_swsscommon.py @@ -0,0 +1,27 @@ +STATE_DB = '' + + +class Table: + def __init__(self, db, table_name): + self.table_name = table_name + self.mock_dict = {} + + def _del(self, key): + del self.mock_dict[key] + pass + + def set(self, key, fvs): + self.mock_dict[key] = fvs.fv_dict + pass + + def get(self, key): + if key in self.mock_dict: + return self.mock_dict[key] + return None + +class FieldValuePairs(dict): + def __init__(self, len): + self.fv_dict = {} + + def __setitem__(self, key, val_tuple): + self.fv_dict[val_tuple[0]] = val_tuple[1] diff --git a/sonic-psud/tests/test_psud.py b/sonic-psud/tests/test_psud.py new file mode 100644 index 000000000..2080d6fde --- /dev/null +++ b/sonic-psud/tests/test_psud.py @@ -0,0 +1,167 @@ +import os +import sys + +from mock import Mock, MagicMock, patch +from sonic_py_common import daemon_base + +from .mock_platform import MockChassis, MockPsu, MockFanDrawer, MockModule +from .mock_device_base import DeviceBase + +SYSLOG_IDENTIFIER = 'psud_test' +NOT_AVAILABLE = 'N/A' + +daemon_base.db_connect = MagicMock() + +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) + +from imp import load_source + +load_source('psud', scripts_path + '/psud') +from psud import * + +CHASSIS_INFO_TABLE = 'CHASSIS_INFO' +CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}' + +CHASSIS_INFO_POWER_CONSUMER_FIELD = 'Consumed Power {}' +CHASSIS_INFO_POWER_SUPPLIER_FIELD = 'Supplied Power {}' +CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD = 'Total Consumed Power' +CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD = 'Total Supplied Power' +CHASSIS_INFO_POWER_KEY_TEMPLATE = 'chassis_power_budget {}' + +def setup_function(): + PsuChassisInfo.log_notice = MagicMock() + PsuChassisInfo.log_warning = MagicMock() + + +def teardown_function(): + PsuChassisInfo.log_notice.reset() + PsuChassisInfo.log_warning.reset() + +#Test cases to cover functionality in PsuChassisInfo class +def test_psuchassis_check_psu_supplied_power(): + chassis = MockChassis() + psu1 = MockPsu(True, True, "PSU 1") + psu1_power = 510.0 + psu1.set_maximum_supplied_power(psu1_power) + chassis.psu_list.append(psu1) + + psu2 = MockPsu(True, True, "PSU 2") + psu2_power = 800.0 + psu2.set_maximum_supplied_power(psu2_power) + chassis.psu_list.append(psu2) + + psu3 = MockPsu(True, True, "PSU 3") + psu3_power = 350.0 + psu3.set_maximum_supplied_power(psu3_power) + chassis.psu_list.append(psu3) + + total_power = psu1_power + psu2_power + psu3_power + state_db = daemon_base.db_connect("STATE_DB") + chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) + chassis_info = PsuChassisInfo(SYSLOG_IDENTIFIER, chassis) + chassis_info.run_power_budget(chassis_tbl) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + + #Check if supplied power is recorded in DB + assert total_power == float(fvs[CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD]) + + #Check if psu1 is not present + psu1.set_presence(False) + total_power = psu2_power + psu3_power + chassis_info.run_power_budget(chassis_tbl) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + assert total_power == float(fvs[CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD]) + + #Check if psu2 status is NOT_OK + psu2.set_status(False) + total_power = psu3_power + chassis_info.run_power_budget(chassis_tbl) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + assert total_power == float(fvs[CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD]) + +def test_psuchassis_check_consumed_power(): + chassis = MockChassis() + fan_drawer1 = MockFanDrawer(True, True, "FanDrawer 1") + fan_drawer1_power = 510.0 + fan_drawer1.set_maximum_consumed_power(fan_drawer1_power) + chassis.fan_drawer_list.append(fan_drawer1) + + module1 = MockFanDrawer(True, True, "Module 1") + module1_power = 700.0 + module1.set_maximum_consumed_power(module1_power) + chassis.module_list.append(module1) + + total_power = fan_drawer1_power + module1_power + state_db = daemon_base.db_connect("STATE_DB") + chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) + chassis_info = PsuChassisInfo(SYSLOG_IDENTIFIER, chassis) + chassis_info.run_power_budget(chassis_tbl) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + + #Check if supplied power is recorded in DB + assert total_power == float(fvs[CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD]) + + #Check if fan_drawer1 present + fan_drawer1.set_presence(False) + total_power = module1_power + chassis_info.run_power_budget(chassis_tbl) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + assert total_power == float(fvs[CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD]) + + #Check if module1 present + fan_drawer1.set_presence(True) + module1.set_presence(False) + total_power = fan_drawer1_power + chassis_info.run_power_budget(chassis_tbl) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + assert total_power == float(fvs[CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD]) + +def test_psuchassis_check_power_budget(): + chassis = MockChassis() + psu = MockPsu(True, True, "PSU 1") + psu1_power = 510.0 + psu.set_maximum_supplied_power(psu1_power) + chassis.psu_list.append(psu) + + fan_drawer1 = MockFanDrawer(True, True, "FanDrawer 1") + fan_drawer1_power = 510.0 + fan_drawer1.set_maximum_consumed_power(fan_drawer1_power) + chassis.fan_drawer_list.append(fan_drawer1) + + module1 = MockFanDrawer(True, True, "Module 1") + module1_power = 700.0 + module1.set_maximum_consumed_power(module1_power) + chassis.module_list.append(module1) + + state_db = daemon_base.db_connect("STATE_DB") + chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) + chassis_info = PsuChassisInfo(SYSLOG_IDENTIFIER, chassis) + + #Check if supplied_power < consumed_power + chassis_info.run_power_budget(chassis_tbl) + if chassis_info.update_master_status(): + chassis_info._set_psu_master_led(chassis_info.master_status_good) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + + assert float(fvs[CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD]) < float(fvs[CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD]) + assert chassis_info.master_status_good == False + assert MockPsu.get_status_master_led() == DeviceBase.STATUS_LED_COLOR_RED + + #Add a PSU + psu = MockPsu(True, True, "PSU 2") + psu2_power = 800.0 + psu.set_maximum_supplied_power(psu2_power) + chassis.psu_list.append(psu) + + #Check if supplied_power > consumed_power + chassis_info.run_power_budget(chassis_tbl) + if chassis_info.update_master_status(): + chassis_info._set_psu_master_led(chassis_info.master_status_good) + fvs = chassis_tbl.get(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1)) + + assert float(fvs[CHASSIS_INFO_TOTAL_POWER_SUPPLIED_FIELD]) > float(fvs[CHASSIS_INFO_TOTAL_POWER_CONSUMED_FIELD]) + assert chassis_info.master_status_good == True + assert MockPsu.get_status_master_led() == DeviceBase.STATUS_LED_COLOR_GREEN From 9751b2525372938bbee9fafa184ce3ebc86b955e Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 28 Oct 2020 15:53:35 -0700 Subject: [PATCH 3/3] Fixing comments and adding pytest-cov --- .gitignore | 1 + sonic-psud/pytest.ini | 3 +-- sonic-psud/scripts/psud | 10 +++++----- sonic-psud/setup.py | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 1c2875d68..ee6450242 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ # Compiled code which doesn't end in '.pyc' sonic-thermalctld/scripts/thermalctldc +sonic-psud/scripts/psudc diff --git a/sonic-psud/pytest.ini b/sonic-psud/pytest.ini index c24fe5bb9..83b74d373 100644 --- a/sonic-psud/pytest.ini +++ b/sonic-psud/pytest.ini @@ -1,3 +1,2 @@ [pytest] -filterwarnings = - ignore::DeprecationWarning +addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud index 4a2132440..8a41ff0fb 100644 --- a/sonic-psud/scripts/psud +++ b/sonic-psud/scripts/psud @@ -26,7 +26,7 @@ except ImportError as e: try: from sonic_platform_base.device_base import DeviceBase -except ImportError, e: +except ImportError as e: from tests.mock_device_base import DeviceBase # @@ -192,7 +192,7 @@ class PsuChassisInfo(logger.Logger): continue name = try_get(psu.get_name, 'PSU {}'.format(index + 1)) - supplied_power = try_get(psu.get_maximum_supplied_power) + supplied_power = try_get(psu.get_maximum_supplied_power, 0.0) total_supplied_power = total_supplied_power + supplied_power fvs[dict_index] = (CHASSIS_INFO_POWER_SUPPLIER_FIELD.format(name), str(supplied_power)) dict_index += 1 @@ -203,7 +203,7 @@ class PsuChassisInfo(logger.Logger): continue name = try_get(power_consumer.get_name, 'FAN-DRAWER {}'.format(index)) - fan_drawer_power = try_get(power_consumer.get_maximum_consumed_power, 0) + fan_drawer_power = try_get(power_consumer.get_maximum_consumed_power, 0.0) total_fan_consumed_power = total_fan_consumed_power + fan_drawer_power fvs[dict_index] = (CHASSIS_INFO_POWER_CONSUMER_FIELD.format(name), str(fan_drawer_power)) dict_index += 1 @@ -214,7 +214,7 @@ class PsuChassisInfo(logger.Logger): continue name = try_get(power_consumer.get_name, 'MODULE {}'.format(index)) - module_power = try_get(power_consumer.get_maximum_consumed_power, 0) + module_power = try_get(power_consumer.get_maximum_consumed_power, 0.0) total_module_consumed_power = total_module_consumed_power + module_power fvs[dict_index] = (CHASSIS_INFO_POWER_CONSUMER_FIELD.format(name), str(module_power)) dict_index += 1 @@ -246,7 +246,7 @@ class PsuChassisInfo(logger.Logger): try: try: from sonic_platform.psu import Psu - except ImportError, e: + except ImportError as e: from tests.mock_platform import MockPsu as Psu color = DeviceBase.STATUS_LED_COLOR_GREEN if master_status else DeviceBase.STATUS_LED_COLOR_RED diff --git a/sonic-psud/setup.py b/sonic-psud/setup.py index 59ef04894..4f8b77216 100644 --- a/sonic-psud/setup.py +++ b/sonic-psud/setup.py @@ -22,7 +22,8 @@ ], tests_require = [ 'pytest', - 'mock>=2.0.0' + 'mock>=2.0.0', + 'pytest-cov' ], classifiers=[ 'Development Status :: 4 - Beta',