diff --git a/ironic_lib/disk_utils.py b/ironic_lib/disk_utils.py index 36558913..4859fa38 100644 --- a/ironic_lib/disk_utils.py +++ b/ironic_lib/disk_utils.py @@ -719,6 +719,28 @@ def _fix_gpt_structs(device, node_uuid): raise exception.InstanceDeployFailure(msg) +def fix_gpt_partition(device, node_uuid): + """Fix GPT partition + + Fix GPT table information when image is written to a disk which + has a bigger extend (e.g. 30GB image written on a 60Gb physical disk). + + :param device: The device path. + :param node_uuid: UUID of the Node. + :raises: InstanceDeployFailure if exception is caught. + """ + try: + disk_is_gpt_partitioned = _is_disk_gpt_partitioned(device, node_uuid) + if disk_is_gpt_partitioned: + _fix_gpt_structs(device, node_uuid) + except Exception as e: + msg = (_('Failed to fix GPT partition on disk %(disk)s ' + 'for node %(node)s. Error: %(error)s') % + {'disk': device, 'node': node_uuid, 'error': e}) + LOG.error(msg) + raise exception.InstanceDeployFailure(msg) + + def create_config_drive_partition(node_uuid, device, configdrive): """Create a partition for config drive @@ -751,6 +773,7 @@ def create_config_drive_partition(node_uuid, device, configdrive): "device: %(dev)s for node %(node)s", {'dev': device, 'size': confdrive_mb, 'node': node_uuid}) + fix_gpt_partition(device, node_uuid) if config_drive_part: LOG.debug("Configdrive for node %(node)s exists at " "%(part)s", @@ -759,7 +782,6 @@ def create_config_drive_partition(node_uuid, device, configdrive): cur_parts = set(part['number'] for part in list_partitions(device)) if _is_disk_gpt_partitioned(device, node_uuid): - _fix_gpt_structs(device, node_uuid) create_option = '0:-%dMB:0' % MAX_CONFIG_DRIVE_SIZE_MB utils.execute('sgdisk', '-n', create_option, device, run_as_root=True) diff --git a/ironic_lib/tests/test_disk_utils.py b/ironic_lib/tests/test_disk_utils.py index f9b9c7a1..2b7f8ba1 100644 --- a/ironic_lib/tests/test_disk_utils.py +++ b/ironic_lib/tests/test_disk_utils.py @@ -997,6 +997,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) @mock.patch.object(disk_utils, 'dd', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_fix_gpt_structs', autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', @@ -1009,8 +1011,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) def test_create_partition_exists(self, mock_get_configdrive, mock_get_labelled_partition, - mock_list_partitions, - mock_is_disk_gpt, mock_fix_gpt, + mock_list_partitions, mock_is_disk_gpt, + mock_fix_gpt, mock_fix_gpt_partition, mock_dd, mock_unlink, mock_execute): config_url = 'http://1.2.3.4/cd' configdrive_part = '/dev/fake-part1' @@ -1021,13 +1023,15 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_configdrive.return_value = (configdrive_mb, configdrive_file) disk_utils.create_config_drive_partition(self.node_uuid, self.dev, config_url) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_get_configdrive.assert_called_with(config_url, self.node_uuid) mock_get_labelled_partition.assert_called_with(self.dev, self.config_part_label, self.node_uuid) self.assertFalse(mock_list_partitions.called) - self.assertFalse(mock_is_disk_gpt.called) self.assertFalse(mock_execute.called) + self.assertFalse(mock_is_disk_gpt.called) + self.assertFalse(mock_fix_gpt.called) mock_dd.assert_called_with(configdrive_file, configdrive_part) mock_unlink.assert_called_with(configdrive_file) @@ -1036,6 +1040,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) @mock.patch.object(disk_utils, 'dd', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_fix_gpt_structs', autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', @@ -1049,8 +1055,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): def test_create_partition_gpt(self, mock_get_configdrive, mock_get_labelled_partition, mock_list_partitions, mock_is_disk_gpt, - mock_fix_gpt, mock_dd, mock_unlink, - mock_execute): + mock_fix_gpt, mock_fix_gpt_partition, + mock_dd, mock_unlink, mock_execute): config_url = 'http://1.2.3.4/cd' configdrive_file = '/tmp/xyz' configdrive_mb = 10 @@ -1091,7 +1097,9 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): self.assertEqual(2, mock_list_partitions.call_count) mock_is_disk_gpt.assert_called_with(self.dev, self.node_uuid) - mock_fix_gpt.assert_called_with(self.dev, self.node_uuid) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) + self.assertFalse(mock_fix_gpt.called) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_dd.assert_called_with(configdrive_file, expected_part) mock_unlink.assert_called_with(configdrive_file) @@ -1104,6 +1112,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) @mock.patch.object(disk_utils, '_is_disk_larger_than_max_size', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_fix_gpt_structs', autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', @@ -1118,6 +1128,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_labelled_partition, mock_list_partitions, mock_is_disk_gpt, mock_fix_gpt, + mock_fix_gpt_partition, mock_disk_exceeds, mock_dd, mock_unlink, mock_log, mock_execute, mock_count, disk_size_exceeds_max=False, @@ -1186,10 +1197,12 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): ]) self.assertEqual(2, mock_list_partitions.call_count) mock_is_disk_gpt.assert_called_with(self.dev, self.node_uuid) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_disk_exceeds.assert_called_with(self.dev, self.node_uuid) mock_dd.assert_called_with(configdrive_file, expected_part) mock_unlink.assert_called_with(configdrive_file) self.assertFalse(mock_fix_gpt.called) + self.assertFalse(mock_fix_gpt.called) mock_count.assert_called_with(self.dev) def test__create_partition_mbr_disk_under_2TB(self): @@ -1220,6 +1233,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) @mock.patch.object(disk_utils, '_is_disk_larger_than_max_size', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_fix_gpt_structs', autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', @@ -1234,6 +1249,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_labelled_partition, mock_list_partitions, mock_is_disk_gpt, mock_fix_gpt, + mock_fix_gpt_partition, mock_disk_exceeds, mock_dd, mock_unlink, mock_execute, mock_count): @@ -1276,6 +1292,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): 'fat32', '-64MiB', '-0', run_as_root=True) self.assertEqual(2, mock_list_partitions.call_count) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_is_disk_gpt.assert_called_with(self.dev, self.node_uuid) mock_disk_exceeds.assert_called_with(self.dev, self.node_uuid) self.assertFalse(mock_fix_gpt.called) @@ -1291,6 +1308,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) @mock.patch.object(disk_utils, '_is_disk_larger_than_max_size', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_fix_gpt_structs', autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', @@ -1305,6 +1324,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_labelled_partition, mock_list_partitions, mock_is_disk_gpt, mock_fix_gpt, + mock_fix_gpt_partition, mock_disk_exceeds, mock_dd, mock_unlink, mock_execute, mock_count): @@ -1341,6 +1361,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): 'fat32', '-64MiB', '-0', run_as_root=True) self.assertEqual(1, mock_list_partitions.call_count) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_is_disk_gpt.assert_called_with(self.dev, self.node_uuid) mock_disk_exceeds.assert_called_with(self.dev, self.node_uuid) self.assertFalse(mock_fix_gpt.called) @@ -1353,6 +1374,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) @mock.patch.object(disk_utils, 'dd', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_fix_gpt_structs', autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', @@ -1367,6 +1390,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_labelled_partition, mock_list_partitions, mock_is_disk_gpt, mock_fix_gpt, + mock_fix_gpt_partition, mock_dd, mock_unlink, mock_count): config_url = 'http://1.2.3.4/cd' @@ -1396,6 +1420,7 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_configdrive.assert_called_with(config_url, self.node_uuid) self.assertEqual(1, mock_list_partitions.call_count) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_is_disk_gpt.assert_called_with(self.dev, self.node_uuid) self.assertFalse(mock_fix_gpt.called) self.assertFalse(mock_dd.called) @@ -1431,6 +1456,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): @mock.patch.object(utils, 'execute', autospec=True) @mock.patch.object(utils, 'unlink_without_raise', autospec=True) + @mock.patch.object(disk_utils, 'fix_gpt_partition', + autospec=True) @mock.patch.object(disk_utils, '_is_disk_gpt_partitioned', autospec=True) @mock.patch.object(disk_utils, '_get_labelled_partition', @@ -1439,7 +1466,8 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): autospec=True) def test_create_partition_conf_drive_error_counting( self, mock_get_configdrive, mock_get_labelled_partition, - mock_is_disk_gpt, mock_unlink, mock_execute, mock_count): + mock_is_disk_gpt, mock_fix_gpt_partition, + mock_unlink, mock_execute, mock_count): config_url = 'http://1.2.3.4/cd' configdrive_file = '/tmp/xyz' configdrive_mb = 10 @@ -1456,5 +1484,6 @@ class WholeDiskConfigDriveTestCases(base.IronicLibTestCase): mock_get_configdrive.assert_called_with(config_url, self.node_uuid) mock_unlink.assert_called_with(configdrive_file) + mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid) mock_is_disk_gpt.assert_called_with(self.dev, self.node_uuid) mock_count.assert_called_once_with(self.dev)