diff options
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/argv_translator.py | 363 |
1 files changed, 283 insertions, 80 deletions
diff --git a/tools/argv_translator.py b/tools/argv_translator.py index 4ee0831ed..eff05a412 100755 --- a/tools/argv_translator.py +++ b/tools/argv_translator.py @@ -4,6 +4,7 @@ import re import sys import os import subprocess +from collections import OrderedDict from copy import deepcopy from pprint import pformat, pprint @@ -156,7 +157,7 @@ def get_argv_translator(line_number, line): return table ''' -def get_argv_variable_indexes(line_number, line): +def get_command_string_variable_indexes(line_number, line): indexes = {} line = line.strip() @@ -177,6 +178,230 @@ def get_argv_variable_indexes(line_number, line): return (max_index, indexes) +def get_token_index_variable_name(line_number, token): + + re_range = re.search('\(\d+-\d+\)', token) + + if token.startswith('['): + assert token.endswith(']'), "Token %s should end with ]" % token + token = token[1:-1] + + if token.startswith('<'): + assert token.endswith('>'), "Token %s should end with >" % token + token = token[1:-1] + + if token == 'A.B.C.D': + return 'idx_ipv4' + + elif token == 'A.B.C.D/M': + return 'idx_ipv4_prefixlen' + + elif token == 'X:X::X:X': + return 'idx_ipv6' + + elif token == 'X:X::X:X/M': + return 'idx_ipv6_prefixlen' + + elif token == 'ASN:nn_or_IP-address:nn': + return 'idx_ext_community' + + elif token == '.AA:NN': + return 'idx_community' + + elif token == 'WORD': + return 'idx_word' + + elif token == 'json': + return 'idx_json' + + elif token == '.LINE': + return 'idx_regex' + + elif token == 'A.B.C.D|INTERFACE': + return 'idx_ipv4_ifname' + + elif token == 'A.B.C.D|INTERFACE|null0': + return 'idx_ipv4_ifname_null' + + elif token == 'X:X::X:X|INTERFACE': + return 'idx_ipv6_ifname' + + elif token == 'reject|blackhole': + return 'idx_reject_blackhole' + + elif token == 'route-map NAME': + return 'idx_route_map' + + elif token == 'recv|send|detail': + return 'idx_recv_send' + + elif token == 'recv|send': + return 'idx_recv_send' + + elif token == 'up|down': + return 'idx_up_down' + + elif token == 'off-link': + return 'idx_off_link' + + elif token == 'no-autoconfig': + return 'idx_no_autoconfig' + + elif token == 'router-address': + return 'idx_router_address' + + elif token == 'high|medium|low': + return 'idx_high_medium_low' + + elif token == '(0-4294967295)|infinite': + return 'idx_number_infinite' + + elif token == '(1-199)|(1300-2699)|WORD': + return 'idx_acl' + + elif token == 'A.B.C.D|X:X::X:X': + return 'idx_ip' + + elif token == 'urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix': + return 'idx_rpf_lookup_mode' + + elif token in ('kernel|connected|static|rip|ospf|isis|pim|table', + 'kernel|connected|static|ripng|ospf6|isis|table', + 'kernel|connected|static|rip|isis|bgp|pim|table', + 'kernel|connected|static|rip|ospf|isis|bgp|pim|table', + 'kernel|connected|static|rip|ospf|isis|bgp|pim|table', + 'kernel|connected|static|rip|ospf|isis|bgp|pim|table|any', + 'kernel|connected|static|ripng|ospf6|isis|bgp|table|any', + 'kernel|connected|static|ripng|ospf6|isis|bgp|table', + 'kernel|connected|static|ospf6|isis|bgp|table', + 'kernel|connected|static|ospf|isis|bgp|pim|table', + 'kernel|connected|static|ripng|isis|bgp|table', + # '', + 'bgp|ospf|rip|ripng|isis|ospf6|connected|system|kernel|static', + 'kernel|connected|static|rip|ripng|ospf|ospf6|bgp|pim|table'): + return 'idx_protocol' + + elif '|' in token: + raise Exception("%d: what variable name for %s" % (line_number, token)) + + elif re_range: + return 'idx_number' + + elif token.upper() == token: + return 'idx_%s' % token.lower() + + else: + raise Exception("%d: what variable name for %s" % (line_number, token)) + + +def get_command_string_index_variable_table(line_number, line): + """ + Return a table that maps an index position to a variable name such as 'idx_ipv4' + """ + indexes = OrderedDict() + + line = line.strip() + assert line.startswith('"'), "line does not start with \"\n%s" % (line) + assert line.endswith('",'), "line does not end with \",\n%s" % (line) + line = line[1:-2] + max_index = 0 + + for (token_index, token) in enumerate(line_to_tokens(line_number, line)): + if not token: + raise Exception("%d: empty token" % line_number) + + if token_is_variable(line_number, token): + # print "%s is a token" % token + idx_variable_name = get_token_index_variable_name(line_number, token) + count = 0 + for tmp in indexes.itervalues(): + if tmp == idx_variable_name: + count += 1 + elif re.search('^%s_\d+' % idx_variable_name, tmp): + count += 1 + if count: + idx_variable_name = "%s_%d" % (idx_variable_name, count + 1) + indexes[token_index] = idx_variable_name + + return indexes + +def expand_command_string(line): + + # in the middle + line = line.replace('" CMD_AS_RANGE "', '(1-4294967295)') + line = line.replace('" DYNAMIC_NEIGHBOR_LIMIT_RANGE "', '(1-5000)') + line = line.replace('" BGP_INSTANCE_CMD "', '<view|vrf> WORD') + line = line.replace('" BGP_INSTANCE_ALL_CMD "', '<view|vrf> all') + line = line.replace('" CMD_RANGE_STR(1, MULTIPATH_NUM) "', '(1-255)') + line = line.replace('" QUAGGA_IP_REDIST_STR_BGPD "', '<kernel|connected|static|rip|ospf|isis|pim|table>') + line = line.replace('" QUAGGA_IP6_REDIST_STR_BGPD "', '<kernel|connected|static|ripng|ospf6|isis|table>') + line = line.replace('" OSPF_LSA_TYPES_CMD_STR "', 'asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as') + line = line.replace('" QUAGGA_REDIST_STR_OSPFD "', '<kernel|connected|static|rip|isis|bgp|pim|table>') + line = line.replace('" VRF_CMD_STR "', 'vrf NAME') + line = line.replace('" VRF_ALL_CMD_STR "', 'vrf all') + line = line.replace('" QUAGGA_IP_PROTOCOL_MAP_STR_ZEBRA "', '<kernel|connected|static|rip|ospf|isis|bgp|pim|table|any>') + line = line.replace('" QUAGGA_IP6_PROTOCOL_MAP_STR_ZEBRA "', '<kernel|connected|static|ripng|ospf6|isis|bgp|table|any>') + line = line.replace('" QUAGGA_REDIST_STR_RIPNGD "', '<kernel|connected|static|ospf6|isis|bgp|table>') + line = line.replace('" QUAGGA_REDIST_STR_RIPD "', '<kernel|connected|static|ospf|isis|bgp|pim|table>') + line = line.replace('" QUAGGA_REDIST_STR_OSPF6D "', '<kernel|connected|static|ripng|isis|bgp|table>') + line = line.replace('" QUAGGA_REDIST_STR_ISISD "', '<kernel|connected|static|rip|ripng|ospf|ospf6|bgp|pim|table>') + line = line.replace('" LOG_FACILITIES "', '<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>') + + # endswith + line = line.replace('" CMD_AS_RANGE,', ' (1-4294967295)",') + line = line.replace('" DYNAMIC_NEIGHBOR_LIMIT_RANGE,', ' (1-5000)",') + line = line.replace('" BGP_INSTANCE_CMD,', ' <view|vrf> WORD",') + line = line.replace('" BGP_INSTANCE_ALL_CMD,', ' <view|vrf> all",') + line = line.replace('" CMD_RANGE_STR(1, MULTIPATH_NUM),', '(1-255)",') + line = line.replace('" CMD_RANGE_STR(1, MAXTTL),', '(1-255)",') + line = line.replace('" BFD_CMD_DETECT_MULT_RANGE BFD_CMD_MIN_RX_RANGE BFD_CMD_MIN_TX_RANGE,', '(2-255) (50-60000) (50-60000)",') + line = line.replace('" OSPF_LSA_TYPES_CMD_STR,', + ' asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as",') + line = line.replace('" BGP_UPDATE_SOURCE_REQ_STR,', ' <A.B.C.D|X:X::X:X|WORD>",') + line = line.replace('" BGP_UPDATE_SOURCE_OPT_STR,', ' [A.B.C.D|X:X::X:X|WORD]",') + line = line.replace('" QUAGGA_IP_REDIST_STR_BGPD,', ' <kernel|connected|static|rip|ospf|isis|pim|table>",') + line = line.replace('" QUAGGA_IP6_REDIST_STR_BGPD,', ' <kernel|connected|static|ripng|ospf6|isis|table>",') + line = line.replace('" QUAGGA_REDIST_STR_OSPFD,', ' <kernel|connected|static|rip|isis|bgp|pim|table>",') + line = line.replace('" VRF_CMD_STR,', ' vrf NAME",') + line = line.replace('" VRF_ALL_CMD_STR,', ' vrf all",') + line = line.replace('" QUAGGA_IP_REDIST_STR_ZEBRA,', ' <kernel|connected|static|rip|ospf|isis|bgp|pim|table>",') + line = line.replace('" QUAGGA_IP6_REDIST_STR_ZEBRA,', ' <kernel|connected|static|ripng|ospf6|isis|bgp|table>",') + line = line.replace('" QUAGGA_IP_PROTOCOL_MAP_STR_ZEBRA,', ' <kernel|connected|static|rip|ospf|isis|bgp|pim|table|any>",') + line = line.replace('" QUAGGA_IP6_PROTOCOL_MAP_STR_ZEBRA,', ' <kernel|connected|static|ripng|ospf6|isis|bgp|table|any>",') + line = line.replace('" QUAGGA_REDIST_STR_RIPNGD,', ' <kernel|connected|static|ospf6|isis|bgp|table>",') + line = line.replace('" QUAGGA_REDIST_STR_RIPD,', ' <kernel|connected|static|ospf|isis|bgp|pim|table>",') + line = line.replace('" PIM_CMD_IP_MULTICAST_ROUTING,', ' ip multicast-routing",') + line = line.replace('" PIM_CMD_IP_IGMP_QUERY_INTERVAL,', ' ip igmp query-interval",') + line = line.replace('" PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,', ' ip igmp query-max-response-time-dsec",') + line = line.replace('" PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME,', ' ip igmp query-max-response-time",') + line = line.replace('" QUAGGA_REDIST_STR_OSPF6D,', ' <kernel|connected|static|ripng|isis|bgp|table>",') + line = line.replace('" QUAGGA_REDIST_STR_ISISD,', ' <kernel|connected|static|rip|ripng|ospf|ospf6|bgp|pim|table>",') + line = line.replace('" LOG_FACILITIES,', ' <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",') + + # startswith + line = line.replace('LISTEN_RANGE_CMD "', '"bgp listen range <A.B.C.D/M|X:X::X:X/M> ') + line = line.replace('NO_NEIGHBOR_CMD2 "', '"no neighbor <A.B.C.D|X:X::X:X|WORD> ') + line = line.replace('NEIGHBOR_CMD2 "', '"neighbor <A.B.C.D|X:X::X:X|WORD> ') + line = line.replace('NO_NEIGHBOR_CMD "', '"no neighbor <A.B.C.D|X:X::X:X> ') + line = line.replace('NEIGHBOR_CMD "', '"neighbor <A.B.C.D|X:X::X:X> ') + line = line.replace('PIM_CMD_NO "', '"no ') + line = line.replace('PIM_CMD_IP_IGMP_QUERY_INTERVAL "', '"ip igmp query-interval ') + line = line.replace('PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME "', '"ip igmp query-max-response-time ') + line = line.replace('PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC "', '"ip igmp query-max-response-time-dsec ') + + # solo + line = line.replace('NO_NEIGHBOR_CMD2,', '"no neighbor <A.B.C.D|X:X::X:X|WORD>",') + line = line.replace('NEIGHBOR_CMD2,', '"neighbor <A.B.C.D|X:X::X:X|WORD>",') + line = line.replace('NO_NEIGHBOR_CMD,', '"no neighbor <A.B.C.D|X:X::X:X>",') + line = line.replace('NEIGHBOR_CMD,', '"neighbor <A.B.C.D|X:X::X:X>",') + line = line.replace('PIM_CMD_IP_MULTICAST_ROUTING,', '"ip multicast-routing",') + + if line.rstrip().endswith('" ,'): + line = line.replace('" ,', '",') + + return line + + class DEFUN(object): def __init__(self, line_number, command_string_expanded, lines): @@ -216,14 +441,14 @@ DEFUN (no_bgp_maxmed_onstartup, elif state == 'HELP': if line.strip() == '{': - self.guts.append(line) + # self.guts.append(line) state = 'BODY' else: self.help_strings.append(line) elif state == 'BODY': if line.rstrip() == '}': - self.guts.append(line) + # self.guts.append(line) state = None else: self.guts.append(line) @@ -239,7 +464,7 @@ DEFUN (no_bgp_maxmed_onstartup, return self.name def sanity_check(self): - (max_index, variable_indexes) = get_argv_variable_indexes(self.line_number, self.command_string_expanded) + (max_index, variable_indexes) = get_command_string_variable_indexes(self.line_number, self.command_string_expanded) # sanity check that each argv index matches a variable in the command string for line in self.guts: @@ -256,7 +481,6 @@ DEFUN (no_bgp_maxmed_onstartup, def get_new_command_string(self): line = self.command_string - # dwalton # Change <1-255> to (1-255) # Change (foo|bar) to <foo|bar> # Change {wazzup} to [wazzup]....there shouldn't be many of these @@ -284,16 +508,66 @@ DEFUN (no_bgp_maxmed_onstartup, line = re_space.group(1) + ' '.join(line.split()) + re_space.group(2) return line + def get_used_idx_variables(self, idx_table): + used = {} + + # sanity check that each argv index matches a variable in the command string + for line in self.guts: + if 'argv[' in line and '->arg' in line: + tmp_line = deepcopy(line) + re_argv = re.search('^.*?argv\[(\w+)\]->arg(.*)$', tmp_line) + + while re_argv: + index = re_argv.group(1) + + if index.isdigit(): + index = int(index) + if index in idx_table: + used[index] = idx_table[index] + else: + print "%d: could not find idx variable for %d" % (self.line_number, index) + else: + for (key, value) in idx_table.iteritems(): + if value == index: + used[key] = value + break + + tmp_line = re_argv.group(2) + re_argv = re.search('^.*?argv\[(\w+)\]->arg(.*)$', tmp_line) + + return used + def dump(self): + new_command_string = self.get_new_command_string() + new_command_string_expanded = expand_command_string(new_command_string) lines = [] lines.append("DEFUN (%s,\n" % self.name) lines.append(" %s,\n" % self.name_cmd) - lines.append(self.get_new_command_string()) + lines.append(new_command_string) lines.extend(self.help_strings) - lines.extend(self.guts) - return ''.join(lines) + lines.append('{\n') + # only print the variables that will be used else we get a compile error + idx_table = get_command_string_index_variable_table(self.line_number, new_command_string_expanded) + idx_table_used = self.get_used_idx_variables(idx_table) + for index in sorted(idx_table_used.keys()): + idx_variable = idx_table_used[index] + lines.append(" int %s = %d;\n" % (idx_variable, index)) + + # sanity check that each argv index matches a variable in the command string + for line in self.guts: + if line.startswith(' int idx_'): + pass + elif 'argv[' in line and '->arg' in line: + for (index, idx_variable) in idx_table.iteritems(): + line = line.replace("argv[%d]->arg" % index, "argv[%s]->arg" % idx_variable) + lines.append(line) + else: + lines.append(line) + + lines.append('}\n') + return ''.join(lines) def update_argvs(filename): @@ -334,78 +608,7 @@ def update_argvs(filename): state = 'DEFUN_BODY' elif line_number == defun_line_number + 2: - - # in the middle - line = line.replace('" CMD_AS_RANGE "', '<1-4294967295>') - line = line.replace('" DYNAMIC_NEIGHBOR_LIMIT_RANGE "', '<1-5000>') - line = line.replace('" BGP_INSTANCE_CMD "', '(view|vrf) WORD') - line = line.replace('" BGP_INSTANCE_ALL_CMD "', '(view|vrf) all') - line = line.replace('" CMD_RANGE_STR(1, MULTIPATH_NUM) "', '<1-255>') - line = line.replace('" QUAGGA_IP_REDIST_STR_BGPD "', '(kernel|connected|static|rip|ospf|isis|pim|table)') - line = line.replace('" QUAGGA_IP6_REDIST_STR_BGPD "', '(kernel|connected|static|ripng|ospf6|isis|table)') - line = line.replace('" OSPF_LSA_TYPES_CMD_STR "', 'asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as') - line = line.replace('" QUAGGA_REDIST_STR_OSPFD "', '(kernel|connected|static|rip|isis|bgp|pim|table)') - line = line.replace('" VRF_CMD_STR "', 'vrf NAME') - line = line.replace('" VRF_ALL_CMD_STR "', 'vrf all') - line = line.replace('" QUAGGA_IP_PROTOCOL_MAP_STR_ZEBRA "', '(kernel|connected|static|rip|ospf|isis|bgp|pim|table|any)') - line = line.replace('" QUAGGA_IP6_PROTOCOL_MAP_STR_ZEBRA "', '(kernel|connected|static|ripng|ospf6|isis|bgp|table|any)') - line = line.replace('" QUAGGA_REDIST_STR_RIPNGD "', '(kernel|connected|static|ospf6|isis|bgp|table)') - line = line.replace('" QUAGGA_REDIST_STR_RIPD "', '(kernel|connected|static|ospf|isis|bgp|pim|table)') - line = line.replace('" QUAGGA_REDIST_STR_OSPF6D "', '(kernel|connected|static|ripng|isis|bgp|table)') - line = line.replace('" QUAGGA_REDIST_STR_ISISD "', '(kernel|connected|static|rip|ripng|ospf|ospf6|bgp|pim|table)') - line = line.replace('" LOG_FACILITIES "', '(kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7)') - - # endswith - line = line.replace('" CMD_AS_RANGE,', ' <1-4294967295>",') - line = line.replace('" DYNAMIC_NEIGHBOR_LIMIT_RANGE,', ' <1-5000>",') - line = line.replace('" BGP_INSTANCE_CMD,', ' (view|vrf) WORD",') - line = line.replace('" BGP_INSTANCE_ALL_CMD,', ' (view|vrf) all",') - line = line.replace('" CMD_RANGE_STR(1, MULTIPATH_NUM),', '<1-255>",') - line = line.replace('" CMD_RANGE_STR(1, MAXTTL),', '<1-255>",') - line = line.replace('" BFD_CMD_DETECT_MULT_RANGE BFD_CMD_MIN_RX_RANGE BFD_CMD_MIN_TX_RANGE,', '<2-255> <50-60000> <50-60000>",') - line = line.replace('" OSPF_LSA_TYPES_CMD_STR,', - ' asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as",') - line = line.replace('" BGP_UPDATE_SOURCE_REQ_STR,', ' (A.B.C.D|X:X::X:X|WORD)",') - line = line.replace('" BGP_UPDATE_SOURCE_OPT_STR,', ' {A.B.C.D|X:X::X:X|WORD}",') - line = line.replace('" QUAGGA_IP_REDIST_STR_BGPD,', ' (kernel|connected|static|rip|ospf|isis|pim|table)",') - line = line.replace('" QUAGGA_IP6_REDIST_STR_BGPD,', ' (kernel|connected|static|ripng|ospf6|isis|table)",') - line = line.replace('" QUAGGA_REDIST_STR_OSPFD,', ' (kernel|connected|static|rip|isis|bgp|pim|table)",') - line = line.replace('" VRF_CMD_STR,', ' vrf NAME",') - line = line.replace('" VRF_ALL_CMD_STR,', ' vrf all",') - line = line.replace('" QUAGGA_IP_REDIST_STR_ZEBRA,', ' (kernel|connected|static|rip|ospf|isis|bgp|pim|table)",') - line = line.replace('" QUAGGA_IP6_REDIST_STR_ZEBRA,', ' (kernel|connected|static|ripng|ospf6|isis|bgp|table)",') - line = line.replace('" QUAGGA_IP_PROTOCOL_MAP_STR_ZEBRA,', ' (kernel|connected|static|rip|ospf|isis|bgp|pim|table|any)",') - line = line.replace('" QUAGGA_IP6_PROTOCOL_MAP_STR_ZEBRA,', ' (kernel|connected|static|ripng|ospf6|isis|bgp|table|any)",') - line = line.replace('" QUAGGA_REDIST_STR_RIPNGD,', ' (kernel|connected|static|ospf6|isis|bgp|table)",') - line = line.replace('" QUAGGA_REDIST_STR_RIPD,', ' (kernel|connected|static|ospf|isis|bgp|pim|table)",') - line = line.replace('" PIM_CMD_IP_MULTICAST_ROUTING,', ' ip multicast-routing",') - line = line.replace('" PIM_CMD_IP_IGMP_QUERY_INTERVAL,', ' ip igmp query-interval",') - line = line.replace('" PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC,', ' ip igmp query-max-response-time-dsec",') - line = line.replace('" PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME,', ' ip igmp query-max-response-time",') - line = line.replace('" QUAGGA_REDIST_STR_OSPF6D,', ' (kernel|connected|static|ripng|isis|bgp|table)",') - line = line.replace('" QUAGGA_REDIST_STR_ISISD,', ' (kernel|connected|static|rip|ripng|ospf|ospf6|bgp|pim|table)",') - line = line.replace('" LOG_FACILITIES,', ' (kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7)",') - - # startswith - line = line.replace('LISTEN_RANGE_CMD "', '"bgp listen range (A.B.C.D/M|X:X::X:X/M) ') - line = line.replace('NO_NEIGHBOR_CMD2 "', '"no neighbor (A.B.C.D|X:X::X:X|WORD) ') - line = line.replace('NEIGHBOR_CMD2 "', '"neighbor (A.B.C.D|X:X::X:X|WORD) ') - line = line.replace('NO_NEIGHBOR_CMD "', '"no neighbor (A.B.C.D|X:X::X:X) ') - line = line.replace('NEIGHBOR_CMD "', '"neighbor (A.B.C.D|X:X::X:X) ') - line = line.replace('PIM_CMD_NO "', '"no ') - line = line.replace('PIM_CMD_IP_IGMP_QUERY_INTERVAL "', '"ip igmp query-interval ') - line = line.replace('PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME "', '"ip igmp query-max-response-time ') - line = line.replace('PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC "', '"ip igmp query-max-response-time-dsec ') - - # solo - line = line.replace('NO_NEIGHBOR_CMD2,', '"no neighbor (A.B.C.D|X:X::X:X|WORD)",') - line = line.replace('NEIGHBOR_CMD2,', '"neighbor (A.B.C.D|X:X::X:X|WORD)",') - line = line.replace('NO_NEIGHBOR_CMD,', '"no neighbor (A.B.C.D|X:X::X:X)",') - line = line.replace('NEIGHBOR_CMD,', '"neighbor (A.B.C.D|X:X::X:X)",') - line = line.replace('PIM_CMD_IP_MULTICAST_ROUTING,', '"ip multicast-routing",') - - if line.rstrip().endswith('" ,'): - line = line.replace('" ,', '",') + line = expand_command_string(line) command_string = line ''' |