Skip to content

Commit

Permalink
[GCU] Supporting Groupings during path-xpath translation (#2044)
Browse files Browse the repository at this point in the history
#### What I did
Fixes #2041

Supporting groupings during `config-db path` <-> `config-yang xpath` translation

This mimics the `config-db` <-> `config-yang`  translation as added by sonic-net/sonic-buildimage#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)
  • Loading branch information
malletvapid23 committed Feb 23, 2022
1 parent 1c5dab6 commit 50bacce
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 1 deletion.
58 changes: 57 additions & 1 deletion generic_config_updater/gu_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ def create_xpath(self, tokens):

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

def _create_sonic_yang_with_loaded_models(self):
return self.config_wrapper.create_sonic_yang_with_loaded_models()

def find_ref_paths(self, path, config):
"""
Finds the paths referencing any line under the given 'path' within the given 'config'.
Expand Down Expand Up @@ -407,7 +410,7 @@ def find_ref_paths(self, path, config):
return self._find_leafref_paths(path, config)

def _find_leafref_paths(self, path, config):
sy = self.config_wrapper.create_sonic_yang_with_loaded_models()
sy = self._create_sonic_yang_with_loaded_models()

tmp_config = copy.deepcopy(config)

Expand Down Expand Up @@ -553,6 +556,13 @@ def _get_xpath_tokens_from_leaf(self, model, token_index, path_tokens, config):
# /module-name:container/leaf-list[.='val']
# Source: Check examples in https://netopeer.liberouter.org/doc/libyang/master/html/howto_x_path.html
return [f"{token}[.='{value}']"]

# checking 'uses' statement
if not isinstance(config[token], list): # leaf-list under uses is not supported yet in sonic_yang
table = path_tokens[0]
uses_leaf_model = self._get_uses_leaf_model(model, table, token)
if uses_leaf_model:
return [token]

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

# checking 'uses' statement
if not isinstance(config[leaf_list_name], list): # leaf-list under uses is not supported yet in sonic_yang
table = xpath_tokens[1]
uses_leaf_model = self._get_uses_leaf_model(model, table, token)
if uses_leaf_model:
return [token]

raise ValueError(f"Xpath token not found.\n model: {model}\n token_index: {token_index}\n " + \
f"xpath_tokens: {xpath_tokens}\n config: {config}")

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

return None

def _get_uses_leaf_model(self, model, table, token):
"""
Getting leaf model in uses model matching the given token.
"""
uses_s = model.get('uses')
if not uses_s:
return None

# a model can be a single dict or a list of dictionaries, unify to a list of dictionaries
if not isinstance(uses_s, list):
uses_s = [uses_s]

sy = self._create_sonic_yang_with_loaded_models()
# find yang module for current table
table_module = sy.confDbYangMap[table]['yangModule']
# uses Example: "@name": "bgpcmn:sonic-bgp-cmn"
for uses in uses_s:
if not isinstance(uses, dict):
raise GenericConfigUpdaterError(f"'uses' is expected to be a dictionary found '{type(uses)}'.\n" \
f" uses: {uses}\n model: {model}\n table: {table}\n token: {token}")

# Assume ':' means reference to another module
if ':' in uses['@name']:
name_parts = uses['@name'].split(':')
prefix = name_parts[0].strip()
uses_module_name = sy._findYangModuleFromPrefix(prefix, table_module)
grouping = name_parts[-1].strip()
else:
uses_module_name = table_module['@name']
grouping = uses['@name']

leafs = sy.preProcessedYang['grouping'][uses_module_name][grouping]

leaf_model = self._get_model(leafs, token)
if leaf_model:
return leaf_model

return None

class TitledLogger(logger.Logger):
def __init__(self, syslog_identifier, title, verbose, print_all_to_console):
super().__init__(syslog_identifier)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"BGP_NEIGHBOR": {
"1.2.3.4": {
"admin_status": "up",
"asn": "65000",
"holdtime": "10",
"keepalive": "3",
"local_addr": "10.0.0.10",
"name": "ARISTA03T1",
"nhopself": "0",
"rrclient": "0"
},
"default|1.2.3.4": {
"local_asn": "65200",
"asn": "65100",
"name": "bgp peer 65100",
"ebgp_multihop_ttl": "3"
}
},
"BGP_MONITORS": {
"5.6.7.8": {
"admin_status": "up",
"asn": "65000",
"holdtime": "180",
"keepalive": "60",
"local_addr": "10.0.0.11",
"name": "BGPMonitor",
"nhopself": "0",
"rrclient": "0"
}
}
}
14 changes: 14 additions & 0 deletions tests/generic_config_updater/files/config_db_with_lldp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"LLDP": {
"GLOBAL": {
"mode": "TRANSMIT",
"enabled": "true",
"hello_time": "12",
"multiplier": "5",
"supp_mgmt_address_tlv": "true",
"supp_system_capabilities_tlv": "false",
"system_name": "sonic",
"system_description": "sonic-system"
}
}
}
24 changes: 24 additions & 0 deletions tests/generic_config_updater/gu_common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,18 @@ def check(path, xpath, config=None):
check(path="/PORTCHANNEL_INTERFACE/PortChannel0001|1.1.1.1~124",
xpath="/sonic-portchannel:sonic-portchannel/PORTCHANNEL_INTERFACE/PORTCHANNEL_INTERFACE_IPPREFIX_LIST[name='PortChannel0001'][ip_prefix='1.1.1.1/24']",
config=Files.CONFIG_DB_WITH_PORTCHANNEL_INTERFACE)
check(path="/BGP_NEIGHBOR/1.2.3.4/holdtime",
xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_TEMPLATE_LIST[neighbor='1.2.3.4']/holdtime",
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
check(path="/BGP_NEIGHBOR/default|1.2.3.4/asn",
xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_LIST[vrf_name='default'][neighbor='1.2.3.4']/asn",
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
check(path="/BGP_MONITORS/5.6.7.8/name",
xpath="/sonic-bgp-monitor:sonic-bgp-monitor/BGP_MONITORS/BGP_MONITORS_LIST[addr='5.6.7.8']/name",
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
check(path="/LLDP/GLOBAL/mode",
xpath="/sonic-lldp:sonic-lldp/LLDP/GLOBAL/mode",
config=Files.CONFIG_DB_WITH_LLDP)

def test_convert_xpath_to_path(self):
def check(xpath, path, config=None):
Expand Down Expand Up @@ -812,6 +824,18 @@ def check(xpath, path, config=None):
check(xpath="/sonic-portchannel:sonic-portchannel/PORTCHANNEL_INTERFACE/PORTCHANNEL_INTERFACE_IPPREFIX_LIST[name='PortChannel0001'][ip_prefix='1.1.1.1/24']",
path="/PORTCHANNEL_INTERFACE/PortChannel0001|1.1.1.1~124",
config=Files.CONFIG_DB_WITH_PORTCHANNEL_INTERFACE)
check(xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_TEMPLATE_LIST[neighbor='1.2.3.4']/holdtime",
path="/BGP_NEIGHBOR/1.2.3.4/holdtime",
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
check(xpath="/sonic-bgp-neighbor:sonic-bgp-neighbor/BGP_NEIGHBOR/BGP_NEIGHBOR_LIST[vrf_name='default'][neighbor='1.2.3.4']/asn",
path="/BGP_NEIGHBOR/default|1.2.3.4/asn",
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
check(xpath="/sonic-bgp-monitor:sonic-bgp-monitor/BGP_MONITORS/BGP_MONITORS_LIST[addr='5.6.7.8']/name",
path="/BGP_MONITORS/5.6.7.8/name",
config=Files.CONFIG_DB_WITH_BGP_NEIGHBOR)
check(xpath="/sonic-lldp:sonic-lldp/LLDP/GLOBAL/mode",
path="/LLDP/GLOBAL/mode",
config=Files.CONFIG_DB_WITH_LLDP)

def test_has_path(self):
def check(config, path, expected):
Expand Down

0 comments on commit 50bacce

Please sign in to comment.