Skip to content

Commit 083ebcc

Browse files
authored
Add transceiver-info items advertised for cmis-supported moddules (#2135)
* include more transceiver-info items advertised for cmis-supported modules * resolving test failure * resolving test failure * include a test case covering cmis-compliant module * resolving test case failure * add units for support laser freq and tx power * add units on laser freq and tx power * correct freq unit * correct a syntax error on key categorization Co-authored-by: Chuan Qin (QINCHUAN) <[email protected]>
1 parent 0811214 commit 083ebcc

File tree

2 files changed

+231
-64
lines changed

2 files changed

+231
-64
lines changed

sfputil/main.py

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,48 @@
6363
'application_advertisement': 'Application Advertisement'
6464
}
6565

66+
QSFP_DD_DATA_MAP = {
67+
'model': 'Vendor PN',
68+
'vendor_oui': 'Vendor OUI',
69+
'vendor_date': 'Vendor Date Code(YYYY-MM-DD Lot)',
70+
'manufacturer': 'Vendor Name',
71+
'vendor_rev': 'Vendor Rev',
72+
'serial': 'Vendor SN',
73+
'type': 'Identifier',
74+
'ext_identifier': 'Extended Identifier',
75+
'ext_rateselect_compliance': 'Extended RateSelect Compliance',
76+
'cable_length': 'cable_length',
77+
'cable_type': 'Length',
78+
'nominal_bit_rate': 'Nominal Bit Rate(100Mbs)',
79+
'specification_compliance': 'Specification compliance',
80+
'encoding': 'Encoding',
81+
'connector': 'Connector',
82+
'application_advertisement': 'Application Advertisement',
83+
'active_firmware': 'Active Firmware Version',
84+
'inactive_firmware': 'Inactive Firmware Version',
85+
'hardware_rev': 'Hardware Revision',
86+
'media_interface_code': 'Media Interface Code',
87+
'host_electrical_interface': 'Host Electrical Interface',
88+
'host_lane_count': 'Host Lane Count',
89+
'media_lane_count': 'Media Lane Count',
90+
'host_lane_assignment_option': 'Host Lane Assignment Options',
91+
'media_lane_assignment_option': 'Media Lane Assignment Options',
92+
'active_apsel_hostlane1': 'Active App Selection Host Lane 1',
93+
'active_apsel_hostlane2': 'Active App Selection Host Lane 2',
94+
'active_apsel_hostlane3': 'Active App Selection Host Lane 3',
95+
'active_apsel_hostlane4': 'Active App Selection Host Lane 4',
96+
'active_apsel_hostlane5': 'Active App Selection Host Lane 5',
97+
'active_apsel_hostlane6': 'Active App Selection Host Lane 6',
98+
'active_apsel_hostlane7': 'Active App Selection Host Lane 7',
99+
'active_apsel_hostlane8': 'Active App Selection Host Lane 8',
100+
'media_interface_technology': 'Media Interface Technology',
101+
'cmis_rev': 'CMIS Revision',
102+
'supported_max_tx_power': 'Supported Max TX Power',
103+
'supported_min_tx_power': 'Supported Min TX Power',
104+
'supported_max_laser_freq': 'Supported Max Laser Frequency',
105+
'supported_min_laser_freq': 'Supported Min Laser Frequency'
106+
}
107+
66108
SFP_DOM_CHANNEL_MONITOR_MAP = {
67109
'rx1power': 'RXPower',
68110
'tx1bias': 'TXBias',
@@ -273,31 +315,68 @@ def format_dict_value_to_string(sorted_key_table,
273315
def convert_sfp_info_to_output_string(sfp_info_dict):
274316
indent = ' ' * 8
275317
output = ''
276-
277-
sorted_qsfp_data_map_keys = sorted(QSFP_DATA_MAP, key=QSFP_DATA_MAP.get)
278-
for key in sorted_qsfp_data_map_keys:
279-
if key == 'cable_type':
280-
output += '{}{}: {}\n'.format(indent, sfp_info_dict['cable_type'], sfp_info_dict['cable_length'])
281-
elif key == 'cable_length':
282-
pass
283-
elif key == 'specification_compliance':
284-
if sfp_info_dict['type'] == "QSFP-DD Double Density 8X Pluggable Transceiver" or \
285-
sfp_info_dict['type'] == "OSFP 8X Pluggable Transceiver" or \
286-
sfp_info_dict['type'] == "QSFP+ or later with CMIS":
287-
output += '{}{}: {}\n'.format(indent, QSFP_DATA_MAP[key], sfp_info_dict[key])
318+
sfp_type = sfp_info_dict['type']
319+
# CMIS supported module types include QSFP-DD and OSFP
320+
if sfp_type.startswith('QSFP-DD') or sfp_type.startswith('OSFP'):
321+
sorted_qsfp_data_map_keys = sorted(QSFP_DD_DATA_MAP, key=QSFP_DD_DATA_MAP.get)
322+
for key in sorted_qsfp_data_map_keys:
323+
if key == 'cable_type':
324+
output += '{}{}: {}\n'.format(indent, sfp_info_dict['cable_type'], sfp_info_dict['cable_length'])
325+
elif key == 'cable_length':
326+
pass
327+
elif key == 'specification_compliance':
328+
if sfp_info_dict['type'] == "QSFP-DD Double Density 8X Pluggable Transceiver" or \
329+
sfp_info_dict['type'] == "OSFP 8X Pluggable Transceiver" or \
330+
sfp_info_dict['type'] == "QSFP+ or later with CMIS":
331+
output += '{}{}: {}\n'.format(indent, QSFP_DD_DATA_MAP[key], sfp_info_dict[key])
332+
else:
333+
output += '{}{}:\n'.format(indent, QSFP_DD_DATA_MAP['specification_compliance'])
334+
335+
spec_compliance_dict = {}
336+
try:
337+
spec_compliance_dict = ast.literal_eval(sfp_info_dict['specification_compliance'])
338+
sorted_compliance_key_table = natsorted(spec_compliance_dict)
339+
for compliance_key in sorted_compliance_key_table:
340+
output += '{}{}: {}\n'.format((indent * 2), compliance_key, spec_compliance_dict[compliance_key])
341+
except ValueError as e:
342+
output += '{}N/A\n'.format((indent * 2))
343+
elif key == 'application_advertisement':
344+
pass
345+
elif key == 'supported_max_tx_power' or key == 'supported_min_tx_power':
346+
output += '{}{}: {}dBm\n'.format(indent, QSFP_DD_DATA_MAP[key], sfp_info_dict[key])
347+
elif key == 'supported_max_laser_freq' or key == 'supported_min_laser_freq':
348+
output += '{}{}: {}GHz\n'.format(indent, QSFP_DD_DATA_MAP[key], sfp_info_dict[key])
288349
else:
289-
output += '{}{}:\n'.format(indent, QSFP_DATA_MAP['specification_compliance'])
290-
291-
spec_compliance_dict = {}
292350
try:
293-
spec_compliance_dict = ast.literal_eval(sfp_info_dict['specification_compliance'])
294-
sorted_compliance_key_table = natsorted(spec_compliance_dict)
295-
for compliance_key in sorted_compliance_key_table:
296-
output += '{}{}: {}\n'.format((indent * 2), compliance_key, spec_compliance_dict[compliance_key])
297-
except ValueError as e:
298-
output += '{}N/A\n'.format((indent * 2))
299-
else:
300-
output += '{}{}: {}\n'.format(indent, QSFP_DATA_MAP[key], sfp_info_dict[key])
351+
output += '{}{}: {}\n'.format(indent, QSFP_DD_DATA_MAP[key], sfp_info_dict[key])
352+
except (KeyError, ValueError) as e:
353+
output += '{}{}: N/A\n'.format(indent, QSFP_DD_DATA_MAP[key])
354+
355+
else:
356+
sorted_qsfp_data_map_keys = sorted(QSFP_DATA_MAP, key=QSFP_DATA_MAP.get)
357+
for key in sorted_qsfp_data_map_keys:
358+
if key == 'cable_type':
359+
output += '{}{}: {}\n'.format(indent, sfp_info_dict['cable_type'], sfp_info_dict['cable_length'])
360+
elif key == 'cable_length':
361+
pass
362+
elif key == 'specification_compliance':
363+
if sfp_info_dict['type'] == "QSFP-DD Double Density 8X Pluggable Transceiver" or \
364+
sfp_info_dict['type'] == "OSFP 8X Pluggable Transceiver" or \
365+
sfp_info_dict['type'] == "QSFP+ or later with CMIS":
366+
output += '{}{}: {}\n'.format(indent, QSFP_DATA_MAP[key], sfp_info_dict[key])
367+
else:
368+
output += '{}{}:\n'.format(indent, QSFP_DATA_MAP['specification_compliance'])
369+
370+
spec_compliance_dict = {}
371+
try:
372+
spec_compliance_dict = ast.literal_eval(sfp_info_dict['specification_compliance'])
373+
sorted_compliance_key_table = natsorted(spec_compliance_dict)
374+
for compliance_key in sorted_compliance_key_table:
375+
output += '{}{}: {}\n'.format((indent * 2), compliance_key, spec_compliance_dict[compliance_key])
376+
except ValueError as e:
377+
output += '{}N/A\n'.format((indent * 2))
378+
else:
379+
output += '{}{}: {}\n'.format(indent, QSFP_DATA_MAP[key], sfp_info_dict[key])
301380

302381
return output
303382

tests/sfputil_test.py

Lines changed: 129 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -77,47 +77,135 @@ def test_format_dict_value_to_string(self):
7777
sfputil.QSFP_DOM_CHANNEL_MONITOR_MAP,
7878
sfputil.DOM_VALUE_UNIT_MAP)
7979
assert output == expected_output
80-
81-
def test_convert_sfp_info_to_output_string(self):
82-
sfp_info_dict = {
83-
'type': 'QSFP28 or later',
84-
'type_abbrv_name': 'QSFP28',
85-
'manufacturer': 'Mellanox',
86-
'model': 'MCP1600-C003',
87-
'vendor_rev': 'A2',
88-
'serial': 'MT1636VS10561',
89-
'vendor_oui': '00-02-c9',
90-
'vendor_date': '2016-07-18',
91-
'connector': 'No separable connector',
92-
'encoding': '64B66B',
93-
'ext_identifier': 'Power Class 1(1.5W max)',
94-
'ext_rateselect_compliance': 'QSFP+ Rate Select Version 1',
95-
'cable_type': 'Length Cable Assembly(m)',
96-
'cable_length': '3',
97-
'application_advertisement': 'N/A',
98-
'specification_compliance': "{'10/40G Ethernet Compliance Code': '40GBASE-CR4'}",
99-
'dom_capability': "{'Tx_power_support': 'no', 'Rx_power_support': 'no', 'Voltage_support': 'no', 'Temp_support': 'no'}",
100-
'nominal_bit_rate': '255'
101-
}
102-
103-
expected_output = '''\
104-
Application Advertisement: N/A
105-
Connector: No separable connector
106-
Encoding: 64B66B
107-
Extended Identifier: Power Class 1(1.5W max)
108-
Extended RateSelect Compliance: QSFP+ Rate Select Version 1
109-
Identifier: QSFP28 or later
110-
Length Cable Assembly(m): 3
111-
Nominal Bit Rate(100Mbs): 255
112-
Specification compliance:
113-
10/40G Ethernet Compliance Code: 40GBASE-CR4
114-
Vendor Date Code(YYYY-MM-DD Lot): 2016-07-18
115-
Vendor Name: Mellanox
116-
Vendor OUI: 00-02-c9
117-
Vendor PN: MCP1600-C003
118-
Vendor Rev: A2
119-
Vendor SN: MT1636VS10561
120-
'''
80+
@pytest.mark.parametrize("sfp_info_dict, expected_output",[
81+
# Non-CMIS module
82+
(
83+
# sfp_info_dict
84+
{
85+
'type': 'QSFP28 or later',
86+
'type_abbrv_name': 'QSFP28',
87+
'manufacturer': 'Mellanox',
88+
'model': 'MCP1600-C003',
89+
'vendor_rev': 'A2',
90+
'serial': 'MT1636VS10561',
91+
'vendor_oui': '00-02-c9',
92+
'vendor_date': '2016-07-18',
93+
'connector': 'No separable connector',
94+
'encoding': '64B66B',
95+
'ext_identifier': 'Power Class 1(1.5W max)',
96+
'ext_rateselect_compliance': 'QSFP+ Rate Select Version 1',
97+
'cable_type': 'Length Cable Assembly(m)',
98+
'cable_length': '3',
99+
'application_advertisement': 'N/A',
100+
'specification_compliance': "{'10/40G Ethernet Compliance Code': '40GBASE-CR4'}",
101+
'dom_capability': "{'Tx_power_support': 'no', 'Rx_power_support': 'no', 'Voltage_support': 'no', 'Temp_support': 'no'}",
102+
'nominal_bit_rate': '255'
103+
},
104+
# expected_output
105+
" Application Advertisement: N/A\n"
106+
" Connector: No separable connector\n"
107+
" Encoding: 64B66B\n"
108+
" Extended Identifier: Power Class 1(1.5W max)\n"
109+
" Extended RateSelect Compliance: QSFP+ Rate Select Version 1\n"
110+
" Identifier: QSFP28 or later\n"
111+
" Length Cable Assembly(m): 3\n"
112+
" Nominal Bit Rate(100Mbs): 255\n"
113+
" Specification compliance:\n"
114+
" 10/40G Ethernet Compliance Code: 40GBASE-CR4\n"
115+
" Vendor Date Code(YYYY-MM-DD Lot): 2016-07-18\n"
116+
" Vendor Name: Mellanox\n"
117+
" Vendor OUI: 00-02-c9\n"
118+
" Vendor PN: MCP1600-C003\n"
119+
" Vendor Rev: A2\n"
120+
" Vendor SN: MT1636VS10561\n"
121+
),
122+
# CMIS compliant module
123+
(
124+
# sfp_info_dict
125+
{
126+
'type': 'QSFP-DD Double Density 8X Pluggable Transceiver',
127+
'type_abbrv_name': 'QSFP-DD',
128+
'manufacturer': 'abc',
129+
'model': 'def',
130+
'vendor_rev': 'ghi',
131+
'serial': 'jkl',
132+
'vendor_oui': '00-00-00',
133+
'vendor_date': '2000-01-01',
134+
'connector': 'LC',
135+
'encoding': 'N/A',
136+
'ext_identifier': 'Power Class 8 (18.0W Max)',
137+
'ext_rateselect_compliance': 'N/A',
138+
'cable_type': 'Length Cable Assembly(m)',
139+
'cable_length': '0',
140+
'application_advertisement': 'N/A',
141+
'specification_compliance': "sm_media_interface",
142+
'dom_capability': "{'Tx_power_support': 'no', 'Rx_power_support': 'no', 'Voltage_support': 'no', 'Temp_support': 'no'}",
143+
'nominal_bit_rate': '0',
144+
'active_firmware': '0.1',
145+
'inactive_firmware': '0.0',
146+
'hardware_rev': '0.0',
147+
'media_interface_code': '400ZR, DWDM, amplified',
148+
'host_electrical_interface': '400GAUI-8 C2M (Annex 120E)',
149+
'host_lane_count': 8,
150+
'media_lane_count': 1,
151+
'host_lane_assignment_option': 1,
152+
'media_lane_assignment_option': 1,
153+
'active_apsel_hostlane1': 1,
154+
'active_apsel_hostlane2': 1,
155+
'active_apsel_hostlane3': 1,
156+
'active_apsel_hostlane4': 1,
157+
'active_apsel_hostlane5': 1,
158+
'active_apsel_hostlane6': 1,
159+
'active_apsel_hostlane7': 1,
160+
'active_apsel_hostlane8': 1,
161+
'media_interface_technology': 'C-band tunable laser',
162+
'cmis_rev': '5.0',
163+
'supported_max_tx_power': 0,
164+
'supported_min_tx_power': -20,
165+
'supported_max_laser_freq': 196100,
166+
'supported_min_laser_freq': 191300
167+
},
168+
# expected_output
169+
" Active App Selection Host Lane 1: 1\n"
170+
" Active App Selection Host Lane 2: 1\n"
171+
" Active App Selection Host Lane 3: 1\n"
172+
" Active App Selection Host Lane 4: 1\n"
173+
" Active App Selection Host Lane 5: 1\n"
174+
" Active App Selection Host Lane 6: 1\n"
175+
" Active App Selection Host Lane 7: 1\n"
176+
" Active App Selection Host Lane 8: 1\n"
177+
" Active Firmware Version: 0.1\n"
178+
" CMIS Revision: 5.0\n"
179+
" Connector: LC\n"
180+
" Encoding: N/A\n"
181+
" Extended Identifier: Power Class 8 (18.0W Max)\n"
182+
" Extended RateSelect Compliance: N/A\n"
183+
" Hardware Revision: 0.0\n"
184+
" Host Electrical Interface: 400GAUI-8 C2M (Annex 120E)\n"
185+
" Host Lane Assignment Options: 1\n"
186+
" Host Lane Count: 8\n"
187+
" Identifier: QSFP-DD Double Density 8X Pluggable Transceiver\n"
188+
" Inactive Firmware Version: 0.0\n"
189+
" Length Cable Assembly(m): 0\n"
190+
" Media Interface Code: 400ZR, DWDM, amplified\n"
191+
" Media Interface Technology: C-band tunable laser\n"
192+
" Media Lane Assignment Options: 1\n"
193+
" Media Lane Count: 1\n"
194+
" Nominal Bit Rate(100Mbs): 0\n"
195+
" Specification compliance: sm_media_interface\n"
196+
" Supported Max Laser Frequency: 196100GHz\n"
197+
" Supported Max TX Power: 0dBm\n"
198+
" Supported Min Laser Frequency: 191300GHz\n"
199+
" Supported Min TX Power: -20dBm\n"
200+
" Vendor Date Code(YYYY-MM-DD Lot): 2000-01-01\n"
201+
" Vendor Name: abc\n"
202+
" Vendor OUI: 00-00-00\n"
203+
" Vendor PN: def\n"
204+
" Vendor Rev: ghi\n"
205+
" Vendor SN: jkl\n"
206+
),
207+
])
208+
def test_convert_sfp_info_to_output_string(self, sfp_info_dict, expected_output):
121209
output = sfputil.convert_sfp_info_to_output_string(sfp_info_dict)
122210
assert output == expected_output
123211

0 commit comments

Comments
 (0)