summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2026-02-13 08:48:39 -0600
committerGitHub <noreply@github.com>2026-02-13 08:48:39 -0600
commitb7b424e7ca181403e20bed502603f767f36f1024 (patch)
tree528b54bad0d2f9efea8b76499b961fdeb4e34a99
parentd6fdf9db8497164921afb28abc22f1c7bcd18f52 (diff)
parentcf5cc40c379e4e032781907e584b8695fd1faf4b (diff)
downloadvyos-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/Makefile4
-rw-r--r--libvyosconfig/lib/bindings.ml37
-rw-r--r--python/vyos/configtree.py46
-rw-r--r--python/vyos/defaults.py2
-rw-r--r--python/vyos/referencetree.py81
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()