Skip to content

Commit 67050bb

Browse files
andrii savkaqiluo-msft
andrii savka
authored andcommitted
Added PSU support
Author: andrii savka <[email protected]>
1 parent 12bc0e0 commit 67050bb

File tree

7 files changed

+280
-1
lines changed

7 files changed

+280
-1
lines changed

src/sonic_ax_impl/main.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from sonic_ax_impl.mibs import ieee802_1ab
1313
from . import logger
1414
from .mibs.ietf import rfc1213, rfc2737, rfc2863, rfc4292, rfc4363
15-
from .mibs.vendor import dell
15+
from .mibs.vendor import dell, cisco
1616

1717
# Background task update frequency ( in seconds )
1818
DEFAULT_UPDATE_FREQUENCY = 5
@@ -31,6 +31,7 @@ class SonicMIB(
3131
ieee802_1ab.LLDPLocPortTable,
3232
ieee802_1ab.LLDPRemTable,
3333
dell.force10.SSeriesMIB,
34+
cisco.ciscoEntityFruControlMIB.cefcFruPowerStatusTable,
3435
):
3536
"""
3637
If SONiC was to create custom MIBEntries, they may be specified here.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import ciscoEntityFruControlMIB
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import imp
2+
import re
3+
import sys
4+
5+
from sonic_ax_impl import mibs
6+
from ax_interface import MIBMeta, ValueType, MIBUpdater, MIBEntry, SubtreeMIBEntry
7+
from ax_interface.encodings import ObjectIdentifier
8+
9+
PSU_PLUGIN_MODULE_NAME = 'psuutil'
10+
PSU_PLUGIN_MODULE_PATH = "/usr/share/sonic/platform/plugins/{}.py".format(PSU_PLUGIN_MODULE_NAME)
11+
PSU_PLUGIN_CLASS_NAME = 'PsuUtil'
12+
13+
class PowerStatusHandler:
14+
"""
15+
Class to handle the SNMP request
16+
"""
17+
def __init__(self):
18+
"""
19+
init the handler
20+
"""
21+
self.psuutil = None
22+
23+
try:
24+
module = imp.load_source(PSU_PLUGIN_MODULE_NAME, PSU_PLUGIN_MODULE_PATH)
25+
except ImportError as e:
26+
mibs.logger.error("Failed to load PSU module '%s': %s" % (PSU_PLUGIN_MODULE_NAME, str(e)), True)
27+
return
28+
except FileNotFoundError as e:
29+
mibs.logger.error("Failed to get platform specific PSU module '%s': %s" % (PSU_PLUGIN_MODULE_NAME, str(e)), True)
30+
return
31+
32+
try:
33+
platform_psuutil_class = getattr(module, PSU_PLUGIN_CLASS_NAME)
34+
self.psuutil = platform_psuutil_class()
35+
except AttributeError as e:
36+
mibs.logger.error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True)
37+
38+
def _getPsuIndex(self, sub_id):
39+
"""
40+
Get the PSU index from sub_id
41+
:return: the index of supported PSU
42+
"""
43+
if not self.psuutil or not sub_id or len(sub_id) > 1:
44+
return None
45+
46+
psu_index = int(sub_id[0])
47+
48+
if psu_index < 1 or psu_index > self.psuutil.get_num_psus():
49+
return None
50+
51+
return psu_index
52+
53+
def get_next(self, sub_id):
54+
"""
55+
:param sub_id: The 1-based snmp sub-identifier query.
56+
:return: the next sub id.
57+
"""
58+
if not self.psuutil:
59+
return None
60+
61+
if not sub_id:
62+
return (1,)
63+
64+
psu_index = self._getPsuIndex(sub_id)
65+
66+
if psu_index and psu_index + 1 <= self.psuutil.get_num_psus():
67+
return (psu_index + 1,)
68+
69+
return None
70+
71+
def getPsuStatus(self, sub_id):
72+
"""
73+
:param sub_id: The 1-based sub-identifier query.
74+
:return: the status of requested PSU according to cefcModuleOperStatus ModuleOperType
75+
2 - PSU has correct functionalling - ok
76+
7 - PSU has a problem with functionalling - failed
77+
: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
78+
"""
79+
psu_index = self._getPsuIndex(sub_id)
80+
81+
if not psu_index:
82+
return None
83+
84+
psu_status = self.psuutil.get_psu_status(psu_index)
85+
86+
if psu_status:
87+
return 2
88+
89+
return 7
90+
91+
92+
class cefcFruPowerStatusTable(metaclass=MIBMeta, prefix='.1.3.6.1.4.1.9.9.117.1.1.2'):
93+
"""
94+
'cefcFruPowerStatusTable' http://oidref.com/1.3.6.1.4.1.9.9.117.1.1.2
95+
"""
96+
97+
power_status_handler = PowerStatusHandler()
98+
99+
# cefcFruPowerStatusTable = '1.3.6.1.4.1.9.9.117.1.1.2'
100+
# csqIfQosGroupStatsEntry = '1.3.6.1.4.1.9.9.117.1.1.2.1'
101+
102+
psu_status = SubtreeMIBEntry('1.2', power_status_handler, ValueType.INTEGER, power_status_handler.getPsuStatus)

tests/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import tests.mock_tables.imp

tests/mock_tables/imp.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import imp
2+
import os
3+
4+
TEST_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5+
6+
# Backup original function
7+
_load_source = getattr(imp, 'load_source')
8+
9+
# Monkey patch
10+
def load_source(name, pathname):
11+
if name == 'psuutil':
12+
return _load_source(name, TEST_DIR + '/plugins/psuutil.py')
13+
14+
# Replace the function with mocked one
15+
imp.load_source = load_source

tests/plugins/psuutil.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env python
2+
3+
#############################################################################
4+
# Mellanox
5+
#
6+
# Module contains an implementation of SONiC PSU Base API and
7+
# provides the PSUs status for SNMP testing
8+
#
9+
#############################################################################
10+
11+
class PsuUtil():
12+
"""PSUutil class for SNMP testing"""
13+
14+
def __init__(self):
15+
""" For testing purpose only """
16+
self.num_of_psus = 2
17+
self.psu_status = { 1 : True, 2 : False }
18+
19+
def get_num_psus(self):
20+
"""
21+
Retrieves the number of PSUs available on the device
22+
23+
:return: An integer, the number of PSUs available on the device
24+
"""
25+
""" For testing purpose only """
26+
return self.num_of_psus
27+
28+
def get_psu_status(self, index):
29+
"""
30+
Retrieves the oprational status of power supply unit (PSU) defined
31+
by 1-based index <index>
32+
33+
:param index: An integer, 1-based index of the PSU of which to query status
34+
:return: Boolean, True if PSU is operating properly, False if PSU is faulty
35+
"""
36+
""" For testing purpose only """
37+
if not isinstance(index, int):
38+
return False
39+
elif index > 0 and index <= self.num_of_psus:
40+
return self.psu_status[index]
41+
else:
42+
return False
43+
44+
def get_psu_presence(self, index):
45+
"""
46+
Retrieves the presence status of power supply unit (PSU) defined
47+
by 1-based index <index>
48+
49+
:param index: An integer, 1-based index of the PSU of which to query status
50+
:return: Boolean, True if PSU is plugged, False if not
51+
"""
52+
""" For testing purpose only """
53+
if not isinstance(index, int):
54+
return False
55+
elif index > 0 and index <= self.num_of_psus:
56+
return self.psu_status[index]
57+
else:
58+
return False

tests/test_psu.py

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import os
2+
import sys
3+
4+
# noinspection PyUnresolvedReferences
5+
import tests.mock_tables.dbconnector
6+
7+
modules_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
8+
sys.path.insert(0, os.path.join(modules_path, 'src'))
9+
10+
from unittest import TestCase
11+
12+
from ax_interface import ValueType
13+
from ax_interface.pdu_implementations import GetPDU, GetNextPDU
14+
from ax_interface.encodings import ObjectIdentifier
15+
from ax_interface.constants import PduTypes
16+
from ax_interface.pdu import PDU, PDUHeader
17+
from ax_interface.mib import MIBTable
18+
from sonic_ax_impl.mibs.vendor.cisco import ciscoEntityFruControlMIB
19+
20+
class TestPsuStatus(TestCase):
21+
@classmethod
22+
def setUpClass(cls):
23+
cls.lut = MIBTable(ciscoEntityFruControlMIB.cefcFruPowerStatusTable)
24+
25+
def test_getPsu1Status(self):
26+
oid = ObjectIdentifier(2, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 117, 1, 1, 2, 1, 2, 1))
27+
get_pdu = GetPDU(
28+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
29+
oids=[oid]
30+
)
31+
32+
encoded = get_pdu.encode()
33+
response = get_pdu.make_response(self.lut)
34+
35+
value0 = response.values[0]
36+
self.assertEqual(value0.type_, ValueType.INTEGER)
37+
self.assertEqual(str(value0.name), str(oid))
38+
self.assertEqual(value0.data, 2)
39+
40+
def test_getNextPsu1(self):
41+
oid = ObjectIdentifier(2, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 117, 1, 1, 2, 1, 2, 1))
42+
expected_oid = ObjectIdentifier(2, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 117, 1, 1, 2, 1, 2, 2))
43+
get_pdu = GetNextPDU(
44+
header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0),
45+
oids=[oid]
46+
)
47+
48+
encoded = get_pdu.encode()
49+
response = get_pdu.make_response(self.lut)
50+
51+
value0 = response.values[0]
52+
self.assertEqual(value0.type_, ValueType.INTEGER)
53+
self.assertEqual(str(value0.name), str(expected_oid))
54+
self.assertEqual(value0.data, 7)
55+
56+
def test_getPsu2Status(self):
57+
oid = ObjectIdentifier(2, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 117, 1, 1, 2, 1, 2, 2))
58+
get_pdu = GetPDU(
59+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
60+
oids=[oid]
61+
)
62+
63+
encoded = get_pdu.encode()
64+
response = get_pdu.make_response(self.lut)
65+
66+
value0 = response.values[0]
67+
self.assertEqual(value0.type_, ValueType.INTEGER)
68+
self.assertEqual(str(value0.name), str(oid))
69+
self.assertEqual(value0.data, 7)
70+
71+
def test_getNextPsu2(self):
72+
oid = ObjectIdentifier(2, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 117, 1, 1, 2, 1, 2, 2))
73+
expected_oid = None
74+
get_pdu = GetNextPDU(
75+
header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0),
76+
oids=[oid]
77+
)
78+
79+
encoded = get_pdu.encode()
80+
response = get_pdu.make_response(self.lut)
81+
82+
value0 = response.values[0]
83+
self.assertEqual(value0.type_, ValueType.END_OF_MIB_VIEW)
84+
self.assertEqual(str(value0.name), str(oid))
85+
self.assertEqual(value0.data, None)
86+
87+
def test_getMissedPsu(self):
88+
oid = ObjectIdentifier(2, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 117, 1, 1, 2, 1, 2, 5, 1))
89+
expected_oid = None
90+
get_pdu = GetNextPDU(
91+
header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0),
92+
oids=[oid]
93+
)
94+
95+
encoded = get_pdu.encode()
96+
response = get_pdu.make_response(self.lut)
97+
98+
value0 = response.values[0]
99+
self.assertEqual(value0.type_, ValueType.END_OF_MIB_VIEW)
100+
self.assertEqual(str(value0.name), str(oid))
101+
self.assertEqual(value0.data, None)

0 commit comments

Comments
 (0)