diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/container.py | 33 | ||||
-rwxr-xr-x | src/conf_mode/protocols_babel.py | 163 | ||||
-rwxr-xr-x | src/op_mode/generate_public_key_command.py | 59 | ||||
-rwxr-xr-x | src/op_mode/openvpn.py | 6 | ||||
-rwxr-xr-x | src/op_mode/restart_frr.py | 2 |
5 files changed, 238 insertions, 25 deletions
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 90e5f84f2..10e9e9213 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -256,6 +256,11 @@ def generate_run_arguments(name, container_config): for k, v in container_config['environment'].items(): env_opt += f" --env \"{k}={v['value']}\"" + hostname = '' + if 'host_name' in container_config: + hostname = container_config['host_name'] + hostname = f'--hostname {hostname}' + # Publish ports port = '' if 'port' in container_config: @@ -277,10 +282,29 @@ def generate_run_arguments(name, container_config): container_base_cmd = f'--detach --interactive --tty --replace {cap_add} ' \ f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \ - f'--name {name} {device} {port} {volume} {env_opt}' + f'--name {name} {hostname} {device} {port} {volume} {env_opt}' + + entrypoint = '' + if 'entrypoint' in container_config: + # it needs to be json-formatted with single quote on the outside + entrypoint = json_write(container_config['entrypoint'].split()).replace('"', """) + entrypoint = f'--entrypoint '{entrypoint}'' + + hostname = '' + if 'host_name' in container_config: + hostname = container_config['host_name'] + hostname = f'--hostname {hostname}' + + command = '' + if 'command' in container_config: + command = container_config['command'].strip() + + command_arguments = '' + if 'arguments' in container_config: + command_arguments = container_config['arguments'].strip() if 'allow_host_networks' in container_config: - return f'{container_base_cmd} --net host {image}' + return f'{container_base_cmd} --net host {entrypoint} {image} {command} {command_arguments}'.strip() ip_param = '' networks = ",".join(container_config['network']) @@ -289,7 +313,7 @@ def generate_run_arguments(name, container_config): address = container_config['network'][network]['address'] ip_param = f'--ip {address}' - return f'{container_base_cmd} --net {networks} {ip_param} {image}' + return f'{container_base_cmd} --net {networks} {ip_param} {entrypoint} {image} {command} {command_arguments}'.strip() def generate(container): # bail out early - looks like removal from running config @@ -341,7 +365,8 @@ def generate(container): file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service') run_args = generate_run_arguments(name, container_config) - render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args}) + render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args,}, + formater=lambda _: _.replace(""", '"').replace("'", "'")) return None diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py new file mode 100755 index 000000000..20821c7f2 --- /dev/null +++ b/src/conf_mode/protocols_babel.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021-2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configdict import node_changed +from vyos.configverify import verify_common_route_maps +from vyos.configverify import verify_access_list +from vyos.configverify import verify_prefix_list +from vyos.util import dict_search +from vyos.xml import defaults +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'babel'] + babel = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + babel['interface_removed'] = list(interfaces_removed) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + babel.update({'deleted' : ''}) + return babel + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + + # XXX: T2665: we currently have no nice way for defaults under tag nodes, + # clean them out and add them manually :( + del default_values['interface'] + + # merge in remaining default values + babel = dict_merge(default_values, babel) + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify(). + # + # XXX: one MUST always call this without the key_mangling() option! See + # vyos.configverify.verify_common_route_maps() for more information. + tmp = conf.get_config_dict(['policy']) + # Merge policy dict into "regular" config dict + babel = dict_merge(tmp, babel) + return babel + +def verify(babel): + if not babel: + return None + + # verify distribute_list + if "distribute_list" in babel: + acl_keys = { + "ipv4": [ + "distribute_list.ipv4.access_list.in", + "distribute_list.ipv4.access_list.out", + ], + "ipv6": [ + "distribute_list.ipv6.access_list.in", + "distribute_list.ipv6.access_list.out", + ] + } + prefix_list_keys = { + "ipv4": [ + "distribute_list.ipv4.prefix_list.in", + "distribute_list.ipv4.prefix_list.out", + ], + "ipv6":[ + "distribute_list.ipv6.prefix_list.in", + "distribute_list.ipv6.prefix_list.out", + ] + } + for address_family in ["ipv4", "ipv6"]: + for iface_key in babel["distribute_list"].get(address_family, {}).get("interface", {}).keys(): + acl_keys[address_family].extend([ + f"distribute_list.{address_family}.interface.{iface_key}.access_list.in", + f"distribute_list.{address_family}.interface.{iface_key}.access_list.out" + ]) + prefix_list_keys[address_family].extend([ + f"distribute_list.{address_family}.interface.{iface_key}.prefix_list.in", + f"distribute_list.{address_family}.interface.{iface_key}.prefix_list.out" + ]) + + for address_family, keys in acl_keys.items(): + for key in keys: + acl = dict_search(key, babel) + if acl: + verify_access_list(acl, babel, version='6' if address_family == 'ipv6' else '') + + for address_family, keys in prefix_list_keys.items(): + for key in keys: + prefix_list = dict_search(key, babel) + if prefix_list: + verify_prefix_list(prefix_list, babel, version='6' if address_family == 'ipv6' else '') + + +def generate(babel): + if not babel or 'deleted' in babel: + return None + + babel['new_frr_config'] = render_to_string('frr/babeld.frr.j2', babel) + return None + +def apply(babel): + babel_daemon = 'babeld' + + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + + frr_cfg.load_configuration(babel_daemon) + frr_cfg.modify_section('^router babel', stop_pattern='^exit', remove_stop_mark=True) + + for key in ['interface', 'interface_removed']: + if key not in babel: + continue + for interface in babel[key]: + frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) + + if 'new_frr_config' in babel: + frr_cfg.add_before(frr.default_add_before, babel['new_frr_config']) + frr_cfg.commit_configuration(babel_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/op_mode/generate_public_key_command.py b/src/op_mode/generate_public_key_command.py index f071ae350..8ba55c901 100755 --- a/src/op_mode/generate_public_key_command.py +++ b/src/op_mode/generate_public_key_command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -19,28 +19,51 @@ import sys import urllib.parse import vyos.remote +from vyos.template import generate_uuid4 -def get_key(path): + +def get_key(path) -> list: + """Get public keys from a local file or remote URL + + Args: + path: Path to the public keys file + + Returns: list of public keys split by new line + + """ url = urllib.parse.urlparse(path) if url.scheme == 'file' or url.scheme == '': with open(os.path.expanduser(path), 'r') as f: key_string = f.read() else: key_string = vyos.remote.get_remote_config(path) - return key_string.split() - -try: - username = sys.argv[1] - algorithm, key, identifier = get_key(sys.argv[2]) -except Exception as e: - print("Failed to retrieve the public key: {}".format(e)) - sys.exit(1) - -print('# To add this key as an embedded key, run the following commands:') -print('configure') -print(f'set system login user {username} authentication public-keys {identifier} key {key}') -print(f'set system login user {username} authentication public-keys {identifier} type {algorithm}') -print('commit') -print('save') -print('exit') + return key_string.split('\n') + + +if __name__ == "__main__": + first_loop = True + + for k in get_key(sys.argv[2]): + k = k.split() + # Skip empty list entry + if k == []: + continue + + try: + username = sys.argv[1] + # Github keys don't have identifier for example 'vyos@localhost' + # 'ssh-rsa AAAA... vyos@localhost' + # Generate uuid4 identifier + identifier = f'github@{generate_uuid4("")}' if sys.argv[2].startswith('https://github.com') else k[2] + algorithm, key = k[0], k[1] + except Exception as e: + print("Failed to retrieve the public key: {}".format(e)) + sys.exit(1) + + if first_loop: + print('# To add this key as an embedded key, run the following commands:') + print('configure') + print(f'set system login user {username} authentication public-keys {identifier} key {key}') + print(f'set system login user {username} authentication public-keys {identifier} type {algorithm}') + first_loop = False diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py index d957a1d01..79130c7c0 100755 --- a/src/op_mode/openvpn.py +++ b/src/op_mode/openvpn.py @@ -173,8 +173,8 @@ def _format_openvpn(data: dict) -> str: 'TX bytes', 'RX bytes', 'Connected Since'] out = '' - data_out = [] for intf in list(data): + data_out = [] l_host = data[intf]['local_host'] l_port = data[intf]['local_port'] for client in list(data[intf]['clients']): @@ -192,7 +192,9 @@ def _format_openvpn(data: dict) -> str: data_out.append([name, remote, tunnel, local, tx_bytes, rx_bytes, online_since]) - out += tabulate(data_out, headers) + if data_out: + out += tabulate(data_out, headers) + out += "\n" return out diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py index 91b25567a..680d9f8cc 100755 --- a/src/op_mode/restart_frr.py +++ b/src/op_mode/restart_frr.py @@ -139,7 +139,7 @@ def _reload_config(daemon): # define program arguments cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons') cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons') -cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons') +cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra', 'babeld'], required=False, nargs='*', help='select single or multiple daemons') # parse arguments cmd_args = cmd_args_parser.parse_args() |