From f249a884b0351c5bc6ed6af9b9985fe17a022e90 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 3 May 2020 11:49:43 +0300 Subject: Change: add script to run manager VM directly via Qemu --- README.md | 10 +++++++++- contractor | 62 +++++++++++++++++++++++++++++++++++++------------------------- vm-qemu.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 26 deletions(-) create mode 100755 vm-qemu.sh diff --git a/README.md b/README.md index 7ff9c98..734f9a8 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,18 @@ the [vmdb2][] tool installed. `sudo ./vm.sh` -* Create the outer, manager VM: +* Create the outer, manager VM, using libvirt: `./vm-libvirt.sh` +* Alternatively, create it using qemu only (this will run until you + shut down the VM): + + `./vm-qemu.sh vm.img 7777` + + You need to specify `--manager-address` (or `-m`) and + `--manager-port` (or `-p) when using contractor. + ## Try the Contractor diff --git a/contractor b/contractor index e8d0a3d..a879417 100755 --- a/contractor +++ b/contractor @@ -41,6 +41,12 @@ class ContractorApplication(cliapp.Application): 'address of the manager VM', metavar='ADDR') + self.settings.integer( + ['manager-port', 'p'], + 'SSH port of the manager VM', + default=22, + metavar='ADDR') + self.settings.string( ['manager-user'], 'user of the manager in the manager VM', @@ -59,13 +65,13 @@ class ContractorApplication(cliapp.Application): def cmd_build(self, args): self.verbose('building according to {}'.format(args[0])) bs = self.load_build_spec(args[0]) - dest = self.manager_destination() - manager = RemoteServer(dest, verbose=self.verbose) - self.verbose('manager is at {}'.format(dest)) + dest, port = self.manager_destination() + manager = RemoteServer(dest, port, verbose=self.verbose) + self.verbose('manager is at {} (port {})'.format(dest, port)) with Timer(self.verbose, 'complete-run'): with Timer(self.verbose, 'upload-worker-image'): - self.upload_worker_image(bs.worker_image(), dest) + self.upload_worker_image(bs.worker_image(), dest, port) # Do the minimum needed to start worker VM. The VM takes a # while to boot and we can do other things while that @@ -86,14 +92,14 @@ class ContractorApplication(cliapp.Application): with Timer(self.verbose, 'upload-saved-workspace'): ws = bs.workspace() if os.path.exists(ws): - self.sync_to_workspace(ws, dest, '.') + self.sync_to_workspace(ws, dest, port, '.') with Timer(self.verbose, 'upload-source'): self.exec_quietly( manager, Mkdir( '/mnt/src', owner=WORKER_UID, group=WORKER_GID)) src = bs.source() - self.sync_to_workspace(src, dest, 'src') + self.sync_to_workspace(src, dest, port, 'src') with Timer(self.verbose, 'wait-for-worker-to-be-available'): execs = [ @@ -108,7 +114,7 @@ class ContractorApplication(cliapp.Application): worker_dest = 'worker@{}'.format(worker_ip) self.verbose( 'worker is at {} (via manager)'.format(worker_dest)) - worker = OnWorker(dest, worker_dest, verbose=self.verbose) + worker = OnWorker(dest, port, worker_dest, verbose=self.verbose) self.exec_quietly( worker, Mkdir('/workspace'), MountWSonWorker()) @@ -134,7 +140,7 @@ class ContractorApplication(cliapp.Application): with Timer(self.verbose, 'save-workspace'): if ws: self.verbose('saving workspace to {}'.format(ws)) - self.sync_from_workspace(dest, ws) + self.sync_from_workspace(dest, port, ws) self.verbose('build finished OK') @@ -142,28 +148,30 @@ class ContractorApplication(cliapp.Application): with open(filename) as f: return BuildSpec(f.read()) - def upload_worker_image(self, filename, dest): + def upload_worker_image(self, filename, dest, port): self.verbose( 'uploading to manager local worker image {}'.format(filename)) target = '{}:{}'.format(dest, WORKER_IMG) - if rsync(filename, target).failed(): + if rsync(filename, target, port).failed(): self.error('could not upload image to worker') sys.exit(1) - def sync_to_workspace(self, frm, dest, subdir): + def sync_to_workspace(self, frm, dest, port, subdir): destdir = '/mnt/{}'.format(subdir) self.verbose('syncing local {} to manager {}'.format(frm, destdir)) er = rsync( - '{}/.'.format(frm), '{}:{}/.'.format(dest, destdir)) + '{}/.'.format(frm), + '{}:{}/.'.format(dest, destdir), + port) if er.failed(): self.error('Failed to rsync saved workspace to worker') sys.exit(1) - def sync_from_workspace(self, dest, ws): + def sync_from_workspace(self, dest, port, ws): self.verbose('syncing manager /mnt to local {}'.format(ws)) if not os.path.exists(ws): os.makedirs(ws) - er = rsync('{}:/mnt/.'.format(dest), '{}/.'.format(ws)) + er = rsync('{}:/mnt/.'.format(dest), '{}/.'.format(ws), port) if er.failed(): self.error('Failed to rsync workspace from worker') sys.exit(1) @@ -182,8 +190,8 @@ class ContractorApplication(cliapp.Application): return self.exec_sequence(manager.verbosely, *execs) def cmd_manager_status(self, args): - dest = self.manager_destination() - manager = RemoteServer(dest) + dest, port = self.manager_destination() + manager = RemoteServer(dest, port) if manager.quietly(TrueCmd()).failed(): self.error('Manager VM is NOT available') sys.exit(1) @@ -192,7 +200,8 @@ class ContractorApplication(cliapp.Application): def manager_destination(self): user = self.settings['manager-user'] addr = self.settings['manager-address'] - return '{}@{}'.format(user, addr) + port = self.settings['manager-port'] + return '{}@{}'.format(user, addr), port def error(self, msg): sys.stderr.write('ERROR: {}\n'.format(msg)) @@ -216,8 +225,9 @@ class ExecResult: return self.exit_code != 0 -def ssh(target, argv, quiet=False): - argv = ['ssh', '--', target] + [shlex.quote(arg) for arg in argv] +def ssh(target, port, argv, quiet=False): + ssh_argv = ['ssh', '-p', str(port), '--', target] + argv = ssh_argv + [shlex.quote(arg) for arg in argv] logging.info('SSH: {!r}'.format(argv)) stdout = None if quiet: @@ -230,9 +240,9 @@ def ssh(target, argv, quiet=False): return ExecResult(out, err, p.returncode) -def rsync(filename, target): +def rsync(filename, target, port): argv = [ - 'rsync', '-aHSs', '--delete', '--exclude=lost+found', '--', + 'rsync', '-essh -p{}'.format(port), '-aHSs', '--delete', '--exclude=lost+found', '--', filename, target, ] logging.info('RSYNC: {!r}'.format(argv)) @@ -243,8 +253,9 @@ def rsync(filename, target): class RemoteServer: - def __init__(self, ssh_destination, verbose=None): + def __init__(self, ssh_destination, ssh_port, verbose=None): self._dest = ssh_destination + self._port = ssh_port self._where = 'manager: {}'.format(ssh_destination) self._verbose = verbose @@ -268,13 +279,14 @@ class RemoteServer: def _execute(self, *execs, quiet=None): assert quiet is not None - return ssh(self._dest, self._argv(execs), quiet=quiet) + return ssh(self._dest, self._port, self._argv(execs), quiet=quiet) class OnWorker(RemoteServer): - def __init__(self, manager, worker, verbose=None): + def __init__(self, manager, ssh_port, worker, verbose=None): self._dest = manager + self._port = ssh_port self._where = 'worker: {}'.format(worker) self._prefix = ['ssh', worker, '--'] self._verbose = verbose @@ -282,7 +294,7 @@ class OnWorker(RemoteServer): def _execute(self, *execs, quiet=None): assert quiet is not None argv = [shlex.quote(a) for a in self._argv(execs)] - return ssh(self._dest, self._prefix + argv, quiet=quiet) + return ssh(self._dest, self._port, self._prefix + argv, quiet=quiet) class RemoteExecution: diff --git a/vm-qemu.sh b/vm-qemu.sh new file mode 100755 index 0000000..2b8f550 --- /dev/null +++ b/vm-qemu.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# Create a new VM using libvirt on the local host. + +set -eu + + +verbose() +{ + echo "INFO: $@" +} + + +die() +{ + echo "$@" 1>&2 + exit 1 +} + + +# Check parameters. + +if [ "$#" -lt 2 ] +then + die "Usage: $ IMAGE PORT" +fi + + +# Command line parameters: image file and port number for SSH. +image="$1" +port="$2" +verbose "creating VM from image $image" +verbose "once it's running, log in from another terminal: ssh -p $port manager@localhost" + +shift 2 + +# Does the image exist? +if [ ! -e "$image" ] +then + echo "$image does not exist" 1>&2 + exit 1 +fi + +# Start VM. +qemu-system-x86_64 \ + -enable-kvm \ + -m 8192 \ + -drive "file=$image,format=raw,if=virtio" \ + -device virtio-net,netdev=user.0 \ + -netdev "user,id=user.0,hostfwd=tcp::$port-:22" + -- cgit v1.2.1