# This file is part of CloudControl. # # CloudControl is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # CloudControl is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with CloudControl. If not, see . import logging import itertools from functools import wraps from xml.etree import cElementTree as et from StringIO import StringIO import libvirt from cloudcontrol.node.utils import execute from cloudcontrol.common.client.tags import ttl, refresh, background logger = logging.getLogger(__name__) def _vir_tag(func): """Catches libvirt related exception. Decorator used for tag declarations that interacts with libvirt. """ @wraps(func) def decorated(dom): if not dom.hypervisor.handler.virt_connected: return try: return func(dom) except libvirt.libvirtError as exc: if 'Domain not found' in str(exc): # sometimes, libvirt tells us too late when a domain is removed # we just ignore the error and remove the domain dom.hypervisor.vm_unregister(dom.name) return logger.exception('Unexpected libvirt error') dom.hypervisor.handler.virt_connect_restart() return decorated def uuid(dom): """Unique identifier of the domain.""" return dom.uuid.lower() @background(max_concurrent=3) def rescue(dom): """Is rescue mode enabled or not""" rescue_path = dom.hypervisor.handler.main.config.rescue_script rcode, output = execute(dom.hypervisor.handler.main, [rescue_path, '-s', dom.name]) if rcode == 0: return output.strip() else: return 'error' def status(dom): return dom.state def hv(dom): # FIXME: what shoud be the value of this tag ? return dom.hypervisor.name def htype(dom): return dom.hypervisor.type @_vir_tag def arch(dom): """VM CPU architecture.""" try: return dict(i686='x86', x86_64='x64')[et.ElementTree().parse( StringIO(dom.lv_dom.XMLDesc(0))).find('os/type').get('arch')] except Exception: logger.exception('Error while get Architecture tag') def h(dom): """Name of the VM.""" return dom.name def t(dom): """Title of the VM (or name if title is not defined).""" if dom.title is None: return dom.name else: return dom.title @_vir_tag def cpu(dom): """Number of CPU of the VM.""" return dom.lv_dom.info()[3] @ttl(10) @refresh(5) @_vir_tag def cpuuse(dom): """Represent CPU use in percent average on 5 seconds.""" state, _, _, vcpu, cpu_time = dom.lv_dom.info() if state != 1: # if VM is not running return None old_cpu_stats = dom.cpu_stats dom.cpu_stats = (dom.hypervisor.handler.main.evloop.now(), cpu_time) # calculate CPU percentage, code is from virt-manager in domain.py:1210 return '%.2f' % max(0., min(100., ((cpu_time - old_cpu_stats[1]) * 100.) / ( (dom.cpu_stats[0] - old_cpu_stats[0]) * 1000. * 1000. * 1000.) / vcpu)) @_vir_tag def mem(dom): """Memory currently allocated.""" if dom.state == 'stopped': return dom.lv_dom.info()[1] * 1024 else: return dom.lv_dom.info()[2] * 1024 @_vir_tag def memmax(dom): """Maximum memory allocation.""" return dom.lv_dom.info()[1] * 1024 @_vir_tag def vncport(dom): """VNC port for the VM console access.""" try: port = et.ElementTree().parse( StringIO(dom.lv_dom.XMLDesc(0)) ).find('devices/graphics[@type="vnc"]').get('port') except Exception: logger.exception('VNCPort') raise try: if 0 < int(port) < 65536: return port except (TypeError, ValueError): pass return @_vir_tag def spiceport(dom): """Spice port for the VM console access.""" try: port = et.ElementTree().parse( StringIO(dom.lv_dom.XMLDesc(0)) ).find('devices/graphics[@type="spice"]').get('port') except Exception: logger.exception('SpicePort') raise try: if 0 < int(port) < 65536: return port except (TypeError, ValueError): pass return @ttl(10) @refresh(10) @_vir_tag def disk(dom): """Get backend disks.""" return u' '.join(map(str, xrange(len(dom.disks)))) or None @ttl(10) @refresh(10) @_vir_tag def nic(dom): """VM network interfaces.""" return u' '.join(map(str, xrange(len(dom.nics)))) or None @ttl(10) @refresh(10) @_vir_tag def nic_vlans(dom): """VM network interfaces.""" vlans = set(itertools.chain(*[x.vlans for x in dom.nics])) return u' '.join(str(x) for x in sorted(vlans)) or None @refresh(10) @_vir_tag def autostart(dom): """Autostart status.""" return {True: 'yes', False: 'no'}[bool(dom.lv_dom.autostart())]