diff --git a/ironic/drivers/modules/ilo/boot.py b/ironic/drivers/modules/ilo/boot.py index f6012db940..804e6772ce 100644 --- a/ironic/drivers/modules/ilo/boot.py +++ b/ironic/drivers/modules/ilo/boot.py @@ -187,7 +187,13 @@ def _get_boot_iso(task, root_uuid): deploy_iso_uuid = deploy_info['ilo_deploy_iso'] boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) boot_iso_object_name = _get_boot_iso_object_name(task.node) - kernel_params = CONF.pxe.pxe_append_params + kernel_params = "" + if deploy_utils.get_boot_option(task.node) == "ramdisk": + i_info = task.node.instance_info + kernel_params = "root=/dev/ram0 text " + kernel_params += i_info.get("ramdisk_kernel_arguments", "") + else: + kernel_params = CONF.pxe.pxe_append_params with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj: boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, @@ -396,7 +402,7 @@ def disable_secure_boot_if_supported(task): class IloVirtualMediaBoot(base.BootInterface): - capabilities = ['iscsi_volume_boot'] + capabilities = ['iscsi_volume_boot', 'ramdisk_boot'] def get_properties(self): # TODO(stendulker): COMMON_PROPERTIES should also include rescue @@ -414,6 +420,22 @@ class IloVirtualMediaBoot(base.BootInterface): missing in the Glance image or 'kernel' and 'ramdisk' not provided in instance_info for non-Glance image. """ + node = task.node + boot_option = deploy_utils.get_boot_option(node) + boot_iso = node.instance_info.get('ilo_boot_iso') + if (boot_option == "ramdisk" and boot_iso): + if not service_utils.is_glance_image(boot_iso): + try: + image_service.HttpImageService().validate_href(boot_iso) + except exception.ImageRefValidationFailed: + with excutils.save_and_reraise_exception(): + LOG.error("Virtual media deploy with 'ramdisk' " + "boot_option accepts only Glance images or " + "HTTP(S) URLs as " + "instance_info['ilo_boot_iso']. Either %s " + "is not a valid HTTP(S) URL or is not " + "reachable.", boot_iso) + return _validate_driver_info(task) @@ -501,10 +523,10 @@ class IloVirtualMediaBoot(base.BootInterface): :raises: InstanceDeployFailure, if its try to boot iSCSI volume in 'BIOS' boot mode. """ - ilo_common.cleanup_vmedia_boot(task) boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node) + boot_option = deploy_utils.get_boot_option(task.node) if deploy_utils.is_iscsi_boot(task): # It will set iSCSI info onto iLO @@ -520,6 +542,12 @@ class IloVirtualMediaBoot(base.BootInterface): else: msg = 'Virtual media can not boot volume in BIOS boot mode.' raise exception.InstanceDeployFailure(msg) + elif boot_option == "ramdisk": + boot_iso = _get_boot_iso(task, None) + ilo_common.setup_vmedia_for_boot(task, boot_iso) + manager_utils.node_set_boot_device(task, + boot_devices.CDROM, + persistent=True) else: # Boot from disk every time if the image deployed is # a whole disk image. diff --git a/ironic/tests/unit/drivers/modules/ilo/test_boot.py b/ironic/tests/unit/drivers/modules/ilo/test_boot.py index 7fa27363b6..f789a44519 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_boot.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_boot.py @@ -683,6 +683,88 @@ class IloVirtualMediaBootTestCase(test_common.BaseIloTest): mock_val_instance_image_info.assert_called_once_with(task) mock_val_driver_info.assert_called_once_with(task) + @mock.patch.object(ilo_boot, '_validate_driver_info', + spec_set=True, autospec=True) + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test_validate_ramdisk_boot_option_glance(self, is_glance_image_mock, + validate_href_mock, + val_driver_info_mock): + instance_info = self.node.instance_info + boot_iso = '6b2f0c0c-79e8-4db6-842e-43c9764204af' + instance_info['ilo_boot_iso'] = boot_iso + instance_info['capabilities'] = '{"boot_option": "ramdisk"}' + self.node.instance_info = instance_info + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + is_glance_image_mock.return_value = True + task.driver.boot.validate(task) + is_glance_image_mock.assert_called_once_with(boot_iso) + self.assertFalse(validate_href_mock.called) + self.assertFalse(val_driver_info_mock.called) + + @mock.patch.object(ilo_boot, '_validate_driver_info', + spec_set=True, autospec=True) + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test_validate_ramdisk_boot_option_webserver(self, is_glance_image_mock, + validate_href_mock, + val_driver_info_mock): + instance_info = self.node.instance_info + boot_iso = 'http://myserver/boot.iso' + instance_info['ilo_boot_iso'] = boot_iso + instance_info['capabilities'] = '{"boot_option": "ramdisk"}' + self.node.instance_info = instance_info + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + is_glance_image_mock.return_value = False + task.driver.boot.validate(task) + is_glance_image_mock.assert_called_once_with(boot_iso) + validate_href_mock.assert_called_once_with(mock.ANY, boot_iso) + self.assertFalse(val_driver_info_mock.called) + + @mock.patch.object(ilo_boot.LOG, 'error', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, '_validate_driver_info', + spec_set=True, autospec=True) + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test_validate_ramdisk_boot_option_webserver_exc(self, + is_glance_image_mock, + validate_href_mock, + val_driver_info_mock, + log_mock): + instance_info = self.node.instance_info + validate_href_mock.side_effect = exception.ImageRefValidationFailed( + image_href='http://myserver/boot.iso', reason='fail') + boot_iso = 'http://myserver/boot.iso' + instance_info['ilo_boot_iso'] = boot_iso + instance_info['capabilities'] = '{"boot_option": "ramdisk"}' + self.node.instance_info = instance_info + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + + is_glance_image_mock.return_value = False + self.assertRaisesRegex(exception.ImageRefValidationFailed, + "Validation of image href " + "http://myserver/boot.iso failed", + task.driver.boot.validate, task) + is_glance_image_mock.assert_called_once_with(boot_iso) + validate_href_mock.assert_called_once_with(mock.ANY, boot_iso) + self.assertFalse(val_driver_info_mock.called) + self.assertIn("Virtual media deploy with 'ramdisk' boot_option " + "accepts only Glance images or HTTP(S) URLs as " + "instance_info['ilo_boot_iso'].", + log_mock.call_args[0][0]) + @mock.patch.object(noop_storage.NoopStorage, 'should_write_image', autospec=True) @mock.patch.object(ilo_boot, '_validate_driver_info', @@ -1086,6 +1168,45 @@ class IloVirtualMediaBootTestCase(test_common.BaseIloTest): self.assertIsNone(task.node.driver_internal_info.get( 'ilo_uefi_iscsi_boot')) + @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, + autospec=True) + @mock.patch.object(deploy_utils, 'is_iscsi_boot', + spec_set=True, autospec=True) + @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', + spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, '_get_boot_iso', + spec_set=True, autospec=True) + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, + autospec=True) + def test_prepare_instance_boot_ramdisk(self, update_secure_boot_mode_mock, + update_boot_mode_mock, + set_boot_device_mock, + get_boot_iso_mock, + setup_vmedia_mock, + is_iscsi_boot_mock, + cleanup_vmedia_boot_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + instance_info = task.node.instance_info + instance_info['capabilities'] = '{"boot_option": "ramdisk"}' + task.node.instance_info = instance_info + task.node.save() + is_iscsi_boot_mock.return_value = False + url = 'http://myserver/boot.iso' + get_boot_iso_mock.return_value = url + task.driver.boot.prepare_instance(task) + cleanup_vmedia_boot_mock.assert_called_once_with(task) + get_boot_iso_mock.assert_called_once_with(task, None) + setup_vmedia_mock.assert_called_once_with(task, url) + set_boot_device_mock.assert_called_once_with( + task, boot_devices.CDROM, persistent=True) + update_boot_mode_mock.assert_called_once_with(task) + update_secure_boot_mode_mock.assert_called_once_with(task, True) + def test_validate_rescue(self): driver_info = self.node.driver_info driver_info['ilo_rescue_iso'] = 'rescue.iso' diff --git a/releasenotes/notes/adds-ramdisk-deploy-interface-support-to-ilo-vmedia-1a7228a834465633.yaml b/releasenotes/notes/adds-ramdisk-deploy-interface-support-to-ilo-vmedia-1a7228a834465633.yaml new file mode 100644 index 0000000000..a73ea43d13 --- /dev/null +++ b/releasenotes/notes/adds-ramdisk-deploy-interface-support-to-ilo-vmedia-1a7228a834465633.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds support for booting a ramdisk using virtual media to + ``ilo-virtual-media`` boot interface when an ironic node is configured + with ``ramdisk`` deploy interface.