import logging import weakref from StringIO import StringIO from xml.etree import cElementTree as et from collections import namedtuple from ccnode.tags import Tag, tag_inspector from ccnode.hypervisor import lib as _libvirt from ccnode.hypervisor.lib import DOMAIN_STATES as STATE from ccnode.hypervisor.domains import vm_tags logger = logging.getLogger(__name__) NetworkInterface = namedtuple('NetworkInterface', ('source', 'mac', 'model')) class VirtualMachine(object): """Represent a VM instance.""" def __init__(self, dom, hypervisor): """ :param dom: libvirt domain instance :param hypervisor: hypervisor where the VM is """ self.hypervisor = weakref.proxy(hypervisor) #: UUID string of domain self.uuid = dom.UUIDString() self.name = dom.name() #: state of VM: started, stoped, paused self._state = STATE[dom.info()[0]] #: tags for this VM self.tags = dict((t.name, t) for t in tag_inspector(vm_tags, self)) # define dynamic tags i = 0 for v in self.iter_disks(): for t in ( Tag('disk%s_size' % i, v.capacity, 10), Tag('disk%s_path' % i, v.path, 10), Tag('disk%s_pool' % i, v.storage, 10), # FIXME: change Tag('disk%s_vol' % i, v.name, 10), ): self.tags[t.name] = t i += 1 i = 0 for nic in self.iter_nics(): for t in ( Tag('nic%s_mac' % i, nic.mac, 10), Tag('nic%s_source' % i, nic.source, 10), Tag('nic%s_model' %i, nic.model, 10), ): self.tags[t.name] = t logger.debug('Virtual Machine tags: %s', self.tags) @property def state(self): return self._state @state.setter def state(self, value): self._state = value self.tags['status'].value = value @property def lv_dom(self): """Libvirt domain instance.""" return _libvirt.connection.lookupByUUIDString(self.uuid) def start(self): self.lv_dom.create() def stop(self): self.lv_dom.shutdown() def suspend(self): self.lv_dom.suspend() def resume(self): self.lv_dom.resume() def destroy(self): self.lv_dom.destroy() def undefine(self): self.lv_dom.undefine() @property def disks(self): return list(self.iter_disks()) def iter_disks(self): for d in et.ElementTree().parse( StringIO(self.lv_dom.XMLDesc(0)) ).findall('devices/disk'): if d.get('device') != 'disk': continue type_ = d.get('type') if type_ not in ('file', 'block'): continue path = d.find('source').get(dict(file='file', block='dev')[type_]) volume = self.hypervisor.storage.get_volume(path) if volume is None: continue yield volume @property def nics(self): return list(self.iter_nics()) def iter_nics(self): for nic in et.ElementTree().parse( StringIO(self.lv_dom.XMLDesc(0)) ).findall('devices/interface'): if nic.get('type') == 'bridge': try: mac = nic.find('mac').get('address') except AttributeError: mac = None try: model = nic.find('model').get('type') except AttributeError: model = None try: source = nic.find('source').get('bridge') except AttributeError: source = None yield NetworkInterface( mac=mac, source=source, model=model, )