openstack-裸金属ironic源码创建分析

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会直接进去系统,