• Nova中的Hook机制


             Nova的代码中支持Hook机制,也就是在某些函数的前后,可以加入自己的代码逻辑。Hook代码可以完全独立于Nova开发,本质上使用setuptools的entry points机制。K版本的OpenStack  Nova中支持Hook的流程有:

             nova.compute .api.API:create

             nova.compute.manager.ComputeManager:_do_build_and_run_instance

             nova.compute.manager.ComputeManager:_delete_instance

             nova.network.base_api:update_instance_cache_with_nw_info

     

    一:Nova中的Hook原理

             以nova.compute.api.API: create方法为例,该方法是nova-api收到创建实例的命令之后要调用的方法,具体代码如下:

    @hooks.add_hook("create_instance")
        def create(self, context, instance_type,
                   image_href, kernel_id=None, ramdisk_id=None,
                   min_count=None, max_count=None,
                   display_name=None, display_description=None,
                   key_name=None, key_data=None, security_group=None,
                   availability_zone=None, user_data=None, metadata=None,
                   injected_files=None, admin_password=None,
                   block_device_mapping=None, access_ip_v4=None,
                   access_ip_v6=None, requested_networks=None, config_drive=None,
                   auto_disk_config=None, scheduler_hints=None, legacy_bdm=True,
                   shutdown_terminate=False, check_server_group_quota=False):
            """Provision instances, sending instance information to the
            scheduler.  The scheduler will determine where the instance(s)
            go and will handle creating the DB entries.
    
            Returns a tuple of (instances, reservation_id)
            """
            ...

              可见该方法被装饰器hooks.add_hook所修饰了,这就是加载钩子的方法,看一下hooks.add_hook代码:

    def add_hook(name, pass_function=False):
        """Execute optional pre and post methods around the decorated
        function.  This is useful for customization around callables.
        """
    
        def outer(f):
            f.__hook_name__ = name
    
            @functools.wraps(f)
            def inner(*args, **kwargs):
                manager = _HOOKS.setdefault(name, HookManager(name))
    
                function = None
                if pass_function:
                    function = f
    
                manager.run_pre(name, args, kwargs, f=function)
                rv = f(*args, **kwargs)
                manager.run_post(name, rv, args, kwargs, f=function)
    
                return rv
    
            return inner
        return outer

             经过该装饰器的修饰之后,调用create(*args, **kwargs)方法时,实际上就是调用的inner(*args,**kwargs)函数。

             inner函数比较简单,首先在全局变量_HOOKS中根据name寻找该方法对应的HookManager。这里的name就是add_hook的入参:"create_instance"。若第一次调用create方法,则创建一个针对该name的HookManager。然后首先调用manager.run_pre,接着是create方法本身,最后调用manager.run_post方法。

     

             这样就相当于在create方法的前后插入了代码。看一下HookManager的实现:

    class HookManager(stevedore.hook.HookManager):
        def __init__(self, name):
            """Invoke_on_load creates an instance of the Hook class
    
            :param name: The name of the hooks to load.
            :type name: str
            """
            super(HookManager, self).__init__(NS, name, invoke_on_load=True)
    
        def _run(self, name, method_type, args, kwargs, func=None):
            if method_type not in ('pre', 'post'):
                msg = _("Wrong type of hook method. "
                        "Only 'pre' and 'post' type allowed")
                raise ValueError(msg)
    
            for e in self.extensions:
                obj = e.obj
                hook_method = getattr(obj, method_type, None)
                if hook_method:
                    LOG.debug("Running %(name)s %(type)s-hook: %(obj)s",
                              {'name': name, 'type': method_type, 'obj': obj})
                    try:
                        if func:
                            hook_method(func, *args, **kwargs)
                        else:
                            hook_method(*args, **kwargs)
                    except FatalHookException:
                        msg = _LE("Fatal Exception running %(name)s "
                                  "%(type)s-hook: %(obj)s")
                        LOG.exception(msg, {'name': name, 'type': method_type,
                                            'obj': obj})
                        raise
                    except Exception:
                        msg = _LE("Exception running %(name)s "
                                  "%(type)s-hook: %(obj)s")
                        LOG.exception(msg, {'name': name, 'type': method_type,
                                            'obj': obj})
    
        def run_pre(self, name, args, kwargs, f=None):
            """Execute optional pre methods of loaded hooks.
    
            :param name: The name of the loaded hooks.
            :param args: Positional arguments which would be transmitted into
                         all pre methods of loaded hooks.
            :param kwargs: Keyword args which would be transmitted into all pre
                           methods of loaded hooks.
            :param f: Target function.
            """
            self._run(name=name, method_type='pre', args=args, kwargs=kwargs,
                      func=f)
    
        def run_post(self, name, rv, args, kwargs, f=None):
            """Execute optional post methods of loaded hooks.
    
            :param name: The name of the loaded hooks.
            :param rv: Return values of target method call.
            :param args: Positional arguments which would be transmitted into
                         all post methods of loaded hooks.
            :param kwargs: Keyword args which would be transmitted into all post
                           methods of loaded hooks.
            :param f: Target function.
            """
            self._run(name=name, method_type='post', args=(rv,) + args,
                      kwargs=kwargs, func=f)

             HookManager继承自stevedore.hook.HookManager。该父类就是stevedore用来管理hook类插件的基本类,关于stevedore可以参考《stevedore简介》。实例化HookManager时,传入的插件的namespace是NS =”nova.hooks”, 插件名是name="create_instance"。

             HookManager.run_pre和HookManager.run_post方法都是调用的HookManager._run方法,只不过参数method_type分别是”pre”和”post”。

             在HookManager._run方法中,轮训找到的插件实例,然后根据method_type寻找对应的实例属性。这样,只要插件类实现了pre和post方法,这里就可以调用这些方法。调用Hook方法时,传入的参数就是调用create方法的参数,还可以加入一个函数参数,这里忽略。

     

    二:创建注册Hook

             下面看一下如何创建并注册一个Hook。按照上面的梳理,只要定义一个实现了pre和post方法的类,然后在setup.py中,使用”nova.hooks”作为entry  points组名,以” create_instance”为entry  point名注册该类即可。下面是类实现:

    class SimpleHookCreate (object):
        def pre(self, *args, **kwargs):
            logger.error("[PRE]this is hook1")
            
        def post(self, *args, **kwargs):
            logger.error("[POST]this is hook1")

              这里pre和post方法,只是在日志中打印简单语句而已。下面是该模块的setup.py脚本:

    import setuptools
    
    setuptools.setup(
        name="demo_nova_hooks",
        packages=['demo_nova_hooks'],
        entry_points={
            'nova.hooks': [
                'create_instance=demo_nova_hooks.simple:SimpleHookCreate'
            ]
        },
    )

             有关setuptools、entry point的相关知识,参阅《Distutils发布Python模块》、《setuptools简介》等相关文章。

             该Hook模块的源码树如下:

    setup.py
    demo_nova_hooks
        __init__.py
        simple.py
        使用 python  setup.py  install命令安装该Hook后,这样在Openstack中创建实例的时候,就会在日志中打印出相应的信息。

             另外,因为使用的是stevedore中的Hook方式加载的插件,因此针对同一个entry point组名下的同一个entry point名,可以安装注册多个Hook类。

     

    参考:

    http://blog.oddbit.com/2014/09/27/integrating-custom-code-with-n/

    http://blog.csdn.net/gqtcgq/article/details/49255995

    http://blog.csdn.net/gqtcgq/article/details/49519685

    http://blog.csdn.net/gqtcgq/article/details/49620279

  • 相关阅读:
    堆排序
    阿里云
    ubuntu下编译内核模块
    字节对齐
    线段树
    c++虚函数表解析
    电面
    sql server数据库定时自动备份
    [hiho1584]Bounce
    五彩斑斓的世界
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247098.html
Copyright © 2020-2023  润新知