From 2b71c1807de324be6a9bb707c561621548e3a48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Tue, 28 Jan 2020 09:25:25 +0100 Subject: [PATCH] Wire uncloud-hack vm module to VMM --- uncloud/hack/main.py | 20 +++++-- uncloud/hack/vm.py | 130 +++++++++++++++++++++---------------------- 2 files changed, 80 insertions(+), 70 deletions(-) diff --git a/uncloud/hack/main.py b/uncloud/hack/main.py index b6d8fad..351f582 100644 --- a/uncloud/hack/main.py +++ b/uncloud/hack/main.py @@ -10,7 +10,6 @@ from uncloud import UncloudException arg_parser = argparse.ArgumentParser('hack', add_help=False) #description="Commands that are unfinished - use at own risk") -arg_parser.add_argument('--create-vm', action='store_true') arg_parser.add_argument('--last-used-mac', action='store_true') arg_parser.add_argument('--get-new-mac', action='store_true') @@ -22,8 +21,15 @@ arg_parser.add_argument('--vni', help="VXLAN ID (decimal)", type=int) arg_parser.add_argument('--run-dns-ra', action='store_true', help="Provide router advertisements and DNS resolution via dnsmasq") arg_parser.add_argument('--use-sudo', help="Use sudo for command requiring root!", action='store_true') + +arg_parser.add_argument('--create-vm', action='store_true') +arg_parser.add_argument('--destroy-vm', action='store_true') +arg_parser.add_argument('--get-vm-status', action='store_true') arg_parser.add_argument('--memory', help="Size of memory (GB)", type=int) arg_parser.add_argument('--cores', help="Amount of CPU cores", type=int) +arg_parser.add_argument('--image', help="Path (under hackprefix) to OS image") +arg_parser.add_argument('--uuid', help="VM UUID") + arg_parser.add_argument('--no-db', help="Disable connection to etcd. For local testing only!", action='store_true') arg_parser.add_argument('--hackprefix', help="hackprefix, if you need it you know it (it's where the iso is located and ifup/down.sh") @@ -32,13 +38,19 @@ log = logging.getLogger(__name__) def main(arguments): - log.debug("args={}".format(arguments)) config = Config(arguments) if arguments['create_vm']: - print("Creating VM") vm = VM(config) - vm.commandline() + vm.create() + + if arguments['destroy_vm']: + vm = VM(config) + vm.stop() + + if arguments['get_vm_status']: + vm = VM(config) + vm.status() if arguments['last_used_mac']: m = MAC(config) diff --git a/uncloud/hack/vm.py b/uncloud/hack/vm.py index 4caa2fe..ce96fbf 100755 --- a/uncloud/hack/vm.py +++ b/uncloud/hack/vm.py @@ -27,89 +27,87 @@ import logging from uncloud.hack.db import DB from uncloud.hack.mac import MAC - +from uncloud.vmm import VMM log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) class VM(object): def __init__(self, config): self.config = config + #TODO: Enable etcd lookup self.no_db = self.config.arguments['no_db'] if not self.no_db: self.db = DB(self.config, prefix="/vm") - #TODO: Select generic - #self.hackprefix="/home/nico/vcs/uncloud/uncloud/hack/hackcloud" #TODO: Should be removed midterm - #self.hackprefix="/home/rouxdo/Work/ungleich/uncloud/uncloud/hack/hackcloud" #TODO: Dominique testing - self.hackprefix=self.config.arguments['hackprefix'] - self.qemu="/usr/bin/qemu-system-x86_64" #TODO: should be in config - self.accel="kvm" #TODO: should be config + # General CLI arguments. + self.hackprefix = self.config.arguments['hackprefix'] + self.uuid = self.config.arguments['uuid'] + self.memory = self.config.arguments['memory'] or '1024M' + self.cores = self.config.arguments['cores'] or 1 + if self.config.arguments['image']: + self.image = os.path.join(self.hackprefix, self.config.arguments['image']) + else: + self.image = None - self.vm = {} + # External components. + self.vmm = VMM(vmm_backend=self.hackprefix) + self.mac = MAC(self.config) - #TODO: Touch later! (when necessary) + # Harcoded & generated values. + self.owner = 'uncoud' + self.image_format='qcow2' + self.accel = 'kvm' + self.threads = 1 self.ifup = os.path.join(self.hackprefix, "ifup.sh") self.ifdown = os.path.join(self.hackprefix, "ifdown.sh") + self.ifname = "uc{}".format(self.mac.to_str_format()) - def commandline(self): - """This method is used to trigger / create a vm from the cli""" - #TODO: read arguments from cli - #TODO: create etcd json object - self.vm['owner']= "nico" - self.vm['memory'] = self.config.arguments['memory'] - self.vm['cores'] = self.config.arguments['cores'] - self.vm['os_image'] = os.path.join(self.hackprefix, "alpine-virt-3.11.3-x86_64.iso") - self.create_template() - # mimics api call = this will already be in etcd - #self.vm['os_image'] = self.db.get("os_image") - self.create() + def get_qemu_args(self): + command = ( + "-name {owner}-{name}" + " -machine pc,accel={accel}" + " -drive file={image},format={image_format},if=virtio" + " -device virtio-rng-pci" + " -m {memory} -smp cores={cores},threads={threads}" + " -netdev tap,id=netmain,script={ifup},downscript={ifdown},ifname={ifname}" + " -device virtio-net-pci,netdev=netmain,id=net0,mac={mac}" + ).format( + owner=self.owner, name=self.uuid, + accel=self.accel, + image=self.image, image_format=self.image_format, + memory=self.memory, cores=self.cores, threads=self.threads, + ifup=self.ifup, ifdown=self.ifdown, ifname=self.ifname, + mac=self.mac + ) - def create_template(self): - self.uuid = uuid.uuid4() - #TODO: This all should be generic - self.vm['uuid'] = str(self.uuid) - #self.vni_hex = "{:x}".format(self.config.arguments['vni']) - self.bridgedev = "br{}".format("{:x}".format(self.config.arguments['vni'])) - - #TODO: Enable sudo -- FIXME! - if self.config.arguments['use_sudo']: - self.sudo = "sudo " - else: - self.sudo = "" - - - self.mac=MAC(self.config) - self.mac.create() - self.vm['mac'] = self.mac - self.vm['ifname'] = "uc{}".format(self.mac.to_str_format()) - - # FIXME: TODO: turn this into a string and THEN - # .split() it later -- easier for using .format() - #self.vm['commandline'] = [ "{}".format(self.sudo), - self.vm['commandline'] = "{sudo}{qemu} -name uncloud-{uuid} -machine pc,accel={accel} -m {memory} -smp {cores} -uuid {uuid} -drive file={os_image},media=cdrom -netdev tap,id=netmain,script={ifup},downscript={ifdown},ifname={ifname} -device virtio-net-pci,netdev=netmain,id=net0,mac={mac}" -# self.vm['commandline'] = [ "{}".format(self.sudo), -# "{}".format(self.qemu), -# "-name", "uncloud-{}".format(self.vm['uuid']), -# "-machine", "pc,accel={}".format(self.accel), -# "-m", "{}".format(self.vm['memory']), -# "-smp", "{}".format(self.vm['cores']), -# "-uuid", "{}".format(self.vm['uuid']), -# "-drive", "file={},media=cdrom".format(self.vm['os_image']), -# "-netdev", "tap,id=netmain,script={},downscript={},ifname={}".format(self.ifup, self.ifdown, self.vm['ifname']), -# "-device", "virtio-net-pci,netdev=netmain,id=net0,mac={}".format(self.vm['mac']) -# ] - - def _execute_cmd(self, cmd_string, **kwargs): - cmd = cmd_string.format(**self.vm, **kwargs) - log.info("Executing: {}".format(cmd)) - subprocess.run(cmd.split()) + return command.split(" ") def create(self): - if not self.no_db: - self.db.set(str(self.vm['uuid']), - self.vm, - as_json=True) + # New VM: new UUID, new MAC. + self.uuid = str(uuid.uuid4()) + self.mac.create() + + qemu_args = self.get_qemu_args() + log.debug("QEMU args passed to VMM: {}".format(qemu_args)) + self.vmm.start( + uuid=self.uuid, + migration=False, + *qemu_args + ) + + def stop(self): + if not self.uuid: + print("Please specific an UUID with the --uuid flag.") + exit(1) + + self.vmm.stop(self.uuid) + + def status(self): + if not self.uuid: + print("Please specific an UUID with the --uuid flag.") + exit(1) + + print(self.vmm.get_status(self.uuid)) - self._execute_cmd(self.vm['commandline'], sudo=self.sudo, qemu=self.qemu, accel=self.accel, ifup=self.ifup, ifdown=self.ifdown) - #TODO: Add interface ifname to bridge brXX (via net.py: public function add iface to bridge)