diff options
| author | John Estabrook <jestabro@vyos.io> | 2026-02-13 08:48:39 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-13 08:48:39 -0600 |
| commit | b7b424e7ca181403e20bed502603f767f36f1024 (patch) | |
| tree | 528b54bad0d2f9efea8b76499b961fdeb4e34a99 | |
| parent | d6fdf9db8497164921afb28abc22f1c7bcd18f52 (diff) | |
| parent | cf5cc40c379e4e032781907e584b8695fd1faf4b (diff) | |
| download | vyos-1x-b7b424e7ca181403e20bed502603f767f36f1024.tar.gz vyos-1x-b7b424e7ca181403e20bed502603f767f36f1024.zip | |
Merge pull request #4971 from jestabro/config-dict
T8232: Simplify and extend config_dict construction
| -rw-r--r-- | libvyosconfig/Makefile | 4 | ||||
| -rw-r--r-- | libvyosconfig/lib/bindings.ml | 37 | ||||
| -rw-r--r-- | python/vyos/configtree.py | 46 | ||||
| -rw-r--r-- | python/vyos/defaults.py | 2 | ||||
| -rw-r--r-- | python/vyos/referencetree.py | 81 |
5 files changed, 159 insertions, 11 deletions
diff --git a/libvyosconfig/Makefile b/libvyosconfig/Makefile index c93f26118..db67de62a 100644 --- a/libvyosconfig/Makefile +++ b/libvyosconfig/Makefile @@ -42,8 +42,8 @@ all: sharedlib PHONY: depends depends: sudo sh -c 'eval $$(opam env --root=/opt/opam --set-root) ;\ - opam pin add vyos1x-config https://github.com/vyos/vyos1x-config.git#6cf3918875b11dd94f9dc380b4083cc4258da74c -y ; \ - opam pin add vyconf https://github.com/vyos/vyconf.git#ee86cf713f45d3bcd735440f432b5b7b57a31ca8 -y' + opam pin add vyos1x-config https://github.com/vyos/vyos1x-config.git#6ebd5d80f9a212beba6352494392ae811f66d64e -y ; \ + opam pin add vyconf https://github.com/vyos/vyconf.git#2c16a5ad7171362d71ff241adab873fa3752b784 -y' sharedlib: depends $(BUILDDIR)/libvyosconfig$(EXTDLL) diff --git a/libvyosconfig/lib/bindings.ml b/libvyosconfig/lib/bindings.ml index 3510a9016..5d0478e02 100644 --- a/libvyosconfig/lib/bindings.ml +++ b/libvyosconfig/lib/bindings.ml @@ -12,6 +12,7 @@ module RT = Reference_tree module TA = Tree_alg module CM = Commit module VC = Vycall_client +module CDict = Config_dict module I = Internal.Make(Config_tree) module IR = Internal.Make(Reference_tree) @@ -94,6 +95,31 @@ let write_internal c_ptr file = with Internal.Write_error msg -> error_message := msg +let render_json_reference_tree c_ptr = + RT.render_json (Root.get c_ptr) + +let read_internal_reference_tree file = + (* alert exn Internal.read_internal: + [Internal.Read_error] caught + *) + try + error_message := ""; + let rt = (IR.read_internal[@alert "-exn"]) file in + Ctypes.Root.create rt + with Internal.Read_error msg -> + error_message := msg; Ctypes.null + +let write_internal_reference_tree c_ptr file = + (* alert exn Internal.write_internal: + [Internal.Write_error] caught + *) + try + error_message := ""; + let ct = Root.get c_ptr in + (IR.write_internal[@alert "-exn"]) ct file + with Internal.Write_error msg -> + error_message := msg + let create_node c_ptr path = (* alert exn CT.create_node: [Vytree.Empty_path] caught @@ -465,6 +491,13 @@ let validate_tree_filter c_ptr rt_cache_path validator_dir = with Internal.Read_error msg -> error_message := msg; c_ptr +let config_dict c_ptr_c c_ptr_r c_ptr_m path get_node with_defaults = + let ct = Root.get c_ptr_c in + let rt = Root.get c_ptr_r in + let mask = Root.get c_ptr_m in + let path = split_on_whitespace path in + let with_node = not get_node in + CDict.config_dict ~with_defaults:with_defaults ~with_first_node:with_node rt ct mask path module Stubs(I : Cstubs_inverted.INTERNAL) = struct @@ -480,6 +513,9 @@ struct let () = I.internal "to_commands" ((ptr void) @-> string @-> returning string) render_commands let () = I.internal "read_internal" (string @-> returning (ptr void)) read_internal let () = I.internal "write_internal" ((ptr void) @-> string @-> returning void) write_internal + let () = I.internal "to_json_reference_tree" ((ptr void) @-> returning string) render_json_reference_tree + let () = I.internal "read_internal_reference_tree" (string @-> returning (ptr void)) read_internal_reference_tree + let () = I.internal "write_internal_reference_tree" ((ptr void) @-> string @-> returning void) write_internal_reference_tree let () = I.internal "create_node" ((ptr void) @-> string @-> returning int) create_node let () = I.internal "set_add_value" ((ptr void) @-> string @-> string @-> returning int) set_add_value let () = I.internal "set_replace_value" ((ptr void) @-> string @-> string @-> returning int) set_replace_value @@ -505,4 +541,5 @@ struct let () = I.internal "reference_tree_to_json" (string @-> string @-> string @-> returning int) reference_tree_to_json let () = I.internal "mask_tree" ((ptr void) @-> (ptr void) @-> returning (ptr void)) mask_tree let () = I.internal "validate_tree_filter" ((ptr void) @-> string @-> string @-> returning (ptr void)) validate_tree_filter + let () = I.internal "config_dict" ((ptr void) @-> (ptr void) @-> (ptr void) @-> string @-> bool @-> bool @-> returning string) config_dict end diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index e57cd5205..afe1d7fe4 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -188,6 +188,17 @@ class ConfigTree(object): self.__equal.argtypes = [c_void_p, c_void_p] self.__equal.restype = c_bool + self.__config_dict = self.__lib.config_dict + self.__config_dict.argtypes = [ + c_void_p, + c_void_p, + c_void_p, + c_char_p, + c_bool, + c_bool, + ] + self.__config_dict.restype = c_char_p + if address is not None: self.__config = address self.__version = '' @@ -224,20 +235,20 @@ class ConfigTree(object): def __eq__(self, other): if isinstance(other, ConfigTree): - return self.__equal(self._get_config(), other._get_config()) + return self.__equal(self.get_tree(), other.get_tree()) return False def __str__(self): return self.to_string() - def _get_config(self): + def get_tree(self): return self.__config def get_version_string(self): return self.__version def write_cache(self, file_name): - self.__write_internal(self._get_config(), file_name.encode()) + self.__write_internal(self.get_tree(), file_name.encode()) def to_string(self, ordered_values=False, no_version=False): config_string = self.__to_string(self.__config, ordered_values).decode() @@ -472,6 +483,23 @@ class ConfigTree(object): subt = ConfigTree(address=res) return subt + def config_dict( + self, ref_tree, path, mask, get_first_key=False, with_defaults=False + ): + check_path(path) + path_str = ' '.join(map(str, path)).encode() + + res_json = self.__config_dict( + self.__config, + ref_tree.get_tree(), + mask.get_tree(), + path_str, + get_first_key, + with_defaults, + ).decode() + res = json.loads(res_json) + return res + def diff_compare(left, right, path=[], commands=False, libpath=LIBPATH): if left is None: @@ -495,7 +523,7 @@ def diff_compare(left, right, path=[], commands=False, libpath=LIBPATH): __get_error.argtypes = [] __get_error.restype = c_char_p - res = __diff_compare(commands, path_str, left._get_config(), right._get_config()) + res = __diff_compare(commands, path_str, left.get_tree(), right.get_tree()) res = res.decode() if res == '#1@': msg = __get_error().decode() @@ -521,7 +549,7 @@ def union(left, right, libpath=LIBPATH): __get_error.argtypes = [] __get_error.restype = c_char_p - res = __tree_union(left._get_config(), right._get_config()) + res = __tree_union(left.get_tree(), right.get_tree()) tree = ConfigTree(address=res) return tree @@ -543,7 +571,7 @@ def merge(left, right, destructive=False, libpath=LIBPATH): __get_error.argtypes = [] __get_error.restype = c_char_p - res = __tree_merge(destructive, left._get_config(), right._get_config()) + res = __tree_merge(destructive, left.get_tree(), right.get_tree()) tree = ConfigTree(address=res) return tree @@ -562,7 +590,7 @@ def mask_inclusive(left, right, libpath=LIBPATH): __get_error.argtypes = [] __get_error.restype = c_char_p - res = __mask_tree(left._get_config(), right._get_config()) + res = __mask_tree(left.get_tree(), right.get_tree()) except Exception as e: raise ConfigTreeError(e) if not res: @@ -657,7 +685,7 @@ def validate_tree_filter( __get_error.argtypes = [] __get_error.restype = c_char_p res = __validate_tree_filter( - config_tree._get_config(), cache_path.encode(), validator_dir.encode() + config_tree.get_tree(), cache_path.encode(), validator_dir.encode() ) except Exception as e: raise ConfigTreeError(e) @@ -706,7 +734,7 @@ class DiffTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__diff_tree(path_str, left._get_config(), right._get_config()) + res = self.__diff_tree(path_str, left.get_tree(), right.get_tree()) # full diff config_tree and python dict representation self.full = ConfigTree(address=res) diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 833ac1fbf..fbaf18919 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -99,3 +99,5 @@ SSH_DSA_DEPRECATION_WARNING: str = \ 'Support for SSH-DSA keys is deprecated and will be removed in VyOS 1.6. ' \ 'Please update affected keys to a supported algorithm (e.g., RSA, ECDSA or ' \ 'ED25519) to avoid authentication failures after the upgrade.' + +reference_tree_cache = '/usr/share/vyos/reftree.cache' diff --git a/python/vyos/referencetree.py b/python/vyos/referencetree.py new file mode 100644 index 000000000..87ed310b4 --- /dev/null +++ b/python/vyos/referencetree.py @@ -0,0 +1,81 @@ +# Copyright 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/>. + +from ctypes import cdll, c_char_p, c_void_p, c_bool + +from vyos.defaults import reference_tree_cache + +LIBPATH = '/usr/lib/libvyosconfig.so.0' + + +class ReferenceTreeError(Exception): + pass + + +class ReferenceTree: + # pylint: disable=too-many-instance-attributes + def __init__(self, cache_file=reference_tree_cache, libpath=LIBPATH): + self.__pointer = None + self.__lib = cdll.LoadLibrary(libpath) + + # Import functions + self.__get_error = self.__lib.get_error + self.__get_error.argtypes = [] + self.__get_error.restype = c_char_p + + self.__read_internal = self.__lib.read_internal_reference_tree + self.__read_internal.argtypes = [c_char_p] + self.__read_internal.restype = c_void_p + + self.__write_internal = self.__lib.write_internal_reference_tree + self.__write_internal.argtypes = [c_void_p, c_char_p] + + self.__to_json = self.__lib.to_json_reference_tree + self.__to_json.argtypes = [c_void_p] + self.__to_json.restype = c_char_p + + self.__destroy = self.__lib.destroy + self.__destroy.argtypes = [c_void_p] + + self.__equal = self.__lib.equal + self.__equal.argtypes = [c_void_p, c_void_p] + self.__equal.restype = c_bool + + pointer = self.__read_internal(cache_file.encode()) + if pointer is None: + msg = self.__get_error().decode() + raise ValueError(f'Failed to read internal rep: {msg}') + self.__pointer = pointer + + def __del__(self): + if self.__pointer is not None: + self.__destroy(self.__pointer) + + def __eq__(self, other): + if isinstance(other, ReferenceTree): + return self.__equal(self.get_tree(), other.get_tree()) + return False + + def __str__(self): + return self.to_json() + + def get_tree(self): + return self.__pointer + + def write_cache(self, file_name): + self.__write_internal(self.get_tree(), file_name.encode()) + + def to_json(self): + return self.__to_json(self.__pointer).decode() |
