summaryrefslogtreecommitdiff
path: root/tools/frr-reload.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/frr-reload.py')
-rwxr-xr-xtools/frr-reload.py109
1 files changed, 79 insertions, 30 deletions
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index f5784b7ecd..3e97635dfe 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -114,7 +114,7 @@ class Config(object):
self.lines = []
self.contexts = OrderedDict()
- def load_from_file(self, filename):
+ def load_from_file(self, filename, bindir, confdir):
"""
Read configuration from specified file and slurp it into internal memory
The internal representation has been marked appropriately by passing it
@@ -123,7 +123,7 @@ class Config(object):
log.info('Loading Config object from file %s', filename)
try:
- file_output = subprocess.check_output(['/usr/bin/vtysh', '-m', '-f', filename],
+ file_output = subprocess.check_output([str(bindir + '/vtysh'), '-m', '-f', filename, '--config_dir', confdir],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
ve = VtyshMarkException(e)
@@ -144,7 +144,7 @@ class Config(object):
self.load_contexts()
- def load_from_show_running(self):
+ def load_from_show_running(self, bindir, confdir, daemon):
"""
Read running configuration and slurp it into internal memory
The internal representation has been marked appropriately by passing it
@@ -154,8 +154,8 @@ class Config(object):
try:
config_text = subprocess.check_output(
- "/usr/bin/vtysh -c 'show run' | /usr/bin/tail -n +4 | /usr/bin/vtysh -m -f -",
- shell=True, stderr=subprocess.STDOUT)
+ bindir + "/vtysh --config_dir " + confdir + " -c 'show run " + daemon + "' | /usr/bin/tail -n +4 | " + bindir + "/vtysh --config_dir " + confdir + " -m -f -",
+ shell=True)
except subprocess.CalledProcessError as e:
ve = VtyshMarkException(e)
ve.output = e.output
@@ -404,7 +404,8 @@ end
"ip ",
"ipv6 ",
"log ",
- "mpls",
+ "mpls lsp",
+ "mpls label",
"no ",
"password ",
"ptm-enable",
@@ -424,7 +425,12 @@ end
continue
# one line contexts
- if new_ctx is True and any(line.startswith(keyword) for keyword in oneline_ctx_keywords):
+ # 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
@@ -467,7 +473,7 @@ end
current_context_lines = []
log.debug('LINE %-50s: popping from subcontext to ctx%-50s', line, ctx_keys)
- elif line == "exit-vni":
+ elif line in ["exit-vni", "exit-ldp-if"]:
if sub_main_ctx_key:
self.save_contexts(ctx_keys, current_context_lines)
@@ -489,7 +495,8 @@ end
elif (line.startswith("address-family ") or
line.startswith("vnc defaults") or
line.startswith("vnc l2-group") or
- line.startswith("vnc nve-group")):
+ line.startswith("vnc nve-group") or
+ line.startswith("member pseudowire")):
main_ctx_key = []
# Save old context first
@@ -498,9 +505,9 @@ end
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug('LINE %-50s: entering sub-context, append to ctx_keys', line)
- if line == "address-family ipv6":
+ 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":
+ 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")
@@ -518,6 +525,18 @@ end
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)
else:
# Continuing in an existing context, add non-commented lines to it
@@ -528,13 +547,15 @@ end
self.save_contexts(ctx_keys, current_context_lines)
-def line_to_vtysh_conft(ctx_keys, line, delete):
+def line_to_vtysh_conft(ctx_keys, line, delete, bindir, confdir):
"""
Return the vtysh command for the specified context line
"""
cmd = []
- cmd.append('vtysh')
+ cmd.append(str(bindir + '/vtysh'))
+ cmd.append('--config_dir')
+ cmd.append(confdir)
cmd.append('-c')
cmd.append('conf t')
@@ -755,9 +776,10 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
if re_nbr_bfd_timers:
nbr = re_nbr_bfd_timers.group(1)
bfd_nbr = "neighbor %s" % nbr
+ bfd_search_string = bfd_nbr + r' bfd (\S+) (\S+) (\S+)'
for (ctx_keys, add_line) in lines_to_add:
- re_add_nbr_bfd_timers = re.search(r'neighbor bfd_nbr bfd (\S+) (\S+) (\S+)', add_line)
+ re_add_nbr_bfd_timers = re.search(bfd_search_string, add_line)
if re_add_nbr_bfd_timers:
found_add_bfd_nbr = line_exist(lines_to_add, ctx_keys, bfd_nbr, False)
@@ -1074,7 +1096,7 @@ def compare_context_objects(newconf, running):
-def vtysh_config_available():
+def vtysh_config_available(bindir, confdir):
"""
Return False if no frr daemon is running or some other vtysh session is
in 'configuration terminal' mode which will prevent us from making any
@@ -1082,8 +1104,8 @@ def vtysh_config_available():
"""
try:
- cmd = ['/usr/bin/vtysh', '-c', 'conf t']
- output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip()
+ cmd = [str(bindir + '/vtysh'), '--config_dir', confdir, '-c', 'conf t']
+ output = subprocess.check_output(cmd).strip()
if 'VTY configuration is locked by other VTY' in output.decode('utf-8'):
print(output)
@@ -1110,6 +1132,11 @@ if __name__ == '__main__':
parser.add_argument('--stdout', action='store_true', help='Log to STDOUT', default=False)
parser.add_argument('filename', help='Location of new frr config file')
parser.add_argument('--overwrite', action='store_true', help='Overwrite frr.conf with running config output', default=False)
+ parser.add_argument('--bindir', help='path to the vtysh executable', default='/usr/bin')
+ parser.add_argument('--confdir', help='path to the daemon config files', default='/etc/frr')
+ parser.add_argument('--rundir', help='path for the temp config file', default='/var/run/frr')
+ parser.add_argument('--daemon', help='daemon for which want to replace the config', default='')
+
args = parser.parse_args()
# Logging
@@ -1149,8 +1176,29 @@ if __name__ == '__main__':
log.error(msg)
sys.exit(1)
+ # Verify that confdir is correct
+ if not os.path.isdir(args.confdir):
+ msg = "Confdir %s is not a valid path" % args.confdir
+ print(msg)
+ log.error(msg)
+ sys.exit(1)
+
+ # Verify that bindir is correct
+ if not os.path.isdir(args.bindir) or not os.path.isfile(args.bindir + '/vtysh'):
+ msg = "Bindir %s is not a valid path to vtysh" % args.bindir
+ print(msg)
+ log.error(msg)
+ sys.exit(1)
+
+ # verify that the daemon, if specified, is valid
+ if args.daemon and args.daemon not in ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd']:
+ msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon
+ print(msg)
+ log.error(msg)
+ sys.exit(1)
+
# Verify that 'service integrated-vtysh-config' is configured
- vtysh_filename = '/etc/frr/vtysh.conf'
+ vtysh_filename = args.confdir + '/vtysh.conf'
service_integrated_vtysh_config = True
if os.path.isfile(vtysh_filename):
@@ -1162,7 +1210,7 @@ if __name__ == '__main__':
service_integrated_vtysh_config = False
break
- if not service_integrated_vtysh_config:
+ if not service_integrated_vtysh_config and not args.daemon:
msg = "'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'"
print(msg)
log.error(msg)
@@ -1175,7 +1223,7 @@ if __name__ == '__main__':
# Create a Config object from the config generated by newconf
newconf = Config()
- newconf.load_from_file(args.filename)
+ newconf.load_from_file(args.filename, args.bindir, args.confdir)
reload_ok = True
if args.test:
@@ -1184,9 +1232,9 @@ if __name__ == '__main__':
running = Config()
if args.input:
- running.load_from_file(args.input)
+ running.load_from_file(args.input, args.bindir, args.confdir)
else:
- running.load_from_show_running()
+ running.load_from_show_running(args.bindir, args.confdir, args.daemon)
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
lines_to_configure = []
@@ -1220,7 +1268,7 @@ if __name__ == '__main__':
elif args.reload:
# We will not be able to do anything, go ahead and exit(1)
- if not vtysh_config_available():
+ if not vtysh_config_available(args.bindir, args.confdir):
sys.exit(1)
log.debug('New Frr Config\n%s', newconf.get_lines())
@@ -1264,7 +1312,7 @@ if __name__ == '__main__':
for x in range(2):
running = Config()
- running.load_from_show_running()
+ running.load_from_show_running(args.bindir, args.confdir, args.daemon)
log.debug('Running Frr Config (Pass #%d)\n%s', x, running.get_lines())
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
@@ -1296,7 +1344,7 @@ if __name__ == '__main__':
# 'no' commands are tricky, we can't just put them in a file and
# vtysh -f that file. See the next comment for an explanation
# of their quirks
- cmd = line_to_vtysh_conft(ctx_keys, line, True)
+ cmd = line_to_vtysh_conft(ctx_keys, line, True, args.bindir, args.confdir)
original_cmd = cmd
# Some commands in frr are picky about taking a "no" of the entire line.
@@ -1315,7 +1363,7 @@ if __name__ == '__main__':
while True:
try:
- _ = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ _ = subprocess.check_output(cmd)
except subprocess.CalledProcessError:
@@ -1352,7 +1400,7 @@ if __name__ == '__main__':
string.ascii_uppercase +
string.digits) for _ in range(6))
- filename = "/var/run/frr/reload-%s.txt" % random_string
+ filename = args.rundir + "/reload-%s.txt" % random_string
log.info("%s content\n%s" % (filename, pformat(lines_to_configure)))
with open(filename, 'w') as fh:
@@ -1360,15 +1408,16 @@ if __name__ == '__main__':
fh.write(line + '\n')
try:
- subprocess.check_output(['/usr/bin/vtysh', '-f', filename], stderr=subprocess.STDOUT)
+ subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename])
except subprocess.CalledProcessError as e:
log.warning("frr-reload.py failed due to\n%s" % e.output)
reload_ok = False
os.unlink(filename)
# Make these changes persistent
- if args.overwrite or args.filename != '/etc/frr/frr.conf':
- subprocess.call(['/usr/bin/vtysh', '-c', 'write'])
+ target = str(args.confdir + '/frr.conf')
+ if args.overwrite or (not args.daemon and args.filename != target):
+ subprocess.call([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-c', 'write'])
if not reload_ok:
sys.exit(1)