Skip to content

Commit f850122

Browse files
[YANG] Fix issue: Non compliant leaf list in config_db schema (sonic-net#10291)
#### Why I did it Fix issue: Non compliant leaf list in config_db schema: sonic-net#9801 #### How I did it The basic flow of DPB is like: 1. Transfer config db json value to YANG json value, name it “yangIn” 2. Validate “yangIn” by libyang 3. Generate a YANG json value to represent the target configuration, name it “yangTarget” 4. Do diff between “yangIn” and “yangTarget” 5. Apply the diff to CONFIG DB json and save it back to DB The fix: • For step #1, If value of a leaf-list field string type, transfer it to a list by splitting it with “,” the purpose here is to make step#2 happy. We also need to save <table_name>.<key>.<field_name> to a set named “leaf_list_with_string_value_set”. • For step#5, loop “leaf_list_with_string_value_set” and change those fields back to a string. #### How to verify it 1. Manual test 2. Changed sample config DB and unit test passed
1 parent 7c4ee43 commit f850122

File tree

3 files changed

+67
-14
lines changed

3 files changed

+67
-14
lines changed

src/sonic-yang-mgmt/sonic_yang.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ def __init__(self, yang_dir, debug=False, print_log_enabled=True, sonic_yang_opt
4444
# below dict will store preProcessed yang objects, which may be needed by
4545
# all yang modules, such as grouping.
4646
self.preProcessedYang = dict()
47-
47+
# element path for CONFIG DB. An example for this list could be:
48+
# ['PORT', 'Ethernet0', 'speed']
49+
self.elementPath = []
4850
try:
4951
self.ctx = ly.Context(yang_dir, sonic_yang_options)
5052
except Exception as e:

src/sonic-yang-mgmt/sonic_yang_ext.py

+53-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@
2020
'CABLE_LENGTH_LIST'
2121
]
2222

23+
# Workaround for those fields who is defined as leaf-list in YANG model but have string value in config DB.
24+
# Dictinary structure key = (<table_name>, <field_name>), value = seperator
25+
LEAF_LIST_WITH_STRING_VALUE_DICT = {
26+
('MIRROR_SESSION', 'src_ip'): ',',
27+
('NTP', 'src_intf'): ';',
28+
('BGP_ALLOWED_PREFIXES', 'prefixes_v4'): ',',
29+
('BGP_ALLOWED_PREFIXES', 'prefixes_v6'): ',',
30+
('BUFFER_PORT_EGRESS_PROFILE_LIST', 'profile_list'): ',',
31+
('BUFFER_PORT_INGRESS_PROFILE_LIST', 'profile_list'): ',',
32+
('PORT', 'adv_speeds'): ',',
33+
('PORT', 'adv_interface_types'): ',',
34+
}
35+
2336
"""
2437
This is the Exception thrown out of all public function of this class.
2538
"""
@@ -407,6 +420,11 @@ def _yangConvert(val):
407420
# if it is a leaf-list do it for each element
408421
if leafDict[key]['__isleafList']:
409422
vValue = list()
423+
if isinstance(value, str) and (self.elementPath[0], self.elementPath[-1]) in LEAF_LIST_WITH_STRING_VALUE_DICT:
424+
# For field defined as leaf-list but has string value in CONFIG DB, need do special handling here. For exampe:
425+
# port.adv_speeds in CONFIG DB has value "100,1000,10000", it shall be transferred to [100,1000,10000] as YANG value here to
426+
# make it align with its YANG definition.
427+
value = (x.strip() for x in value.split(LEAF_LIST_WITH_STRING_VALUE_DICT[(self.elementPath[0], self.elementPath[-1])]))
410428
for v in value:
411429
vValue.append(_yangConvert(v))
412430
else:
@@ -545,16 +563,21 @@ def _xlateList(self, model, yang, config, table, exceptionList):
545563
primaryKeys = list(config.keys())
546564
for pkey in primaryKeys:
547565
try:
566+
self.elementPath.append(pkey)
548567
vKey = None
549568
self.sysLog(syslog.LOG_DEBUG, "xlateList Extract pkey:{}".\
550569
format(pkey))
551570
# Find and extracts key from each dict in config
552571
keyDict = self._extractKey(pkey, listKeys)
553572
# fill rest of the values in keyDict
554573
for vKey in config[pkey]:
574+
self.elementPath.append(vKey)
555575
self.sysLog(syslog.LOG_DEBUG, "xlateList vkey {}".format(vKey))
556-
keyDict[vKey] = self._findYangTypedValue(vKey, \
557-
config[pkey][vKey], leafDict)
576+
try:
577+
keyDict[vKey] = self._findYangTypedValue(vKey, \
578+
config[pkey][vKey], leafDict)
579+
finally:
580+
self.elementPath.pop()
558581
yang.append(keyDict)
559582
# delete pkey from config, done to match one key with one list
560583
del config[pkey]
@@ -566,6 +589,8 @@ def _xlateList(self, model, yang, config, table, exceptionList):
566589
exceptionList.append(str(e))
567590
# with multilist, we continue matching other keys.
568591
continue
592+
finally:
593+
self.elementPath.pop()
569594

570595
return
571596

@@ -596,13 +621,17 @@ def _xlateContainerInContainer(self, model, yang, configC, table):
596621
if ccName not in configC:
597622
# Inner container doesn't exist in config
598623
return
624+
599625
if len(configC[ccName]) == 0:
600626
# Empty container, clean config and return
601627
del configC[ccName]
602628
return
603629
self.sysLog(msg="xlateProcessListOfContainer: {}".format(ccName))
630+
self.elementPath.append(ccName)
604631
self._xlateContainer(ccontainer, yang[ccName], \
605632
configC[ccName], table)
633+
self.elementPath.pop()
634+
606635
# clean empty container
607636
if len(yang[ccName]) == 0:
608637
del yang[ccName]
@@ -650,8 +679,10 @@ def _xlateContainer(self, model, yang, config, table):
650679
for vKey in vKeys:
651680
#vkey must be a leaf\leaf-list\choice in container
652681
if leafDict.get(vKey):
682+
self.elementPath.append(vKey)
653683
self.sysLog(syslog.LOG_DEBUG, "xlateContainer vkey {}".format(vKey))
654684
yang[vKey] = self._findYangTypedValue(vKey, configC[vKey], leafDict)
685+
self.elementPath.pop()
655686
# delete entry from copy of config
656687
del configC[vKey]
657688

@@ -681,8 +712,10 @@ def _xlateConfigDBtoYang(self, jIn, yangJ):
681712
yangJ[key] = dict() if yangJ.get(key) is None else yangJ[key]
682713
yangJ[key][subkey] = dict()
683714
self.sysLog(msg="xlateConfigDBtoYang {}:{}".format(key, subkey))
715+
self.elementPath.append(table)
684716
self._xlateContainer(cmap['container'], yangJ[key][subkey], \
685717
jIn[table], table)
718+
self.elementPath = []
686719

687720
return
688721

@@ -739,9 +772,14 @@ def _revYangConvert(val):
739772

740773
# if it is a leaf-list do it for each element
741774
if leafDict[key]['__isleafList']:
742-
vValue = list()
743-
for v in value:
744-
vValue.append(_revYangConvert(v))
775+
if isinstance(value, list) and (self.elementPath[0], self.elementPath[-1]) in LEAF_LIST_WITH_STRING_VALUE_DICT:
776+
# For field defined as leaf-list but has string value in CONFIG DB, we need do special handling here:
777+
# e.g. port.adv_speeds is [10,100,1000] in YANG, need to convert it into a string for CONFIG DB: "10,100,1000"
778+
vValue = LEAF_LIST_WITH_STRING_VALUE_DICT[(self.elementPath[0], self.elementPath[-1])].join((_revYangConvert(x) for x in value))
779+
else:
780+
vValue = list()
781+
for v in value:
782+
vValue.append(_revYangConvert(v))
745783
elif leafDict[key]['type']['@name'] == 'boolean':
746784
vValue = 'true' if value else 'false'
747785
else:
@@ -850,12 +888,16 @@ def _revXlateList(self, model, yang, config, table):
850888
# create key of config DB table
851889
pkey, pkeydict = self._createKey(entry, listKeys)
852890
self.sysLog(syslog.LOG_DEBUG, "revXlateList pkey:{}".format(pkey))
891+
self.elementPath.append(pkey)
853892
config[pkey]= dict()
854893
# fill rest of the entries
855894
for key in entry:
856895
if key not in pkeydict:
896+
self.elementPath.append(key)
857897
config[pkey][key] = self._revFindYangTypedValue(key, \
858898
entry[key], leafDict)
899+
self.elementPath.pop()
900+
self.elementPath.pop()
859901

860902
return
861903

@@ -879,8 +921,10 @@ def _revXlateContainerInContainer(self, model, yang, config, table):
879921
if yang.get(modelContainer['@name']):
880922
config[modelContainer['@name']] = dict()
881923
self.sysLog(msg="revXlateContainerInContainer {}".format(modelContainer['@name']))
924+
self.elementPath.append(modelContainer['@name'])
882925
self._revXlateContainer(modelContainer, yang[modelContainer['@name']], \
883926
config[modelContainer['@name']], table)
927+
self.elementPath.pop()
884928
return
885929

886930
"""
@@ -912,7 +956,9 @@ def _revXlateContainer(self, model, yang, config, table):
912956
#vkey must be a leaf\leaf-list\choice in container
913957
if leafDict.get(vKey):
914958
self.sysLog(syslog.LOG_DEBUG, "revXlateContainer vkey {}".format(vKey))
959+
self.elementPath.append(vKey)
915960
config[vKey] = self._revFindYangTypedValue(vKey, yang[vKey], leafDict)
961+
self.elementPath.pop()
916962

917963
return
918964

@@ -940,8 +986,10 @@ def _revXlateYangtoConfigDB(self, yangJ, cDbJson):
940986
cDbJson[table] = dict()
941987
#print(key + "--" + subkey)
942988
self.sysLog(msg="revXlateYangtoConfigDB {}".format(table))
989+
self.elementPath.append(table)
943990
self._revXlateContainer(cmap['container'], yangJ[module_top][container], \
944991
cDbJson[table], table)
992+
self.elementPath = []
945993

946994
return
947995

src/sonic-yang-models/tests/files/sample_config_db.json

+11-8
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@
6565
},
6666
"BUFFER_PORT_INGRESS_PROFILE_LIST": {
6767
"Ethernet9": {
68-
"profile_list": ["ingress_lossy_profile"]
68+
"profile_list": "ingress_lossy_profile"
6969
}
7070
},
7171
"BUFFER_PORT_EGRESS_PROFILE_LIST": {
7272
"Ethernet9": {
73-
"profile_list": ["egress_lossless_profile", "egress_lossy_profile"]
73+
"profile_list": "egress_lossless_profile,egress_lossy_profile"
7474
}
7575
},
7676
"PORTCHANNEL": {
@@ -408,10 +408,7 @@
408408
"NTP": {
409409
"global": {
410410
"vrf": "mgmt",
411-
"src_intf": [
412-
"eth0",
413-
"Loopback0"
414-
]
411+
"src_intf": "eth0;Loopback0"
415412
}
416413
},
417414
"NTP_SERVER": {
@@ -449,15 +446,21 @@
449446
"description": "",
450447
"speed": "11100",
451448
"tpid": "0x8100",
452-
"admin_status": "up"
449+
"admin_status": "up",
450+
"autoneg": "on",
451+
"adv_speeds": "100000,50000",
452+
"adv_interface_types": "CR,CR4"
453453
},
454454
"Ethernet2": {
455455
"alias": "Eth1/3",
456456
"lanes": "67",
457457
"description": "",
458458
"speed": "11100",
459459
"tpid": "0x8100",
460-
"admin_status": "up"
460+
"admin_status": "up",
461+
"autoneg": "on",
462+
"adv_speeds": "all",
463+
"adv_interface_types": "all"
461464
},
462465
"Ethernet3": {
463466
"alias": "Eth1/4",

0 commit comments

Comments
 (0)