diff --git a/src/database.py b/src/database.py index ea0e7bc5d5e158bec51c7c9e93a27f918a264aca..4758c9c7fac8b31a337c4de965c667557aede0fc 100644 --- a/src/database.py +++ b/src/database.py @@ -1,6 +1,7 @@ +import collections import util +import vm import mount -import collections class DataBase: @@ -18,7 +19,8 @@ class DataBase: else: vms = util.get_vms(db) pass - vm = dict(map(lambda vm: (vm.name, vm), vms)) - return collections.OrderedDict([ (n,vm[n]) for n in sorted(vm)]) + vm_dict = dict(map(lambda vm_elem: (vm_elem.name, vm_elem), vms)) + return collections.OrderedDict([ (n,vm_dict[n]) for n in sorted(vm_dict)]) self.vms = util.FutureCache(vms, self) + self.hypervisors = vm.get_hypervisors(self) self.mount = util.FutureCache(mount.get_active_mounts) diff --git a/src/hostinfo-libvirt.py b/src/hostinfo-libvirt.py index a927c5a90cfca39cc83c2f71a560fbdbfad5eeb5..968abcb13c65b384f0e49c8d09f4f931c3129699 100644 --- a/src/hostinfo-libvirt.py +++ b/src/hostinfo-libvirt.py @@ -148,8 +148,10 @@ if __name__ == '__main__': setattr(options, attr, set(o)) pass - options.physical_disks = dict([tuple(d.split(':')) for d in set(options.physical_disks)]) - options.physical_disk_paths = dict([tuple(d.split(':')) for d in set(options.physical_disk_paths)]) + options.physical_disks = dict([tuple(d.split(':')) + for d in set(options.physical_disks)]) + options.physical_disk_paths = dict([tuple(d.split(':')) + for d in set(options.physical_disk_paths)]) tree = parser.parse(options.hostinfo) db = database.DataBase(tree, options) diff --git a/src/util.py b/src/util.py index 497bd4b7cc8ceafb518fb4d0dead9228c039be2a..5d19c66cef5be1f684dd7647c698235a19139b60 100644 --- a/src/util.py +++ b/src/util.py @@ -1,4 +1,5 @@ import os +import calendar import ipaddress import subprocess import re @@ -7,6 +8,7 @@ import collections import uuid import time from hashlib import sha512 +import parser NFS_POOL = '/var/lib/libvirt/images-nfs' NFS_ROOT = '/var/lib/libvirt/images-nfs-mount' @@ -83,6 +85,12 @@ class FutureCache: pass return self._cache[key] + def __bool__(self): + if self._cache == None: + self._cache = self._func(*self._args) + pass + return bool(self._cache) + def __len__(self): if self._cache == None: self._cache = self._func(*self._args) @@ -163,18 +171,33 @@ class VirtualMachine: self.db = db self.qemu = qemu self.netgroups = netgroups + self.correct = True if db.option.virtualize_physical: self.name = qemu.name[0] - self.memory = db.option.physical_ram - self.arch = db.option.physical_arch - self.machine_type = db.option.physical_machine_type - self.cpu_model = db.option.physical_model - self.cpus = db.option.physical_cores + self.physical = True encoded_name = self.name.encode('utf-8') name_hash = sha512(encoded_name).digest() hypervisor_index = (int.from_bytes(name_hash) % len(db.option.check_hypervisor)) self.host = list(sorted(db.option.check_hypervisor))[hypervisor_index] + + if not db.option.create: + if (self.host not in db.hypervisors or + self.name not in db.hypervisors[self.host].domain): + self.correct = False + return + dom = db.hypervisors[self.host].domain[self.name].inactive + hostinfo = dom._metadata_[0]._child("control.lth.se:hostinfo")[0] + qemu._add(hostinfo._host_[0]._qemu_[0]) + pass + pass + + if db.option.virtualize_physical and db.option.create: + self.memory = db.option.physical_ram + self.arch = db.option.physical_arch + self.machine_type = db.option.physical_machine_type + self.cpu_model = db.option.physical_model + self.cpus = db.option.physical_cores uuid_seed=(int.from_bytes(encoded_name) + int.from_bytes(self.memory.encode('utf-8')) + int.from_bytes(self.arch.encode('utf-8')) @@ -189,24 +212,48 @@ class VirtualMachine: uuid_seed = uuid_seed >> (uuid_seed.bit_length() - 128) pass self.uuid = str(uuid.UUID(version=4, int=uuid_seed)) + + attr = { + 'arch': self.arch, + 'cpus': self.cpus, + 'cpu_model': self.cpu_model, + 'memory': self.memory, + 'uuid': self.uuid, + } + qemusubtag = parser.Node("qemu", None, attr) + qemu._add(qemusubtag) pass else: - self.name = qemu.name[1] - self.memory = qemu.memory[0] - self.arch = qemu.arch[0] - self.machine_type = qemu.machine[0] - self.cpu_model = qemu.cpu_model[0] - self.cpus = qemu.cpus[0] - self.uuid = qemu.uuid[0] - pass - def disk(): + qemu_tag = qemu._qemu_[0] if db.option.virtualize_physical else qemu + self.name = qemu_tag.name[1] + self.memory = qemu_tag.memory[0] + self.arch = qemu_tag.arch[0] + self.machine_type = qemu_tag.machine[0] + self.cpu_model = qemu_tag.cpu_model[0] + self.cpus = qemu_tag.cpus[0] + self.physical = False + self.uuid = qemu_tag.uuid[0] + pass + def regular_disks(disk_tree): def is_raid(d): if d.raid[0] and (d.raid[0].lower() == 'yes' or d.raid[0] == '1'): return True return False disk = [] - if db.option.virtualize_physical: + for d in disk_tree: + disk.append(Disk(db=db, + vm=self, + host=d.host[0], + root=d.root[0], id=d.id[0], + raid=is_raid(d), format=d.format[0], + size=d.size[0])) + pass + return disk + def physical_disks(): + disk = [] + pd = qemu._qemu_[0] + if db.option.create: for d_name in db.option.physical_disks: size = db.option.physical_disks[d_name] path = db.option.physical_disk_paths[d_name] or '/tw/general' @@ -216,17 +263,27 @@ class VirtualMachine: root=path, id=d_name, raid=False, format='qcow2', size=size)) + attr = {"root": path, + "host": self.host, + "id": d_name, + "size": size, + "format": 'qcow2' + } + node = parser.Node("disk", None, attr) + pd._add(node) pass pass + return disk + def disk(): + disk = [] + if db.option.virtualize_physical and db.option.create: + disk = physical_disks() + pass else: - for d in qemu._disk_: - disk.append(Disk(db=db, - vm=self, - host=d.host[0], - root=d.root[0], id=d.id[0], - raid=is_raid(d), format=d.format[0], - size=d.size[0])) - pass + disk_tree = qemu._disk_ + if db.option.virtualize_physical: + disk_tree = disk_tree = qemu._qemu_[0]._disk_ + disk = regular_disks(disk_tree) pass return disk self.disk = FutureCache(disk) @@ -258,9 +315,9 @@ class VirtualMachine: def __init__(self, db, mac, i, index): self.mac = mac if not db.option.virtualize_physical: - self.bridge = i.qemu_bridge[0] or 'main' + b = i.qemu_bridge[0] or 'main' pass - else: + elif db.option.create: bridge_len = len(db.option.physical_interface_bridge) if index < bridge_len: b = db.option.physical_interface_bridge[index] @@ -268,18 +325,37 @@ class VirtualMachine: else: b = i.bridge[0] or 'tw-int' pass - self.bridge = b + i._set("bridge", b) + pass + else: + b = i.bridge[0] or 'tw-int' + i._set("bridge", b) pass + self.bridge = b + pass if not self.db.option.virtualize_physical: l = self.qemu._parent._interface_ pass - else: + elif self.db.option.create: l = self.qemu._interface_ pass - return [ Interface(self.db, mac, i, index) + else: + if (self.host not in self.db.hypervisors or + self.name not in self.db.hypervisors[self.host].domain): + self.correct = False + return + l = self.qemu._interface_ + dom = self.db.hypervisors[self.host].domain[self.name].inactive + hostinfo = dom._metadata_[0]._child("control.lth.se:hostinfo")[0] + li = hostinfo._host_[0]._interface_ + for i in l: + meta_interface = next((mi.bridge[0] for mi in li + if mi.ethernet[0] in i.ethernet[0]), 'tw-int') + i._set("bridge", meta_interface) + pass + return [ Interface(self.db, re.split('[^:0-9a-fA-F]+', i.ethernet[0])[0], i, index) for index, i in enumerate( - [i for i in l if i.ethernet[0] != None]) - for mac in i.ethernet[0].split(',')] + [i for i in l if i.ethernet[0] != None])] def disks(self): result = collections.OrderedDict() @@ -576,7 +652,8 @@ def get_vms(db): return get_from_netgroup(db, lambda h: not h._qemu_) def get_physical(db): - return get_from_netgroup(db, lambda h: h._qemu_) + filter_hosts = lambda h: h._qemu_ or h.name[0] in db.option.check_hypervisor + return get_from_netgroup(db, filter_hosts) def get_from_netgroup(db, filter_host): result = set() diff --git a/src/vm.py b/src/vm.py index 0d07026c0ec64cab0f00ffb1bfde0496d29e6590..496621585fbc45d4aeea0906c7ea472a495c19b5 100644 --- a/src/vm.py +++ b/src/vm.py @@ -167,7 +167,7 @@ def get_hypervisors(db): def safe_hypervisor(hosts): for hy in hosts: try: - yield hy,Hypervisor(hy, db=db) + yield hy,util.FutureCache(Hypervisor, hy, db) pass except libvirt.libvirtError as e: print('Hypervisor failure: %s' % e, file=sys.stderr) @@ -329,20 +329,24 @@ def check(db): # Restricted by wrong netgroup #pring("Restrict netgroup") return False - print('Check:', vm, hypervisor.host if hypervisor else '') + #print('Check:', vm, hypervisor.host if hypervisor else '') # Check all VMs return True - qemu = get_hypervisors(db) nfs_checklist = set() for vm in db.vms.values(): + if not vm.correct: + print('%s needs to be (re)defined on %s' % ( + vm.name, vm.host), file=sys.stderr) + continue if not do_check(vm): continue #print(vm, len(db.vms.values())) - for q in [ q for q in qemu.values() if q.host in vm.disk_by_host ]: + for q in [ q for q in db.hypervisors.values() if q.host in vm.disk_by_host ]: if not do_check(vm, q): continue nfs_checklist.add(q) - result = (check_cpu_model(vm, q) and + result = (vm.correct and + check_cpu_model(vm, q) and check_disks(vm, q) and check_interfaces(vm, q) and check_RNG(vm, q, (db.option.attach and @@ -399,11 +403,17 @@ def domain_xml(virtual_machine): cpu_model = CPU_MODEL_TEMPLATE % dict( cpu_model=virtual_machine.cpu_model) pass + if virtual_machine.physical: + metadata = virtual_machine.qemu._xml() + pass + else: + metadata = virtual_machine.qemu._parent._xml() + pass return DOMAIN_TEMPLATE % dict( name=virtual_machine.name, memory=util.eval_size(virtual_machine.memory or '1G'), uuid=virtual_machine.uuid, - metadata=virtual_machine.qemu._parent._xml(), + metadata=metadata, cpus=virtual_machine.cpus or '1', arch=virtual_machine.arch or 'x86_64', cpu_model=cpu_model or '',