mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-28 21:19:20 +00:00
Merge pull request #5224 from manuhalo/fix_frr_reload_paths
Fixes and extensions to frr_reload.py
This commit is contained in:
commit
1d92edb209
@ -414,9 +414,9 @@ struct cmd_node {
|
||||
|
||||
/* Daemons lists */
|
||||
#define DAEMONS_STR \
|
||||
"For the zebra daemon\nFor the rip daemon\nFor the ripng daemon\nFor the ospf daemon\nFor the ospfv6 daemon\nFor the bgp daemon\nFor the isis daemon\nFor the pbr daemon\nFor the fabricd daemon\nFor the pim daemon\nFor the static daemon\nFor the sharpd daemon\nFor the vrrpd daemon\n"
|
||||
"For the zebra daemon\nFor the rip daemon\nFor the ripng daemon\nFor the ospf daemon\nFor the ospfv6 daemon\nFor the bgp daemon\nFor the isis daemon\nFor the pbr daemon\nFor the fabricd daemon\nFor the pim daemon\nFor the static daemon\nFor the sharpd daemon\nFor the vrrpd daemon\nFor the ldpd daemon\n"
|
||||
#define DAEMONS_LIST \
|
||||
"<zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pbrd|fabricd|pimd|staticd|sharpd|vrrpd>"
|
||||
"<zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pbrd|fabricd|pimd|staticd|sharpd|vrrpd|ldpd>"
|
||||
|
||||
/* Prototypes. */
|
||||
extern void install_node(struct cmd_node *, int (*)(struct vty *));
|
||||
|
@ -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,7 +154,7 @@ 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 -",
|
||||
bindir + "/vtysh --config_dir " + confdir + " -c 'show run " + daemon + "' | /usr/bin/tail -n +4 | " + bindir + "/vtysh --config_dir " + confdir + " -m -f -",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
ve = VtyshMarkException(e)
|
||||
@ -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')
|
||||
|
||||
@ -1075,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
|
||||
@ -1083,7 +1104,7 @@ def vtysh_config_available():
|
||||
"""
|
||||
|
||||
try:
|
||||
cmd = ['/usr/bin/vtysh', '-c', 'conf t']
|
||||
cmd = [str(bindir + '/vtysh'), '--config_dir', confdir, '-c', 'conf t']
|
||||
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip()
|
||||
|
||||
if 'VTY configuration is locked by other VTY' in output.decode('utf-8'):
|
||||
@ -1111,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
|
||||
@ -1150,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):
|
||||
@ -1163,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)
|
||||
@ -1176,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:
|
||||
@ -1185,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 = []
|
||||
@ -1221,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())
|
||||
@ -1265,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)
|
||||
@ -1297,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.
|
||||
@ -1353,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:
|
||||
@ -1361,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], stderr=subprocess.STDOUT)
|
||||
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)
|
||||
|
@ -725,19 +725,19 @@ int vtysh_mark_file(const char *filename)
|
||||
switch (vty->node) {
|
||||
case LDP_IPV4_IFACE_NODE:
|
||||
if (strncmp(vty_buf_copy, " ", 3)) {
|
||||
vty_out(vty, " end\n");
|
||||
vty_out(vty, " exit-ldp-if\n");
|
||||
vty->node = LDP_IPV4_NODE;
|
||||
}
|
||||
break;
|
||||
case LDP_IPV6_IFACE_NODE:
|
||||
if (strncmp(vty_buf_copy, " ", 3)) {
|
||||
vty_out(vty, " end\n");
|
||||
vty_out(vty, " exit-ldp-if\n");
|
||||
vty->node = LDP_IPV6_NODE;
|
||||
}
|
||||
break;
|
||||
case LDP_PSEUDOWIRE_NODE:
|
||||
if (strncmp(vty_buf_copy, " ", 2)) {
|
||||
vty_out(vty, " end\n");
|
||||
vty_out(vty, " exit\n");
|
||||
vty->node = LDP_L2VPN_NODE;
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user