python实战系列(六)之通过libvirt操作KVM

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 sys
try:
        import libvirt
        HAS_LIBVIRT = True
except Exception:
        HAS_LIBVIRT = False
def 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 conn
def 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_list
def list_inactive_vms():
        ‘‘‘
        获取关机状态的instance,返回虚拟机的名字
        ‘‘‘
        vms_list = []
        conn = get_conn()
        for id in conn.listDefinedDomains():
                vms_list.append(id)
        close_conn(conn)
        return vms_list
def list_all_vms():
        ‘‘‘
        获取所有的虚拟机
        ‘‘‘
        vms = []
        vms.extend(list_active_vms())
        vms.extend(list_inactive_vms())
        return vms
def get_capability():
        ‘‘‘
        得到hypervisor的容量信息,返回格式为XML
        ‘‘‘
        conn = get_conn()
        capability = conn.getCapabilities()
        conn.close()
        return capability
def get_hostname():
        ‘‘‘
        attain hypervisor‘s hostname
        ‘‘‘
        conn = get_conn()
        hostname = conn.getHostname()
        conn.close()
        return hostname
def get_max_vcpus():
        ‘‘‘
        获取hypervisor支持虚拟机的最大CPU数
        ‘‘‘
        conn = get_conn()
        max_vcpus = conn.getMaxVcpus(None)
        conn.close()
        return max_vcpus
if __name__ == "__main__":
        print "当前主机%s的虚拟机列表:" % (get_hostname())
        for vms in list_active_vms():
                print vms

3. 测试

[[email protected]_10_16_2_19 ~]# python libvirt_vm.py 
当前主机ChuangYiYuan_10_16_2_19的虚拟机列表:
instance-0000006b
instance-000001c1
instance-000000b9
instance-00000181
instance-000001f5
instance-000000cb
instance-0000007f
instance-000000eb
instance-00000145
instance-0000019b
instance-000001b9
instance-000000d7
instance-0000012b
instance-00000077
instance-00000165
instance-00000083

4. 总结

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

5. 附录

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

"""
Supports KVM, LXC, QEMU, UML, and XEN.
"""
import errno
import eventlet
import functools
import glob
import mmap
import os
import shutil
import socket
import sys
import tempfile
import threading
import time
import uuid

class LibvirtDriver(driver.ComputeDriver):
    capabilities = {
        "has_imagecache": 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.image_cache_manager = imagecache.ImageCacheManager()
        self.image_backend = imagebackend.Backend(CONF.use_cow_images)
        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.images_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
时间: 2024-10-12 20:12:47

python实战系列(六)之通过libvirt操作KVM的相关文章

Python学习系列(五)(文件操作及其字典)

Python学习系列(五)(文件操作及其字典) Python学习系列(四)(列表及其函数) 一.文件操作 1,读文件 在以'r'读模式打开文件以后可以调用read函数一次性将文件内容全部读出,也可以指定每次read读多少字节,例如: 1 #coding:utf-8 2 fn='test1.py' 3 fp=open(fn,'r') #以读的方式打开文件,文件必须首先存在和,.文件在同一目录下py 4 print 'reading pos:',fp.tell() 5 r=fp.read(20) #

Python 实战系列-微信或网页远程控制电脑

本系列课程为Python实战系列课程:使用微信控制电脑,使用Python的Web框架Flask搭建网页,并使用网页控制电脑:使用wxPython编写图形化的程序,并进一步将这个程序发展为远程控制程序. 课程目录: |--|----|----1 微信远控:Python 控制电脑的两种方法|--|----|----|----1 课程介绍.mp4|--|----|----|----2 命令提示符 CMD 入门.mp4|--|----|----|----3 Python 执行 CMD 命令.mp4|--

python selenium系列(三)常用操作类型及方法

一 前言 开展WEB UI自动化的核心思路,无非就是找到元素,然后操作元素这两个内容.在python selenium系列(二)元素定位方式一文中,已经介绍了如何找到元素这项技能,本文将介绍第二项内容,即如何操作已经找到的元素. 二 操作方法分类 总体来说,可以将操作大体分成四类,即浏览器操作.键盘操作.鼠标操作.js脚本. 1.  浏览器常用操作方法: 方法 描述 driver.maximize_window() 窗口最大化 driver.back() 页面返回 driver.forward(

Python基础(六) 基础文件操作

今天学习python下对文件的基础操作,主要从open函数.File对象的属性.文件定位.简单操作.举例说明几个步骤开始学习,下面开始进入今天的主题: 一.open函数介绍 open函数主要是打开一个文件,创建一个file对象,相关的方法可以调用它进行读写 . 语法格式如下: 1 2 3 file object = open(文件名,打开文件的模式) file object  = with open (文件名,打开文件的模式) as 变量名 两种语法格式的不同在于下面这种方法不用输入f.clos

python实战系列之批量主机ping网络测试(07)

1.需求说明   工作环境中,经常会有使用到ping对网络上的主机做网络测试,如机器上架,下线,测试等一些需要,对于大批量的机器来说,逐个主机ping测试,显然难以满足要求,对于机器比较多的场景,可以将需要执行ping测试的IP地址存放至一个文件内,调用脚本执行网络测试,方便,便捷. 2.程序内容 vim ping.py  #!/usr/bin/env python #_*_ coding:utf8 _*_ #author: Happy #来自Happy试验试验 http://happylab.

python实战系列之ip地址排序问题(02)

1. 背景说明 从openstack的nova list中获取了虚拟机的ip地址,但这些ip都没有排序和分组,为了进一步增强可读性,对ip地址执行排序操作,在shell下,可以通过sort命令,执行排序操作,具体操作如下: [[email protected] ~]# cat ip.txt | sort -t "." -k1,1n -k2,2n -k3,3n -k4,4n 10.1.104.75 10.1.104.87 10.1.104.149 10.1.104.151 10.1.10

MP实战系列(十三)之批量修改操作(前后台异步交互)

MyBatis的批量操作其实同MyBatis基本是一样的.并无多大区别,要说区别,除了封装的方法之外,主要就是注解方面的区别,比如@TableId.@TableField.@TableName等等区别. 示例描述: 本次描述的是批量相关的操作,主要是批量修改等操作. 项目讲解:如何批量修改开锁方式? 准备环境和IDE工具:MySQL5.7+Maven3以上+JDK8或者JDK7+SSM框架+tomcat8或者tomcat7应用服务器+Eclipse. 本文核心:主要是Controller代码和数

python实战系列之RTX发送告警消息(04)

背景说明:  在工作环境中,经常需要监控和告警相互配合,如基础监控,进程监控,业务监控,当触发监控时,可以发送短信,邮件,或者是RTX,方面管理员在第一时间,获知系统的健康状况,从而对系统系统进行管理. 程序内容: vim send_warning.py  #!/usr/bin/env python #_*_ coding:utf8 _*_ #来自Happy实验室 import urllib import urllib2 def send_warning(receiver,title,conte

python实战系列之生成随机验证码(03)

背景:在一些登陆网站中,输入用户名和密码之后,通常也需要输入验证码,验证码能够用于加密的salt,防止一些恶意攻击,如下通过python生成任意长度的随机验证码,验证码大写字母,小写字母和数字组成,其中小写字母由97至122的ASIIC码组成,大小字母则有65至90组成,通过chr()函数,将ASIIC码转换为字母,如下通过几行代码即可实现. 程序内容: #!/usr/bin/env python #_*_ coding:utf8 _*_ #author:Happy #来自Happy实验室,该程