From 369585302f3d30dfe5d9a517e04903f55a7d365e Mon Sep 17 00:00:00 2001 From: Thomas Finstad Date: Fri, 31 May 2024 11:51:17 +0000 Subject: [PATCH] vyconf: T5083: add childRequirement to reference tree --- .gitignore | 2 + Makefile | 37 + src/reference_tree.ml | 46 +- src/reference_tree.mli | 8 + test/dune | 4 + test/test_vyos1x.ml | 23 + .../interface-definitions/container.xml | 638 ++++++++++++++++++ vyos1x-config.opam | 3 + vyos1x-config.opam.locked | 97 +++ 9 files changed, 855 insertions(+), 3 deletions(-) create mode 100644 Makefile create mode 100644 test/dune create mode 100644 test/test_vyos1x.ml create mode 100644 test/testdata/interface-definitions/container.xml create mode 100644 vyos1x-config.opam.locked diff --git a/.gitignore b/.gitignore index bf22eed..87b699a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ _build/* *.merlin *.install + +test/testresults/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..72dc957 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +SHELL := bash +.ONESHELL: +.SHELLFLAGS := -xeu -o pipefail -c # Enable output of every command as they are ran +.DELETE_ON_ERROR: +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rules +MAKEDIR := $(CURDIR) + +default: deps deps-lock test build + +.PHONY: deps +deps: vyos1x-config.opam + @opam install -y . --deps-only + +.PHONY:build +build: deps + @dune build + +.PHONY: deps-lock +deps-lock: deps + @opam lock . + +.PHONY: test-prep +test-prep: clean + @opam install -y . + dune build ./test/ + +.PHONY: test +test: test-prep + @cd _build/default/test/ + dune exec ./test_vyos1x.bc + cd $(MAKEDIR) + +.PHONY: clean +clean: + @dune clean + rm -rf test/testresults diff --git a/src/reference_tree.ml b/src/reference_tree.ml index 4889734..ba85a5e 100644 --- a/src/reference_tree.ml +++ b/src/reference_tree.ml @@ -13,6 +13,14 @@ type value_constraint = | External of string * string option [@name "exec"] [@@deriving yojson] +type child_requirements_type = + | Require of string list [@name "require"] + | Conflict of (string * string) [@name "conflict"] + | AtLeastOneOf of string list [@name "atLeastOneOf"] + | Depend of (string * string) [@name "depend"] + [@@deriving to_yojson] + + type completion_help_type = | List of string [@name "list"] | Path of string [@name "path"] @@ -24,6 +32,7 @@ type ref_node_data = { constraints: value_constraint list; constraint_group: value_constraint list; constraint_error_message: string; + child_requirements: child_requirements_type list; completion_help: completion_help_type list; help: string; value_help: (string * string) list; @@ -47,6 +56,7 @@ let default_data = { constraints = []; constraint_group = []; constraint_error_message = "Invalid value"; + child_requirements = []; completion_help = []; help = "No help available"; value_help = []; @@ -118,7 +128,7 @@ let load_completion_help_from_xml d c = match c with | Xml.Element (_, _, [Xml.PCData s]) -> l @ [completion_help_type_of_string (Xml.tag c) s] - | _ -> raise (Bad_interface_definition "Malformed completion help") + | _ -> raise (Bad_interface_definition ("Malformed completion help: " ^ Xml.to_string c)) in Xml.fold aux [] c in let l = d.completion_help in let l' = l @ res in @@ -136,7 +146,7 @@ let load_constraint_from_xml d c = | Xml.Element ("validator", [("name", n)], _) -> let cs = (External (n, None)) :: d.constraints in {d with constraints=cs} - | _ -> raise (Bad_interface_definition "Malformed constraint") + | _ -> raise (Bad_interface_definition ("Malformed constraint: " ^ Xml.to_string c)) in Xml.fold aux d c let load_constraint_group_from_xml d c = @@ -154,6 +164,35 @@ let load_constraint_group_from_xml d c = | _ -> raise (Bad_interface_definition "Malformed constraint") in Xml.fold aux d c +let load_child_requirements_from_xml data xml = + let get_name_attr r = Xml.attrib r "name" in + + let aux d xml = + match xml with + | Xml.Element ("require", _, _) -> + let requires = (Xml.map get_name_attr xml) in + {d with child_requirements = ((Require requires) :: d.child_requirements)} + + | Xml.Element ("conflict", _, _) -> + + let child = get_name_attr xml in + let conflicts = (Xml.map get_name_attr xml) in + let add_requirements dd conflict = {dd with child_requirements = ((Conflict (child, conflict) ) :: dd.child_requirements)} in + List.fold_left add_requirements d conflicts + + | Xml.Element ("atLeastOneOf", _, _) -> + let atLeastOneOfs = (Xml.map get_name_attr xml) in + {d with child_requirements = ((AtLeastOneOf atLeastOneOfs) :: d.child_requirements)} + + | Xml.Element ("depend", _, _) -> + let child = get_name_attr xml in + let depends = (Xml.map get_name_attr xml) in + let add_requirements dd depend = {dd with child_requirements = ((Depend (child, depend) ) :: dd.child_requirements)} in + List.fold_left add_requirements d depends + + | _ -> raise (Bad_interface_definition ("Unknown child requirement: " ^ Xml.to_string_fmt xml)) in + Xml.fold aux data xml + let data_from_xml d x = let aux d x = match x with @@ -171,7 +210,8 @@ let data_from_xml d x = {d with priority=Some i} | Xml.Element ("hidden", _, _) -> {d with hidden=true} | Xml.Element ("secret", _, _) -> {d with secret=true} - | _ -> raise (Bad_interface_definition "Malformed property tag") + | Xml.Element ("childRequirements", _, _) -> load_child_requirements_from_xml d x + | _ -> raise (Bad_interface_definition ("Malformed property tag: " ^ Xml.to_string x)) in Xml.fold aux d x let rec insert_from_xml basepath reftree xml = diff --git a/src/reference_tree.mli b/src/reference_tree.mli index a18d875..470040b 100644 --- a/src/reference_tree.mli +++ b/src/reference_tree.mli @@ -8,6 +8,13 @@ type value_constraint = | External of string * string option [@name "exec"] [@@deriving yojson] +type child_requirements_type = + | Require of string list [@name "require"] + | Conflict of (string * string) [@name "conflict"] + | AtLeastOneOf of string list [@name "atLeastOneOf"] + | Depend of (string * string) [@name "depend"] + [@@deriving to_yojson] + type completion_help_type = | List of string [@name "list"] | Path of string [@name "path"] @@ -19,6 +26,7 @@ type ref_node_data = { constraints: value_constraint list; constraint_group: value_constraint list; constraint_error_message: string; + child_requirements: child_requirements_type list; completion_help: completion_help_type list; help: string; value_help: (string * string) list; diff --git a/test/dune b/test/dune new file mode 100644 index 0000000..1fd2d3d --- /dev/null +++ b/test/dune @@ -0,0 +1,4 @@ +(test + (name test_vyos1x) + (libraries vyos1x alcotest) + (modes byte exe)) diff --git a/test/test_vyos1x.ml b/test/test_vyos1x.ml new file mode 100644 index 0000000..68d1cf2 --- /dev/null +++ b/test/test_vyos1x.ml @@ -0,0 +1,23 @@ +let create_newdir path perm = + if not (Sys.file_exists path) then Sys.mkdir path perm + +let test_example name () = + let greeting = "Hello " ^ name ^ "!" in + let expected = Printf.sprintf "Hello %s!" name in + Alcotest.check Alcotest.string "same string" greeting expected + + + +let test_reference_tree_to_json from_dir to_file () = + let _ = print_endline (Sys.getcwd()) in + let _ = create_newdir "../../../test/testresults" 0o777 in + Vyos1x.Generate.reference_tree_to_json from_dir to_file + +let interfaceDefinitionSuite = + [ + "can greet Tom", `Quick, test_example "Tom"; + "Generate xml to json cache", `Quick, test_reference_tree_to_json "../../../test/testdata/interface-definitions" "../../../test/testresults/xml_cache.json"; + ] + +let () = + Alcotest.run "Test Suite" [ "InterfaceDefinitions", interfaceDefinitionSuite ] \ No newline at end of file diff --git a/test/testdata/interface-definitions/container.xml b/test/testdata/interface-definitions/container.xml new file mode 100644 index 0000000..f38310d --- /dev/null +++ b/test/testdata/interface-definitions/container.xml @@ -0,0 +1,638 @@ + + + + Container applications + 450 + + + + + Container name + + [-a-zA-Z0-9]+ + + Container name must be alphanumeric and can contain hyphens + + + + + + + + + + + + + + + + + + + + + + + + + + + Allow host networks in container + + + + + + Grant individual Linux capability to container instance + + net-admin net-bind-service net-raw setpcap sys-admin sys-module sys-nice sys-time + + + net-admin + Network operations (interface, firewall, routing tables) + + + net-bind-service + Bind a socket to privileged ports (port numbers less than 1024) + + + net-raw + Permission to create raw network sockets + + + setpcap + Capability sets (from bounded or inherited set) + + + sys-admin + Administation operations (quotactl, mount, sethostname, setdomainame) + + + sys-module + Load, unload and delete kernel modules + + + sys-nice + Permission to set process nice value + + + sys-time + Permission to set system clock + + + (net-admin|net-bind-service|net-raw|setpcap|sys-admin|sys-module|sys-nice|sys-time) + + + + + + + + Description + + txt + Description + + + .{0,255} + + Description too long (limit 255 characters) + + + + + + Add a host device to the container + + + + + Source device (Example: "/dev/x") + + txt + Source device + + + + + + Destination container device (Example: "/dev/x") + + txt + Destination container device + + + + + + + + + Disable instance + + + + + + + Add custom environment variables + + [-_a-zA-Z0-9]+ + + Environment variable name must be alphanumeric and can contain hyphen and underscores + + + + + Set environment option value + + txt + Set environment option value + + + + + + + + Override the default ENTRYPOINT from the image + + [ !#-%&(-~]+ + + Entrypoint must be ASCII characters, use " and &apos for double and single quotes respectively + + + + + Container host name + + +[A-Za-z0-9][-.A-Za-z0-9]*[A-Za-z0-9] + + + Host-name must be alphanumeric and can contain hyphens + + + + + Container image to use + + + + + txt + Image name in the hub-registry + + + [[:ascii:]]{1,255} + + + + + + Override the default CMD from the image + + [ !#-%&(-~]+ + + Command must be ASCII characters, use " and &apos for double and single quotes respectively + + + + + The command's arguments for this container + + [ !#-%&(-~]+ + + The command's arguments must be ASCII characters, use " and &apos for double and single quotes respectively + + + + + Add label variables + + [a-z0-9](?:[a-z0-9.-]*[a-z0-9])? + + Label variable name must be alphanumeric and can contain hyphen, dots and underscores + + + + + Set label option value + + txt + Set label option value + + + [[:ascii:]]{1,255} + + + + + + + + Memory (RAM) available to this container + + u32:0 + Unlimited + + + u32:1-16384 + Container memory in megabytes (MB) + + + + + Container memory must be in range 0 to 16384 MB + + 512 + + + + Shared memory available to this container + + u32:0 + Unlimited + + + u32:1-8192 + Container memory in megabytes (MB) + + + + + Container memory must be in range 0 to 8192 MB + + 64 + + + + Attach user defined network to container + + container network + + + + [-_a-zA-Z0-9]{1,11} + +Network name cannot be longer than 11 characters + + + + + + Assign static IP address to container + + ipv4 + IPv4 address + + + ipv6 + IPv6 address + + + + + + + + + + + + Publish port to the container + + + + + + Local IP addresses to listen on + + + + + ipv4 + IPv4 address to listen for incoming connections + + + ipv6 + IPv6 address to listen for incoming connections + + + + + + + + + + + + Source host port + + u32:1-65535 + Source host port + + + start-end + Source host port range (e.g. 10025-10030) + + + + + + + + + Destination container port + + u32:1-65535 + Destination container port + + + start-end + Destination container port range (e.g. 10025-10030) + + + + + + + + + Transport protocol used for port mapping + + tcp udp + + + tcp + Use Transmission Control Protocol for given port + + + udp + Use User Datagram Protocol for given port + + + (tcp|udp) + + + tcp + + + + + + Restart options for container + + no on-failure always + + + no + Do not restart containers on exit + + + on-failure + Restart containers when they exit with a non-zero exit code, retrying indefinitely + + + always + Restart containers when they exit, regardless of status, retrying indefinitely + + + (no|on-failure|always) + + + on-failure + + + + User ID this container will run as + + u32:0-65535 + User ID this container will run as + + + + + + + + + Group ID this container will run as + + u32:0-65535 + Group ID this container will run as + + + + + + + + + Mount a volume into the container + + + + + Source host directory + + txt + Source host directory + + + + + + Destination container directory + + txt + Destination container directory + + + + + + Volume access mode ro/rw + + ro rw + + + ro + Volume mounted into the container as read-only + + + rw + Volume mounted into the container as read-write + + + (ro|rw) + + + rw + + + + Volume bind propagation + + shared slave private rshared rslave rprivate + + + shared + Sub-mounts of the original mount are exposed to replica mounts + + + slave + Allow replica mount to see sub-mount from the original mount but not vice versa + + + private + Sub-mounts within a mount are not visible to replica mounts or the original mount + + + rshared + Allows sharing of mount points and their nested mount points between both the original and replica mounts + + + rslave + Allows mount point and their nested mount points between original an replica mounts + + + rprivate + No mount points within original or replica mounts in any direction + + + (shared|slave|private|rshared|rslave|rprivate) + + + rprivate + + + + + + + + Network name + + + [-_a-zA-Z0-9]{1,11} + +Network name cannot be longer than 11 characters + + + + + + + Description + + txt + Description + + + .{0,255} + + Description too long (limit 255 characters) + + + + + + Prefix which allocated to that network + + ipv4net + IPv4 network prefix + + + ipv6net + IPv6 network prefix + + + + + + + + + + + + VRF instance name + + txt + VRF instance name + + + vrf name + + + + + +VRF instance name must be 15 characters or less and can not\nbe named as regular network interfaces.\nA name must starts from a letter.\n + + + + + + + + + Registry Name + + docker.io quay.io + + + + + Authentication settings + + + + + + Username used for authentication + + txt + Username + + + [[:ascii:]]{1,128} + + Username is limited to ASCII characters only, with a total length of 128 + + + + + + + Password used for authentication + + txt + Password + + + [[:ascii:]]{1,128} + + Password is limited to ASCII characters only, with a total length of 128 + + + + + + + + + + Disable instance + + + + + + + + + diff --git a/vyos1x-config.opam b/vyos1x-config.opam index 589f918..acb16f2 100644 --- a/vyos1x-config.opam +++ b/vyos1x-config.opam @@ -21,4 +21,7 @@ depends: [ "dune" {build & >= "1.4.0"} "ppx_deriving_yojson" "yojson" + "xml-light" + "pcre" + "fileutils" ] diff --git a/vyos1x-config.opam.locked b/vyos1x-config.opam.locked new file mode 100644 index 0000000..64bdd9d --- /dev/null +++ b/vyos1x-config.opam.locked @@ -0,0 +1,97 @@ +opam-version: "2.0" +name: "vyos1x-config" +version: "0.2" +synopsis: "VyOS 1.x and EdgeOS config file manipulation library" +description: + "A library for parsing, manipulating, and exporting VyOS 1.x and EdgeOS config files." +maintainer: "Daniil Baturin " +authors: "VyOS maintainers and contributors " +license: "MIT" +homepage: "https://github.com/vyos/vyos1x-config" +bug-reports: "https://phabricator.vyos.net" +depends: [ + "alcotest" {= "1.7.0"} + "angstrom" {= "0.16.0"} + "angstrom-lwt-unix" {= "0.16.0"} + "astring" {= "0.8.5"} + "base" {= "v0.17.0"} + "base-bigarray" {= "base"} + "base-bytes" {= "base"} + "base-domains" {= "base"} + "base-nnp" {= "base"} + "base-threads" {= "base"} + "base-unix" {= "base"} + "bigstringaf" {= "0.9.1"} + "cmdliner" {= "1.3.0"} + "conf-libpcre" {= "1"} + "conf-pkg-config" {= "3"} + "cppo" {= "1.6.9"} + "csexp" {= "1.5.2"} + "dap" {= "1.0.6"} + "dune" {= "3.15.3"} + "dune-configurator" {= "3.15.3"} + "earlybird" {= "1.3.2"} + "fileutils" {= "0.6.4"} + "fmt" {= "0.9.0"} + "iter" {= "1.8"} + "jane-street-headers" {= "v0.17.0"} + "jst-config" {= "v0.17.0"} + "logs" {= "0.7.0"} + "lru" {= "0.3.1"} + "lwt" {= "5.7.0"} + "lwt_ppx" {= "2.1.0"} + "lwt_react" {= "1.2.0"} + "menhir" {= "20231231"} + "menhirCST" {= "20231231"} + "menhirLib" {= "20231231"} + "menhirSdk" {= "20231231"} + "num" {= "1.5"} + "ocaml" {= "5.1.1"} + "ocaml-base-compiler" {= "5.1.1"} + "ocaml-compiler-libs" {= "v0.12.4"} + "ocaml-config" {= "3"} + "ocaml-options-vanilla" {= "1"} + "ocaml-syntax-shims" {= "1.0.0"} + "ocaml_intrinsics_kernel" {= "v0.17.0"} + "ocamlbuild" {= "0.14.3"} + "ocamlfind" {= "1.9.6"} + "ocplib-endian" {= "1.2"} + "parsexp" {= "v0.17.0"} + "path_glob" {= "0.3"} + "pcre" {= "7.5.0"} + "ppx_assert" {= "v0.17.0"} + "ppx_base" {= "v0.17.0"} + "ppx_cold" {= "v0.17.0"} + "ppx_compare" {= "v0.17.0"} + "ppx_derivers" {= "1.2.1"} + "ppx_deriving" {= "6.0.2"} + "ppx_deriving_yojson" {= "3.8.0"} + "ppx_enumerate" {= "v0.17.0"} + "ppx_expect" {= "v0.17.0"} + "ppx_globalize" {= "v0.17.0"} + "ppx_hash" {= "v0.17.0"} + "ppx_here" {= "v0.17.0"} + "ppx_inline_test" {= "v0.17.0"} + "ppx_optcomp" {= "v0.17.0"} + "ppx_sexp_conv" {= "v0.17.0"} + "ppxlib" {= "0.32.1"} + "ppxlib_jane" {= "v0.17.0"} + "psq" {= "0.2.1"} + "re" {= "1.11.0"} + "react" {= "1.2.2"} + "seq" {= "base"} + "sexplib" {= "v0.17.0"} + "sexplib0" {= "v0.17.0"} + "stdio" {= "v0.17.0"} + "stdlib-shims" {= "0.3.0"} + "time_now" {= "v0.17.0"} + "topkg" {= "1.0.7"} + "uutf" {= "1.0.3"} + "xml-light" {= "2.5"} + "yojson" {= "2.1.2"} +] +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name] +] +dev-repo: "git+https://github.com/vyos/vyos1x-config/" \ No newline at end of file