diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/vyos/configtree.py | 463 | ||||
| -rw-r--r-- | python/vyos/referencetree.py | 42 |
2 files changed, 190 insertions, 315 deletions
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index 6791b1c29..90e889692 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -18,6 +18,7 @@ import json import logging from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool +from functools import lru_cache from typing import TYPE_CHECKING from pathlib import Path @@ -31,6 +32,105 @@ BUILD_PATH = '/tmp/libvyosconfig/_build/libvyosconfig.so' INSTALL_PATH = '/usr/lib/libvyosconfig.so.0' LIBPATH = BUILD_PATH if os.path.isfile(BUILD_PATH) else INSTALL_PATH +# C symbol -> (argtypes, restype). A restype of None means "leave the ctypes +# default (c_int)", which is what these entry points relied on before. +_PROTOTYPES = { + 'from_string': ([c_char_p], c_void_p), + 'get_error': ([], c_char_p), + 'to_string': ([c_void_p, c_bool], c_char_p), + 'to_commands': ([c_void_p, c_char_p], c_char_p), + 'read_internal': ([c_char_p], c_void_p), + 'write_internal': ([c_void_p, c_char_p], None), + 'read_internal_string': ([c_char_p], c_void_p), + 'write_internal_string': ([c_void_p], c_char_p), + 'to_json': ([c_void_p], c_char_p), + 'to_json_ast': ([c_void_p], c_char_p), + 'create_node': ([c_void_p, c_char_p], c_int), + 'set_add_value': ([c_void_p, c_char_p, c_char_p], c_int), + 'delete_value': ([c_void_p, c_char_p, c_char_p], c_int), + 'delete_node': ([c_void_p, c_char_p], c_int), + 'rename_node': ([c_void_p, c_char_p, c_char_p], c_int), + 'copy_node': ([c_void_p, c_char_p, c_char_p], c_int), + 'set_replace_value': ([c_void_p, c_char_p, c_char_p], c_int), + 'set_valueless': ([c_void_p, c_char_p], c_int), + 'exists': ([c_void_p, c_char_p], c_int), + 'value_exists': ([c_void_p, c_char_p, c_char_p], c_int), + 'list_nodes': ([c_void_p, c_char_p], c_char_p), + 'return_value': ([c_void_p, c_char_p], c_char_p), + 'return_values': ([c_void_p, c_char_p], c_char_p), + 'is_tag': ([c_void_p, c_char_p], c_int), + 'set_tag': ([c_void_p, c_char_p, c_bool], c_int), + 'is_leaf': ([c_void_p, c_char_p], c_bool), + 'set_leaf': ([c_void_p, c_char_p, c_bool], c_int), + # third argument 'with_node' was passed unchecked before + 'get_subtree': ([c_void_p, c_char_p, c_bool], c_void_p), + 'destroy': ([c_void_p], None), + 'equal': ([c_void_p, c_void_p], c_bool), + 'config_dict': ( + [c_void_p, c_void_p, c_void_p, c_char_p, c_bool, c_bool], + c_char_p, + ), + 'diff_tree': ([c_char_p, c_void_p, c_void_p], c_void_p), + 'diff_compare': ([c_bool, c_char_p, c_void_p, c_void_p], c_char_p), + 'diff_show': ([c_void_p, c_void_p, c_void_p, c_char_p], c_char_p), + 'tree_union': ([c_void_p, c_void_p], c_void_p), + 'tree_merge': ([c_bool, c_void_p, c_void_p], c_void_p), + 'mask_inclusive': ([c_void_p, c_void_p], c_void_p), + 'mask_exclusive': ([c_void_p, c_void_p], c_void_p), + 'subtree_from_partial': ([c_void_p, c_void_p, c_void_p, c_char_p], c_void_p), + 'reference_tree_to_json': ([c_char_p, c_char_p, c_char_p], None), + 'merge_reference_tree_cache': ([c_char_p, c_char_p, c_char_p], None), + 'interface_definitions_to_cache': ([c_char_p, c_char_p], None), + 'reference_tree_cache_to_json': ([c_char_p, c_char_p], None), + # returns a tree pointer; without an explicit restype ctypes defaulted to + # c_int and truncated it to 32 bits + 'validate_tree_filter': ([c_void_p, c_char_p, c_char_p], c_void_p), + # vyos.referencetree + 'read_internal_string_reference_tree': ([c_char_p], c_void_p), + 'write_internal_reference_tree': ([c_void_p, c_char_p], None), + 'to_json_reference_tree': ([c_void_p], c_char_p), +} + + +class _Lib: + """Lazily resolving, prototype-declaring wrapper around libvyosconfig. + + A symbol is looked up and given its argtypes/restype on first use, then + cached as an instance attribute so __getattr__ is bypassed from then on. + Resolution stays lazy on purpose: not every build of the library exports + every entry point, and the previous per-instance binding code only failed + on symbols that were actually used. + """ + + def __init__(self, libpath): + self.__dict__['_lib'] = cdll.LoadLibrary(libpath) + + def __getattr__(self, name): + try: + argtypes, restype = _PROTOTYPES[name] + except KeyError: + raise AttributeError( + f'{name} is not a known libvyosconfig entry point' + ) from None + func = getattr(self._lib, name) + func.argtypes = argtypes + if restype is not None: + func.restype = restype + setattr(self, name, func) + return func + + +@lru_cache(maxsize=None) +def get_lib(libpath=LIBPATH): + """Load libvyosconfig once per process. + + Every ConfigTree/DiffTree used to re-open the library and re-assign all + argtypes/restypes in its constructor; the result is cached here instead. + Loading stays lazy so that importing this module does not require the + shared object to be present. + """ + return _Lib(libpath) + def replace_backslash(s, search, replace): """Modify quoted strings containing backslashes not of escape sequences""" @@ -95,137 +195,7 @@ class ConfigTree: arg = [(k, v) for (k, v) in zip(arg_names, args) if v is not None][0] self.__config = None - self.__lib = cdll.LoadLibrary(libpath) - - # Import functions - self.__from_string = self.__lib.from_string - self.__from_string.argtypes = [c_char_p] - self.__from_string.restype = c_void_p - - self.__get_error = self.__lib.get_error - self.__get_error.argtypes = [] - self.__get_error.restype = c_char_p - - self.__to_string = self.__lib.to_string - self.__to_string.argtypes = [c_void_p, c_bool] - self.__to_string.restype = c_char_p - - self.__to_commands = self.__lib.to_commands - self.__to_commands.argtypes = [c_void_p, c_char_p] - self.__to_commands.restype = c_char_p - - self.__read_internal = self.__lib.read_internal - self.__read_internal.argtypes = [c_char_p] - self.__read_internal.restype = c_void_p - - 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 - - self.__to_json_ast = self.__lib.to_json_ast - self.__to_json_ast.argtypes = [c_void_p] - self.__to_json_ast.restype = c_char_p - - self.__create_node = self.__lib.create_node - self.__create_node.argtypes = [c_void_p, c_char_p] - self.__create_node.restype = c_int - - self.__set_add_value = self.__lib.set_add_value - self.__set_add_value.argtypes = [c_void_p, c_char_p, c_char_p] - self.__set_add_value.restype = c_int - - self.__delete_value = self.__lib.delete_value - self.__delete_value.argtypes = [c_void_p, c_char_p, c_char_p] - self.__delete_value.restype = c_int - - self.__delete = self.__lib.delete_node - self.__delete.argtypes = [c_void_p, c_char_p] - self.__delete.restype = c_int - - self.__rename = self.__lib.rename_node - self.__rename.argtypes = [c_void_p, c_char_p, c_char_p] - self.__rename.restype = c_int - - self.__copy = self.__lib.copy_node - self.__copy.argtypes = [c_void_p, c_char_p, c_char_p] - self.__copy.restype = c_int - - self.__set_replace_value = self.__lib.set_replace_value - self.__set_replace_value.argtypes = [c_void_p, c_char_p, c_char_p] - self.__set_replace_value.restype = c_int - - self.__set_valueless = self.__lib.set_valueless - self.__set_valueless.argtypes = [c_void_p, c_char_p] - self.__set_valueless.restype = c_int - - self.__exists = self.__lib.exists - self.__exists.argtypes = [c_void_p, c_char_p] - self.__exists.restype = c_int - - self.__value_exists = self.__lib.value_exists - self.__value_exists.argtypes = [c_void_p, c_char_p, c_char_p] - self.__value_exists.restype = c_int - - self.__list_nodes = self.__lib.list_nodes - self.__list_nodes.argtypes = [c_void_p, c_char_p] - self.__list_nodes.restype = c_char_p - - self.__return_value = self.__lib.return_value - self.__return_value.argtypes = [c_void_p, c_char_p] - self.__return_value.restype = c_char_p - - self.__return_values = self.__lib.return_values - self.__return_values.argtypes = [c_void_p, c_char_p] - self.__return_values.restype = c_char_p - - self.__is_tag = self.__lib.is_tag - self.__is_tag.argtypes = [c_void_p, c_char_p] - self.__is_tag.restype = c_int - - self.__set_tag = self.__lib.set_tag - self.__set_tag.argtypes = [c_void_p, c_char_p, c_bool] - self.__set_tag.restype = c_int - - self.__is_leaf = self.__lib.is_leaf - self.__is_leaf.argtypes = [c_void_p, c_char_p] - self.__is_leaf.restype = c_bool - - self.__set_leaf = self.__lib.set_leaf - self.__set_leaf.argtypes = [c_void_p, c_char_p, c_bool] - self.__set_leaf.restype = c_int - - self.__get_subtree = self.__lib.get_subtree - self.__get_subtree.argtypes = [c_void_p, c_char_p] - self.__get_subtree.restype = c_void_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 - - 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 + self.__lib = get_lib(libpath) match arg: case ('address', address): @@ -236,18 +206,18 @@ class ConfigTree: cache_string = Path(internal).read_bytes() except OSError as e: raise ValueError(f'Failed to read internal cache file: {e}') - config = self.__read_internal_string(cache_string) + config = self.__lib.read_internal_string(cache_string) if config is None: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ValueError( f'Failed to read internal representation from file {internal}: {msg}' ) self.__config = config self.__version = '' case ('internal_string', internal_string): - config = self.__read_internal_string(internal_string.encode()) + config = self.__lib.read_internal_string(internal_string.encode()) if config is None: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ValueError( f'Failed to read internal representation from string: {msg}' ) @@ -256,9 +226,9 @@ class ConfigTree: case ('config_string', config_string): config_section, version_section = extract_version(config_string) config_section = escape_backslash(config_section) - config = self.__from_string(config_section.encode()) + config = self.__lib.from_string(config_section.encode()) if config is None: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ValueError(f'Failed to parse config: {msg}') self.__config = config self.__version = version_section @@ -286,11 +256,11 @@ class ConfigTree: def __del__(self): if self.__config is not None: - self.__destroy(self.__config) + self.__lib.destroy(self.__config) def __eq__(self, other): if isinstance(other, ConfigTree): - return self.__equal(self.get_tree(), other.get_tree()) + return self.__lib.equal(self.get_tree(), other.get_tree()) return False def __str__(self): @@ -303,18 +273,18 @@ class ConfigTree: return self.__version def write_cache(self, file_name): - cache_string = self.__write_internal_string(self.get_tree()) + cache_string = self.__lib.write_internal_string(self.get_tree()) try: Path(file_name).write_bytes(cache_string) except OSError as e: raise ValueError(f'Failed to write internal cache file: {e}') def write_internal_string(self) -> str: - res = self.__write_internal_string(self.get_tree()) + res = self.__lib.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 = self.__lib.to_string(self.__config, ordered_values).decode() config_string = unescape_backslash(config_string) if no_version: return config_string @@ -322,23 +292,23 @@ class ConfigTree: return config_string def to_commands(self, op='set'): - commands = self.__to_commands(self.__config, op.encode()).decode() + commands = self.__lib.to_commands(self.__config, op.encode()).decode() commands = unescape_backslash(commands) return commands def to_json(self): - return self.__to_json(self.__config).decode() + return self.__lib.to_json(self.__config).decode() def to_json_ast(self): - return self.__to_json_ast(self.__config).decode() + return self.__lib.to_json_ast(self.__config).decode() def create_node(self, path): check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__create_node(self.__config, path_str) + res = self.__lib.create_node(self.__config, path_str) if res != 0: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(f'{msg}: {path}') def set(self, path, value=None, replace=True): @@ -354,17 +324,17 @@ class ConfigTree: path_str = ' '.join(map(str, path)).encode() if value is None: - res = self.__set_valueless(self.__config, path_str) + res = self.__lib.set_valueless(self.__config, path_str) else: if replace: - res = self.__set_replace_value( + res = self.__lib.set_replace_value( self.__config, path_str, str(value).encode() ) else: - res = self.__set_add_value(self.__config, path_str, str(value).encode()) + res = self.__lib.set_add_value(self.__config, path_str, str(value).encode()) if res != 0: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError( f'{msg}: path "{path}" value "{value}" replace "{replace}"' ) @@ -378,9 +348,9 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__delete(self.__config, path_str) + res = self.__lib.delete_node(self.__config, path_str) if res != 0: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(f'{msg}: path "{path}"') if self.__migration: @@ -390,9 +360,9 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__delete_value(self.__config, path_str, value.encode()) + res = self.__lib.delete_value(self.__config, path_str, value.encode()) if res != 0: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(f'{msg}: path "{path}" value "{value}"') if self.__migration: @@ -408,9 +378,9 @@ class ConfigTree: if self.exists(new_path): raise ConfigTreeError(f'Name {new_name} already exists') - res = self.__rename(self.__config, path_str, newname_str) + res = self.__lib.rename_node(self.__config, path_str, newname_str) if res != 0: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(f'{msg}: {path}') if self.__migration: @@ -427,9 +397,9 @@ class ConfigTree: # Check if a node with intended new name already exists if self.exists(new_path): raise ConfigTreeError() - res = self.__copy(self.__config, oldpath_str, newpath_str) + res = self.__lib.copy_node(self.__config, oldpath_str, newpath_str) if res != 0: - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(msg) if self.__migration: @@ -441,21 +411,21 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__exists(self.__config, path_str) + res = self.__lib.exists(self.__config, path_str) return bool(res) def value_exists(self, path, value): check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__value_exists(self.__config, path_str, value.encode()) + res = self.__lib.value_exists(self.__config, path_str, value.encode()) return bool(res) def list_nodes(self, path, path_must_exist=True): check_path(path) path_str = ' '.join(map(str, path)).encode() - res_json = self.__list_nodes(self.__config, path_str).decode() + res_json = self.__lib.list_nodes(self.__config, path_str).decode() res = json.loads(res_json) if res is None: @@ -468,7 +438,7 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res_json = self.__return_value(self.__config, path_str).decode() + res_json = self.__lib.return_value(self.__config, path_str).decode() res = json.loads(res_json) if res is None: @@ -479,7 +449,7 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res_json = self.__return_values(self.__config, path_str).decode() + res_json = self.__lib.return_values(self.__config, path_str).decode() res = json.loads(res_json) if res is None: @@ -490,41 +460,41 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__is_tag(self.__config, path_str) + res = self.__lib.is_tag(self.__config, path_str) return bool(res) def set_tag(self, path, value=True): check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__set_tag(self.__config, path_str, value) + res = self.__lib.set_tag(self.__config, path_str, value) if res == 0: return True - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(f'{msg}: {path}') def is_leaf(self, path): check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__is_leaf(self.__config, path_str) + res = self.__lib.is_leaf(self.__config, path_str) return bool(res) def set_leaf(self, path, value): check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__set_leaf(self.__config, path_str, value) + res = self.__lib.set_leaf(self.__config, path_str, value) if res == 0: return True - msg = self.__get_error().decode() + msg = self.__lib.get_error().decode() raise ConfigTreeError(f'{msg}: {path}') def get_subtree(self, path, with_node=False): check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__get_subtree(self.__config, path_str, with_node) + res = self.__lib.get_subtree(self.__config, path_str, with_node) subt = ConfigTree(address=res) return subt @@ -534,7 +504,7 @@ class ConfigTree: check_path(path) path_str = ' '.join(map(str, path)).encode() - res_json = self.__config_dict( + res_json = self.__lib.config_dict( self.__config, ref_tree.get_tree(), mask.get_tree(), @@ -561,18 +531,12 @@ def diff_compare(left, right, path=None, commands=False, libpath=LIBPATH): check_path(path) path_str = ' '.join(map(str, path)).encode() - __lib = cdll.LoadLibrary(libpath) - __diff_compare = __lib.diff_compare - __diff_compare.argtypes = [c_bool, c_char_p, c_void_p, c_void_p] - __diff_compare.restype = c_char_p - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p + lib = get_lib(libpath) - res = __diff_compare(commands, path_str, left.get_tree(), right.get_tree()) + res = lib.diff_compare(commands, path_str, left.get_tree(), right.get_tree()) res = res.decode() if res == '#1@': - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) res = unescape_backslash(res) @@ -587,18 +551,12 @@ def diff_show(rt, left, right, path=None, libpath=LIBPATH): check_path(path) path_str = ' '.join(map(str, path)).encode() - __lib = cdll.LoadLibrary(libpath) - __diff_show = __lib.diff_show - __diff_show.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p] - __diff_show.restype = c_char_p - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p + lib = get_lib(libpath) - res = __diff_show(rt.get_tree(), left.get_tree(), right.get_tree(), path_str) + res = lib.diff_show(rt.get_tree(), left.get_tree(), right.get_tree(), path_str) res = res.decode() if res == '#1@': - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) res = unescape_backslash(res) @@ -613,15 +571,9 @@ def union(left, right, libpath=LIBPATH): if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)): raise TypeError('Arguments must be instances of ConfigTree') - __lib = cdll.LoadLibrary(libpath) - __tree_union = __lib.tree_union - __tree_union.argtypes = [c_void_p, c_void_p] - __tree_union.restype = c_void_p - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p + lib = get_lib(libpath) - res = __tree_union(left.get_tree(), right.get_tree()) + res = lib.tree_union(left.get_tree(), right.get_tree()) tree = ConfigTree(address=res) return tree @@ -635,15 +587,9 @@ def merge(left, right, destructive=False, libpath=LIBPATH): if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)): raise TypeError('Arguments must be instances of ConfigTree') - __lib = cdll.LoadLibrary(libpath) - __tree_merge = __lib.tree_merge - __tree_merge.argtypes = [c_bool, c_void_p, c_void_p] - __tree_merge.restype = c_void_p - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p + lib = get_lib(libpath) - res = __tree_merge(destructive, left.get_tree(), right.get_tree()) + res = lib.tree_merge(destructive, left.get_tree(), right.get_tree()) tree = ConfigTree(address=res) return tree @@ -655,19 +601,13 @@ def mask_inclusive(left, right, libpath=LIBPATH): raise TypeError('Arguments must be instances of ConfigTree') try: - __lib = cdll.LoadLibrary(libpath) - __mask_inclusive = __lib.mask_inclusive - __mask_inclusive.argtypes = [c_void_p, c_void_p] - __mask_inclusive.restype = c_void_p - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - - res = __mask_inclusive(left.get_tree(), right.get_tree()) + lib = get_lib(libpath) + + res = lib.mask_inclusive(left.get_tree(), right.get_tree()) except Exception as e: raise ConfigTreeError(e) if not res: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) tree = ConfigTree(address=res) @@ -681,19 +621,13 @@ def mask_exclusive(left, right, libpath=LIBPATH): raise TypeError('Arguments must be instances of ConfigTree') try: - __lib = cdll.LoadLibrary(libpath) - __mask_exclusive = __lib.mask_exclusive - __mask_exclusive.argtypes = [c_void_p, c_void_p] - __mask_exclusive.restype = c_void_p - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - - res = __mask_exclusive(left.get_tree(), right.get_tree()) + lib = get_lib(libpath) + + res = lib.mask_exclusive(left.get_tree(), right.get_tree()) except Exception as e: raise ConfigTreeError(e) if not res: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) tree = ConfigTree(address=res) @@ -741,15 +675,9 @@ def subtree_from_partial( 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( + lib = get_lib(libpath) + + res = lib.subtree_from_partial( reference_tree.get_tree(), config_tree.get_tree(), start.get_tree(), @@ -758,7 +686,7 @@ def subtree_from_partial( except Exception as e: raise ConfigTreeError(e) if not res: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) tree = ConfigTree(address=res) @@ -769,72 +697,52 @@ def subtree_from_partial( def reference_tree_to_json(from_dir, to_file, internal_cache='', libpath=LIBPATH): # pylint: disable=raise-missing-from try: - __lib = cdll.LoadLibrary(libpath) - __reference_tree_to_json = __lib.reference_tree_to_json - __reference_tree_to_json.argtypes = [c_char_p, c_char_p, c_char_p] - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - res = __reference_tree_to_json( + lib = get_lib(libpath) + res = lib.reference_tree_to_json( internal_cache.encode(), from_dir.encode(), to_file.encode() ) except Exception as e: raise ConfigTreeError(e) if res == 1: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) def merge_reference_tree_cache(cache_dir, primary_name, result_name, libpath=LIBPATH): # pylint: disable=raise-missing-from try: - __lib = cdll.LoadLibrary(libpath) - __merge_reference_tree_cache = __lib.merge_reference_tree_cache - __merge_reference_tree_cache.argtypes = [c_char_p, c_char_p, c_char_p] - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - res = __merge_reference_tree_cache( + lib = get_lib(libpath) + res = lib.merge_reference_tree_cache( cache_dir.encode(), primary_name.encode(), result_name.encode() ) except Exception as e: raise ConfigTreeError(e) if res == 1: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) def interface_definitions_to_cache(from_dir, cache_path, libpath=LIBPATH): # pylint: disable=raise-missing-from try: - __lib = cdll.LoadLibrary(libpath) - __interface_definitions_to_cache = __lib.interface_definitions_to_cache - __interface_definitions_to_cache.argtypes = [c_char_p, c_char_p] - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - res = __interface_definitions_to_cache(from_dir.encode(), cache_path.encode()) + lib = get_lib(libpath) + res = lib.interface_definitions_to_cache(from_dir.encode(), cache_path.encode()) except Exception as e: raise ConfigTreeError(e) if res == 1: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) def reference_tree_cache_to_json(cache_path, render_file, libpath=LIBPATH): # pylint: disable=raise-missing-from try: - __lib = cdll.LoadLibrary(libpath) - __reference_tree_cache_to_json = __lib.reference_tree_cache_to_json - __reference_tree_cache_to_json.argtypes = [c_char_p, c_char_p] - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - res = __reference_tree_cache_to_json(cache_path.encode(), render_file.encode()) + lib = get_lib(libpath) + res = lib.reference_tree_cache_to_json(cache_path.encode(), render_file.encode()) except Exception as e: raise ConfigTreeError(e) if res == 1: - msg = __get_error().decode() + msg = lib.get_error().decode() raise ConfigTreeError(msg) @@ -847,19 +755,14 @@ def validate_tree_filter( ): # pylint: disable=raise-missing-from try: - __lib = cdll.LoadLibrary(libpath) - __validate_tree_filter = __lib.validate_tree_filter - __validate_tree_filter.argtypes = [c_void_p, c_char_p, c_char_p] - __get_error = __lib.get_error - __get_error.argtypes = [] - __get_error.restype = c_char_p - res = __validate_tree_filter( + lib = get_lib(libpath) + res = lib.validate_tree_filter( config_tree.get_tree(), cache_path.encode(), validator_dir.encode() ) except Exception as e: raise ConfigTreeError(e) - msg = __get_error().decode() + msg = lib.get_error().decode() tree = ConfigTree(address=res) return tree, msg @@ -896,16 +799,12 @@ class DiffTree: self.left = left self.right = right - self.__lib = cdll.LoadLibrary(libpath) - - self.__diff_tree = self.__lib.diff_tree - self.__diff_tree.argtypes = [c_char_p, c_void_p, c_void_p] - self.__diff_tree.restype = c_void_p + self.__lib = get_lib(libpath) check_path(path) path_str = ' '.join(map(str, path)).encode() - res = self.__diff_tree(path_str, left.get_tree(), right.get_tree()) + res = self.__lib.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/referencetree.py b/python/vyos/referencetree.py index b535615dd..c2cb654b4 100644 --- a/python/vyos/referencetree.py +++ b/python/vyos/referencetree.py @@ -13,12 +13,11 @@ # 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 pathlib import Path from vyos.defaults import reference_tree_cache - -LIBPATH = '/usr/lib/libvyosconfig.so.0' +from vyos.configtree import LIBPATH +from vyos.configtree import get_lib class ReferenceTreeError(Exception): @@ -29,49 +28,26 @@ class ReferenceTree: # pylint: disable=too-many-instance-attributes,raise-missing-from 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_string = self.__lib.read_internal_string_reference_tree - self.__read_internal_string.argtypes = [c_char_p] - self.__read_internal_string.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 + self.__lib = get_lib(libpath) try: cache_string = Path(cache_file).read_bytes() except OSError as e: raise ValueError(f'Failed to read cache_file: {e}') - pointer = self.__read_internal_string(cache_string) + pointer = self.__lib.read_internal_string_reference_tree(cache_string) if pointer is None: - msg = self.__get_error().decode() + msg = self.__lib.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) + self.__lib.destroy(self.__pointer) def __eq__(self, other): if isinstance(other, ReferenceTree): - return self.__equal(self.get_tree(), other.get_tree()) + return self.__lib.equal(self.get_tree(), other.get_tree()) return False def __str__(self): @@ -81,7 +57,7 @@ class ReferenceTree: return self.__pointer def write_cache(self, file_name): - self.__write_internal(self.get_tree(), file_name.encode()) + self.__lib.write_internal_reference_tree(self.get_tree(), file_name.encode()) def to_json(self): - return self.__to_json(self.__pointer).decode() + return self.__lib.to_json_reference_tree(self.__pointer).decode() |
