summaryrefslogtreecommitdiff
path: root/contractor
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-04-09 10:32:46 +0300
committerLars Wirzenius <liw@liw.fi>2020-04-09 10:32:46 +0300
commit6e76b45f9fcf1d47aca42dd0dfad7bdc16f13b75 (patch)
tree18b926761dfd6e0ea9197d7e8f8188095dd31586 /contractor
parent677760a88fc4c7ce45cc614959c6c4121004dfa8 (diff)
downloadick-contractor-6e76b45f9fcf1d47aca42dd0dfad7bdc16f13b75.tar.gz
Change: set up worker
Diffstat (limited to 'contractor')
-rwxr-xr-xcontractor112
1 files changed, 107 insertions, 5 deletions
diff --git a/contractor b/contractor
index cc02613..934819b 100755
--- a/contractor
+++ b/contractor
@@ -21,7 +21,13 @@ def ssh(target, argv, quiet=False):
stdout = None
if quiet:
stdout = PIPE
- return subprocess.call(argv, stderr=STDOUT, stdout=stdout)
+ logging.debug(
+ 'EXEC: {!r} stdout={!r} stderr={!r}'.format(
+ argv, stdout, STDOUT))
+ p = subprocess.Popen(argv, stderr=STDOUT, stdout=stdout)
+ out, err = p.communicate()
+ logging.debug('RESULT: {} {!r} {!r}'.format(p.returncode, out, err))
+ return out, err, p.returncode
def rsync(filename, target):
@@ -63,11 +69,21 @@ class ContractorApplication(cliapp.Application):
self.verbose(
'uploading image to worker: {}'.format(bs.worker_image()))
- m.upload_image(bs.worker_image())
+ m.upload_worker_image(bs.worker_image())
+
+ self.verbose('starting worker')
+ if not m.start_worker():
+ self.error('could not start worker')
+
+ # install = bs.install()
+ # if install:
+ # self.verbose(
+ # 'installing packages: {}'.format(', '.join(install)))
+ # else:
+ # self.verbose('no packages to install')
+ # m.install_on
- # pkgs = build.get('install', [])
# if pkgs:
- # self.verbose('Installing packages: {}'.format(', '.join(pkgs)))
# self.cmd_setup(
# ['env', 'DEBIAN_INTERFACE=noninteractive',
# 'apt-get', 'install', '-y'] + pkgs)
@@ -370,8 +386,94 @@ class Manager:
self.virsh(['destroy', 'worker'])
self.virsh(['undefine', 'worker'])
- def upload_image(self, filename):
+ def upload_worker_image(self, filename):
rsync(filename, '{}:worker.img'.format(self._target))
+ def copy_worker_image(self):
+ self.ssh(['cp', 'worker.img', 'temp.img'])
+
+ def get_cpu_count(self):
+ out, err, exit_code = self.ssh(['lscpu'], quiet=True)
+ if exit_code != 0:
+ logging.error('lscpu on manager failed: {!r}'.format(err))
+ return None
+
+ for line in out.decode('UTF8').splitlines():
+ if line.startswith('CPU(s):'):
+ return int(line.split()[-1])
+
+ logging.error('Could not find number of CPUs on manager')
+ return None
+
+ def worker_ip(self):
+ filename = '/var/lib/libvirt/dnsmasq/virbr0.status'
+ out, err, exit_code = self.ssh(['cat', filename], quiet=True)
+ if exit_code != 0:
+ logging.error('Could not read dnsmasq status file')
+ return None
+ logging.debug('virbr0.status from manager: {!r}'.format(out))
+
+ status = json.loads(out)
+ if not status:
+ return None
+
+ status.sort(key=lambda e: e['expiry-time'])
+ return status[-1]['ip-address']
+
+ def start_worker(self):
+ self.virsh(['net-autostart', 'default'])
+ self.virsh(['net-start', 'default'])
+ n = self.get_cpu_count()
+ if not isinstance(n, int):
+ logging.error('Could not start worker due to missing CPU count')
+ return None
+
+ out, err, exit_code = self.ssh([
+ 'virt-install',
+ '--connect', 'qemu:///system',
+ '--quiet',
+ '--name=worker',
+ '--memory=4096',
+ '--vcpus={}'.format(n-1),
+ '--cpu=host',
+ '--import',
+ '--os-variant=debian9',
+ '--disk=path=temp.img,cache=none',
+ '--disk=path=/dev/vdb,cache=none',
+ '--network=network=default',
+ '--graphics=spice',
+ '--noautoconsole',
+ ])
+ if exit_code != 0:
+ logging.error('Could not create worker VM')
+ return None
+
+ return self.wait_for_worker()
+
+ def wait_for_worker(self):
+ # We look up the IP and try to use it. The IP might be for a
+ # previous worker instance.
+ while True:
+ ip = self.worker_ip()
+ if ip is None:
+ continue
+ w = Worker(self, ip)
+ out, err, exit_code = w.ssh(['true'], quiet=True)
+ if exit_code == 0:
+ return w
+ time.sleep(2)
+
+
+class Worker:
+
+ def __init__(self, manager, ip):
+ self._manager = manager
+ self._target = 'worker@{}'.format(ip)
+
+ def ssh(self, argv, quiet=False):
+ argv = [shlex.quote(arg) for arg in argv]
+ argv = ['ssh', self._target, '--'] + argv
+ return self._manager.ssh(argv, quiet=quiet)
+
ContractorApplication().run()