diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py index 1b045974cf..eda5e6f175 100644 --- a/ironic/drivers/modules/agent.py +++ b/ironic/drivers/modules/agent.py @@ -258,6 +258,7 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): if no_proxy is not None: image_info['no_proxy'] = no_proxy + image_info['node_uuid'] = node.uuid iwdi = node.driver_internal_info.get('is_whole_disk_image') if not iwdi: for label in PARTITION_IMAGE_LABELS: @@ -272,7 +273,6 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): disk_label = deploy_utils.get_disk_label(node) if disk_label is not None: image_info['disk_label'] = disk_label - image_info['node_uuid'] = node.uuid # Tell the client to download and write the image with the given args self._client.prepare_image(node, image_info) diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py index ce4be5c9ec..f8354647d3 100644 --- a/ironic/drivers/modules/deploy_utils.py +++ b/ironic/drivers/modules/deploy_utils.py @@ -363,7 +363,7 @@ def deploy_partition_image( def deploy_disk_image(address, port, iqn, lun, - image_path, node_uuid): + image_path, node_uuid, configdrive=None): """All-in-one function to deploy a whole disk image to a node. :param address: The iSCSI IP address. @@ -373,6 +373,8 @@ def deploy_disk_image(address, port, iqn, lun, :param image_path: Path for the instance's disk image. :param node_uuid: node's uuid. Used for logging. Currently not in use by this function but could be used in the future. + :param configdrive: Optional. Base64 encoded Gzipped configdrive content + or configdrive HTTP URL. :returns: a dictionary containing the key 'disk identifier' to identify the disk which was used for deployment. """ @@ -381,6 +383,10 @@ def deploy_disk_image(address, port, iqn, lun, disk_utils.populate_image(image_path, dev) disk_identifier = disk_utils.get_disk_identifier(dev) + if configdrive: + disk_utils.create_config_drive_partition(node_uuid, dev, + configdrive) + return {'disk identifier': disk_identifier} @@ -1181,6 +1187,7 @@ def parse_instance_info(node): # ensuring that it is possible i_info['swap_mb'] = info.get('swap_mb', 0) i_info['ephemeral_gb'] = info.get('ephemeral_gb', 0) + i_info['configdrive'] = info.get('configdrive') err_msg_invalid = _("Cannot validate parameter for driver deploy. " "Invalid parameter %(param)s. Reason: %(reason)s") for param in DISK_LAYOUT_PARAMS: diff --git a/ironic/drivers/modules/iscsi_deploy.py b/ironic/drivers/modules/iscsi_deploy.py index 3cecb4482e..74d67f8967 100644 --- a/ironic/drivers/modules/iscsi_deploy.py +++ b/ironic/drivers/modules/iscsi_deploy.py @@ -183,12 +183,13 @@ def get_deploy_info(node, address, iqn, port=None, lun='1'): _("Parameters %s were not passed to ironic" " for deploy.") % missing) + # configdrive is nullable + params['configdrive'] = i_info.get('configdrive') if is_whole_disk_image: return params - # configdrive and ephemeral_format are nullable + # ephemeral_format is nullable params['ephemeral_format'] = i_info.get('ephemeral_format') - params['configdrive'] = i_info.get('configdrive') return params diff --git a/ironic/tests/unit/drivers/modules/test_agent.py b/ironic/tests/unit/drivers/modules/test_agent.py index ca8cb474b7..ad88994b16 100644 --- a/ironic/tests/unit/drivers/modules/test_agent.py +++ b/ironic/tests/unit/drivers/modules/test_agent.py @@ -662,6 +662,7 @@ class TestAgentVendor(db_base.DbTestCase): expected_image_info = { 'urls': [test_temp_url], 'id': 'fake-image', + 'node_uuid': self.node.uuid, 'checksum': 'checksum', 'disk_format': 'qcow2', 'container_format': 'bare', diff --git a/ironic/tests/unit/drivers/modules/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/test_deploy_utils.py index ceb8c4ab23..683d60b465 100644 --- a/ironic/tests/unit/drivers/modules/test_deploy_utils.py +++ b/ironic/tests/unit/drivers/modules/test_deploy_utils.py @@ -874,8 +874,10 @@ class PhysicalWorkTestCase(tests_base.TestCase): self.assertEqual(root_uuid, uuid_dict_returned['root uuid']) mock_unlink.assert_called_once_with('configdrive-path') + @mock.patch.object(disk_utils, 'create_config_drive_partition', + autospec=True) @mock.patch.object(disk_utils, 'get_disk_identifier', autospec=True) - def test_deploy_whole_disk_image(self, mock_gdi): + def test_deploy_whole_disk_image(self, mock_gdi, create_config_drive_mock): """Check loosely all functions are called with right args.""" address = '127.0.0.1' port = 3306 @@ -904,10 +906,55 @@ class PhysicalWorkTestCase(tests_base.TestCase): mock.call.populate_image(image_path, dev)] uuid_dict_returned = utils.deploy_disk_image(address, port, iqn, lun, - image_path, node_uuid) + image_path, node_uuid, + configdrive=None) self.assertEqual(utils_calls_expected, utils_mock.mock_calls) self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls) + self.assertFalse(create_config_drive_mock.called) + self.assertEqual('0x12345678', uuid_dict_returned['disk identifier']) + + @mock.patch.object(disk_utils, 'create_config_drive_partition', + autospec=True) + @mock.patch.object(disk_utils, 'get_disk_identifier', autospec=True) + def test_deploy_whole_disk_image_with_config_drive(self, mock_gdi, + create_partition_mock): + """Check loosely all functions are called with right args.""" + address = '127.0.0.1' + port = 3306 + iqn = 'iqn.xyz' + lun = 1 + image_path = '/tmp/xyz/image' + node_uuid = "12345678-1234-1234-1234-1234567890abcxyz" + config_url = 'http://1.2.3.4/cd' + + dev = '/dev/fake' + utils_list = ['get_dev', 'discovery', 'login_iscsi', 'logout_iscsi', + 'delete_iscsi'] + + disk_utils_list = ['is_block_device', 'populate_image'] + utils_mock = self._mock_calls(utils_list, utils) + disk_utils_mock = self._mock_calls(disk_utils_list, disk_utils) + utils_mock.get_dev.return_value = dev + disk_utils_mock.is_block_device.return_value = True + mock_gdi.return_value = '0x12345678' + utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun), + mock.call.discovery(address, port), + mock.call.login_iscsi(address, port, iqn), + mock.call.logout_iscsi(address, port, iqn), + mock.call.delete_iscsi(address, port, iqn)] + + disk_utils_calls_expected = [mock.call.is_block_device(dev), + mock.call.populate_image(image_path, dev)] + + uuid_dict_returned = utils.deploy_disk_image(address, port, iqn, lun, + image_path, node_uuid, + configdrive=config_url) + + utils_mock.assert_has_calls(utils_calls_expected) + disk_utils_mock.assert_has_calls(disk_utils_calls_expected) + create_partition_mock.assert_called_once_with(node_uuid, dev, + config_url) self.assertEqual('0x12345678', uuid_dict_returned['disk identifier']) @mock.patch.object(common_utils, 'execute', autospec=True) diff --git a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py index 423839c979..7ca3771fa4 100644 --- a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py +++ b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py @@ -386,6 +386,17 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase): ret_val = self._test_get_deploy_info() self.assertEqual(3266, ret_val['port']) + def test_get_deploy_info_whole_disk_image(self): + instance_info = self.node.instance_info + instance_info['configdrive'] = 'My configdrive' + self.node.instance_info = instance_info + self.node.driver_internal_info['is_whole_disk_image'] = True + kwargs = {'address': '1.1.1.1', 'iqn': 'target-iqn'} + ret_val = iscsi_deploy.get_deploy_info(self.node, **kwargs) + self.assertEqual('1.1.1.1', ret_val['address']) + self.assertEqual('target-iqn', ret_val['iqn']) + self.assertEqual('My configdrive', ret_val['configdrive']) + @mock.patch.object(iscsi_deploy, 'continue_deploy', autospec=True) def test_do_agent_iscsi_deploy_okay(self, continue_deploy_mock): agent_client_mock = mock.MagicMock(spec_set=agent_client.AgentClient) diff --git a/releasenotes/notes/config-drive-support-for-whole-disk-images-in-iscsi-deploy-0193c5222a7cd129.yaml b/releasenotes/notes/config-drive-support-for-whole-disk-images-in-iscsi-deploy-0193c5222a7cd129.yaml new file mode 100644 index 0000000000..f043b1032b --- /dev/null +++ b/releasenotes/notes/config-drive-support-for-whole-disk-images-in-iscsi-deploy-0193c5222a7cd129.yaml @@ -0,0 +1,7 @@ +--- +features: + - Added configdrive support for whole disk images + for iSCSI based deploy. + This will work for UEFI only or BIOS only images. + It will not work for hybrid images which are + capable of booting from BIOS and UEFI boot mode.