summaryrefslogtreecommitdiff
path: root/tools/frr-reload.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/frr-reload.py')
-rwxr-xr-xtools/frr-reload.py577
1 files changed, 135 insertions, 442 deletions
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 9d41305ec3..da51c231d1 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
# Frr Reloader
# Copyright (C) 2014 Cumulus Networks, Inc.
#
@@ -30,7 +30,6 @@ This program
from __future__ import print_function, unicode_literals
import argparse
-import copy
import logging
import os, os.path
import random
@@ -39,25 +38,12 @@ import string
import subprocess
import sys
from collections import OrderedDict
-
-try:
- from ipaddress import IPv6Address, ip_network
-except ImportError:
- from ipaddr import IPv6Address, IPNetwork
+from ipaddress import IPv6Address, ip_network
from pprint import pformat
-try:
- dict.iteritems
-except AttributeError:
- # Python 3
- def iteritems(d):
- return iter(d.items())
-
-
-else:
- # Python 2
- def iteritems(d):
- return d.iteritems()
+# Python 3
+def iteritems(d):
+ return iter(d.items())
log = logging.getLogger(__name__)
@@ -372,22 +358,13 @@ class Config(object):
addr = re_key_rt.group(2)
if "/" in addr:
try:
- if "ipaddress" not in sys.modules:
- newaddr = IPNetwork(addr)
- key[0] = "%s route %s/%s%s" % (
- re_key_rt.group(1),
- newaddr.network,
- newaddr.prefixlen,
- re_key_rt.group(3),
- )
- else:
- newaddr = ip_network(addr, strict=False)
- key[0] = "%s route %s/%s%s" % (
- re_key_rt.group(1),
- str(newaddr.network_address),
- newaddr.prefixlen,
- re_key_rt.group(3),
- )
+ newaddr = ip_network(addr, strict=False)
+ key[0] = "%s route %s/%s%s" % (
+ re_key_rt.group(1),
+ str(newaddr.network_address),
+ newaddr.prefixlen,
+ re_key_rt.group(3),
+ )
except ValueError:
pass
@@ -398,17 +375,11 @@ class Config(object):
addr = re_key_rt.group(4)
if "/" in addr:
try:
- if "ipaddress" not in sys.modules:
- newaddr = "%s/%s" % (
- IPNetwork(addr).network,
- IPNetwork(addr).prefixlen,
- )
- else:
- network_addr = ip_network(addr, strict=False)
- newaddr = "%s/%s" % (
- str(network_addr.network_address),
- network_addr.prefixlen,
- )
+ network_addr = ip_network(addr, strict=False)
+ newaddr = "%s/%s" % (
+ str(network_addr.network_address),
+ network_addr.prefixlen,
+ )
except ValueError:
newaddr = addr
else:
@@ -444,20 +415,12 @@ class Config(object):
addr = addr + "/8"
try:
- if "ipaddress" not in sys.modules:
- newaddr = IPNetwork(addr)
- line = "network %s/%s %s" % (
- newaddr.network,
- newaddr.prefixlen,
- re_net.group(2),
- )
- else:
- network_addr = ip_network(addr, strict=False)
- line = "network %s/%s %s" % (
- str(network_addr.network_address),
- network_addr.prefixlen,
- re_net.group(2),
- )
+ network_addr = ip_network(addr, strict=False)
+ line = "network %s/%s %s" % (
+ str(network_addr.network_address),
+ network_addr.prefixlen,
+ re_net.group(2),
+ )
newlines.append(line)
except ValueError:
# Really this should be an error. Whats a network
@@ -513,9 +476,6 @@ class Config(object):
Parse the configuration and create contexts for each appropriate block
"""
- current_context_lines = []
- ctx_keys = []
-
"""
The end of a context is flagged via the 'end' keyword:
@@ -574,42 +534,57 @@ end
# 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 = {
+ "router bgp ": {
+ "address-family ": {
+ "vni ": {},
+ },
+ "vnc ": {},
+ "vrf-policy ": {},
+ "bmp ": {},
+ "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
ctx_keys = []
- main_ctx_key = []
- new_ctx = True
-
- # the keywords that we know are single line contexts. bgp in this case
- # is not the main router bgp block, but enabling multi-instance
- oneline_ctx_keywords = (
- "access-list ",
- "agentx",
- "allow-external-route-update",
- "bgp ",
- "debug ",
- "domainname ",
- "dump ",
- "enable ",
- "frr ",
- "fpm ",
- "hostname ",
- "ip ",
- "ipv6 ",
- "log ",
- "mac access-list ",
- "mpls lsp",
- "mpls label",
- "no ",
- "password ",
- "pbr ",
- "ptm-enable",
- "router-id ",
- "service ",
- "table ",
- "username ",
- "zebra ",
- "vrrp autoconfigure",
- "evpn mh",
- )
+ # stack of context keywords
+ cur_ctx_keywords = [ctx_keywords]
+ # list of stored commands
+ cur_ctx_lines = []
for line in self.lines:
@@ -619,357 +594,77 @@ end
if line.startswith("!") or line.startswith("#"):
continue
- if (
- len(ctx_keys) == 2
- and ctx_keys[0].startswith("bfd")
- and ctx_keys[1].startswith("profile ")
- and line == "end"
- ):
- log.debug("LINE %-50s: popping from sub context, %-50s", line, ctx_keys)
-
- if main_ctx_key:
- self.save_contexts(ctx_keys, current_context_lines)
- ctx_keys = copy.deepcopy(main_ctx_key)
- current_context_lines = []
+ if line.startswith("exit"):
+ # ignore on top level
+ if len(ctx_keys) == 0:
continue
- # one line contexts
- # there is one exception though: ldpd accepts a 'router-id' clause
- # as part of its 'mpls ldp' config context. If we are processing
- # ldp configuration and encounter a router-id we should NOT switch
- # to a new context
- if (
- new_ctx is True
- and any(line.startswith(keyword) for keyword in oneline_ctx_keywords)
- and not (
- ctx_keys
- and ctx_keys[0].startswith("mpls ldp")
- and line.startswith("router-id ")
- )
- ):
- self.save_contexts(ctx_keys, current_context_lines)
-
- # Start a new context
- main_ctx_key = []
- ctx_keys = [
- line,
- ]
- current_context_lines = []
-
- log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys)
- self.save_contexts(ctx_keys, current_context_lines)
- new_ctx = True
-
- elif line == "end":
- self.save_contexts(ctx_keys, current_context_lines)
- log.debug("LINE %-50s: exiting old context, %-50s", line, ctx_keys)
-
- # Start a new context
- new_ctx = True
- main_ctx_key = []
- ctx_keys = []
- current_context_lines = []
-
- elif line == "exit" and ctx_keys[0].startswith("rpki"):
- self.save_contexts(ctx_keys, current_context_lines)
- log.debug("LINE %-50s: exiting old context, %-50s", line, ctx_keys)
-
- # Start a new context
- new_ctx = True
- main_ctx_key = []
- ctx_keys = []
- current_context_lines = []
-
- elif line == "exit-vrf":
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines.append(line)
- log.debug(
- "LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys
- )
+ # save current context
+ self.save_contexts(ctx_keys, cur_ctx_lines)
- # Start a new context
- new_ctx = True
- main_ctx_key = []
- ctx_keys = []
- current_context_lines = []
+ # exit current context
+ log.debug("LINE %-50s: exit context %-50s", line, ctx_keys)
- elif (
- line == "exit"
- and len(ctx_keys) > 1
- and ctx_keys[0].startswith("segment-routing")
- ):
- self.save_contexts(ctx_keys, current_context_lines)
-
- # Start a new context
- ctx_keys = ctx_keys[:-1]
- current_context_lines = []
- log.debug(
- "LINE %-50s: popping segment routing sub-context to ctx%-50s",
- line,
- ctx_keys,
- )
+ ctx_keys.pop()
+ cur_ctx_keywords.pop()
+ cur_ctx_lines = []
- elif line in ["exit-address-family", "exit", "exit-vnc"]:
- # if this exit is for address-family ipv4 unicast, ignore the pop
- if main_ctx_key:
- self.save_contexts(ctx_keys, current_context_lines)
-
- # Start a new context
- ctx_keys = copy.deepcopy(main_ctx_key)
- current_context_lines = []
- log.debug(
- "LINE %-50s: popping from subcontext to ctx%-50s",
- line,
- ctx_keys,
- )
+ continue
- elif line in ["exit-vni", "exit-ldp-if"]:
- if sub_main_ctx_key:
- self.save_contexts(ctx_keys, current_context_lines)
-
- # Start a new context
- ctx_keys = copy.deepcopy(sub_main_ctx_key)
- current_context_lines = []
- log.debug(
- "LINE %-50s: popping from sub-subcontext to ctx%-50s",
- line,
- ctx_keys,
- )
+ if line.startswith("end"):
+ # exit all contexts
+ while len(ctx_keys) > 0:
+ # save current context
+ self.save_contexts(ctx_keys, cur_ctx_lines)
- elif new_ctx is True:
- if not main_ctx_key:
- ctx_keys = [
- line,
- ]
- else:
- ctx_keys = copy.deepcopy(main_ctx_key)
- main_ctx_key = []
+ # exit current context
+ log.debug("LINE %-50s: exit context %-50s", line, ctx_keys)
- current_context_lines = []
- new_ctx = False
- log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys)
+ ctx_keys.pop()
+ cur_ctx_keywords.pop()
+ cur_ctx_lines = []
- elif (
- line.startswith("address-family ")
- or line.startswith("vnc defaults")
- or line.startswith("vnc l2-group")
- or line.startswith("vnc nve-group")
- or line.startswith("peer")
- or line.startswith("key ")
- or line.startswith("member pseudowire")
- ):
- main_ctx_key = []
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug("LINE %-50s: entering sub-context, append to ctx_keys", line)
+ continue
- if line == "address-family ipv6" and not ctx_keys[0].startswith(
- "mpls ldp"
- ):
- ctx_keys.append("address-family ipv6 unicast")
- elif line == "address-family ipv4" and not ctx_keys[0].startswith(
- "mpls ldp"
- ):
- ctx_keys.append("address-family ipv4 unicast")
- elif line == "address-family evpn":
- ctx_keys.append("address-family l2vpn evpn")
- else:
+ new_ctx = False
+
+ # check if the line is a context-entering keyword
+ for k, v in cur_ctx_keywords[-1].items():
+ if line.startswith(k):
+ # candidate-path is a special case. It may be a node and
+ # may be a single-line command. The distinguisher is the
+ # word "dynamic" or "explicit" at the middle of the line.
+ # It was perhaps not the best choice by the pathd authors
+ # but we have what we have.
+ if k == "candidate-path " and "explicit" in line:
+ # this is a single-line command
+ break
+
+ # save current context
+ self.save_contexts(ctx_keys, cur_ctx_lines)
+
+ # enter new context
+ new_ctx = True
ctx_keys.append(line)
+ cur_ctx_keywords.append(v)
+ cur_ctx_lines = []
- elif (
- line.startswith("vni ")
- and len(ctx_keys) == 2
- and ctx_keys[0].startswith("router bgp")
- and ctx_keys[1] == "address-family l2vpn evpn"
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- sub_main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering sub-sub-context, append to ctx_keys", line
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("interface ")
- and len(ctx_keys) == 2
- and ctx_keys[0].startswith("mpls ldp")
- and ctx_keys[1].startswith("address-family")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- sub_main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering sub-sub-context, append to ctx_keys", line
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("traffic-eng")
- and len(ctx_keys) == 1
- and ctx_keys[0].startswith("segment-routing")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- log.debug(
- "LINE %-50s: entering segment routing sub-context, append to ctx_keys",
- line,
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("segment-list ")
- and len(ctx_keys) == 2
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- log.debug(
- "LINE %-50s: entering segment routing sub-context, append to ctx_keys",
- line,
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("policy ")
- and len(ctx_keys) == 2
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- log.debug(
- "LINE %-50s: entering segment routing sub-context, append to ctx_keys",
- line,
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("candidate-path ")
- and line.endswith(" dynamic")
- and len(ctx_keys) == 3
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- and ctx_keys[2].startswith("policy")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering candidate-path sub-context, append to ctx_keys",
- line,
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("pcep")
- and len(ctx_keys) == 2
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering pcep sub-context, append to ctx_keys", line
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("pce-config ")
- and len(ctx_keys) == 3
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- and ctx_keys[2].startswith("pcep")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering pce-config sub-context, append to ctx_keys",
- line,
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("pce ")
- and len(ctx_keys) == 3
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- and ctx_keys[2].startswith("pcep")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering pce sub-context, append to ctx_keys", line
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("pcc")
- and len(ctx_keys) == 3
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- and ctx_keys[2].startswith("pcep")
- ):
-
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering pcc sub-context, append to ctx_keys", line
- )
- ctx_keys.append(line)
-
- elif (
- line.startswith("profile ")
- and len(ctx_keys) == 1
- and ctx_keys[0].startswith("bfd")
- ):
+ log.debug("LINE %-50s: enter context %-50s", line, ctx_keys)
+ break
- # Save old context first
- self.save_contexts(ctx_keys, current_context_lines)
- current_context_lines = []
- main_ctx_key = copy.deepcopy(ctx_keys)
- log.debug(
- "LINE %-50s: entering BFD profile sub-context, append to ctx_keys",
- line,
- )
- ctx_keys.append(line)
+ if new_ctx:
+ continue
+ if len(ctx_keys) == 0:
+ log.debug("LINE %-50s: single-line context", line)
+ self.save_contexts([line], [])
else:
- # Continuing in an existing context, add non-commented lines to it
- current_context_lines.append(line)
- log.debug(
- "LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys
- )
+ log.debug("LINE %-50s: add to current context %-50s", line, ctx_keys)
+ cur_ctx_lines.append(line)
# Save the context of the last one
- self.save_contexts(ctx_keys, current_context_lines)
+ if len(ctx_keys) > 0:
+ self.save_contexts(ctx_keys, cur_ctx_lines)
def lines_to_config(ctx_keys, line, delete):
@@ -1030,15 +725,11 @@ def get_normalized_ipv6_line(line):
norm_word = None
if "/" in word:
try:
- if "ipaddress" not in sys.modules:
- v6word = IPNetwork(word)
- norm_word = "%s/%s" % (v6word.network, v6word.prefixlen)
- else:
- v6word = ip_network(word, strict=False)
- norm_word = "%s/%s" % (
- str(v6word.network_address),
- v6word.prefixlen,
- )
+ v6word = ip_network(word, strict=False)
+ norm_word = "%s/%s" % (
+ str(v6word.network_address),
+ v6word.prefixlen,
+ )
except ValueError:
pass
if not norm_word:
@@ -2177,7 +1868,9 @@ if __name__ == "__main__":
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]
+ nolines = [
+ x for x in nolines if "debug" not in x and "log file" not in x
+ ]
if not nolines:
continue