1. 概述

    libvirt是基于KVM的上层封装,提供了操作KVM的生层接口,如虚拟机的生命周期(创建,删除,查看,管理)等,网络的管理和存储的管理。通过libvirt可以操作KVM,实现类似于virsh,virt-manager这些工具能够实现的功能,本文以查看当前hypervisor的所有instance为例,讲述通过libvirt模块,查看当前机器的虚拟机列表,关于libvirt的更多操作,如开机,关机,重启,网络管理,存储管理等操作,参考附件。

2. 实现代码

cat libvirt_vm.py       #!/usr/bin/env python#_*_ coding:utf8 _*_#author:Happy#blog adddress: http://happylab.blog.51cto.com#来自Happy实验室import systry:        import libvirt        HAS_LIBVIRT = Trueexcept Exception:        HAS_LIBVIRT = Falsedef is_virtual():        '''        判断当前系统是否支持KVM虚拟化,不支持则退出        '''        if not HAS_LIBVIRT:                sys.exit("current system are not support Virtualization")        return 'virt' def get_conn():        '''        获取libvirt的连接句柄,用于提供操作libivrt的接口        '''        if is_virtual() == 'virt':                try:                        conn = libvirt.open('qemu:///system')                except Exception as e:                        sys.exit(e)        return conndef close_conn(conn):        '''        关闭libvirt的连接句柄        '''        return conn.close()def list_active_vms():        '''        获取所有开机状态的instance,返回虚拟机的名字        '''        vms_list = []        conn = get_conn()        domain_list = conn.listDomainsID()        for id in domain_list:                vms_list.append(conn.lookupByID(id).name())        close_conn(conn)        return vms_listdef list_inactive_vms():        '''        获取关机状态的instance,返回虚拟机的名字        '''        vms_list = []        conn = get_conn()        for id in conn.listDefinedDomains():                vms_list.append(id)        close_conn(conn)        return vms_listdef list_all_vms():        '''        获取所有的虚拟机        '''        vms = []        vms.extend(list_active_vms())        vms.extend(list_inactive_vms())        return vmsdef get_capability():        '''        得到hypervisor的容量信息,返回格式为XML        '''        conn = get_conn()        capability = conn.getCapabilities()        conn.close()        return capabilitydef get_hostname():        '''        attain hypervisor's hostname        '''        conn = get_conn()        hostname = conn.getHostname()        conn.close()        return hostnamedef get_max_vcpus():        '''        获取hypervisor支持虚拟机的最大CPU数        '''        conn = get_conn()        max_vcpus = conn.getMaxVcpus(None)        conn.close()        return max_vcpusif __name__ == "__main__":        print "当前主机%s的虚拟机列表:" % (get_hostname())        for vms in list_active_vms():                print vms

3. 测试

[root@ChuangYiYuan_10_16_2_19 ~]# python libvirt_vm.py 当前主机ChuangYiYuan_10_16_2_19的虚拟机列表:instance-0000006binstance-000001c1instance-000000b9instance-00000181instance-000001f5instance-000000cbinstance-0000007finstance-000000ebinstance-00000145instance-0000019binstance-000001b9instance-000000d7instance-0000012binstance-00000077instance-00000165instance-00000083

4. 总结

    通过libvirt能够实现KVM的管理,libvirt提供了大部分管理KVM的接口,通过改接口,可以实现openstack底层的操作。

5. 附录

    openstack关于libvirt底层的实现代码,供大家参考

"""Supports KVM, LXC, QEMU, UML, and XEN."""import errnoimport eventletimport functoolsimport globimport mmapimport osimport shutilimport socketimport sysimport tempfileimport threadingimport timeimport uuidclass LibvirtDriver(driver.ComputeDriver):    capabilities = {        "has_p_w_picpathcache": True,        "supports_recreate": True,        }    def __init__(self, virtapi, read_only=False):        super(LibvirtDriver, self).__init__(virtapi)        global libvirt        if libvirt is None:            libvirt = __import__('libvirt')        self._host_state = None        self._initiator = None        self._fc_wwnns = None        self._fc_wwpns = None        self._wrapped_conn = None        self._wrapped_conn_lock = threading.Lock()        self._caps = None        self._vcpu_total = 0        self.read_only = read_only        self.firewall_driver = firewall.load_driver(            DEFAULT_FIREWALL_DRIVER,            self.virtapi,            get_connection=self._get_connection)        vif_class = importutils.import_class(CONF.libvirt.vif_driver)        self.vif_driver = vif_class(self._get_connection)        self.volume_drivers = driver.driver_dict_from_config(            CONF.libvirt.volume_drivers, self)        self.dev_filter = pci_whitelist.get_pci_devices_filter()        self._event_queue = None        self._disk_cachemode = None        self.p_w_picpath_cache_manager = p_w_picpathcache.ImageCacheManager()        self.p_w_picpath_backend = p_w_picpathbackend.Backend(CONF.use_cow_p_w_picpaths)        self.disk_cachemodes = {}        self.valid_cachemodes = ["default",                                 "none",                                 "writethrough",                                 "writeback",                                 "directsync",                                 "unsafe",                                ]        for mode_str in CONF.libvirt.disk_cachemodes:            disk_type, sep, cache_mode = mode_str.partition('=')            if cache_mode not in self.valid_cachemodes:                LOG.warn(_('Invalid cachemode %(cache_mode)s specified '                           'for disk type %(disk_type)s.'),                         {'cache_mode': cache_mode, 'disk_type': disk_type})                continue            self.disk_cachemodes[disk_type] = cache_mode        self._volume_api = volume.API()     def _get_new_connection(self):        # call with _wrapped_conn_lock held        LOG.debug(_('Connecting to libvirt: %s'), self.uri())        wrapped_conn = None        try:            wrapped_conn = self._connect(self.uri(), self.read_only)        finally:            # Enabling the compute service, in case it was disabled            # since the connection was successful.            disable_reason = DISABLE_REASON_UNDEFINED            if not wrapped_conn:                disable_reason = 'Failed to connect to libvirt'            self._set_host_enabled(bool(wrapped_conn), disable_reason)        self._wrapped_conn = wrapped_conn        try:            LOG.debug(_("Registering for lifecycle events %s"), self)            wrapped_conn.domainEventRegisterAny(                None,                libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,                self._event_lifecycle_callback,                self)        except Exception as e:            LOG.warn(_("URI %(uri)s does not support events: %(error)s"),                     {'uri': self.uri(), 'error': e})        try:            LOG.debug(_("Registering for connection events: %s") %                      str(self))            wrapped_conn.registerCloseCallback(self._close_callback, None)        except (TypeError, AttributeError) as e:            # NOTE: The registerCloseCallback of python-libvirt 1.0.1+            # is defined with 3 arguments, and the above registerClose-            # Callback succeeds. However, the one of python-libvirt 1.0.0            # is defined with 4 arguments and TypeError happens here.            # Then python-libvirt 0.9 does not define a method register-            # CloseCallback.            LOG.debug(_("The version of python-libvirt does not support "                        "registerCloseCallback or is too old: %s"), e)        except libvirt.libvirtError as e:            LOG.warn(_("URI %(uri)s does not support connection"                       " events: %(error)s"),                     {'uri': self.uri(), 'error': e})        return wrapped_conn   @staticmethod    def uri():        if CONF.libvirt.virt_type == 'uml':            uri = CONF.libvirt.connection_uri or 'uml:///system'        elif CONF.libvirt.virt_type == 'xen':            uri = CONF.libvirt.connection_uri or 'xen:///'        elif CONF.libvirt.virt_type == 'lxc':            uri = CONF.libvirt.connection_uri or 'lxc:///'        else:            uri = CONF.libvirt.connection_uri or 'qemu:///system'        return uri    @staticmethod    def _connect(uri, read_only):        def _connect_auth_cb(creds, opaque):            if len(creds) == 0:                return 0            LOG.warning(                _("Can not handle authentication request for %d credentials")                % len(creds))            raise exception.NovaException(                _("Can not handle authentication request for %d credentials")                % len(creds))        auth = [[libvirt.VIR_CRED_AUTHNAME,                 libvirt.VIR_CRED_ECHOPROMPT,                 libvirt.VIR_CRED_REALM,                 libvirt.VIR_CRED_PASSPHRASE,                 libvirt.VIR_CRED_NOECHOPROMPT,                 libvirt.VIR_CRED_EXTERNAL],                _connect_auth_cb,                None]        try:            flags = 0            if read_only:                flags = libvirt.VIR_CONNECT_RO            # tpool.proxy_call creates a native thread. Due to limitations            # with eventlet locking we cannot use the logging API inside            # the called function.            return tpool.proxy_call(                (libvirt.virDomain, libvirt.virConnect),                libvirt.openAuth, uri, auth, flags)        except libvirt.libvirtError as ex:            LOG.exception(_("Connection to libvirt failed: %s"), ex)            payload = dict(ip=LibvirtDriver.get_host_ip_addr(),                           method='_connect',                           reason=ex)            rpc.get_notifier('compute').error(nova_context.get_admin_context(),                                              'compute.libvirt.error',                                              payload)            raise exception.HypervisorUnavailable(host=CONF.host)    '''                返回instance的个数,conn.numOfDomains()用于显示active的vm个数,conn.numOfDefinedDomains()则显示inactive的vm个数    '''    def get_num_instances(self):        """Efficient override of base instance_exists method."""        return self._conn.numOfDomains()    '''            检查虚拟机是否存在,根据名字校验    '''    def instance_exists(self, instance_name):        """Efficient override of base instance_exists method."""        try:            self._lookup_by_name(instance_name)            return True        except exception.NovaException:            return False    '''                查看libvirt active虚拟机的id号码,conn.numOfDomains()用于显示active虚拟机的个数,conn.numOfDefinedDomains()则用于显示inactive的虚拟机个数    '''    # TODO(Shrews): Remove when libvirt Bugzilla bug # 836647 is fixed.    def list_instance_ids(self):        if self._conn.numOfDomains() == 0:            return []        return self._conn.listDomainsID()    '''                返回虚拟机列表的名字,调用list_instance_ids()函数,只是显示active虚拟机的名字,其中conn.lookupByID(ids).name()用于显示instance的名字    '''    def list_instances(self):        names = []        for domain_id in self.list_instance_ids():            try:                # We skip domains with ID 0 (hypervisors).                if domain_id != 0:                    domain = self._lookup_by_id(domain_id)                    names.append(domain.name())            except exception.InstanceNotFound:                # Ignore deleted instance while listing                continue        # extend instance list to contain also defined domains        names.extend([vm for vm in self._conn.listDefinedDomains()                    if vm not in names])        return names    '''                查看instance的UUID号码,显示active+inactive状态的虚拟机的UUID号码,其中conn.lookupByID(ids).UUIDString()用于返回active instance的UUID号码                                                            conn.lookupByName('name').UUIDString()则返回inactive虚拟机的UUID号    '''    def list_instance_uuids(self):        uuids = set()        for domain_id in self.list_instance_ids():            try:                # We skip domains with ID 0 (hypervisors).                if domain_id != 0:                    domain = self._lookup_by_id(domain_id)                    uuids.add(domain.UUIDString())            except exception.InstanceNotFound:                # Ignore deleted instance while listing                continue        # extend instance list to contain also defined domains        for domain_name in self._conn.listDefinedDomains():            try:                uuids.add(self._lookup_by_name(domain_name).UUIDString())            except exception.InstanceNotFound:                # Ignore deleted instance while listing                continue        return list(uuids)    def plug_vifs(self, instance, network_info):        """Plug VIFs into networks."""        for vif in network_info:            self.vif_driver.plug(instance, vif)    def unplug_vifs(self, instance, network_info, ignore_errors=False):        """Unplug VIFs from networks."""        for vif in network_info:            try:                self.vif_driver.unplug(instance, vif)            except exception.NovaException:                if not ignore_errors:                    raise    def _teardown_container(self, instance):        inst_path = libvirt_utils.get_instance_path(instance)        container_dir = os.path.join(inst_path, 'rootfs')        container_root_device = instance.get('root_device_name')        disk.teardown_container(container_dir, container_root_device)       def _undefine_domain(self, instance):        try:            virt_dom = self._lookup_by_name(instance['name'])        except exception.InstanceNotFound:            virt_dom = None        if virt_dom:            try:                try:                    virt_dom.undefineFlags(                        libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE)                except libvirt.libvirtError:                    LOG.debug(_("Error from libvirt during undefineFlags."                        " Retrying with undefine"), instance=instance)                    virt_dom.undefine()                except AttributeError:                    # NOTE(vish): Older versions of libvirt don't support                    #             undefine flags, so attempt to do the                    #             right thing.                    try:                        if virt_dom.hasManagedSaveImage(0):                            virt_dom.managedSaveRemove(0)                    except AttributeError:                        pass                    virt_dom.undefine()            except libvirt.libvirtError as e:                with excutils.save_and_reraise_exception():                    errcode = e.get_error_code()                    LOG.error(_('Error from libvirt during undefine. '                                'Code=%(errcode)s Error=%(e)s') %                              {'errcode': errcode, 'e': e}, instance=instance)      def _cleanup_rbd(self, instance):        pool = CONF.libvirt.p_w_picpaths_rbd_pool        volumes = libvirt_utils.list_rbd_volumes(pool)        pattern = instance['uuid']        def belongs_to_instance(disk):            return disk.startswith(pattern)        volumes = filter(belongs_to_instance, volumes)        if volumes:            libvirt_utils.remove_rbd_volumes(pool, *volumes)    def _cleanup_lvm(self, instance):        """Delete all LVM disks for given instance object."""        disks = self._lvm_disks(instance)        if disks:            libvirt_utils.remove_logical_volumes(*disks)       @staticmethod    def _get_disk_xml(xml, device):        """Returns the xml for the disk mounted at device."""        try:            doc = etree.fromstring(xml)        except Exception:            return None        ret = doc.findall('./devices/disk')        for node in ret:            for child in node.getchildren():                if child.tag == 'target':                    if child.get('dev') == device:                        return etree.tostring(node)    def _get_existing_domain_xml(self, instance, network_info,                                 block_device_info=None):        try:            virt_dom = self._lookup_by_name(instance['name'])            xml = virt_dom.XMLDesc(0)        except exception.InstanceNotFound:            disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,                                                instance,                                                block_device_info)            xml = self.to_xml(nova_context.get_admin_context(),                              instance, network_info, disk_info,                              block_device_info=block_device_info)        return xml