diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py index cc37db5950..f7f221894e 100644 --- a/ironic/drivers/modules/ilo/deploy.py +++ b/ironic/drivers/modules/ilo/deploy.py @@ -730,6 +730,44 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor): iscsi_deploy.get_deploy_info(task.node, **kwargs) elif method == 'pass_bootloader_install_info': iscsi_deploy.validate_pass_bootloader_info_input(task, kwargs) + elif method == 'boot_into_iso': + self._validate_boot_into_iso(task, kwargs) + + def _validate_boot_into_iso(self, task, kwargs): + """Validates if attach_iso can be called and if inputs are proper.""" + if not (task.node.provision_state == states.MANAGEABLE or + task.node.maintenance is True): + msg = (_("The requested action 'boot_into_iso' can be performed " + "only when node %(node_uuid)s is in %(state)s state or " + "in 'maintenance' mode") % + {'node_uuid': task.node.uuid, + 'state': states.MANAGEABLE}) + raise exception.InvalidStateRequested(msg) + d_info = {'boot_iso_href': kwargs.get('boot_iso_href')} + error_msg = _("Error validating input for boot_into_iso vendor " + "passthru. Some parameters were not provided: ") + deploy_utils.check_for_missing_params(d_info, error_msg) + iscsi_deploy.validate_image_properties( + task.context, {'image_source': kwargs.get('boot_iso_href')}, []) + + @base.passthru(['POST']) + @task_manager.require_exclusive_lock + def boot_into_iso(self, task, **kwargs): + """Attaches an ISO image in glance and reboots bare metal. + + This method accepts an ISO image href (a Glance UUID or an HTTP(S) URL) + attaches it as virtual media and then reboots the node. This is + useful for debugging purposes. This can be invoked only when the node + is in manage state. + + :param task: A TaskManager object. + :param kwargs: The arguments sent with vendor passthru. The expected + kwargs are:: + + 'boot_iso_href': href of the image to be booted. This can be + a Glance UUID or an HTTP(S) URL. + """ + _reboot_into(task, kwargs['boot_iso_href'], ramdisk_options=None) def _configure_vmedia_boot(self, task, root_uuid): """Configure vmedia boot for the node.""" diff --git a/ironic/tests/drivers/ilo/test_deploy.py b/ironic/tests/drivers/ilo/test_deploy.py index be0d5937fb..af0f839f59 100644 --- a/ironic/tests/drivers/ilo/test_deploy.py +++ b/ironic/tests/drivers/ilo/test_deploy.py @@ -1239,6 +1239,68 @@ class VendorPassthruTestCase(db_base.DbTestCase): reboot_and_finish_deploy_mock.assert_called_once_with( mock.ANY, task) + @mock.patch.object(ilo_deploy, '_reboot_into', spec_set=True, + autospec=True) + def test_boot_into_iso(self, reboot_into_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.vendor.boot_into_iso(task, boot_iso_href='foo') + reboot_into_mock.assert_called_once_with(task, 'foo', + ramdisk_options=None) + + @mock.patch.object(ilo_deploy.VendorPassthru, '_validate_boot_into_iso', + spec_set=True, autospec=True) + def test_validate_boot_into_iso(self, validate_boot_into_iso_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + vendor = ilo_deploy.VendorPassthru() + vendor.validate(task, method='boot_into_iso', foo='bar') + validate_boot_into_iso_mock.assert_called_once_with( + vendor, task, {'foo': 'bar'}) + + def test__validate_boot_into_iso_invalid_state(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.provision_state = states.AVAILABLE + self.assertRaises( + exception.InvalidStateRequested, + task.driver.vendor._validate_boot_into_iso, + task, {}) + + def test__validate_boot_into_iso_missing_boot_iso_href(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.provision_state = states.MANAGEABLE + self.assertRaises( + exception.MissingParameterValue, + task.driver.vendor._validate_boot_into_iso, + task, {}) + + @mock.patch.object(iscsi_deploy, 'validate_image_properties', + spec_set=True, autospec=True) + def test__validate_boot_into_iso_manage(self, validate_image_prop_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + info = {'boot_iso_href': 'foo'} + task.node.provision_state = states.MANAGEABLE + task.driver.vendor._validate_boot_into_iso( + task, info) + validate_image_prop_mock.assert_called_once_with( + task.context, {'image_source': 'foo'}, []) + + @mock.patch.object(iscsi_deploy, 'validate_image_properties', + spec_set=True, autospec=True) + def test__validate_boot_into_iso_maintenance( + self, validate_image_prop_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + info = {'boot_iso_href': 'foo'} + task.node.maintenance = True + task.driver.vendor._validate_boot_into_iso( + task, info) + validate_image_prop_mock.assert_called_once_with( + task.context, {'image_source': 'foo'}, []) + class IloPXEDeployTestCase(db_base.DbTestCase):