IPA使用lookup和hearteat机制与Ironic Conductor进行交互,启动时agent给Conductor的vendor_passthru lookup endpoint(地址为/v1/drivers/{driver}/vendor_passthru/lookup)发送一个硬件的profile,
然后Ironic就可以得出该节点的uuid,在成功发现该节点之后,agent隔N秒发送心跳包给Conductor(hearteat地址为/v1/nodes/{node_ident}/vendor_passthru/heartbeat ),conductor执行一系列动作,包括查询已经运行的命令的状态,启动带内清洁任务或将镜像部署到节点。
#主函数入口,定义了IronicPythonAgent类的对象, 初始化它,并调用该对象的run()函数. #机器上电后,ramdisk在内存中执行,然后启动IPA,入口为cmd.agent.run(), #然后调用ironic-python-agent.agent.run(),其代码如下: def run(self): """Run the Ironic Python Agent.""" # Get the UUID so we can heartbeat to Ironic. Raises LookupNodeError # if there is an issue (uncaught, restart agent) self.started_at = _time() #运行ipa,获取node的uuid,发送心跳包 # Cached hw managers at runtime, not load time. See bug 1490008. hardware.load_managers() #调用hardwarre.load_managers加载硬件到缓存,以优先级顺序获取硬件管理器的列表。 # Operator-settable delay before hardware actually comes up. # Helps with slow RAID drivers - see bug 1582797. if self.hardware_initialization_delay > 0: LOG.info('Waiting %d seconds before proceeding', self.hardware_initialization_delay) time.sleep(self.hardware_initialization_delay) #该函数中会判断是否为standalone模式启动,如果不是,则会向ironic-api查询当前节点的信息,比如uuid等: if not self.standalone: # Inspection should be started before call to lookup, otherwise # lookup will fail due to unknown MAC. uuid = None if cfg.CONF.inspection_callback_url: uuid = inspector.inspect() #利用Ironic API给Condutor发送lookup()请求,用户获取UUID,相当于自发现 if self.api_url: self._wait_for_interface() #此处会调取ironic_python_agent.agent._wait_for_interface函数, #通过ironic_python_agent.hardware.dispatch_to_managers获取网卡信息 content = self.api_client.lookup_node( #调用到ironic_python_agent.ironic_api_client.lookup_node的_do_lookup(), #然后发送一个GET /{api_version}/drivers/{driver}/vendor_passthru/lookup请求。 #Condutor API在接受到lookup请求后调用指定驱动的lookup函数处理,返回节点UUID。 hardware_info=hardware.dispatch_to_managers( 'list_hardware_info'), #调取ironic_python_agent.hardware.dispatch_to_managers获取硬件信息列表 timeout=self.lookup_timeout, starting_interval=self.lookup_interval, node_uuid=uuid) LOG.debug('Received lookup results: %s', content) self.node = content['node'] LOG.info('Lookup succeeded, node UUID is %s', self.node['uuid']) hardware.cache_node(self.node) self.heartbeat_timeout = content['config']['heartbeat_timeout'] # Update config with values from Ironic config = content.get('config', {}) if config.get('metrics'): for opt, val in config.items(): setattr(cfg.CONF.metrics, opt, val) if config.get('metrics_statsd'): for opt, val in config.items(): setattr(cfg.CONF.metrics_statsd, opt, val) elif cfg.CONF.inspection_callback_url: LOG.info('No ipa-api-url configured, Heartbeat and lookup ' 'skipped for inspector.') else: LOG.error('Neither ipa-api-url nor inspection_callback_url' 'found, please check your pxe append parameters.') self.serve_ipa_api()#调取ironic_python_agent.agent.serve_ipa_api,创建一个wsgi的server服务对象,发送心跳包 if not self.standalone and self.api_url: self.heartbeater.stop() #部署完成后停止心跳包
但谈何容易。