OpenStack基础之stevedore

来源:互联网 发布:程序员一般工资多少 编辑:程序博客网 时间:2024/05/22 08:29

    利用python语言的特性,运行时动态载入代码变得更加容易.很多python应用程序利用这样的特性在运行时发现和载入所谓的"插件",使得自己更易于扩展.python库stevedore就是在Setuptools的entry points基础上,构造了一层抽象层,使开发者可以更容易地在运行时发现和载入插件.

    stevedore的代码库在https://github.com/openstack/stevedore,项目主页在https://launchpad.net/python-stevedore,参考文档在http://stevedore.readthedocs.org/.

    entry points的每一个命名空间里,可以包含多个entry point项.stevedore要求每一项都符合如下格式:

name = module:importable

    左边是插件的名称,右边是它的具体实现,中间用等号分隔开.插件的具体实现用"模块:可导入的对象"的形式来指定,以Ceilometer为例:

ceilometer.compute.virt =     libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector    hyperv = ceilometer.compute.virt.hyperv.inspector:HyperVInspector    vsphere = ceilometer.compute.virt.vmware.inspector:VsphereInspectorceilometer.hardware.inspectors =    snmp = ceilometer.hardware.inspectors.snmp:SNMPInspector

    示例中显示了两个不同的entry points的命名空间,“ceilometer.compute.virt"和"ceilometer.hardware.inspectors",分别注册有3个和1个插件.每个插件都符合"名字=模块:可导入对象”的格式,在“ceilometer.compute.virt"命名空间里的libvirt插件,它的具体可载入的实现是ceilometer.compute.virt.libvirt.inspector模块中的LibvirtInspector类.

    根据每个插件在entry point中名字和具体实现的数量之间的对应关系不同,stevedore提供了多种不同的类来帮助开发者发现和载入插件,如下图所示:

插件名字: 具体实现建议选用stevedore中的类1: 1stevedore.driver.DriverManager1: nstevedore.hook.HookManagern: mstevedore.extension.ExtensionManager

    使用stevedore来帮助程序动态载入插件的过程主要分为三个部分:插件的实现,插件的注册,以及插件的载入.下面我们以Ceilometer里动态载入compute agent上的inspector驱动为例来分别进行介绍.

  • 插件的实现

Ceilometer的inspector驱动,为从不同类型hypervisor中获取相关数据提供统一的接口以供compute agent调用.下面是它的基类:

#ceilometer/compute/virt/inspector.py
class Inspector(object):        def inspect_instances(self):        """List the instances on the current host."""        raise NotImplementedError()        def inspect_cpus(self, instance_name):        """Inspect the CPU statistics for an instance.                :param instance_name: the name of the target instance        :return: the number of CPUs and cumulative CPU time        """        raise NotImplementedError()        ...

    ceilometer/compute/virt/libvirt/inspector.py,ceilometer/compute/virt/hyperv/inspector.py和ceilometer/compute/virt/vmware/inspector.py分别为kvm,hyperv和vsphere三种不同hypervisor的具体实现,比如:

class LibvirtInspector(virt_inspector.Inspector):    def __init__(self, conf):        super(LibvirtInspector, self).__init__(conf)        self._connection = None    @property    def connection(self):        if not self._connection:            self._connection = libvirt_utils.get_libvirt_connection(self.conf)        return self._connection    @libvirt_utils.retry_on_disconnect    def _lookup_by_uuid(self, instance):        instance_name = util.instance_name(instance)        try:            return self.connection.lookupByUUIDString(instance.id)        except Exception as ex:            if not libvirt or not isinstance(ex, libvirt.libvirtError):                raise virt_inspector.InspectorException(six.text_type(ex))            error_code = ex.get_error_code()            if (error_code in (libvirt.VIR_ERR_SYSTEM_ERROR,                               libvirt.VIR_ERR_INTERNAL_ERROR) and                ex.get_error_domain() in (libvirt.VIR_FROM_REMOTE,                                          libvirt.VIR_FROM_RPC)):                raise            msg = _("Error from libvirt while looking up instance "                    "<name=%(name)s, id=%(id)s>: "                    "[Error Code %(error_code)s] "                    "%(ex)s") % {'name': instance_name,                                 'id': instance.id,                                 'error_code': error_code,                                 'ex': ex}            raise virt_inspector.InstanceNotFoundException(msg)    def inspect_cpus(self, instance):        domain = self._get_domain_not_shut_off_or_raise(instance)        # TODO(gordc): this can probably be cached since it can be used to get        # all data related        stats = self.connection.domainListGetStats([domain])        dom_stat = stats[0][1]        return virt_inspector.CPUStats(number=dom_stat['vcpu.current'],                                       time=dom_stat['cpu.time'])

  • 插件的注册

#setup.cfg
ceilometer.compute.virt =    libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector    hyperv = ceilometer.compute.virt.hyperv.inspector:HyperVInspector    vsphere = ceilometer.compute.virt.vmware.inspector:VsphereInspector    xenapi = ceilometer.compute.virt.xenapi.inspector:XenapiInspector
    这三个插件注册在命名空间"ceilometer.compute.virt"下,分别叫做libvirt,hyper和vsphere.


  • 插件的载入
#ceilometer/compute/virt/libvirt/inspector.py
def get_hypervisor_inspector(conf):    try:        namespace = 'ceilometer.compute.virt'        mgr = driver.DriverManager(namespace,                                   conf.hypervisor_inspector,                                   invoke_on_load=True,                                   invoke_args=(conf, ))        return mgr.driver    except ImportError as e:        LOG.error(_LE("Unable to load the hypervisor inspector: %s") % e)        return Inspector(conf)

    Ceilometer的compute agent通过调用函数get_hypervisor_inspector来载入具体的某一个插件.此处由于插件和具体实现之间是一对一的关系,所以选用了stevedore和DriverManager类,这个类实例化时可接受的参数如下表所示:

参数 = 默认值说  明namespace(字符串类型)命名空间name(字符串类型)插件名invoke_on_load = False(布尔类型)是否调用entry point所返回的插件对象.(在这个例子中,由于entry point所指向的对象是类,相当于是否实例化类对象)invoke_args = {}(元组类型)调用插件对象时所需要的位置参数invoke_kwds = {}(字典类型)调用插件对象时所需要的命名参数on_load_failure_callback = None(函数类型)载入某个entry point失败时的回调函数,参数为(manager,entry point,exception)verify_requirements = False(布尔类型)是否用setup tools来确保此插件的依赖关系都能满足

    注意这里我们使用的命名空间"ceilometer.compute.virt"需要和Setuptools中注册的命名空间一致.具体需要载入的插件名称从配置项hypervisor_inspector中读入.

0 0
原创粉丝点击