summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorSebastien Merle <sebastien@netdef.org>2020-07-31 18:04:20 +0200
committerSebastien Merle <sebastien@netdef.org>2020-12-18 16:34:02 +0100
commit4d7b695d3abe80fbb1b666aed8faa1b9e9387fdb (patch)
tree9dc538cd213acebfe50152a048799f4f09bbcbc9 /tools
parentMerge pull request #7717 from ranjanyash54/2368 (diff)
downloadfrr-4d7b695d3abe80fbb1b666aed8faa1b9e9387fdb.tar.xz
frr-4d7b695d3abe80fbb1b666aed8faa1b9e9387fdb.zip
pathd: New SR-TE policy management daemon
This new daemon manages Segment-Routing Traffic-Engineering (SR-TE) Policies and installs them into zebra. It provides the usual yang support and vtysh commands to define or change SR-TE Policies. In a nutshell SR-TE Policies provide the possibility to steer traffic through a (possibly dynamic) list of Segment Routing segments to the endpoint of the policy. This list of segments is part of a Candidate Path which again belongs to the SR-TE Policy. SR-TE Policies are uniquely identified by their color and endpoint. The color can be used to e.g. match BGP communities on incoming traffic. There can be multiple Candidate Paths for a single policy, the active Candidate Path is chosen according to certain conditions of which the most important is its preference. Candidate Paths can be explicit (fixed list of segments) or dynamic (list of segment comes from e.g. PCEP, see below). Configuration example: segment-routing traffic-eng segment-list SL index 10 mpls label 1111 index 20 mpls label 2222 ! policy color 4 endpoint 10.10.10.4 name POL4 binding-sid 104 candidate-path preference 100 name exp explicit segment-list SL candidate-path preference 200 name dyn dynamic ! ! ! There is an important connection between dynamic Candidate Paths and the overall topic of Path Computation. Later on for pathd a dynamic module will be introduced that is capable of communicating via the PCEP protocol with a PCE (Path Computation Element) which again is capable of calculating paths according to its local TED (Traffic Engineering Database). This dynamic module will be able to inject the mentioned dynamic Candidate Paths into pathd based on calculated paths from a PCE. https://tools.ietf.org/html/draft-ietf-spring-segment-routing-policy-06 Co-authored-by: Sebastien Merle <sebastien@netdef.org> Co-authored-by: Renato Westphal <renato@opensourcerouting.org> Co-authored-by: GalaxyGorilla <sascha@netdef.org> Co-authored-by: Emanuele Di Pascale <emanuele@voltanet.io> Signed-off-by: Sebastien Merle <sebastien@netdef.org>
Diffstat (limited to 'tools')
-rwxr-xr-xtools/frr-reload.py163
-rwxr-xr-xtools/frr.in2
2 files changed, 156 insertions, 9 deletions
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 3121551ee..137a3d5a0 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -626,6 +626,22 @@ end
ctx_keys = []
current_context_lines = []
+ 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:
@@ -637,7 +653,7 @@ end
log.debug(
"LINE %-50s: popping from subcontext to ctx%-50s",
line,
- ctx_keys,
+ ctx_keys
)
elif line in ["exit-vni", "exit-ldp-if"]:
@@ -727,6 +743,68 @@ end
)
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)
+
else:
# Continuing in an existing context, add non-commented lines to it
current_context_lines.append(line)
@@ -1244,6 +1322,9 @@ def compare_context_objects(newconf, running):
# Compare the two Config objects to find the lines that we need to add/del
lines_to_add = []
lines_to_del = []
+ pollist_to_del = []
+ seglist_to_del = []
+ candidates_to_add = []
delete_bgpd = False
# Find contexts that are in newconf but not in running
@@ -1326,6 +1407,32 @@ def compare_context_objects(newconf, running):
(running_ctx_keys[:1], None) in lines_to_del):
continue
+ # Segment routing and traffic engineering never need to be deleted
+ elif (
+ len(running_ctx_keys) > 1
+ and len(running_ctx_keys) < 3
+ and running_ctx_keys[0].startswith('segment-routing')
+ ):
+ continue
+
+ # Segment lists can only be deleted after we removed all the candidate paths that
+ # use them, so add them to a separate array that is going to be appended at the end
+ elif (
+ len(running_ctx_keys) == 3
+ and running_ctx_keys[0].startswith('segment-routing')
+ and running_ctx_keys[2].startswith('segment-list')
+ ):
+ seglist_to_del.append((running_ctx_keys, None))
+
+ # Policies must be deleted after there candidate path, to be sure
+ # we add them to a separate array that is going to be appended at the end
+ elif (
+ len(running_ctx_keys) == 3
+ and running_ctx_keys[0].startswith('segment-routing')
+ and running_ctx_keys[2].startswith('policy')
+ ):
+ pollist_to_del.append((running_ctx_keys, None))
+
# Non-global context
elif running_ctx_keys and not any(
"address-family" in key for key in running_ctx_keys
@@ -1340,6 +1447,14 @@ def compare_context_objects(newconf, running):
for line in running_ctx.lines:
lines_to_del.append((running_ctx_keys, line))
+ # if we have some policies commands to delete, append them to lines_to_del
+ if len(pollist_to_del) > 0:
+ lines_to_del.extend(pollist_to_del)
+
+ # if we have some segment list commands to delete, append them to lines_to_del
+ if len(seglist_to_del) > 0:
+ lines_to_del.extend(seglist_to_del)
+
# Find the lines within each context to add
# Find the lines within each context to del
for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts):
@@ -1349,7 +1464,19 @@ def compare_context_objects(newconf, running):
for line in newconf_ctx.lines:
if line not in running_ctx.dlines:
- lines_to_add.append((newconf_ctx_keys, line))
+
+ # candidate paths can only be added after the policy and segment list,
+ # so add them to a separate array that is going to be appended at the end
+ if (
+ len(newconf_ctx_keys) == 3
+ and newconf_ctx_keys[0].startswith('segment-routing')
+ and newconf_ctx_keys[2].startswith('policy ')
+ and line.startswith('candidate-path ')
+ ):
+ candidates_to_add.append((newconf_ctx_keys, line))
+
+ else:
+ lines_to_add.append((newconf_ctx_keys, line))
for line in running_ctx.lines:
if line not in newconf_ctx.dlines:
@@ -1358,10 +1485,27 @@ def compare_context_objects(newconf, running):
for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts):
if newconf_ctx_keys not in running.contexts:
- lines_to_add.append((newconf_ctx_keys, None))
- for line in newconf_ctx.lines:
- lines_to_add.append((newconf_ctx_keys, line))
+ # candidate paths can only be added after the policy and segment list,
+ # so add them to a separate array that is going to be appended at the end
+ if (
+ len(newconf_ctx_keys) == 4
+ and newconf_ctx_keys[0].startswith('segment-routing')
+ and newconf_ctx_keys[3].startswith('candidate-path')
+ ):
+ candidates_to_add.append((newconf_ctx_keys, None))
+ for line in newconf_ctx.lines:
+ candidates_to_add.append((newconf_ctx_keys, line))
+
+ else:
+ lines_to_add.append((newconf_ctx_keys, None))
+
+ for line in newconf_ctx.lines:
+ lines_to_add.append((newconf_ctx_keys, line))
+
+ # if we have some candidate paths commands to add, append them to lines_to_add
+ if len(candidates_to_add) > 0:
+ 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(
@@ -1523,10 +1667,11 @@ if __name__ == "__main__":
"staticd",
"vrrpd",
"ldpd",
+ "pathd",
]:
- log.error(
- "Daemon %s is not a valid option for 'show running-config'" % args.daemon
- )
+ msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon
+ print(msg)
+ log.error(msg)
sys.exit(1)
vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace)
@@ -1574,6 +1719,8 @@ if __name__ == "__main__":
else:
running.load_from_show_running(args.daemon)
+
+
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
lines_to_configure = []
diff --git a/tools/frr.in b/tools/frr.in
index b860797d5..889c075f8 100755
--- a/tools/frr.in
+++ b/tools/frr.in
@@ -27,7 +27,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
MAX_INSTANCES=5
RELOAD_SCRIPT="$D_PATH/frr-reload.py"