Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F119063247
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py
new file mode 100644
index 000000000..50222e385
--- /dev/null
+++ b/python/vyos/configsource.py
@@ -0,0 +1,318 @@
+
+# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import re
+import subprocess
+
+from vyos.configtree import ConfigTree
+
+class VyOSError(Exception):
+ """
+ Raised on config access errors.
+ """
+ pass
+
+class ConfigSourceError(Exception):
+ '''
+ Raised on error in ConfigSource subclass init.
+ '''
+ pass
+
+class ConfigSource:
+ def __init__(self):
+ self._running_config: ConfigTree = None
+ self._session_config: ConfigTree = None
+
+ def get_configtree_tuple(self):
+ return self._running_config, self._session_config
+
+ def session_changed(self):
+ """
+ Returns:
+ True if the config session has uncommited changes, False otherwise.
+ """
+ raise NotImplementedError(f"function not available for {type(self)}")
+
+ def in_session(self):
+ """
+ Returns:
+ True if called from a configuration session, False otherwise.
+ """
+ raise NotImplementedError(f"function not available for {type(self)}")
+
+ def show_config(self, path=[], default=None, effective=False):
+ """
+ Args:
+ path (str|list): Configuration tree path, or empty
+ default (str): Default value to return
+
+ Returns:
+ str: working configuration
+ """
+ raise NotImplementedError(f"function not available for {type(self)}")
+
+ def is_multi(self, path):
+ """
+ Args:
+ path (str): Configuration tree path
+
+ Returns:
+ True if a node can have multiple values, False otherwise.
+
+ Note:
+ It also returns False if node doesn't exist.
+ """
+ raise NotImplementedError(f"function not available for {type(self)}")
+
+ def is_tag(self, path):
+ """
+ Args:
+ path (str): Configuration tree path
+
+ Returns:
+ True if a node is a tag node, False otherwise.
+
+ Note:
+ It also returns False if node doesn't exist.
+ """
+ raise NotImplementedError(f"function not available for {type(self)}")
+
+ def is_leaf(self, path):
+ """
+ Args:
+ path (str): Configuration tree path
+
+ Returns:
+ True if a node is a leaf node, False otherwise.
+
+ Note:
+ It also returns False if node doesn't exist.
+ """
+ raise NotImplementedError(f"function not available for {type(self)}")
+
+class ConfigSourceSession(ConfigSource):
+ def __init__(self, session_env=None):
+ super().__init__()
+ self._cli_shell_api = "/bin/cli-shell-api"
+ self._level = []
+ 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 = ''
+ else:
+ running_config_text = ''
+
+ # 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 = ConfigTree(running_config_text)
+ else:
+ self._running_config = None
+
+ if session_config_text:
+ self._session_config = 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
+
+ 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.
+ Once set, all operations will be relative to this path,
+ for example, after ``set_level("system")``, calling
+ ``exists("name-server")`` is equivalent to calling
+ ``exists("system name-server"`` without ``set_level``.
+
+ Args:
+ path (str|list): relative config path
+ """
+ # Make sure there's always a space between default path (level)
+ # and path supplied as method argument
+ # XXX: for small strings in-place concatenation is not a problem
+ if isinstance(path, str):
+ if path:
+ self._level = re.split(r'\s+', path)
+ else:
+ self._level = []
+ elif isinstance(path, list):
+ self._level = path.copy()
+ else:
+ raise TypeError("Level path must be either a whitespace-separated string or a list")
+
+ def session_changed(self):
+ """
+ Returns:
+ True if the config session has uncommited changes, False otherwise.
+ """
+ try:
+ self._run(self._make_command('sessionChanged', ''))
+ return True
+ except VyOSError:
+ return False
+
+ 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
+
+ def show_config(self, path=[], default=None, effective=False):
+ """
+ Args:
+ path (str|list): Configuration tree path, or empty
+ default (str): Default value to return
+
+ Returns:
+ str: working configuration
+ """
+
+ # 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
+
+ # 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
+ 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)
+
+ def is_multi(self, path):
+ """
+ Args:
+ path (str): Configuration tree path
+
+ Returns:
+ True if a node can have multiple values, False otherwise.
+
+ 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
+
+ def is_tag(self, path):
+ """
+ Args:
+ path (str): Configuration tree path
+
+ Returns:
+ True if a node is a tag node, False otherwise.
+
+ 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
+
+ def is_leaf(self, path):
+ """
+ Args:
+ path (str): Configuration tree path
+
+ Returns:
+ True if a node is a leaf node, False otherwise.
+
+ 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
+
+class ConfigSourceString(ConfigSource):
+ def __init__(self, running_config_text=None, session_config_text=None):
+ super().__init__()
+
+ try:
+ self._running_config = ConfigTree(running_config_text) if running_config_text else None
+ self._session_config = ConfigTree(session_config_text) if session_config_text else None
+ except ValueError:
+ raise ConfigSourceError(f"Init error in {type(self)}")
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 6, 8:18 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4653384
Default Alt Text
(10 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment