A simple hypervisor controller
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
4.3 KiB

import os
import logging
import subprocess
from time import sleep
from threading import Thread
from zhypervisor.util import TapDevice, Machine
class QMachine(Machine):
machine_type = "q"
def __init__(self, spec):
Machine.__init__(self, spec)
self.proc = None
self.tap = TapDevice()
self.block_respawns = False
# TODO validate specs
def start_machine(self):
if self.proc:
raise Exception("Machine already running!")
else:
qemu_args = self.get_args(tap=str(self.tap))
logging.info("spawning qemu with: {}".format(' '.join(qemu_args)))
sleep(1) # anti-spin
self.proc = subprocess.Popen(qemu_args, preexec_fn=lambda: os.setpgrp(), stdin=subprocess.PIPE)
# TODO handle stdout/err - stream to logs?
Thread(target=self.wait_on_exit, args=[self.proc]).start()
def wait_on_exit(self, proc):
proc.wait()
logging.info("qemu process has exited")
self.proc = None
if not self.block_respawns and self.spec.options.get("respawn", False):
self.start_machine()
def stop_machine(self):
if self.proc:
logging.info("stopping machine...")
self.proc.stdin.write(b"system_powerdown\n")
self.proc.stdin.flush()
self.proc.wait()
self.proc = None
def kill_machine(self):
if self.proc:
self.proc.terminate()
self.proc.wait()
self.proc = None
def get_args(self, tap):
argv = ['qemu-system-x86_64']
argv += self.get_args_system()
argv += self.get_args_drives()
argv += self.get_args_network(tap)
return argv
def get_args_system(self):
"""
Return system-related args:
- Qemu meta args
- CPU core settings
- Mem amnt
- Boot device
"""
args = ["-monitor", "stdio", "-machine", "accel=kvm", "-smp"]
args.append("cpus={}".format(self.spec.properties.get("cores", 1))) # why doesn't this work: ,cores={}
args.append("-m")
args.append(str(self.spec.properties.get("mem", 256)))
args.append("-boot")
args.append("cd")
if self.spec.properties.get("vnc", False):
args.append("-vnc")
assert type(self.spec.properties.get("vnc")) == int, "VNC port should be an integer"
args.append(":{}".format(self.spec.properties.get("vnc")))
return args
def get_args_network(self, tap_name):
"""
Hard-coded for now
"""
args = []
for iface in self.spec.properties.get("netifaces"):
iface_type = iface.get("type")
if iface_type == "tap":
if "ifname" not in iface:
iface["ifname"] = tap_name
iface["script"] = "/root/zhypervisor/testenv/bin/zd_ifup" # TODO fixme
iface["downscript"] = "no"
args.append("-net")
args.append(QMachine.format_args(iface))
return args
# return ['-net', 'nic,vlan=0,model=e1000,macaddr=82:25:60:41:D5:97',
# '-net', 'tap,ifname={},script=if_up.sh,downscript=no'.format(tap_name)]
def get_args_drives(self):
"""
Inspect props.drives expecting a format like: {"file": "/tmp/ubuntu.qcow2", "index": 0, "if": "virtio"}
"""
drives = []
for drive in self.spec.properties.get("drives", []):
drive_info = dict(drive)
drives.append("-drive")
# translate datastore paths if neede
if "file" in drive_info:
drive_info["file"] = self.get_datastore_path(drive_info["datastore"], drive_info["file"])
del drive_info["datastore"]
drives.append(QMachine.format_args(drive_info))
return drives
@staticmethod
def format_args(d):
"""
Given a dictionary like: {"file": "/dev/zd0", "index": 0, "if", "virtio"}
Return a string like: file=/dev/zd0,index=0,if=virtio
"""
args = []
for item, value in d.items():
if item == "type":
args.insert(0, value)
else:
args.append("{}={}".format(item, value))
if not args:
return None
return ','.join(args)