diff options
Diffstat (limited to 'src/helpers')
-rwxr-xr-x | src/helpers/run-config-migration.py | 86 | ||||
-rwxr-xr-x | src/helpers/system-versions-foot.py | 39 | ||||
-rwxr-xr-x | src/helpers/vyos-boot-config-loader.py | 178 | ||||
-rwxr-xr-x | src/helpers/vyos-bridge-sync.py | 51 | ||||
-rwxr-xr-x | src/helpers/vyos-load-config.py | 90 | ||||
-rwxr-xr-x | src/helpers/vyos-merge-config.py | 111 | ||||
-rwxr-xr-x | src/helpers/vyos-sudo.py | 33 |
7 files changed, 588 insertions, 0 deletions
diff --git a/src/helpers/run-config-migration.py b/src/helpers/run-config-migration.py new file mode 100755 index 000000000..cc7166c22 --- /dev/null +++ b/src/helpers/run-config-migration.py @@ -0,0 +1,86 @@ +#!/usr/bin/python3 + +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys +import argparse +import datetime + +from vyos.util import cmd +from vyos.migrator import Migrator, VirtualMigrator + +def main(): + argparser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter) + argparser.add_argument('config_file', type=str, + help="configuration file to migrate") + argparser.add_argument('--force', action='store_true', + help="Force calling of all migration scripts.") + argparser.add_argument('--set-vintage', type=str, + choices=['vyatta', 'vyos'], + help="Set the format for the config version footer in config" + " file:\n" + "set to 'vyatta':\n" + "(for '/* === vyatta-config-version ... */' format)\n" + "or 'vyos':\n" + "(for '// vyos-config-version ...' format).") + argparser.add_argument('--virtual', action='store_true', + help="Update the format of the trailing comments in" + " config file,\nfrom 'vyatta' to 'vyos'; no migration" + " scripts are run.") + args = argparser.parse_args() + + config_file_name = args.config_file + force_on = args.force + vintage = args.set_vintage + virtual = args.virtual + + if not os.access(config_file_name, os.R_OK): + print("Read error: {}.".format(config_file_name)) + sys.exit(1) + + if not os.access(config_file_name, os.W_OK): + print("Write error: {}.".format(config_file_name)) + sys.exit(1) + + separator = "." + backup_file_name = separator.join([config_file_name, + '{0:%Y-%m-%d-%H%M%S}'.format(datetime.datetime.now()), + 'pre-migration']) + + cmd(f'cp -p {config_file_name} {backup_file_name}') + + if not virtual: + virtual_migration = VirtualMigrator(config_file_name) + virtual_migration.run() + + migration = Migrator(config_file_name, force=force_on) + migration.run() + + if not migration.config_changed(): + os.remove(backup_file_name) + else: + virtual_migration = VirtualMigrator(config_file_name, + set_vintage=vintage) + + virtual_migration.run() + + if not virtual_migration.config_changed(): + os.remove(backup_file_name) + +if __name__ == '__main__': + main() diff --git a/src/helpers/system-versions-foot.py b/src/helpers/system-versions-foot.py new file mode 100755 index 000000000..c33e41d79 --- /dev/null +++ b/src/helpers/system-versions-foot.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 + +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +import sys +import vyos.formatversions as formatversions +import vyos.systemversions as systemversions +import vyos.defaults +import vyos.version + +sys_versions = systemversions.get_system_versions() + +component_string = formatversions.format_versions_string(sys_versions) + +os_version_string = vyos.version.get_version() + +sys.stdout.write("\n\n") +if vyos.defaults.cfg_vintage == 'vyos': + formatversions.write_vyos_versions_foot(None, component_string, + os_version_string) +elif vyos.defaults.cfg_vintage == 'vyatta': + formatversions.write_vyatta_versions_foot(None, component_string, + os_version_string) +else: + formatversions.write_vyatta_versions_foot(None, component_string, + os_version_string) diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py new file mode 100755 index 000000000..c5bf22f10 --- /dev/null +++ b/src/helpers/vyos-boot-config-loader.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 +import sys +import pwd +import grp +import traceback +from datetime import datetime + +from vyos.defaults import directories +from vyos.configsession import ConfigSession, ConfigSessionError +from vyos.configtree import ConfigTree +from vyos.util import cmd + +STATUS_FILE = '/tmp/vyos-config-status' +TRACE_FILE = '/tmp/boot-config-trace' + +CFG_GROUP = 'vyattacfg' + +trace_config = False + +if 'log' in directories: + LOG_DIR = directories['log'] +else: + LOG_DIR = '/var/log/vyatta' + +LOG_FILE = LOG_DIR + '/vyos-boot-config-loader.log' + +try: + with open('/proc/cmdline', 'r') as f: + cmdline = f.read() + if 'vyos-debug' in cmdline: + os.environ['VYOS_DEBUG'] = 'yes' + if 'vyos-config-debug' in cmdline: + os.environ['VYOS_DEBUG'] = 'yes' + trace_config = True +except Exception as e: + print('{0}'.format(e)) + +def write_config_status(status): + try: + with open(STATUS_FILE, 'w') as f: + f.write('{0}\n'.format(status)) + except Exception as e: + print('{0}'.format(e)) + +def trace_to_file(trace_file_name): + try: + with open(trace_file_name, 'w') as trace_file: + traceback.print_exc(file=trace_file) + except Exception as e: + print('{0}'.format(e)) + +def failsafe(config_file_name): + fail_msg = """ + !!!!! + There were errors loading the configuration + Please examine the errors in + {0} + and correct + !!!!! + """.format(TRACE_FILE) + + print(fail_msg, file=sys.stderr) + + users = [x[0] for x in pwd.getpwall()] + if 'vyos' in users: + return + + try: + with open(config_file_name, 'r') as f: + config_file = f.read() + except Exception as e: + print("Catastrophic: no default config file " + "'{0}'".format(config_file_name)) + sys.exit(1) + + config = ConfigTree(config_file) + if not config.exists(['system', 'login', 'user', 'vyos', + 'authentication', 'encrypted-password']): + print("No password entry in default config file;") + print("unable to recover password for user 'vyos'.") + sys.exit(1) + else: + passwd = config.return_value(['system', 'login', 'user', 'vyos', + 'authentication', + 'encrypted-password']) + + cmd(f"useradd -s /bin/bash -G 'users,sudo' -m -N -p '{passwd}' vyos") + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Must specify boot config file.") + sys.exit(1) + else: + file_name = sys.argv[1] + + # Set user and group options, so that others will be able to commit + # Currently, the only caller does 'sg CFG_GROUP', but that may change + cfg_group = grp.getgrnam(CFG_GROUP) + os.setgid(cfg_group.gr_gid) + + # Need to set file permissions to 775 so that every vyattacfg group + # member has write access to the running config + os.umask(0o002) + + session = ConfigSession(os.getpid(), 'vyos-boot-config-loader') + env = session.get_session_env() + + default_file_name = env['vyatta_sysconfdir'] + '/config.boot.default' + + try: + with open(file_name, 'r') as f: + config_file = f.read() + except Exception: + write_config_status(1) + if trace_config: + failsafe(default_file_name) + trace_to_file(TRACE_FILE) + sys.exit(1) + + try: + time_begin_load = datetime.now() + load_out = session.load_config(file_name) + time_end_load = datetime.now() + time_begin_commit = datetime.now() + commit_out = session.commit() + time_end_commit = datetime.now() + write_config_status(0) + except ConfigSessionError: + # If here, there is no use doing session.discard, as we have no + # recoverable config environment, and will only throw an error + write_config_status(1) + if trace_config: + failsafe(default_file_name) + trace_to_file(TRACE_FILE) + sys.exit(1) + + time_elapsed_load = time_end_load - time_begin_load + time_elapsed_commit = time_end_commit - time_begin_commit + + try: + if not os.path.exists(LOG_DIR): + os.mkdir(LOG_DIR) + with open(LOG_FILE, 'a') as f: + f.write('\n\n') + f.write('{0} Begin config load\n' + ''.format(time_begin_load)) + f.write(load_out) + f.write('{0} End config load\n' + ''.format(time_end_load)) + f.write('Elapsed time for config load: {0}\n' + ''.format(time_elapsed_load)) + f.write('{0} Begin config commit\n' + ''.format(time_begin_commit)) + f.write(commit_out) + f.write('{0} End config commit\n' + ''.format(time_end_commit)) + f.write('Elapsed time for config commit: {0}\n' + ''.format(time_elapsed_commit)) + except Exception as e: + print('{0}'.format(e)) diff --git a/src/helpers/vyos-bridge-sync.py b/src/helpers/vyos-bridge-sync.py new file mode 100755 index 000000000..097d28d85 --- /dev/null +++ b/src/helpers/vyos-bridge-sync.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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/>. +# + +# Script is used to synchronize configured bridge interfaces. +# one can add a non existing interface to a bridge group (e.g. VLAN) +# but the vlan interface itself does yet not exist. It should be added +# to the bridge automatically once it's available + +import argparse +from sys import exit +from time import sleep + +from vyos.config import Config +from vyos.util import cmd, run + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--interface', action='store', help='Interface name which should be added to bridge it is configured for', required=True) + args, unknownargs = parser.parse_known_args() + + conf = Config() + if not conf.list_nodes('interfaces bridge'): + # no bridge interfaces exist .. bail out early + exit(0) + else: + for bridge in conf.list_nodes('interfaces bridge'): + for member_if in conf.list_nodes('interfaces bridge {} member interface'.format(bridge)): + if args.interface == member_if: + command = 'brctl addif "{}" "{}"'.format(bridge, args.interface) + # let interfaces etc. settle - especially required for OpenVPN bridged interfaces + sleep(4) + # XXX: This is ignoring any issue, should be cmd but kept as it + # XXX: during the migration to not cause any regression + run(command) + + exit(0) diff --git a/src/helpers/vyos-load-config.py b/src/helpers/vyos-load-config.py new file mode 100755 index 000000000..c2da1bb11 --- /dev/null +++ b/src/helpers/vyos-load-config.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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/>. +# +# + +"""Load config file from within config session. +Config file specified by URI or path (without scheme prefix). +Example: load https://somewhere.net/some.config + or + load /tmp/some.config +""" + +import sys +import tempfile +import vyos.defaults +import vyos.remote +from vyos.configsource import ConfigSourceSession, VyOSError +from vyos.migrator import Migrator, VirtualMigrator, MigratorError + +class LoadConfig(ConfigSourceSession): + """A subclass for calling 'loadFile'. + This does not belong in configsource.py, and only has a single caller. + """ + def load_config(self, path): + return self._run(['/bin/cli-shell-api','loadFile',path]) + + +file_name = sys.argv[1] if len(sys.argv) > 1 else 'config.boot' +configdir = vyos.defaults.directories['config'] +protocols = ['scp', 'sftp', 'http', 'https', 'ftp', 'tftp'] + + +if any(x in file_name for x in protocols): + config_file = vyos.remote.get_remote_config(file_name) + if not config_file: + sys.exit("No config file by that name.") +else: + canonical_path = '{0}/{1}'.format(configdir, file_name) + try: + with open(canonical_path, 'r') as f: + config_file = f.read() + except OSError as err1: + try: + with open(file_name, 'r') as f: + config_file = f.read() + except OSError as err2: + sys.exit('{0}\n{1}'.format(err1, err2)) + +config = LoadConfig() + +print("Loading configuration from '{}'".format(file_name)) + +with tempfile.NamedTemporaryFile() as fp: + with open(fp.name, 'w') as fd: + fd.write(config_file) + + virtual_migration = VirtualMigrator(fp.name) + try: + virtual_migration.run() + except MigratorError as err: + sys.exit('{}'.format(err)) + + migration = Migrator(fp.name) + try: + migration.run() + except MigratorError as err: + sys.exit('{}'.format(err)) + + try: + config.load_config(fp.name) + except VyOSError as err: + sys.exit('{}'.format(err)) + +if config.session_changed(): + print("Load complete. Use 'commit' to make changes effective.") +else: + print("No configuration changes to commit.") diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py new file mode 100755 index 000000000..14df2734b --- /dev/null +++ b/src/helpers/vyos-merge-config.py @@ -0,0 +1,111 @@ +#!/usr/bin/python3 + +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +import sys +import os +import tempfile +import vyos.defaults +import vyos.remote +from vyos.config import Config +from vyos.configtree import ConfigTree +from vyos.migrator import Migrator, VirtualMigrator +from vyos.util import cmd, DEVNULL + + +if (len(sys.argv) < 2): + print("Need config file name to merge.") + print("Usage: merge <config file> [config path]") + sys.exit(0) + +file_name = sys.argv[1] + +configdir = vyos.defaults.directories['config'] + +protocols = ['scp', 'sftp', 'http', 'https', 'ftp', 'tftp'] + +if any(x in file_name for x in protocols): + config_file = vyos.remote.get_remote_config(file_name) + if not config_file: + sys.exit("No config file by that name.") +else: + canonical_path = "{0}/{1}".format(configdir, file_name) + first_err = None + try: + with open(canonical_path, 'r') as f: + config_file = f.read() + except Exception as err: + first_err = err + try: + with open(file_name, 'r') as f: + config_file = f.read() + except Exception as err: + print(first_err) + print(err) + sys.exit(1) + +with tempfile.NamedTemporaryFile() as file_to_migrate: + with open(file_to_migrate.name, 'w') as fd: + fd.write(config_file) + + virtual_migration = VirtualMigrator(file_to_migrate.name) + virtual_migration.run() + + migration = Migrator(file_to_migrate.name) + migration.run() + + if virtual_migration.config_changed() or migration.config_changed(): + with open(file_to_migrate.name, 'r') as fd: + config_file = fd.read() + +merge_config_tree = ConfigTree(config_file) + +effective_config = Config() +effective_config_tree = effective_config._running_config + +effective_cmds = effective_config_tree.to_commands() +merge_cmds = merge_config_tree.to_commands() + +effective_cmd_list = effective_cmds.splitlines() +merge_cmd_list = merge_cmds.splitlines() + +effective_cmd_set = set(effective_cmd_list) +add_cmds = [ cmd for cmd in merge_cmd_list if cmd not in effective_cmd_set ] + +path = None +if (len(sys.argv) > 2): + path = sys.argv[2:] + if (not effective_config_tree.exists(path) and not + merge_config_tree.exists(path)): + print("path {} does not exist in either effective or merge" + " config; will use root.".format(path)) + path = None + else: + path = " ".join(path) + +if path: + add_cmds = [ cmd for cmd in add_cmds if path in cmd ] + +for add in add_cmds: + try: + cmd(f'/opt/vyatta/sbin/my_{add}', shell=True, stderr=DEVNULL) + except OSError as err: + print(err) + +if effective_config.session_changed(): + print("Merge complete. Use 'commit' to make changes effective.") +else: + print("No configuration changes to commit.") diff --git a/src/helpers/vyos-sudo.py b/src/helpers/vyos-sudo.py new file mode 100755 index 000000000..3e4c196d9 --- /dev/null +++ b/src/helpers/vyos-sudo.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys + +from vyos.util import is_admin + + +if __name__ == '__main__': + if len(sys.argv) < 2: + print('Missing command argument') + sys.exit(1) + + if not is_admin(): + print('This account is not authorized to run this command') + sys.exit(1) + + os.execvp('sudo', ['sudo'] + sys.argv[1:]) |