#!/usr/bin/env python3 # Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import print_function import os import sys import glob import argparse import time import platform import subprocess import logging import datetime import json import multiprocessing import xml.etree.ElementTree as ET # TODO: # - add docker provider # https://developer.fedoraproject.org/tools/docker/docker-installation.html # - add CCACHE support # - improve building from tarball # - improve native-pkg builds # - avoid using network if possible (e.g. check first if pkgs are installed) SYSTEMS = { 'fedora': ['27', '28', '29'], 'centos': ['7'], #'rhel': ['7', '8'], 'rhel': ['8'], 'ubuntu': ['16.04', '18.04', '18.10'], 'debian': ['8', '9'], #'freebsd': ['11.0', '11.1', '11.2', '12.0'], 'freebsd': ['11.2', '12.0'], } IMAGE_TEMPLATES = { 'fedora-27-lxc': {'bare': 'lxc-fedora-27', 'kea': 'godfryd/kea-fedora-27'}, 'fedora-27-virtualbox': {'bare': 'generic/fedora27', 'kea': 'godfryd/kea-fedora-27'}, 'fedora-28-lxc': {'bare': 'lxc-fedora-28', 'kea': 'godfryd/kea-fedora-28'}, 'fedora-28-virtualbox': {'bare': 'generic/fedora28', 'kea': 'godfryd/kea-fedora-28'}, 'fedora-29-lxc': {'bare': 'godfryd/lxc-fedora-29', 'kea': 'godfryd/kea-fedora-29'}, 'fedora-29-virtualbox': {'bare': 'generic/fedora29', 'kea': 'godfryd/kea-fedora-29'}, 'centos-7-lxc': {'bare': 'godfryd/lxc-centos-7', 'kea': 'godfryd/kea-centos-7'}, 'centos-7-virtualbox': {'bare': 'generic/centos7', 'kea': 'godfryd/kea-centos-7'}, # 'rhel-7-virtualbox': {'bare': 'generic/rhel7', 'kea': 'generic/rhel7'}, # TODO: subsciption needed 'rhel-8-virtualbox': {'bare': 'generic/rhel8', 'kea': 'generic/rhel8'}, 'ubuntu-16.04-lxc': {'bare': 'godfryd/lxc-ubuntu-16.04', 'kea': 'godfryd/kea-ubuntu-16.04'}, 'ubuntu-16.04-virtualbox': {'bare': 'ubuntu/xenial64', 'kea': 'godfryd/kea-ubuntu-16.04'}, 'ubuntu-18.04-lxc': {'bare': 'godfryd/lxc-ubuntu-18.04', 'kea': 'godfryd/kea-ubuntu-18.04'}, 'ubuntu-18.04-virtualbox': {'bare': 'ubuntu/bionic64', 'kea': 'godfryd/kea-ubuntu-18.04'}, 'ubuntu-18.10-lxc': {'bare': 'godfryd/lxc-ubuntu-18.10', 'kea': 'godfryd/kea-ubuntu-18.10'}, 'ubuntu-18.10-virtualbox': {'bare': 'ubuntu/cosmic64', 'kea': 'godfryd/kea-ubuntu-18.10'}, 'debian-8-lxc': {'bare': 'godfryd/lxc-debian-8', 'kea': 'godfryd/kea-debian-8'}, 'debian-8-virtualbox': {'bare': 'debian/jessie64', 'kea': 'godfryd/kea-debian-8'}, 'debian-9-lxc': {'bare': 'godfryd/lxc-debian-9', 'kea': 'godfryd/kea-debian-9'}, 'debian-9-virtualbox': {'bare': 'debian/stretch64', 'kea': 'godfryd/kea-debian-9'}, 'freebsd-11.2-virtualbox': {'bare': 'generic/freebsd11', 'kea': 'godfryd/kea-freebsd-11.2'}, 'freebsd-12.0-virtualbox': {'bare': 'generic/freebsd12', 'kea': 'godfryd/kea-freebsd-12.0'}, } LXC_VAGRANTFILE_TPL = """# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.hostname = "{name}" config.vm.box = "{image_tpl}" config.vm.provider "lxc" do |lxc| lxc.container_name = "{name}" end config.vm.synced_folder '.', '/vagrant', disabled: true end """ VBOX_VAGRANTFILE_TPL = """# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.hostname = "{name}" config.vm.box = "{image_tpl}" config.vm.provider "virtualbox" do |v| v.name = "{name}" v.memory = 8192 nproc = Etc.nprocessors if nproc > 8 nproc -= 2 elsif nproc > 1 nproc -= 1 end v.cpus = nproc end config.vm.synced_folder '.', '/vagrant', disabled: true end """ log = logging.getLogger() def red(txt): if sys.stdout.isatty(): return '\033[1;31m%s\033[0;0m' % txt else: return txt def green(txt): if sys.stdout.isatty(): return '\033[0;32m%s\033[0;0m' % txt else: return txt def blue(txt): if sys.stdout.isatty(): return '\033[0;34m%s\033[0;0m' % txt else: return txt def get_system_revision(): system = platform.system() if system == 'Linux': system, revision, _ = platform.dist() if system == 'debian': if revision.startswith('8.'): revision = '8' elif system == 'redhat': system = 'rhel' if revision.startswith('8.'): revision = '8' elif system == 'FreeBSD': system = system.lower() revision = platform.release() return system.lower(), revision class ExecutionError(Exception): pass def execute(cmd, timeout=60, cwd=None, env=None, raise_error=True, dry_run=False, log_file_path=None, quiet=False, check_times=False): log.info('>>>>> Executing %s in %s', cmd, cwd if cwd else os.getcwd()) if not check_times: timeout = None if dry_run: return 0 if log_file_path: p = subprocess.Popen(cmd, cwd=cwd, env=env, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) with open(log_file_path, "wb") as log_file: t0 = time.time() t1 = time.time() while p.poll() is None and (timeout is None or t1 - t0 < timeout): line = p.stdout.readline() if line: if not quiet: print(line.decode(errors='ignore').strip() + '\r') log_file.write(line) t1 = time.time() if p.poll() is None: raise ExecutionError('Execution timeout') exitcode = p.returncode else: p = subprocess.Popen(cmd, cwd=cwd, env=env, shell=True) ver = platform.python_version() if ver.startswith('2'): exitcode = p.wait() else: exitcode = p.wait(timeout) if exitcode != 0 and raise_error: raise ExecutionError("The command return non-zero exitcode %s, cmd: '%s'" % (exitcode, cmd)) return exitcode def install_yum(pkgs, env=None, check_times=False): if isinstance(pkgs, list): pkgs = ' '.join(pkgs) # skip.... to detect case when one packet is not found and no error is returned cmd = 'sudo yum install -y --setopt=skip_missing_names_on_install=False %s' % pkgs execute(cmd, env=env, check_times=check_times) class VagrantEnv(object): def __init__(self, provider, system, sys_revision, features, image_template_variant, dry_run, quiet=False, check_times=False): self.provider = provider self.system = system self.sys_revision = sys_revision self.features = features self.dry_run = dry_run self.quiet = quiet self.check_times = check_times if provider == "virtualbox": vagrantfile_tpl = VBOX_VAGRANTFILE_TPL elif provider == "lxc": vagrantfile_tpl = LXC_VAGRANTFILE_TPL image_tpl = IMAGE_TEMPLATES["%s-%s-%s" % (system, sys_revision, provider)][image_template_variant] self.repo_dir = os.getcwd() self.name = "hmr-%s-%s-kea-srv" % (system, sys_revision.replace('.', '-')) vagrantfile = vagrantfile_tpl.format(image_tpl=image_tpl, name=self.name) sys_dir = "%s-%s" % (system, sys_revision) if provider == "virtualbox": self.vagrant_dir = os.path.join(self.repo_dir, 'hammer', sys_dir, 'vbox') elif provider == "lxc": self.vagrant_dir = os.path.join(self.repo_dir, 'hammer', sys_dir, 'lxc') if dry_run: return if not os.path.exists(self.vagrant_dir): os.makedirs(self.vagrant_dir) vagrantfile_path = os.path.join(self.vagrant_dir, "Vagrantfile") if os.path.exists(vagrantfile_path): # TODO: destroy any existing VM pass with open(vagrantfile_path, "w") as f: f.write(vagrantfile) def up(self): execute("vagrant box update", cwd=self.vagrant_dir, timeout=20 * 60, dry_run=self.dry_run) execute("vagrant up --no-provision --provider %s" % self.provider, cwd=self.vagrant_dir, timeout=15 * 60, dry_run=self.dry_run) def package(self): if self.provider == 'virtualbox': cmd = "vagrant package --output kea-%s-%s.box" % (self.system, self.sys_revision) execute(cmd, cwd=self.vagrant_dir, timeout=4 * 60, dry_run=self.dry_run) elif self.provider == 'lxc': execute('vagrant halt', cwd=self.vagrant_dir, dry_run=self.dry_run, raise_error=False) box_path = os.path.join(self.vagrant_dir, 'kea-%s-%s.box' % (self.system, self.sys_revision)) if os.path.exists(box_path): os.unlink(box_path) lxc_box_dir = os.path.join(self.vagrant_dir, 'lxc-box') if os.path.exists(lxc_box_dir): execute('sudo rm -rf %s' % lxc_box_dir) os.mkdir(lxc_box_dir) lxc_container_path = os.path.join('/var/lib/lxc', self.name) execute('sudo bash -c \'echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" > %s/rootfs/home/vagrant/.ssh/authorized_keys\'' % lxc_container_path) execute('sudo bash -c "cd %s && tar --numeric-owner --anchored --exclude=./rootfs/dev/log -czf %s/rootfs.tar.gz ./rootfs/*"' % (lxc_container_path, lxc_box_dir)) execute('sudo cp %s/config %s/lxc-config' % (lxc_container_path, lxc_box_dir)) execute('sudo chown `id -un`:`id -gn` *', cwd=lxc_box_dir) with open(os.path.join(lxc_box_dir, 'metadata.json'), 'w') as f: now = datetime.datetime.now() f.write('{\n') f.write(' "provider": "lxc",\n') f.write(' "version": "1.0.0",\n') f.write(' "built-on": "%s"\n' % now.strftime('%c')) f.write('}\n') execute('tar -czf %s ./*' % box_path, cwd=lxc_box_dir) execute('sudo rm -rf %s' % lxc_box_dir) def run_build_and_test(self, tarball_path): if self.dry_run: return 0, 0 if not tarball_path: name_ver = 'kea-1.5.0' cmd = 'tar --transform "flags=r;s|^|%s/|" --exclude hammer ' % name_ver cmd += ' --exclude "*~" --exclude .git --exclude .libs --exclude .deps --exclude \'*.o\' --exclude \'*.lo\' ' cmd += ' -zcf /tmp/%s.tar.gz .' % name_ver execute(cmd) tarball_path = '/tmp/%s.tar.gz' % name_ver execute('vagrant upload %s %s.tar.gz' % (tarball_path, name_ver), cwd=self.vagrant_dir) log_file_path = os.path.join(self.vagrant_dir, 'build.log') log.info('Build log file stored to %s', log_file_path) t0 = time.time() bld_cmd = "%s hammer.py build -p local -t %s.tar.gz" % (self.python, name_ver) if self.features_arg: bld_cmd += ' ' + self.features_arg if self.nofeatures_arg: bld_cmd += ' ' + self.nofeatures_arg if self.check_times: bld_cmd += ' -i' self.execute(bld_cmd, timeout=40 * 60, log_file_path=log_file_path, quiet=self.quiet) # timeout: 40 minutes ssh_cfg_path = self.dump_ssh_config() if 'native-pkg' in self.features: execute('scp -F %s -r default:/home/vagrant/rpm-root/RPMS/x86_64/ .' % ssh_cfg_path) t1 = time.time() dt = int(t1 - t0) log.info('Build log file stored to %s', log_file_path) log.info("") log.info(">>>>>> Build time %s:%s", dt // 60, dt % 60) log.info("") total = 0 passed = 0 try: if 'unittest' in self.features: execute('scp -F %s -r default:/home/vagrant/unit-test-results.json .' % ssh_cfg_path, cwd=self.vagrant_dir) results_file = os.path.join(self.vagrant_dir, 'unit-test-results.json') if os.path.exists(results_file): with open(results_file) as f: txt = f.read() results = json.loads(txt) total = results['grand_total'] passed = results['grand_passed'] except: log.exception('ignored issue with parsing unit test results') return total, passed def destroy(self, force=False): cmd = 'vagrant destroy' if force: cmd += ' --force' execute(cmd, cwd=self.vagrant_dir, timeout=3 * 60, dry_run=self.dry_run) # timeout: 3 minutes def ssh(self): execute('vagrant ssh', cwd=self.vagrant_dir, timeout=None, dry_run=self.dry_run) def dump_ssh_config(self): ssh_cfg_path = os.path.join(self.vagrant_dir, 'ssh.cfg') execute('vagrant ssh-config > %s' % ssh_cfg_path, cwd=self.vagrant_dir) return ssh_cfg_path def execute(self, cmd, timeout=None, raise_error=True, log_file_path=None, quiet=False, env=None): if not env: env = os.environ.copy() env['LANGUAGE'] = env['LANG'] = env['LC_ALL'] = 'C' return execute('vagrant ssh -c "%s"' % cmd, env=env, cwd=self.vagrant_dir, timeout=timeout, raise_error=raise_error, dry_run=self.dry_run, log_file_path=log_file_path, quiet=quiet, check_times=self.check_times) def prepare_deps(self): if self.features: self.features_arg = '--with ' + ' '.join(self.features) else: self.features_arg = '' nofeatures = set(DEFAULT_FEATURES) - self.features if nofeatures: self.nofeatures_arg = '--without ' + ' '.join(nofeatures) else: self.nofeatures_arg = '' if self.system == 'centos' and self.sys_revision == '7' or (self.system == 'debian' and self.sys_revision == '8' and self.provider != 'lxc'): self.python = 'python' elif self.system == 'freebsd': self.python = 'python3.6' else: self.python = 'python3' if self.system == 'rhel' and self.sys_revision == '8': exitcode = self.execute("sudo subscription-manager repos --list-enabled | grep rhel-8-for-x86_64-baseos-beta-rpms", raise_error=False) if exitcode != 0: env = os.environ.copy() with open(os.path.expanduser('~/rhel-creds.txt')) as f: env['RHEL_USER'] = f.readline().strip() env['RHEL_PASSWD'] = f.readline().strip() self.execute('sudo subscription-manager register --user $RHEL_USER --password "$RHEL_PASSWD"', env=env) self.execute("sudo subscription-manager refresh") self.execute("sudo subscription-manager attach --pool 8a85f99a67cdc3e70167e45c85f47429") self.execute("sudo subscription-manager repos --enable rhel-8-for-x86_64-baseos-beta-rpms") self.execute("sudo dnf install -y python36") hmr_py_path = os.path.join(self.repo_dir, 'hammer.py') execute('vagrant upload %s' % hmr_py_path, cwd=self.vagrant_dir, dry_run=self.dry_run) log_file_path = os.path.join(self.vagrant_dir, 'prepare.log') log.info('Prepare log file stored to %s', log_file_path) cmd = "{python} hammer.py prepare-deps {features} {nofeatures} {check_times}" cmd = cmd.format(features=self.features_arg, nofeatures=self.nofeatures_arg, python=self.python, check_times='-i' if self.check_times else '') self.execute(cmd, timeout=40 * 60, log_file_path=log_file_path, quiet=self.quiet) def _install_gtest_sources(): if not os.path.exists('/usr/src/googletest-release-1.8.0/googletest'): execute('wget --no-verbose -O /tmp/gtest.tar.gz https://github.com/google/googletest/archive/release-1.8.0.tar.gz') execute('sudo tar -C /usr/src -zxf /tmp/gtest.tar.gz') os.unlink('/tmp/gtest.tar.gz') def _configure_mysql(system): if system in ['fedora', 'centos']: execute('sudo systemctl enable mariadb.service') execute('sudo systemctl start mariadb.service') time.sleep(5) cmd = "echo 'DROP DATABASE IF EXISTS keatest;' | sudo mysql -u root" execute(cmd) cmd = "echo 'DROP USER 'keatest'@'localhost';' | sudo mysql -u root" execute(cmd, raise_error=False) cmd = "echo 'DROP USER 'keatest_readonly'@'localhost';' | sudo mysql -u root" execute(cmd, raise_error=False) cmd = "bash -c \"cat < 0 or grand_total == 0: result = red(result) else: result = green(result) log.info('Unit test results: %s', result) with open('unit-test-results.json', 'w') as f: f.write(json.dumps(results)) if 'install' in features: execute('sudo make install', cwd=src_path, env=env, check_times=check_times) execute('df -h') def build_in_vagrant(provider, system, sys_revision, features, leave_system, tarball_path, dry_run, quiet, clean_start, check_times): log.info('') log.info(">>> Building %s, %s, %s" % (provider, system, sys_revision)) log.info('') t0 = time.time() error = None total = 0 passed = 0 try: ve = VagrantEnv(provider, system, sys_revision, features, 'kea', dry_run, quiet, check_times) if clean_start: ve.destroy(force=True) ve.up() ve.prepare_deps() total, passed = ve.run_build_and_test(tarball_path) msg = ' - ' + green('all ok') except KeyboardInterrupt as e: error = e msg = ' - keyboard interrupt' except ExecutionError as e: error = e msg = ' - ' + red(str(e)) except Exception as e: log.exception('Building erred') error = e msg = ' - ' + red(str(e)) finally: if not leave_system: ve.destroy(force=True) t1 = time.time() dt = int(t1 - t0) log.info('') log.info(">>> Building %s, %s, %s completed in %s:%s%s", provider, system, sys_revision, dt // 60, dt % 60, msg) log.info('') return dt, error, total, passed def package_box(provider, system, sys_revision, features, dry_run, check_times): ve = VagrantEnv(provider, system, sys_revision, features, 'bare', dry_run, check_times=check_times) ve.destroy(force=True) ve.up() ve.prepare_deps() # TODO cleanup ve.package() def prepare_system(provider, system, sys_revision, features, dry_run, check_times, clean_start): ve = VagrantEnv(provider, system, sys_revision, features, 'kea', dry_run, check_times=check_times) if clean_start: ve.destroy(force=True) ve.up() ve.prepare_deps() def ssh(provider, system, sys_revision, features, dry_run): ve = VagrantEnv(provider, system, sys_revision, features, 'kea', dry_run) ve.up() ve.ssh() def ensure_hammer_deps(): distro, _ = get_system_revision() exitcode = execute('vagrant version', raise_error=False) if exitcode != 0: if distro in ['fedora', 'centos', 'rhel']: execute('wget --no-verbose -O /tmp/vagrant_2.2.2_x86_64.rpm https://releases.hashicorp.com/vagrant/2.2.2/vagrant_2.2.2_x86_64.rpm') execute('sudo rpm -i /tmp/vagrant_2.2.2_x86_64.rpm') os.unlink('/tmp/vagrant_2.2.2_x86_64.rpm') elif distro in ['debian', 'ubuntu']: execute('wget --no-verbose -O /tmp/vagrant_2.2.2_x86_64.deb https://releases.hashicorp.com/vagrant/2.2.2/vagrant_2.2.2_x86_64.deb') execute('sudo dpkg -i /tmp/vagrant_2.2.2_x86_64.deb') os.unlink('/tmp/vagrant_2.2.2_x86_64.deb') else: # TODO: check for packages here: https://www.vagrantup.com/downloads.html raise NotImplementedError exitcode = execute('vagrant plugin list | grep vagrant-lxc', raise_error=False) if exitcode != 0: execute('vagrant plugin install vagrant-lxc') DEFAULT_FEATURES = ['install', 'unittest', 'docs'] ALL_FEATURES = ['install', 'unittest', 'docs', 'mysql', 'pgsql', 'cql', 'native-pkg', 'radius'] def parse_args(): parser = argparse.ArgumentParser(description='Kea develepment environment management tool.') parser.add_argument('command', choices=['package-box', 'prepare-system', 'build', 'prepare-deps', 'list-systems', 'ssh', 'ensure-hammer-deps'], help='Commands.') parser.add_argument('-p', '--provider', default='virtualbox', choices=['lxc', 'virtualbox', 'all', 'local'], help="Backend build executor. If 'all' then build is executed several times on all providers. " "If 'local' then build is executed on current system. Default is 'virtualbox'.") parser.add_argument('-s', '--system', default='all', choices=list(SYSTEMS.keys()) + ['all'], help="Build is executed on selected system. If 'all' then build is executed several times on all systems. " "If provider is 'local' then this option is ignored. Default is 'all'.") parser.add_argument('-r', '--revision', default='all', help="Revision of selected system. If 'all' then build is executed several times " "on all revisions of selected system. To list supported systems and their revisions invoke 'list-systems'. " "Default is 'all'.") parser.add_argument('-w', '--with', nargs='+', default=set(), choices=ALL_FEATURES, help="Enabled, comma-separated features. Default is '%s'." % ' '.join(DEFAULT_FEATURES)) parser.add_argument('-x', '--without', nargs='+', default=set(), choices=ALL_FEATURES, help="Disabled, comma-separated features. Default is ''.") parser.add_argument('-l', '--leave-system', action='store_true', help='At the end of the command do not destroy vagrant system. Default behavior is destroing the system.') parser.add_argument('-t', '--from-tarball', help='Instead of building sources in current folder use provided tarball package (e.g. tar.gz).') parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose mode.') parser.add_argument('-q', '--quiet', action='store_true', help='Enable quiet mode.') parser.add_argument('-n', '--dry-run', action='store_true', help='Print only what would be done.') parser.add_argument('-c', '--clean-start', action='store_true', help='If there is pre-existing system then it is destroyed first.') parser.add_argument('-i', '--check-times', action='store_true', help='Do not allow executing commands infinitelly.') args = parser.parse_args() return args def list_systems(): for system, revisions in SYSTEMS.items(): print('%s:' % system) for r in revisions: providers = [] for p in ['lxc', 'virtualbox']: k = '%s-%s-%s' % (system, r, p) if k in IMAGE_TEMPLATES: providers.append(p) providers = ', '.join(providers) print(' - %s: %s' % (r, providers)) def _what_features(args): features = set(vars(args)['with']) features = features.union(DEFAULT_FEATURES) nofeatures = set(args.without) features = features.difference(nofeatures) return features def _print_summary(results, features): print("") print("+===== Hammer Summary ====================================================+") print("| provider | system | revision | duration | status | unit tests |") print("+------------+------------+----------+-----------+---------+--------------+") total_dt = 0 for key, result in results.items(): provider, system, revision = key dt, error, ut_total, ut_passed = result total_dt += dt if error is None: status = ' %s' % green('ok') elif error == 'not run': status = blue('not run') else: status = ' %s' % red('error') if 'unittest' in features: ut_results = '%s/%s' % (ut_passed, ut_total) padding = ' ' * (12 - len(ut_results)) if ut_passed < ut_total or ut_total == 0: ut_results = padding + red(ut_results) else: ut_results = padding + green(ut_results) else: ut_results = ' not planned' print('| %10s | %10s | %8s | %6d:%02d | %s | %s |' % (provider, system, revision, dt // 60, dt % 60, status, ut_results)) print("+------------+------------+----------+-----------+---------+--------------+") print("| Total: %6d:%02d | |" % (total_dt // 60, total_dt % 60)) print("+=========================================================================+") def main(): args = parse_args() level = logging.INFO if args.verbose: level = logging.DEBUG format = '[HAMMER] %(asctime)-15s %(message)s' logging.basicConfig(format=format, level=level) features = _what_features(args) if args.command == 'list-systems': list_systems() elif args.command == "package-box": log.info('Enabled features: %s', ' '.join(features)) package_box(args.provider, args.system, args.revision, features, args.dry_run, args.check_times) elif args.command == "prepare-system": log.info('Enabled features: %s', ' '.join(features)) prepare_system(args.provider, args.system, args.revision, features, args.dry_run, args.check_times, args.clean_start) elif args.command == "build": log.info('Enabled features: %s', ' '.join(features)) if args.provider == 'local': build_local(features, args.from_tarball, args.check_times) return if args.provider == 'all': providers = ['lxc', 'virtualbox'] else: providers = [args.provider] if args.system == 'all': systems = SYSTEMS.keys() else: systems = [args.system] plan = [] results = {} log.info('Build plan:') for provider in providers: for system in systems: if args.revision == 'all': revisions = SYSTEMS[system] else: revisions = [args.revision] for revision in revisions: if args.revision == 'all': key = '%s-%s-%s' % (system, revision, provider) if key not in IMAGE_TEMPLATES: continue plan.append((provider, system, revision)) log.info(' - %s, %s, %s', provider, system, revision) results[(provider, system, revision)] = (0, 'not run') fail = False for provider, system, revision in plan: duration, error, total, passed = build_in_vagrant(provider, system, revision, features, args.leave_system, args.from_tarball, args.dry_run, args.quiet, args.clean_start, args.check_times) results[(provider, system, revision)] = (duration, error, total, passed) if error: fail = True if isinstance(error, KeyboardInterrupt): break _print_summary(results, features) if fail: sys.exit(1) elif args.command == "prepare-deps": log.info('Enabled features: %s', ' '.join(features)) prepare_deps_local(features, args.check_times) elif args.command == "ssh": ssh(args.provider, args.system, args.revision, features, args.dry_run) elif args.command == "ensure-hammer-deps": ensure_hammer_deps() if __name__ == '__main__': # results = { # ('virtualbox', 'centos', '7'): (920, False), # ('lxc', 'fedora', '29'): (120, False), # } # _print_summary(results) main()