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 1/5] 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) From 4c6a126d8b0a59a454ec69cbbc867786f0b7b04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Tue, 28 Jan 2020 11:02:18 +0100 Subject: [PATCH 2/5] Hack/VM: wire get_vnc and list_vms --- uncloud/hack/main.py | 10 ++++++++++ uncloud/hack/vm.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/uncloud/hack/main.py b/uncloud/hack/main.py index 351f582..9607ec2 100644 --- a/uncloud/hack/main.py +++ b/uncloud/hack/main.py @@ -25,6 +25,8 @@ arg_parser.add_argument('--use-sudo', help="Use sudo for command requiring root! 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('--get-vm-vnc', action='store_true') +arg_parser.add_argument('--list-vms', 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") @@ -52,6 +54,14 @@ def main(arguments): vm = VM(config) vm.status() + if arguments['get_vm_vnc']: + vm = VM(config) + vm.vnc_addr() + + if arguments['list_vms']: + vm = VM(config) + vm.list() + if arguments['last_used_mac']: m = MAC(config) print(m.last_used_mac()) diff --git a/uncloud/hack/vm.py b/uncloud/hack/vm.py index ce96fbf..e9b7719 100755 --- a/uncloud/hack/vm.py +++ b/uncloud/hack/vm.py @@ -111,3 +111,13 @@ class VM(object): print(self.vmm.get_status(self.uuid)) + def vnc_addr(self): + if not self.uuid: + print("Please specific an UUID with the --uuid flag.") + exit(1) + + print(self.vmm.get_vnc(self.uuid)) + + def list(self): + print(self.vmm.discover()) + From a759b8aa39ae96a08904119b15c5306048c34c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Tue, 28 Jan 2020 12:24:26 +0100 Subject: [PATCH 3/5] VMM: make use of socket_dir --- uncloud/vmm/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uncloud/vmm/__init__.py b/uncloud/vmm/__init__.py index 719bdbe..6db61eb 100644 --- a/uncloud/vmm/__init__.py +++ b/uncloud/vmm/__init__.py @@ -125,7 +125,7 @@ class VMM: os.makedirs(self.socket_dir, exist_ok=True) def is_running(self, uuid): - sock_path = os.path.join(self.vmm_backend, uuid) + sock_path = os.path.join(self.socket_dir, uuid) try: sock = socket.socket(socket.AF_UNIX) sock.connect(sock_path) @@ -163,7 +163,7 @@ class VMM: qmp_arg = ( "-qmp", "unix:{},server,nowait".format( - join_path(self.vmm_backend, uuid) + join_path(self.socket_dir, uuid) ), ) vnc_arg = ( @@ -212,7 +212,7 @@ class VMM: def execute_command(self, uuid, command, **kwargs): # execute_command -> sucess?, output try: - with VMQMPHandles(os.path.join(self.vmm_backend, uuid)) as ( + with VMQMPHandles(os.path.join(self.socket_dir, uuid)) as ( sock_handle, file_handle, ): @@ -255,8 +255,8 @@ class VMM: def discover(self): vms = [ uuid - for uuid in os.listdir(self.vmm_backend) - if not isdir(join_path(self.vmm_backend, uuid)) + for uuid in os.listdir(self.socket_dir) + if not isdir(join_path(self.socket_dir, uuid)) ] return vms From 1758629ca1b861c0406e80591ff35073d7d6331f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Tue, 28 Jan 2020 12:33:36 +0100 Subject: [PATCH 4/5] Add minimal doc to hack/vm.py --- uncloud/hack/vm.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/uncloud/hack/vm.py b/uncloud/hack/vm.py index e9b7719..f9cd31a 100755 --- a/uncloud/hack/vm.py +++ b/uncloud/hack/vm.py @@ -17,8 +17,21 @@ # # You should have received a copy of the GNU General Public License # along with uncloud. If not, see . + +# This module is directly called from the hack module, and can be used as follow: # +# Create a new VM with default CPU/Memory. The path of the image file is relative to $hackprefix. +# `uncloud hack --hackprefix /tmp/hackcloud --create-vm --image mysuperimage.qcow2` # +# List running VMs (returns a list of UUIDs). +# `uncloud hack --hackprefix /tmp/hackcloud --list-vms +# +# Get VM status: +# `uncloud hack --hackprefix /tmp/hackcloud --get-vm-status --uuid my-vm-uuid` +# +# Stop a VM: +# `uncloud hack --hackprefix /tmp/hackcloud --destroy-vm --uuid my-vm-uuid` + `` import subprocess import uuid From e2cd44826b9c307f816d170ed93b3a172edcf712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Tue, 28 Jan 2020 13:45:20 +0100 Subject: [PATCH 5/5] Fix typo in hack/vm.py --- uncloud/hack/vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uncloud/hack/vm.py b/uncloud/hack/vm.py index f9cd31a..ac403d8 100755 --- a/uncloud/hack/vm.py +++ b/uncloud/hack/vm.py @@ -31,7 +31,7 @@ # # Stop a VM: # `uncloud hack --hackprefix /tmp/hackcloud --destroy-vm --uuid my-vm-uuid` - `` +# `` import subprocess import uuid