Missing container in list support in YANG Model #16704 (#18091)

Fixes "Missing container in list support in YANG Model https://github.com/sonic-net/sonic-buildimage/issues/16704"

Why I did it
Adds support for container in list

How I did it
Identify container in list's leaf and add its data.

Fixes "Update dhcpv6 option yang model" #16290

Why I did it
Adds support for single "choice" statement in container/list

How I did it
Check if choice data is dictionary (instead of list).

How to verify it
Reconstruction details in bug's description.

Tested branch (Please provide the tested image version)
 202311
Description for the changelog
Adds support for container in list to yang parsing

Link to config_db schema for YANG module changes
https://github.com/sonic-net/sonic-buildimage/blob/master/src/sonic-yang-models/doc/Configuration.md#dhcp_relay

Signed-off-by: vkuk [vkuk@marvell.com]
This commit is contained in:
Vladimir Kuk 2024-03-21 19:51:04 +02:00 committed by GitHub
parent b3845b620d
commit 0688aa38ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 210 additions and 3 deletions

View File

@ -378,10 +378,17 @@ class SonicYangExtMixin:
#choices, this is tricky, since leafs are under cases in tree. #choices, this is tricky, since leafs are under cases in tree.
choices = model.get('choice') choices = model.get('choice')
if choices: if choices:
for choice in choices: # If single choice exists in container/list
cases = choice['case'] if isinstance(choices, dict):
cases = choices['case']
for case in cases: for case in cases:
self._fillLeafDict(case.get('leaf'), leafDict) self._fillLeafDict(case.get('leaf'), leafDict)
# If multiple choices exist in container/list
else:
for choice in choices:
cases = choice['case']
for case in cases:
self._fillLeafDict(case.get('leaf'), leafDict)
# leaf-lists # leaf-lists
self._fillLeafDict(model.get('leaf-list'), leafDict, True) self._fillLeafDict(model.get('leaf-list'), leafDict, True)
@ -535,6 +542,29 @@ class SonicYangExtMixin:
continue continue
return return
"""
Process container inside a List.
This function will call xlateContainer based on Container(s) present
in outer List.
"""
def _xlateContainerInList(self, model, yang, configC, table):
ccontainer = model
ccName = ccontainer.get('@name')
if ccName not in configC:
# Inner container doesn't exist in config
return
if bool(configC[ccName]):
# Empty container - return
return
self.sysLog(msg="xlateProcessListOfContainer: {}".format(ccName))
self.elementPath.append(ccName)
self._xlateContainer(ccontainer, yang, configC[ccName], table)
self.elementPath.pop()
return
""" """
Xlate a list Xlate a list
This function will xlate from a dict in config DB to a Yang JSON list This function will xlate from a dict in config DB to a Yang JSON list
@ -553,6 +583,9 @@ class SonicYangExtMixin:
self._xlateType1MapList(model, yang, config, table, exceptionList) self._xlateType1MapList(model, yang, config, table, exceptionList)
return return
# For handling of container(s) in list
ccontainer = model.get('container')
#create a dict to map each key under primary key with a dict yang model. #create a dict to map each key under primary key with a dict yang model.
#This is done to improve performance of mapping from values of TABLEs in #This is done to improve performance of mapping from values of TABLEs in
#config DB to leaf in YANG LIST. #config DB to leaf in YANG LIST.
@ -572,6 +605,19 @@ class SonicYangExtMixin:
keyDict = self._extractKey(pkey, listKeys) keyDict = self._extractKey(pkey, listKeys)
# fill rest of the values in keyDict # fill rest of the values in keyDict
for vKey in config[pkey]: for vKey in config[pkey]:
if ccontainer and vKey == ccontainer.get('@name'):
self.sysLog(syslog.LOG_DEBUG, "xlateList Handle container {} in list {}".\
format(vKey, table))
yangContainer = dict()
if isinstance(ccontainer, dict) and bool(config):
self._xlateContainerInList(ccontainer, yangContainer, config[pkey], table)
# If multi-list exists in container,
elif ccontainer and isinstance(ccontainer, list) and bool(config):
for modelContainer in ccontainer:
self._xlateContainerInList(modelContainer, yangContainer, config[pkey], table)
if len(yangContainer):
keyDict[vKey] = yangContainer
continue
self.elementPath.append(vKey) self.elementPath.append(vKey)
self.sysLog(syslog.LOG_DEBUG, "xlateList vkey {}".format(vKey)) self.sysLog(syslog.LOG_DEBUG, "xlateList vkey {}".format(vKey))
try: try:
@ -617,7 +663,7 @@ class SonicYangExtMixin:
""" """
def _xlateContainerInContainer(self, model, yang, configC, table): def _xlateContainerInContainer(self, model, yang, configC, table):
ccontainer = model ccontainer = model
ccName = ccontainer['@name'] ccName = ccontainer.get('@name')
yang[ccName] = dict() yang[ccName] = dict()
if ccName not in configC: if ccName not in configC:
# Inner container doesn't exist in config # Inner container doesn't exist in config
@ -876,6 +922,9 @@ class SonicYangExtMixin:
self._revXlateType1MapList(model, yang, config, table) self._revXlateType1MapList(model, yang, config, table)
return return
# For handling of container(s) in list
ccontainer = model.get('container')
# get keys from YANG model list itself # get keys from YANG model list itself
listKeys = model['key']['@value'] listKeys = model['key']['@value']
# create a dict to map each key under primary key with a dict yang model. # create a dict to map each key under primary key with a dict yang model.
@ -894,6 +943,17 @@ class SonicYangExtMixin:
# fill rest of the entries # fill rest of the entries
for key in entry: for key in entry:
if key not in pkeydict: if key not in pkeydict:
if ccontainer and key == ccontainer['@name']:
self.sysLog(syslog.LOG_DEBUG, "revXlateList handle container {} in list {}".format(pkey, table))
# IF container has only one inner container
if isinstance(ccontainer, dict):
self._revXlateContainerInContainer(ccontainer, entry, config[pkey], table)
# IF container has many inner container
elif isinstance(ccontainer, list):
for modelContainer in ccontainer:
self._revXlateContainerInContainer(modelContainer, entry, config[pkey], table)
continue
self.elementPath.append(key) self.elementPath.append(key)
config[pkey][key] = self._revFindYangTypedValue(key, \ config[pkey][key] = self._revFindYangTypedValue(key, \
entry[key], leafDict) entry[key], leafDict)

View File

@ -256,5 +256,39 @@
} }
] ]
} }
},
"test-yang-structure:test-yang-container": {
"test-yang-structure:YANG_STRUCT_TEST": {
"YANG_LIST_TEST_LIST": [{
"name" : "Vlan1001",
"leaf-list-test": [
"fc02:2000::1",
"fc02:2000::2"
],
"container-in-list-test": {
"leaf-1": true,
"leaf-2": "test1",
"mc-case-leaf-1": 55,
"mc-case-leaf-3": 1234
},
"case-leaf-1": 101
},
{
"name" : "Test123",
"leaf-list-test": [
"3003:2000::1",
"2002:2001::2"
],
"container-in-list-test": {
"leaf-1": false,
"leaf-2": "test2",
"mc-case-leaf-2": 77,
"mc-case-leaf-3": 4321
},
"case-leaf-2": 1001
}
]
}
} }
} }

View File

@ -0,0 +1,105 @@
module test-yang-structure {
namespace "http://github.com/sonic-net/test";
prefix yangstructtest;
yang-version 1.1;
import ietf-yang-types {
prefix yang;
}
import ietf-inet-types {
prefix inet;
}
import test-head {
prefix head;
revision-date 2019-07-01;
}
revision 2021-10-30 {
description "First Revision";
}
container test-yang-container {
container YANG_STRUCT_TEST {
description "sample test container";
list YANG_LIST_TEST_LIST {
key "name";
leaf name {
type string;
}
leaf-list leaf-list-test {
description "Test leaf-list statement";
type inet:ipv6-address;
}
container container-in-list-test {
leaf leaf-1 {
description "test leaf in container";
type string {
pattern "false|true";
}
}
leaf leaf-2 {
description "test leaf in container";
type string;
}
choice multi-choice-in-container-test-1 {
case mc-case-test-1 {
leaf mc-case-leaf-1 {
description "test leaf in multi choice";
type uint32;
}
}
case mc-case-test-2 {
leaf mc-case-leaf-2 {
description "test leaf in multi choice";
type uint8;
}
}
}
choice multi-choice-in-container-test-2 {
case mc-case-test-3 {
leaf mc-case-leaf-3 {
description "test leaf in multi choice";
type uint16;
}
}
}
}
choice single-choice-in-list-test {
case case-test-1 {
leaf case-leaf-1 {
description "test leaf in single choice";
type uint32;
}
}
case case-test-2 {
leaf case-leaf-2 {
description "test leaf in single choice";
type uint16;
}
}
}
}
/* end of YANG_LIST_TEST_LIST */
}
/* end of container YANG_STRUCT_TEST */
}
/* end of container test-yang-container */
}
/* end of module test-yang-structure */

View File

@ -168,6 +168,10 @@
{ {
"file" : "test-vlan.yang", "file" : "test-vlan.yang",
"module" : "test-vlan" "module" : "test-vlan"
},
{
"file" : "test-yang-structure.yang",
"module" : "test-yang-structure"
} }
], ],
"new_nodes" : [ "new_nodes" : [
@ -244,6 +248,10 @@
{ {
"module_name" : "test-vlan", "module_name" : "test-vlan",
"module_prefix" : "vlan" "module_prefix" : "vlan"
},
{
"module_name" : "test-yang-structure",
"module_prefix" : "yangstructtest"
} }
], ],
"schema_dependencies" : [ "schema_dependencies" : [