Skip to content

Commit 45edd7e

Browse files
[snmpagent] Fix hardcoded qsfp lane count by reading sensor status from DB (sonic-net#184)
**- What I did** The current snmpagent implementation hardcoded qsfp lanes to 4, but qsfp-dd has 8 lanes. In that case snmp query only display the sensors of first 4 lanes. This PR is to fix it. **- How I did it** 1. read actual sensor status from redis db, now we only care temperature, voltage, txpower, rxpower, txbias. 2. sort sensor data to make sure it has a solid order 3. store the data to snmpagent data structure **- How to verify it** Manual test on master and run existing regression
1 parent 3b72a6f commit 45edd7e

File tree

7 files changed

+211
-157
lines changed

7 files changed

+211
-157
lines changed

src/sonic_ax_impl/mibs/ietf/physical_entity_sub_oid_generator.py

+4-22
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,6 @@
8080
CHASSIS_SUB_ID = 1
8181
CHASSIS_MGMT_SUB_ID = MODULE_TYPE_MGMT
8282

83-
# This is used in both rfc2737 and rfc3433
84-
XCVR_SENSOR_PART_ID_MAP = {
85-
"temperature": SENSOR_TYPE_TEMP,
86-
"tx1power": SENSOR_TYPE_PORT_TX_POWER + 1,
87-
"tx2power": SENSOR_TYPE_PORT_TX_POWER + 2,
88-
"tx3power": SENSOR_TYPE_PORT_TX_POWER + 3,
89-
"tx4power": SENSOR_TYPE_PORT_TX_POWER + 4,
90-
"rx1power": SENSOR_TYPE_PORT_RX_POWER + 1,
91-
"rx2power": SENSOR_TYPE_PORT_RX_POWER + 2,
92-
"rx3power": SENSOR_TYPE_PORT_RX_POWER + 3,
93-
"rx4power": SENSOR_TYPE_PORT_RX_POWER + 4,
94-
"tx1bias": SENSOR_TYPE_PORT_TX_BIAS + 1,
95-
"tx2bias": SENSOR_TYPE_PORT_TX_BIAS + 2,
96-
"tx3bias": SENSOR_TYPE_PORT_TX_BIAS + 3,
97-
"tx4bias": SENSOR_TYPE_PORT_TX_BIAS + 4,
98-
"voltage": SENSOR_TYPE_VOLTAGE,
99-
}
100-
10183
PSU_SENSOR_PART_ID_MAP = {
10284
'temperature': SENSOR_TYPE_TEMP,
10385
'power': SENSOR_TYPE_POWER,
@@ -175,14 +157,14 @@ def get_transceiver_sub_id(ifindex):
175157
"""
176158
return (MODULE_TYPE_PORT + ifindex * PORT_IFINDEX_MULTIPLE, )
177159

178-
def get_transceiver_sensor_sub_id(ifindex, sensor):
160+
def get_transceiver_sensor_sub_id(ifindex, offset):
179161
"""
180162
Returns sub OID for transceiver sensor. Sub OID is calculated as folows:
181-
sub OID = transceiver_oid + XCVR_SENSOR_PART_ID_MAP[sensor]
163+
sub OID = transceiver_oid + offset
182164
:param ifindex: interface index
183-
:param sensor: sensor key
165+
:param offset: sensor OID offset
184166
:return: sub OID = {{index}} * 1000 + {{lane}} * 10 + sensor id
185167
"""
186168

187169
transceiver_oid, = get_transceiver_sub_id(ifindex)
188-
return (transceiver_oid + XCVR_SENSOR_PART_ID_MAP[sensor],)
170+
return (transceiver_oid + offset,)

src/sonic_ax_impl/mibs/ietf/rfc2737.py

+14-60
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .physical_entity_sub_oid_generator import get_psu_sensor_sub_id
2222
from .physical_entity_sub_oid_generator import get_transceiver_sub_id
2323
from .physical_entity_sub_oid_generator import get_transceiver_sensor_sub_id
24+
from .transceiver_sensor_data import TransceiverSensorData
2425

2526

2627
@unique
@@ -126,43 +127,7 @@ class ThermalInfoDB(str, Enum):
126127
'voltage' : 4
127128
}
128129

129-
# Map used to generate transceiver sensor description
130-
XCVR_SENSOR_NAME_MAP = {
131-
"temperature" : "Temperature",
132-
"voltage" : "Voltage",
133-
"rx1power" : "RX Power",
134-
"rx2power" : "RX Power",
135-
"rx3power" : "RX Power",
136-
"rx4power" : "RX Power",
137-
"tx1bias" : "TX Bias",
138-
"tx2bias" : "TX Bias",
139-
"tx3bias" : "TX Bias",
140-
"tx4bias" : "TX Bias",
141-
"tx1power" : "TX Power",
142-
"tx2power" : "TX Power",
143-
"tx3power" : "TX Power",
144-
"tx4power" : "TX Power",
145-
}
146-
147-
XCVR_SENSOR_INDEX_MAP = {
148-
"temperature" : 1,
149-
"tx1power" : 2,
150-
"tx2power" : 3,
151-
"tx3power" : 4,
152-
"tx4power" : 5,
153-
"rx1power" : 6,
154-
"rx2power" : 7,
155-
"rx3power" : 8,
156-
"rx4power" : 9,
157-
"tx1bias" : 10,
158-
"tx2bias" : 11,
159-
"tx3bias" : 12,
160-
"tx4bias" : 13,
161-
"voltage" : 14,
162-
}
163-
164130
NOT_AVAILABLE = 'N/A'
165-
QSFP_LANES = (1, 2, 3, 4)
166131

167132
def is_null_str(value):
168133
"""
@@ -195,29 +160,20 @@ def get_transceiver_description(sfp_type, if_alias):
195160

196161
return "{} for {}".format(sfp_type, if_alias)
197162

198-
def get_transceiver_sensor_description(sensor, if_alias):
163+
164+
def get_transceiver_sensor_description(name, lane_number, if_alias):
199165
"""
200-
:param sensor: sensor key name
166+
:param name: sensor name
167+
:param lane_number: lane number of this sensor
201168
:param if_alias: interface alias
202169
:return: description string about sensor
203170
"""
204-
205-
# assume sensors that is per channel in transceiver port
206-
# has digit equals to channel number in the sensor's key name in DB
207-
# e.g. rx3power (lane 3)
208-
lane_number = list(filter(lambda c: c.isdigit(), sensor))
209-
210-
if len(lane_number) == 0:
171+
if lane_number == 0:
211172
port_name = if_alias
212-
elif len(lane_number) == 1 and int(lane_number[0]) in QSFP_LANES:
213-
port_name = "{}/{}".format(if_alias, lane_number[0])
214173
else:
215-
mibs.logger.warning("Tried to parse lane number from sensor name - {} ".format(sensor)
216-
+ "but parsed value is not a valid QSFP lane number")
217-
# continue as with non per channel sensor
218-
port_name = if_alias
174+
port_name = "{}/{}".format(if_alias, lane_number)
219175

220-
return "DOM {} Sensor for {}".format(XCVR_SENSOR_NAME_MAP[sensor], port_name)
176+
return "DOM {} Sensor for {}".format(name, port_name)
221177

222178

223179
class Callback(object):
@@ -845,19 +801,17 @@ def _update_transceiver_sensor_cache(self, interface, sub_id):
845801
if not transceiver_dom_entry:
846802
return
847803

848-
# go over transceiver sensors
849-
for sensor in transceiver_dom_entry:
850-
if sensor not in XCVR_SENSOR_NAME_MAP:
851-
continue
852-
sensor_sub_id = get_transceiver_sensor_sub_id(ifindex, sensor)
804+
sensor_data_list = TransceiverSensorData.create_sensor_data(transceiver_dom_entry)
805+
sensor_data_list = TransceiverSensorData.sort_sensor_data(sensor_data_list)
806+
for index, sensor_data in enumerate(sensor_data_list):
807+
sensor_sub_id = get_transceiver_sensor_sub_id(ifindex, sensor_data.get_oid_offset())
853808
self._add_entity_related_oid(interface, sensor_sub_id)
854-
sensor_description = get_transceiver_sensor_description(sensor, ifalias)
855-
856809
self.mib_updater.set_phy_class(sensor_sub_id, PhysicalClass.SENSOR)
810+
sensor_description = get_transceiver_sensor_description(sensor_data.get_name(), sensor_data.get_lane_number(), ifalias)
857811
self.mib_updater.set_phy_descr(sensor_sub_id, sensor_description)
858812
self.mib_updater.set_phy_name(sensor_sub_id, sensor_description)
859813
self.mib_updater.set_phy_contained_in(sensor_sub_id, sub_id)
860-
self.mib_updater.set_phy_parent_relative_pos(sensor_sub_id, XCVR_SENSOR_INDEX_MAP[sensor])
814+
self.mib_updater.set_phy_parent_relative_pos(sensor_sub_id, index + 1)
861815
self.mib_updater.set_phy_fru(sensor_sub_id, False)
862816
# add to available OIDs list
863817
self.mib_updater.add_sub_id(sensor_sub_id)

src/sonic_ax_impl/mibs/ietf/rfc3433.py

+14-36
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from sonic_ax_impl.mibs import Namespace
1212

1313
from .physical_entity_sub_oid_generator import get_transceiver_sensor_sub_id
14+
from .transceiver_sensor_data import TransceiverSensorData
15+
1416

1517
@unique
1618
class EntitySensorDataType(int, Enum):
@@ -208,34 +210,13 @@ class XcvrTxPowerSensor(SensorInterface):
208210
CONVERTER = Converters.CONV_dBm_mW
209211

210212

211-
# mapping between DB key and Sensor object
212-
TRANSCEIVER_SENSOR_MAP = {
213-
"temperature": XcvrTempSensor,
214-
"voltage": XcvrVoltageSensor,
215-
"rx1power": XcvrRxPowerSensor,
216-
"rx2power": XcvrRxPowerSensor,
217-
"rx3power": XcvrRxPowerSensor,
218-
"rx4power": XcvrRxPowerSensor,
219-
"tx1bias": XcvrTxBiasSensor,
220-
"tx2bias": XcvrTxBiasSensor,
221-
"tx3bias": XcvrTxBiasSensor,
222-
"tx4bias": XcvrTxBiasSensor,
223-
"tx1power": XcvrTxPowerSensor,
224-
"tx2power": XcvrTxPowerSensor,
225-
"tx3power": XcvrTxPowerSensor,
226-
"tx4power": XcvrTxPowerSensor,
227-
}
228-
229-
230-
def get_transceiver_sensor(sensor_key):
231-
"""
232-
Gets transceiver sensor object
233-
:param sensor_key: Sensor key from XcvrDomDB
234-
:param ifindex: Interface index associated with transceiver
235-
:return: Sensor object.
236-
"""
237-
238-
return TRANSCEIVER_SENSOR_MAP[sensor_key]
213+
TransceiverSensorData.bind_sensor_interface({
214+
'temperature': XcvrTempSensor,
215+
'voltage' : XcvrVoltageSensor,
216+
'rxpower' : XcvrRxPowerSensor,
217+
'txpower' : XcvrTxPowerSensor,
218+
'txbias' : XcvrTxBiasSensor
219+
})
239220

240221

241222
class PhysicalSensorTableMIBUpdater(MIBUpdater):
@@ -313,14 +294,11 @@ def update_data(self):
313294
if not transceiver_dom_entry_data:
314295
continue
315296

316-
for sensor_key in transceiver_dom_entry_data:
317-
if sensor_key not in TRANSCEIVER_SENSOR_MAP:
318-
continue
319-
320-
raw_sensor_value = transceiver_dom_entry_data.get(sensor_key)
321-
322-
sensor = get_transceiver_sensor(sensor_key)
323-
sub_id = get_transceiver_sensor_sub_id(ifindex, sensor_key)
297+
sensor_data_list = TransceiverSensorData.create_sensor_data(transceiver_dom_entry_data)
298+
for sensor_data in sensor_data_list:
299+
raw_sensor_value = sensor_data.get_raw_value()
300+
sensor = sensor_data.get_sensor_interface()
301+
sub_id = get_transceiver_sensor_sub_id(ifindex, sensor_data.get_oid_offset())
324302

325303
try:
326304
mib_values = sensor.mib_values(raw_sensor_value)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import re
2+
3+
from .physical_entity_sub_oid_generator import SENSOR_TYPE_TEMP
4+
from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_TX_POWER
5+
from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_RX_POWER
6+
from .physical_entity_sub_oid_generator import SENSOR_TYPE_PORT_TX_BIAS
7+
from .physical_entity_sub_oid_generator import SENSOR_TYPE_VOLTAGE
8+
9+
10+
class TransceiverSensorData:
11+
"""
12+
Base transceiver sensor data class. Responsible for:
13+
1. Manage concrete sensor data class
14+
2. Create concrete sensor data instances
15+
3. Provide common logic for concrete sensor data class
16+
"""
17+
18+
sensor_attr_dict = {
19+
'temperature': {
20+
'pattern': 'temperature',
21+
'name': 'Temperature',
22+
'oid_offset_base': SENSOR_TYPE_TEMP,
23+
'sort_factor': 0,
24+
'lane_based_sensor': False
25+
},
26+
'voltage': {
27+
'pattern': 'voltage',
28+
'name': 'Voltage',
29+
'oid_offset_base': SENSOR_TYPE_VOLTAGE,
30+
'sort_factor': 9000,
31+
'lane_based_sensor': False
32+
},
33+
'rxpower': {
34+
'pattern': r'rx(\d+)power',
35+
'name': 'RX Power',
36+
'oid_offset_base': SENSOR_TYPE_PORT_RX_POWER,
37+
'sort_factor': 2000,
38+
'lane_based_sensor': True
39+
},
40+
'txpower': {
41+
'pattern': r'tx(\d+)power',
42+
'name': 'TX Power',
43+
'oid_offset_base': SENSOR_TYPE_PORT_TX_POWER,
44+
'sort_factor': 1000,
45+
'lane_based_sensor': True
46+
},
47+
'txbias': {
48+
'pattern': r'tx(\d+)bias',
49+
'name': 'TX Bias',
50+
'oid_offset_base': SENSOR_TYPE_PORT_TX_BIAS,
51+
'sort_factor': 3000,
52+
'lane_based_sensor': True
53+
}
54+
}
55+
56+
def __init__(self, key, value, sensor_attrs, match_result):
57+
self._key = key
58+
self._value = value
59+
self._sensor_attrs = sensor_attrs
60+
self._match_result = match_result
61+
62+
@classmethod
63+
def create_sensor_data(cls, sensor_data_dict):
64+
"""
65+
Create sensor data instances according to the sensor data got from redis
66+
:param sensor_data_dict: sensor data got from redis
67+
:return: A sorted sensor data instance list
68+
"""
69+
sensor_data_list = []
70+
for name, value in sensor_data_dict.items():
71+
for sensor_attrs in cls.sensor_attr_dict.values():
72+
match_result = re.match(sensor_attrs['pattern'], name)
73+
if match_result:
74+
sensor_data = TransceiverSensorData(name, value, sensor_attrs, match_result)
75+
sensor_data_list.append(sensor_data)
76+
77+
return sensor_data_list
78+
79+
@classmethod
80+
def sort_sensor_data(cls, sensor_data_list):
81+
return sorted(sensor_data_list, key=lambda x: x.get_sort_factor())
82+
83+
@classmethod
84+
def bind_sensor_interface(cls, sensor_interface_dict):
85+
for name, sensor_attrs in cls.sensor_attr_dict.items():
86+
if name in sensor_interface_dict:
87+
sensor_attrs['sensor_interface'] = sensor_interface_dict[name]
88+
89+
def get_key(self):
90+
"""
91+
Get the redis key of this sensor
92+
"""
93+
return self._key
94+
95+
def get_raw_value(self):
96+
"""
97+
Get raw redis value of this sensor
98+
"""
99+
return self._value
100+
101+
def get_name(self):
102+
"""
103+
Get the name of this sensor. Concrete sensor data class must override
104+
this.
105+
"""
106+
return self._sensor_attrs['name']
107+
108+
def get_sort_factor(self):
109+
"""
110+
Get sort factor for this sensor. Concrete sensor data class must override
111+
this.
112+
"""
113+
return self._sensor_attrs['sort_factor'] + self.get_lane_number()
114+
115+
def get_lane_number(self):
116+
"""
117+
Get lane number of this sensor. For example, some transceivers have more than one rx power sensor, the sub index
118+
of rx1power is 1, the sub index of rx2power is 2.
119+
"""
120+
return int(self._match_result.group(1)) if self._sensor_attrs['lane_based_sensor'] else 0
121+
122+
def get_oid_offset(self):
123+
"""
124+
Get OID offset of this sensor.
125+
"""
126+
return self._sensor_attrs['oid_offset_base'] + self.get_lane_number()
127+
128+
def get_sensor_interface(self):
129+
"""
130+
Get sensor interface of this sensor. Used by rfc3433.
131+
"""
132+
return self._sensor_attrs['sensor_interface']

0 commit comments

Comments
 (0)