加載heat resource的代碼分析

Heat的核心是stack,stack又是由各類各樣的資源組成的,heat除了自定義的大量資源外,還容許用戶自定義本身須要的資源。python

heat資源加載流程

咱們先從heat-engine的啓動腳原本看:json

heat/cmd/engine.py
if __name__ == '__main__':  
  
    cfg.CONF(project='heat', prog='heat-engine')  
    logging.setup('heat')  
    messaging.setup()  
  
    mgr = None  
    try:  
	#加載heat的模板類  
        mgr = template._get_template_extension_manager()  
    except template.TemplatePluginNotRegistered as ex:  
        LOG.critical(_LC("%s"), ex)  
    if not mgr or not mgr.names():  
        sys.exit("ERROR: No template format plugins registered")  
  
    from heat.engine import service as engine  
  
    srv = engine.EngineService(cfg.CONF.host, rpc_api.ENGINE_TOPIC)

上面爲heat-egine進程啓動的代碼,在建立EngineService的時候,這裏面有個resources初始化的步驟,api

這裏應該就是完成資源加載的關鍵,以下面的代碼所示app

heat/engine/service.py EngineService.__init__():

def __init__(self, host, topic, manager=None):  
        super(EngineService, self).__init__()  
        resources.initialise() #對資源進行初始化,分析代碼能夠除了初始化資源,此處還會初始化clients. 
        self.host = host  
        self.topic = topic

首先是有個全局變量_environment,因爲是初次加載,因此會執行clients.initialise()

heat/engine/resources/__init__.py
def initialise():  
    global _environment  
    if _environment is not None:  
        return  
  
    clients.initialise()  
  
    global_env = environment.Environment({}, user_env=False)  
    _load_global_environment(global_env)  
    _environment = global_env

clients其實就是heat與各個openstack組件通訊的關鍵,相似的,這裏採用了全局_mgr來管理clients的加載,函數

初次加載的時候因爲全局變量_mgr爲None,因此採用stevedore加載heat須要用到的各類clients,ui

_mgr = None  
  
  
def has_client(name):  
    return _mgr and name in _mgr.names()  
  
heat/engine/clients/__init__.py 
def initialise():  
    global _mgr  
    if _mgr:  
        return  
  
    _mgr = extension.ExtensionManager(  
        namespace='heat.clients',  
        invoke_on_load=False,  
        verify_requirements=False)

這裏的clients爲entrypoints中定義的名稱空間爲heat.clients中的各類client,具體能夠查看heat的api-paste.ini文件。

加載完這些clients後,會定義一個Environment對象:
spa

heat/engine/environment.py
def __init__(self, env=None, user_env=True):  
        """Create an Environment from a dict of varying format. 
        1) old-school flat parameters 
        2) or newer {resource_registry: bla, parameters: foo} 
 
        :param env: the json environment 
        :param user_env: boolean, if false then we manage python resources too. 
        """  
        if env is None:  
            env = {}  
        if user_env:  
            from heat.engine import resources  
            global_registry = resources.global_env().registry  
        else:  
            global_registry = None  
  
        self.registry = ResourceRegistry(global_registry)  
        self.registry.load(env.get(env_fmt.RESOURCE_REGISTRY, {}))  
  (部分代碼有刪減)

後面咱們加載資源的關鍵了

self.registry = ResourceRegistry(global_registry)  def _load_global_environment(env):  
    _load_global_resources(env)  
    environment.read_global_environment(env)  
  
  
def _load_global_resources(env):  
    _register_constraints(env, _get_mapping('heat.constraints'))  
    _register_stack_lifecycle_plugins(  
        env,  
        _get_mapping('heat.stack_lifecycle_plugins'))  
  
    manager = plugin_manager.PluginManager(__name__)  
    # Sometimes resources should not be available for registration in Heat due  
    # to unsatisfied dependencies. We look first for the function  
    # 'available_resource_mapping', which should return the filtered resources.  
    # If it is not found, we look for the legacy 'resource_mapping'.  
    resource_mapping = plugin_manager.PluginMapping(['available_resource',  
                                                     'resource'])  
    constraint_mapping = plugin_manager.PluginMapping('constraint')  
  
    _register_resources(env, resource_mapping.load_all(manager))  
  
    _register_constraints(env, constraint_mapping.load_all(manager))

在_load_global_resources方法裏面,首先是往env裏面註冊constraintsstack_lifecycle_plugins插件

註冊的原理很簡單,也是用stevedore進行插件的加載,須要加載的項在api-paste.init中的entry_points裏面能夠看到。code


而後經過下面的方法,將這些插件註冊到前面定義的global_env對象中,接下面就是resources的加載的,orm

這裏定義了2個插件相關的類,PluginManager以及PluginMapping,首先看下PluginManager

heat/engine/plugin_manager.py
class PluginManager(object):  
    '''''A class for managing plugin modules.'''  
  
    def __init__(self, *extra_packages):  
        '''''Initialise the Heat Engine plugin package, and any others. 
 
        The heat.engine.plugins package is always created, if it does not 
        exist, from the plugin directories specified in the config file, and 
        searched for modules. In addition, any extra packages specified are 
        also searched for modules. e.g. 
 
        >>> PluginManager('heat.engine.resources') 
 
        will load all modules in the heat.engine.resources package as well as 
        any user-supplied plugin modules. 
 
        '''  
        def packages():  
            for package_name in extra_packages:  
                yield sys.modules[package_name]  
  
            cfg.CONF.import_opt('plugin_dirs', 'heat.common.config')  
            yield plugin_loader.create_subpackage(cfg.CONF.plugin_dirs,  
                                                  'heat.engine')  
  
        def modules():  
            pkg_modules = itertools.imap(plugin_loader.load_modules,  
                                         packages())  
            return itertools.chain.from_iterable(pkg_modules)  
  
        self.modules = list(modules())


其屬性modules主要是列出了資源所在的各個模塊,這裏默認加載的'heat.engine.resources'包下面的各個模塊,

此外,heat還容許咱們本身配置加載資源插件的路徑,可是默認來講,heat自身只會加載'heat.engine.resources'下面的模塊,

因此咱們擴展資源插件的時候,能夠選擇放在這個目錄heat/engine/resources/。


接下來定義了2個PluginMapping,分別針對resources和constraints,注意到這裏傳入的參數,resources傳入的是

available_resource和resource,後面咱們會看到爲何要傳這個.

resource_mapping= plugin_manager.PluginMapping(['available_resource',  
                                                    'resource']) 
constraint_mapping = plugin_manager.PluginMapping('constraint')

接着會執行下面這段代碼

_register_resources(env, resource_mapping.load_all(manager))  
def _register_resources(env, type_pairs):  
    for res_name, res_class in type_pairs:  
        env.register_class(res_name, res_class)

那type_pairs裏面究竟是啥呢,咱們看下相關的方法,首先是resource_mapping.load_all(manager)的返回,

能夠看到這裏返回了個迭代器,迭代器中的內容又是一個迭代器,每一個迭代器的內容又來源於itertools.imap(function,self.modules),

也就是將load_from_module方法做用於以前加載起來的在resources下面的各個資源模塊。

def load_all(self, plugin_manager):  
        '''''Iterate over the mappings from all modules in the plugin manager. 
 
        Mappings are returned as a list of (key, value) tuples. 
        '''  
        mod_dicts = plugin_manager.map_to_modules(self.load_from_module)  
        return itertools.chain.from_iterable(six.iteritems(d) for d  
def map_to_modules(self, function):  
        '''''Iterate over the results of calling a function on every module.'''  
        return itertools.imap(function, self.modules)

咱們來看下PluginMapping中的load_from_module這個方法,能夠看到,以前傳的available_resource和resource參數就起做用了,

該方法會從這個模塊嘗試去取available_resource_mapping和resource_mapping,若是available_resource_mapping或resource_mapping是函數,就會取resouce_mapping方法的內容

heat/engine/plugin_manager.py
def load_from_module(self, module):  
        '''''Return the mapping specified in the given module. 
 
        If no such mapping is specified, an empty dictionary is returned. 
        '''  
        for mapping_name in self.names:  
            mapping_func = getattr(module, mapping_name, None)  
            if callable(mapping_func):  
                fmt_data = {'mapping_name': mapping_name, 'module': module}  
                try:  
                    mapping_dict = mapping_func(*self.args, **self.kwargs)  
     (部分代碼有刪減)

咱們以heat自帶的autoscaling模塊爲例,它的返回以下

def resource_mapping():  
    return {  
        'AWS::AutoScaling::AutoScalingGroup': AutoScalingGroup,  
        'OS::Heat::InstanceGroup': InstanceGroup,  
        'OS::Heat::AutoScalingGroup': AutoScalingResourceGroup,  
    }
因此接着上面的代碼看,這裏的res_name和res_class就是resource_mapping或者available_mapping返回的字

典的內容,而後往以前的env對象中註冊該資源.

def _register_resources(env, type_pairs):  
    for res_name, res_class in type_pairs:  
        env.register_class(res_name, res_class)  
def register_class(self, resource_type, resource_class):  
        ri = ResourceInfo(self, [resource_type], resource_class)  
        self._register_info([resource_type], ri)

這裏首先根據咱們傳入的資源建立一個合適的資源類,而後註冊到env中,直至把module中的資源加載完成。

加載constraints的過程也和resource相似,這裏不繼續展開。


當env加載完各類插件後,回到最以前的代碼會把這個global_env對象賦值給全局變量_environment,

def initialise():  
    global _environment  
    if _environment is not None:  
        return  
  
    clients.initialise()  
  
    global_env = environment.Environment({}, user_env=False)  
    _load_global_environment(global_env)  
    _environment = global_env

添加自定義的資源到heat中

如今咱們在resource目錄下自定義一個本身的插件,按照上面的分析,能夠這麼寫,讓heat可以正確加載咱們的插件

from heat.engine import resource  
  
  
class MyResource(resource.Resource):  	
        def handle_create(self):
		pass
	def handle_update(self):
		pass
	def handle_delete(self):
		pass
      
  
def available_resource_mapping():  
    return {'OS::Openstack::MyRSC': MyResource}  

#咱們在加載資源插件以後加一句打印代碼
def _load_global_resources(env):  
    _register_constraints(env, _get_mapping('heat.constraints'))  
    _register_stack_lifecycle_plugins(  
        env,  
        _get_mapping('heat.stack_lifecycle_plugins'))  
  
    manager = plugin_manager.PluginManager(__name__)  
    # Sometimes resources should not be available for registration in Heat due  
    # to unsatisfied dependencies. We look first for the function  
    # 'available_resource_mapping', which should return the filtered resources.  
    # If it is not found, we look for the legacy 'resource_mapping'.  
    resource_mapping = plugin_manager.PluginMapping(['available_resource',  
                                                     'resource'])  
    constraint_mapping = plugin_manager.PluginMapping('constraint')  
  
    _register_resources(env, resource_mapping.load_all(manager))  
  
    _register_constraints(env, constraint_mapping.load_all(manager))  
    print 'OS::Openstack::MyRSC' in env.registry._registry  

能夠發現打印爲True,表明咱們已經成功加載咱們自定義的插件了.
相關文章
相關標籤/搜索