Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F117521032
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
57 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/libvyosconfig/Makefile b/libvyosconfig/Makefile
index 8e37495b1..26563ef78 100644
--- a/libvyosconfig/Makefile
+++ b/libvyosconfig/Makefile
@@ -1,84 +1,84 @@
BUILDDIR=_build
VPATH=$(BUILDDIR)
OCAMLDIR=$(shell eval $$(opam env --root=/opt/opam --set-root); ocamlopt -where)
$(shell mkdir -p $(BUILDDIR) $(BUILDDIR)/stub $(BUILDDIR)/lib $(BUILDDIR)/stub_generator $(BUILDDIR)/test $(BUILDDIR)/generated)
PACKAGES=vyos1x-config,vyconf.vyconfd-config,vyconf.vycall-client,re,ctypes.stubs,ctypes.foreign
GENERATOR_FILES=$(BUILDDIR)/lib/bindings.cmx \
$(BUILDDIR)/stub_generator/generate.cmx
LIBFILES=$(BUILDDIR)/lib/bindings.cmx \
$(BUILDDIR)/generated/vyosconfig_bindings.cmx \
$(BUILDDIR)/lib/apply_bindings.cmx \
$(BUILDDIR)/generated/vyosconfig.o
CAML_INIT=$(BUILDDIR)/stub/init.o
CAML_CTYPES:=$(shell eval $$(opam env --root=/opt/opam --set-root); ocamlfind query ctypes)
CAML_STDLIB:=$(shell eval $$(opam env --root=/opt/opam --set-root); ocamlfind ocamlc -config | awk '/^standard_library_default:/ {print $$2}')
# The files that we'll generate
GENERATED=$(BUILDDIR)/generated/vyosconfig.h \
$(BUILDDIR)/generated/vyosconfig.c \
$(BUILDDIR)/generated/vyosconfig_bindings.ml
OSTYPE:=$(shell eval $$(opam env --root=/opt/opam --set-root); ocamlfind ocamlc -config | awk '/^os_type:/ {print $$2}')
SYSTEM:=$(shell eval $$(opam env --root=/opt/opam --set-root); ocamlfind ocamlc -config | awk '/^system:/ {print $$2}')
EXTDLL:=$(shell eval $$(opam env --root=/opt/opam --set-root); ocamlfind ocamlc -config | awk '/^ext_dll:/ {print $$2}')
CC:= $(shell eval $$(opam env --root=/opt/opam --set-root); ocamlfind ocamlc -config | awk '/^bytecomp_c_compiler/ {for(i=2;i<=NF;i++) printf "%s " ,$$i}')
LIBNAME:=libvyosconfig.so.0
ifeq ($(OSTYPE),$(filter $(OSTYPE),Win32 Cygwin))
EXTEXE=.exe
else
EXTEXE=
endif
GENERATOR=$(BUILDDIR)/generate$(EXTEXE)
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#52132ad2c0992bf6f17a06173384030d93a29053 -y ; \
+ opam pin add vyos1x-config https://github.com/vyos/vyos1x-config.git#e80771d973fc249ab04fd20a6bb2463698194880 -y ; \
opam pin add vyconf https://github.com/vyos/vyconf.git#e25b13ae3040d02326f01bf9bedd097795fb3a62 -y'
sharedlib: depends $(BUILDDIR)/libvyosconfig$(EXTDLL)
ifeq ($(OSTYPE),$(filter $(OSTYPE),Win32 Cygwin))
$(BUILDDIR)/libvyosconfig$(EXTDLL): $(CAML_INIT) $(LIBFILES)
ocamlfind opt -o $@ -linkpkg -output-obj -verbose -package $(PACKAGES) $^
else ifeq ($(SYSTEM),$(filter $(SYSTEM),macosx))
$(BUILDDIR)/libvyosconfig$(EXTDLL): $(CAML_INIT) $(LIBFILES)
ocamlfind opt -o $@ -linkpkg -runtime-variant _pic -verbose -ccopt -dynamiclib -package $(PACKAGES) $^
else
$(BUILDDIR)/libvyosconfig$(EXTDLL): $(CAML_INIT) $(LIBFILES)
eval $$(opam env --root=/opt/opam --set-root); ocamlfind opt -o $@ -linkpkg -output-obj -runtime-variant _pic -verbose -thread -package $(PACKAGES) -ccopt "-Wl,-soname,$(LIBNAME)" $^
endif
stubs: $(GENERATED)
$(BUILDDIR)/stub/%.o:
eval $$(opam env --root=/opt/opam --set-root); ocamlc -g -c stub/init.c
mv init.o $@
$(GENERATED): $(GENERATOR)
$(GENERATOR) $(BUILDDIR)/generated
$(BUILDDIR)/%.o: %.c
$(CC) -c -o $@ -fPIC -I $(CAML_CTYPES) -I $(CAML_STDLIB) -I $(OCAMLDIR) -I $(OCAMLDIR)/../ctypes $<
$(BUILDDIR)/%.cmx: %.ml
eval $$(opam env --root=/opt/opam --set-root); ocamlfind opt -c -o $@ -I $(BUILDDIR)/generated -I $(BUILDDIR)/lib -thread -package $(PACKAGES) $<
$(GENERATOR): $(GENERATOR_FILES)
eval $$(opam env --root=/opt/opam --set-root); ocamlfind opt -o $@ -I $(BUILDDIR)/lib -linkpkg -thread -package $(PACKAGES) $^
install: sharedlib
@mkdir -p /usr/lib
@cp $(BUILDDIR)/libvyosconfig$(EXTDLL) /usr/lib/$(LIBNAME)
clean:
rm -rf $(BUILDDIR)
diff --git a/libvyosconfig/lib/bindings.ml b/libvyosconfig/lib/bindings.ml
index 5d0478e02..0759edae2 100644
--- a/libvyosconfig/lib/bindings.ml
+++ b/libvyosconfig/lib/bindings.ml
@@ -1,545 +1,575 @@
open Ctypes
open Foreign
open Vyos1x
open Vyconfd_config
open Commitd_client
module VT = Vytree
module CT = Config_tree
module CD = Config_diff
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)
let error_message = ref ""
let make_syntax_error pos err =
match pos with
| None -> Printf.sprintf "Syntax error: %s" err
| Some (l, c) ->
Printf.sprintf "Syntax error on line %d, character %d: %s" l c err
let to_json_str = fun s -> `String s
let split_on_whitespace s = Re.split (Re.Perl.compile_pat "\\s+") s
let make_config_tree name = Ctypes.Root.create (CT.make name)
let destroy c_ptr =
Root.release c_ptr
let equal c_ptr_l c_ptr_r =
(Root.get c_ptr_l) = (Root.get c_ptr_r)
let from_string s =
(* alert exn Parser.from_string:
[Vyos1x.Parser.from_string] caught
*)
try
error_message := "";
let config = (Parser.from_string[@alert "-exn"]) s in
Ctypes.Root.create config
with
| Failure s -> error_message := s; Ctypes.null
| Util.Syntax_error (pos, err) ->
let msg = make_syntax_error pos err in
error_message := msg; Ctypes.null
| _ -> error_message := "Parse error"; Ctypes.null
let get_error () = !error_message
let render_config c_ptr ord_val =
CT.render_config ~ord_val:ord_val (Root.get c_ptr)
let render_json c_ptr =
CT.render_json (Root.get c_ptr)
let render_json_ast c_ptr =
CT.render_json_ast (Root.get c_ptr)
let render_commands c_ptr op =
(* alert exn CT.render_commands:
[Vytree.Nonexistent_path] not possible for path []
*)
match op with
| "delete" ->
(CT.render_commands[@alert "-exn"]) ~op:CT.Delete (Root.get c_ptr) []
| _ ->
(CT.render_commands[@alert "-exn"]) ~op:CT.Set (Root.get c_ptr) []
let read_internal file =
(* alert exn Internal.read_internal:
[Internal.Read_error] caught
*)
try
error_message := "";
let ct = (I.read_internal[@alert "-exn"]) file in
Ctypes.Root.create ct
with Internal.Read_error msg ->
error_message := msg; Ctypes.null
let write_internal c_ptr file =
(* alert exn Internal.write_internal:
[Internal.Write_error] caught
*)
try
error_message := "";
let ct = Root.get c_ptr in
(I.write_internal[@alert "-exn"]) ct file
with Internal.Write_error msg ->
error_message := msg
+let read_internal_string s =
+ (* alert exn Internal.read_string:
+ [Internal.Read_error] caught
+ *)
+ try
+ error_message := "";
+ let ct = (I.read_string[@alert "-exn"]) s in
+ Ctypes.Root.create ct
+ with Internal.Read_error msg ->
+ error_message := msg; Ctypes.null
+
+let write_internal_string c_ptr =
+ let ct = Root.get c_ptr in
+ I.write_string ct
+
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
[Config_tree.Useless_set] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (CT.create_node[@alert "-exn"]) ct path in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Path is empty"; 1
(* be lenient on redundant set *)
| CT.Useless_set -> 0
let set_add_value c_ptr path value =
(* alert exn CT.set:
[Vytree.Empty_path] caught
[Config_tree.Useless_set] not reachable
[Config_tree.Duplicate_value] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (CT.set[@alert "-exn"]) ct path (Some value) CT.AddValue in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Path is empty"; 1
(* be lenient on redundant value *)
| CT.Duplicate_value -> 0
let set_replace_value c_ptr path value =
(* alert exn CT.set:
[Vytree.Empty_path] caught
[Config_tree.Useless_set] not reachable
[Config_tree.Duplicate_value] not reachable
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (CT.set[@alert "-exn"]) ct path (Some value) CT.ReplaceValue in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Path is empty"; 1
let set_valueless c_ptr path =
(* alert exn CT.set:
[Vytree.Empty_path] caught
[Config_tree.Useless_set] caught
[Config_tree.Duplicate_value] not reachable
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (CT.set[@alert "-exn"]) ct path None CT.AddValue in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Path is empty"; 1
(* be lenient on redundant set *)
| CT.Useless_set -> 0
let delete_value c_ptr path value =
(* alert exn CT.delete:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
[Config_tree.No_such_value] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (CT.delete[@alert "-exn"]) ct path (Some value) in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Empty path"; 1
| VT.Nonexistent_path -> error_message := "Path doesn't exist"; 1
| CT.No_such_value -> error_message := "Value doesn't exist"; 1
let delete_node c_ptr path =
(* alert exn CT.delete:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
[Config_tree.No_such_value] not possible for None value
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (CT.delete[@alert "-exn"]) ct path None in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Empty path"; 1
| VT.Nonexistent_path -> error_message := "Path doesn't exist"; 1
let rename_node c_ptr path newname =
(* alert exn VT.rename:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
[Not_found] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
let new_ct = (VT.rename[@alert "-exn"]) ct path newname in
Root.set c_ptr new_ct;
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Empty path"; 1
| VT.Nonexistent_path -> error_message := "Path doesn't exist"; 1
(* strictly speaking, the exception above will obscure the one below *)
| Not_found -> error_message := "Path not found"; 1
let set_tag c_ptr path value =
(* alert exn CT.set_tag:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
Root.set c_ptr ((CT.set_tag[@alert "-exn"]) ct path value);
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Empty path"; 1
| VT.Nonexistent_path -> error_message := "Path doesn't exist"; 1
let is_tag c_ptr path =
(* alert exn CT.set_tag:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
if ((CT.is_tag[@alert "-exn"]) ct path) then 1 else 0
with
| VT.Empty_path -> 0
| VT.Nonexistent_path -> 0
let set_leaf c_ptr path value =
(* alert exn CT.set_leaf:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
error_message := "";
Root.set c_ptr ((CT.set_leaf[@alert "-exn"]) ct path value);
0 (* return 0 *)
with
| VT.Empty_path -> error_message := "Empty path"; 1
| VT.Nonexistent_path -> error_message := "Path doesn't exist"; 1
let is_leaf c_ptr path =
(* alert exn CT.is_leaf:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
if ((CT.is_leaf[@alert "-exn"]) ct path) then 1 else 0
with
| VT.Empty_path -> 0
| VT.Nonexistent_path -> 0
let get_subtree c_ptr path with_node =
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
let subt = CT.get_subtree ~with_node:with_node ct path in
Ctypes.Root.create subt
let exists c_ptr path =
(* alert exn VT.exists:
[Vytree.Empty_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
if ((VT.exists[@alert "-exn"]) ct path) then 1 else 0
with VT.Empty_path -> 0
let value_exists c_ptr path value =
(* alert exn VT.exists:
[Vytree.Empty_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
if ((CT.value_exists[@alert "-exn"]) ct path value) then 1 else 0
with VT.Empty_path -> 0
let list_nodes c_ptr path =
(* alert exn VT.children_of_node:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
let nodes = (VT.children_of_path[@alert "-exn"]) ct path in
let nodes_json = `List (List.map to_json_str nodes) in
Yojson.Safe.to_string nodes_json
with _ -> Yojson.Safe.to_string `Null
let return_value c_ptr path =
(* alert exn CT.get_value:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
[Config_tree.Node_has_no_value] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
try
Yojson.Safe.to_string (`String ((CT.get_value[@alert "-exn"]) ct path))
with
| CT.Node_has_no_value -> Yojson.Safe.to_string (`String "")
| _ -> Yojson.Safe.to_string `Null
let return_values c_ptr path =
(* alert exn CT.get_values:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
*)
let ct = Root.get c_ptr in
let path = split_on_whitespace path in
let to_json_str = fun s -> `String s in
try
let values = (CT.get_values[@alert "-exn"]) ct path in
let values_json = `List (List.map to_json_str values) in
Yojson.Safe.to_string values_json
with _ -> Yojson.Safe.to_string `Null
let copy_node c_ptr old_path new_path =
(* alert exn VT.copy:
[Vytree.Empty_path] caught
[Vytree.Nonexistent_path] caught
[Vytree.Insert_error] caught
*)
let ct = Root.get c_ptr in
let old_path_str = old_path in
let old_path = split_on_whitespace old_path in
let new_path = split_on_whitespace new_path in
try
error_message := "";
let new_ct = (VT.copy[@alert "-exn"]) ct old_path new_path in
Root.set c_ptr new_ct;
0
with
| Vytree.Empty_path ->
error_message := "Empty path"; 1
| Vytree.Nonexistent_path ->
let s = Printf.sprintf "Non-existent path \'%s\'" old_path_str in
error_message := s; 1
| Vytree.Insert_error s -> error_message := s; 1
let diff_tree path c_ptr_l c_ptr_r =
(* alert exn CD.diff_tree:
[Config_diff.Incommensurable] caught
[Config_diff.Empty_comparison] caught
*)
let path = split_on_whitespace path in
let ct_l = Root.get c_ptr_l in
let ct_r = Root.get c_ptr_r in
try
let ct_ret = (CD.diff_tree[@alert "-exn"]) path ct_l ct_r in
Ctypes.Root.create ct_ret
with
| CD.Incommensurable -> error_message := "Incommensurable"; Ctypes.null
| CD.Empty_comparison -> error_message := "Empty comparison"; Ctypes.null
let diff_compare cmds path c_ptr_l c_ptr_r =
(* alert exn CD.show_diff:
[Config_diff.Incommensurable] caught
[Config_diff.Empty_comparison] caught
*)
let path = split_on_whitespace path in
let ct_l = Root.get c_ptr_l in
let ct_r = Root.get c_ptr_r in
try
(CD.diff_compare[@alert "-exn"]) ~cmds:cmds path ct_l ct_r
with
| CD.Incommensurable -> error_message := "Incommensurable"; "#1@"
| CD.Empty_comparison -> error_message := "Empty comparison"; "#1@"
let tree_union c_ptr_l c_ptr_r =
(* alert exn CD.tree_union:
[Tree_alg.Incompatible_union] caught
[Tree_alg.Nonexistent_child] caught
*)
let ct_l = Root.get c_ptr_l in
let ct_r = Root.get c_ptr_r in
try
let ct_ret = (CD.tree_union[@alert "-exn"]) ct_l ct_r in
Ctypes.Root.create ct_ret
with
| TA.Nonexistent_child -> error_message := "Nonexistent child"; Ctypes.null
| TA.Incompatible_union -> error_message := "Trees must have equivalent root"; Ctypes.null
let tree_merge destructive c_ptr_l c_ptr_r =
(* alert exn CD.tree_merge:
[Tree_alg.Incompatible_union] caught
[Tree_alg.Nonexistent_child] caught
*)
let ct_l = Root.get c_ptr_l in
let ct_r = Root.get c_ptr_r in
try
let ct_ret = (CD.tree_merge[@alert "-exn"]) ~destructive:destructive ct_l ct_r in
Ctypes.Root.create ct_ret
with
| TA.Nonexistent_child -> error_message := "Nonexistent child"; Ctypes.null
| TA.Incompatible_union -> error_message := "Trees must have equivalent root"; Ctypes.null
let reference_tree_to_json internal_cache from_dir to_file =
(* alert exn Generate.reference_tree_to_json:
[Generate.Load_error] caught
[Generate.Write_error] caught
*)
try
(Generate.reference_tree_to_json[@alert "-exn"]) ~internal_cache:internal_cache from_dir to_file;
0
with
| Generate.Load_error msg ->
let s = Printf.sprintf "Load_error \'%s\'" msg in
error_message := s; 1
| Generate.Write_error msg ->
let s = Printf.sprintf "Write_error \'%s\'" msg in
error_message := s; 1
-let mask_tree c_ptr_l c_ptr_r =
+let mask_tree c_ptr_l c_ptr_r exclusive =
(* alert exn CD.mask_tree:
[Config_diff.Incommensurable] caught
[Config_diff.Empty_comparison] caught
*)
let ct_l = Root.get c_ptr_l in
let ct_r = Root.get c_ptr_r in
try
- let ct_ret = (CD.mask_tree[@alert "-exn"]) ct_l ct_r in
+ let ct_ret = (CD.mask_tree[@alert "-exn"]) ~exclusive:exclusive ct_l ct_r in
Ctypes.Root.create ct_ret
with
| CD.Incommensurable -> error_message := "Incommensurable"; Ctypes.null
| CD.Empty_comparison -> error_message := "Empty comparison"; Ctypes.null
+let subtree_from_partial r_ptr c_ptr i_ptr path =
+ let rt = Root.get r_ptr in
+ let ct = Root.get c_ptr in
+ let input = Root.get i_ptr in
+ let path = split_on_whitespace path in
+ try
+ let ct_ret = (CD.subtree_from_partial[@alert "-exn"]) rt ct input path in
+ Ctypes.Root.create ct_ret
+ with
+ CD.Malformed_path s ->
+ error_message := s; Ctypes.null
+
let validate_tree_filter c_ptr rt_cache_path validator_dir =
(* alert exn Internal.read_internal:
[Internal.Read_error] caught
*)
let ct = Root.get c_ptr in
try
let rt = (IR.read_internal[@alert "-exn"]) rt_cache_path
in
let ct_ret, out =
RT.validate_tree_filter validator_dir rt ct
in
error_message := out; Ctypes.Root.create ct_ret
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
let () = I.internal "make" (string @-> returning (ptr void)) make_config_tree
let () = I.internal "destroy" ((ptr void) @-> returning void) destroy
let () = I.internal "equal" ((ptr void) @-> (ptr void) @-> returning bool) equal
let () = I.internal "from_string" (string @-> returning (ptr void)) from_string
let () = I.internal "get_error" (void @-> returning string) get_error
let () = I.internal "to_string" ((ptr void) @-> bool @-> returning string) render_config
let () = I.internal "to_json" ((ptr void) @-> returning string) render_json
let () = I.internal "to_json_ast" ((ptr void) @-> returning string) render_json_ast
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 "read_internal_string" (string @-> returning (ptr void)) read_internal_string
+ let () = I.internal "write_internal_string" ((ptr void) @-> returning string) write_internal_string
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
let () = I.internal "set_valueless" ((ptr void) @-> string @-> returning int) set_valueless
let () = I.internal "delete_value" ((ptr void) @-> string @-> string @-> returning int) delete_value
let () = I.internal "delete_node" ((ptr void) @-> string @-> returning int) delete_node
let () = I.internal "rename_node" ((ptr void) @-> string @-> string @-> returning int) rename_node
let () = I.internal "copy_node" ((ptr void) @-> string @-> string @-> returning int) copy_node
let () = I.internal "set_tag" ((ptr void) @-> string @-> bool @-> returning int) set_tag
let () = I.internal "is_tag" ((ptr void) @-> string @-> returning int) is_tag
let () = I.internal "set_leaf" ((ptr void) @-> string @-> bool @-> returning int) set_leaf
let () = I.internal "is_leaf" ((ptr void) @-> string @-> returning int) is_leaf
let () = I.internal "get_subtree" ((ptr void) @-> string @-> bool @-> returning (ptr void)) get_subtree
let () = I.internal "exists" ((ptr void) @-> string @-> returning int) exists
let () = I.internal "value_exists" ((ptr void) @-> string @-> string @-> returning int) value_exists
let () = I.internal "list_nodes" ((ptr void) @-> string @-> returning string) list_nodes
let () = I.internal "return_value" ((ptr void) @-> string @-> returning string) return_value
let () = I.internal "return_values" ((ptr void) @-> string @-> returning string) return_values
let () = I.internal "diff_tree" (string @-> (ptr void) @-> (ptr void) @-> returning (ptr void)) diff_tree
let () = I.internal "diff_compare" (bool @-> string @-> (ptr void) @-> (ptr void) @-> returning string) diff_compare
let () = I.internal "tree_union" ((ptr void) @-> (ptr void) @-> returning (ptr void)) tree_union
let () = I.internal "tree_merge" (bool @-> (ptr void) @-> (ptr void) @-> returning (ptr void)) tree_merge
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 "mask_tree" ((ptr void) @-> (ptr void) @-> bool @-> returning (ptr void)) mask_tree
+ let () = I.internal "subtree_from_partial" ((ptr void) @-> (ptr void) @-> (ptr void) @-> string @-> returning (ptr void)) subtree_from_partial
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 61ef6428c..d92137c14 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -1,758 +1,865 @@
# configtree -- a standalone VyOS config file manipulation library (Python bindings)
# 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import re
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'
LIBPATH = BUILD_PATH if os.path.isfile(BUILD_PATH) else INSTALL_PATH
def replace_backslash(s, search, replace):
"""Modify quoted strings containing backslashes not of escape sequences"""
def replace_method(match):
result = match.group().replace(search, replace)
return result
p = re.compile(r'("[^"]*[\\][^"]*"\n|\'[^\']*[\\][^\']*\'\n)')
return p.sub(replace_method, s)
def escape_backslash(string: str) -> str:
"""Escape single backslashes in quoted strings"""
result = replace_backslash(string, '\\', '\\\\')
return result
def unescape_backslash(string: str) -> str:
"""Unescape backslashes in quoted strings"""
result = replace_backslash(string, '\\\\', '\\')
return result
def extract_version(s):
"""Extract the version string from the config string"""
t = re.split('(^//)', s, maxsplit=1, flags=re.MULTILINE)
return (t[0], ''.join(t[1:]))
def check_path(path):
# Necessary type checking
if not isinstance(path, list):
raise TypeError('Expected a list, got a {}'.format(type(path)))
else:
pass
class ConfigTreeError(Exception):
pass
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
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
if address is not None:
self.__config = address
self.__version = ''
elif internal is not None:
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 = ''
elif config_string is not None:
config_section, version_section = extract_version(config_string)
config_section = escape_backslash(config_section)
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
else:
raise TypeError(
"ConfigTree() requires one of 'config_string', 'address', or 'internal'"
)
self.__migration = os.environ.get('VYOS_MIGRATION')
if self.__migration:
self.migration_log = logging.getLogger('vyos.migrate')
def __del__(self):
if self.__config is not None:
self.__destroy(self.__config)
def __eq__(self, other):
if isinstance(other, ConfigTree):
return self.__equal(self.get_tree(), other.get_tree())
return False
def __str__(self):
return self.to_string()
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_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)
if no_version:
return config_string
config_string = '{0}\n{1}'.format(config_string, self.__version)
return config_string
def to_commands(self, op='set'):
commands = self.__to_commands(self.__config, op.encode()).decode()
commands = unescape_backslash(commands)
return commands
def to_json(self):
return self.__to_json(self.__config).decode()
def to_json_ast(self):
return self.__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)
if res != 0:
msg = self.__get_error().decode()
raise ConfigTreeError(f'{msg}: {path}')
def set(self, path, value=None, replace=True):
"""Set new entry in VyOS configuration.
path: configuration path e.g. 'system dns forwarding listen-address'
value: value to be added to node, e.g. '172.18.254.201'
replace: True: current occurrence will be replaced
False: new value will be appended to current occurrences - use
this for adding values to a multi node
"""
check_path(path)
path_str = ' '.join(map(str, path)).encode()
if value is None:
res = self.__set_valueless(self.__config, path_str)
else:
if replace:
res = self.__set_replace_value(
self.__config, path_str, str(value).encode()
)
else:
res = self.__set_add_value(self.__config, path_str, str(value).encode())
if res != 0:
msg = self.__get_error().decode()
raise ConfigTreeError(
f'{msg}: path "{path}" value "{value}" replace "{replace}"'
)
if self.__migration:
self.migration_log.info(
f'- op: set path: {path} value: {value} replace: {replace}'
)
def delete(self, path):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
res = self.__delete(self.__config, path_str)
if res != 0:
msg = self.__get_error().decode()
raise ConfigTreeError(f'{msg}: path "{path}"')
if self.__migration:
self.migration_log.info(f'- op: delete path: {path}')
def delete_value(self, path, value):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
res = self.__delete_value(self.__config, path_str, value.encode())
if res != 0:
msg = self.__get_error().decode()
raise ConfigTreeError(f'{msg}: path "{path}" value "{value}"')
if self.__migration:
self.migration_log.info(f'- op: delete_value path: {path} value: {value}')
def rename(self, path, new_name):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
newname_str = new_name.encode()
# Check if a node with intended new name already exists
new_path = path[:-1] + [new_name]
if self.exists(new_path):
raise ConfigTreeError(f'Name {new_name} already exists')
res = self.__rename(self.__config, path_str, newname_str)
if res != 0:
msg = self.__get_error().decode()
raise ConfigTreeError(f'{msg}: {path}')
if self.__migration:
self.migration_log.info(
f'- op: rename old_path: {path} new_path: {new_path}'
)
def copy(self, old_path, new_path):
check_path(old_path)
check_path(new_path)
oldpath_str = ' '.join(map(str, old_path)).encode()
newpath_str = ' '.join(map(str, new_path)).encode()
# 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)
if res != 0:
msg = self.__get_error().decode()
raise ConfigTreeError(msg)
if self.__migration:
self.migration_log.info(
f'- op: copy old_path: {old_path} new_path: {new_path}'
)
def exists(self, path):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
res = self.__exists(self.__config, path_str)
if res == 0:
return False
else:
return True
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())
if res == 0:
return False
else:
return True
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.loads(res_json)
if res is None:
if path_must_exist:
raise ConfigTreeError("Path [{}] doesn't exist".format(path_str))
else:
return []
else:
return res
def return_value(self, path):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
res_json = self.__return_value(self.__config, path_str).decode()
res = json.loads(res_json)
if res is None:
raise ConfigTreeError("Path [{}] doesn't exist".format(path_str))
else:
return res
def return_values(self, path):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
res_json = self.__return_values(self.__config, path_str).decode()
res = json.loads(res_json)
if res is None:
raise ConfigTreeError("Path [{}] doesn't exist".format(path_str))
else:
return res
def is_tag(self, path):
check_path(path)
path_str = ' '.join(map(str, path)).encode()
res = self.__is_tag(self.__config, path_str)
if res >= 1:
return True
else:
return False
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)
if res == 0:
return True
else:
msg = self.__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)
if res >= 1:
return True
else:
return False
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)
if res == 0:
return True
else:
msg = self.__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)
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:
left = ConfigTree(config_string='\n')
if right is None:
right = ConfigTree(config_string='\n')
if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)):
raise TypeError('Arguments must be instances of ConfigTree')
if path:
if (not left.exists(path)) and (not right.exists(path)):
raise ConfigTreeError(f"Path {path} doesn't exist")
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
res = __diff_compare(commands, path_str, left.get_tree(), right.get_tree())
res = res.decode()
if res == '#1@':
msg = __get_error().decode()
raise ConfigTreeError(msg)
res = unescape_backslash(res)
return res
def union(left, right, libpath=LIBPATH):
if left is None:
left = ConfigTree(config_string='\n')
if right is None:
right = ConfigTree(config_string='\n')
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
res = __tree_union(left.get_tree(), right.get_tree())
tree = ConfigTree(address=res)
return tree
def merge(left, right, destructive=False, libpath=LIBPATH):
if left is None:
left = ConfigTree(config_string='\n')
if right is None:
right = ConfigTree(config_string='\n')
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
res = __tree_merge(destructive, left.get_tree(), right.get_tree())
tree = ConfigTree(address=res)
return tree
def mask_inclusive(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]
+ __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:
msg = __get_error().decode()
raise ConfigTreeError(msg)
tree = ConfigTree(address=res)
return tree
def reference_tree_to_json(from_dir, to_file, internal_cache='', libpath=LIBPATH):
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(
internal_cache.encode(), from_dir.encode(), to_file.encode()
)
except Exception as e:
raise ConfigTreeError(e)
if res == 1:
msg = __get_error().decode()
raise ConfigTreeError(msg)
def merge_reference_tree_cache(cache_dir, primary_name, result_name, libpath=LIBPATH):
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(
cache_dir.encode(), primary_name.encode(), result_name.encode()
)
except Exception as e:
raise ConfigTreeError(e)
if res == 1:
msg = __get_error().decode()
raise ConfigTreeError(msg)
def interface_definitions_to_cache(from_dir, cache_path, libpath=LIBPATH):
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())
except Exception as e:
raise ConfigTreeError(e)
if res == 1:
msg = __get_error().decode()
raise ConfigTreeError(msg)
def reference_tree_cache_to_json(cache_path, render_file, libpath=LIBPATH):
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())
except Exception as e:
raise ConfigTreeError(e)
if res == 1:
msg = __get_error().decode()
raise ConfigTreeError(msg)
# validate_tree_filter c_ptr rt_cache validator_dir
def validate_tree_filter(
config_tree,
cache_path='/usr/share/vyos/reftree.cache',
validator_dir='/usr/libexec/vyos/validators',
libpath=LIBPATH,
):
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(
config_tree.get_tree(), cache_path.encode(), validator_dir.encode()
)
except Exception as e:
raise ConfigTreeError(e)
msg = __get_error().decode()
tree = ConfigTree(address=res)
return tree, msg
def validate_tree(
config_tree,
cache_path='/usr/share/vyos/reftree.cache',
validator_dir='/usr/libexec/vyos/validators',
):
_, out = validate_tree_filter(
config_tree, cache_path=cache_path, validator_dir=validator_dir
)
return out
class DiffTree:
def __init__(self, left, right, path=[], libpath=LIBPATH):
if left is None:
left = ConfigTree(config_string='\n')
if right is None:
right = ConfigTree(config_string='\n')
if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)):
raise TypeError('Arguments must be instances of ConfigTree')
if path:
if not left.exists(path):
raise ConfigTreeError(f"Path {path} doesn't exist in lhs tree")
if not right.exists(path):
raise ConfigTreeError(f"Path {path} doesn't exist in rhs tree")
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
check_path(path)
path_str = ' '.join(map(str, path)).encode()
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)
self.dict = json.loads(self.full.to_json())
# config_tree sub-trees
self.add = self.full.get_subtree(['add'])
self.sub = self.full.get_subtree(['sub'])
self.inter = self.full.get_subtree(['inter'])
self.delete = self.full.get_subtree(['del'])
def to_commands(self):
add = self.add.to_commands()
delete = self.delete.to_commands(op='delete')
return delete + '\n' + add
def deep_copy(config_tree: ConfigTree) -> ConfigTree:
"""An inelegant, but reasonably fast, copy; replace with backend copy"""
D = DiffTree(None, config_tree)
return D.add
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
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Sep 26, 1:22 PM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4285252
Default Alt Text
(57 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment