From 28b9dfd3a294904150b2d33bd3ff35a1a637d28d Mon Sep 17 00:00:00 2001 From: Qi Luo Date: Tue, 18 May 2021 03:45:41 +0000 Subject: [PATCH] Extend rfc3433.py to support more Physical Entity Sensor MIB entries **- What I did** Extend RFC3433 implementation with: 1. FAN tachometers 2. PSU current sensor 3. PSU voltage sensor 4. PSU power sensor 5. PSU temp sensor 6. Chassis temp sensor MIB HLD update PR to reflect this change please refer to: https://github.com/Azure/SONiC/pull/766 A fix for the LGTM checker **- How I did it** 1. Refactor sensor data parsing class by adding a base class BaseSensorData; inherit TransceiverSensorData, PSUSensorData, FANSensorData, and ThermalSensorData from it to reduce redundant code. 2. Adding more sensor MIB entry class: PSUTempSensor, PSUVoltageSensor, PSUCurrentSensor, PSUPowerSensor, FANSpeedSensor, and ThermalSensor. 3. Separate MIB update to different functions according to different sensors types: update_xcvr_dom_data, update_psu_sensor_data, update_fan_sensor_data, and update_thermal_sensor_data. 4. Add unit test cases to cover the new added MIB entries. 5. Add lgtm.yaml to fix the LGTM checker. **- How to verify it** Manual test and run updated community SNMP test case(https://github.com/Azure/sonic-mgmt/pull/3357). --- lgtm.yml | 25 ++ src/sonic_ax_impl/mibs/__init__.py | 2 + src/sonic_ax_impl/mibs/ietf/rfc2737.py | 3 +- src/sonic_ax_impl/mibs/ietf/rfc3433.py | 340 +++++++++++++++++- src/sonic_ax_impl/mibs/ietf/sensor_data.py | 281 +++++++++++++++ .../mibs/ietf/transceiver_sensor_data.py | 132 ------- tests/mock_tables/global_db/state_db.json | 125 +++++++ tests/mock_tables/state_db.json | 13 + tests/namespace/test_sensor.py | 167 +++++++-- tests/test_sensor.py | 161 ++++++++- 10 files changed, 1057 insertions(+), 192 deletions(-) create mode 100644 lgtm.yml create mode 100644 src/sonic_ax_impl/mibs/ietf/sensor_data.py delete mode 100644 src/sonic_ax_impl/mibs/ietf/transceiver_sensor_data.py diff --git a/lgtm.yml b/lgtm.yml new file mode 100644 index 000000000..124b90c50 --- /dev/null +++ b/lgtm.yml @@ -0,0 +1,25 @@ +extraction: + python: + python_setup: + version: "3" + prepare: + packages: + - libxml-simple-perl + - aspell + - aspell-en + - libhiredis-dev + - libnl-3-dev + - libnl-genl-3-dev + - libnl-route-3-dev + - libnl-nf-3-dev + - libzmq3-dev + - libzmq5 + - swig3.0 + - libpython2.7-dev + - libgtest-dev + - dh-exec + - doxygen + - graphviz + after_prepare: + - git clone https://github.com/Azure/sonic-py-swsssdk.git; pushd sonic-py-swsssdk; python3 setup.py bdist_wheel; popd + - pip3 install --upgrade --user sonic-py-swsssdk/dist/swsssdk-2.0.1-py3-none-any.whl diff --git a/src/sonic_ax_impl/mibs/__init__.py b/src/sonic_ax_impl/mibs/__init__.py index f3b0d17fa..fe7e74ddb 100644 --- a/src/sonic_ax_impl/mibs/__init__.py +++ b/src/sonic_ax_impl/mibs/__init__.py @@ -25,6 +25,8 @@ TABLE_NAME_SEPARATOR_COLON = ':' TABLE_NAME_SEPARATOR_VBAR = '|' +HOST_NAMESPACE_DB_IDX = 0 + redis_kwargs = {'unix_socket_path': '/var/run/redis/redis.sock'} diff --git a/src/sonic_ax_impl/mibs/ietf/rfc2737.py b/src/sonic_ax_impl/mibs/ietf/rfc2737.py index db5b86579..9c123bf25 100644 --- a/src/sonic_ax_impl/mibs/ietf/rfc2737.py +++ b/src/sonic_ax_impl/mibs/ietf/rfc2737.py @@ -21,8 +21,7 @@ from .physical_entity_sub_oid_generator import get_psu_sensor_sub_id from .physical_entity_sub_oid_generator import get_transceiver_sub_id from .physical_entity_sub_oid_generator import get_transceiver_sensor_sub_id -from .transceiver_sensor_data import TransceiverSensorData - +from .sensor_data import TransceiverSensorData @unique class PhysicalClass(int, Enum): diff --git a/src/sonic_ax_impl/mibs/ietf/rfc3433.py b/src/sonic_ax_impl/mibs/ietf/rfc3433.py index 82879cba8..1bde5d4ab 100644 --- a/src/sonic_ax_impl/mibs/ietf/rfc3433.py +++ b/src/sonic_ax_impl/mibs/ietf/rfc3433.py @@ -8,10 +8,52 @@ from swsssdk import port_util from ax_interface import MIBMeta, MIBUpdater, ValueType, SubtreeMIBEntry from sonic_ax_impl import mibs +from sonic_ax_impl.mibs import HOST_NAMESPACE_DB_IDX from sonic_ax_impl.mibs import Namespace - +from .physical_entity_sub_oid_generator import CHASSIS_SUB_ID from .physical_entity_sub_oid_generator import get_transceiver_sensor_sub_id -from .transceiver_sensor_data import TransceiverSensorData +from .physical_entity_sub_oid_generator import get_fan_drawer_sub_id +from .physical_entity_sub_oid_generator import get_fan_sub_id +from .physical_entity_sub_oid_generator import get_fan_tachometers_sub_id +from .physical_entity_sub_oid_generator import get_psu_sub_id +from .physical_entity_sub_oid_generator import get_psu_sensor_sub_id +from .physical_entity_sub_oid_generator import get_chassis_thermal_sub_id +from .sensor_data import ThermalSensorData, FANSensorData, PSUSensorData, TransceiverSensorData + +NOT_AVAILABLE = 'N/A' +CHASSIS_NAME_SUB_STRING = 'chassis' +PSU_NAME_SUB_STRING = 'PSU' + + +def is_null_empty_str(value): + """ + Indicate if a string value is null + :param value: input string value + :return: True is string value is empty or equal to 'N/A' or 'None' + """ + if not isinstance(value, str) or value == NOT_AVAILABLE or value == 'None' or value == '': + return True + return False + + +def get_db_data(info_dict, enum_type): + """ + :param info_dict: db info dict + :param enum_type: db field enum + :return: tuple of fields values defined in enum_type; + Empty string if field not in info_dict + """ + return (info_dict.get(field.value, "") + for field in enum_type) + + +@unique +class PhysicalRelationInfoDB(str, Enum): + """ + Physical relation info keys + """ + POSITION_IN_PARENT = 'position_in_parent' + PARENT_NAME = 'parent_name' @unique @@ -101,7 +143,6 @@ class SensorInterface: PRECISION = None CONVERTER = None - @classmethod def mib_values(cls, raw_value): """ @@ -219,12 +260,93 @@ class XcvrTxPowerSensor(SensorInterface): }) +class PSUTempSensor(SensorInterface): + """ + PSU temperature sensor. + """ + + TYPE = EntitySensorDataType.CELSIUS + SCALE = EntitySensorDataScale.UNITS + PRECISION = 3 + + +class PSUVoltageSensor(SensorInterface): + """ + PSU voltage sensor. + """ + + TYPE = EntitySensorDataType.VOLTS_DC + SCALE = EntitySensorDataScale.UNITS + PRECISION = 3 + + +class PSUCurrentSensor(SensorInterface): + """ + PSU current sensor. + """ + + TYPE = EntitySensorDataType.AMPERES + SCALE = EntitySensorDataScale.UNITS + PRECISION = 3 + + +class PSUPowerSensor(SensorInterface): + """ + PSU power sensor. + """ + + TYPE = EntitySensorDataType.WATTS + SCALE = EntitySensorDataScale.UNITS + PRECISION = 3 + + +PSUSensorData.bind_sensor_interface({ + 'temperature': PSUTempSensor, + 'voltage' : PSUVoltageSensor, + 'power' : PSUPowerSensor, + 'current' : PSUCurrentSensor +}) + + +class FANSpeedSensor(SensorInterface): + """ + FAN speed sensor. + """ + + TYPE = EntitySensorDataType.UNKNOWN + SCALE = EntitySensorDataScale.UNITS + PRECISION = 0 + + +FANSensorData.bind_sensor_interface({ + 'speed': FANSpeedSensor +}) + + +class ThermalSensor(SensorInterface): + """ + Temperature sensor. + """ + + TYPE = EntitySensorDataType.CELSIUS + SCALE = EntitySensorDataScale.UNITS + PRECISION = 3 + + +ThermalSensorData.bind_sensor_interface({ + 'temperature': ThermalSensor +}) + + class PhysicalSensorTableMIBUpdater(MIBUpdater): """ Updater for sensors. """ TRANSCEIVER_DOM_KEY_PATTERN = mibs.transceiver_dom_table("*") + PSU_SENSOR_KEY_PATTERN = mibs.psu_info_table("*") + FAN_SENSOR_KEY_PATTERN = mibs.fan_info_table("*") + THERMAL_SENSOR_KEY_PATTERN = mibs.thermal_info_table("*") def __init__(self): """ @@ -239,7 +361,7 @@ def __init__(self): # list of available sub OIDs self.sub_ids = [] - # sensor MIB requiered values + # sensor MIB required values self.ent_phy_sensor_type_map = {} self.ent_phy_sensor_scale_map = {} self.ent_phy_sensor_precision_map = {} @@ -247,6 +369,9 @@ def __init__(self): self.ent_phy_sensor_oper_state_map = {} self.transceiver_dom = [] + self.fan_sensor = [] + self.psu_sensor = [] + self.thermal_sensor = [] def reinit_data(self): """ @@ -260,18 +385,28 @@ def reinit_data(self): self.ent_phy_sensor_value_map = {} self.ent_phy_sensor_oper_state_map = {} - transceiver_dom_encoded = Namespace.dbs_keys(self.statedb, mibs.STATE_DB, - self.TRANSCEIVER_DOM_KEY_PATTERN) + transceiver_dom_encoded = Namespace.dbs_keys(self.statedb, mibs.STATE_DB, self.TRANSCEIVER_DOM_KEY_PATTERN) if transceiver_dom_encoded: self.transceiver_dom = [entry for entry in transceiver_dom_encoded] - def update_data(self): - """ - Update sensors cache. - """ + # for FAN, PSU and thermal sensors, they are in host namespace DB, to avoid iterating all namespace DBs, + # just get data from host namespace DB, which is self.statedb[0]. + fan_sensor_encoded = self.statedb[HOST_NAMESPACE_DB_IDX].keys(self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, + self.FAN_SENSOR_KEY_PATTERN) + if fan_sensor_encoded: + self.fan_sensor = [entry for entry in fan_sensor_encoded] - self.sub_ids = [] + psu_sensor_encoded = self.statedb[HOST_NAMESPACE_DB_IDX].keys(self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, + self.PSU_SENSOR_KEY_PATTERN) + if psu_sensor_encoded: + self.psu_sensor = [entry for entry in psu_sensor_encoded] + + thermal_sensor_encoded = self.statedb[HOST_NAMESPACE_DB_IDX].keys(self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, + self.THERMAL_SENSOR_KEY_PATTERN) + if thermal_sensor_encoded: + self.thermal_sensor = [entry for entry in thermal_sensor_encoded] + def update_xcvr_dom_data(self): if not self.transceiver_dom: return @@ -288,8 +423,7 @@ def update_data(self): continue # get transceiver sensors from transceiver dom entry in STATE DB - transceiver_dom_entry_data = Namespace.dbs_get_all(self.statedb, mibs.STATE_DB, - transceiver_dom_entry) + transceiver_dom_entry_data = Namespace.dbs_get_all(self.statedb, mibs.STATE_DB, transceiver_dom_entry) if not transceiver_dom_entry_data: continue @@ -303,19 +437,187 @@ def update_data(self): try: mib_values = sensor.mib_values(raw_sensor_value) except (ValueError, ArithmeticError): - mibs.logger.error("Exception occured when converting" + mibs.logger.error("Exception occurred when converting" "value for sensor {} interface {}".format(sensor, interface)) - # skip continue else: self.ent_phy_sensor_type_map[sub_id], \ - self.ent_phy_sensor_scale_map[sub_id], \ - self.ent_phy_sensor_precision_map[sub_id], \ - self.ent_phy_sensor_value_map[sub_id], \ - self.ent_phy_sensor_oper_state_map[sub_id] = mib_values + self.ent_phy_sensor_scale_map[sub_id], \ + self.ent_phy_sensor_precision_map[sub_id], \ + self.ent_phy_sensor_value_map[sub_id], \ + self.ent_phy_sensor_oper_state_map[sub_id] = mib_values + + self.sub_ids.append(sub_id) + + def update_psu_sensor_data(self): + if not self.psu_sensor: + return + + for psu_sensor_entry in self.psu_sensor: + psu_name = psu_sensor_entry.split(mibs.TABLE_NAME_SEPARATOR_VBAR)[-1] + psu_relation_info = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, mibs.physical_entity_info_table(psu_name)) + psu_position, psu_parent_name = get_db_data(psu_relation_info, PhysicalRelationInfoDB) + if is_null_empty_str(psu_position): + continue + psu_position = int(psu_position) + psu_sub_id = get_psu_sub_id(psu_position) + + psu_sensor_entry_data = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, psu_sensor_entry) + + if not psu_sensor_entry_data: + continue + + sensor_data_list = PSUSensorData.create_sensor_data(psu_sensor_entry_data) + for sensor_data in sensor_data_list: + raw_sensor_value = sensor_data.get_raw_value() + if is_null_empty_str(raw_sensor_value): + continue + sensor = sensor_data.get_sensor_interface() + sub_id = get_psu_sensor_sub_id(psu_sub_id, sensor_data.get_name().lower()) + + try: + mib_values = sensor.mib_values(raw_sensor_value) + except (ValueError, ArithmeticError): + mibs.logger.error("Exception occurred when converting" + "value for sensor {} PSU {}".format(sensor, psu_name)) + continue + else: + self.ent_phy_sensor_type_map[sub_id], \ + self.ent_phy_sensor_scale_map[sub_id], \ + self.ent_phy_sensor_precision_map[sub_id], \ + self.ent_phy_sensor_value_map[sub_id], \ + self.ent_phy_sensor_oper_state_map[sub_id] = mib_values self.sub_ids.append(sub_id) + def update_fan_sensor_data(self): + if not self.fan_sensor: + return + + fan_parent_sub_id = 0 + for fan_sensor_entry in self.fan_sensor: + fan_name = fan_sensor_entry.split(mibs.TABLE_NAME_SEPARATOR_VBAR)[-1] + fan_relation_info = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, mibs.physical_entity_info_table(fan_name)) + fan_position, fan_parent_name = get_db_data(fan_relation_info, PhysicalRelationInfoDB) + if is_null_empty_str(fan_position): + continue + + fan_position = int(fan_position) + + if CHASSIS_NAME_SUB_STRING in fan_parent_name: + fan_parent_sub_id = (CHASSIS_SUB_ID,) + else: + fan_parent_relation_info = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, mibs.physical_entity_info_table(fan_parent_name)) + if fan_parent_relation_info: + fan_parent_position, fan_grad_parent_name = get_db_data(fan_parent_relation_info, + PhysicalRelationInfoDB) + + fan_parent_position = int(fan_parent_position) + + if PSU_NAME_SUB_STRING in fan_parent_name: + fan_parent_sub_id = get_psu_sub_id(fan_parent_position) + else: + fan_parent_sub_id = get_fan_drawer_sub_id(fan_parent_position) + else: + mibs.logger.error("fan_name = {} get fan parent failed".format(fan_name)) + continue + + fan_sub_id = get_fan_sub_id(fan_parent_sub_id, fan_position) + + fan_sensor_entry_data = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, fan_sensor_entry) + + if not fan_sensor_entry_data: + mibs.logger.error("fan_name = {} get fan_sensor_entry_data failed".format(fan_name)) + continue + + sensor_data_list = FANSensorData.create_sensor_data(fan_sensor_entry_data) + for sensor_data in sensor_data_list: + raw_sensor_value = sensor_data.get_raw_value() + if is_null_empty_str(raw_sensor_value): + continue + sensor = sensor_data.get_sensor_interface() + sub_id = get_fan_tachometers_sub_id(fan_sub_id) + + try: + mib_values = sensor.mib_values(raw_sensor_value) + except (ValueError, ArithmeticError): + mibs.logger.error("Exception occurred when converting" + "value for sensor {} PSU {}".format(sensor, fan_name)) + continue + else: + self.ent_phy_sensor_type_map[sub_id], \ + self.ent_phy_sensor_scale_map[sub_id], \ + self.ent_phy_sensor_precision_map[sub_id], \ + self.ent_phy_sensor_value_map[sub_id], \ + self.ent_phy_sensor_oper_state_map[sub_id] = mib_values + + self.sub_ids.append(sub_id) + + def update_thermal_sensor_data(self): + if not self.thermal_sensor: + return + + for thermal_sensor_entry in self.thermal_sensor: + thermal_name = thermal_sensor_entry.split(mibs.TABLE_NAME_SEPARATOR_VBAR)[-1] + thermal_relation_info = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, mibs.physical_entity_info_table(thermal_name)) + thermal_position, thermal_parent_name = get_db_data(thermal_relation_info, PhysicalRelationInfoDB) + + if is_null_empty_str(thermal_parent_name) or is_null_empty_str(thermal_parent_name) or \ + CHASSIS_NAME_SUB_STRING not in thermal_parent_name.lower(): + continue + + thermal_position = int(thermal_position) + + thermal_sensor_entry_data = self.statedb[HOST_NAMESPACE_DB_IDX].get_all( + self.statedb[HOST_NAMESPACE_DB_IDX].STATE_DB, thermal_sensor_entry) + + if not thermal_sensor_entry_data: + continue + + sensor_data_list = ThermalSensorData.create_sensor_data(thermal_sensor_entry_data) + for sensor_data in sensor_data_list: + raw_sensor_value = sensor_data.get_raw_value() + if is_null_empty_str(raw_sensor_value): + continue + sensor = sensor_data.get_sensor_interface() + sub_id = get_chassis_thermal_sub_id(thermal_position) + + try: + mib_values = sensor.mib_values(raw_sensor_value) + except (ValueError, ArithmeticError): + mibs.logger.error("Exception occurred when converting" + "value for sensor {} PSU {}".format(sensor, thermal_name)) + continue + else: + self.ent_phy_sensor_type_map[sub_id], \ + self.ent_phy_sensor_scale_map[sub_id], \ + self.ent_phy_sensor_precision_map[sub_id], \ + self.ent_phy_sensor_value_map[sub_id], \ + self.ent_phy_sensor_oper_state_map[sub_id] = mib_values + + self.sub_ids.append(sub_id) + + def update_data(self): + """ + Update sensors cache. + """ + + self.sub_ids = [] + + self.update_xcvr_dom_data() + + self.update_psu_sensor_data() + + self.update_fan_sensor_data() + + self.update_thermal_sensor_data() + self.sub_ids.sort() def get_next(self, sub_id): diff --git a/src/sonic_ax_impl/mibs/ietf/sensor_data.py b/src/sonic_ax_impl/mibs/ietf/sensor_data.py new file mode 100644 index 000000000..7f864e57a --- /dev/null +++ b/src/sonic_ax_impl/mibs/ietf/sensor_data.py @@ -0,0 +1,281 @@ +import re + +from .physical_entity_sub_oid_generator import SENSOR_TYPE_TEMP +from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_TX_POWER +from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_RX_POWER +from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_TX_BIAS +from .physical_entity_sub_oid_generator import SENSOR_TYPE_VOLTAGE +from .physical_entity_sub_oid_generator import SENSOR_TYPE_FAN +from .physical_entity_sub_oid_generator import SENSOR_TYPE_POWER +from .physical_entity_sub_oid_generator import SENSOR_TYPE_CURRENT + + +class BaseSensorData: + """ + Base sensor data class. Responsible for: + 1. Manage concrete sensor data class + 2. Create concrete sensor data instances + 3. Provide common logic for concrete sensor data class + """ + sensor_attr_dict = {} + + @classmethod + def sort_sensor_data(cls, sensor_data_list): + return sorted(sensor_data_list, key=lambda x: x.get_sort_factor()) + + @classmethod + def bind_sensor_interface(cls, sensor_interface_dict): + for name, sensor_attrs in cls.sensor_attr_dict.items(): + if name in sensor_interface_dict: + sensor_attrs['sensor_interface'] = sensor_interface_dict[name] + + def __init__(self, key, value, sensor_attrs, match_result): + self._key = key + self._value = value + self._sensor_attrs = sensor_attrs + self._match_result = match_result + + def get_key(self): + """ + Get the redis key of this sensor + """ + return self._key + + def get_raw_value(self): + """ + Get raw redis value of this sensor + """ + return self._value + + def get_name(self): + """ + Get the name of this sensor. Concrete sensor data class must override + this. + """ + return self._sensor_attrs['name'] + + def get_sort_factor(self): + """ + Get sort factor for this sensor. Concrete sensor data class must override + this. + """ + return self._sensor_attrs['sort_factor'] + + def get_oid_offset(self): + """ + Get OID offset of this sensor. + """ + return self._sensor_attrs['oid_offset_base'] + + def get_sensor_interface(self): + """ + Get sensor interface of this sensor. Used by rfc3433. + """ + return self._sensor_attrs['sensor_interface'] + + +class TransceiverSensorData(BaseSensorData): + """ + Base transceiver sensor data class. Responsible for: + 1. Manage concrete sensor data class + 2. Create concrete sensor data instances + 3. Provide common logic for concrete sensor data class + """ + + sensor_attr_dict = { + 'temperature': { + 'pattern': 'temperature', + 'name': 'Temperature', + 'oid_offset_base': SENSOR_TYPE_TEMP, + 'sort_factor': 0, + 'lane_based_sensor': False + }, + 'voltage': { + 'pattern': 'voltage', + 'name': 'Voltage', + 'oid_offset_base': SENSOR_TYPE_VOLTAGE, + 'sort_factor': 9000, + 'lane_based_sensor': False + }, + 'rxpower': { + 'pattern': r'rx(\d+)power', + 'name': 'RX Power', + 'oid_offset_base': SENSOR_TYPE_PORT_RX_POWER, + 'sort_factor': 2000, + 'lane_based_sensor': True + }, + 'txpower': { + 'pattern': r'tx(\d+)power', + 'name': 'TX Power', + 'oid_offset_base': SENSOR_TYPE_PORT_TX_POWER, + 'sort_factor': 1000, + 'lane_based_sensor': True + }, + 'txbias': { + 'pattern': r'tx(\d+)bias', + 'name': 'TX Bias', + 'oid_offset_base': SENSOR_TYPE_PORT_TX_BIAS, + 'sort_factor': 3000, + 'lane_based_sensor': True + } + } + + @classmethod + def create_sensor_data(cls, sensor_data_dict): + """ + Create sensor data instances according to the sensor data got from redis + :param sensor_data_dict: sensor data got from redis + :return: A sorted sensor data instance list + """ + sensor_data_list = [] + for name, value in sensor_data_dict.items(): + for sensor_attrs in cls.sensor_attr_dict.values(): + match_result = re.match(sensor_attrs['pattern'], name) + if match_result: + sensor_data = TransceiverSensorData(name, value, sensor_attrs, match_result) + sensor_data_list.append(sensor_data) + + return sensor_data_list + + def get_sort_factor(self): + """ + Get sort factor for this sensor. Concrete sensor data class must override + this. + """ + return self._sensor_attrs['sort_factor'] + self.get_lane_number() + + def get_lane_number(self): + """ + Get lane number of this sensor. For example, some transceivers have more than one rx power sensor, the sub index + of rx1power is 1, the sub index of rx2power is 2. + """ + return int(self._match_result.group(1)) if self._sensor_attrs['lane_based_sensor'] else 0 + + def get_oid_offset(self): + """ + Get OID offset of this sensor. + """ + return self._sensor_attrs['oid_offset_base'] + self.get_lane_number() + + +class PSUSensorData(BaseSensorData): + """ + Base PSU sensor data class. Responsible for: + 1. Manage concrete sensor data class + 2. Create concrete sensor data instances + 3. Provide common logic for concrete sensor data class + """ + + sensor_attr_dict = { + 'temperature': { + 'pattern': 'temp', + 'name': 'Temperature', + 'oid_offset_base': SENSOR_TYPE_TEMP, + 'sort_factor': 0 + }, + 'voltage': { + 'pattern': 'voltage', + 'name': 'Voltage', + 'oid_offset_base': SENSOR_TYPE_VOLTAGE, + 'sort_factor': 9000 + }, + 'power': { + 'pattern': 'power', + 'name': 'Power', + 'oid_offset_base': SENSOR_TYPE_POWER, + 'sort_factor': 2000 + }, + 'current': { + 'pattern': 'current', + 'name': 'Current', + 'oid_offset_base': SENSOR_TYPE_CURRENT, + 'sort_factor': 1000 + } + } + + @classmethod + def create_sensor_data(cls, sensor_data_dict): + """ + Create sensor data instances according to the sensor data got from redis + :param sensor_data_dict: sensor data got from redis + :return: A sorted sensor data instance list + """ + sensor_data_list = [] + for name, value in sensor_data_dict.items(): + for sensor_attrs in cls.sensor_attr_dict.values(): + match_result = re.fullmatch(sensor_attrs['pattern'], name) + if match_result: + sensor_data = PSUSensorData(name, value, sensor_attrs, match_result) + sensor_data_list.append(sensor_data) + + return sensor_data_list + + +class FANSensorData(BaseSensorData): + """ + Base FAN sensor data class. Responsible for: + 1. Manage concrete sensor data class + 2. Create concrete sensor data instances + 3. Provide common logic for concrete sensor data class + """ + + sensor_attr_dict = { + 'speed': { + 'pattern': 'speed', + 'name': 'Speed', + 'oid_offset_base': SENSOR_TYPE_FAN, + 'sort_factor': 0 + } + } + + @classmethod + def create_sensor_data(cls, sensor_data_dict): + """ + Create sensor data instances according to the sensor data got from redis + :param sensor_data_dict: sensor data got from redis + :return: A sorted sensor data instance list + """ + sensor_data_list = [] + for name, value in sensor_data_dict.items(): + for sensor_attrs in cls.sensor_attr_dict.values(): + match_result = re.fullmatch(sensor_attrs['pattern'], name) + if match_result: + sensor_data = FANSensorData(name, value, sensor_attrs, match_result) + sensor_data_list.append(sensor_data) + + return sensor_data_list + + +class ThermalSensorData(BaseSensorData): + """ + Base Thermal sensor data class. Responsible for: + 1. Manage concrete sensor data class + 2. Create concrete sensor data instances + 3. Provide common logic for concrete sensor data class + """ + + sensor_attr_dict = { + 'temperature': { + 'pattern': 'temperature', + 'name': 'Temperature', + 'oid_offset_base': SENSOR_TYPE_TEMP, + 'sort_factor': 0 + } + } + + @classmethod + def create_sensor_data(cls, sensor_data_dict): + """ + Create sensor data instances according to the sensor data got from redis + :param sensor_data_dict: sensor data got from redis + :return: A sorted sensor data instance list + """ + sensor_data_list = [] + for name, value in sensor_data_dict.items(): + for sensor_attrs in cls.sensor_attr_dict.values(): + match_result = re.fullmatch(sensor_attrs['pattern'], name) + if match_result: + sensor_data = ThermalSensorData(name, value, sensor_attrs, match_result) + sensor_data_list.append(sensor_data) + + return sensor_data_list diff --git a/src/sonic_ax_impl/mibs/ietf/transceiver_sensor_data.py b/src/sonic_ax_impl/mibs/ietf/transceiver_sensor_data.py deleted file mode 100644 index 587d8adbe..000000000 --- a/src/sonic_ax_impl/mibs/ietf/transceiver_sensor_data.py +++ /dev/null @@ -1,132 +0,0 @@ -import re - -from .physical_entity_sub_oid_generator import SENSOR_TYPE_TEMP -from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_TX_POWER -from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_RX_POWER -from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_TX_BIAS -from .physical_entity_sub_oid_generator import SENSOR_TYPE_VOLTAGE - - -class TransceiverSensorData: - """ - Base transceiver sensor data class. Responsible for: - 1. Manage concrete sensor data class - 2. Create concrete sensor data instances - 3. Provide common logic for concrete sensor data class - """ - - sensor_attr_dict = { - 'temperature': { - 'pattern': 'temperature', - 'name': 'Temperature', - 'oid_offset_base': SENSOR_TYPE_TEMP, - 'sort_factor': 0, - 'lane_based_sensor': False - }, - 'voltage': { - 'pattern': 'voltage', - 'name': 'Voltage', - 'oid_offset_base': SENSOR_TYPE_VOLTAGE, - 'sort_factor': 9000, - 'lane_based_sensor': False - }, - 'rxpower': { - 'pattern': r'rx(\d+)power', - 'name': 'RX Power', - 'oid_offset_base': SENSOR_TYPE_PORT_RX_POWER, - 'sort_factor': 2000, - 'lane_based_sensor': True - }, - 'txpower': { - 'pattern': r'tx(\d+)power', - 'name': 'TX Power', - 'oid_offset_base': SENSOR_TYPE_PORT_TX_POWER, - 'sort_factor': 1000, - 'lane_based_sensor': True - }, - 'txbias': { - 'pattern': r'tx(\d+)bias', - 'name': 'TX Bias', - 'oid_offset_base': SENSOR_TYPE_PORT_TX_BIAS, - 'sort_factor': 3000, - 'lane_based_sensor': True - } - } - - def __init__(self, key, value, sensor_attrs, match_result): - self._key = key - self._value = value - self._sensor_attrs = sensor_attrs - self._match_result = match_result - - @classmethod - def create_sensor_data(cls, sensor_data_dict): - """ - Create sensor data instances according to the sensor data got from redis - :param sensor_data_dict: sensor data got from redis - :return: A sorted sensor data instance list - """ - sensor_data_list = [] - for name, value in sensor_data_dict.items(): - for sensor_attrs in cls.sensor_attr_dict.values(): - match_result = re.match(sensor_attrs['pattern'], name) - if match_result: - sensor_data = TransceiverSensorData(name, value, sensor_attrs, match_result) - sensor_data_list.append(sensor_data) - - return sensor_data_list - - @classmethod - def sort_sensor_data(cls, sensor_data_list): - return sorted(sensor_data_list, key=lambda x: x.get_sort_factor()) - - @classmethod - def bind_sensor_interface(cls, sensor_interface_dict): - for name, sensor_attrs in cls.sensor_attr_dict.items(): - if name in sensor_interface_dict: - sensor_attrs['sensor_interface'] = sensor_interface_dict[name] - - def get_key(self): - """ - Get the redis key of this sensor - """ - return self._key - - def get_raw_value(self): - """ - Get raw redis value of this sensor - """ - return self._value - - def get_name(self): - """ - Get the name of this sensor. Concrete sensor data class must override - this. - """ - return self._sensor_attrs['name'] - - def get_sort_factor(self): - """ - Get sort factor for this sensor. Concrete sensor data class must override - this. - """ - return self._sensor_attrs['sort_factor'] + self.get_lane_number() - - def get_lane_number(self): - """ - Get lane number of this sensor. For example, some transceivers have more than one rx power sensor, the sub index - of rx1power is 1, the sub index of rx2power is 2. - """ - return int(self._match_result.group(1)) if self._sensor_attrs['lane_based_sensor'] else 0 - - def get_oid_offset(self): - """ - Get OID offset of this sensor. - """ - return self._sensor_attrs['oid_offset_base'] + self.get_lane_number() - - def get_sensor_interface(self): - """ - Get sensor interface of this sensor. Used by rfc3433. - """ - return self._sensor_attrs['sensor_interface'] diff --git a/tests/mock_tables/global_db/state_db.json b/tests/mock_tables/global_db/state_db.json index 2c63c0851..0674d83ab 100644 --- a/tests/mock_tables/global_db/state_db.json +++ b/tests/mock_tables/global_db/state_db.json @@ -1,2 +1,127 @@ { + "DEVICE_METADATA|localhost": { + "chassis_serial_number": "SAMPLETESTSN" + }, + "PSU_INFO|PSU 1": { + "presence": "false", + "status": "false", + "current": "15.4", + "power": "302.6", + "voltage": "15.1", + "temp": "30.1" + }, + "PSU_INFO|PSU 2": { + "presence": "true", + "status": "true", + "current": "13.4", + "power": "312.6", + "voltage": "13.1", + "temp": "31.1", + "model": "PSU_MODEL", + "serial": "PSU_SERIAL", + "is_replaceable": "True" + }, + "PSU_INFO|PSU 3": { + "presence": "true", + "status": "false", + "current": "15.5", + "power": "302.8", + "voltage": "15.5", + "temp": "30.8" + }, + "CHASSIS_INFO|chassis 1": { + "psu_num": "3" + }, + "TRANSCEIVER_INFO|Ethernet0": { + "type": "QSFP+", + "hardware_rev": "A1", + "serial": "SERIAL_NUM", + "manufacturer": "VENDOR_NAME", + "model": "MODEL_NAME", + "is_replaceable": "True" + }, + "TRANSCEIVER_INFO|Ethernet1": { + "type": "QSFP-DD", + "hardware_rev": "A1", + "serial": "SERIAL_NUM", + "manufacturer": "VENDOR_NAME", + "model": "MODEL_NAME", + "is_replaceable": "True" + }, + "TRANSCEIVER_DOM_SENSOR|Ethernet0": { + "temperature": 25.39, + "voltage": 3.37, + "tx1bias": "N/A", + "tx2bias": 4.44, + "tx3bias": "inf", + "tx4bias": 4.44, + "rx1power": "-inf", + "rx2power": -0.97, + "rx3power": -0.97, + "rx4power": -0.97, + "tx1power": -5.4, + "tx2power": -5.4, + "tx3power": -5.4, + "tx4power": -5.4 + }, + "MGMT_PORT_TABLE|eth0": { + "oper_status": "down" + }, + "MGMT_PORT_TABLE|eth1": { + "oper_status": "up" + }, + "PHYSICAL_ENTITY_INFO|PSU 1": { + "position_in_parent": 1, + "parent_name": "chassis 1" + }, + "PHYSICAL_ENTITY_INFO|PSU 2": { + "position_in_parent": 2, + "parent_name": "chassis 1" + }, + "PHYSICAL_ENTITY_INFO|PSU 3": { + "position_in_parent": 3, + "parent_name": "chassis 1" + }, + "PHYSICAL_ENTITY_INFO|drawer1": { + "position_in_parent": 1, + "parent_name": "chassis 1" + }, + "PHYSICAL_ENTITY_INFO|fan1": { + "position_in_parent": 1, + "parent_name": "drawer1" + }, + "PHYSICAL_ENTITY_INFO|thermal1": { + "position_in_parent": 1, + "parent_name": "chassis 1" + }, + "PHYSICAL_ENTITY_INFO|psu_2_fan_1": { + "position_in_parent": "1", + "parent_name": "PSU 2" + }, + "FAN_DRAWER_INFO|drawer1": { + "model": "DRAWERMODEL", + "serial": "DRAWERSERIAL", + "presence": "True", + "is_replaceable": "True" + }, + "FAN_INFO|fan1": { + "model": "FANMODEL", + "serial": "FANSERIAL", + "speed": "50", + "presence": "True", + "is_replaceable": "True" + }, + "FAN_INFO|psu_2_fan_1": { + "presence": "True", + "status": "True", + "speed": "57", + "drawer_name": "N/A", + "model": "PSUFANMODEL", + "serial": "PSUFANSERIAL", + "is_replaceable": "False" + }, + "TEMPERATURE_INFO|thermal1": { + "temperature": "20.5", + "is_replaceable": "False" + } } diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 20066305d..ae45b16a4 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -94,6 +94,10 @@ "position_in_parent": 1, "parent_name": "chassis 1" }, + "PHYSICAL_ENTITY_INFO|psu_2_fan_1": { + "position_in_parent": "1", + "parent_name": "PSU 2" + }, "FAN_DRAWER_INFO|drawer1": { "model": "DRAWERMODEL", "serial": "DRAWERSERIAL", @@ -107,6 +111,15 @@ "presence": "True", "is_replaceable": "True" }, + "FAN_INFO|psu_2_fan_1": { + "presence": "True", + "status": "True", + "speed": "57", + "drawer_name": "N/A", + "model": "PSUFANMODEL", + "serial": "PSUFANSERIAL", + "is_replaceable": "False" +}, "TEMPERATURE_INFO|thermal1": { "temperature": "20.5", "is_replaceable": "False" diff --git a/tests/namespace/test_sensor.py b/tests/namespace/test_sensor.py index 3c12bb68a..b5eaf7cae 100644 --- a/tests/namespace/test_sensor.py +++ b/tests/namespace/test_sensor.py @@ -17,6 +17,12 @@ from ax_interface.encodings import ObjectIdentifier from ax_interface.constants import PduTypes from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_transceiver_sub_id, get_transceiver_sensor_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_psu_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_psu_sensor_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_fan_drawer_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_fan_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_fan_tachometers_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_chassis_thermal_sub_id from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import SENSOR_TYPE_TEMP from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import SENSOR_TYPE_VOLTAGE from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_RX_POWER @@ -36,6 +42,11 @@ def setUpClass(cls): cls.XCVR_SUB_ID = get_transceiver_sub_id(cls.IFINDEX) cls.XCVR_SUB_ID_ASIC1 = get_transceiver_sub_id(cls.IFINDEX_ASIC1) cls.XCVR_CHANNELS = (1, 2, 3, 4) + cls.PSU_POSITION = 2 + cls.FAN_DRAWER_POSITION = 1 + cls.FAN_POSITION = 1 + cls.PSU_FAN_POSITION = 1 + cls.THERMAL_POSITION = 1 # Update MIBs for updater in cls.lut.updater_instances: @@ -48,7 +59,7 @@ def generate_oids_for_physical_sensor_mib(sub_id): return [ObjectIdentifier(12, 0, 0, 0, (1, 3, 6, 1, 2, 1, 99, 1, 1, 1, i, sub_id)) for i in range(1, 5)] - def _test_getpdu_xcvr_sensor(self, sub_id, expected_values): + def _test_getpdu_sensor(self, sub_id, expected_values): """ Test case for correctness of transceiver sensor MIB values :param sub_id: sub OID of the sensor @@ -75,7 +86,7 @@ def _test_getpdu_xcvr_sensor(self, sub_id, expected_values): def test_getpdu_xcvr_temperature_sensor(self): """ - Test case for correct transceiver temperature sensor MIB values + Test case for correctness of transceiver temperature sensor MIB values """ expected_values = [ rfc3433.EntitySensorDataType.CELSIUS, @@ -85,12 +96,12 @@ def test_getpdu_xcvr_temperature_sensor(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_TEMP)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_TEMP)[0], expected_values) def test_getpdu_xcvr_temperature_sensor_asic1(self): """ - Test case for correct transceiver temperature sensor MIB values + Test case for correctness of transceiver temperature sensor MIB values """ print(rfc3433.PhysicalSensorTableMIB.updater.sub_ids) expected_values = [ @@ -101,11 +112,11 @@ def test_getpdu_xcvr_temperature_sensor_asic1(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX_ASIC1, SENSOR_TYPE_TEMP)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX_ASIC1, SENSOR_TYPE_TEMP)[0], expected_values) def test_getpdu_xcvr_voltage_sensor(self): """ - Test case for correct transceiver voltage sensor MIB values + Test case for correctness of transceiver voltage sensor MIB values """ expected_values = [ @@ -116,12 +127,12 @@ def test_getpdu_xcvr_voltage_sensor(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_VOLTAGE)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_VOLTAGE)[0], expected_values) def test_getpdu_xcvr_voltage_sensor_asic1(self): """ - Test case for correct transceiver voltage sensor MIB values + Test case for correctness of transceiver voltage sensor MIB values """ expected_values = [ @@ -132,11 +143,11 @@ def test_getpdu_xcvr_voltage_sensor_asic1(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX_ASIC1, SENSOR_TYPE_VOLTAGE)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX_ASIC1, SENSOR_TYPE_VOLTAGE)[0], expected_values) def test_getpdu_xcvr_rx_power_sensor_minus_infinity(self): """ - Test case for correct transceiver rx power sensor MIB values + Test case for correctness of transceiver rx power sensor MIB values in case when rx power == -inf """ @@ -148,11 +159,11 @@ def test_getpdu_xcvr_rx_power_sensor_minus_infinity(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_RX_POWER)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_RX_POWER)[0], expected_values) def test_getpdu_xcvr_rx_power_sensor(self): """ - Test case for correct transceiver rx power sensor MIB values + Test case for correctness of transceiver rx power sensor MIB values """ expected_values = [ @@ -165,11 +176,11 @@ def test_getpdu_xcvr_rx_power_sensor(self): # test for each channel except first, we already test above for channel in (2, 3, 4): - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_RX_POWER + channel)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_RX_POWER + channel)[0], expected_values) def test_getpdu_xcvr_tx_power_sensor(self): """ - Test case for correct transceiver rx power sensor MIB values + Test case for correctness of transceiver rx power sensor MIB values """ expected_values = [ @@ -182,11 +193,11 @@ def test_getpdu_xcvr_tx_power_sensor(self): # test for each channel except first, we already test above for channel in (1, 2, 3, 4): - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_POWER + channel)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_POWER + channel)[0], expected_values) def test_getpdu_xcvr_tx_bias_sensor_unknown(self): """ - Test case for correct transceiver tx bias sensor MIB values, when + Test case for correctness of transceiver tx bias sensor MIB values, when tx bias sensor is set to "UNKNOWN" in state DB """ @@ -198,11 +209,11 @@ def test_getpdu_xcvr_tx_bias_sensor_unknown(self): rfc3433.EntitySensorStatus.UNAVAILABLE ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) def test_getpdu_xcvr_tx_bias_sensor_overflow(self): """ - Test case for correct transceiver tx bias sensor MIB values + Test case for correctness of transceiver tx bias sensor MIB values when tx bias is grater than 1E9 """ @@ -214,11 +225,11 @@ def test_getpdu_xcvr_tx_bias_sensor_overflow(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 3 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 3 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) def test_getpdu_xcvr_tx_bias_sensor(self): """ - Test case for correct transceiver tx bias sensor MIB values + Test case for correctness of transceiver tx bias sensor MIB values """ expected_values = [ @@ -231,5 +242,119 @@ def test_getpdu_xcvr_tx_bias_sensor(self): # test for each channel for channel in (2, 4): - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_BIAS + channel)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_BIAS + channel)[0], expected_values) + def test_getpdu_psu_temp_sensor(self): + """ + Test case for correctness of psu temperature sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.CELSIUS, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 31100, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "temperature")[0], expected_values) + + def test_getpdu_psu_voltage_sensor(self): + """ + Test case for correctness of psu voltage sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.VOLTS_DC, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 13100, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "voltage")[0], expected_values) + + def test_getpdu_psu_power_sensor(self): + """ + Test case for correctness of psu voltage sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.WATTS, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 312600, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "power")[0], expected_values) + + def test_getpdu_psu_current_sensor(self): + """ + Test case for correctness of psu current sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.AMPERES, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 13400, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "current")[0], expected_values) + + def test_getpdu_chassis_fan_speed_sensor(self): + """ + Test case for correctness of fan speed sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.UNKNOWN, + rfc3433.EntitySensorDataScale.UNITS, + 0, # precision + 50, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + fan_parent_sub_id = get_fan_drawer_sub_id(self.FAN_DRAWER_POSITION) + fan_sub_id = get_fan_sub_id(fan_parent_sub_id, self.FAN_POSITION) + + self._test_getpdu_sensor(get_fan_tachometers_sub_id(fan_sub_id)[0], expected_values) + + def test_getpdu_psu_fan_speed_sensor(self): + """ + Test case for correctness of psu fan speed sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.UNKNOWN, + rfc3433.EntitySensorDataScale.UNITS, + 0, # precision + 57, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + fan_sub_id = get_fan_sub_id(psu_sub_id, self.PSU_FAN_POSITION) + + self._test_getpdu_sensor(get_fan_tachometers_sub_id(fan_sub_id)[0], expected_values) + + def test_getpdu_chassis_temp_sensor(self): + """ + Test case for correctness of chassis temp sensors MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.CELSIUS, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 20500, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + self._test_getpdu_sensor(get_chassis_thermal_sub_id(self.THERMAL_POSITION)[0], expected_values) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 78e7f75f2..1dc0f0bb6 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -16,6 +16,12 @@ from ax_interface.encodings import ObjectIdentifier from ax_interface.constants import PduTypes from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_transceiver_sub_id, get_transceiver_sensor_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_psu_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_psu_sensor_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_fan_drawer_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_fan_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_fan_tachometers_sub_id +from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import get_chassis_thermal_sub_id from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import SENSOR_TYPE_TEMP from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import SENSOR_TYPE_VOLTAGE from sonic_ax_impl.mibs.ietf.physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_RX_POWER @@ -31,6 +37,11 @@ def setUpClass(cls): cls.IFINDEX = 1 cls.XCVR_SUB_ID = get_transceiver_sub_id(cls.IFINDEX) cls.XCVR_CHANNELS = (1, 2, 3, 4) + cls.PSU_POSITION = 2 + cls.FAN_DRAWER_POSITION = 1 + cls.FAN_POSITION = 1 + cls.PSU_FAN_POSITION = 1 + cls.THERMAL_POSITION = 1 # Update MIBs for updater in cls.lut.updater_instances: @@ -43,7 +54,7 @@ def generate_oids_for_physical_sensor_mib(sub_id): return [ObjectIdentifier(12, 0, 0, 0, (1, 3, 6, 1, 2, 1, 99, 1, 1, 1, i, sub_id)) for i in range(1, 5)] - def _test_getpdu_xcvr_sensor(self, sub_id, expected_values): + def _test_getpdu_sensor(self, sub_id, expected_values): """ Test case for correctness of transceiver sensor MIB values :param sub_id: sub OID of the sensor @@ -67,10 +78,9 @@ def _test_getpdu_xcvr_sensor(self, sub_id, expected_values): self.assertEqual(value.type_, ValueType.INTEGER) self.assertEqual(value.data, expected_values[index]) - def test_getpdu_xcvr_temperature_sensor(self): """ - Test case for correct transceiver temperature sensor MIB values + Test case for correctness of transceiver temperature sensor MIB values """ expected_values = [ @@ -81,11 +91,11 @@ def test_getpdu_xcvr_temperature_sensor(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_TEMP)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_TEMP)[0], expected_values) def test_getpdu_xcvr_voltage_sensor(self): """ - Test case for correct transceiver voltage sensor MIB values + Test case for correctness of transceiver voltage sensor MIB values """ expected_values = [ @@ -96,11 +106,11 @@ def test_getpdu_xcvr_voltage_sensor(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_VOLTAGE)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_VOLTAGE)[0], expected_values) def test_getpdu_xcvr_rx_power_sensor_minus_infinity(self): """ - Test case for correct transceiver rx power sensor MIB values + Test case for correctness of transceiver rx power sensor MIB values in case when rx power == -inf """ @@ -112,11 +122,11 @@ def test_getpdu_xcvr_rx_power_sensor_minus_infinity(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_RX_POWER)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_RX_POWER)[0], expected_values) def test_getpdu_xcvr_rx_power_sensor(self): """ - Test case for correct transceiver rx power sensor MIB values + Test case for correctness of transceiver rx power sensor MIB values """ expected_values = [ @@ -129,11 +139,11 @@ def test_getpdu_xcvr_rx_power_sensor(self): # test for each channel except first, we already test above for channel in (2, 3, 4): - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_RX_POWER + channel)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_RX_POWER + channel)[0], expected_values) def test_getpdu_xcvr_tx_power_sensor(self): """ - Test case for correct transceiver rx power sensor MIB values + Test case for correctness of transceiver rx power sensor MIB values """ expected_values = [ @@ -146,11 +156,11 @@ def test_getpdu_xcvr_tx_power_sensor(self): # test for each channel except first, we already test above for channel in (1, 2, 3, 4): - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_POWER + channel)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_POWER + channel)[0], expected_values) def test_getpdu_xcvr_tx_bias_sensor_unknown(self): """ - Test case for correct transceiver tx bias sensor MIB values, when + Test case for correctness of transceiver tx bias sensor MIB values, when tx bias sensor is set to "UNKNOWN" in state DB """ @@ -162,11 +172,11 @@ def test_getpdu_xcvr_tx_bias_sensor_unknown(self): rfc3433.EntitySensorStatus.UNAVAILABLE ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 1 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) def test_getpdu_xcvr_tx_bias_sensor_overflow(self): """ - Test case for correct transceiver tx bias sensor MIB values + Test case for correctness of transceiver tx bias sensor MIB values when tx bias is grater than 1E9 """ @@ -178,11 +188,11 @@ def test_getpdu_xcvr_tx_bias_sensor_overflow(self): rfc3433.EntitySensorStatus.OK ] - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 3 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, 3 + SENSOR_TYPE_PORT_TX_BIAS)[0], expected_values) def test_getpdu_xcvr_tx_bias_sensor(self): """ - Test case for correct transceiver tx bias sensor MIB values + Test case for correctness of transceiver tx bias sensor MIB values """ expected_values = [ @@ -195,4 +205,119 @@ def test_getpdu_xcvr_tx_bias_sensor(self): # test for each channel for channel in (2, 4): - self._test_getpdu_xcvr_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_BIAS + channel)[0], expected_values) + self._test_getpdu_sensor(get_transceiver_sensor_sub_id(self.IFINDEX, SENSOR_TYPE_PORT_TX_BIAS + channel)[0], expected_values) + + def test_getpdu_psu_temp_sensor(self): + """ + Test case for correctness of psu temperature sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.CELSIUS, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 31100, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "temperature")[0], expected_values) + + def test_getpdu_psu_voltage_sensor(self): + """ + Test case for correctness of psu voltage sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.VOLTS_DC, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 13100, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "voltage")[0], expected_values) + + def test_getpdu_psu_power_sensor(self): + """ + Test case for correctness of psu voltage sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.WATTS, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 312600, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "power")[0], expected_values) + + def test_getpdu_psu_current_sensor(self): + """ + Test case for correctness of psu current sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.AMPERES, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 13400, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + self._test_getpdu_sensor(get_psu_sensor_sub_id(psu_sub_id, "current")[0], expected_values) + + def test_getpdu_chassis_fan_speed_sensor(self): + """ + Test case for correctness of fan speed sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.UNKNOWN, + rfc3433.EntitySensorDataScale.UNITS, + 0, # precision + 50, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + fan_parent_sub_id = get_fan_drawer_sub_id(self.FAN_DRAWER_POSITION) + fan_sub_id = get_fan_sub_id(fan_parent_sub_id, self.FAN_POSITION) + + self._test_getpdu_sensor(get_fan_tachometers_sub_id(fan_sub_id)[0], expected_values) + + def test_getpdu_psu_fan_speed_sensor(self): + """ + Test case for correctness of psu fan speed sensor MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.UNKNOWN, + rfc3433.EntitySensorDataScale.UNITS, + 0, # precision + 57, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + psu_sub_id = get_psu_sub_id(self.PSU_POSITION) + fan_sub_id = get_fan_sub_id(psu_sub_id, self.PSU_FAN_POSITION) + + self._test_getpdu_sensor(get_fan_tachometers_sub_id(fan_sub_id)[0], expected_values) + + def test_getpdu_chassis_temp_sensor(self): + """ + Test case for correctness of chassis temp sensors MIB values + """ + + expected_values = [ + rfc3433.EntitySensorDataType.CELSIUS, + rfc3433.EntitySensorDataScale.UNITS, + 3, # precision + 20500, # expected sensor value + rfc3433.EntitySensorStatus.OK + ] + + self._test_getpdu_sensor(get_chassis_thermal_sub_id(self.THERMAL_POSITION)[0], expected_values)