From cb5a0226c3d514896a1818e1681ad9d35eac6fb8 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Sun, 27 May 2018 22:40:14 +0700 Subject: Add a formatter for VyOS 1.x config files. --- Makefile | 3 +- lib/vyos1x_renderer.ml | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 lib/vyos1x_renderer.ml diff --git a/Makefile b/Makefile index 7ce3047..e196d22 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ LIBFILES=$(BUILDDIR)/lib/bindings.cmx \ $(BUILDDIR)/lib/apply_bindings.cmx \ $(BUILDDIR)/generated/vyosconfig.o \ $(BUILDDIR)/lib/vyos1x_parser.cmx \ - $(BUILDDIR)/lib/vyos1x_lexer.cmx + $(BUILDDIR)/lib/vyos1x_lexer.cmx \ + $(BUILDDIR)/lib/vyos1x_renderer.cmx CAML_INIT=$(BUILDDIR)/stub/init.o diff --git a/lib/vyos1x_renderer.ml b/lib/vyos1x_renderer.ml new file mode 100644 index 0000000..4a1f4a0 --- /dev/null +++ b/lib/vyos1x_renderer.ml @@ -0,0 +1,82 @@ +(* The renderer makes two assumptions about the invariants of the config files: + that top level nodes are never tag or leaf nodes, + and that immediate children of tag nodes are never themselves tag nodes. + + They are true in all existing VyOS configs and configs with those invariant + broken will never load, so these assumptions are safe to make when + processing existing configs. In configs built from scratch, the user is + responsible for its validness. + + The original loader behaviour with tag nodes is strange: deep down, after + config loading, they are indistinguishable from any other nodes, but at load + time, they fail to validate unless they are formatted as tag nodes in the + config file, that is, "ethernet eth0 { ..." as opposed to "ethernet { eth0 { ...". + + Since Vyconf makes no distinction between normal nodes and tag nodes other than + at set command validation and formatting time, I reused the ephemeral flag + which is never used in VyOS 1.x for marking nodes as tag nodes at parsing time. + + *) + +module CT = Config_tree +module VT = Vytree + +let make_indent indent level = String.make (level * indent) ' ' + +let render_values indent_str name values = + match values with + | [] -> Printf.sprintf "%s%s { }\n" indent_str name + | [v] -> Printf.sprintf "%s%s %s\n" indent_str name v + | _ -> + let rendered = List.map (fun s -> Printf.sprintf "%s%s %s" indent_str name s) values in + String.concat "\n" rendered + +let render_comment indent c = + match c with + | None -> "" + | Some c -> Printf.sprintf "%s/* %s */\n" indent c + +let rec render_node indent level node = + let open CT in + let indent_str = make_indent indent level in + let name = VT.name_of_node node in + let data = VT.data_of_node node in + let is_tag = data.ephemeral (* sic! look in the parser *) in + let comment = render_comment indent_str data.comment in + let values = render_values indent_str name data.values in + let children = VT.children_of_node node in + match children with + | [] -> Printf.sprintf "%s\n%s" comment values + | _ -> + if is_tag then + begin + let inner = List.map (render_tag_node indent (level + 1) name) children in + String.concat "\n" inner + end + else + begin + let inner = List.map (render_node indent (level + 1)) children in + let inner = String.concat "\n" inner in + Printf.sprintf "%s%s%s {\n%s\n%s}\n" indent_str comment name inner indent_str + end + +and render_tag_node indent level parent node = + let open CT in + let indent_str = make_indent indent level in + let name = VT.name_of_node node in + let data = VT.data_of_node node in + let comment = render_comment indent_str data.comment in + let values = render_values indent_str name data.values in + let children = VT.children_of_node node in + match children with + | [] -> Printf.sprintf "%s\n%s" comment values + | _ -> + (* Exploiting the fact that immediate children of tag nodes are + never themselves tag nodes *) + let inner = List.map (render_node indent (level + 1)) children in + let inner = String.concat "\n" inner in + Printf.sprintf "%s%s%s %s {\n%s\n%s}\n" indent_str comment parent name inner indent_str + + + + -- cgit v1.2.3