Ironic Deploy流程分析
创建物理机实例
使用openstack命令创建物理机,进入deploy阶段,其中--user-data
、--image
、--flavor
、--nic
等参数根据实际情况进行传值
1 | root@ironic-stein:~# openstack server create --config-drive true --user-data ./user-data.txt \ |
代码流程
-> ironic.conductor.manager:ConductorManager.do_node_deploy
- 设置event为’deploy’
- 验证参数(power.validate、deploy.validate、boot.validate、validate_instance_info_traits、validate_deploy_templates)
- 调用ironic.conductor.manager.do_node_deploy
-> ironic.conductor.manager.do_node_deploy
- 如果有configdrive,设置configdrive
- 调用ironic.drivers.modules.iscsi_deploy:ISCSIDeploy.prepare
- 获取部署的步骤,并将步骤信息存入driver_internal_info[‘deploy_steps’]
- 调用ironic.conductor.manager._do_next_deploy_step
- 在fsm为’wait’事件预订状态,根据fsm的当前状态(fsm.current_state)更新node的provision_state,根据fsm的目标状态(fsm.target_state)更新node的target_provision_state
- 此时日志:Node 83fccb58-0998-4a1f-a0d7-e6f5066dd922 moved to provision state “deploying” from state “wait call-back”; target provision state is “active”
-> ironic.conductor.manager._do_next_deploy_step
- 从部署步骤中找到对应task.driver.deploy的deploy函数,调用该函数,此处调用ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.deploy
-> ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.deploy
- 缓存用户镜像到/tftpboot/master_images/目录下,以node的uuid命名
- 通过ipmitool重启服务器
- 返回状态’wait call-back’
-> ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.prepare
- 通过ipmitool开机
- 去除租户网络配置,根据ironic中port、portgroup的internal_info的’tenant_vif_port_id’或者extra的’vif_port_id’判断是否有关联租户网络,更新以下内容至neutron的port中(此处调用neutron的update_port接口):
1
2body_unbind = {'port': {'binding:host_id': '', 'binding:profile': {}}}
body_reset_mac = {'port': {'mac_address': None}} - 向node添加provision网络,先清理已存在的provision网卡(此处调用neutron的list_ports、delete_port接口),使用body的内容创建pxe_enabled_port(此处调用neutron的create_port接口),body内容为:更新ironic中该port的internal_info,设置’provisioning_vif_port_id’为neutron中的port的id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18body = {
'port': {
'network_id': 'f5988291-5e39-499c-9c0d-7da6aef895d7',
'admin_state_up': True,
'binding:vnic_type': 'baremetal',
'device_owner': 'baremetal:none',
'binding:host_id': '83fccb58-0998-4a1f-a0d7-e6f5066dd922',
'security_groups': ['5d492102-4a2d-421c-b1ba-b98ed0aaec4f'],
# instance的uuid
'device_id': 'e54b4514-c22d-4432-9ca7-07c2704eadb4',
# 该物理机port的mac地址(此处为虚构)
'mac_address': '11:af:6f:52:34:48',
'binding:profile': {
# 该物理机port对应的交换机lldp信息(此处为虚构)
'local_link_information': [{'switch_id': '8a:d9:31:e1:fc:b1', 'port_id': 'GigabitEthernet2/0/43'}]
}
}
} - 调用ironic.drivers.modules.pxe.PXEBoot.prepare_ramdisk,传入参数为deploy_opts,deploy_opts内容为:
1
deploy_opts = {'ipa-api-url': get_ironic_api_url(), 'coreos.configdrive': 0}
-> ironic.drivers.modules.pxe.PXEBoot.prepare_ramdisk
- 更新dhcp_options至neutron的port中(此处调用neutron的update_port接口),dhcp_options内容为:
1
2
3
4
5
6
7dhcp_opts = [
{'opt_name': '67', 'opt_value': 'pxelinux.0', 'ip_version': 4},
{'opt_name': '210', 'opt_value': '/tftpboot', 'ip_version': 4},
{'opt_name': '66', 'opt_value': '192.168.0.157', 'ip_version': 4},
{'opt_name': '150', 'opt_value': '192.168.0.157', 'ip_version': 4},
{'opt_name': 'server-ip-address', 'opt_value': '192.168.0.157', 'ip_version': 4}
] - 生成pxe_opts,内容为:
1
2
3
4
5
6
7pxe_opts = {
'aki_path': 'deploy_kernel',
'ari_path': 'deploy_ramdisk',
'pxe_append_params': CONF.pxe.pxe_append_params+=' ipa-debug=1',
'tftp_server': '192.168.0.157',
'ipxe_timeout': 0
} - 根据pxe_opts和ironic.drivers.modules.pxe_config.template模板,生成pxe的config文件
- 通过ipmitool设置为pxe启动方式,并且根据driver_info中的force_persistent_boot_device参数设置是否要持久化启动方式
- 从Glance缓存镜像到本地的/tftpboot目录
-> ironic_python_agent.agent.IronicPythonAgent.run
- 获取hardware_manager,此处为ironic_python_agent.hardware:GenericHardwareManager
- 从/sys/class/net下获取网卡设备,并逐个获取ipv4及ipv6地址、运行状态、vendor id、product id、biosdevname,并等待至少一个网卡启动
- 调用ironic的/lookup接口,传入参数为hardware_info,node_uuid,hardware_info内容为:
1
2
3
4
5
6
7
8
9hardware_info = {
'interfaces': hardware_manager.list_network_interfaces(),
'cpu': hardware_manager.get_cpus(),
'disks': hardware_manager.list_block_devices(),
'memory': hardware_manager.get_memory(),
'bmc_address': hardware_manager.get_bmc_address(),
'system_vendor': hardware_manager.get_system_vendor_info(),
'boot': hardware_manager.get_boot_info()
} - 经过lookup接口验证后,缓存node信息,调用ironic_python_agent.agent.IronicPythonAgent.serve_ipa_api函数
-> ironic_python_agent.agent.IronicPythonAgent.serve_ipa_api
- 启动WSGI server,监听本地9999端口
- 调用ironic_python_agent.IronicPythonAgentHeartbeater.start函数启动线程,执行ironic_python_agent.IronicPythonAgentHeartbeater.run函数
-> ironic_python_agent.IronicPythonAgentHeartbeater.run
- 定时执行ironic_python_agent.IronicPythonAgentHeartbeater.do_heartbeat函数,调用ironic的/heartbeat接口,即ironic.conductor.manager.ConductorManager.heartbeat函数
-> ironic.conductor.manager.ConductorManager.heartbeat
- 调用ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.heartbeat,因ISCSIDeploy集成了HeartbeatMixin类,所以实际调用的是ironic.drivers.modules.agent_base_vendor.HeartbeatMixin.heartbeat
-> ironic.drivers.modules.agent_base_vendor.HeartbeatMixin.heartbeat
- 判断此时node的状态,仅在’wait call-back’、’clean wait’、’rescue wait’时可继续执行
- 更新driver_internal_info信息,更新内容为:
1
driver_internal_info = {'agent_url': "http://192.168.0.171:9999", 'agent_version': "4.0.1.dev12", 'agent_last_heartbeat': "2019-08-06T06:33:06.212339"}
- 判断当前状态,此时如未开始部署,则调用ironic.drivers.modules.iscsi_deploy.AgentDeployMixin.continue_deploy函数
-> ironic.drivers.modules.iscsi_deploy.AgentDeployMixin.continue_deploy
- 在fsm为’resume’事件预订状态,根据fsm的当前状态(fsm.current_state)更新node的provision_state,根据fsm的目标状态(fsm.target_state)更新node的target_provision_state
- 调用ironic_python_agent的接口,将物理机的磁盘通过iscsi协议挂载到ironic节点,使用dd命令传输用户镜像
- 调用ironic.drivers.modules.pxe.PXEBoot.prepare_instance准备主机
-> ironic.drivers.modules.pxe.PXEBoot.prepare_instance
- 根据配置获取到当前boot_option为’local’,通过ipmitool设置为disk启动方式,并且根据driver_info中的force_persistent_boot_device参数设置是否要持久化启动方式
- 收集ramdisk日志到/var/log/ironic/deploy目录下
- 调用ironic.drivers.modules.agent_base_vendor.AgentDeployMixin.reboot_and_finish_deploy函数
-> ironic.drivers.modules.agent_base_vendor.AgentDeployMixin.reboot_and_finish_deploy
- 关闭物理服务器
- 清理已存在的provision网卡(此处调用neutron的list_ports、delete_port接口)
- 配置租户网络信息,更新以下内容至neutron的port中(此处调用neutron的update_port接口):
1
2
3
4
5
6
7
8
9
10
11
12
13
14body_unbind = {'port': {'binding:vnic_type': baremetal, 'binding:host_id': node.uuid, 'mac_address': port_like_obj.address, 'binding:profile': {'local_link_information': local_link_info}}}
body_reset_mac = {'port': {'mac_address': None}}
body = {
'port': {
'binding:vnic_type': 'baremetal',
'binding:host_id': '83fccb58-0998-4a1f-a0d7-e6f5066dd922',
# 此处为provision网卡的mac地址(此处为虚构)
'mac_address': 'd4:02:ec:71:a1:a2',
'binding:profile': {
# 此处为provision网卡的lldp信息(此处为虚构)
'local_link_information': [{'switch_id': '84:d9:31:ec:fc:b2', 'port_id': 'GigabitEthernet2/0/31'}]
}
}
} - 开启物理服务器