From 4497ab0add61de82095adb25978bd8d693627abd Mon Sep 17 00:00:00 2001 From: Naohiro Tamura Date: Tue, 28 Apr 2015 18:19:38 +0900 Subject: [PATCH] Add whole disk image support for iscsi_irmc driver This adds local boot capability to whole disk deployed image in addition to partition deployed image in iscsi_ilo driver. The primary change is to set a bare metal node to boot from local disk at the end of deployment if the deployed image is a whole disk image. Implements: blueprint whole-disk-image-support Change-Id: Ia7d0156511048eb45b52d573d25a546d84cc6875 --- ironic/drivers/modules/irmc/deploy.py | 26 +++-- ironic/tests/drivers/irmc/test_deploy.py | 116 +++++++++++++++++++++++ 2 files changed, 133 insertions(+), 9 deletions(-) diff --git a/ironic/drivers/modules/irmc/deploy.py b/ironic/drivers/modules/irmc/deploy.py index bcdc7fdfee..944811c1f1 100644 --- a/ironic/drivers/modules/irmc/deploy.py +++ b/ironic/drivers/modules/irmc/deploy.py @@ -560,7 +560,9 @@ class IRMCVirtualMediaIscsiDeploy(base.DeployInterface): iscsi_deploy.validate(task) d_info = _parse_deploy_info(task.node) - if service_utils.is_glance_image(d_info['image_source']): + if task.node.driver_internal_info.get('is_whole_disk_image'): + props = [] + elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] @@ -807,24 +809,30 @@ class VendorPassthru(base.VendorInterface): node = task.node task.process_event('resume') - root_dict = iscsi_deploy.continue_deploy(task, **kwargs) - root_uuid = root_dict.get('root uuid') + is_whole_disk_image = node.driver_internal_info.get( + 'is_whole_disk_image') + uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs) + root_uuid_or_disk_id = uuid_dict.get( + 'root uuid', uuid_dict.get('disk identifier')) try: _cleanup_vmedia_boot(task) - if iscsi_deploy.get_boot_option(node) == "local": + if (iscsi_deploy.get_boot_option(node) == "local" or + is_whole_disk_image): manager_utils.node_set_boot_device(task, boot_devices.DISK, persistent=True) # Ask the ramdisk to install bootloader and # wait for the call-back through the vendor passthru - # 'pass_bootloader_install_info'. - deploy_utils.notify_ramdisk_to_proceed(kwargs['address']) - task.process_event('wait') - return + # 'pass_bootloader_install_info', if it's not a whole + # disk image. + if not is_whole_disk_image: + deploy_utils.notify_ramdisk_to_proceed(kwargs['address']) + task.process_event('wait') + return else: - _prepare_boot_iso(task, root_uuid) + _prepare_boot_iso(task, root_uuid_or_disk_id) setup_vmedia_for_boot( task, node.driver_internal_info['irmc_boot_iso']) manager_utils.node_set_boot_device(task, boot_devices.CDROM, diff --git a/ironic/tests/drivers/irmc/test_deploy.py b/ironic/tests/drivers/irmc/test_deploy.py index e78e344e0c..ec97fa2c51 100644 --- a/ironic/tests/drivers/irmc/test_deploy.py +++ b/ironic/tests/drivers/irmc/test_deploy.py @@ -795,7 +795,43 @@ class IRMCVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @mock.patch.object(irmc_deploy, '_parse_deploy_info', spec_set=True, autospec=True) @mock.patch.object(iscsi_deploy, 'validate', spec_set=True, autospec=True) + @mock.patch.object(irmc_deploy, '_check_share_fs_mounted', spec_set=True, + autospec=True) + def test_validate_whole_disk_image(self, + _check_share_fs_mounted_mock, + validate_mock, + deploy_info_mock, + is_glance_image_mock, + validate_prop_mock, + validate_capabilities_mock): + d_info = {'image_source': '733d1c44-a2ea-414b-aca7-69decf20d810'} + deploy_info_mock.return_value = d_info + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.driver_internal_info = {'is_whole_disk_image': True} + task.driver.deploy.validate(task) + + _check_share_fs_mounted_mock.assert_called_once_with() + validate_mock.assert_called_once_with(task) + deploy_info_mock.assert_called_once_with(task.node) + self.assertFalse(is_glance_image_mock.called) + validate_prop_mock.assert_called_once_with(task.context, + d_info, []) + validate_capabilities_mock.assert_called_once_with(task.node) + + @mock.patch.object(deploy_utils, 'validate_capabilities', + spec_set=True, autospec=True) + @mock.patch.object(iscsi_deploy, 'validate_image_properties', + spec_set=True, autospec=True) + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + @mock.patch.object(irmc_deploy, '_parse_deploy_info', spec_set=True, + autospec=True) + @mock.patch.object(iscsi_deploy, 'validate', spec_set=True, autospec=True) + @mock.patch.object(irmc_deploy, '_check_share_fs_mounted', spec_set=True, + autospec=True) def test_validate_glance_image(self, + _check_share_fs_mounted_mock, validate_mock, deploy_info_mock, is_glance_image_mock, @@ -808,6 +844,7 @@ class IRMCVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): shared=False) as task: task.driver.deploy.validate(task) + _check_share_fs_mounted_mock.assert_called_once_with() validate_mock.assert_called_once_with(task) deploy_info_mock.assert_called_once_with(task.node) validate_prop_mock.assert_called_once_with( @@ -1155,6 +1192,7 @@ class VendorPassthruTestCase(db_base.DbTestCase): continue_deploy_mock.return_value = {'root uuid': 'root_uuid'} _prepare_boot_iso_mock.side_effect = Exception("fake error") + self.node.driver_internal_info = {'is_whole_disk_image': False} self.node.provision_state = states.DEPLOYWAIT self.node.target_provision_state = states.ACTIVE self.node.save() @@ -1193,6 +1231,7 @@ class VendorPassthruTestCase(db_base.DbTestCase): kwargs = {'method': 'pass_deploy_info', 'address': '123456'} continue_deploy_mock.return_value = {'root uuid': ''} + self.node.driver_internal_info = {'is_whole_disk_image': False} self.node.provision_state = states.DEPLOYWAIT self.node.target_provision_state = states.ACTIVE self.node.instance_info = {'capabilities': '{"boot_option": "local"}'} @@ -1210,3 +1249,80 @@ class VendorPassthruTestCase(db_base.DbTestCase): notify_ramdisk_to_proceed_mock.assert_called_once_with('123456') self.assertEqual(states.DEPLOYWAIT, task.node.provision_state) self.assertEqual(states.ACTIVE, task.node.target_provision_state) + + @mock.patch.object(iscsi_deploy, 'finish_deploy', spec_set=True, + autospec=True) + @mock.patch.object(deploy_utils, 'notify_ramdisk_to_proceed', + spec_set=True, autospec=True) + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(iscsi_deploy, 'continue_deploy', spec_set=True, + autospec=True) + @mock.patch.object(irmc_deploy, '_cleanup_vmedia_boot', spec_set=True, + autospec=True) + def test_pass_deploy_info_whole_disk_image( + self, + _cleanup_vmedia_boot_mock, + continue_deploy_mock, + set_boot_device_mock, + notify_ramdisk_to_proceed_mock, + finish_deploy_mock): + + kwargs = {'method': 'pass_deploy_info', 'address': '123456'} + continue_deploy_mock.return_value = {'root uuid': ''} + + self.node.driver_internal_info = {'is_whole_disk_image': True} + self.node.provision_state = states.DEPLOYWAIT + self.node.target_provision_state = states.ACTIVE + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + vendor = task.driver.vendor + vendor.pass_deploy_info(task, **kwargs) + + _cleanup_vmedia_boot_mock.assert_called_once_with(task) + continue_deploy_mock.assert_called_once_with(task, **kwargs) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.DISK, + persistent=True) + self.assertFalse(notify_ramdisk_to_proceed_mock.called) + finish_deploy_mock.assert_called_once_with(task, '123456') + + @mock.patch.object(iscsi_deploy, 'finish_deploy', spec_set=True, + autospec=True) + @mock.patch.object(deploy_utils, 'notify_ramdisk_to_proceed', + spec_set=True, autospec=True) + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(iscsi_deploy, 'continue_deploy', spec_set=True, + autospec=True) + @mock.patch.object(irmc_deploy, '_cleanup_vmedia_boot', spec_set=True, + autospec=True) + def test_pass_deploy_info_whole_disk_image_local( + self, + _cleanup_vmedia_boot_mock, + continue_deploy_mock, + set_boot_device_mock, + notify_ramdisk_to_proceed_mock, + finish_deploy_mock): + + kwargs = {'method': 'pass_deploy_info', 'address': '123456'} + continue_deploy_mock.return_value = {'root uuid': ''} + + self.node.driver_internal_info = {'is_whole_disk_image': True} + self.node.instance_info = {'capabilities': '{"boot_option": "local"}'} + self.node.provision_state = states.DEPLOYWAIT + self.node.target_provision_state = states.ACTIVE + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + vendor = task.driver.vendor + vendor.pass_deploy_info(task, **kwargs) + + _cleanup_vmedia_boot_mock.assert_called_once_with(task) + continue_deploy_mock.assert_called_once_with(task, **kwargs) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.DISK, + persistent=True) + self.assertFalse(notify_ramdisk_to_proceed_mock.called) + finish_deploy_mock.assert_called_once_with(task, '123456')