【openstack】Nova(Folsom)虚拟化层Driver分析

来源:互联网 发布:python开发应用 编辑:程序博客网 时间:2024/06/09 21:51

【转载】Nova(Folsom)虚拟化层Driver分析

    本博客欢迎转发,但请保留原作者(@孔令贤HW)信息!内容系本人学习、研究和总结,如有雷同,实属荣幸!       
    本文是我一个同事的一篇分析,征求了同事的意见,获取同意后,在此转载。

    本文依据 Openstack 目前最新folsom版本中,从代码走读的角度,分析了Openstack异构不同hypervisor 下virt部分的代码结构。同时,给出了当前不同hypervisor对于Openstack 定义的通用接口的支持情况。

 1. Folsom版本 virt代码重构

两个版本相比,virt部分的逻辑结构更加清晰。下面是两个版本virt代码中libvirt 的结构对比:



  2. Driver 类型

当前支持的虚拟化平台,主要有以下五种:

     

 

 

3. virt 继承关系

在Folsom版本中,对应不同的hypervisor实现,所有插件类全部继承于一个基类/nova/virt/driver.py,文件中定义到了所有需要driver支持的接口(当然有少部分接口是选配的),文件中仅有接口定义。

各个driver的继承关系如下:




4. ComputeDriver 基类

接下来,我们来看下/nova/virt/driver.py 中的全部接口定义,它是整个virt部分的基础。详见文档中第一部分~

 

5. 基类接口在不同hypervisor 下的支持情况

上面那份表格第二部分,罗列出了不同hypervisor 对基类中全部接口的支持情况。从对比中可以看出,Openstack 对于 libvirt 的支持最好,XenAPI也基本都覆盖到了;这也和Openstack 设计时候的出发点有关。另外,对于VMWareAPI、Hyper-V,一些基本的功能也大致覆盖到了,但接口总体支持情况仅能算一般。

接下来,我们分别看一下不同hypervisor 的Driver情况~

6. 创vm中 调用 virt

之前我在分析《创建vm》文档中写到过,经过调度的消息在compute模块中,会走到/nova/compute/manager.py 中ComputeManager 的__init__() 方法。方法继续走到nova.virt.connection.get_connection 处:

Python代码  收藏代码
  1. driver_name = known_drivers.get(FLAGS.connection_type)  
  2.   
  3. if driver_name is None:  
  4.     raise exception.VirtDriverNotFound(name=FLAGS.connection_type)  
  5.   
  6. conn = importutils.import_object_ns('nova.virt', driver_name,  
  7.                                     read_only=read_only)  
  8.   
  9. if conn is None:  
  10.     LOG.error(_('Failed to open connection to underlying virt platform'))  
  11.     sys.exit(1)  
  12. return utils.check_isinstance(conn, driver.ComputeDriver)  

 

 

原有essex版本中的基类connections.py,目前在folsom版本中已被简化,仅用来读取配置文件中的hypervisor类型,如:




并加载一个工具类 /nova/openstack/common/importutils.py,来返回一个nova.virt.libvirt 类。如下:




 

7. libvirt Driver

首先看一下libvirt的包结构,结构很简单,核心的方法都放在/nova/virt/libvirt/driver.py中,比起此前essex版本中放到connection.py的做法结构更加清晰了:




 

7.1 有关 libvirt

目前libvirt 下,一共支持 kvm、lxc、qemu、uml、xen 五种具体的libvirt_type,默认选用kvm。

虚拟机的状态映射如下:

VIR 内部状态

对外呈现状态

说明

VIR_DOMAIN_NOSTATE

NOSTATE

 

VIR_DOMAIN_RUNNING

RUNNING

 

VIR_DOMAIN_BLOCKED

RUNNING

仅在Xen中存在Blocked状态

VIR_DOMAIN_PAUSED

PAUSED

 

VIR_DOMAIN_SHUTDOWN

SHUTDOWN

libvirt API文档中已指出,此时vm可能仍旧处于Running状态

VIR_DOMAIN_SHUTOFF

SHUTDOWN

 

VIR_DOMAIN_CRASHED

CRASHED

 

VIR_DOMAIN_PMSUSPENDED

SUSPENDED

 

 

7.2 libvirt 连接hypervisor

接下来开始看代码。整个libvrit的driver.py文件 非常长(3000+行python)。。。初始化部分,首先调用基类ComputerDriver类中的__init__(),之后主要由配置文件读取了一些基本的配置项。

libvrit 接口由_conn = property(_get_connection) 来连接底层的hypervisor。而实际的连接工作,由内部的一个静态方法 _connect()进行调用,一种是只读方式,另一种是拥有读写权限的,后一种需要用root身份登录。代码如下:

Python代码  收藏代码
  1. @staticmethod  
  2.     def _connect(uri, read_only):  
  3.         def _connect_auth_cb(creds, opaque):  
  4.             if len(creds) == 0:  
  5.                 return 0  
  6.             LOG.warning(  
  7.                 _("Can not handle authentication request for %d credentials")  
  8.                 % len(creds))  
  9.             raise exception.NovaException(  
  10.                 _("Can not handle authentication request for %d credentials")  
  11.                 % len(creds))  
  12.   
  13.         auth = [[libvirt.VIR_CRED_AUTHNAME,  
  14.                  libvirt.VIR_CRED_ECHOPROMPT,  
  15.                  libvirt.VIR_CRED_REALM,  
  16.                  libvirt.VIR_CRED_PASSPHRASE,  
  17.                  libvirt.VIR_CRED_NOECHOPROMPT,  
  18.                  libvirt.VIR_CRED_EXTERNAL],  
  19.                 _connect_auth_cb,  
  20.                 None]  
  21.   
  22.         if read_only:  
  23.             return libvirt.openReadOnly(uri)  
  24.         else:  
  25.             return libvirt.openAuth(uri, auth, 0)  

 

 

      

7.3 libvirt 创建vm实例

我们接着来看下spawn()方法,这个是libvirt 对外的创建vm接口。传入参数,都是由Nova 处理过的虚拟机、镜像、网络、安全等方面的参数。

 

Python代码  收藏代码
  1. @exception.wrap_exception()  
  2.     def spawn(self, context, instance, image_meta, injected_files,  
  3.               admin_password, network_info=None, block_device_info=None):  
  4.         xml = self.to_xml(instance, network_info, image_meta,  
  5.                           block_device_info=block_device_info)  
  6.         self._create_image(context, instance, xml, network_info=network_info,  
  7.                            block_device_info=block_device_info,  
  8.                            files=injected_files,  
  9.                            admin_pass=admin_password)  
  10.         self._create_domain_and_network(xml, instance, network_info,  
  11.                                         block_device_info)  
  12.         LOG.debug(_("Instance is running"), instance=instance)  
  13.   
  14.         def _wait_for_boot():  
  15.             """Called at an interval until the VM is running."""  
  16.             state = self.get_info(instance)['state']  
  17.   
  18.             if state == power_state.RUNNING:  
  19.                 LOG.info(_("Instance spawned successfully."),  
  20.                          instance=instance)  
  21.                 raise utils.LoopingCallDone()  
  22.   
  23.         timer = utils.LoopingCall(_wait_for_boot)  
  24.         timer.start(interval=0.5).wait()  

 
 方法入口处的 self.to_xml()方法,把之前传入的虚拟机、网络、镜像等信息,转化为创建vm时使用的xml描述文件。这个方法中,针对不同 libvrit_type(xen/kvm/qemu/…)做了大量的分别处理。方法最终走到config.py中,调用基类LibvirtConfigObject 的to_xml()方法, 生成描述文件,代码如下:

 

Python代码  收藏代码
  1. def to_xml(self, pretty_print=True):  
  2.     root = self.format_dom()  
  3.     xml_str = etree.tostring(root, pretty_print=pretty_print)  
  4.     LOG.debug("Generated XML %s " % (xml_str,))  
  5.     return xml_str  

 

             

回到spawn()中,_create_image() 方法略过。接下来看下_create_domain_and_network(),用来设置卷映射、构建网络参数,最后创建vm实例。以下是方法的简明分析:




 

其中,在底层创建vm的动作,是在_create_domain()中完成的。它根据上一步生成好的xml的内容,定义一个虚拟机,相当于创建了一个virDomain对象,注意这时还没有启动这个虚拟机。

另外,其他需要vm的动作都是通过组装+调用该方法实现的。

Python代码  收藏代码
  1. def _create_domain(self, xml=None, domain=None, launch_flags=0):  
  2.     """Create a domain. 
  3.  
  4.     Either domain or xml must be passed in. If both are passed, then 
  5.     the domain definition is overwritten from the xml. 
  6.     """  
  7.     if xml:  
  8.         domain = self._conn.defineXML(xml)  
  9.     domain.createWithFlags(launch_flags)  
  10.     self._enable_hairpin(domain.XMLDesc(0))  
  11.     return domain  

 

      

       其余libvirt API分析,因时间关系暂略。

 

8. XenAPI Driver

还是先看下 XenAPI驱动部分的包结构。和libvirt的结构基本相同。




XenAPI Driver部分,一共分两个类,包括连接 XenServer或者 Xen Cloud的XenAPIDriver 部分,以及一个调用XenAPI的 XenAPISession 类。

连接的初始化部分如下,其中,所有Xen相关的配置,和其他配置项一起保存在nova.conf中,通过之前《创建vm》文档中提到的flags.py来读取。配置项太长了,这里就不贴了。

Python代码  收藏代码
  1. class XenAPIDriver(driver.ComputeDriver):  
  2.     """A connection to XenServer or Xen Cloud Platform"""  
  3.   
  4.     def __init__(self, read_only=False):  
  5.         super(XenAPIDriver, self).__init__()  
  6.   
  7.         url = FLAGS.xenapi_connection_url  
  8.         username = FLAGS.xenapi_connection_username  
  9.         password = FLAGS.xenapi_connection_password  
  10.         if not url or password is None:  
  11.             raise Exception(_('Must specify xenapi_connection_url, '  
  12.                               'xenapi_connection_username (optionally), and '  
  13.                               'xenapi_connection_password to use '  
  14.                               'compute_driver=xenapi.XenAPIDriver'))  
  15.   
  16.         self._session = XenAPISession(url, username, password)  
  17.         self._volumeops = volumeops.VolumeOps(self._session)  
  18.         self._host_state = None  
  19.         self._host = host.Host(self._session)  
  20.         self._vmops = vmops.VMOps(self._session)  
  21.         self._initiator = None  
  22.         self._hypervisor_hostname = None  
  23.         self._pool = pool.ResourcePool(self._session)  

 

之后对于Xen主机、卷、资源池的操作,都是通过上面初始化了的对象,通过XenAPISession发送消息。不细写了~

 

9. VMWareApi Driver

目前,Openstack对于VMWare虚拟化的支持,还是主要集中于对于 ESX的异构,暂时还无法覆盖到vCenter的层面。

 

依旧先来看下 VMwareAPI驱动的包结构:




还是先看下driver.py,文件开头的配置部分列出了连接ESX所需的一些基础配置项,包括主机地址、用户名、密码等常规内容。这些都可以在nova.conf 中进行配置。如下:

 

Python代码  收藏代码
  1. vmwareapi_opts = [  
  2.     cfg.StrOpt('vmwareapi_host_ip',  
  3.                default=None,  
  4.                help='URL for connection to VMWare ESX host.Required if '  
  5.                     'compute_driver is vmwareapi.VMWareESXDriver.'),  
  6.     cfg.StrOpt('vmwareapi_host_username',  
  7.                default=None,  
  8.                help='Username for connection to VMWare ESX host. '  
  9.                     'Used only if compute_driver is '  
  10.                     'vmwareapi.VMWareESXDriver.'),  
  11.     cfg.StrOpt('vmwareapi_host_password',  
  12.                default=None,  
  13.                help='Password for connection to VMWare ESX host. '  
  14.                     'Used only if compute_driver is '  
  15.                     'vmwareapi.VMWareESXDriver.'),  
  16.     cfg.FloatOpt('vmwareapi_task_poll_interval',  
  17.                  default=5.0,  
  18.                  help='The interval used for polling of remote tasks. '  
  19.                        'Used only if compute_driver is '  
  20.                        'vmwareapi.VMWareESXDriver.'),  
  21.     cfg.IntOpt('vmwareapi_api_retry_count',  
  22.                default=10,  
  23.                help='The number of times we retry on failures, e.g., '  
  24.                     'socket error, etc. '  
  25.                     'Used only if compute_driver is '  
  26.                     'vmwareapi.VMWareESXDriver.'),  
  27.     cfg.StrOpt('vmwareapi_vlan_interface',  
  28.                default='vmnic0',  
  29.                help='Physical ethernet adapter name for vlan networking'),  
  30.     ]  
  31.   
  32. FLAGS = flags.FLAGS  
  33. FLAGS.register_opts(vmwareapi_opts)  
 

以下是配置文件的sample,因未配置所以这几项目前是None:

 


 

继续看driver.py的代码,可以发现VMWare Driver部分的代码结构,与XenAPI的非常相似。都是有两个类,包括一个作为ESX主机连接对象的VMWareESXDriver 部分 + 一个用于与ESX主机建立连接并处理全部请求调用的VMWareAPISession 类。代码不细写了~

      

10. Hyper-V Driver

在essex版本规划中,本来计划要删掉的Hyper-V driver,由于微软积极投入开发人员更新代码,反而把功能补齐了。这里是Hyper-V driver的包结构:




Hyper-V 的Driver支持,主要是使用一个基于python的Windows Management Instrumentation (WMI) 来实现。这里有微软关于WMI的介绍:http://msdn.microsoft.com/en-us/library/cc723875%28v=VS.85%29.aspx。

主要处理类都在HyperVDriver的初始化方法中定义了:

Python代码  收藏代码
  1. class HyperVDriver(driver.ComputeDriver):  
  2.     def __init__(self):  
  3.         super(HyperVDriver, self).__init__()  
  4.   
  5.         self._volumeops = volumeops.VolumeOps()  
  6.         self._vmops = vmops.VMOps(self._volumeops)  
  7.         self._snapshotops = snapshotops.SnapshotOps()  
  8.         self._livemigrationops = livemigrationops.LiveMigrationOps(  
  9.             self._volumeops)  

 

 

              其他基本相同,过程略。

本博客欢迎转发,但请保留原作者(@孔令贤HW)信息!内容系本人学习、研究和总结,如有雷同,实属荣幸!

原创粉丝点击