diff options
Diffstat (limited to 'scripts/build-command-op-templates')
| -rwxr-xr-x | scripts/build-command-op-templates | 225 | 
1 files changed, 225 insertions, 0 deletions
| diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates new file mode 100755 index 000000000..c60b32a1e --- /dev/null +++ b/scripts/build-command-op-templates @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# +#    build-command-template: converts new style command definitions in XML +#      to the old style (bunch of dirs and node.def's) command templates +# +#    Copyright (C) 2017 VyOS maintainers <maintainers@vyos.net> +# +#    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 +#    USA + +import sys +import os +import argparse +import copy +import functools + +from lxml import etree as ET + +# Defaults + +validator_dir = "/opt/vyatta/libexec/validators" +default_constraint_err_msg = "Invalid value" + + +## Get arguments + +parser = argparse.ArgumentParser(description='Converts new-style XML interface definitions to old-style command templates') +parser.add_argument('--debug', help='Enable debug information output', action='store_true') +parser.add_argument('INPUT_FILE', type=str, help="XML interface definition file") +parser.add_argument('SCHEMA_FILE', type=str, help="RelaxNG schema file") +parser.add_argument('OUTPUT_DIR', type=str, help="Output directory") + +args = parser.parse_args() + +input_file = args.INPUT_FILE +schema_file = args.SCHEMA_FILE +output_dir = args.OUTPUT_DIR +debug = args.debug + +## Load and validate the inputs + +try: +    xml = ET.parse(input_file) +except Exception as e: +    print("Failed to load interface definition file {0}".format(input_file)) +    print(e) +    sys.exit(1) + +try: +    relaxng_xml = ET.parse(schema_file) +    validator = ET.RelaxNG(relaxng_xml) + +    if not validator.validate(xml): +        print(validator.error_log) +        print("Interface definition file {0} does not match the schema!".format(input_file)) +        sys.exit(1) +except Exception as e: +    print("Failed to load the XML schema {0}".format(schema_file)) +    print(e) +    sys.exit(1) + +if not os.access(output_dir, os.W_OK): +    print("The output directory {0} is not writeable".format(output_dir)) +    sys.exit(1) + +## If we got this far, everything must be ok and we can convert the file + +def make_path(l): +    path = functools.reduce(os.path.join, l) +    if debug: +        print(path) +    return path + +def get_properties(p): +    props = {} + +    if p is None: +        return props + +    # Get the help string +    try: +        props["help"] = p.find("help").text +    except: +        props["help"] = "No help available" + + +    # Get the completion help strings +    try: +        che = p.findall("completionHelp") +        ch = "" +        for c in che: +            scripts = c.findall("script") +            paths = c.findall("path") +            lists = c.findall("list") + +            # Current backend doesn't support multiple allowed: tags +            # so we get to emulate it +            comp_exprs = [] +            for i in lists: +                comp_exprs.append("echo \"{0}\"".format(i.text)) +            for i in paths: +                comp_exprs.append("/bin/cli-shell-api listActiveNodes {0} | sed -e \"s/'//g\" && echo".format(i.text)) +            for i in scripts: +                comp_exprs.append("{0}".format(i.text)) +            comp_help = " && ".join(comp_exprs) +            props["comp_help"] = comp_help +    except: +        props["comp_help"] = [] + +    return props + + +def make_node_def(props, command): +    # XXX: replace with a template processor if it grows +    #      out of control + +    node_def = "" + +    if "help" in props: +        node_def += "help: {0}\n".format(props["help"]) + + +    if "comp_help" in props: +        node_def += "allowed: {0}\n".format(props["comp_help"]) + + +    if command is not None: +        node_def += "run: {0}\n".format(command.text) + + +    if debug: +        print("The contents of the node.def file:\n", node_def) + +    return node_def + +def process_node(n, tmpl_dir): +    # Avoid mangling the path from the outer call +    my_tmpl_dir = copy.copy(tmpl_dir) + +    props_elem = n.find("properties") +    children = n.find("children") +    command = n.find("command") + +    name = n.get("name") + +    node_type = n.tag + +    my_tmpl_dir.append(name) + +    if debug: +        print("Name of the node: {};\n Created directory: ".format(name), end="") +    os.makedirs(make_path(my_tmpl_dir), exist_ok=True) + +    props = get_properties(props_elem) + +    if node_type == "node": +        if debug: +          print("Processing node {}".format(name)) + +        nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") +        if not os.path.exists(nodedef_path): +            with open(nodedef_path, "w") as f: +                f.write(make_node_def(props, command)) +        else: +           # Something has already generated this file +           pass + +        if children is not None: +            inner_nodes = children.iterfind("*") +            for inner_n in inner_nodes: +                process_node(inner_n, my_tmpl_dir) +    if node_type == "tagNode": +        if debug: +          print("Processing tag node {}".format(name)) + +        os.makedirs(make_path(my_tmpl_dir), exist_ok=True) + +        nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") +        if not os.path.exists(nodedef_path): +            with open(nodedef_path, "w") as f: +                f.write('help: {0}\n'.format(props['help'])) +        else: +            # Something has already generated this file +            pass + +        # Create the inner node.tag part +        my_tmpl_dir.append("node.tag") +        os.makedirs(make_path(my_tmpl_dir), exist_ok=True) +        if debug: +            print("Created path for the tagNode: {}".format(make_path(my_tmpl_dir)), end="") + +        # Not sure if we want partially defined tag nodes, write the file unconditionally +        with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: +            f.write(make_node_def(props, command)) + +        if children is not None: +            inner_nodes = children.iterfind("*") +            for inner_n in inner_nodes: +                process_node(inner_n, my_tmpl_dir) +    else: +        # This is a leaf node +        if debug: +            print("Processing leaf node {}".format(name)) + +        with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: +            f.write(make_node_def(props, command)) + + +root = xml.getroot() + +nodes = root.iterfind("*") +for n in nodes: +    process_node(n, [output_dir]) | 
