diff options
Diffstat (limited to 'tools/frr-reload.py')
| -rwxr-xr-x | tools/frr-reload.py | 500 |
1 files changed, 127 insertions, 373 deletions
diff --git a/tools/frr-reload.py b/tools/frr-reload.py index a326ecc0f9..7c6a83a51d 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -513,9 +513,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,43 +571,80 @@ 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 ", - "evpn mh", - "frr ", - "fpm ", - "hostname ", - "ip ", - "ipv6 ", - "log ", - "mac access-list ", - "mpls lsp", - "mpls label", - "no ", - "password ", - "pbr ", - "ptm-enable", - "router-id ", - "service ", - "table ", - "username ", - "vni ", - "vrrp autoconfigure", - "zebra " - ) + # stack of context keywords + cur_ctx_keywords = [ctx_keywords] + # list of stored commands + cur_ctx_lines = [] for line in self.lines: @@ -620,357 +654,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, - ) - - 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, - ) + ctx_keys.pop() + cur_ctx_keywords.pop() + cur_ctx_lines = [] - 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, - ) + continue - elif new_ctx is True: - if not main_ctx_key: - ctx_keys = [ - line, - ] - else: - ctx_keys = copy.deepcopy(main_ctx_key) - main_ctx_key = [] + if line.startswith("end"): + # exit all contexts + while len(ctx_keys) > 0: + # save current context + self.save_contexts(ctx_keys, cur_ctx_lines) - current_context_lines = [] - new_ctx = False - log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys) + # exit current context + log.debug("LINE %-50s: exit context %-50s", line, ctx_keys) - 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 = [] + ctx_keys.pop() + cur_ctx_keywords.pop() + cur_ctx_lines = [] - # 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): |
