]> git.puffer.fish Git - matthieu/frr.git/commitdiff
tools: added alias_destroyer.py
authorDaniel Walton <dwalton@cumulusnetworks.com>
Fri, 23 Sep 2016 03:27:05 +0000 (03:27 +0000)
committerDaniel Walton <dwalton@cumulusnetworks.com>
Fri, 23 Sep 2016 03:27:05 +0000 (03:27 +0000)
Signed-off-by: Daniel Walton <dwalton@cumulusnetworks.com>
tools/alias_destroyer.py [new file with mode: 0755]

diff --git a/tools/alias_destroyer.py b/tools/alias_destroyer.py
new file mode 100755 (executable)
index 0000000..b5aba48
--- /dev/null
@@ -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