Skip to content
Snippets Groups Projects
libvirtwrapper.py 22.3 KiB
Newer Older
Thibault VINCENT's avatar
Thibault VINCENT committed
# -*- coding: utf-8 -*-
Benziane Chakib's avatar
Benziane Chakib committed

import libvirt
import xml.dom.minidom
Thibault VINCENT's avatar
Thibault VINCENT committed
from logging import error, warning, info, debug
from time import sleep
from common import Hypervisor, VM, Storage, StoragePool, StorageVolume
Thibault VINCENT's avatar
Thibault VINCENT committed
from utils import RWLock
from errors import HypervisorError, VMError, StoragePoolError
Benziane Chakib's avatar
Benziane Chakib committed


KVM_LIBVIRT_SESSION = 'qemu:///system'
XEN_LIBVIRT_SESSION = 'xen:///'

MEGABYTE_DIV = 1024 * 1024
GIGABYTE_DIV = 1024 * 1024 * 1024
KILOBYTE_DIV = 1024


class LibvirtHypervisor(Hypervisor):
        super(LibvirtHypervisor, self).__init__()
Thibault VINCENT's avatar
Thibault VINCENT committed
                warning("LibvirtHypervisor: initialized as KVM")
                self._lvcon_handle = libvirt.open(KVM_LIBVIRT_SESSION)
            elif hv_type == 'xen':
Thibault VINCENT's avatar
Thibault VINCENT committed
                warning("LibvirtHypervisor: initialized as Xen")
Thibault VINCENT's avatar
Thibault VINCENT committed
                self._lvcon_handle = libvirt.open(XEN_LIBVIRT_SESSION)
            else:
                raise NotImplemented('Unknown hypervisor type')
        except libvirt.libvirtError as error:
            raise HypervisorError('libvirt cannot connect to hypervisor')
        
        self._hv_type = hv_type
        self._sto_handle = LibvirtStorage(self)
        self._vm_cache_running = {}
        self._vm_cache_defined = {}
        self._vm_cache = {}
Thibault VINCENT's avatar
Thibault VINCENT committed
        self._vm_cache_lock = RWLock()
    
    def scheduler_run(self):
        self._cache_vm_rebuild()
Thibault VINCENT's avatar
Thibault VINCENT committed
    def _cache_vm_rebuild(self):
Thibault VINCENT's avatar
Thibault VINCENT committed
        running = {}
        defined = {}
        
        for dom_id in self._lvcon_handle.listDomainsID():
Thibault VINCENT's avatar
Thibault VINCENT committed
            try:
                vm = LibvirtVm(self, self._lvcon_handle.lookupByID(dom_id))
                running[vm.get_name()] = vm
            except Exception as err:
Thibault VINCENT's avatar
Thibault VINCENT committed
                debug("_cache_vm_rebuild: listDomainsID: `%s` -> `%s`",
                                                                repr(err), err)
Thibault VINCENT's avatar
Thibault VINCENT committed
        
        for dom_name in self._lvcon_handle.listDefinedDomains():
Thibault VINCENT's avatar
Thibault VINCENT committed
            try:
                vm = LibvirtVm(self,
                                self._lvcon_handle.lookupByName(dom_name))
                defined[vm.get_name()] = vm
            except Exception as err:
Thibault VINCENT's avatar
Thibault VINCENT committed
                debug("_cache_vm_rebuild: listDefinedDomains: `%s` -> `%s`",
                                                                repr(err), err)
Thibault VINCENT's avatar
Thibault VINCENT committed
        with self._vm_cache_lock.write:
            self._vm_cache_running = running
            self._vm_cache_defined = defined
            self._vm_cache = self._vm_cache_running
            self._vm_cache.update(self._vm_cache_defined)
        return self._hv_type
    
    def get_hv_version(self):
        version = None
        try:
            data = self._lvcon_handle.getVersion()
            if data:
Thibault VINCENT's avatar
Thibault VINCENT committed
                version = data  
        except:
            pass
        return version
    
    def get_libvirt_version(self):
        '''
        '''
        version = None
        try:
            data = self._lvcon_handle.getLibVersion()
            if data:
                version = data
        except:
            pass
        return version
        return int(self.get_cpu() / self.get_cpu_threads())
        return self._lvcon_handle.getInfo()[7] * self.get_cpu_core()
        return self._lvcon_handle.getInfo()[3]
    
    def storage(self):
    def vm_define(self, data):
        '''
        '''
        vm = self._lvcon_handle.defineXML(data)
        self._cache_vm_rebuild()
        if hasattr(vm, 'name'):
            return vm.name()
        else:
            raise HypervisorError('VM not defined properly')
    
Thibault VINCENT's avatar
Thibault VINCENT committed
        with self._vm_cache_lock.read:
            return self._vm_cache.keys()
Thibault VINCENT's avatar
Thibault VINCENT committed
        with self._vm_cache_lock.read:
            for vm_name, vm in self._vm_cache_running.iteritems():
                if vm.is_active():
                    running.append(vm_name)
Thibault VINCENT's avatar
Thibault VINCENT committed
        with self._vm_cache_lock.read:
            return self._vm_cache_defined.keys()
Thibault VINCENT's avatar
Thibault VINCENT committed
        with self._vm_cache_lock.read:
            for vm_name, vm in self._vm_cache_running.iteritems():
                if vm.is_paused():
                    paused.append(vm_name)
        return paused
    
    def vm_get(self, name):
        if name in self.vm_list():
            try:
                with self._vm_cache_lock.read:
                    return self._vm_cache[name]
            except:
                raise HypervisorError('VM `%s` has vanished' % name)
        else:
            raise HypervisorError('host has no VM named `%s`' % name)
class LibvirtStorage(Storage):
Benziane Chakib's avatar
Benziane Chakib committed
        if isinstance(hypervisor, LibvirtHypervisor):
            self._hv_handle = hypervisor
Benziane Chakib's avatar
Benziane Chakib committed
        else:
            raise TypeError('Expected `%s` given `%s`' % (LibvirtHypervisor,
                                                        hypervisor))
        
        self._pool_cache_running = {}
        self._pool_cache_defined = {}
        self._pool_cache = {}
        self._pool_cache_lock = RWLock()
        with self._pool_cache_lock.write:
            self._pool_cache_running = {}
            self._pool_cache_defined = {}
            self._pool_cache = {}
            
            for name in self._hv_handle._lvcon_handle.listStoragePools():
                pool = LibvirtStoragePool(self,
                        self._hv_handle._lvcon_handle.storagePoolLookupByName(name))
                self._pool_cache_running[pool.get_name()] = pool
            
            for name in self._hv_handle._lvcon_handle.listDefinedStoragePools():
                pool = LibvirtStoragePool(self,
                        self._hv_handle._lvcon_handle.storagePoolLookupByName(name))
                self._pool_cache_defined[pool.get_name()] = pool
            
            self._pool_cache = self._pool_cache_running
            self._pool_cache.update(self._pool_cache_defined)
        if not self._pool_cache:
            self._pool_cache_rebuild()
        with self._pool_cache_lock.read:
            return self._pool_cache.keys()
        if name in self.pool_list():
            try:
                with self._pool_cache_lock.read:
                    return self._pool_cache[name]
            except:
                raise StorageError('storage pool `%s` vanished' % name)
            raise StorageError('no storage pool with name `%s`' % name)
        capacity = 0
        for pool_name in self.pool_list():
            pool = self.pool_get(pool_name)
            capacity += pool.get_space_capacity()
        return capacity
    def find_volumes(self, path=None, name=None):
        volumes = []
        if path is not None or name is not None:
            for pool_name in self.pool_list():
                pool = self.pool_get(pool_name)
                for vol_name in pool.volume_list():
                    vol = pool.volume_get(vol_name)
                    if (path is not None and vol.get_path() == path) \
                            or (name is not None and vol.get_name() == name):
                        volumes.append(vol)
        return volumes


class LibvirtStoragePool(StoragePool):
    def __init__(self, storage, libvirt_pool):
        if isinstance(storage, LibvirtStorage):
            self._sto_handle = storage
Benziane Chakib's avatar
Benziane Chakib committed
        else:
            raise TypeError('Expected `%s` given `%s`' % (LibvirtStorage,
                                                        storage))
        if isinstance(libvirt_pool, libvirt.virStoragePool):
            self._lvpool_handle = libvirt_pool
        else:
            raise TypeError('Expected `%s` given `%s`' % (libvirt.virStoragePool
                                                        , libvirt_pool))
        
        self._vol_cache = {}
        self._vol_cache_lock = RWLock()
    def _vol_cache_rebuild(self):
        with self._vol_cache_lock.write:
            self._vol_cache = {}
            if self._lvpool_handle.isActive():
                for name in self._lvpool_handle.listVolumes():
                    vol = LibvirtStorageVolume(self,
                            self._lvpool_handle.storageVolLookupByName(name))
                    self._vol_cache[vol.get_name()] = vol
    
    def volume_list(self):
        '''
        '''
        if not self._vol_cache:
            self._vol_cache_rebuild()
        with self._vol_cache_lock.read:
            return self._vol_cache.keys()
    
    def volume_get(self, name):
        '''
        '''
        if name in self.volume_list():
            try:
                with self._vol_cache_lock.read:
                    return self._vol_cache[name]
            except:
                raise StoragePoolError('volume `%s` has vanished from pool `%s`'
                                                    %(name, self.get_name()))
        else:
            raise StoragePoolError('pool `%s` has no volume `%s`' % (
                                                        self.get_name(), name))
    
    def volume_create(self, name, size):
        '''
        '''
        xml = '''
            <volume>
                <name>%(name)s</name>
                <capacity>%(capacity)i</capacity>
            </volume>
        ''' % {
                'name' : name,
                'capacity' : size
            }
        try:
            vol = self._lvpool_handle.createXML(xml, 0)
            if isinstance(vol, libvirt.virStorageVol):
                self._vol_cache_rebuild()
                return vol
            else:
                raise StoragePoolError('volume creation failed for an unknown reason')
        except libvirt.libvirtError as err:
            raise StoragePoolError('volume creation failed : `%r` : `%s`' %
                                                                    (err, err))
            data = self._lvpool_handle.name()
            if data:
                name = data
Thibault VINCENT's avatar
Thibault VINCENT committed
        except libvirt.libvirtError:
    def get_space_capacity(self):
Benziane Chakib's avatar
Benziane Chakib committed
        try:
            return self._lvpool_handle.info()[1]
        except libvirt.libvirtError as e:
            raise StoragePoolError("can't get pool information (%s)" % e)
Benziane Chakib's avatar
Benziane Chakib committed
        try:
            return self._lvpool_handle.info()[3]
        except libvirt.libvirtError as e:
            raise StoragePoolError("can't get pool information (%s)" % e)
Benziane Chakib's avatar
Benziane Chakib committed
        try:
            return self._lvpool_handle.info()[2]
        except libvirt.libvirtError as e:
            raise StoragePoolError("can't get pool information (%s)" % e)


class LibvirtStorageVolume(StorageVolume):
    
    def __init__(self, pool, libvirt_vol):
        if isinstance(pool, LibvirtStoragePool):
            self._pool_handle = pool
        else:
            raise TypeError('Expected `%s` given `%s`' % (LibvirtStoragePool,
                                                        pool))
        if isinstance(libvirt_vol, libvirt.virStorageVol):
            self._lvvol_handle = libvirt_vol
        else:
            raise TypeError('Expected `%s` given `%s`' % (libvirt.virStorageVol,
                                                                libvirt_vol))
    def wipe(self):
        '''
        '''
        try:
            if self._lvvol_handle.wipe(0):
                raise StorageVolumeError('volume wipe failed for an unknown reason')
        except libvirt.libvirtError as err:
            raise StorageVolumeError('volume wipe failed : `%r` : `%s`' % (err,
                                                                        err))
    
    def delete(self):
        '''
        '''
        try:
            if self._lvvol_handle.delete(0):
                raise StorageVolumeError('volume deletion failed for an unknown reason')
            else:
                self._pool_handle._vol_cache_rebuild()
        except libvirt.libvirtError as err:
            raise StorageVolumeError('volume deletion failed : `%r` : `%s`' %
                                                                    (err, err))
    
    def get_pool(self):
        '''
        '''
        pool = None
        try:
            data = self._lvvol_handle.storagePoolLookupByVolume()
            if data:
                pool = data
        except libvirt.libvirtError:
            pass
        return pool
    
            data = self._lvvol_handle.name()
            if data:
                name = data
Thibault VINCENT's avatar
Thibault VINCENT committed
        except libvirt.libvirtError:
    def get_space_capacity(self):
        capacity = None
        try:
            capacity = self._lvvol_handle.info()[1]
Thibault VINCENT's avatar
Thibault VINCENT committed
        except libvirt.libvirtError:
    def get_space_allocation(self):
Benziane Chakib's avatar
Benziane Chakib committed
        try:
            allocated = self._lvvol_handle.info()[2]
Thibault VINCENT's avatar
Thibault VINCENT committed
        except libvirt.libvirtError:
            path = self._lvvol_handle.path()
Thibault VINCENT's avatar
Thibault VINCENT committed
        except libvirt.libvirtError:
    ARCH = {
        'i686' : 'x86',
        'x86_64' : 'x64',
    }
    STATUS = (
        'No state',
        'Running',
        'Blocked on resource',
        'Paused',
        'Shutting down ...',
        'Shutdown',
        'Crashed',
    )
    STATUS_STOPPED = [0, 5, 6]
    STATUS_RUNNING = [1, 2 , 3, 4]
    STATUS_PAUSED = [3]
    def __init__(self, hypervisor, domain):
        super(LibvirtVm, self).__init__()
        if isinstance(domain, libvirt.virDomain):
            self._domain = domain
        else:
            raise TypeError('Need virDomain object given %s' % type(domain))
            
        self._hv_handle = hypervisor
        self._find_pid()
        result = find_process_id(self.get_uuid())
        if result:
            self._pid = int(result.pop())
        else:
            self._pid = None
    def undefine(self):
        '''
        '''
        if self._domain.undefine():
            raise VMError('deletion of VM `%s` failed' % self.get_name())
        self._hv_handle._cache_vm_rebuild()
    
            self._domain.create()
        except libvirt.libvirtError:
            raise VMError('`%s` is already running' % self.get_name())
        try:
            self._domain.destroy()
        except libvirt.libvirtError:
            raise VMError('`%s` is not running' % self.get_name())
        try:
            self._domain.shutdown()
        except libvirt.libvirtError:
            raise VMError('`%s` is not running' % self.get_name())
            self._domain.suspend()
        except libvirt.libvirtError:
            raise VMError('`%s` is not running, or already paused'
                                                            % self.get_name())
            self._domain.resume()
        except libvirt.libvirtError:
            raise VMError('`%s` is not paused, or not running'
                                                            % self.get_name())
        active = None
        try:
            active = self._domain.info()[0] in self.STATUS_RUNNING
        except libvirt.libvirtError:
            paused = self._domain.info()[0] in self.STATUS_PAUSED
        except libvirt.libvirtError:
    def get_config(self):
        '''
        '''
        return self._domain.XMLDesc(0)
    
        return self._domain.UUIDString()
            xroot = xml.dom.minidom.parseString(
                        self._domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
            xdomain = xroot.getElementsByTagName('domain').pop()
            xos = xdomain.getElementsByTagName('os').pop()
            xtype = xos.getElementsByTagName('type').pop()
            xarch = xtype.getAttribute('arch')
            if xarch in self.ARCH:
                arch = self.ARCH[xarch]
        except:
Thibault VINCENT's avatar
Thibault VINCENT committed
            pass
        return self._domain.info()[3]
        usage = None
        if self._pid is not None:
            try:
                p = psutil.Process(self._pid)
                sleep(0.2)
                usage = p.get_cpu_percent()
            except:
                pass
        return usage
        if (self.hypervisor().get_hv_type() == 'xen'
                                            && self.get_name() == 'Domain-0'):
            return self._domain.info()[1]
        else:
            return self._domain.info()[1] * KILOBYTE_DIV
        return self._domain.info()[2] * KILOBYTE_DIV
    
    def get_mem_free(self):
        return (self.get_mem() - self.get_mem_used()) * KILOBYTE_DIV
Thibault VINCENT's avatar
Thibault VINCENT committed
    def get_vnc_port(self):
        '''
        '''
        port = None
        try:
            xroot = xml.dom.minidom.parseString(
                        self._domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
            xdomain = xroot.getElementsByTagName('domain').pop()
            xgraphics = xdomain.getElementsByTagName('graphics').pop()
            data = xgraphics.getAttribute('port')
            if data > 0 and data <= 65535:
                port = data
        except:
            pass
        return port
    
            xroot = xml.dom.minidom.parseString(
                        self._domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
            xdomain = xroot.getElementsByTagName('domain').pop()
            xdevices = xdomain.getElementsByTagName('devices').pop()
            # iter on "disk" devices
            for xdisk in xdevices.getElementsByTagName('disk'):
                try:
                    # disks we can handle
                    if xdisk.getAttribute('device') == 'disk':
                        # get type
                        d_type = xdisk.getAttribute('type')
                        # get backend path
                        if d_type == 'file':
                            d_path = xdisk.getElementsByTagName('source').pop()\
                                                        .getAttribute('file')
                        elif d_type == 'block':
                            d_path = xdisk.getElementsByTagName('source').pop()\
                                                            .getAttribute('dev')
                        # search the volume object
                        if d_type in ['file', 'block']:
                            volumes.append(self._hv_handle._sto_handle
                                            .find_volumes(path=d_path).pop())
                except Exception as e:
                    print e
        except:
            pass
        return volumes
Thibault VINCENT's avatar
Thibault VINCENT committed
    
    def get_nics(self):
        '''
        '''
        nics = []
        try:
            xroot = xml.dom.minidom.parseString(
                        self._domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
            xdomain = xroot.getElementsByTagName('domain').pop()
            xdevices = xdomain.getElementsByTagName('devices').pop()
            # iter on "interface" devices
            for xint in xdevices.getElementsByTagName('interface'):
                nic = {}
                try:
                    # search for network interfaces
                    if xint.getAttribute('type') in ['bridge']:
                        # mac
                        nic['mac'] = xint.getElementsByTagName('mac').pop()\
                                                        .getAttribute('address')
                        # model
                        nic['model'] = xint.getElementsByTagName('model').pop()\
                                                        .getAttribute('type')
                        # source
                        nic['source'] = xint.getElementsByTagName('source')\
                                                .pop().getAttribute('bridge')
                except:
                    pass
                else:
                    nics.append(nic)
        except:
            pass
        return nics
def find_process_id(cmd_subchain):
    return [p.pid for p in psutil.get_process_list()
                                        if p.cmdline.__contains__(cmd_subchain)]