From: Daniel Walton Date: Fri, 23 Sep 2016 03:27:05 +0000 (+0000) Subject: tools: added alias_destroyer.py X-Git-Tag: frr-3.0-branchpoint~129^2~184 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=c66bbd697dd04c158807e7de81ac7691616c79a3;p=matthieu%2Ffrr.git tools: added alias_destroyer.py Signed-off-by: Daniel Walton --- diff --git a/tools/alias_destroyer.py b/tools/alias_destroyer.py new file mode 100755 index 0000000000..b5aba48fb1 --- /dev/null +++ b/tools/alias_destroyer.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python + +import re +import sys +import os +from pprint import pformat, pprint + + +class DEFUN(object): + + def __init__(self, lines): + # name, name_cmd, command_string, help_strings, guts): + self.name = None + self.name_cmd = None + self.command_string = None + self.help_strings = [] + self.guts = [] + self.aliases = [] + + ''' +DEFUN (no_bgp_maxmed_onstartup, + no_bgp_maxmed_onstartup_cmd, + "no bgp max-med on-startup", + NO_STR + BGP_STR + "Advertise routes with max-med\n" + "Effective on a startup\n") + + ''' + state = 'HEADER' + for (line_number, line) in enumerate(lines): + + if state == 'HEADER': + if line_number == 0: + re_name = re.search('DEFUN \((.*),', line.strip()) + self.name = re_name.group(1) + + elif line_number == 1: + self.name_cmd = line.strip()[0:-1] # chop the trailing comma + + elif line_number == 2: + self.command_string = line + state = 'HELP' + + elif state == 'HELP': + if line.strip() == '{': + self.guts.append(line) + state = 'BODY' + else: + self.help_strings.append(line) + + elif state == 'BODY': + if line.rstrip() == '}': + self.guts.append(line) + state = None + else: + self.guts.append(line) + + else: + raise Exception("invalid state %s" % state) + + # print "%d %7s: %s" % (line_number, state, line.rstrip()) + + assert self.command_string, "No command string for\n%s" % pformat(lines) + + def __str__(self): + return self.name + + def dump(self): + lines = [] + + if self.aliases: + lines.append("/*\n") + lines.append(" * CHECK ME - The following ALIASes need to be implemented in this DEFUN\n") + + for alias in self.aliases: + lines.append(" * %s\n" % alias.command_string.strip()) + for line in alias.help_strings: + lines.append(" * %s\n" % line) + lines.append(" *\n") + + lines.append(" */\n") + + lines.append("DEFUN (%s,\n" % self.name) + lines.append(" %s,\n" % self.name_cmd) + lines.append(self.command_string) + lines.extend(self.help_strings) + lines.extend(self.guts) + return ''.join(lines) + + +class ALIAS(object): + + def __init__(self, lines): + self.name = None + self.name_cmd = None + self.command_string = None + self.help_strings = [] + + ''' +ALIAS (no_bgp_maxmed_onstartup, + no_bgp_maxmed_onstartup_period_cmd, + "no bgp max-med on-startup <5-86400>", + NO_STR + BGP_STR + "Advertise routes with max-med\n" + "Effective on a startup\n" + "Time (seconds) period for max-med\n") + ''' + state = 'HEADER' + for (line_number, line) in enumerate(lines): + + if state == 'HEADER': + if line_number == 0: + re_name = re.search('ALIAS \((.*),', line) + + try: + self.name = re_name.group(1) + except AttributeError: + pprint(lines) + raise + + elif line_number == 1: + self.name_cmd = line.strip()[0:-1] # chop the trailing comma + + elif line_number == 2: + self.command_string = line + state = 'HELP' + + elif state == 'HELP': + if line.strip() == '{': + raise Exception("should not see { in an ALIAS") + else: + line = line.strip() + if line.endswith(')'): + line = line[0:-1] # strip the trailing ) + self.help_strings.append(line) + + else: + raise Exception("invalid state %s" % state) + + assert self.command_string, "No command string for\n%s" % pformat(lines) + + def __str__(self): + return self.name_cmd + + +def alias_destroy(filename): + lines = [] + defuns = {} + aliases = {} + + with open(filename, 'r') as fh: + state = None + defun_lines = [] + alias_lines = [] + + for (line_number, line) in enumerate(fh.readlines()): + + if state is None: + if line.startswith('DEFUN ('): + assert line.count(',') == 1, "%d: Too many commas in\n%s" % (line_number, line) + defun_lines.append(line) + state = 'DEFUN_HEADER' + + elif line.startswith('ALIAS ('): + assert line.count(',') == 1, "%d: Too many commas in\n%s" % (line_number, line) + alias_lines.append(line) + state = 'ALIAS_HEADER' + + elif state == 'DEFUN_HEADER': + defun_lines.append(line) + + if line.startswith('DEFUN'): + raise Exception("ERROR on line %d, found DEFUN inside DEFUN" % line_number) + + elif line.startswith('ALIAS'): + raise Exception("ERROR on line %d, found ALIAS inside DEFUN" % line_number) + + elif line.strip() == '{': + state = 'DEFUN_BODY' + + elif state == 'ALIAS_HEADER': + alias_lines.append(line) + + if line.startswith('ALIAS'): + raise Exception("ERROR on line %d, found ALIAS inside ALIAS" % line_number) + + elif line.startswith('DEFUN'): + raise Exception("ERROR on line %d, found DEFUN inside ALIAS" % line_number) + + if line.rstrip().endswith(')'): + new_alias = ALIAS(alias_lines) + aliases[new_alias.name_cmd] = new_alias + state = None + alias_lines = [] + + elif state == 'DEFUN_BODY': + defun_lines.append(line) + + if line.rstrip() == '}': + new_defun = DEFUN(defun_lines) + defuns[new_defun.name] = new_defun + state = None + defun_lines = [] + + # uncomment to debug state machine + print "%5d %12s: %s" % (line_number, state, line.rstrip()) + + lines.append(line) + + + # At this point we know all of the aliases and all of the tokens + # Assign each ALIAS to its parent DEFUN + for alias in aliases.itervalues(): + defun = defuns.get(alias.name) + assert defun, "Could not find DEFUN for %s" % alias + defun.aliases.append(alias) + + # Now write the file but: + # - do not write any ALIASes + # - do not write the install_element for any ALIASes + # - when you write the DEFUN include a comment that contains the ALIAS command strings it needs to cover + with open(filename, 'w') as fh: + state = None + + for line in lines: + + if state is None: + if line.startswith('DEFUN ('): + state = 'DEFUN_HEADER' + re_name = re.search('DEFUN \((.*),', line.strip()) + name = re_name.group(1) + defun = defuns.get(name) + fh.write(defun.dump()) + + elif line.startswith('ALIAS ('): + state = 'ALIAS_HEADER' + + else: + if 'install_element' in line: + # install_element (CONFIG_NODE, &ip_community_list_name_standard_cmd); + re_install_element = re.search('install_element\s*\(\w+,\s*&(.*)\s*\)', line.strip()) + cmd = re_install_element.group(1) + if cmd not in aliases: + fh.write(line) + else: + fh.write(line) + + elif state == 'DEFUN_HEADER': + if line.strip() == '{': + state = 'DEFUN_BODY' + + elif state == 'ALIAS_HEADER': + if line.rstrip().endswith(')'): + state = None + + elif state == 'DEFUN_BODY': + if line.rstrip() == '}': + state = None + + +if __name__ == '__main__': + + filename = sys.argv[1] + if os.path.exists(filename): + alias_destroy(filename) + else: + print "ERROR: could not find file %s" % filename