diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/build-command-templates | 2 | ||||
-rwxr-xr-x | scripts/import-conf-mode-commands | 255 | ||||
-rwxr-xr-x | scripts/override-default | 18 |
3 files changed, 17 insertions, 258 deletions
diff --git a/scripts/build-command-templates b/scripts/build-command-templates index c8ae83d9d..2e7f8b994 100755 --- a/scripts/build-command-templates +++ b/scripts/build-command-templates @@ -145,6 +145,8 @@ def get_properties(p, default=None): description = v.find("description").text if default != None and default.text == format: description += f' (default)' + # Is no description was specified, keep it empty + if not description: description = '' vh.append( (format, description) ) props["val_help"] = vh except: diff --git a/scripts/import-conf-mode-commands b/scripts/import-conf-mode-commands deleted file mode 100755 index 996b31c9c..000000000 --- a/scripts/import-conf-mode-commands +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python3 -# -# build-command-template: converts old style commands definitions to XML -# -# Copyright (C) 2019 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 os -import re -import sys - -from lxml import etree - - -# Node types -NODE = 0 -LEAF_NODE = 1 -TAG_NODE = 2 - -def parse_command_data(t): - regs = { - 'help': r'\bhelp:(.*)(?:\n|$)', - 'priority': r'\bpriority:(.*)(?:\n|$)', - 'type': r'\btype:(.*)(?:\n|$)', - 'syntax_expression_var': r'\bsyntax:expression: \$VAR\(\@\) in (.*)' - } - - data = {'multi': False, 'help': ""} - - for r in regs: - try: - data[r] = re.search(regs[r], t).group(1).strip() - except: - data[r] = None - - # val_help is special: there can be multiple instances - val_help_strings = re.findall(r'\bval_help:(.*)(?:\n|$)', t) - val_help = [] - for v in val_help_strings: - try: - fmt, msg = re.match(r'\s*(.*)\s*;\s*(.*)\s*(?:\n|$)', v).groups() - except: - fmt = "<text>" - msg = v - val_help.append((fmt, msg)) - data['val_help'] = val_help - - # multi is on/off - if re.match(r'\bmulti:', t): - data['multi'] = True - - return(data) - -def walk(tree, base_path, name): - path = os.path.join(base_path, name) - - contents = os.listdir(path) - - # Determine node type and create XML element for the node - # Tag node dirs will always have 'node.tag' subdir and 'node.def' file - # Leaf node dirs have nothing but a 'node.def' file - # Everything that doesn't match either of these patterns is a normal node - if 'node.tag' in contents: - print("Creating a tag node from {0}".format(path)) - elem = etree.Element('tagNode') - node_type = TAG_NODE - elif contents == ['node.def']: - print("Creating a leaf node from {0}".format(path)) - elem = etree.Element('leafNode') - node_type = LEAF_NODE - else: - print("Creating a node from {0}".format(path)) - elem = etree.Element('node') - node_type = NODE - - # Read and parse the command definition data (the 'node.def' file) - with open(os.path.join(path, 'node.def'), 'r') as f: - node_def = f.read() - data = parse_command_data(node_def) - - # Import the data into the properties element - props_elem = etree.Element('properties') - - if data['priority']: - # Priority values sometimes come with comments that explain the value choice - try: - prio, prio_comment = re.match(r'\s*(\d+)\s*#(.*)', data['priority']).groups() - except: - prio = data['priority'].strip() - prio_comment = None - prio_elem = etree.Element('priority') - prio_elem.text = prio - props_elem.append(prio_elem) - if prio_comment: - prio_comment_elem = etree.Comment(prio_comment) - props_elem.append(prio_comment_elem) - - if data['multi']: - multi_elem = etree.Element('multi') - props_elem.append(multi_elem) - - if data['help']: - help_elem = etree.Element('help') - help_elem.text = data['help'] - props_elem.append(help_elem) - - # For leaf nodes, absense of a type: tag means they take no values - # For any other nodes, it doesn't mean anything - if not data['type'] and (node_type == LEAF_NODE): - valueless = etree.Element('valueless') - props_elem.append(valueless) - - # There can be only one constraint element in the definition - # Create it now, we'll modify it in the next two cases, then append - constraint_elem = etree.Element('constraint') - has_constraint = False - - # Add regexp field for multiple options - if data['syntax_expression_var']: - regex = etree.Element('regex') - constraint_error=etree.Element('constraintErrorMessage') - values = re.search(r'(.+) ; (.+)', data['syntax_expression_var']).group(1) - message = re.search(r'(.+) ; (.+)', data['syntax_expression_var']).group(2) - values = re.findall(r'\"(.+?)\"', values) - regex.text = '|'.join(values) - constraint_error.text = re.sub('\".*?VAR.*?\"', '', message) - constraint_error.text = re.sub(r'[\"|\\]', '', message) - constraint_elem.append(regex) - props_elem.append(constraint_elem) - props_elem.append(constraint_error) - - if data['val_help']: - for vh in data['val_help']: - vh_elem = etree.Element('valueHelp') - - vh_fmt_elem = etree.Element('format') - # Many commands use special "u32:<start>-<end>" format for ranges - if re.match(r'u32:', vh[0]): - vh_fmt = re.match(r'u32:(.*)', vh[0]).group(1).strip() - - # If valid range of values is specified in val_help, we can automatically - # create a constraint for it - # Extracting it from syntax:expression: would be much more complicated - vh_validator = etree.Element('validator') - vh_validator.set("name", "numeric") - vh_validator.set("argument", "--range {0}".format(vh_fmt)) - constraint_elem.append(vh_validator) - has_constraint = True - else: - vh_fmt = vh[0] - vh_fmt_elem.text = vh_fmt - - vh_help_elem = etree.Element('description') - vh_help_elem.text = vh[1] - - vh_elem.append(vh_fmt_elem) - vh_elem.append(vh_help_elem) - props_elem.append(vh_elem) - - # Translate the "type:" to the new validator system - if data['type']: - t = data['type'] - if t == 'txt': - # Can't infer anything from the generic "txt" type - pass - else: - validator = etree.Element('validator') - if t == 'u32': - validator.set('name', 'numeric') - validator.set('argument', '--non-negative') - elif t == 'ipv4': - validator.set('name', 'ipv4-address') - elif t == 'ipv4net': - validator.set('name', 'ipv4-prefix') - elif t == 'ipv6': - validator.set('name', 'ipv6-address') - elif t == 'ipv6net': - validator.set('name', 'ipv6-prefix') - elif t == 'macaddr': - validator.set('name', 'mac-address') - else: - print("Warning: unsupported type \'{0}\'".format(t)) - validator = None - - if (validator is not None) and (not has_constraint): - # If has_constraint is true, it means a more specific validator - # was already extracted from another option - constraint_elem.append(validator) - has_constraint = True - - if has_constraint: - props_elem.append(constraint_elem) - - elem.append(props_elem) - - elem.set("name", name) - - if node_type != LEAF_NODE: - children = etree.Element('children') - - # Create the next level dir path, - # accounting for the "virtual" node.tag subdir for tag nodes - next_level = path - if node_type == TAG_NODE: - next_level = os.path.join(path, 'node.tag') - - # Walk the subdirs of the next level - for d in os.listdir(next_level): - dp = os.path.join(next_level, d) - if os.path.isdir(dp): - walk(children, next_level, d) - - elem.append(children) - - tree.append(elem) - -if __name__ == '__main__': - if len(sys.argv) < 2: - print("Usage: {0} <base path>".format(sys.argv[0])) - sys.exit(1) - else: - base_path = sys.argv[1] - - root = etree.Element('interfaceDefinition') - contents = os.listdir(base_path) - elem = etree.Element('node') - elem.set('name', os.path.basename(base_path)) - children = etree.Element('children') - - for c in contents: - path = os.path.join(base_path, c) - if os.path.isdir(path): - walk(children, base_path, c) - - elem.append(children) - root.append(elem) - - xml_data = etree.tostring(root, pretty_print=True).decode() - with open('output.xml', 'w') as f: - f.write(xml_data) diff --git a/scripts/override-default b/scripts/override-default index 0c49087c8..5058e79b3 100755 --- a/scripts/override-default +++ b/scripts/override-default @@ -41,6 +41,14 @@ if debug: else: logger.setLevel(logging.INFO) +def clear_empty_path(el): + # on the odd chance of interleaved comments + tmp = [l for l in el if isinstance(l.tag, str)] + if not tmp: + p = el.getparent() + p.remove(el) + clear_empty_path(p) + def override_element(l: list): """ Allow multiple override elements; use the final one (in document order). @@ -59,7 +67,9 @@ def override_element(l: list): # remove all but overridden element for el in parents: - el.getparent().remove(el) + tmp = el.getparent() + tmp.remove(el) + clear_empty_path(tmp) def merge_remaining(l: list, elementtree): """ @@ -81,7 +91,9 @@ def merge_remaining(l: list, elementtree): # override_element() has already run for child in el: rp[0].append(deepcopy(child)) - el.getparent().remove(el) + tmp = el.getparent() + tmp.remove(el) + clear_empty_path(tmp) def collect_and_override(dir_name): """ @@ -104,7 +116,7 @@ def collect_and_override(dir_name): for k, v in defv.items(): if len(v) > 1: - logger.info(f"overridding default in path '{k}'") + logger.info(f"overriding default in path '{k}'") override_element(v) to_merge = list(defv) |