Merge pull request #10048 from qlyoung/fix-reload-doc-comments

Fix reload comments
This commit is contained in:
Donatas Abraitis 2021-11-15 17:54:18 +02:00 committed by GitHub
commit fd7d888003
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -172,7 +172,6 @@ class Vtysh(object):
class Context(object): class Context(object):
""" """
A Context object represents a section of frr configuration such as: A Context object represents a section of frr configuration such as:
! !
@ -234,7 +233,6 @@ def get_normalized_mac_ip_line(line):
class Config(object): class Config(object):
""" """
A frr configuration is stored in a Config object. A Config object A frr configuration is stored in a Config object. A Config object
contains a dictionary of Context objects where the Context keys contains a dictionary of Context objects where the Context keys
@ -265,22 +263,20 @@ class Config(object):
if ":" in line: if ":" in line:
line = get_normalized_mac_ip_line(line) line = get_normalized_mac_ip_line(line)
""" # vrf static routes can be added in two ways. The old way is:
vrf static routes can be added in two ways. The old way is: #
# "ip route x.x.x.x/x y.y.y.y vrf <vrfname>"
"ip route x.x.x.x/x y.y.y.y vrf <vrfname>" #
# but it's rendered in the configuration as the new way::
but it's rendered in the configuration as the new way:: #
# vrf <vrf-name>
vrf <vrf-name> # ip route x.x.x.x/x y.y.y.y
ip route x.x.x.x/x y.y.y.y # exit-vrf
exit-vrf #
# this difference causes frr-reload to not consider them a
this difference causes frr-reload to not consider them a # match and delete vrf static routes incorrectly.
match and delete vrf static routes incorrectly. # fix the old way to match new "show running" output so a
fix the old way to match new "show running" output so a # proper match is found.
proper match is found.
"""
if ( if (
line.startswith("ip route ") or line.startswith("ipv6 route ") line.startswith("ip route ") or line.startswith("ipv6 route ")
) and " vrf " in line: ) and " vrf " in line:
@ -326,14 +322,12 @@ class Config(object):
""" """
Return the lines read in from the configuration Return the lines read in from the configuration
""" """
return "\n".join(self.lines) return "\n".join(self.lines)
def get_contexts(self): def get_contexts(self):
""" """
Return the parsed context as strings for display, log etc. Return the parsed context as strings for display, log etc.
""" """
for (_, ctx) in sorted(iteritems(self.contexts)): for (_, ctx) in sorted(iteritems(self.contexts)):
print(str(ctx) + "\n") print(str(ctx) + "\n")
@ -341,18 +335,15 @@ class Config(object):
""" """
Save the provided key and lines as a context Save the provided key and lines as a context
""" """
if not key: if not key:
return return
""" # IP addresses specified in "network" statements, "ip prefix-lists"
IP addresses specified in "network" statements, "ip prefix-lists" # etc. can differ in the host part of the specification the user
etc. can differ in the host part of the specification the user # provides and what the running config displays. For example, user can
provides and what the running config displays. For example, user # specify 11.1.1.1/24, and the running config displays this as
can specify 11.1.1.1/24, and the running config displays this as # 11.1.1.0/24. Ensure we don't do a needless operation for such lines.
11.1.1.0/24. Ensure we don't do a needless operation for such # IS-IS & OSPFv3 have no "network" support.
lines. IS-IS & OSPFv3 have no "network" support.
"""
re_key_rt = re.match(r"(ip|ipv6)\s+route\s+([A-Fa-f:.0-9/]+)(.*)$", key[0]) re_key_rt = re.match(r"(ip|ipv6)\s+route\s+([A-Fa-f:.0-9/]+)(.*)$", key[0])
if re_key_rt: if re_key_rt:
addr = re_key_rt.group(2) addr = re_key_rt.group(2)
@ -430,10 +421,8 @@ class Config(object):
newlines.append(line) newlines.append(line)
lines = newlines lines = newlines
""" # More fixups in user specification and what running config shows.
More fixups in user specification and what running config shows. # "null0" in routes must be replaced by Null0.
"null0" in routes must be replaced by Null0.
"""
if ( if (
key[0].startswith("ip route") key[0].startswith("ip route")
or key[0].startswith("ipv6 route") or key[0].startswith("ipv6 route")
@ -441,10 +430,8 @@ class Config(object):
): ):
key[0] = re.sub(r"\s+null0(\s*$)", " Null0", key[0]) key[0] = re.sub(r"\s+null0(\s*$)", " Null0", key[0])
""" # Similar to above, but when the static is in a vrf, it turns into a
Similar to above, but when the static is in a vrf, it turns into a # blackhole nexthop for both null0 and Null0. Fix it accordingly
blackhole nexthop for both null0 and Null0. Fix it accordingly
"""
if lines and key[0].startswith("vrf "): if lines and key[0].startswith("vrf "):
newlines = [] newlines = []
for line in lines: for line in lines:
@ -474,71 +461,69 @@ class Config(object):
def load_contexts(self): def load_contexts(self):
""" """
Parse the configuration and create contexts for each appropriate block Parse the configuration and create contexts for each appropriate block
"""
"""
The end of a context is flagged via the 'end' keyword: The end of a context is flagged via the 'end' keyword:
! !
interface swp52 interface swp52
ipv6 nd suppress-ra ipv6 nd suppress-ra
link-detect link-detect
! !
end end
router bgp 10 router bgp 10
bgp router-id 10.0.0.1 bgp router-id 10.0.0.1
bgp log-neighbor-changes bgp log-neighbor-changes
no bgp default ipv4-unicast no bgp default ipv4-unicast
neighbor EBGP peer-group neighbor EBGP peer-group
neighbor EBGP advertisement-interval 1 neighbor EBGP advertisement-interval 1
neighbor EBGP timers connect 10 neighbor EBGP timers connect 10
neighbor 2001:40:1:4::6 remote-as 40 neighbor 2001:40:1:4::6 remote-as 40
neighbor 2001:40:1:8::a remote-as 40 neighbor 2001:40:1:8::a remote-as 40
! !
end end
address-family ipv6 address-family ipv6
neighbor IBGPv6 activate neighbor IBGPv6 activate
neighbor 2001:10::2 peer-group IBGPv6 neighbor 2001:10::2 peer-group IBGPv6
neighbor 2001:10::3 peer-group IBGPv6 neighbor 2001:10::3 peer-group IBGPv6
exit-address-family exit-address-family
! !
end end
address-family evpn address-family evpn
neighbor LEAF activate neighbor LEAF activate
advertise-all-vni advertise-all-vni
vni 10100 vni 10100
rd 65000:10100 rd 65000:10100
route-target import 10.1.1.1:10100 route-target import 10.1.1.1:10100
route-target export 10.1.1.1:10100 route-target export 10.1.1.1:10100
exit-vni exit-vni
exit-address-family exit-address-family
! !
end end
router ospf router ospf
ospf router-id 10.0.0.1 ospf router-id 10.0.0.1
log-adjacency-changes detail log-adjacency-changes detail
timers throttle spf 0 50 5000 timers throttle spf 0 50 5000
! !
end end
The code assumes that its working on the output from the "vtysh -m"
command. That provides the appropriate markers to signify end of
a context. This routine uses that to build the contexts for the
config.
There are single line contexts such as "log file /media/node/zebra.log"
and multi-line contexts such as "router ospf" and subcontexts
within a context such as "address-family" within "router bgp"
In each of these cases, the first line of the context becomes the
key of the context. So "router bgp 10" is the key for the non-address
family part of bgp, "router bgp 10, address-family ipv6 unicast" is
the key for the subcontext and so on.
This dictionary contains a tree of all commands that we know start a
new multi-line context. All other commands are treated either as
commands inside a multi-line context or as single-line contexts. This
dictionary should be updated whenever a new node is added to FRR.
""" """
# The code assumes that its working on the output from the "vtysh -m"
# command. That provides the appropriate markers to signify end of
# a context. This routine uses that to build the contexts for the
# config.
#
# There are single line contexts such as "log file /media/node/zebra.log"
# and multi-line contexts such as "router ospf" and subcontexts
# within a context such as "address-family" within "router bgp"
# In each of these cases, the first line of the context becomes the
# key of the context. So "router bgp 10" is the key for the non-address
# family part of bgp, "router bgp 10, address-family ipv6 unicast" is
# the key for the subcontext and so on.
# This dictionary contains a tree of all commands that we know start a
# new multi-line context. All other commands are treated either as
# commands inside a multi-line context or as single-line contexts. This
# dictionary should be updated whenever a new node is added to FRR.
ctx_keywords = { ctx_keywords = {
"router bgp ": { "router bgp ": {
"address-family ": { "address-family ": {
@ -788,16 +773,12 @@ def check_for_exit_vrf(lines_to_add, lines_to_del):
return (lines_to_add, lines_to_del) 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): def delete_move_lines(lines_to_add, lines_to_del):
"""
This function 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.
"""
del_dict = dict() del_dict = dict()
# Stores the lines to move to the end of the pending list. # Stores the lines to move to the end of the pending list.
lines_to_del_to_del = [] lines_to_del_to_del = []
@ -805,54 +786,51 @@ def delete_move_lines(lines_to_add, lines_to_del):
lines_to_del_to_app = [] lines_to_del_to_app = []
found_pg_del_cmd = False found_pg_del_cmd = False
""" # When "neighbor <pg_name> peer-group" under a bgp instance is removed,
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
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.
peer-group related to a peer are errored out as the peer no longer exists. # To cleanup peer-group and associated peer(s) configs:
To cleanup peer-group and associated peer(s) configs: # - Remove all the peers config lines from the pending list (lines_to_del list).
- 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
- Move peer-group deletion line to the end of the pending list, to allow # removal of any of the peer-group specific configs.
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
Create a dictionary of config context (i.e. router bgp vrf x). # New order:
Under each context node, create a dictionary of a peer-group name. # "router bgp 200 no bgp bestpath as-path multipath-relax"
Append a peer associated to the peer-group into a list under a peer-group node. # "router bgp 200 no neighbor underlay advertisement-interval 0"
Remove all of the peer associated config lines from the pending list. # "router bgp 200 no neighbor underlay timers 3 9"
Append peer-group deletion line to end of the pending list. # "router bgp 200 no neighbor underlay timers connect 10"
# "router bgp 200 no neighbor uplink1 advertisement-interval 0"
Example: # "router bgp 200 no neighbor uplink1 timers 3 9"
neighbor underlay peer-group # "router bgp 200 no neighbor uplink1 timers connect 10"
neighbor underlay remote-as external # "router bgp 200 no neighbor underlay remote-as external"
neighbor underlay advertisement-interval 0 # "router bgp 200 no neighbor uplink1 interface remote-as internal"
neighbor underlay timers 3 9 # "router bgp 200 no neighbor underlay peer-group"
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: for (ctx_keys, line) in lines_to_del:
if ( if (
@ -860,37 +838,33 @@ def delete_move_lines(lines_to_add, lines_to_del):
and line and line
and line.startswith("neighbor ") and line.startswith("neighbor ")
): ):
""" # When 'neighbor <peer> remote-as <>' is removed it deletes the peer,
When 'neighbor <peer> remote-as <>' is removed it deletes the peer, # there might be a peer associated config which also needs to be removed
there might be a peer associated config which also needs to be removed # prior to peer.
prior to peer. # Append the 'neighbor <peer> remote-as <>' to the lines_to_del.
Append the 'neighbor <peer> remote-as <>' to the lines_to_del. # Example:
Example: #
# neighbor uplink1 interface remote-as internal
# neighbor uplink1 advertisement-interval 0
# neighbor uplink1 timers 3 9
# neighbor uplink1 timers connect 10
neighbor uplink1 interface remote-as internal # Move to end:
neighbor uplink1 advertisement-interval 0 # neighbor uplink1 advertisement-interval 0
neighbor uplink1 timers 3 9 # neighbor uplink1 timers 3 9
neighbor uplink1 timers connect 10 # neighbor uplink1 timers connect 10
# ...
Move to end: #
neighbor uplink1 advertisement-interval 0 # neighbor uplink1 interface remote-as internal
neighbor uplink1 timers 3 9 #
neighbor uplink1 timers connect 10
...
neighbor uplink1 interface remote-as internal
"""
# 'no neighbor peer [interface] remote-as <>' # 'no neighbor peer [interface] remote-as <>'
nb_remoteas = "neighbor (\S+) .*remote-as (\S+)" nb_remoteas = "neighbor (\S+) .*remote-as (\S+)"
re_nb_remoteas = re.search(nb_remoteas, line) re_nb_remoteas = re.search(nb_remoteas, line)
if re_nb_remoteas: if re_nb_remoteas:
lines_to_del_to_app.append((ctx_keys, line)) lines_to_del_to_app.append((ctx_keys, line))
""" # {'router bgp 65001': {'PG': [], 'PG1': []},
{'router bgp 65001': {'PG': [], 'PG1': []}, # 'router bgp 65001 vrf vrf1': {'PG': [], 'PG1': []}}
'router bgp 65001 vrf vrf1': {'PG': [], 'PG1': []}}
"""
if ctx_keys[0] not in del_dict: if ctx_keys[0] not in del_dict:
del_dict[ctx_keys[0]] = dict() del_dict[ctx_keys[0]] = dict()
# find 'no neighbor <pg_name> peer-group' # find 'no neighbor <pg_name> peer-group'
@ -905,10 +879,8 @@ def delete_move_lines(lines_to_add, lines_to_del):
if found_pg_del_cmd == False: if found_pg_del_cmd == False:
return (lines_to_add, lines_to_del) return (lines_to_add, lines_to_del)
""" # {'router bgp 65001': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']},
{'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']}}
'router bgp 65001 vrf vrf1': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']}}
"""
for (ctx_keys, line) in lines_to_del: for (ctx_keys, line) in lines_to_del:
if ( if (
ctx_keys[0].startswith("router bgp") ctx_keys[0].startswith("router bgp")
@ -982,25 +954,22 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
if ctx_keys[0].startswith("router bgp") and line: if ctx_keys[0].startswith("router bgp") and line:
if line.startswith("neighbor "): if line.startswith("neighbor "):
""" # BGP changed how it displays swpX peers that are part of peer-group. Older
BGP changed how it displays swpX peers that are part of peer-group. Older # versions of frr would display these on separate lines:
versions of frr would display these on separate lines: # neighbor swp1 interface
neighbor swp1 interface # neighbor swp1 peer-group FOO
neighbor swp1 peer-group FOO #
# but today we display via a single line
but today we display via a single line # neighbor swp1 interface peer-group FOO
neighbor swp1 interface peer-group FOO #
# This change confuses frr-reload.py so check to see if we are deleting
This change confuses frr-reload.py so check to see if we are deleting # neighbor swp1 interface peer-group FOO
neighbor swp1 interface peer-group FOO #
# and adding
and adding # neighbor swp1 interface
neighbor swp1 interface # neighbor swp1 peer-group FOO
neighbor swp1 peer-group FOO #
# If so then chop the del line and the corresponding add lines
If so then chop the del line and the corresponding add lines
"""
re_swpx_int_peergroup = re.search( re_swpx_int_peergroup = re.search(
"neighbor (\S+) interface peer-group (\S+)", line "neighbor (\S+) interface peer-group (\S+)", line
) )
@ -1052,12 +1021,10 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_add_to_del.append((ctx_keys, swpx_interface)) lines_to_add_to_del.append((ctx_keys, swpx_interface))
lines_to_add_to_del.append((tmp_ctx_keys, swpx_peergroup)) lines_to_add_to_del.append((tmp_ctx_keys, swpx_peergroup))
""" # Changing the bfd timers on neighbors is allowed without doing
Changing the bfd timers on neighbors is allowed without doing # a delete/add process. Since doing a "no neighbor blah bfd
a delete/add process. Since doing a "no neighbor blah bfd ..." # ..." will cause the peer to bounce unnecessarily, just skip
will cause the peer to bounce unnecessarily, just skip the delete # the delete and just do the add.
and just do the add.
"""
re_nbr_bfd_timers = re.search( re_nbr_bfd_timers = re.search(
r"neighbor (\S+) bfd (\S+) (\S+) (\S+)", line r"neighbor (\S+) bfd (\S+) (\S+) (\S+)", line
) )
@ -1081,16 +1048,14 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
if found_add_bfd_nbr: if found_add_bfd_nbr:
lines_to_del_to_del.append((ctx_keys, line)) lines_to_del_to_del.append((ctx_keys, line))
""" # Neighbor changes of route-maps need to be accounted for in
Neighbor changes of route-maps need to be accounted for in that we # that we do not want to do a `no route-map...` `route-map
do not want to do a `no route-map...` `route-map ....` when changing # ....` when changing a route-map. This is bad mojo as that we
a route-map. This is bad mojo as that we will send/receive # will send/receive data we don't want. Additionally we need
data we don't want. # to ensure that if we have different afi/safi variants that
Additionally we need to ensure that if we have different afi/safi # they actually match and if we are going from a very old style
variants that they actually match and if we are going from a very # command such that the neighbor command is under the `router
old style command such that the neighbor command is under the # bgp ..` node that we need to handle that appropriately
`router bgp ..` node that we need to handle that appropriately
"""
re_nbr_rm = re.search("neighbor(.*)route-map(.*)(in|out)$", line) re_nbr_rm = re.search("neighbor(.*)route-map(.*)(in|out)$", line)
if re_nbr_rm: if re_nbr_rm:
adjust_for_bgp_node = 0 adjust_for_bgp_node = 0
@ -1128,29 +1093,27 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
if save_line == dl_line: if save_line == dl_line:
lines_to_del_to_del.append((ctx_keys_dl, save_line)) lines_to_del_to_del.append((ctx_keys_dl, save_line))
""" # We changed how we display the neighbor interface command. Older
We changed how we display the neighbor interface command. Older # versions of frr would display the following:
versions of frr would display the following: # neighbor swp1 interface
neighbor swp1 interface # neighbor swp1 remote-as external
neighbor swp1 remote-as external # neighbor swp1 capability extended-nexthop
neighbor swp1 capability extended-nexthop #
# but today we display via a single line
but today we display via a single line # neighbor swp1 interface remote-as external
neighbor swp1 interface remote-as external #
# and capability extended-nexthop is no longer needed because we
and capability extended-nexthop is no longer needed because we # automatically enable it when the neighbor is of type interface.
automatically enable it when the neighbor is of type interface. #
# This change confuses frr-reload.py so check to see if we are deleting
This change confuses frr-reload.py so check to see if we are deleting # neighbor swp1 interface remote-as (external|internal|ASNUM)
neighbor swp1 interface remote-as (external|internal|ASNUM) #
# and adding
and adding # neighbor swp1 interface
neighbor swp1 interface # neighbor swp1 remote-as (external|internal|ASNUM)
neighbor swp1 remote-as (external|internal|ASNUM) # neighbor swp1 capability extended-nexthop
neighbor swp1 capability extended-nexthop #
# If so then chop the del line and the corresponding add lines
If so then chop the del line and the corresponding add lines
"""
re_swpx_int_remoteas = re.search( re_swpx_int_remoteas = re.search(
"neighbor (\S+) interface remote-as (\S+)", line "neighbor (\S+) interface remote-as (\S+)", line
) )
@ -1186,15 +1149,13 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_add_to_del.append((ctx_keys, swpx_interface)) lines_to_add_to_del.append((ctx_keys, swpx_interface))
lines_to_add_to_del.append((tmp_ctx_keys, swpx_remoteas)) lines_to_add_to_del.append((tmp_ctx_keys, swpx_remoteas))
""" # We made the 'bgp bestpath as-path multipath-relax' command
We made the 'bgp bestpath as-path multipath-relax' command # automatically assume 'no-as-set' since the lack of this option
automatically assume 'no-as-set' since the lack of this option caused # caused weird routing problems. When the running config is shown
weird routing problems. When the running config is shown in # in releases with this change, the no-as-set keyword is not shown
releases with this change, the no-as-set keyword is not shown as it # as it is the default. This causes frr-reload to unnecessarily
is the default. This causes frr-reload to unnecessarily unapply # unapply this option only to apply it back again, causing
this option only to apply it back again, causing unnecessary session # unnecessary session resets.
resets.
"""
if "multipath-relax" in line: if "multipath-relax" in line:
re_asrelax_new = re.search( re_asrelax_new = re.search(
"^bgp\s+bestpath\s+as-path\s+multipath-relax$", line "^bgp\s+bestpath\s+as-path\s+multipath-relax$", line
@ -1207,25 +1168,21 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_del_to_del.append((ctx_keys, line)) lines_to_del_to_del.append((ctx_keys, line))
lines_to_add_to_del.append((ctx_keys, old_asrelax_cmd)) lines_to_add_to_del.append((ctx_keys, old_asrelax_cmd))
""" # If we are modifying the BGP table-map we need to avoid a del/add
If we are modifying the BGP table-map we need to avoid a del/add and # and instead modify the table-map in place via an add. This is
instead modify the table-map in place via an add. This is needed to # needed to avoid installing all routes in the RIB the second the
avoid installing all routes in the RIB the second the 'no table-map' # 'no table-map' is issued.
is issued.
"""
if line.startswith("table-map"): if line.startswith("table-map"):
found_table_map = line_exist(lines_to_add, ctx_keys, "table-map", False) found_table_map = line_exist(lines_to_add, ctx_keys, "table-map", False)
if found_table_map: if found_table_map:
lines_to_del_to_del.append((ctx_keys, line)) lines_to_del_to_del.append((ctx_keys, line))
""" # More old-to-new config handling. ip import-table no longer accepts
More old-to-new config handling. ip import-table no longer accepts # distance, but we honor the old syntax. But 'show running' shows only
distance, but we honor the old syntax. But 'show running' shows only # the new syntax. This causes an unnecessary 'no import-table' followed
the new syntax. This causes an unnecessary 'no import-table' followed # by the same old 'ip import-table' which causes perturbations in
by the same old 'ip import-table' which causes perturbations in # announced routes leading to traffic blackholes. Fix this issue.
announced routes leading to traffic blackholes. Fix this issue.
"""
re_importtbl = re.search("^ip\s+import-table\s+(\d+)$", ctx_keys[0]) re_importtbl = re.search("^ip\s+import-table\s+(\d+)$", ctx_keys[0])
if re_importtbl: if re_importtbl:
table_num = re_importtbl.group(1) table_num = re_importtbl.group(1)
@ -1236,17 +1193,16 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
) )
lines_to_add_to_del.append((ctx[0], None)) lines_to_add_to_del.append((ctx[0], None))
""" # ip/ipv6 prefix-lists and access-lists can be specified without a seq
ip/ipv6 prefix-lists and access-lists can be specified without a seq number. # number. However, the running config always adds 'seq x', where x is
However, the running config always adds 'seq x', where x is a number # a number incremented by 5 for every element of the prefix/access
incremented by 5 for every element of the prefix/access list. # list. So, ignore such lines as well. Sample prefix-list and
So, ignore such lines as well. Sample prefix-list and acces-list lines: # acces-list lines:
ip prefix-list PR-TABLE-2 seq 5 permit 20.8.2.0/24 le 32 # ip prefix-list PR-TABLE-2 seq 5 permit 20.8.2.0/24 le 32
ip prefix-list PR-TABLE-2 seq 10 permit 20.8.2.0/24 le 32 # ip prefix-list PR-TABLE-2 seq 10 permit 20.8.2.0/24 le 32
ipv6 prefix-list vrfdev6-12 permit 2000:9:2::/64 gt 64 # ipv6 prefix-list vrfdev6-12 permit 2000:9:2::/64 gt 64
access-list FOO seq 5 permit 2.2.2.2/32 # access-list FOO seq 5 permit 2.2.2.2/32
ipv6 access-list BAR seq 5 permit 2:2:2::2/128 # ipv6 access-list BAR seq 5 permit 2:2:2::2/128
"""
re_acl_pfxlst = re.search( re_acl_pfxlst = re.search(
"^(ip |ipv6 |)(prefix-list|access-list)(\s+\S+\s+)(seq \d+\s+)(permit|deny)(.*)$", "^(ip |ipv6 |)(prefix-list|access-list)(\s+\S+\s+)(seq \d+\s+)(permit|deny)(.*)$",
ctx_keys[0], ctx_keys[0],
@ -1265,12 +1221,9 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_del_to_del.append((ctx_keys, None)) lines_to_del_to_del.append((ctx_keys, None))
lines_to_add_to_del.append(((tmpline,), None)) lines_to_add_to_del.append(((tmpline,), None))
found = True found = True
""" # If prefix-lists or access-lists are being deleted and not added
If prefix-lists or access-lists are being deleted and # (see comment above), add command with 'no' to lines_to_add and
not added (see comment above), add command with 'no' to # remove from lines_to_del to improve scaling performance.
lines_to_add and remove from lines_to_del to improve
scaling performance.
"""
if found is False: if found is False:
add_cmd = ("no " + ctx_keys[0],) add_cmd = ("no " + ctx_keys[0],)
lines_to_add.append((add_cmd, None)) lines_to_add.append((add_cmd, None))
@ -1302,16 +1255,12 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_add, ctx_keys, route_target_both_line lines_to_add, ctx_keys, route_target_both_line
) )
""" # If the running configs has
If the running configs has # route-target import 1:1
route-target import 1:1 # route-target export 1:1
route-target export 1:1 # and the config we are reloading against has
# route-target both 1:1
and the config we are reloading against has # then we can ignore deleting the import/export and ignore adding the 'both'
route-target both 1:1
then we can ignore deleting the import/export and ignore adding the 'both'
"""
if found_route_target_export_line and found_route_target_both_line: if found_route_target_export_line and found_route_target_both_line:
lines_to_del_to_del.append((ctx_keys, route_target_import_line)) lines_to_del_to_del.append((ctx_keys, route_target_import_line))
lines_to_del_to_del.append((ctx_keys, route_target_export_line)) lines_to_del_to_del.append((ctx_keys, route_target_export_line))
@ -1333,23 +1282,21 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_del_to_del.append((ctx_keys, line)) lines_to_del_to_del.append((ctx_keys, line))
lines_to_add_to_del.append((ctx_keys, line)) lines_to_add_to_del.append((ctx_keys, line))
else: else:
""" # We have commands that used to be displayed in the global part
We have commands that used to be displayed in the global part # of 'router bgp' that are now displayed under 'address-family ipv4 unicast'
of 'router bgp' that are now displayed under 'address-family ipv4 unicast' #
# # old way
# old way # router bgp 64900
router bgp 64900 # neighbor ISL advertisement-interval 0
neighbor ISL advertisement-interval 0 #
# vs.
vs. #
# # new way
# new way # router bgp 64900
router bgp 64900 # address-family ipv4 unicast
address-family ipv4 unicast # neighbor ISL advertisement-interval 0
neighbor ISL advertisement-interval 0 #
# Look to see if we are deleting it in one format just to add it back in the other
Look to see if we are deleting it in one format just to add it back in the other
"""
if ( if (
ctx_keys[0].startswith("router bgp") ctx_keys[0].startswith("router bgp")
and len(ctx_keys) > 1 and len(ctx_keys) > 1
@ -1382,19 +1329,22 @@ def ignore_unconfigurable_lines(lines_to_add, lines_to_del):
for (ctx_keys, line) in lines_to_del: for (ctx_keys, line) in lines_to_del:
if ( # The integrated-vtysh-config one is technically "no"able but if we did
ctx_keys[0].startswith("frr version") # so frr-reload would stop working so do not let the user shoot
or ctx_keys[0].startswith("frr defaults") # themselves in the foot by removing this.
or ctx_keys[0].startswith("username") if any(
or ctx_keys[0].startswith("password") [
or ctx_keys[0].startswith("line vty") ctx_keys[0].startswith(x)
or for x in [
# This is technically "no"able but if we did so frr-reload would "frr version",
# stop working so do not let the user shoot themselves in the foot "frr defaults",
# by removing this. "username",
ctx_keys[0].startswith("service integrated-vtysh-config") "password",
"line vty",
"service integrated-vtysh-config",
]
]
): ):
log.info('"%s" cannot be removed' % (ctx_keys[-1],)) log.info('"%s" cannot be removed' % (ctx_keys[-1],))
lines_to_del_to_del.append((ctx_keys, line)) lines_to_del_to_del.append((ctx_keys, line))