diff --git a/ironic/tests/unit/common/test_pxe_utils.py b/ironic/tests/unit/common/test_pxe_utils.py index ef1e5d1f37..3c43076158 100644 --- a/ironic/tests/unit/common/test_pxe_utils.py +++ b/ironic/tests/unit/common/test_pxe_utils.py @@ -128,6 +128,10 @@ class TestPXEUtils(db_base.DbTestCase): 'boot_from_iso': True, 'boot_iso_url': 'http://1.2.3.4:1234/uuid/iso' }) + self.ipxe_options_boot_from_ramdisk = self.ipxe_options.copy() + self.ipxe_options_boot_from_ramdisk.update({ + 'ramdisk_kernel_arguments': 'ramdisk_params' + }) self.node = object_utils.create_test_node(self.context) @@ -290,6 +294,27 @@ class TestPXEUtils(db_base.DbTestCase): expected_template = f.read().rstrip() self.assertEqual(str(expected_template), rendered_template) + def test_default_ipxe_boot_from_ramdisk(self): + self.config( + pxe_config_template='ironic/drivers/modules/ipxe_config.template', + group='pxe' + ) + self.config(http_url='http://1.2.3.4:1234', group='deploy') + + pxe_options = self.ipxe_options_boot_from_ramdisk + + rendered_template = utils.render_template( + CONF.pxe.pxe_config_template, + {'pxe_options': pxe_options, + 'ROOT': '{{ ROOT }}'}, + ) + + templ_file = 'ironic/tests/unit/drivers/' \ + 'ipxe_config_boot_from_ramdisk.template' + with open(templ_file) as f: + expected_template = f.read().rstrip() + self.assertEqual(str(expected_template), rendered_template) + def test_default_grub_config(self): pxe_opts = self.pxe_options pxe_opts['boot_mode'] = 'uefi' @@ -1571,7 +1596,8 @@ class PXEBuildConfigOptionsTestCase(db_base.DbTestCase): whle_dsk_img=False, debug=False, mode='deploy', ramdisk_params=None, - expected_pxe_params=None): + expected_pxe_params=None, + ramdisk_kernel_opt=None): self.config(debug=debug) self.config(kernel_append_params='test_param', group='pxe') @@ -1633,6 +1659,8 @@ class PXEBuildConfigOptionsTestCase(db_base.DbTestCase): 'ari_path': ramdisk, 'aki_path': kernel, } + if ramdisk_kernel_opt: + expected_options.update({'ramdisk_opts': ramdisk_kernel_opt}) if mode == 'rescue': self.node.provision_state = states.RESCUING @@ -1658,6 +1686,10 @@ class PXEBuildConfigOptionsTestCase(db_base.DbTestCase): del self.node.driver_internal_info['is_whole_disk_image'] self._test_build_pxe_config_options_pxe(debug=True, mode='rescue') + def test_build_pxe_config_options_pxe_opts_ramdisk_opt(self): + self.node.instance_info = {'ramdisk_kernel_arguments': 'cat meow'} + self._test_build_pxe_config_options_pxe(ramdisk_kernel_opt='cat meow') + def test_build_pxe_config_options_pxe_local_boot(self): del self.node.driver_internal_info['is_whole_disk_image'] i_info = self.node.instance_info diff --git a/ironic/tests/unit/drivers/ipxe_config_boot_from_ramdisk.template b/ironic/tests/unit/drivers/ipxe_config_boot_from_ramdisk.template new file mode 100644 index 0000000000..70f8a03f1e --- /dev/null +++ b/ironic/tests/unit/drivers/ipxe_config_boot_from_ramdisk.template @@ -0,0 +1,47 @@ +#!ipxe + +set attempts:int32 10 +set i:int32 0 + +goto deploy + +:deploy +imgfree +kernel http://1.2.3.4:1234/deploy_kernel selinux=0 troubleshoot=0 text test_param BOOTIF=${mac} initrd=deploy_ramdisk || goto retry + +initrd http://1.2.3.4:1234/deploy_ramdisk || goto retry +boot + +:retry +iseq ${i} ${attempts} && goto fail || +inc i +echo No response, retrying in ${i} seconds. +sleep ${i} +goto deploy + +:fail +echo Failed to get a response after ${attempts} attempts +echo Powering off in 30 seconds. +sleep 30 +poweroff + +:boot_partition +imgfree +kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramdisk || goto boot_partition +initrd http://1.2.3.4:1234/ramdisk || goto boot_partition +boot + +:boot_anaconda +imgfree +kernel http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda +initrd http://1.2.3.4:1234/ramdisk || goto boot_anaconda +boot + +:boot_ramdisk +imgfree +kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk +initrd http://1.2.3.4:1234/ramdisk || goto boot_ramdisk +boot + +:boot_whole_disk +sanboot --no-describe diff --git a/ironic/tests/unit/drivers/modules/test_ipxe.py b/ironic/tests/unit/drivers/modules/test_ipxe.py index 085c96c411..b6377417a5 100644 --- a/ironic/tests/unit/drivers/modules/test_ipxe.py +++ b/ironic/tests/unit/drivers/modules/test_ipxe.py @@ -1091,6 +1091,64 @@ class iPXEBootTestCase(db_base.DbTestCase): boot_devices.PXE, persistent=True) + @mock.patch('os.path.isfile', lambda filename: False) + @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + def test_prepare_instance_netboot_ramdisk_with_kernel_arg( + self, get_image_info_mock, cache_mock, + dhcp_factory_mock, switch_pxe_config_mock, + set_boot_device_mock, create_pxe_config_mock): + http_url = 'http://192.1.2.3:1234' + self.config(http_url=http_url, group='deploy') + self.config(enabled_deploy_interfaces='ramdisk') + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + self.node.instance_info = {'ramdisk_kernel_arguments': 'cat meow'} + image_info = {'kernel': ('', '/path/to/kernel'), + 'deploy_kernel': ('', '/path/to/kernel'), + 'ramdisk': ('', '/path/to/ramdisk'), + 'deploy_ramdisk': ('', '/path/to/ramdisk')} + get_image_info_mock.return_value = image_info + self.node.provision_state = states.DEPLOYING + self.node.deploy_interface = 'ramdisk' + self.node.save() + with task_manager.acquire(self.context, self.node.uuid) as task: + dhcp_opts = pxe_utils.dhcp_options_for_instance(task, + ipxe_enabled=True) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid, ipxe_enabled=True) + task.driver.boot.prepare_instance(task) + self.assertTrue(get_image_info_mock.called) + self.assertTrue(cache_mock.called) + uuid = self.node.uuid + expected_params = { + 'aki_path': 'http://192.1.2.3:1234/' + uuid + '/kernel', + 'ari_path': 'http://192.1.2.3:1234/' + uuid + '/ramdisk', + 'ramdisk_opts': 'cat meow', + 'pxe_append_params': 'nofb nomodeset vga=normal ipa-debug=1 ' + 'ipa-global-request-' + 'id=' + task.context.request_id, + 'tftp_server': mock.ANY, + 'ipxe_timeout': 0 + } + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + create_pxe_config_mock.assert_called_once_with( + task, expected_params, CONF.pxe.ipxe_config_template, + ipxe_enabled=True) + switch_pxe_config_mock.assert_called_once_with( + pxe_config_path, None, boot_modes.UEFI, False, + ipxe_enabled=True, iscsi_boot=False, ramdisk_boot=True, + anaconda_boot=False) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.PXE, + persistent=True) + @mock.patch.object(boot_mode_utils, 'configure_secure_boot_if_needed', autospec=True) @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)