heat 更新stack的代碼調用分析heat stack-update


heat中與nova中有個地方不太相同,在nova中,RPC發送後是由nova-manager來接收,對應的文件在nova/compute/manager.py;在heat中,RPC發送後是由 heat-engine來接收,對應的文件在heat/engine/service.py.
Heat/api/openstack/v1/stacks.py
    @util.identified_stack
    def update(self, req, identity, body):
        """Update an existing stack with a new template and/or parameters."""
        data = InstantiationData(body)  

        args = self.prepare_args(data)  
        self.rpc_client.update_stack(   
            req.context,       
            identity,          
            data.template(),   
            data.environment(),
            data.files(),      
            args,
            environment_files=data.environment_files())

        raise exc.HTTPAccepted()

Heat/rpc/client.py
    def update_stack(self, ctxt, stack_identity, template, params,
                     files, args, environment_files=None):
        """Updates an existing stack based on the provided template and params.

        Note that at this stage the template has already been fetched from the
        heat-api process if using a template-url.

        :param ctxt: RPC context.
        :param stack_name: Name of the stack you want to create.
        :param template: Template of stack you want to create.
        :param params: Stack Input Params/Environment
        :param files: files referenced from the environment.
        :param args: Request parameters/args passed from API
        :param environment_files: optional ordered list of environment file
               names included in the files dict
        :type  environment_files: list or None
        """
        return self.call(ctxt, 
                         self.make_msg('update_stack',   
                                       stack_identity=stack_identity,  
                                       template=template,              
                                       params=params,                  
                                       files=files,                    
                                       environment_files=environment_files,
                                       args=args),                     
                         version='1.23')  
Heat/enigne/service.py
主要看如下代碼的調用,從回調可以知道,後面將會執行current_stack.update這個函數,實
際上他就是stack.update(heat/engine/stack.py)
            th = self.thread_group_mgr.start_with_lock(cnxt, current_stack,
                                                       self.engine_id,
                                                       current_stack.update,
                                                       updated_stack,
                                                       event=event)
    @context.request_context
    def update_stack(self, cnxt, stack_identity, template, params,
                     files, args, environment_files=None):
        """Update an existing stack based on the provided template and params.

        Note that at this stage the template has already been fetched from the
        heat-api process if using a template-url.

        :param cnxt: RPC context.
        :param stack_identity: Name of the stack you want to create.
        :param template: Template of stack you want to create.
        :param params: Stack Input Params
        :param files: Files referenced from the template
        :param args: Request parameters/args passed from API
        :param environment_files: optional ordered list of environment file
               names included in the files dict
        :type  environment_files: list or None
        """
		(省略掉部分代碼,來看關鍵部分)
        if current_stack.convergence:   
            current_stack.thread_group_mgr = self.thread_group_mgr
            current_stack.converge_stack(template=tmpl,
                                         new_stack=updated_stack)
        else:
            event = eventlet.event.Event()
            th = self.thread_group_mgr.start_with_lock(cnxt, current_stack,
                                                       self.engine_id,
                                                       current_stack.update,
                                                       updated_stack,
                                                       event=event)
Heat/engine/stack.py
同調用heat stack-create,這裏也是創建了一個TaskRunner的對象,然後調用self.update_task(這個函數是在
Heat/engine/stack.py中定義,可以回顧一下heat stack-create中的stack_task()這個函數。)
    @profiler.trace('Stack.update', hide_args=False)
    @reset_state_on_error      
    def update(self, newstack, event=None):
        """Update the stack.   

        Compare the current stack with newstack,
        and where necessary create/update/delete the resources until
        this stack aligns with newstack.

        Note update of existing stack resources depends on update
        being implemented in the underlying resource types

        Update will fail if it exceeds the specified timeout. The default is
        60 minutes, set in the constructor
        """
        LOG.debug("Jeffrey: stack.update")
        self.updated_time = oslo_timeutils.utcnow()
        updater = scheduler.TaskRunner(self.update_task, newstack,
                                       event=event)                    
        updater()
接下來我們看一下self.update_task(),同樣我們省略部分代碼只看關鍵代碼。
先是聲明一個StackUpdata的對象update_task,然後這個對象被TaskRunner()調用。
Heat/engine/stack.py
   @scheduler.wrappertask
    def update_task(self, newstack, action=UPDATE, event=None):
        try:

            update_task = update.StackUpdate(
                self, newstack, backup_stack,
                rollback=action == self.ROLLBACK,
                error_wait_time=cfg.CONF.error_wait_time)
            updater = scheduler.TaskRunner(update_task)

            try:
                updater.start(timeout=self.timeout_secs())
                yield
                while not updater.step():
                    if event is None or not event.ready():
                        yield
                    else:
                        message = event.wait()
                        self._message_parser(message)
在scheduler.TaskRunner初始化的時候,self.task的值被賦爲update.StackUpdate()類型的變量

接下來去heat/engine/scheduler.py中看一下start()方法的實現, 我們可以發現,heat engine通過 start()->step()->run_to_completion()這三個函數實現了循環,然後藉助yield 及裝飾器wrappertask()實現了迭代。

    def start(self, timeout=None):  
        """Initialise the task and run its first step.

        If a timeout is specified, any attempt to step the task after that
        number of seconds has elapsed will result in a Timeout being
        raised inside the task.
        """
        assert self._runner is None, "Task already started" 
        assert not self._done, "Task already cancelled"
        LOG.debug("Jeffrey: scheduler.TaskRunner.start")

        LOG.debug('%s starting' % six.text_type(self))

        if timeout is not None:
            self._timeout = Timeout(self, timeout)

        result = self._task(*self._args, **self._kwargs)
        if isinstance(result, types.GeneratorType):
		   #注意此處的代碼,調用step ,把subtask進行分解成一個個的step進行執行。
            self._runner = result           
            self.step()        
        else:
            self._runner = False            
            self._done = True  
            LOG.debug('%s done (not resumable)' % six.text_type(self))

    def step(self):            
        """Run another step of the task.

        Return True if the task is complete; False otherwise.
        """
        LOG.debug("Jeffrey: scheduler.TaskRunner.step: self.done()=%s" % self.done())
        if not self.done():
            assert self._runner is not None, "Task not started"

            if self._timeout is not None and self._timeout.expired():
                LOG.info(_LI('%s timed out'), six.text_type(self))
                self._done = True

                self._timeout.trigger(self._runner)
            else:
                LOG.debug('%s running' % six.text_type(self))

                try:
				#此處執行self._runner這個對象的__call__方法,也就是update.StackUpdate的__call__()。它定義在:
				#heat/engine/update.py
                    LOG.debug("Jeffrey: scheduler.TaskRunner.step: next(self._runner)=%s" % next(self._runner))
                    next(self._runner)
                except StopIteration:
                    self._done = True
                    LOG.debug('%s complete' % six.text_type(self))

        return self._done

    def run_to_completion(self, wait_time=1):
        """Run the task to completion.

        The task will sleep for `wait_time` seconds between steps. To avoid
        sleeping, pass `None` for `wait_time`.
        """
        while not self.step():
            self._sleep(wait_time)

在step中的我們看到了next(self._runner)這樣的代碼,其執行是的是StackUpdater中的__call__().
注意cleanup_prev和self.updater這兩個DependencyTaskGroup類型的對象。前者的回調函數是
Self._remove_backup_resource,後者的回調是self._resource_update.
Heat/engine/update.py
    @scheduler.wrappertask     
    def __call__(self):        
        """Return a co-routine that updates the stack."""

        cleanup_prev = scheduler.DependencyTaskGroup(
            self.previous_stack.dependencies,
            self._remove_backup_resource,   
            reverse=True)      

        self.updater = scheduler.DependencyTaskGroup(
            self.dependencies(),            
            self._resource_update,          
            error_wait_time=self.error_wait_time)

        if not self.rollback:  
            LOG.debug("Jeffrey in stack.StackUpdate: run cleanup_prev()")
            yield cleanup_prev()        

        try:
            LOG.debug("Jeffrey in stack.StackUpdate: run updater()")
            yield self.updater()        
        finally:               
            self.previous_stack.reset_dependencies()

找到回調函數後,我們的繼續看self._resource_update這個函數,可以發現對於已存在的資源和沒有存在的資源的
更新,heat分別調用的是不同的接口來實現的。
Heat/engine/update.py
    def _resource_update(self, res):    
        if res.name in self.new_stack and self.new_stack[res.name] is res:
            return self._process_new_resource_update(res)
        else:                  
            return self._process_existing_resource_update(res)


後面的就不再接着分析了…………


發佈了91 篇原創文章 · 獲贊 37 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章