From 5a11647335dc179834c55749b177bd1a47eff32a Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Tue, 3 Aug 2021 15:37:57 -0500 Subject: configquery: T3402: add op-mode get_config_dict --- python/vyos/configquery.py | 62 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/python/vyos/configquery.py b/python/vyos/configquery.py index 0f0bc2f79..6871602d5 100644 --- a/python/vyos/configquery.py +++ b/python/vyos/configquery.py @@ -18,8 +18,12 @@ A small library that allows querying existence or value(s) of config settings from op mode, and execution of arbitrary op mode commands. ''' +import re +import json from subprocess import STDOUT -from vyos.util import popen + +import vyos.util +import vyos.xml from vyos.configtree import ConfigTree class ConfigQueryError(Exception): @@ -51,7 +55,7 @@ class CliShellApiConfigQuery(GenericConfigQuery): def exists(self, path: list): cmd = ' '.join(path) - (_, err) = popen(f'cli-shell-api existsActive {cmd}') + (_, err) = vyos.util.popen(f'cli-shell-api existsActive {cmd}') if err: return False return True @@ -78,6 +82,8 @@ class ConfigTreeActiveQuery(GenericConfigQuery): config_file = f.read() self.configtree = ConfigTree(config_file) + self._level = [] + def exists(self, path: list): return self.configtree.exists(path) @@ -90,6 +96,58 @@ class ConfigTreeActiveQuery(GenericConfigQuery): def list_nodes(self, path: list): return self.configtree.list_nodes(path) + def _make_path(self, path): + # Backwards-compatibility stuff: original implementation used string paths + # libvyosconfig paths are lists, but since node names cannot contain whitespace, + # splitting at whitespace is reasonably safe. + # It may cause problems with exists() when it's used for checking values, + # since values may contain whitespace. + if isinstance(path, str): + path = re.split(r'\s+', path) + elif isinstance(path, list): + pass + else: + raise TypeError("Path must be a whitespace-separated string or a list") + return (self._level + path) + + def get_config_dict(self, path=[], key_mangling=None, + get_first_key=False, no_multi_convert=False, + no_tag_node_value_mangle=False): + """ + Args: + path (str list): Configuration tree path, can be empty + key_mangling=None: mangle dict keys according to regex and replacement + get_first_key=False: if k = path[:-1], return sub-dict d[k] instead of {k: d[k]} + no_multi_convert=False: if convert, return single value of multi node as list + + Returns: a dict representation of the config under path + """ + lpath = self._make_path(path) + root_dict = json.loads(self.configtree.to_json()) + conf_dict = vyos.util.get_sub_dict(root_dict, lpath, get_first_key) + + if not key_mangling and no_multi_convert: + return deepcopy(conf_dict) + + xmlpath = lpath if get_first_key else lpath[:-1] + + if not key_mangling: + conf_dict = vyos.xml.multi_to_list(xmlpath, conf_dict) + return conf_dict + + if no_multi_convert is False: + conf_dict = multi_to_list(xmlpath, conf_dict) + + if not (isinstance(key_mangling, tuple) and \ + (len(key_mangling) == 2) and \ + isinstance(key_mangling[0], str) and \ + isinstance(key_mangling[1], str)): + raise ValueError("key_mangling must be a tuple of two strings") + + conf_dict = vyos.util.mangle_dict_keys(conf_dict, key_mangling[0], key_mangling[1], abs_path=xmlpath, no_tag_node_value_mangle=no_tag_node_value_mangle) + + return conf_dict + class VbashOpRun(GenericOpRun): def __init__(self): super().__init__() -- cgit v1.2.3