Page MenuHomeVyOS Platform

No OneTemporary

Size
57 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Sat, Sep 26, 1:22 PM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4285252
Default Alt Text
(57 KB)

Event Timeline