11
11
import time
12
12
import signal
13
13
import threading
14
+ import json
15
+ import string
16
+ import ast
14
17
import multiprocessing
15
18
from swsscommon import swsscommon
16
19
from sonic_daemon_base import daemon_base
@@ -51,6 +54,8 @@ VOLT_UNIT = 'Volts'
51
54
POWER_UNIT = 'dBm'
52
55
BIAS_UNIT = 'mA'
53
56
57
+ media_settings = ''
58
+ g_dict = {}
54
59
# Global platform specific sfputil class instance
55
60
platform_sfputil = None
56
61
@@ -108,7 +113,8 @@ def beautify_dom_info_dict(dom_info_dict):
108
113
dom_info_dict ['tx4power' ] = strip_unit_and_beautify (dom_info_dict ['tx4power' ], POWER_UNIT )
109
114
110
115
# 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 ()):
112
118
ganged_port = False
113
119
ganged_member_num = 1
114
120
@@ -133,6 +139,7 @@ def post_port_sfp_info_to_db(logical_port_name, table, stop=threading.Event()):
133
139
try :
134
140
port_info_dict = platform_sfputil .get_transceiver_info_dict (physical_port )
135
141
if port_info_dict is not None :
142
+ transceiver_dict [physical_port ]= port_info_dict
136
143
fvs = swsscommon .FieldValuePairs ([('type' , port_info_dict ['type' ]),
137
144
('hardwarerev' , port_info_dict ['hardwarerev' ]),
138
145
('serialnum' , port_info_dict ['serialnum' ]),
@@ -206,20 +213,29 @@ def post_port_dom_info_to_db(logical_port_name, table, stop=threading.Event()):
206
213
sys .exit (NOT_IMPLEMENTED_ERROR )
207
214
208
215
# 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 ()):
210
217
# Connect to STATE_DB and create transceiver dom/sfp info tables
218
+ transceiver_dict = {}
211
219
state_db = daemon_base .db_connect (swsscommon .STATE_DB )
212
220
int_tbl = swsscommon .Table (state_db , TRANSCEIVER_INFO_TABLE )
213
221
dom_tbl = swsscommon .Table (state_db , TRANSCEIVER_DOM_SENSOR_TABLE )
214
222
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
+
215
227
# Post all the current interface dom/sfp info to STATE_DB
216
228
logical_port_list = platform_sfputil .logical
217
229
for logical_port_name in logical_port_list :
218
230
if stop .is_set ():
219
231
break
220
232
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 )
222
234
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 ()
223
239
224
240
# Delete port dom/sfp info from db
225
241
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):
246
262
logger .log_error ("This functionality is currently not implemented for this platform" )
247
263
sys .exit (NOT_IMPLEMENTED_ERROR )
248
264
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
+
249
468
#
250
469
# Helper classes ===============================================================
251
470
#
@@ -291,11 +510,17 @@ class sfp_state_update_task:
291
510
def task_worker (self , stopping_event ):
292
511
logger .log_info ("Start SFP monitoring loop" )
293
512
513
+ transceiver_dict = {}
294
514
# Connect to STATE_DB and create transceiver dom/sfp info tables
295
515
state_db = daemon_base .db_connect (swsscommon .STATE_DB )
296
516
int_tbl = swsscommon .Table (state_db , TRANSCEIVER_INFO_TABLE )
297
517
dom_tbl = swsscommon .Table (state_db , TRANSCEIVER_DOM_SENSOR_TABLE )
298
518
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
+
299
524
# Start loop to listen to the sfp change event
300
525
while not stopping_event .is_set ():
301
526
status , port_dict = platform_sfputil .get_transceiver_change_event ()
@@ -305,13 +530,15 @@ class sfp_state_update_task:
305
530
for logical_port in logical_port_list :
306
531
if value == SFP_STATUS_INSERTED :
307
532
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 )
309
534
# If we didn't get the sfp info, assuming the eeprom is not ready, give a try again.
310
535
if rc == SFP_EEPROM_NOT_READY :
311
536
logger .log_warning ("SFP EEPROM is not ready. One more try..." )
312
537
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 )
314
539
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 ()
315
542
elif value == SFP_STATUS_REMOVED :
316
543
logger .log_info ("Got SFP removed event" )
317
544
del_port_sfp_dom_info_from_db (logical_port , int_tbl , dom_tbl )
@@ -384,6 +611,20 @@ class DaemonXcvrd(DaemonBase):
384
611
if key in ["PortConfigDone" , "PortInitDone" ]:
385
612
break
386
613
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
+
387
628
# Initialize daemon
388
629
def init (self ):
389
630
global platform_sfputil
@@ -405,13 +646,19 @@ class DaemonXcvrd(DaemonBase):
405
646
logger .log_error ("Failed to read port info: %s" % (str (e )), True )
406
647
sys .exit (PORT_CONFIG_LOAD_ERROR )
407
648
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
+
408
655
# Make sure this daemon started after all port configured
409
656
logger .log_info ("Wait for port config is done" )
410
657
self .wait_for_port_config_done ()
411
658
412
659
# Post all the current interface dom/sfp info to STATE_DB
413
660
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 )
415
662
416
663
# Deinitialize daemon
417
664
def deinit (self ):
0 commit comments