mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-29 23:09:34 +00:00
Merge pull request #10048 from qlyoung/fix-reload-doc-comments
Fix reload comments
This commit is contained in:
commit
fd7d888003
@ -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))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user