Skip to content

Commit ed6e66e

Browse files
authored
[GCU] Supporting Groupings during path-xpath translation (sonic-net#2044)
#### What I did Fixes sonic-net#2041 Supporting groupings during `config-db path` <-> `config-yang xpath` translation This mimics the `config-db` <-> `config-yang` translation as added by sonic-net#8318 #### How I did it Handled groupings of leaf commands only as that's what is supported in sonic-yang-mgmt #### How to verify it unit-test #### Previous command output (if the output of a command-line utility has changed) #### New command output (if the output of a command-line utility has changed)
1 parent 25b3455 commit ed6e66e

File tree

4 files changed

+127
-1
lines changed

4 files changed

+127
-1
lines changed

generic_config_updater/gu_common.py

+57-1
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ def create_xpath(self, tokens):
366366

367367
return f"{PathAddressing.XPATH_SEPARATOR}{PathAddressing.XPATH_SEPARATOR.join(str(t) for t in tokens)}"
368368

369+
def _create_sonic_yang_with_loaded_models(self):
370+
return self.config_wrapper.create_sonic_yang_with_loaded_models()
371+
369372
def find_ref_paths(self, path, config):
370373
"""
371374
Finds the paths referencing any line under the given 'path' within the given 'config'.
@@ -407,7 +410,7 @@ def find_ref_paths(self, path, config):
407410
return self._find_leafref_paths(path, config)
408411

409412
def _find_leafref_paths(self, path, config):
410-
sy = self.config_wrapper.create_sonic_yang_with_loaded_models()
413+
sy = self._create_sonic_yang_with_loaded_models()
411414

412415
tmp_config = copy.deepcopy(config)
413416

@@ -553,6 +556,13 @@ def _get_xpath_tokens_from_leaf(self, model, token_index, path_tokens, config):
553556
# /module-name:container/leaf-list[.='val']
554557
# Source: Check examples in https://netopeer.liberouter.org/doc/libyang/master/html/howto_x_path.html
555558
return [f"{token}[.='{value}']"]
559+
560+
# checking 'uses' statement
561+
if not isinstance(config[token], list): # leaf-list under uses is not supported yet in sonic_yang
562+
table = path_tokens[0]
563+
uses_leaf_model = self._get_uses_leaf_model(model, table, token)
564+
if uses_leaf_model:
565+
return [token]
556566

557567
raise ValueError(f"Path token not found.\n model: {model}\n token_index: {token_index}\n " + \
558568
f"path_tokens: {path_tokens}\n config: {config}")
@@ -719,6 +729,13 @@ def _get_path_tokens_from_leaf(self, model, token_index, xpath_tokens, config):
719729
list_idx = list_config.index(leaf_list_value)
720730
return [leaf_list_name, list_idx]
721731

732+
# checking 'uses' statement
733+
if not isinstance(config[leaf_list_name], list): # leaf-list under uses is not supported yet in sonic_yang
734+
table = xpath_tokens[1]
735+
uses_leaf_model = self._get_uses_leaf_model(model, table, token)
736+
if uses_leaf_model:
737+
return [token]
738+
722739
raise ValueError(f"Xpath token not found.\n model: {model}\n token_index: {token_index}\n " + \
723740
f"xpath_tokens: {xpath_tokens}\n config: {config}")
724741

@@ -754,6 +771,45 @@ def _get_model(self, model, name):
754771

755772
return None
756773

774+
def _get_uses_leaf_model(self, model, table, token):
775+
"""
776+
Getting leaf model in uses model matching the given token.
777+
"""
778+
uses_s = model.get('uses')
779+
if not uses_s:
780+
return None
781+
782+
# a model can be a single dict or a list of dictionaries, unify to a list of dictionaries
783+
if not isinstance(uses_s, list):
784+
uses_s = [uses_s]
785+
786+
sy = self._create_sonic_yang_with_loaded_models()
787+
# find yang module for current table
788+
table_module = sy.confDbYangMap[table]['yangModule']
789+
# uses Example: "@name": "bgpcmn:sonic-bgp-cmn"
790+
for uses in uses_s:
791+
if not isinstance(uses, dict):
792+
raise GenericConfigUpdaterError(f"'uses' is expected to be a dictionary found '{type(uses)}'.\n" \
793+
f" uses: {uses}\n model: {model}\n table: {table}\n token: {token}")
794+
795+
# Assume ':' means reference to another module
796+
if ':' in uses['@name']:
797+
name_parts = uses['@name'].split(':')
798+
prefix = name_parts[0].strip()
799+
uses_module_name = sy._findYangModuleFromPrefix(prefix, table_module)
800+
grouping = name_parts[-1].strip()
801+
else:
802+
uses_module_name = table_module['@name']
803+
grouping = uses['@name']
804+
805+
leafs = sy.preProcessedYang['grouping'][uses_module_name][grouping]
806+
807+
leaf_model = self._get_model(leafs, token)
808+
if leaf_model:
809+
return leaf_model
810+
811+
return None
812+
757813
class TitledLogger(logger.Logger):
758814
def __init__(self, syslog_identifier, title, verbose, print_all_to_console):
759815
super().__init__(syslog_identifier)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"BGP_NEIGHBOR": {
3+
"1.2.3.4": {
4+
"admin_status": "up",
5+
"asn": "65000",
6+
"holdtime": "10",
7+
"keepalive": "3",
8+
"local_addr": "10.0.0.10",
9+
"name": "ARISTA03T1",
10+
"nhopself": "0",
11+
"rrclient": "0"
12+
},
13+
"default|1.2.3.4": {
14+
"local_asn": "65200",
15+
"asn": "65100",
16+
"name": "bgp peer 65100",
17+
"ebgp_multihop_ttl": "3"
18+
}
19+
},
20+
"BGP_MONITORS": {
21+
"5.6.7.8": {
22+
"admin_status": "up",
23+
"asn": "65000",
24+
"holdtime": "180",
25+
"keepalive": "60",
26+
"local_addr": "10.0.0.11",
27+
"name": "BGPMonitor",
28+
"nhopself": "0",
29+
"rrclient": "0"
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"LLDP": {
3+
"GLOBAL": {
4+
"mode": "TRANSMIT",
5+
"enabled": "true",
6+
"hello_time": "12",
7+
"multiplier": "5",
8+
"supp_mgmt_address_tlv": "true",
9+
"supp_system_capabilities_tlv": "false",
10+
"system_name": "sonic",
11+
"system_description": "sonic-system"
12+
}
13+
}
14+
}

tests/generic_config_updater/gu_common_test.py

+24
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,18 @@ def check(path, xpath, config=None):
749749
check(path="/PORTCHANNEL_INTERFACE/PortChannel0001|1.1.1.1~124",
750750
xpath="/sonic-portchannel:sonic-portchannel/PORTCHANNEL_INTERFACE/PORTCHANNEL_INTERFACE_IPPREFIX_LIST[name='PortChannel0001'][ip_prefix='1.1.1.1/24']",
751751
config=Files.CONFIG_DB_WITH_PORTCHANNEL_INTERFACE)
752+
check(path="/BGP_NEIGHBOR/1.2.3.4/holdtime",
753+
xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_TEMPLATE_LIST[neighbor='1.2.3.4']/holdtime",
754+
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
755+
check(path="/BGP_NEIGHBOR/default|1.2.3.4/asn",
756+
xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_LIST[vrf_name='default'][neighbor='1.2.3.4']/asn",
757+
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
758+
check(path="/BGP_MONITORS/5.6.7.8/name",
759+
xpath="/sonic-bgp-monitor:sonic-bgp-monitor/BGP_MONITORS/BGP_MONITORS_LIST[addr='5.6.7.8']/name",
760+
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
761+
check(path="/LLDP/GLOBAL/mode",
762+
xpath="/sonic-lldp:sonic-lldp/LLDP/GLOBAL/mode",
763+
config=Files.CONFIG_DB_WITH_LLDP)
752764

753765
def test_convert_xpath_to_path(self):
754766
def check(xpath, path, config=None):
@@ -812,6 +824,18 @@ def check(xpath, path, config=None):
812824
check(xpath="/sonic-portchannel:sonic-portchannel/PORTCHANNEL_INTERFACE/PORTCHANNEL_INTERFACE_IPPREFIX_LIST[name='PortChannel0001'][ip_prefix='1.1.1.1/24']",
813825
path="/PORTCHANNEL_INTERFACE/PortChannel0001|1.1.1.1~124",
814826
config=Files.CONFIG_DB_WITH_PORTCHANNEL_INTERFACE)
827+
check(xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_TEMPLATE_LIST[neighbor='1.2.3.4']/holdtime",
828+
path="/BGP_NEIGHBOR/1.2.3.4/holdtime",
829+
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
830+
check(xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_LIST[vrf_name='default'][neighbor='1.2.3.4']/asn",
831+
path="/BGP_NEIGHBOR/default|1.2.3.4/asn",
832+
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
833+
check(xpath="/sonic-bgp-monitor:sonic-bgp-monitor/BGP_MONITORS/BGP_MONITORS_LIST[addr='5.6.7.8']/name",
834+
path="/BGP_MONITORS/5.6.7.8/name",
835+
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
836+
check(xpath="/sonic-lldp:sonic-lldp/LLDP/GLOBAL/mode",
837+
path="/LLDP/GLOBAL/mode",
838+
config=Files.CONFIG_DB_WITH_LLDP)
815839

816840
def test_has_path(self):
817841
def check(config, path, expected):

0 commit comments

Comments
 (0)