#!/usr/bin/env python3 # This file is part of Cockpit. # # Copyright (C) 2015 Red Hat, Inc. # # Cockpit 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. # # Cockpit 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 Cockpit; If not, see . import argparse import os import re import sys import subprocess BOTS = os.path.abspath(os.path.dirname(__file__)) BASE = os.path.normpath(os.path.join(BOTS, "..")) TEST = os.path.join(BASE, "test") os.environ["PATH"] = "{0}:{1}".format(os.environ.get("PATH"), BOTS) from machine import testvm parser = argparse.ArgumentParser( description='Run command inside or install packages into a Cockpit virtual machine', formatter_class=argparse.ArgumentDefaultsHelpFormatter ) parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose progress details') parser.add_argument('-i', '--install', action='append', dest="packagelist", default=[], help='Install packages') parser.add_argument('-I', '--install-command', action='store', dest="installcommand", default="yum --setopt=skip_missing_names_on_install=False -y install", help="Command used to install packages in machine") parser.add_argument('-r', '--run-command', action='append', dest="commandlist", default=[], help='Run command inside virtual machine') parser.add_argument('-s', '--script', action='append', dest="scriptlist", default=[], help='Run selected script inside virtual machine') parser.add_argument('-u', '--upload', action='append', dest="uploadlist", default=[], help='Upload file/dir to destination file/dir separated by ":" example: -u file.txt:/var/lib') parser.add_argument('--base-image', help='Base image name, if "image" does not match a standard Cockpit VM image name') parser.add_argument('--resize', help="Resize the image. Size in bytes with using K, M, or G suffix.") parser.add_argument('image', help='The image to use (destination name when using --base-image)') args = parser.parse_args() if not args.base_image: args.base_image = os.path.basename(args.image) args.base_image = testvm.get_test_image(args.base_image) # Create the necessary layered image for the build/install def prepare_install_image(base_image, install_image): if "/" not in base_image: base_image = os.path.join(testvm.IMAGES_DIR, base_image) if "/" not in install_image: install_image = os.path.join(os.path.join(TEST, "images"), os.path.basename(install_image)) # In vm-customize we don't force recreate images if not os.path.exists(install_image): install_image_dir = os.path.dirname(install_image) os.makedirs(install_image_dir, exist_ok=True) base_image = os.path.realpath(base_image) qcow2_image = "{0}.qcow2".format(install_image) subprocess.check_call([ "qemu-img", "create", "-q", "-f", "qcow2", "-o", "backing_file={0},backing_fmt=qcow2".format(base_image), qcow2_image ]) if os.path.lexists(install_image): os.unlink(install_image) os.symlink(os.path.basename(qcow2_image), install_image) if args.resize: subprocess.check_call(["qemu-img", "resize", install_image, args.resize]) return install_image def run_command(machine_instance, commandlist): """Run command inside image""" for foo in commandlist: try: machine_instance.execute(foo, timeout=1800) except subprocess.CalledProcessError as e: sys.stderr.write("%s\n" % e) sys.exit(e.returncode) def run_script(machine_instance, scriptlist): """Run script inside image""" for foo in scriptlist: if os.path.isfile(foo): pname = os.path.basename(foo) uploadpath = "/var/tmp/" + pname machine_instance.upload([os.path.abspath(foo)], uploadpath) machine_instance.execute("chmod a+x %s" % uploadpath) try: machine_instance.execute(uploadpath, timeout=1800) except subprocess.CalledProcessError as e: sys.stderr.write("%s\n" % e) sys.exit(e.returncode) else: sys.stderr.write("Bad path to script: %s\n" % foo) def upload_files(machine_instance, uploadfiles): """Upload files/directories inside image""" for foo in uploadfiles: srcfile, dest = foo.split(":") src_absolute = os.path.join(os.getcwd(), srcfile) machine_instance.upload([src_absolute], dest) def install_packages(machine_instance, packagelist, install_command): """Install packages into a test image It could be done via local rpms or normal package installation """ allpackages = [] for foo in packagelist: if os.path.isfile(foo): pname = os.path.basename(foo) machine_instance.upload([foo], "/var/tmp/" + pname) allpackages.append("/var/tmp/" + pname) elif not re.search("/", foo): allpackages.append(foo) else: sys.stderr.write("Bad package name or path: %s\n" % foo) if allpackages: machine_instance.execute(install_command + " " + ' '.join(allpackages), timeout=1800) if args.commandlist or args.packagelist or args.scriptlist or args.uploadlist or args.resize: if '/' not in args.base_image: subprocess.check_call(["image-download", args.base_image]) machine = testvm.VirtMachine(maintain=True, verbose=args.verbose, image=prepare_install_image(args.base_image, args.image)) machine.start() machine.wait_boot() try: if args.uploadlist: upload_files(machine, args.uploadlist) if args.commandlist: run_command(machine, args.commandlist) if args.packagelist: install_packages(machine, args.packagelist, args.installcommand) if args.scriptlist: run_script(machine, args.scriptlist) finally: machine.stop()