mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-10 22:26:42 +00:00
tools: always append "exit" in frr-reload.py
When reloading the following config: ``` router ospf6 area 0 range 2001:db8::/32 advertise exit ! interface eth0 ipv6 ospf6 area 0 exit ``` frr-reload.py doesn't execute "exit" commands. Because of that, it tries to execute "interface eth0" inside the "router ospf6" context and fails. To always execute all commands from the correct context, frr-reload.py should properly exit from every entered node. Fixes #10132. Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
This commit is contained in:
parent
c411512246
commit
792ada4738
@ -219,6 +219,53 @@ def get_normalized_mac_ip_line(line):
|
|||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
# 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 = {
|
||||||
|
"router bgp ": {
|
||||||
|
"address-family ": {
|
||||||
|
"vni ": {},
|
||||||
|
},
|
||||||
|
"vnc defaults": {},
|
||||||
|
"vnc nve-group ": {},
|
||||||
|
"vnc l2-group ": {},
|
||||||
|
"vrf-policy ": {},
|
||||||
|
"bmp targets ": {},
|
||||||
|
"segment-routing srv6": {},
|
||||||
|
},
|
||||||
|
"router rip": {},
|
||||||
|
"router ripng": {},
|
||||||
|
"router isis ": {},
|
||||||
|
"router openfabric ": {},
|
||||||
|
"router ospf": {},
|
||||||
|
"router ospf6": {},
|
||||||
|
"router eigrp ": {},
|
||||||
|
"router babel": {},
|
||||||
|
"mpls ldp": {"address-family ": {"interface ": {}}},
|
||||||
|
"l2vpn ": {"member pseudowire ": {}},
|
||||||
|
"key chain ": {"key ": {}},
|
||||||
|
"vrf ": {},
|
||||||
|
"interface ": {"link-params": {}},
|
||||||
|
"pseudowire ": {},
|
||||||
|
"segment-routing": {
|
||||||
|
"traffic-eng": {
|
||||||
|
"segment-list ": {},
|
||||||
|
"policy ": {"candidate-path ": {}},
|
||||||
|
"pcep": {"pcc": {}, "pce ": {}, "pce-config ": {}},
|
||||||
|
},
|
||||||
|
"srv6": {"locators": {"locator ": {}}},
|
||||||
|
},
|
||||||
|
"nexthop-group ": {},
|
||||||
|
"route-map ": {},
|
||||||
|
"pbr-map ": {},
|
||||||
|
"rpki": {},
|
||||||
|
"bfd": {"peer ": {}, "profile ": {}},
|
||||||
|
"line vty": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@ -490,54 +537,7 @@ class Config(object):
|
|||||||
key of the context. So "router bgp 10" is the key for the non-address
|
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
|
family part of bgp, "router bgp 10, address-family ipv6 unicast" is
|
||||||
the key for the subcontext and so on.
|
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 = {
|
|
||||||
"router bgp ": {
|
|
||||||
"address-family ": {
|
|
||||||
"vni ": {},
|
|
||||||
},
|
|
||||||
"vnc defaults": {},
|
|
||||||
"vnc nve-group ": {},
|
|
||||||
"vnc l2-group ": {},
|
|
||||||
"vrf-policy ": {},
|
|
||||||
"bmp targets ": {},
|
|
||||||
"segment-routing srv6": {},
|
|
||||||
},
|
|
||||||
"router rip": {},
|
|
||||||
"router ripng": {},
|
|
||||||
"router isis ": {},
|
|
||||||
"router openfabric ": {},
|
|
||||||
"router ospf": {},
|
|
||||||
"router ospf6": {},
|
|
||||||
"router eigrp ": {},
|
|
||||||
"router babel": {},
|
|
||||||
"mpls ldp": {"address-family ": {"interface ": {}}},
|
|
||||||
"l2vpn ": {"member pseudowire ": {}},
|
|
||||||
"key chain ": {"key ": {}},
|
|
||||||
"vrf ": {},
|
|
||||||
"interface ": {"link-params": {}},
|
|
||||||
"pseudowire ": {},
|
|
||||||
"segment-routing": {
|
|
||||||
"traffic-eng": {
|
|
||||||
"segment-list ": {},
|
|
||||||
"policy ": {"candidate-path ": {}},
|
|
||||||
"pcep": {"pcc": {}, "pce ": {}, "pce-config ": {}},
|
|
||||||
},
|
|
||||||
"srv6": {"locators": {"locator ": {}}},
|
|
||||||
},
|
|
||||||
"nexthop-group ": {},
|
|
||||||
"route-map ": {},
|
|
||||||
"pbr-map ": {},
|
|
||||||
"rpki": {},
|
|
||||||
"bfd": {"peer ": {}, "profile ": {}},
|
|
||||||
"line vty": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
# stack of context keys
|
# stack of context keys
|
||||||
ctx_keys = []
|
ctx_keys = []
|
||||||
# stack of context keywords
|
# stack of context keywords
|
||||||
@ -632,6 +632,20 @@ def lines_to_config(ctx_keys, line, delete):
|
|||||||
"""
|
"""
|
||||||
cmd = []
|
cmd = []
|
||||||
|
|
||||||
|
# If there's no `line` and `ctx_keys` length is 1, then it may be a single-line command.
|
||||||
|
# In this case, we should treat it as a single command in an empty context.
|
||||||
|
if len(ctx_keys) == 1 and not line:
|
||||||
|
single = True
|
||||||
|
|
||||||
|
for k, v in ctx_keywords.items():
|
||||||
|
if ctx_keys[0].startswith(k):
|
||||||
|
single = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if single:
|
||||||
|
line = ctx_keys[0]
|
||||||
|
ctx_keys = []
|
||||||
|
|
||||||
if line:
|
if line:
|
||||||
for (i, ctx_key) in enumerate(ctx_keys):
|
for (i, ctx_key) in enumerate(ctx_keys):
|
||||||
cmd.append(" " * i + ctx_key)
|
cmd.append(" " * i + ctx_key)
|
||||||
@ -652,6 +666,9 @@ def lines_to_config(ctx_keys, line, delete):
|
|||||||
else:
|
else:
|
||||||
cmd.append(indent + line)
|
cmd.append(indent + line)
|
||||||
|
|
||||||
|
for i in reversed(range(len(ctx_keys))):
|
||||||
|
cmd.append(" " * i + "exit")
|
||||||
|
|
||||||
# If line is None then we are typically deleting an entire
|
# If line is None then we are typically deleting an entire
|
||||||
# context ('no router ospf' for example)
|
# context ('no router ospf' for example)
|
||||||
else:
|
else:
|
||||||
@ -666,6 +683,10 @@ def lines_to_config(ctx_keys, line, delete):
|
|||||||
cmd.append("%sno %s" % (" " * (len(ctx_keys) - 1), ctx_keys[-1]))
|
cmd.append("%sno %s" % (" " * (len(ctx_keys) - 1), ctx_keys[-1]))
|
||||||
else:
|
else:
|
||||||
cmd.append("%s%s" % (" " * (len(ctx_keys) - 1), ctx_keys[-1]))
|
cmd.append("%s%s" % (" " * (len(ctx_keys) - 1), ctx_keys[-1]))
|
||||||
|
cmd.append("%sexit" % (" " * (len(ctx_keys) - 1)))
|
||||||
|
|
||||||
|
for i in reversed(range(len(ctx_keys) - 1)):
|
||||||
|
cmd.append(" " * i + "exit")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
@ -715,38 +736,6 @@ def line_exist(lines, target_ctx_keys, target_line, exact_match=True):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_for_exit_vrf(lines_to_add, lines_to_del):
|
|
||||||
|
|
||||||
# exit-vrf is a bit tricky. If the new config is missing it but we
|
|
||||||
# have configs under a vrf, we need to add it at the end to do the
|
|
||||||
# right context changes. If exit-vrf exists in both the running and
|
|
||||||
# new config, we cannot delete it or it will break context changes.
|
|
||||||
add_exit_vrf = False
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
for (ctx_keys, line) in lines_to_add:
|
|
||||||
if add_exit_vrf == True:
|
|
||||||
if ctx_keys[0] != prior_ctx_key:
|
|
||||||
insert_key = ((prior_ctx_key),)
|
|
||||||
lines_to_add.insert(index, ((insert_key, "exit-vrf")))
|
|
||||||
add_exit_vrf = False
|
|
||||||
|
|
||||||
if ctx_keys[0].startswith("vrf") and line:
|
|
||||||
if line != "exit-vrf":
|
|
||||||
add_exit_vrf = True
|
|
||||||
prior_ctx_key = ctx_keys[0]
|
|
||||||
else:
|
|
||||||
add_exit_vrf = False
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
for (ctx_keys, line) in lines_to_del:
|
|
||||||
if line == "exit-vrf":
|
|
||||||
if line_exist(lines_to_add, ctx_keys, line):
|
|
||||||
lines_to_del.remove((ctx_keys, line))
|
|
||||||
|
|
||||||
return (lines_to_add, lines_to_del)
|
|
||||||
|
|
||||||
|
|
||||||
def bgp_delete_inst_move_line(lines_to_del):
|
def bgp_delete_inst_move_line(lines_to_del):
|
||||||
# Deletion of bgp default inst followed by
|
# Deletion of bgp default inst followed by
|
||||||
# bgp vrf inst leads to issue of default
|
# bgp vrf inst leads to issue of default
|
||||||
@ -1788,7 +1777,6 @@ def compare_context_objects(newconf, running):
|
|||||||
if len(candidates_to_add) > 0:
|
if len(candidates_to_add) > 0:
|
||||||
lines_to_add.extend(candidates_to_add)
|
lines_to_add.extend(candidates_to_add)
|
||||||
|
|
||||||
(lines_to_add, lines_to_del) = check_for_exit_vrf(lines_to_add, lines_to_del)
|
|
||||||
(lines_to_add, lines_to_del) = ignore_delete_re_add_lines(
|
(lines_to_add, lines_to_del) = ignore_delete_re_add_lines(
|
||||||
lines_to_add, lines_to_del
|
lines_to_add, lines_to_del
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user