Skip to content

Commit d0e7d9a

Browse files
[YANG] Fix issue: Non compliant leaf list in config_db schema (sonic-net#10291) (sonic-net#10768)
Fix issue: Non compliant leaf list in config_db schema: sonic-net/sonic-buildimage#9801 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 sonic-net#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. 1. Manual test 2. Changed sample config DB and unit test passed Conflicts: src/sonic-yang-mgmt/sonic_yang_ext.py
1 parent a428301 commit d0e7d9a

3 files changed

Lines changed: 65 additions & 14 deletions

File tree

src/sonic-yang-mgmt/sonic_yang.py

Lines changed: 3 additions & 1 deletion
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

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@
1515
'MAP_PFC_PRIORITY_TO_QUEUE_LIST',
1616
'PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_LIST']
1717

18+
# Workaround for those fields who is defined as leaf-list in YANG model but have string value in config DB.
19+
# Dictinary structure key = (<table_name>, <field_name>), value = seperator
20+
LEAF_LIST_WITH_STRING_VALUE_DICT = {
21+
('MIRROR_SESSION', 'src_ip'): ',',
22+
('NTP', 'src_intf'): ';',
23+
('BGP_ALLOWED_PREFIXES', 'prefixes_v4'): ',',
24+
('BGP_ALLOWED_PREFIXES', 'prefixes_v6'): ',',
25+
('BUFFER_PORT_EGRESS_PROFILE_LIST', 'profile_list'): ',',
26+
('BUFFER_PORT_INGRESS_PROFILE_LIST', 'profile_list'): ',',
27+
('PORT', 'adv_speeds'): ',',
28+
('PORT', 'adv_interface_types'): ',',
29+
}
30+
1831
"""
1932
This is the Exception thrown out of all public function of this class.
2033
"""
@@ -402,6 +415,11 @@ def _yangConvert(val):
402415
# if it is a leaf-list do it for each element
403416
if leafDict[key]['__isleafList']:
404417
vValue = list()
418+
if isinstance(value, str) and (self.elementPath[0], self.elementPath[-1]) in LEAF_LIST_WITH_STRING_VALUE_DICT:
419+
# For field defined as leaf-list but has string value in CONFIG DB, need do special handling here. For exampe:
420+
# port.adv_speeds in CONFIG DB has value "100,1000,10000", it shall be transferred to [100,1000,10000] as YANG value here to
421+
# make it align with its YANG definition.
422+
value = (x.strip() for x in value.split(LEAF_LIST_WITH_STRING_VALUE_DICT[(self.elementPath[0], self.elementPath[-1])]))
405423
for v in value:
406424
vValue.append(_yangConvert(v))
407425
else:
@@ -540,16 +558,21 @@ def _xlateList(self, model, yang, config, table, exceptionList):
540558
primaryKeys = list(config.keys())
541559
for pkey in primaryKeys:
542560
try:
561+
self.elementPath.append(pkey)
543562
vKey = None
544563
self.sysLog(syslog.LOG_DEBUG, "xlateList Extract pkey:{}".\
545564
format(pkey))
546565
# Find and extracts key from each dict in config
547566
keyDict = self._extractKey(pkey, listKeys)
548567
# fill rest of the values in keyDict
549568
for vKey in config[pkey]:
569+
self.elementPath.append(vKey)
550570
self.sysLog(syslog.LOG_DEBUG, "xlateList vkey {}".format(vKey))
551-
keyDict[vKey] = self._findYangTypedValue(vKey, \
552-
config[pkey][vKey], leafDict)
571+
try:
572+
keyDict[vKey] = self._findYangTypedValue(vKey, \
573+
config[pkey][vKey], leafDict)
574+
finally:
575+
self.elementPath.pop()
553576
yang.append(keyDict)
554577
# delete pkey from config, done to match one key with one list
555578
del config[pkey]
@@ -561,6 +584,8 @@ def _xlateList(self, model, yang, config, table, exceptionList):
561584
exceptionList.append(str(e))
562585
# with multilist, we continue matching other keys.
563586
continue
587+
finally:
588+
self.elementPath.pop()
564589

565590
return
566591

@@ -591,8 +616,10 @@ def _xlateContainerInContainer(self, model, yang, configC, table):
591616
if not configC.get(ccontainer['@name']):
592617
return
593618
self.sysLog(msg="xlateProcessListOfContainer: {}".format(ccontainer['@name']))
619+
self.elementPath.append(ccontainer['@name'])
594620
self._xlateContainer(ccontainer, yang[ccontainer['@name']], \
595621
configC[ccontainer['@name']], table)
622+
self.elementPath.pop()
596623
# clean empty container
597624
if len(yang[ccontainer['@name']]) == 0:
598625
del yang[ccontainer['@name']]
@@ -640,8 +667,10 @@ def _xlateContainer(self, model, yang, config, table):
640667
for vKey in vKeys:
641668
#vkey must be a leaf\leaf-list\choice in container
642669
if leafDict.get(vKey):
670+
self.elementPath.append(vKey)
643671
self.sysLog(syslog.LOG_DEBUG, "xlateContainer vkey {}".format(vKey))
644672
yang[vKey] = self._findYangTypedValue(vKey, configC[vKey], leafDict)
673+
self.elementPath.pop()
645674
# delete entry from copy of config
646675
del configC[vKey]
647676

@@ -671,8 +700,10 @@ def _xlateConfigDBtoYang(self, jIn, yangJ):
671700
yangJ[key] = dict() if yangJ.get(key) is None else yangJ[key]
672701
yangJ[key][subkey] = dict()
673702
self.sysLog(msg="xlateConfigDBtoYang {}:{}".format(key, subkey))
703+
self.elementPath.append(table)
674704
self._xlateContainer(cmap['container'], yangJ[key][subkey], \
675705
jIn[table], table)
706+
self.elementPath = []
676707

677708
return
678709

@@ -729,9 +760,14 @@ def _revYangConvert(val):
729760

730761
# if it is a leaf-list do it for each element
731762
if leafDict[key]['__isleafList']:
732-
vValue = list()
733-
for v in value:
734-
vValue.append(_revYangConvert(v))
763+
if isinstance(value, list) and (self.elementPath[0], self.elementPath[-1]) in LEAF_LIST_WITH_STRING_VALUE_DICT:
764+
# For field defined as leaf-list but has string value in CONFIG DB, we need do special handling here:
765+
# e.g. port.adv_speeds is [10,100,1000] in YANG, need to convert it into a string for CONFIG DB: "10,100,1000"
766+
vValue = LEAF_LIST_WITH_STRING_VALUE_DICT[(self.elementPath[0], self.elementPath[-1])].join((_revYangConvert(x) for x in value))
767+
else:
768+
vValue = list()
769+
for v in value:
770+
vValue.append(_revYangConvert(v))
735771
elif leafDict[key]['type']['@name'] == 'boolean':
736772
vValue = 'true' if value else 'false'
737773
else:
@@ -840,12 +876,16 @@ def _revXlateList(self, model, yang, config, table):
840876
# create key of config DB table
841877
pkey, pkeydict = self._createKey(entry, listKeys)
842878
self.sysLog(syslog.LOG_DEBUG, "revXlateList pkey:{}".format(pkey))
879+
self.elementPath.append(pkey)
843880
config[pkey]= dict()
844881
# fill rest of the entries
845882
for key in entry:
846883
if key not in pkeydict:
884+
self.elementPath.append(key)
847885
config[pkey][key] = self._revFindYangTypedValue(key, \
848886
entry[key], leafDict)
887+
self.elementPath.pop()
888+
self.elementPath.pop()
849889

850890
return
851891

@@ -869,8 +909,10 @@ def _revXlateContainerInContainer(self, model, yang, config, table):
869909
if yang.get(modelContainer['@name']):
870910
config[modelContainer['@name']] = dict()
871911
self.sysLog(msg="revXlateContainerInContainer {}".format(modelContainer['@name']))
912+
self.elementPath.append(modelContainer['@name'])
872913
self._revXlateContainer(modelContainer, yang[modelContainer['@name']], \
873914
config[modelContainer['@name']], table)
915+
self.elementPath.pop()
874916
return
875917

876918
"""
@@ -902,7 +944,9 @@ def _revXlateContainer(self, model, yang, config, table):
902944
#vkey must be a leaf\leaf-list\choice in container
903945
if leafDict.get(vKey):
904946
self.sysLog(syslog.LOG_DEBUG, "revXlateContainer vkey {}".format(vKey))
947+
self.elementPath.append(vKey)
905948
config[vKey] = self._revFindYangTypedValue(vKey, yang[vKey], leafDict)
949+
self.elementPath.pop()
906950

907951
return
908952

@@ -930,8 +974,10 @@ def _revXlateYangtoConfigDB(self, yangJ, cDbJson):
930974
cDbJson[table] = dict()
931975
#print(key + "--" + subkey)
932976
self.sysLog(msg="revXlateYangtoConfigDB {}".format(table))
977+
self.elementPath.append(table)
933978
self._revXlateContainer(cmap['container'], yangJ[module_top][container], \
934979
cDbJson[table], table)
980+
self.elementPath = []
935981

936982
return
937983

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

Lines changed: 11 additions & 8 deletions
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": {
@@ -363,10 +363,7 @@
363363
"NTP": {
364364
"global": {
365365
"vrf": "mgmt",
366-
"src_intf": [
367-
"eth0",
368-
"Loopback0"
369-
]
366+
"src_intf": "eth0;Loopback0"
370367
}
371368
},
372369
"NTP_SERVER": {
@@ -397,15 +394,21 @@
397394
"description": "",
398395
"speed": "11100",
399396
"tpid": "0x8100",
400-
"admin_status": "up"
397+
"admin_status": "up",
398+
"autoneg": "on",
399+
"adv_speeds": "100000,50000",
400+
"adv_interface_types": "CR,CR4"
401401
},
402402
"Ethernet2": {
403403
"alias": "Eth1/3",
404404
"lanes": "67",
405405
"description": "",
406406
"speed": "11100",
407407
"tpid": "0x8100",
408-
"admin_status": "up"
408+
"admin_status": "up",
409+
"autoneg": "on",
410+
"adv_speeds": "all",
411+
"adv_interface_types": "all"
409412
},
410413
"Ethernet3": {
411414
"alias": "Eth1/4",

0 commit comments

Comments
 (0)