summaryrefslogtreecommitdiff
path: root/python/vyos/config.py
diff options
context:
space:
mode:
authorMarcus Hoff <marcus.hoff@ring2.dk>2020-09-05 09:58:03 +0200
committerMarcus Hoff <marcus.hoff@ring2.dk>2020-09-05 09:58:03 +0200
commit46fb580fa0131f6815bbcfc95631654f6fe999a8 (patch)
tree73ae9fcaa97d5cfab7883bc6fbf3ea036677c2a3 /python/vyos/config.py
parent0377b8e40b0d3e424da11194e97659c5066c0a1d (diff)
parentb6b61bc9ecf1328e67a0c15934f8bf3966a6b66d (diff)
downloadvyos-1x-46fb580fa0131f6815bbcfc95631654f6fe999a8.tar.gz
vyos-1x-46fb580fa0131f6815bbcfc95631654f6fe999a8.zip
Merge remote-tracking branch 'upstream/current' into current
Diffstat (limited to 'python/vyos/config.py')
-rw-r--r--python/vyos/config.py209
1 files changed, 55 insertions, 154 deletions
diff --git a/python/vyos/config.py b/python/vyos/config.py
index 5d58316e7..de79a3654 100644
--- a/python/vyos/config.py
+++ b/python/vyos/config.py
@@ -63,23 +63,14 @@ In operational mode, all functions return values from the running config.
"""
-import os
import re
import json
-import subprocess
from copy import deepcopy
+import vyos.xml
import vyos.util
import vyos.configtree
-
-class VyOSError(Exception):
- """
- Raised on config access errors, most commonly if the type of a config tree node
- in the system does not match the type of operation.
-
- """
- pass
-
+from vyos.configsource import ConfigSource, ConfigSourceSession
class Config(object):
"""
@@ -89,51 +80,18 @@ class Config(object):
the only state it keeps is relative *config path* for convenient access to config
subtrees.
"""
- def __init__(self, session_env=None):
- self._cli_shell_api = "/bin/cli-shell-api"
- self._level = []
- self._dict_cache = {}
-
- if session_env:
- self.__session_env = session_env
- else:
- self.__session_env = None
-
- # Running config can be obtained either from op or conf mode, it always succeeds
- # once the config system is initialized during boot;
- # before initialization, set to empty string
- if os.path.isfile('/tmp/vyos-config-status'):
- try:
- running_config_text = self._run([self._cli_shell_api, '--show-active-only', '--show-show-defaults', '--show-ignore-edit', 'showConfig'])
- except VyOSError:
- running_config_text = ''
+ def __init__(self, session_env=None, config_source=None):
+ if config_source is None:
+ self._config_source = ConfigSourceSession(session_env)
else:
- running_config_text = ''
+ if not isinstance(config_source, ConfigSource):
+ raise TypeError("config_source not of type ConfigSource")
+ self._config_source = config_source
- # Session config ("active") only exists in conf mode.
- # In op mode, we'll just use the same running config for both active and session configs.
- if self.in_session():
- try:
- session_config_text = self._run([self._cli_shell_api, '--show-working-only', '--show-show-defaults', '--show-ignore-edit', 'showConfig'])
- except VyOSError:
- session_config_text = ''
- else:
- session_config_text = running_config_text
-
- if running_config_text:
- self._running_config = vyos.configtree.ConfigTree(running_config_text)
- else:
- self._running_config = None
-
- if session_config_text:
- self._session_config = vyos.configtree.ConfigTree(session_config_text)
- else:
- self._session_config = None
-
- def _make_command(self, op, path):
- args = path.split()
- cmd = [self._cli_shell_api, op] + args
- return cmd
+ self._level = []
+ self._dict_cache = {}
+ (self._running_config,
+ self._session_config) = self._config_source.get_configtree_tuple()
def _make_path(self, path):
# Backwards-compatibility stuff: original implementation used string paths
@@ -149,19 +107,6 @@ class Config(object):
raise TypeError("Path must be a whitespace-separated string or a list")
return (self._level + path)
- def _run(self, cmd):
- if self.__session_env:
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=self.__session_env)
- else:
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
- out = p.stdout.read()
- p.wait()
- p.communicate()
- if p.returncode != 0:
- raise VyOSError()
- else:
- return out.decode('ascii')
-
def set_level(self, path):
"""
Set the *edit level*, that is, a relative config tree path.
@@ -229,22 +174,14 @@ class Config(object):
Returns:
True if the config session has uncommited changes, False otherwise.
"""
- try:
- self._run(self._make_command('sessionChanged', ''))
- return True
- except VyOSError:
- return False
+ return self._config_source.session_changed()
def in_session(self):
"""
Returns:
True if called from a configuration session, False otherwise.
"""
- try:
- self._run(self._make_command('inSession', ''))
- return True
- except VyOSError:
- return False
+ return self._config_source.in_session()
def show_config(self, path=[], default=None, effective=False):
"""
@@ -255,85 +192,64 @@ class Config(object):
Returns:
str: working configuration
"""
+ return self._config_source.show_config(path, default, effective)
- # show_config should be independent of CLI edit level.
- # Set the CLI edit environment to the top level, and
- # restore original on exit.
- save_env = self.__session_env
-
- env_str = self._run(self._make_command('getEditResetEnv', ''))
- env_list = re.findall(r'([A-Z_]+)=\'([^;\s]+)\'', env_str)
- root_env = os.environ
- for k, v in env_list:
- root_env[k] = v
-
- self.__session_env = root_env
+ def get_cached_root_dict(self, effective=False):
+ cached = self._dict_cache.get(effective, {})
+ if cached:
+ return cached
- # FIXUP: by default, showConfig will give you a diff
- # if there are uncommitted changes.
- # The config parser obviously cannot work with diffs,
- # so we need to supress diff production using appropriate
- # options for getting either running (active)
- # or proposed (working) config.
if effective:
- path = ['--show-active-only'] + path
+ config = self._running_config
else:
- path = ['--show-working-only'] + path
-
- if isinstance(path, list):
- path = " ".join(path)
- try:
- out = self._run(self._make_command('showConfig', path))
- self.__session_env = save_env
- return out
- except VyOSError:
- self.__session_env = save_env
- return(default)
+ config = self._session_config
- def get_cached_dict(self, effective=False):
- cached = self._dict_cache.get(effective, {})
- if cached:
- config_dict = cached
+ if config:
+ config_dict = json.loads(config.to_json())
else:
config_dict = {}
- if effective:
- if self._running_config:
- config_dict = json.loads((self._running_config).to_json())
- else:
- if self._session_config:
- config_dict = json.loads((self._session_config).to_json())
-
- self._dict_cache[effective] = config_dict
+ self._dict_cache[effective] = config_dict
return config_dict
- def get_config_dict(self, path=[], effective=False, key_mangling=None, get_first_key=False):
+ def get_config_dict(self, path=[], effective=False, key_mangling=None,
+ get_first_key=False, no_multi_convert=False):
"""
Args:
path (str list): Configuration tree path, can be empty
effective=False: effective or session config
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
"""
- config_dict = self.get_cached_dict(effective)
+ lpath = self._make_path(path)
+ root_dict = self.get_cached_root_dict(effective)
+ conf_dict = vyos.util.get_sub_dict(root_dict, lpath, get_first_key)
- config_dict = vyos.util.get_sub_dict(config_dict, self._make_path(path), get_first_key)
+ if not key_mangling and no_multi_convert:
+ return deepcopy(conf_dict)
- if key_mangling:
- 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")
- else:
- config_dict = vyos.util.mangle_dict_keys(config_dict, key_mangling[0], key_mangling[1])
- else:
- config_dict = deepcopy(config_dict)
+ xmlpath = lpath if get_first_key else lpath[:-1]
- return config_dict
+ 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 = vyos.xml.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])
+
+ return conf_dict
def is_multi(self, path):
"""
@@ -346,12 +262,8 @@ class Config(object):
Note:
It also returns False if node doesn't exist.
"""
- try:
- path = " ".join(self._level) + " " + path
- self._run(self._make_command('isMulti', path))
- return True
- except VyOSError:
- return False
+ self._config_source.set_level(self.get_level)
+ return self._config_source.is_multi(path)
def is_tag(self, path):
"""
@@ -364,12 +276,8 @@ class Config(object):
Note:
It also returns False if node doesn't exist.
"""
- try:
- path = " ".join(self._level) + " " + path
- self._run(self._make_command('isTag', path))
- return True
- except VyOSError:
- return False
+ self._config_source.set_level(self.get_level)
+ return self._config_source.is_tag(path)
def is_leaf(self, path):
"""
@@ -382,12 +290,8 @@ class Config(object):
Note:
It also returns False if node doesn't exist.
"""
- try:
- path = " ".join(self._level) + " " + path
- self._run(self._make_command('isLeaf', path))
- return True
- except VyOSError:
- return False
+ self._config_source.set_level(self.get_level)
+ return self._config_source.is_leaf(path)
def return_value(self, path, default=None):
"""
@@ -548,9 +452,6 @@ class Config(object):
Returns:
str list: child node names
-
- Raises:
- VyOSError: if the node is not a tag node
"""
if self._running_config:
try: