re_lege.group(2),
re_lege.group(4),
)
- re_lege = re.search(r"(.*)ge\s+(\d+)\s+le\s+(\d+)(.*)", legestr)
-
- if re_lege and (
- (re_key_rt.group(1) == "ip" and re_lege.group(3) == "32")
- or (re_key_rt.group(1) == "ipv6" and re_lege.group(3) == "128")
- ):
- legestr = "%sge %s%s" % (
- re_lege.group(1),
- re_lege.group(2),
- re_lege.group(4),
- )
key[0] = "%s prefix-list%s%s %s%s" % (
re_key_rt.group(1),
"domainname ",
"dump ",
"enable ",
+ "evpn mh",
"frr ",
"fpm ",
"hostname ",
"ip ",
"ipv6 ",
"log ",
+ "mac access-list ",
"mpls lsp",
"mpls label",
"no ",
"password ",
+ "pbr ",
"ptm-enable",
"router-id ",
"service ",
"table ",
"username ",
- "zebra ",
+ "vni ",
"vrrp autoconfigure",
- "evpn mh",
+ "zebra "
)
for line in self.lines:
return (lines_to_add, lines_to_del)
+"""
+This method handles deletion of bgp peer group config.
+The objective is to delete config lines related to peers
+associated with the peer-group and move the peer-group
+config line to the end of the lines_to_del list.
+"""
+
+
+def delete_move_lines(lines_to_add, lines_to_del):
+
+ del_dict = dict()
+ # Stores the lines to move to the end of the pending list.
+ lines_to_del_to_del = []
+ # Stores the lines to move to end of the pending list.
+ lines_to_del_to_app = []
+ found_pg_del_cmd = False
+
+ """
+ When "neighbor <pg_name> peer-group" under a bgp instance is removed,
+ it also deletes the associated peer config. Any config line below no form of
+ peer-group related to a peer are errored out as the peer no longer exists.
+ To cleanup peer-group and associated peer(s) configs:
+ - Remove all the peers config lines from the pending list (lines_to_del list).
+ - Move peer-group deletion line to the end of the pending list, to allow
+ removal of any of the peer-group specific configs.
+
+ Create a dictionary of config context (i.e. router bgp vrf x).
+ Under each context node, create a dictionary of a peer-group name.
+ Append a peer associated to the peer-group into a list under a peer-group node.
+ Remove all of the peer associated config lines from the pending list.
+ Append peer-group deletion line to end of the pending list.
+
+ Example:
+ neighbor underlay peer-group
+ neighbor underlay remote-as external
+ neighbor underlay advertisement-interval 0
+ neighbor underlay timers 3 9
+ neighbor underlay timers connect 10
+ neighbor swp1 interface peer-group underlay
+ neighbor swp1 advertisement-interval 0
+ neighbor swp1 timers 3 9
+ neighbor swp1 timers connect 10
+ neighbor swp2 interface peer-group underlay
+ neighbor swp2 advertisement-interval 0
+ neighbor swp2 timers 3 9
+ neighbor swp2 timers connect 10
+ neighbor swp3 interface peer-group underlay
+ neighbor uplink1 interface remote-as internal
+ neighbor uplink1 advertisement-interval 0
+ neighbor uplink1 timers 3 9
+ neighbor uplink1 timers connect 10
+
+ New order:
+ "router bgp 200 no bgp bestpath as-path multipath-relax"
+ "router bgp 200 no neighbor underlay advertisement-interval 0"
+ "router bgp 200 no neighbor underlay timers 3 9"
+ "router bgp 200 no neighbor underlay timers connect 10"
+ "router bgp 200 no neighbor uplink1 advertisement-interval 0"
+ "router bgp 200 no neighbor uplink1 timers 3 9"
+ "router bgp 200 no neighbor uplink1 timers connect 10"
+ "router bgp 200 no neighbor underlay remote-as external"
+ "router bgp 200 no neighbor uplink1 interface remote-as internal"
+ "router bgp 200 no neighbor underlay peer-group"
+
+ """
+
+ for (ctx_keys, line) in lines_to_del:
+ if (
+ ctx_keys[0].startswith("router bgp")
+ and line
+ and line.startswith("neighbor ")
+ ):
+ """
+ When 'neighbor <peer> remote-as <>' is removed it deletes the peer,
+ there might be a peer associated config which also needs to be removed
+ prior to peer.
+ Append the 'neighbor <peer> remote-as <>' to the lines_to_del.
+ Example:
+
+ neighbor uplink1 interface remote-as internal
+ neighbor uplink1 advertisement-interval 0
+ neighbor uplink1 timers 3 9
+ neighbor uplink1 timers connect 10
+
+ Move to end:
+ neighbor uplink1 advertisement-interval 0
+ neighbor uplink1 timers 3 9
+ neighbor uplink1 timers connect 10
+ ...
+
+ neighbor uplink1 interface remote-as internal
+
+ """
+ # 'no neighbor peer [interface] remote-as <>'
+ nb_remoteas = "neighbor (\S+) .*remote-as (\S+)"
+ re_nb_remoteas = re.search(nb_remoteas, line)
+ if re_nb_remoteas:
+ lines_to_del_to_app.append((ctx_keys, line))
+
+ """
+ {'router bgp 65001': {'PG': [], 'PG1': []},
+ 'router bgp 65001 vrf vrf1': {'PG': [], 'PG1': []}}
+ """
+ if ctx_keys[0] not in del_dict:
+ del_dict[ctx_keys[0]] = dict()
+ # find 'no neighbor <pg_name> peer-group'
+ re_pg = re.match("neighbor (\S+) peer-group$", line)
+ if re_pg and re_pg.group(1) not in del_dict[ctx_keys[0]]:
+ del_dict[ctx_keys[0]][re_pg.group(1)] = list()
+
+ for (ctx_keys, line) in lines_to_del_to_app:
+ lines_to_del.remove((ctx_keys, line))
+ lines_to_del.append((ctx_keys, line))
+
+ if found_pg_del_cmd == False:
+ return (lines_to_add, lines_to_del)
+
+ """
+ {'router bgp 65001': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']},
+ 'router bgp 65001 vrf vrf1': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']}}
+ """
+ for (ctx_keys, line) in lines_to_del:
+ if (
+ ctx_keys[0].startswith("router bgp")
+ and line
+ and line.startswith("neighbor ")
+ ):
+ if ctx_keys[0] in del_dict:
+ for pg_key in del_dict[ctx_keys[0]]:
+ # 'neighbor <peer> [interface] peer-group <pg_name>'
+ nb_pg = "neighbor (\S+) .*peer-group %s$" % pg_key
+ re_nbr_pg = re.search(nb_pg, line)
+ if (
+ re_nbr_pg
+ and re_nbr_pg.group(1) not in del_dict[ctx_keys[0]][pg_key]
+ ):
+ del_dict[ctx_keys[0]][pg_key].append(re_nbr_pg.group(1))
+
+ lines_to_del_to_app = []
+ for (ctx_keys, line) in lines_to_del:
+ if (
+ ctx_keys[0].startswith("router bgp")
+ and line
+ and line.startswith("neighbor ")
+ ):
+ if ctx_keys[0] in del_dict:
+ for pg in del_dict[ctx_keys[0]]:
+ for nbr in del_dict[ctx_keys[0]][pg]:
+ nb_exp = "neighbor %s .*" % nbr
+ re_nb = re.search(nb_exp, line)
+ # add peer configs to delete list.
+ if re_nb and line not in lines_to_del_to_del:
+ lines_to_del_to_del.append((ctx_keys, line))
+
+ pg_exp = "neighbor %s peer-group$" % pg
+ re_pg = re.match(pg_exp, line)
+ if re_pg:
+ lines_to_del_to_app.append((ctx_keys, line))
+
+ for (ctx_keys, line) in lines_to_del_to_del:
+ lines_to_del.remove((ctx_keys, line))
+
+ for (ctx_keys, line) in lines_to_del_to_app:
+ lines_to_del.remove((ctx_keys, line))
+ lines_to_del.append((ctx_keys, line))
+
+ return (lines_to_add, lines_to_del)
+
+
def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
# Quite possibly the most confusing (while accurate) variable names in history
if found_add_bfd_nbr:
lines_to_del_to_del.append((ctx_keys, line))
+ """
+ Neighbor changes of route-maps need to be accounted for in that we
+ do not want to do a `no route-map...` `route-map ....` when changing
+ a route-map. This is bad mojo as that we will send/receive
+ data we don't want.
+ Additionally we need to ensure that if we have different afi/safi
+ variants that they actually match and if we are going from a very
+ old style command such that the neighbor command is under the
+ `router bgp ..` node that we need to handle that appropriately
+ """
+ re_nbr_rm = re.search("neighbor(.*)route-map(.*)(in|out)$", line)
+ if re_nbr_rm:
+ adjust_for_bgp_node = 0
+ neighbor_name = re_nbr_rm.group(1)
+ rm_name_del = re_nbr_rm.group(2)
+ dir = re_nbr_rm.group(3)
+ search = "neighbor%sroute-map(.*)%s" % (neighbor_name, dir)
+ save_line = "EMPTY"
+ for (ctx_keys_al, add_line) in lines_to_add:
+ if ctx_keys_al[0].startswith("router bgp"):
+ if add_line:
+ rm_match = re.search(search, add_line)
+ if rm_match:
+ rm_name_add = rm_match.group(1)
+ if rm_name_add == rm_name_del:
+ continue
+ if len(ctx_keys_al) == 1:
+ save_line = line
+ adjust_for_bgp_node = 1
+ else:
+ if (
+ len(ctx_keys) > 1
+ and len(ctx_keys_al) > 1
+ and ctx_keys[1] == ctx_keys_al[1]
+ ):
+ lines_to_del_to_del.append((ctx_keys_al, line))
+
+ if adjust_for_bgp_node == 1:
+ for (ctx_keys_dl, dl_line) in lines_to_del:
+ if (
+ ctx_keys_dl[0].startswith("router bgp")
+ and len(ctx_keys_dl) > 1
+ and ctx_keys_dl[1] == "address-family ipv4 unicast"
+ ):
+ if save_line == dl_line:
+ lines_to_del_to_del.append((ctx_keys_dl, save_line))
+
"""
We changed how we display the neighbor interface command. Older
versions of frr would display the following:
(lines_to_add, lines_to_del) = ignore_delete_re_add_lines(
lines_to_add, lines_to_del
)
+ (lines_to_add, lines_to_del) = delete_move_lines(lines_to_add, lines_to_del)
(lines_to_add, lines_to_del) = ignore_unconfigurable_lines(
lines_to_add, lines_to_del
)
parser.add_argument(
"--daemon", help="daemon for which want to replace the config", default=""
)
+ parser.add_argument(
+ "--test-reset",
+ action="store_true",
+ help="Used by topotest to not delete debug or log file commands",
+ )
args = parser.parse_args()
service_integrated_vtysh_config = False
break
- if not service_integrated_vtysh_config and not args.daemon:
+ if not args.test and not service_integrated_vtysh_config and not args.daemon:
log.error(
"'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'"
)
running.load_from_show_running(args.daemon)
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
- lines_to_configure = []
if lines_to_del:
- print("\nLines To Delete")
- print("===============")
+ if not args.test_reset:
+ print("\nLines To Delete")
+ print("===============")
for (ctx_keys, line) in lines_to_del:
if line == "!":
continue
- cmd = "\n".join(lines_to_config(ctx_keys, line, True))
- lines_to_configure.append(cmd)
+ nolines = lines_to_config(ctx_keys, line, True)
+
+ if args.test_reset:
+ # For topotests the original code stripped the lines, and ommitted blank lines
+ # after, do that here
+ nolines = [x.strip() for x in nolines]
+ # For topotests leave these lines in (don't delete them)
+ # [chopps: why is "log file" more special than other "log" commands?]
+ nolines = [x for x in nolines if "debug" not in x and "log file" not in x]
+ if not nolines:
+ continue
+
+ cmd = "\n".join(nolines)
print(cmd)
if lines_to_add:
- print("\nLines To Add")
- print("============")
+ if not args.test_reset:
+ print("\nLines To Add")
+ print("============")
for (ctx_keys, line) in lines_to_add:
if line == "!":
continue
- cmd = "\n".join(lines_to_config(ctx_keys, line, False))
- lines_to_configure.append(cmd)
+ lines = lines_to_config(ctx_keys, line, False)
+
+ if args.test_reset:
+ # For topotests the original code stripped the lines, and ommitted blank lines
+ # after, do that here
+ lines = [x.strip() for x in lines if x.strip()]
+ if not lines:
+ continue
+
+ cmd = "\n".join(lines)
print(cmd)
elif args.reload:
+ lines_to_configure = []
# We will not be able to do anything, go ahead and exit(1)
if not vtysh.is_config_available():