ucloud-vm/main.py

128 lines
3.8 KiB
Python

# 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=False):
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}"""
if vm:
vm_start_command += " -display none"
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, is_vm):
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, is_vm).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")
argparser.add_argument("--vm", type=bool, default=False)
args = argparser.parse_args()
main(args.hostname, args.vm)