Skip to content

Commit

Permalink
Added logic to get PSU info directly from the state DB (sonic-net#101)
Browse files Browse the repository at this point in the history
* Added logic to get PSU info directly from the state DB.

Signed-off-by: Nazarii Hnydyn <nazariig@mellanox.com>

* Fixed PSU UTs.

Signed-off-by: Nazarii Hnydyn <nazariig@mellanox.com>

* Aligned method names according to PEP8.

Signed-off-by: Nazarii Hnydyn <nazariig@mellanox.com>
  • Loading branch information
nazariig authored and Stepan Blyschak committed Apr 12, 2019
1 parent 26cc8a0 commit 14fe916
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 139 deletions.
16 changes: 16 additions & 0 deletions src/sonic_ax_impl/mibs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@

redis_kwargs = {'unix_socket_path': '/var/run/redis/redis.sock'}

def chassis_info_table(chassis_name):
"""
:param: chassis_name: chassis name
:return: chassis info entry for this chassis
"""

return "CHASSIS_INFO" + TABLE_NAME_SEPARATOR_VBAR + chassis_name

def psu_info_table(psu_name):
"""
:param: psu_name: psu name
:return: psu info entry for this psu
"""

return "PSU_INFO" + TABLE_NAME_SEPARATOR_VBAR + psu_name

def counter_table(sai_id):
"""
:param if_name: given sai_id to cast.
Expand Down
132 changes: 90 additions & 42 deletions src/sonic_ax_impl/mibs/vendor/cisco/ciscoEntityFruControlMIB.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,48 @@
import imp
import re
import sys

from enum import Enum, unique
from sonic_ax_impl import mibs
from ax_interface import MIBMeta, ValueType, MIBUpdater, MIBEntry, SubtreeMIBEntry
from ax_interface.encodings import ObjectIdentifier
from ax_interface import MIBMeta, ValueType, SubtreeMIBEntry
from swsssdk import SonicV2Connector

CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}'
PSU_INFO_KEY_TEMPLATE = 'PSU {}'

PSU_PRESENCE_OK = 'true'
PSU_STATUS_OK = 'true'

@unique
class CHASSISInfoDB(bytes, Enum):
"""
CHASSIS info keys
"""

PSU_NUM = b"psu_num"

@unique
class PSUInfoDB(bytes, Enum):
"""
PSU info keys
"""

PRESENCE = b"presence"
STATUS = b"status"

def get_chassis_data(chassis_info):
"""
:param chassis_info: chassis info dict
:return: tuple (psu_num) of chassis;
Empty string if field not in chassis_info
"""

return tuple(chassis_info.get(chassis_field.value, b"").decode() for chassis_field in CHASSISInfoDB)

def get_psu_data(psu_info):
"""
:param psu_info: psu info dict
:return: tuple (presence, status) of psu;
Empty string if field not in psu_info
"""

PSU_PLUGIN_MODULE_NAME = 'psuutil'
PSU_PLUGIN_MODULE_PATH = "/usr/share/sonic/platform/plugins/{}.py".format(PSU_PLUGIN_MODULE_NAME)
PSU_PLUGIN_CLASS_NAME = 'PsuUtil'
return tuple(psu_info.get(psu_field.value, b"").decode() for psu_field in PSUInfoDB)

class PowerStatusHandler:
"""
Expand All @@ -18,38 +52,57 @@ def __init__(self):
"""
init the handler
"""
self.psuutil = None
self.statedb = SonicV2Connector()
self.statedb.connect(self.statedb.STATE_DB)

try:
module = imp.load_source(PSU_PLUGIN_MODULE_NAME, PSU_PLUGIN_MODULE_PATH)
except ImportError as e:
mibs.logger.error("Failed to load PSU module '%s': %s" % (PSU_PLUGIN_MODULE_NAME, str(e)), True)
return
except FileNotFoundError as e:
mibs.logger.error("Failed to get platform specific PSU module '%s': %s" % (PSU_PLUGIN_MODULE_NAME, str(e)), True)
return
def _get_num_psus(self):
"""
Get PSU number
:return: the number of supported PSU
"""
chassis_name = CHASSIS_INFO_KEY_TEMPLATE.format(1)
chassis_info = self.statedb.get_all(self.statedb.STATE_DB, mibs.chassis_info_table(chassis_name))
num_psus = get_chassis_data(chassis_info)

try:
platform_psuutil_class = getattr(module, PSU_PLUGIN_CLASS_NAME)
self.psuutil = platform_psuutil_class()
except AttributeError as e:
mibs.logger.error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True)
return int(num_psus[0])

def _get_psu_presence(self, psu_index):
"""
Get PSU presence
:return: the presence of particular PSU
"""
psu_name = PSU_INFO_KEY_TEMPLATE.format(psu_index)
psu_info = self.statedb.get_all(self.statedb.STATE_DB, mibs.psu_info_table(psu_name))
presence, status = get_psu_data(psu_info)

return presence == PSU_PRESENCE_OK

def _getPsuIndex(self, sub_id):
def _get_psu_status(self, psu_index):
"""
Get PSU status
:return: the status of particular PSU
"""
psu_name = PSU_INFO_KEY_TEMPLATE.format(psu_index)
psu_info = self.statedb.get_all(self.statedb.STATE_DB, mibs.psu_info_table(psu_name))
presence, status = get_psu_data(psu_info)

return status == PSU_STATUS_OK

def _get_psu_index(self, sub_id):
"""
Get the PSU index from sub_id
:return: the index of supported PSU
"""
if not self.psuutil or not sub_id or len(sub_id) > 1:
if not sub_id or len(sub_id) > 1:
return None

psu_index = int(sub_id[0])

try:
num_psus = self.psuutil.get_num_psus()
num_psus = self._get_num_psus()
except Exception:
# Any unexpected exception or error, log it and keep running
mibs.logger.exception("PowerStatusHandler._getPsuIndex() caught an unexpected exception during get_num_psus()")
mibs.logger.exception("PowerStatusHandler._get_psu_index() caught an unexpected exception during _get_num_psus()")
return None

if psu_index < 1 or psu_index > num_psus:
Expand All @@ -62,27 +115,23 @@ def get_next(self, sub_id):
:param sub_id: The 1-based snmp sub-identifier query.
:return: the next sub id.
"""
if not self.psuutil:
return None

if not sub_id:
return (1,)

psu_index = self._getPsuIndex(sub_id)
psu_index = self._get_psu_index(sub_id)
try:
num_psus = self.psuutil.get_num_psus()
num_psus = self._get_num_psus()
except Exception:
# Any unexpected exception or error, log it and keep running
mibs.logger.exception("PowerStatusHandler.get_next() caught an unexpected exception during get_num_psus()")
mibs.logger.exception("PowerStatusHandler.get_next() caught an unexpected exception during _get_num_psus()")
return None


if psu_index and psu_index + 1 <= num_psus:
return (psu_index + 1,)

return None

def getPsuStatus(self, sub_id):
def get_psu_status(self, sub_id):
"""
:param sub_id: The 1-based sub-identifier query.
:return: the status of requested PSU according to cefcModuleOperStatus ModuleOperType
Expand All @@ -91,24 +140,24 @@ def getPsuStatus(self, sub_id):
8 - the module is provisioned, but it is missing. This is a failure state.
:ref: https://www.cisco.com/c/en/us/td/docs/switches/wan/mgx/mgx_8850/software/mgx_r2-0-10/pxm/reference/guide/pxm/cscoent.html
"""
psu_index = self._getPsuIndex(sub_id)
psu_index = self._get_psu_index(sub_id)

if not psu_index:
return None

try:
psu_presence = self.psuutil.get_psu_presence(psu_index)
psu_presence = self._get_psu_presence(psu_index)
except Exception:
# Any unexpected exception or error, log it and keep running
mibs.logger.exception("PowerStatusHandler.getPsuStatus() caught an unexpected exception during get_psu_presence()")
mibs.logger.exception("PowerStatusHandler.get_psu_status() caught an unexpected exception during _get_psu_presence()")
return None

if psu_presence:
try:
psu_status = self.psuutil.get_psu_status(psu_index)
psu_status = self._get_psu_status(psu_index)
except Exception:
# Any unexpected exception or error, log it and keep running
mibs.logger.exception("PowerStatusHandler.getPsuStatus() caught an unexpected exception during get_psu_status()")
mibs.logger.exception("PowerStatusHandler.get_psu_status() caught an unexpected exception during _get_psu_status()")
return None

if psu_status:
Expand All @@ -118,7 +167,6 @@ def getPsuStatus(self, sub_id):
else:
return 8


class cefcFruPowerStatusTable(metaclass=MIBMeta, prefix='.1.3.6.1.4.1.9.9.117.1.1.2'):
"""
'cefcFruPowerStatusTable' http://oidref.com/1.3.6.1.4.1.9.9.117.1.1.2
Expand All @@ -129,4 +177,4 @@ class cefcFruPowerStatusTable(metaclass=MIBMeta, prefix='.1.3.6.1.4.1.9.9.117.1.
# cefcFruPowerStatusTable = '1.3.6.1.4.1.9.9.117.1.1.2'
# csqIfQosGroupStatsEntry = '1.3.6.1.4.1.9.9.117.1.1.2.1'

psu_status = SubtreeMIBEntry('1.2', power_status_handler, ValueType.INTEGER, power_status_handler.getPsuStatus)
psu_status = SubtreeMIBEntry('1.2', power_status_handler, ValueType.INTEGER, power_status_handler.get_psu_status)
1 change: 0 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
import tests.mock_tables.imp
15 changes: 0 additions & 15 deletions tests/mock_tables/imp.py

This file was deleted.

53 changes: 34 additions & 19 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,43 @@
"DEVICE_METADATA|localhost": {
"chassis_serial_number": "SAMPLETESTSN"
},
"PSU_INFO|PSU 1": {
"presence": "false",
"status": "false"
},
"PSU_INFO|PSU 2": {
"presence": "true",
"status": "true"
},
"PSU_INFO|PSU 3": {
"presence": "true",
"status": "false"
},
"CHASSIS_INFO|chassis 1": {
"psu_num": "3"
},
"TRANSCEIVER_INFO|Ethernet0": {
"type": "QSFP+",
"hardwarerev" : "A1",
"serialnum": "SERIAL_NUM",
"manufacturename": "VENDOR_NAME",
"modelname": "MODEL_NAME"
"type": "QSFP+",
"hardwarerev" : "A1",
"serialnum": "SERIAL_NUM",
"manufacturename": "VENDOR_NAME",
"modelname": "MODEL_NAME"
},
"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
"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"
Expand Down
62 changes: 0 additions & 62 deletions tests/plugins/psuutil.py

This file was deleted.

0 comments on commit 14fe916

Please sign in to comment.