Skip to content

Commit 366ac0e

Browse files
Sudharsan D.Glguohan
Sudharsan D.G
authored andcommitted
Dynamic transceiver tuning support (sonic-net#26)
1 parent ae041bc commit 366ac0e

File tree

1 file changed

+253
-6
lines changed

1 file changed

+253
-6
lines changed

sonic-xcvrd/scripts/xcvrd

+253-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ try:
1111
import time
1212
import signal
1313
import threading
14+
import json
15+
import string
16+
import ast
1417
import multiprocessing
1518
from swsscommon import swsscommon
1619
from sonic_daemon_base import daemon_base
@@ -51,6 +54,8 @@ VOLT_UNIT = 'Volts'
5154
POWER_UNIT = 'dBm'
5255
BIAS_UNIT = 'mA'
5356

57+
media_settings = ''
58+
g_dict = {}
5459
# Global platform specific sfputil class instance
5560
platform_sfputil = None
5661

@@ -108,7 +113,8 @@ def beautify_dom_info_dict(dom_info_dict):
108113
dom_info_dict['tx4power'] = strip_unit_and_beautify(dom_info_dict['tx4power'], POWER_UNIT)
109114

110115
# Update port sfp info in db
111-
def post_port_sfp_info_to_db(logical_port_name, table, stop=threading.Event()):
116+
def post_port_sfp_info_to_db(logical_port_name, table, transceiver_dict,
117+
stop=threading.Event()):
112118
ganged_port = False
113119
ganged_member_num = 1
114120

@@ -133,6 +139,7 @@ def post_port_sfp_info_to_db(logical_port_name, table, stop=threading.Event()):
133139
try:
134140
port_info_dict = platform_sfputil.get_transceiver_info_dict(physical_port)
135141
if port_info_dict is not None:
142+
transceiver_dict[physical_port]=port_info_dict
136143
fvs = swsscommon.FieldValuePairs([('type', port_info_dict['type']),
137144
('hardwarerev', port_info_dict['hardwarerev']),
138145
('serialnum', port_info_dict['serialnum']),
@@ -206,20 +213,29 @@ def post_port_dom_info_to_db(logical_port_name, table, stop=threading.Event()):
206213
sys.exit(NOT_IMPLEMENTED_ERROR)
207214

208215
# Update port dom/sfp info in db
209-
def post_port_sfp_dom_info_to_db(stop=threading.Event()):
216+
def post_port_sfp_dom_info_to_db(is_warm_start, stop=threading.Event()):
210217
# Connect to STATE_DB and create transceiver dom/sfp info tables
218+
transceiver_dict = {}
211219
state_db = daemon_base.db_connect(swsscommon.STATE_DB)
212220
int_tbl = swsscommon.Table(state_db, TRANSCEIVER_INFO_TABLE)
213221
dom_tbl = swsscommon.Table(state_db, TRANSCEIVER_DOM_SENSOR_TABLE)
214222

223+
appl_db = daemon_base.db_connect(swsscommon.APPL_DB)
224+
app_port_tbl = swsscommon.ProducerStateTable(appl_db,
225+
swsscommon.APP_PORT_TABLE_NAME)
226+
215227
# Post all the current interface dom/sfp info to STATE_DB
216228
logical_port_list = platform_sfputil.logical
217229
for logical_port_name in logical_port_list:
218230
if stop.is_set():
219231
break
220232

221-
post_port_sfp_info_to_db(logical_port_name, int_tbl, stop)
233+
post_port_sfp_info_to_db(logical_port_name, int_tbl, transceiver_dict, stop)
222234
post_port_dom_info_to_db(logical_port_name, dom_tbl, stop)
235+
## Do not notify media settings during warm reboot to avoid dataplane traffic impact
236+
if is_warm_start == False:
237+
notify_media_setting(logical_port_name, transceiver_dict, app_port_tbl)
238+
transceiver_dict.clear()
223239

224240
# Delete port dom/sfp info from db
225241
def del_port_sfp_dom_info_from_db(logical_port_name, int_tbl, dom_tbl):
@@ -246,6 +262,209 @@ def del_port_sfp_dom_info_from_db(logical_port_name, int_tbl, dom_tbl):
246262
logger.log_error("This functionality is currently not implemented for this platform")
247263
sys.exit(NOT_IMPLEMENTED_ERROR)
248264

265+
def check_port_in_range(range_str, physical_port):
266+
range_separator = '-'
267+
range_list = range_str.split(range_separator)
268+
start_num = int(range_list[0].strip())
269+
end_num = int(range_list[1].strip())
270+
if start_num <= physical_port <= end_num:
271+
return True
272+
return False
273+
274+
275+
def get_media_settings_value(physical_port, key):
276+
range_separator = '-'
277+
comma_separator = ','
278+
media_dict = {}
279+
default_dict = {}
280+
281+
# Keys under global media settings can be a list or range or list of ranges
282+
# of physical port numbers. Below are some examples
283+
# 1-32
284+
# 1,2,3,4,5
285+
# 1-4,9-12
286+
287+
if "GLOBAL_MEDIA_SETTINGS" in g_dict:
288+
for keys in g_dict["GLOBAL_MEDIA_SETTINGS"]:
289+
if comma_separator in keys:
290+
port_list = keys.split(comma_separator)
291+
for port in port_list:
292+
if range_separator in port:
293+
if check_port_in_range(port, physical_port):
294+
media_dict = g_dict["GLOBAL_MEDIA_SETTINGS"][keys]
295+
break
296+
elif str(physical_port) == port:
297+
media_dict = g_dict["GLOBAL_MEDIA_SETTINGS"][keys]
298+
break
299+
300+
elif range_separator in keys:
301+
if check_port_in_range(keys, physical_port):
302+
media_dict = g_dict["GLOBAL_MEDIA_SETTINGS"][keys]
303+
304+
# If there is a match in the global profile for a media type,
305+
# fetch those values
306+
if key[0] in media_dict:
307+
return media_dict[key[0]]
308+
elif key[1] in media_dict:
309+
return media_dict[key[1]]
310+
elif "Default" in media_dict:
311+
default_dict = media_dict['Default']
312+
313+
media_dict = {}
314+
315+
if "PORT_MEDIA_SETTINGS" in g_dict:
316+
for keys in g_dict["PORT_MEDIA_SETTINGS"]:
317+
if int(keys) == physical_port:
318+
media_dict = g_dict["PORT_MEDIA_SETTINGS"][keys]
319+
break
320+
if len(media_dict) == 0:
321+
if default_dict != 0:
322+
return default_dict
323+
else:
324+
logger.log_error("Error: No values for physical port '%d'"
325+
% physical_port)
326+
return {}
327+
if key[0] in media_dict:
328+
return media_dict[key[0]]
329+
elif key[1] in media_dict:
330+
return media_dict[key[1]]
331+
elif "Default" in media_dict:
332+
return media_dict['Default']
333+
elif len(default_dict) != 0:
334+
return default_dict
335+
else:
336+
return {}
337+
else:
338+
if default_dict != 0:
339+
return default_dict
340+
341+
def get_media_settings_key(physical_port, transceiver_dict):
342+
sup_compliance_str = '10/40G Ethernet Compliance Code'
343+
sup_len_str = 'Length Cable Assembly(m)'
344+
vendor_name_str = transceiver_dict[physical_port]['manufacturename']
345+
vendor_pn_str = transceiver_dict[physical_port]['modelname']
346+
vendor_key = string.upper(vendor_name_str) + '-' + vendor_pn_str
347+
348+
media_len = ''
349+
if transceiver_dict[physical_port]['cable_type'] == sup_len_str:
350+
media_len = transceiver_dict[physical_port]['cable_length']
351+
352+
media_compliance_dict_str = transceiver_dict[physical_port]['specification_compliance']
353+
media_compliance_dict = ast.literal_eval(media_compliance_dict_str)
354+
media_compliance_code = ''
355+
media_type = ''
356+
357+
if sup_compliance_str in media_compliance_dict:
358+
media_compliance_code = media_compliance_dict[sup_compliance_str]
359+
360+
media_type = transceiver_dict[physical_port]['type_abbrv_name']
361+
362+
media_key = ''
363+
364+
if len(media_type) != 0:
365+
media_key += media_type
366+
if len(media_compliance_code) != 0:
367+
media_key += '-' + media_compliance_code
368+
if len(media_len) != 0:
369+
media_key += '-' + media_len + 'M'
370+
371+
return [vendor_key, media_key]
372+
373+
def get_media_val_str_from_dict(media_dict):
374+
media_str = ''
375+
lane_str = 'lane'
376+
lane_separator = ','
377+
tmp_dict={}
378+
379+
for keys in media_dict:
380+
lane_num = int(keys.strip()[len(lane_str):])
381+
tmp_dict[lane_num] = media_dict[keys]
382+
383+
for key in range(0, len(tmp_dict)):
384+
media_str += tmp_dict[key]
385+
if key != tmp_dict.keys()[-1]:
386+
media_str += lane_separator
387+
return media_str
388+
389+
def get_media_val_str(num_logical_ports, lane_dict):
390+
lane_str = "lane"
391+
logical_media_dict = {}
392+
num_lanes_on_port = len(lane_dict)
393+
394+
# The physical ports has more than one logical port meaning it is
395+
# in breakout mode. So fetch the corresponding lanes from the file
396+
media_val_str = ''
397+
if (num_logical_ports > 1) and \
398+
(num_lanes_on_port > num_logical_ports):
399+
num_lanes_per_logical_port = num_lanes_on_port/num_logical_ports
400+
start_lane = logical_idx * num_lanes_per_logical_port
401+
402+
for lane_idx in range(start_lane, start_lane + \
403+
num_lanes_per_logical_port):
404+
lane_idx_str = lane_str + str(lane_idx)
405+
logical_lane_idx_str = lane_str + str(lane_idx - start_lane)
406+
logical_media_dict[logical_lane_idx_str] = lane_dict[lane_idx_str]
407+
408+
media_val_str = get_media_val_str_from_dict(logical_media_dict)
409+
else:
410+
media_val_str = get_media_val_str_from_dict(lane_dict)
411+
return media_val_str
412+
413+
def notify_media_setting(logical_port_name, transceiver_dict,
414+
app_port_tbl):
415+
if len(media_settings) == 0:
416+
return
417+
418+
ganged_port = False
419+
ganged_member_num = 1
420+
421+
physical_port_list = logical_port_name_to_physical_port_list(logical_port_name)
422+
if physical_port_list is None:
423+
logger.log_error("Error: No physical ports found for "
424+
"logical port '%s'" % logical_port_name)
425+
return PHYSICAL_PORT_NOT_EXIST
426+
427+
if len(physical_port_list) > 1:
428+
ganged_port = True
429+
430+
for physical_port in physical_port_list:
431+
logical_port_list = platform_sfputil.get_physical_to_logical(physical_port)
432+
num_logical_ports = len(logical_port_list)
433+
logical_idx = logical_port_list.index(logical_port_name)
434+
if not platform_sfputil.get_presence(physical_port):
435+
logger.log_info("Media %d presence not detected during notify"
436+
% physical_port)
437+
continue
438+
if physical_port not in transceiver_dict:
439+
logger.log_error("Media %d eeprom not populated in "
440+
"transceiver dict" % physical_port)
441+
continue
442+
443+
port_name = get_physical_port_name(logical_port_name,
444+
ganged_member_num, ganged_port)
445+
ganged_member_num += 1
446+
key = get_media_settings_key(physical_port, transceiver_dict)
447+
media_dict = get_media_settings_value(physical_port, key)
448+
449+
if(len(media_dict) == 0):
450+
logger.log_error("Error in obtainning media setting")
451+
return
452+
453+
fvs = swsscommon.FieldValuePairs(len(media_dict))
454+
455+
index = 0
456+
for media_key in media_dict:
457+
if type(media_dict[media_key]) is dict:
458+
media_val_str = get_media_val_str(num_logical_ports, \
459+
media_dict[media_key])
460+
else:
461+
media_val_str = media_dict[media_key]
462+
fvs[index] = (str(media_key), str(media_val_str))
463+
index += 1
464+
465+
app_port_tbl.set(port_name, fvs)
466+
467+
249468
#
250469
# Helper classes ===============================================================
251470
#
@@ -291,11 +510,17 @@ class sfp_state_update_task:
291510
def task_worker(self, stopping_event):
292511
logger.log_info("Start SFP monitoring loop")
293512

513+
transceiver_dict = {}
294514
# Connect to STATE_DB and create transceiver dom/sfp info tables
295515
state_db = daemon_base.db_connect(swsscommon.STATE_DB)
296516
int_tbl = swsscommon.Table(state_db, TRANSCEIVER_INFO_TABLE)
297517
dom_tbl = swsscommon.Table(state_db, TRANSCEIVER_DOM_SENSOR_TABLE)
298518

519+
# Connect to APPL_DB to notify Media notifications
520+
appl_db = daemon_base.db_connect(swsscommon.APPL_DB)
521+
app_port_tbl = swsscommon.ProducerStateTable(appl_db,
522+
swsscommon.APP_PORT_TABLE_NAME)
523+
299524
# Start loop to listen to the sfp change event
300525
while not stopping_event.is_set():
301526
status, port_dict = platform_sfputil.get_transceiver_change_event()
@@ -305,13 +530,15 @@ class sfp_state_update_task:
305530
for logical_port in logical_port_list:
306531
if value == SFP_STATUS_INSERTED:
307532
logger.log_info("Got SFP inserted event")
308-
rc = post_port_sfp_info_to_db(logical_port, int_tbl)
533+
rc = post_port_sfp_info_to_db(logical_port, int_tbl, transceiver_dict)
309534
# If we didn't get the sfp info, assuming the eeprom is not ready, give a try again.
310535
if rc == SFP_EEPROM_NOT_READY:
311536
logger.log_warning("SFP EEPROM is not ready. One more try...")
312537
time.sleep(TIME_FOR_SFP_READY_SECS)
313-
post_port_sfp_info_to_db(logical_port, int_tbl)
538+
post_port_sfp_info_to_db(logical_port, int_tbl, transceiver_dict)
314539
post_port_dom_info_to_db(logical_port, dom_tbl)
540+
notify_media_setting(logical_port, transceiver_dict, app_port_tbl)
541+
transceiver_dict.clear()
315542
elif value == SFP_STATUS_REMOVED:
316543
logger.log_info("Got SFP removed event")
317544
del_port_sfp_dom_info_from_db(logical_port, int_tbl, dom_tbl)
@@ -384,6 +611,20 @@ class DaemonXcvrd(DaemonBase):
384611
if key in ["PortConfigDone", "PortInitDone"]:
385612
break
386613

614+
def load_media_settings(self):
615+
global media_settings
616+
global g_dict
617+
(platform, hwsku_path) = self.get_path_to_platform_and_hwsku()
618+
619+
media_settings_file_path = "/".join([platform, "media_settings.json"])
620+
if not os.path.isfile(media_settings_file_path):
621+
logger.log_info("xcvrd: No media file exists")
622+
return {}
623+
624+
media_file = open(media_settings_file_path, "r")
625+
media_settings = media_file.read()
626+
g_dict = json.loads(media_settings)
627+
387628
# Initialize daemon
388629
def init(self):
389630
global platform_sfputil
@@ -405,13 +646,19 @@ class DaemonXcvrd(DaemonBase):
405646
logger.log_error("Failed to read port info: %s" % (str(e)), True)
406647
sys.exit(PORT_CONFIG_LOAD_ERROR)
407648

649+
self.load_media_settings()
650+
warmstart = swsscommon.WarmStart()
651+
warmstart.initialize("xcvrd", "pmon")
652+
warmstart.checkWarmStart("xcvrd", "pmon", False)
653+
is_warm_start = warmstart.isWarmStart()
654+
408655
# Make sure this daemon started after all port configured
409656
logger.log_info("Wait for port config is done")
410657
self.wait_for_port_config_done()
411658

412659
# Post all the current interface dom/sfp info to STATE_DB
413660
logger.log_info("Post all port DOM/SFP info to DB")
414-
post_port_sfp_dom_info_to_db(self.stop)
661+
post_port_sfp_dom_info_to_db(is_warm_start, self.stop)
415662

416663
# Deinitialize daemon
417664
def deinit(self):

0 commit comments

Comments
 (0)