Ironic Deploy流程分析

创建物理机实例

使用openstack命令创建物理机,进入deploy阶段,其中--user-data--image--flavor--nic等参数根据实际情况进行传值

1
2
3
root@ironic-stein:~# openstack server create --config-drive true --user-data ./user-data.txt \
--image 0091d9f6-3b74-4b1b-8e25-f6a98769eeda --flavor 544d47bd-175d-45fc-aae8-9c1fccc4bc6b \
--nic port-id=a2d6ea8e-8d6a-41ea-a8ef-e955866ef80d node1

代码流程

-> ironic.conductor.manager:ConductorManager.do_node_deploy

  1. 设置event为’deploy’
  2. 验证参数(power.validate、deploy.validate、boot.validate、validate_instance_info_traits、validate_deploy_templates)
  3. 调用ironic.conductor.manager.do_node_deploy

-> ironic.conductor.manager.do_node_deploy

  1. 如果有configdrive,设置configdrive
  2. 调用ironic.drivers.modules.iscsi_deploy:ISCSIDeploy.prepare
  3. 获取部署的步骤,并将步骤信息存入driver_internal_info[‘deploy_steps’]
  4. 调用ironic.conductor.manager._do_next_deploy_step
  5. 在fsm为’wait’事件预订状态,根据fsm的当前状态(fsm.current_state)更新node的provision_state,根据fsm的目标状态(fsm.target_state)更新node的target_provision_state
  6. 此时日志: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

  1. 从部署步骤中找到对应task.driver.deploy的deploy函数,调用该函数,此处调用ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.deploy

-> ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.deploy

  1. 缓存用户镜像到/tftpboot/master_images/目录下,以node的uuid命名
  2. 通过ipmitool重启服务器
  3. 返回状态’wait call-back’

-> ironic.drivers.modules.iscsi_deploy.ISCSIDeploy.prepare

  1. 通过ipmitool开机
  2. 去除租户网络配置,根据ironic中port、portgroup的internal_info的’tenant_vif_port_id’或者extra的’vif_port_id’判断是否有关联租户网络,更新以下内容至neutron的port中(此处调用neutron的update_port接口):
    1
    2
    body_unbind = {'port': {'binding:host_id': '', 'binding:profile': {}}}
    body_reset_mac = {'port': {'mac_address': None}}
  3. 向node添加provision网络,先清理已存在的provision网卡(此处调用neutron的list_ports、delete_port接口),使用body的内容创建pxe_enabled_port(此处调用neutron的create_port接口),body内容为:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    body = {
    '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中该port的internal_info,设置’provisioning_vif_port_id’为neutron中的port的id
  4. 调用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

  1. 更新dhcp_options至neutron的port中(此处调用neutron的update_port接口),dhcp_options内容为:
    1
    2
    3
    4
    5
    6
    7
    dhcp_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}
    ]
  2. 生成pxe_opts,内容为:
    1
    2
    3
    4
    5
    6
    7
    pxe_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
    }
  3. 根据pxe_opts和ironic.drivers.modules.pxe_config.template模板,生成pxe的config文件
  4. 通过ipmitool设置为pxe启动方式,并且根据driver_info中的force_persistent_boot_device参数设置是否要持久化启动方式
  5. 从Glance缓存镜像到本地的/tftpboot目录

-> ironic_python_agent.agent.IronicPythonAgent.run

  1. 获取hardware_manager,此处为ironic_python_agent.hardware:GenericHardwareManager
  2. 从/sys/class/net下获取网卡设备,并逐个获取ipv4及ipv6地址、运行状态、vendor id、product id、biosdevname,并等待至少一个网卡启动
  3. 调用ironic的/lookup接口,传入参数为hardware_info,node_uuid,hardware_info内容为:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    hardware_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()
    }
  4. 经过lookup接口验证后,缓存node信息,调用ironic_python_agent.agent.IronicPythonAgent.serve_ipa_api函数

-> ironic_python_agent.agent.IronicPythonAgent.serve_ipa_api

  1. 启动WSGI server,监听本地9999端口
  2. 调用ironic_python_agent.IronicPythonAgentHeartbeater.start函数启动线程,执行ironic_python_agent.IronicPythonAgentHeartbeater.run函数

-> ironic_python_agent.IronicPythonAgentHeartbeater.run

  1. 定时执行ironic_python_agent.IronicPythonAgentHeartbeater.do_heartbeat函数,调用ironic的/heartbeat接口,即ironic.conductor.manager.ConductorManager.heartbeat函数

-> ironic.conductor.manager.ConductorManager.heartbeat

  1. 调用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

  1. 判断此时node的状态,仅在’wait call-back’、’clean wait’、’rescue wait’时可继续执行
  2. 更新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"}
  3. 判断当前状态,此时如未开始部署,则调用ironic.drivers.modules.iscsi_deploy.AgentDeployMixin.continue_deploy函数

-> ironic.drivers.modules.iscsi_deploy.AgentDeployMixin.continue_deploy

  1. 在fsm为’resume’事件预订状态,根据fsm的当前状态(fsm.current_state)更新node的provision_state,根据fsm的目标状态(fsm.target_state)更新node的target_provision_state
  2. 调用ironic_python_agent的接口,将物理机的磁盘通过iscsi协议挂载到ironic节点,使用dd命令传输用户镜像
  3. 调用ironic.drivers.modules.pxe.PXEBoot.prepare_instance准备主机

-> ironic.drivers.modules.pxe.PXEBoot.prepare_instance

  1. 根据配置获取到当前boot_option为’local’,通过ipmitool设置为disk启动方式,并且根据driver_info中的force_persistent_boot_device参数设置是否要持久化启动方式
  2. 收集ramdisk日志到/var/log/ironic/deploy目录下
  3. 调用ironic.drivers.modules.agent_base_vendor.AgentDeployMixin.reboot_and_finish_deploy函数

-> ironic.drivers.modules.agent_base_vendor.AgentDeployMixin.reboot_and_finish_deploy

  1. 关闭物理服务器
  2. 清理已存在的provision网卡(此处调用neutron的list_ports、delete_port接口)
  3. 配置租户网络信息,更新以下内容至neutron的port中(此处调用neutron的update_port接口):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    body_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'}]
    }
    }
    }
  4. 开启物理服务器