summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/vyos/configtree.py121
-rw-r--r--python/vyos/derivedtree.py56
2 files changed, 170 insertions, 7 deletions
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py
index 61ef6428c..d92137c14 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -18,6 +18,12 @@ import json
import logging
from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool
+from typing import TYPE_CHECKING
+
+# https://peps.python.org/pep-0484/#forward-references
+# for type 'ConfigDict'
+if TYPE_CHECKING:
+ from vyos.referencetree import ReferenceTree
BUILD_PATH = '/tmp/libvyosconfig/_build/libvyosconfig.so'
INSTALL_PATH = '/usr/lib/libvyosconfig.so.0'
@@ -67,11 +73,21 @@ class ConfigTreeError(Exception):
class ConfigTree(object):
def __init__(
- self, config_string=None, address=None, internal=None, libpath=LIBPATH
+ self,
+ config_string=None,
+ address=None,
+ internal=None,
+ internal_string=None,
+ libpath=LIBPATH,
):
- if config_string is None and address is None and internal is None:
+ if (
+ config_string is None
+ and address is None
+ and internal is None
+ and internal_string is None
+ ):
raise TypeError(
- "ConfigTree() requires one of 'config_string', 'address', or 'internal'"
+ "ConfigTree() requires one of 'config_string', 'address', 'internal', or 'internal_string'"
)
self.__config = None
@@ -101,6 +117,14 @@ class ConfigTree(object):
self.__write_internal = self.__lib.write_internal
self.__write_internal.argtypes = [c_void_p, c_char_p]
+ self.__read_internal_string = self.__lib.read_internal_string
+ self.__read_internal_string.argtypes = [c_char_p]
+ self.__read_internal_string.restype = c_void_p
+
+ self.__write_internal_string = self.__lib.write_internal_string
+ self.__write_internal_string.argtypes = [c_void_p]
+ self.__write_internal_string.restype = c_char_p
+
self.__to_json = self.__lib.to_json
self.__to_json.argtypes = [c_void_p]
self.__to_json.restype = c_char_p
@@ -206,7 +230,19 @@ class ConfigTree(object):
config = self.__read_internal(internal.encode())
if config is None:
msg = self.__get_error().decode()
- raise ValueError('Failed to read internal rep: {0}'.format(msg))
+ raise ValueError(
+ f'Failed to read internal representation from file {internal}: {msg}'
+ )
+ else:
+ self.__config = config
+ self.__version = ''
+ elif internal_string is not None:
+ config = self.__read_internal_string(internal_string.encode())
+ if config is None:
+ msg = self.__get_error().decode()
+ raise ValueError(
+ f'Failed to read internal representation from string: {msg}'
+ )
else:
self.__config = config
self.__version = ''
@@ -216,7 +252,7 @@ class ConfigTree(object):
config = self.__from_string(config_section.encode())
if config is None:
msg = self.__get_error().decode()
- raise ValueError('Failed to parse config: {0}'.format(msg))
+ raise ValueError(f'Failed to parse config: {msg}')
else:
self.__config = config
self.__version = version_section
@@ -250,6 +286,10 @@ class ConfigTree(object):
def write_cache(self, file_name):
self.__write_internal(self.get_tree(), file_name.encode())
+ def write_internal_string(self) -> str:
+ res = self.__write_internal_string(self.get_tree())
+ return res.decode()
+
def to_string(self, ordered_values=False, no_version=False):
config_string = self.__to_string(self.__config, ordered_values).decode()
config_string = unescape_backslash(config_string)
@@ -584,13 +624,80 @@ def mask_inclusive(left, right, libpath=LIBPATH):
try:
__lib = cdll.LoadLibrary(libpath)
__mask_tree = __lib.mask_tree
- __mask_tree.argtypes = [c_void_p, c_void_p]
+ __mask_tree.argtypes = [c_void_p, c_void_p, c_bool]
+ __mask_tree.restype = c_void_p
+ __get_error = __lib.get_error
+ __get_error.argtypes = []
+ __get_error.restype = c_char_p
+
+ res = __mask_tree(left.get_tree(), right.get_tree(), False)
+ except Exception as e:
+ raise ConfigTreeError(e)
+ if not res:
+ msg = __get_error().decode()
+ raise ConfigTreeError(msg)
+
+ tree = ConfigTree(address=res)
+
+ return tree
+
+
+def mask_exclusive(left, right, libpath=LIBPATH):
+ if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)):
+ raise TypeError('Arguments must be instances of ConfigTree')
+
+ try:
+ __lib = cdll.LoadLibrary(libpath)
+ __mask_tree = __lib.mask_tree
+ __mask_tree.argtypes = [c_void_p, c_void_p, c_bool]
__mask_tree.restype = c_void_p
__get_error = __lib.get_error
__get_error.argtypes = []
__get_error.restype = c_char_p
- res = __mask_tree(left.get_tree(), right.get_tree())
+ res = __mask_tree(left.get_tree(), right.get_tree(), True)
+ except Exception as e:
+ raise ConfigTreeError(e)
+ if not res:
+ msg = __get_error().decode()
+ raise ConfigTreeError(msg)
+
+ tree = ConfigTree(address=res)
+
+ return tree
+
+
+def subtree_from_partial(
+ config_tree: ConfigTree,
+ path: list[str],
+ reference_tree: 'ReferenceTree',
+ start: ConfigTree = None,
+ libpath=LIBPATH,
+):
+ if start:
+ if not isinstance(start, ConfigTree):
+ raise TypeError("Argument 'start' must be an instance of ConfigTree")
+ else:
+ start = ConfigTree('')
+
+ check_path(path)
+ path_str = ' '.join(map(str, path)).encode()
+
+ try:
+ __lib = cdll.LoadLibrary(libpath)
+ __subtree_from_partial = __lib.subtree_from_partial
+ __subtree_from_partial.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p]
+ __subtree_from_partial.restype = c_void_p
+ __get_error = __lib.get_error
+ __get_error.argtypes = []
+ __get_error.restype = c_char_p
+
+ res = __subtree_from_partial(
+ reference_tree.get_tree(),
+ config_tree.get_tree(),
+ start.get_tree(),
+ path_str,
+ )
except Exception as e:
raise ConfigTreeError(e)
if not res:
diff --git a/python/vyos/derivedtree.py b/python/vyos/derivedtree.py
new file mode 100644
index 000000000..0a57921b2
--- /dev/null
+++ b/python/vyos/derivedtree.py
@@ -0,0 +1,56 @@
+# Copyright (C) VyOS Inc.
+#
+# 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/>.
+
+
+from vyos.referencetree import ReferenceTree
+from vyos.configtree import ConfigTree
+from vyos.configtree import ConfigTreeError
+from vyos.configtree import subtree_from_partial
+
+
+class DerivedTreeError(Exception):
+ """Error to be raised by functions of derivedtree"""
+
+
+def subtree_from_list_of_partial_paths(
+ ctree: ConfigTree, paths: list[list[str]], accumulator: ConfigTree = None
+):
+ """Return the union of subtrees of the ConfigTree argument matching each
+ of the 'partial' paths. A partial path is one that may or may not
+ contain intervening tag node values, in which case it will match for all
+ values that apply.
+
+ An existing subtree may be passed as the initial value of accumulator.
+ """
+ if accumulator:
+ if not isinstance(accumulator, ConfigTree):
+ raise TypeError("Argument 'accumulator' must be an instance of ConfigTree")
+ else:
+ accumulator = ConfigTree('')
+
+ rtree = ReferenceTree()
+
+ errors = []
+ for path in paths:
+ try:
+ accumulator = subtree_from_partial(ctree, path, rtree, accumulator)
+ except ConfigTreeError as e:
+ errors.append(str(e))
+ continue
+
+ if errors:
+ raise DerivedTreeError(f'Nonsensical paths: {errors}')
+
+ return accumulator