openstack ironic 裸金属创建裸机流程源码分析
基于 ironic stein
ironic-api接收到了provision_state的设置请求,然后返回202的异步请求,api请求到了ironic.api.controllers.v1.node.NodeStatesController的provision方法, 然后调用了 _do_provision_action 方法 。
1 2 3 4 5 6 7 8 9 10 @METRICS.timer('NodeStatesController.provision') @expose.expose(None, types.uuid_or_name, wtypes.text, types.jsontype, types.jsontype, wtypes.text, status_code=http_client.ACCEPTED) def provision(self, node_ident, target, configdrive=None, clean_steps=None, rescue_password=None): *************** self._do_provision_action(rpc_node, target, configdrive, clean_steps, rescue_password)
1 2 3 4 5 6 7 8 9 10 def _do_provision_action(self, rpc_node, target, configdrive=None, clean_steps=None, rescue_password=None): ************ if target in (ir_states.ACTIVE, ir_states.REBUILD): rebuild = (target == ir_states.REBUILD) pecan.request.rpcapi.do_node_deploy(context=pecan.request.context, node_id=rpc_node.uuid, rebuild=rebuild, configdrive=configdrive, topic=topic)
然后RPC调用的ironic.condutor.manager.ConductorManager.do_node_deploy方法, 最终启动一个task ,调用 ironic.condutor.manager.do_node_deploy方法, 如果配置了configdrive会传至swift接口,然后调用 task.driver.deploy.prepare(task) 过程, 不同驱动的动作不一样 pxe_* 驱动使用的是iscsi_deploy.ISCSIDeploy.prepare,本地用的 task.driver.deploy.deploy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @METRICS.timer('ConductorManager.do_node_deploy') @messaging.expected_exceptions(exception.NoFreeConductorWorker, exception.NodeLocked, exception.NodeInMaintenance, exception.InstanceDeployFailure, exception.InvalidStateRequested, exception.NodeProtected) def do_node_deploy(self, context, node_id, rebuild=False, configdrive=None): ******** #状态相关检查,确保裸机参数及电源正常 try: task.driver.power.validate(task) task.driver.deploy.validate(task) utils.validate_instance_info_traits(task.node) conductor_steps.validate_deploy_templates(task) except exception.InvalidParameterValue as e: raise exception.InstanceDeployFailure( _("Failed to validate deploy or power info for node " "%(node_uuid)s. Error: %(msg)s") % {'node_uuid': node.uuid, 'msg': e}, code=e.code) LOG.debug("do_node_deploy Calling event: %(event)s for node: " "%(node)s", {'event': event, 'node': node.uuid}) try: task.process_event( event, callback=self._spawn_worker, call_args=(do_node_deploy, task, self.conductor.id, configdrive), err_handler=utils.provisioning_error_handler) except exception.InvalidState: raise exception.InvalidStateRequested( action=event, node=task.node.uuid, state=task.node.provision_state)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @METRICS.timer('do_node_deploy') @task_manager.require_exclusive_lock def do_node_deploy(task, conductor_id=None, configdrive=None): """Prepare the environment and deploy a node.""" node = task.node try: if configdrive: if isinstance(configdrive, dict): configdrive = utils.build_configdrive(node, configdrive) _store_configdrive(node, configdrive) ****************** try: task.driver.deploy.prepare(task)
如果是iSCSI启动,task.driver.deploy.prepare(task) 调用 iscsi_deploy.py 的 prepare(task), 初次调用 task.driver.boot.prepare_instance(task) , 配置dhcp ,准备pxe config和缓存镜像, 通过ipmi操控bios配置启动设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ***** pxe_utils.cache_ramdisk_kernel(task, instance_image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) ***** pxe_utils.prepare_instance_pxe_config( task, instance_image_info, iscsi_boot=deploy_utils.is_iscsi_boot(task), ramdisk_boot=(boot_option == "ramdisk"), ipxe_enabled=CONF.pxe.ipxe_enabled) ***** # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) ***** manager_utils.node_set_boot_device(task, boot_device, persistent=persistent)
默认本地启动调用 ironic.drivers.modules.pxe.PXERamdiskDeploy.prepare中会检查boot_option是否正确, 然后调用 populate_storage_driver_internal_info,如果使用iSCSI启动,保存boot的volume 信息至internal_info , 最终调用 调用 ironic.drivers.modules.pxe.PXEBoot.prepare_instance 启动部署
1 2 3 4 5 6 7 8 9 @METRICS.timer('RamdiskDeploy.prepare') @task_manager.require_exclusive_lock def prepare(self, task): ***** deploy_utils.populate_storage_driver_internal_info(task) ****** if node.provision_state in (states.ACTIVE, states.UNRESCUING): # In the event of takeover or unrescue. task.driver.boot.prepare_instance(task)
调用 ironic.drivers.modules.pxe.PXEBoot.prepare_instance, 配置dhcp ,准备pxe config和缓存镜像, 通过ipmi操控bios配置启动设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 **** #缓存镜像到本地 pxe_utils.cache_ramdisk_kernel(task, instance_image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) **** #为当前裸机准备 单独的 pxe 文件 pxe_utils.prepare_instance_pxe_config( task, instance_image_info, iscsi_boot=deploy_utils.is_iscsi_boot(task), ramdisk_boot=(boot_option == "ramdisk"), ipxe_enabled=CONF.pxe.ipxe_enabled) boot_device = boot_devices.PXE ***** #因pxe启动,需update ironic DHCP配置文件配置pxe,bootfile等信息,本地启动时清理pxe文件更改启动设备为磁盘 # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) **** # manager_utils.node_set_boot_device(task, boot_device, persistent=persistent) **** #开始部署 _do_next_deploy_step(task, 0, conductor_id)
调用task.deploy
1 2 3 ***** result = interface.execute_deploy_step(task, step)
如果是iSCSI启动直接调用了 iscsi_deploy.ISCSIDeploy.deploy, cinder盘,不需要缓存镜像,所以,关机,切换租户网络,开机
1 2 3 4 5 6 7 8 9 10 11 ***** manager_utils.node_power_action(task, states.POWER_OFF) power_state_to_restore = ( manager_utils.power_on_node_if_needed(task)) task.driver.network.remove_provisioning_network(task) task.driver.network.configure_tenant_networks(task) manager_utils.restore_power_state_if_needed( task, power_state_to_restore) task.driver.boot.prepare_instance(task) manager_utils.node_power_action(task, states.POWER_ON)
本地盘 调用pxe.PXEBoot.deploy启动deploy会关机,配置租户网络, 再开机
1 2 3 4 5 6 ***** manager_utils.node_power_action(task, states.POWER_OFF) ****** task.driver.network.configure_tenant_networks(task) ***** manager_utils.node_power_action(task, states.POWER_ON)
至此deploy完,本地盘会进入ramdisk, iSCSI启动不走ramdisk会直接进去系统,