# TODO # 1. Verify that commands successfully did what they are # supposed to do by querying state of VM using QMP. # 2. Implement Monitoring of VM. # For QEMU Monitor Protocol Commands Information, See # https://qemu.weilnetz.de/doc/qemu-doc.html#pcsys_005fmonitor import json import shutil import os import subprocess import argparse import qmp from etcd3_wrapper import Etcd3Wrapper from decouple import config def get_vm_start_cmd(owner_dir, vm_uuid): vm_sock_flags = f"-qmp unix:{owner_dir}/.vm/{vm_uuid}-sock,server,nowait" vm_start_command_flags = ( f"-boot c -net nic -net user -m 256 {vm_sock_flags} -daemonize" ) vm_start_command = f"""qemu-system-x86_64 {owner_dir}/.vm/{vm_uuid}.raw {vm_start_command_flags}""" return vm_start_command def get_qemu_mon(sock_file): m = qmp.QEMUMonitorProtocol(sock_file) try: m.connect() except FileNotFoundError as _: return None return m def main(hostname): client = Etcd3Wrapper() events = client.watch_prefix("/v1/vm/") # events = client.get_prefix("/v1/vm/") for e in events: e.value = json.loads(e.value) e_hostname = e.value["hostname"] e_status = e.value["status"] vm_uuid = e.key.split("/")[-1] owner_dir = f"/var/www/{e.value['owner']}" # If it is not for me then skip it if e_hostname != hostname: continue print(e_status, e) if e_status == "SCHEDULED_DEPLOY": image = client.get( f"/v1/image/{e.value['image_uuid']}", value_in_json=True ) if image: image_uuid = e.value["image_uuid"] print(image) print("Creating New VM...") os.makedirs(f"{owner_dir}/.vm", exist_ok=True) if not os.path.isfile(f"{owner_dir}/.vm/{vm_uuid}.raw"): shutil.copy( f"/var/vm/{image_uuid}.raw", f"{owner_dir}/.vm/{vm_uuid}.raw", ) e.value["status"] = "REQUESTED_START" client.put(e.key, json.dumps(e.value)) elif e_status == "REQUESTED_SUSPEND": m = get_qemu_mon(f"{owner_dir}/.vm/{vm_uuid}-sock") if m: print("Suspending") m.command("stop") m.close() e.value["status"] = "SUSPENDED" client.put(e.key, json.dumps(e.value)) else: print("VM Not Running") elif e_status == "REQUESTED_RESUME": m = get_qemu_mon(f"{owner_dir}/.vm/{vm_uuid}-sock") if m: print("Resuming") m.command("cont") m.close() e.value["status"] = "RESUMED" client.put(e.key, json.dumps(e.value)) else: print("VM Not Running") elif e_status == "REQUESTED_START": m = get_qemu_mon(f"{owner_dir}/.vm/{vm_uuid}-sock") if m: m.close() print("VM already running") e.value["status"] = "RUNNING" client.put(e.key, e.value, value_in_json=True) else: print("Starting VM") subprocess.run( get_vm_start_cmd(owner_dir, vm_uuid).split(" ") ) e.value["status"] = "RUNNING" client.put(e.key, e.value, value_in_json=True) else: continue argparser = argparse.ArgumentParser() argparser.add_argument("hostname", help="Name of this host. e.g /v1/host/1") args = argparser.parse_args() main(args.hostname)