def test_update_dhcp_some_failures(self, mock_gnvi, mock_updo): # confirm update is called twice, one fails, but no exception raised mock_gnvi.return_value = { 'ports': { 'p1': 'v1', 'p2': 'v2' }, 'portgroups': {} } exc = exception.FailedToUpdateDHCPOptOnPort('fake exception') mock_updo.side_effect = [None, exc] with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory() api.update_dhcp(task, self.node) mock_gnvi.assert_called_once_with(task) self.assertEqual(2, mock_updo.call_count)
def test_create_cleaning_ports_fail(self, create_mock, rollback_mock): # Check that if creating a port fails, the ports are cleaned up create_mock.side_effect = neutron_client_exc.ConnectionFailed api = dhcp_factory.DHCPFactory().provider with task_manager.acquire(self.context, self.node.uuid) as task: self.assertRaises(exception.NodeCleaningFailure, api.create_cleaning_ports, task) create_mock.assert_called_once_with({ 'port': { 'network_id': '00000000-0000-0000-0000-000000000000', 'admin_state_up': True, 'mac_address': self.ports[0].address } }) rollback_mock.assert_called_once_with(task)
def _test__get_ip_addresses_portgroup(self, key, mock_gfia): if key == "extra": kwargs1 = {key: {'vif_port_id': 'test-vif-A'}} else: kwargs1 = {key: {'tenant_vif_port_id': 'test-vif-A'}} ip_address = '10.10.0.1' expected = [ip_address] pg = object_utils.create_test_portgroup( self.context, node_id=self.node.id, address='aa:bb:cc:dd:ee:ff', uuid=uuidutils.generate_uuid(), **kwargs1) mock_gfia.return_value = ip_address with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory().provider result = api._get_ip_addresses(task, [pg], mock.sentinel.client) self.assertEqual(expected, result)
def test_create_cleaning_ports(self, create_mock): # Ensure we can create cleaning ports for in band cleaning create_mock.return_value = {'port': self.neutron_port} expected = {self.ports[0].uuid: self.neutron_port['id']} api = dhcp_factory.DHCPFactory().provider with task_manager.acquire(self.context, self.node.uuid) as task: ports = api.create_cleaning_ports(task) self.assertEqual(expected, ports) create_mock.assert_called_once_with({ 'port': { 'network_id': '00000000-0000-0000-0000-000000000000', 'admin_state_up': True, 'mac_address': self.ports[0].address } })
def test_get_ip_addresses_for_port_and_portgroup(self, get_ip_mock, client_mock): object_utils.create_test_portgroup( self.context, node_id=self.node.id, address='aa:bb:cc:dd:ee:ff', uuid=uuidutils.generate_uuid(), internal_info={'tenant_vif_port_id': 'test-vif-A'}) with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory().provider api.get_ip_addresses(task) get_ip_mock.assert_has_calls( [mock.call(mock.ANY, task, task.ports[0], client_mock.return_value), mock.call(mock.ANY, task, task.portgroups[0], client_mock.return_value)] )
def test__get_port_ip_address_with_exception(self, mock_gnvi, mock_gfia): expected = "192.168.1.3" port = object_utils.create_test_port( self.context, node_id=self.node.id, id=6, address='aa:bb:cc', uuid=utils.generate_uuid(), extra={'vif_port_id': 'test-vif-A'}, driver='fake') mock_gnvi.return_value = None mock_gfia.return_value = expected with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory().provider self.assertRaises(exception.FailedToGetIPAddressOnPort, api._get_port_ip_address, task, port)
def prepare_instance_pxe_config(task, image_info, iscsi_boot=False, ramdisk_boot=False, ipxe_enabled=False): """Prepares the config file for PXE boot :param task: a task from TaskManager. :param image_info: a dict of values of instance image metadata to set on the configuration file. :param iscsi_boot: if boot is from an iSCSI volume or not. :param ramdisk_boot: if the boot is to a ramdisk configuration. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :returns: None """ node = task.node # Generate options for both IPv4 and IPv6, and they can be # filtered down later based upon the port options. # TODO(TheJulia): This should be re-tooled during the Victoria # development cycle so that we call a single method and return # combined options. The method we currently call is relied upon # by two eternal projects, to changing the behavior is not ideal. dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled, ip_version=4) dhcp_opts += dhcp_options_for_instance(task, ipxe_enabled, ip_version=6) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) pxe_config_path = get_pxe_config_file_path(node.uuid, ipxe_enabled=ipxe_enabled) if not os.path.isfile(pxe_config_path): pxe_options = build_pxe_config_options(task, image_info, service=ramdisk_boot, ipxe_enabled=ipxe_enabled) pxe_config_template = (deploy_utils.get_pxe_config_template(node)) create_pxe_config(task, pxe_options, pxe_config_template, ipxe_enabled=ipxe_enabled) deploy_utils.switch_pxe_config(pxe_config_path, None, boot_mode_utils.get_boot_mode(node), False, iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot, ipxe_enabled=ipxe_enabled)
def test__get_port_ip_address(self, mock_gnvi, mock_gfia): expected = "192.168.1.3" port = object_utils.create_test_port( self.context, node_id=self.node.id, id=6, address='aa:bb:cc', uuid=utils.generate_uuid(), extra={'vif_port_id': 'test-vif-A'}, driver='fake') mock_gnvi.return_value = {port.uuid: 'vif-uuid'} mock_gfia.return_value = expected with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory(token=task.context.auth_token) api = api.provider result = api._get_port_ip_address(task, port.uuid) self.assertEqual(expected, result)
def test_update_dhcp_set_sleep_and_fake(self, mock_gnvi, mock_updo, mock_ts, mock_log): mock_gnvi.return_value = { 'ports': { 'port-uuid': 'vif-uuid' }, 'portgroups': {} } self.config(port_setup_delay=30, group='neutron') with task_manager.acquire(self.context, self.node.uuid) as task: opts = pxe_utils.dhcp_options_for_instance(task) api = dhcp_factory.DHCPFactory() api.update_dhcp(task, opts) mock_log.debug.assert_called_once_with( "Waiting %d seconds for Neutron.", 30) mock_ts.assert_called_with(30) mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts)
def clean_up_pxe_config(task): """Clean up the TFTP environment for the task's node. :param task: A TaskManager instance. """ LOG.debug("Cleaning up PXE config for node %s", task.node.uuid) is_uefi_boot_mode = (boot_mode_utils.get_boot_mode_for_deploy(task.node) == 'uefi') if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled: api = dhcp_factory.DHCPFactory().provider ip_addresses = api.get_ip_addresses(task) if not ip_addresses: return for port_ip_address in ip_addresses: try: # Get xx.xx.xx.xx based grub config file ip_address_path = _get_pxe_ip_address_path(port_ip_address, False) # NOTE(TheJulia): Remove elilo support after the deprecation # period, in the Queens release. # Get 0AOAOAOA based elilo config file hex_ip_path = _get_pxe_ip_address_path(port_ip_address, True) except exception.InvalidIPv4Address: continue except exception.FailedToGetIPAddressOnPort: continue # Cleaning up config files created for grub2. ironic_utils.unlink_without_raise(ip_address_path) # Cleaning up config files created for elilo. ironic_utils.unlink_without_raise(hex_ip_path) for port in task.ports: client_id = port.extra.get('client-id') # syslinux, ipxe, etc. ironic_utils.unlink_without_raise( _get_pxe_mac_path(port.address, client_id=client_id)) # Grub2 MAC address based confiuration ironic_utils.unlink_without_raise( _get_pxe_grub_mac_path(port.address)) utils.rmtree_without_raise(os.path.join(get_root_dir(), task.node.uuid))
def test_update_dhcp_unset_sleep_and_fake(self, mock_gnvi, mock_log): mock_gnvi.return_value = { 'ports': { 'port-uuid': 'vif-uuid' }, 'portgroups': {} } with task_manager.acquire(self.context, self.node.uuid) as task: opts = pxe_utils.dhcp_options_for_instance(task) api = dhcp_factory.DHCPFactory() with mock.patch.object(api.provider, 'update_port_dhcp_opts', autospec=True) as mock_updo: api.update_dhcp(task, opts) mock_log.debug.assert_not_called() mock_updo.assert_called_once_with('vif-uuid', opts, context=task.context)
def test_update_port_dhcp_opts(self, mock_client_init, mock_update_port): opts = [{ 'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0' }, { 'opt_name': 'tftp-server', 'opt_value': '1.1.1.1' }, { 'opt_name': 'server-ip-address', 'opt_value': '1.1.1.1' }] port_id = 'fake-port-id' expected = {'port': {'extra_dhcp_opts': opts}} mock_client_init.return_value = None api = dhcp_factory.DHCPFactory() api.provider.update_port_dhcp_opts(port_id, opts) mock_update_port.assert_called_once_with(port_id, expected)
def test__get_port_ip_address(self, mock_gnvi, mock_gfia): expected = "192.168.1.3" port = object_utils.create_test_port( self.context, node_id=self.node.id, address='aa:bb:cc:dd:ee:ff', uuid=uuidutils.generate_uuid(), extra={'vif_port_id': 'test-vif-A'}, driver='fake') mock_gnvi.return_value = {port.uuid: 'vif-uuid'} mock_gfia.return_value = expected with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory().provider result = api._get_port_ip_address(task, port.uuid, mock.sentinel.client) mock_gnvi.assert_called_once_with(task) self.assertEqual(expected, result) mock_gfia.assert_called_once_with('vif-uuid', mock.sentinel.client)
def deploy(self, task): """Perform a deployment to a node. Perform the necessary work to deploy an image onto the specified node. This method will be called after prepare(), which may have already performed any preparatory steps, such as pre-caching some data for the node. :param task: a TaskManager instance. :returns: status of the deploy. One of ironic.common.states. """ dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory(token=task.context.auth_token) provider.update_dhcp(task, dhcp_opts) manager_utils.node_set_boot_device(task, 'pxe', persistent=True) manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT
def test_update_dhcp_unset_sleep_and_fake(self, mock_gnvi, mock_updo, mock_log): mock_gnvi.return_value = { 'ports': { 'port-uuid': 'vif-uuid' }, 'portgroups': {} } with task_manager.acquire(self.context, self.node.uuid) as task: opts = pxe_utils.dhcp_options_for_instance(task) api = dhcp_factory.DHCPFactory() api.update_dhcp(task, opts) mock_log.debug.assert_not_called() mock_log.warning.assert_not_called() mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts, token=self.context.auth_token)
def deploy(self, task): """Start deployment of the task's node'. Fetches instance image, creates a temporary keystone token file, updates the DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru._continue_deploy(). :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ iscsi_deploy.cache_instance_image(task.context, task.node) iscsi_deploy.check_image_size(task) # TODO(yuriyz): more secure way needed for pass auth token # to deploy ramdisk _create_token_file(task) dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ # between different machines. IPMI does not work for setting boot # devices in UEFI mode for certain machines. # Expected IPMI failure for uefi boot mode. Logging a message to # set the boot device manually and continue with deploy. try: manager_utils.node_set_boot_device(task, 'pxe', persistent=True) except exception.IPMIFailure: if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi': LOG.warning( _LW("ipmitool is unable to set boot device while " "the node is in UEFI boot mode." "Please set the boot device manually.")) else: raise manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT
def test_update_dhcp_set_sleep_and_ssh(self, mock_gnvi, mock_updo, mock_ts): mock_gnvi.return_value = { 'ports': { 'port-uuid': 'vif-uuid' }, 'portgroups': {} } self.config(port_setup_delay=30, group='neutron') with task_manager.acquire(self.context, self.node.uuid) as task: task.driver.power = ssh.SSHPower() opts = pxe_utils.dhcp_options_for_instance(task) api = dhcp_factory.DHCPFactory() api.update_dhcp(task, opts) mock_ts.assert_called_with(30) mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts, token=self.context.auth_token)
def prepare_cleaning(self, task): """Boot into the agent to prepare for cleaning.""" # Create cleaning ports if necessary provider = dhcp_factory.DHCPFactory().provider # If we have left over ports from a previous cleaning, remove them if getattr(provider, 'delete_cleaning_ports', None): provider.delete_cleaning_ports(task) if getattr(provider, 'create_cleaning_ports', None): provider.create_cleaning_ports(task) # Append required config parameters to node's driver_internal_info # to pass to IPA. deploy_utils.agent_add_clean_params(task) _prepare_agent_vmedia_boot(task) # Tell the conductor we are waiting for the agent to boot. return states.CLEANING
def clean_up_pxe_config(task, ipxe_enabled=False): """Clean up the TFTP environment for the task's node. :param task: A TaskManager instance. """ LOG.debug("Cleaning up PXE config for node %s", task.node.uuid) is_uefi_boot_mode = (boot_mode_utils.get_boot_mode(task.node) == 'uefi') if is_uefi_boot_mode and not ipxe_enabled: api = dhcp_factory.DHCPFactory().provider ip_addresses = api.get_ip_addresses(task) if not ip_addresses: return for port_ip_address in ip_addresses: try: # Get xx.xx.xx.xx based grub config file ip_address_path = _get_pxe_ip_address_path(port_ip_address) except exception.InvalidIPv4Address: continue except exception.FailedToGetIPAddressOnPort: continue # Cleaning up config files created for grub2. ironic_utils.unlink_without_raise(ip_address_path) for port in task.ports: client_id = port.extra.get('client-id') # syslinux, ipxe, etc. ironic_utils.unlink_without_raise( _get_pxe_mac_path(port.address, client_id=client_id, ipxe_enabled=ipxe_enabled)) # Grub2 MAC address based confiuration ironic_utils.unlink_without_raise( _get_pxe_grub_mac_path(port.address, ipxe_enabled=ipxe_enabled)) if ipxe_enabled: utils.rmtree_without_raise( os.path.join(get_ipxe_root_dir(), task.node.uuid)) else: utils.rmtree_without_raise(os.path.join(get_root_dir(), task.node.uuid))
def take_over(self, task): """Take over management of this node from a dead conductor. If conductors' hosts maintain a static relationship to nodes, this method should be implemented by the driver to allow conductors to perform the necessary work during the remapping of nodes to conductors when a conductor joins or leaves the cluster. For example, the PXE driver has an external dependency: Neutron must forward DHCP BOOT requests to a conductor which has prepared the tftpboot environment for the given node. When a conductor goes offline, another conductor must change this setting in Neutron as part of remapping that node's control to itself. This is performed within the `takeover` method. :param task: a TaskManager instance. """ provider = dhcp_factory.DHCPFactory(token=task.context.auth_token) provider.update_dhcp(task, CONF.agent.agent_pxe_bootfile_name)
def test_update_port_dhcp_opts_v6(self, update_mock, client_mock): opts = [{ 'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0', 'ip_version': 4 }, { 'opt_name': 'tftp-server', 'opt_value': '1.1.1.1', 'ip_version': 4 }, { 'opt_name': 'server-ip-address', 'opt_value': '1.1.1.1', 'ip_version': 4 }, { 'opt_name': 'bootfile-url', 'opt_value': 'tftp://::1/file.name', 'ip_version': 6 }] port_id = 'fake-port-id' expected = { 'port': { 'extra_dhcp_opts': [{ 'opt_name': 'bootfile-url', 'opt_value': 'tftp://::1/file.name', 'ip_version': 6 }] } } port_data = { "id": port_id, "fixed_ips": [{ "ip_address": "2001:db8::201", }], } client_mock.return_value.show_port.return_value = {'port': port_data} api = dhcp_factory.DHCPFactory() with task_manager.acquire(self.context, self.node.uuid) as task: api.provider.update_port_dhcp_opts(port_id, opts, context=task.context) update_mock.assert_called_once_with(task.context, port_id, expected)
def test_update_port_dhcp_opts(self, update_mock): opts = [{ 'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0' }, { 'opt_name': 'tftp-server', 'opt_value': '1.1.1.1' }, { 'opt_name': 'server-ip-address', 'opt_value': '1.1.1.1' }] port_id = 'fake-port-id' expected = {'port': {'extra_dhcp_opts': opts}} api = dhcp_factory.DHCPFactory() with task_manager.acquire(self.context, self.node.uuid) as task: api.provider.update_port_dhcp_opts(port_id, opts, context=task.context) update_mock.assert_called_once_with(task.context, port_id, expected)
def clean_up(self, task): """Clean up the deployment environment for this node. If preparation of the deployment environment ahead of time is possible, this method should be implemented by the driver. It should erase anything cached by the `prepare` method. If implemented, this method must be idempotent. It may be called multiple times for the same node on the same conductor, and it may be called by multiple conductors in parallel. Therefore, it must not require an exclusive lock. This method is called before `tear_down`. :param task: a TaskManager instance. """ if CONF.agent.manage_agent_boot: task.driver.boot.clean_up_ramdisk(task) provider = dhcp_factory.DHCPFactory() provider.clean_dhcp(task)
def _link_ip_address_pxe_configs(task): """Link each IP address with the PXE configuration file. :param task: A TaskManager instance. :raises: FailedToGetIPAddressOnPort :raises: InvalidIPv4Address """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid) api = dhcp_factory.DHCPFactory().provider ip_addrs = api.get_ip_addresses(task) if not ip_addrs: raise exception.FailedToGetIPAddressOnPort( _("Failed to get IP address for any port on node %s.") % task.node.uuid) for port_ip_address in ip_addrs: ip_address_path = _get_pxe_ip_address_path(port_ip_address) utils.unlink_without_raise(ip_address_path) utils.create_link_without_raise(pxe_config_file_path, ip_address_path)
def test_update_port_dhcp_opts_with_exception(self, update_mock, client_mock): opts = [{}] port_id = 'fake-port-id' port_data = { "id": port_id, "fixed_ips": [{ "ip_address": "192.168.1.3", }], } client_mock.return_value.show_port.return_value = {'port': port_data} update_mock.side_effect = (neutron_client_exc.NeutronClientException()) api = dhcp_factory.DHCPFactory() with task_manager.acquire(self.context, self.node.uuid) as task: self.assertRaises(exception.FailedToUpdateDHCPOptOnPort, api.provider.update_port_dhcp_opts, port_id, opts, context=task.context)
def prepare_cleaning_ports(task): """Prepare the Ironic ports of the node for cleaning. This method deletes the cleaning ports currently existing for all the ports of the node and then creates a new one for each one of them. It also adds 'vif_port_id' to port.extra of each Ironic port, after creating the cleaning ports. :param task: a TaskManager object containing the node :raises NodeCleaningFailure: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created """ provider = dhcp_factory.DHCPFactory() # If we have left over ports from a previous cleaning, remove them if getattr(provider.provider, 'delete_cleaning_ports', None): # Allow to raise if it fails, is caught and handled in conductor provider.provider.delete_cleaning_ports(task) # Create cleaning ports if necessary if getattr(provider.provider, 'create_cleaning_ports', None): # Allow to raise if it fails, is caught and handled in conductor ports = provider.provider.create_cleaning_ports(task) # Add vif_port_id for each of the ports because some boot # interfaces expects these to prepare for booting ramdisk. for port in task.ports: extra_dict = port.extra try: extra_dict['vif_port_id'] = ports[port.uuid] except KeyError: # This is an internal error in Ironic. All DHCP providers # implementing create_cleaning_ports are supposed to # return a VIF port ID for all Ironic ports. But # that doesn't seem to be true here. error = (_("When creating cleaning ports, DHCP provider " "didn't return VIF port ID for %s") % port.uuid) raise exception.NodeCleaningFailure( node=task.node.uuid, reason=error) else: port.extra = extra_dict port.save()
def test_update_port_address_with_binding(self, mock_client_init, mock_update_port, mock_show_port): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' expected = { 'port': { 'mac_address': address, 'binding:host_id': 'host' } } mock_client_init.return_value = None mock_show_port.return_value = {'port': {'binding:host_id': 'host'}} api = dhcp_factory.DHCPFactory() api.provider.update_port_address(port_id, address) mock_update_port.assert_any_call(port_id, {'port': { 'binding:host_id': '' }}) mock_update_port.assert_any_call(port_id, expected)
def test_update_dhcp_unset_sleep_and_ssh(self, mock_gnvi, mock_updo, mock_ts, mock_log): mock_gnvi.return_value = { 'ports': { 'port-uuid': 'vif-uuid' }, 'portgroups': {} } with task_manager.acquire(self.context, self.node.uuid) as task: opts = pxe_utils.dhcp_options_for_instance(task) task.driver.power = ssh.SSHPower() api = dhcp_factory.DHCPFactory() api.update_dhcp(task, opts) self.assertTrue(mock_log.warning.called) self.assertIn('Setting the port delay to 15 for SSH', mock_log.warning.call_args[0][0]) mock_ts.assert_called_with(15) mock_updo.assert_called_once_with(mock.ANY, 'vif-uuid', opts, token=self.context.auth_token)
def _test__get_port_ip_address(self, mock_gfia, network): expected = "192.168.1.3" fake_vif = 'test-vif-%s' % network port = object_utils.create_test_port( self.context, node_id=self.node.id, address='aa:bb:cc:dd:ee:ff', uuid=uuidutils.generate_uuid(), extra={'vif_port_id': fake_vif} if network == 'tenant' else {}, internal_info={ 'cleaning_vif_port_id': (fake_vif if network == 'cleaning' else None), 'provisioning_vif_port_id': (fake_vif if network == 'provisioning' else None), }) mock_gfia.return_value = expected with task_manager.acquire(self.context, self.node.uuid) as task: api = dhcp_factory.DHCPFactory().provider result = api._get_port_ip_address(task, port, mock.sentinel.client) self.assertEqual(expected, result) mock_gfia.assert_called_once_with(fake_vif, mock.sentinel.client)
def prepare_instance_pxe_config(task, image_info, iscsi_boot=False, ramdisk_boot=False, ipxe_enabled=False): """Prepares the config file for PXE boot :param task: a task from TaskManager. :param image_info: a dict of values of instance image metadata to set on the configuration file. :param iscsi_boot: if boot is from an iSCSI volume or not. :param ramdisk_boot: if the boot is to a ramdisk configuration. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :returns: None """ node = task.node dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) pxe_config_path = get_pxe_config_file_path(node.uuid, ipxe_enabled=ipxe_enabled) if not os.path.isfile(pxe_config_path): pxe_options = build_pxe_config_options(task, image_info, service=ramdisk_boot, ipxe_enabled=ipxe_enabled) pxe_config_template = (deploy_utils.get_pxe_config_template(node)) create_pxe_config(task, pxe_options, pxe_config_template, ipxe_enabled=ipxe_enabled) deploy_utils.switch_pxe_config(pxe_config_path, None, boot_mode_utils.get_boot_mode(node), False, iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot, ipxe_enabled=ipxe_enabled)