diff options
| author | Zuul <zuul@review.opendev.org> | 2019-11-28 11:18:02 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2019-11-28 11:18:02 +0000 |
| commit | 8e246f4482151ebbce4de552a6bad403aa0f37ea (patch) | |
| tree | ed38dbf57d519ffdb105df2ffe967dc97424fa22 /ironic_python_agent | |
| parent | cdee7bb6e15b46901b2a9cf6b9cda57847c0e936 (diff) | |
| parent | e4659c94cfffae88c8cc7879cd7130c8ed24e757 (diff) | |
| download | ironic-python-agent-8e246f4482151ebbce4de552a6bad403aa0f37ea.tar.gz | |
Merge "RAID 5/6"
Diffstat (limited to 'ironic_python_agent')
| -rw-r--r-- | ironic_python_agent/hardware.py | 14 | ||||
| -rw-r--r-- | ironic_python_agent/tests/unit/test_hardware.py | 248 |
2 files changed, 260 insertions, 2 deletions
diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index 412f6110..79f7ac8f 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -52,7 +52,7 @@ UNIT_CONVERTER.define('MB = 1048576 bytes') _MEMORY_ID_RE = re.compile(r'^memory(:\d+)?$') NODE = None -SUPPORTED_SOFTWARE_RAID_LEVELS = frozenset(['0', '1', '1+0']) +SUPPORTED_SOFTWARE_RAID_LEVELS = frozenset(['0', '1', '1+0', '5', '6']) def _get_device_info(dev, devclass, field): @@ -1822,7 +1822,17 @@ class GenericHardwareManager(HardwareManager): msg = ("Software RAID configuration does not support " "RAID level %s" % current_level) raid_errors.append(msg) - + physical_device_count = len(self.list_block_devices()) + if current_level == '5' and physical_device_count < 3: + msg = ("Software RAID configuration is not possible for " + "RAID level 5 with only %s block devices found." + % physical_device_count) + raid_errors.append(msg) + if current_level == '6' and physical_device_count < 4: + msg = ("Software RAID configuration is not possible for " + "RAID level 6 with only %s block devices found." + % physical_device_count) + raid_errors.append(msg) if raid_errors: error = ('Could not validate Software RAID config for %(node)s: ' '%(errors)s') % {'node': node['uuid'], diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py index 169baa39..1caa0d93 100644 --- a/ironic_python_agent/tests/unit/test_hardware.py +++ b/ironic_python_agent/tests/unit/test_hardware.py @@ -2773,6 +2773,178 @@ class TestGenericHardwareManager(base.IronicAgentTest): self.assertEqual(raid_config, result) @mock.patch.object(utils, 'execute', autospec=True) + def test_create_configuration_raid_5(self, mocked_execute): + node = self.node + raid_config = { + "logical_disks": [ + { + "size_gb": "10", + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": "MAX", + "raid_level": "5", + "controller": "software", + }, + ] + } + node['target_raid_config'] = raid_config + device1 = hardware.BlockDevice('/dev/sda', 'sda', 107374182400, True) + device2 = hardware.BlockDevice('/dev/sdb', 'sdb', 107374182400, True) + device3 = hardware.BlockDevice('/dev/sdc', 'sdc', 107374182400, True) + self.hardware.list_block_devices = mock.Mock() + self.hardware.list_block_devices.return_value = [device1, device2, + device3] + + mocked_execute.side_effect = [ + None, # mklabel sda + ('42', None), # sgdisk -F sda + None, # mklabel sdb + ('42', None), # sgdisk -F sdb + None, # mklabel sdc + ('42', None), # sgdisk -F sdc + None, None, # parted + partx sda + None, None, # parted + partx sdb + None, None, # parted + partx sdc + None, None, # parted + partx sda + None, None, # parted + partx sdb + None, None, # parted + partx sdc + None, None # mdadms + ] + + result = self.hardware.create_configuration(node, []) + + mocked_execute.assert_has_calls([ + mock.call('parted', '/dev/sda', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sda'), + mock.call('parted', '/dev/sdb', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sdb'), + mock.call('parted', '/dev/sdc', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sdc'), + mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sda', check_exit_code=False), + mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sdb', check_exit_code=False), + mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sdc', check_exit_code=False), + mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sda', check_exit_code=False), + mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sdb', check_exit_code=False), + mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sdc', check_exit_code=False), + mock.call('mdadm', '--create', '/dev/md0', '--force', '--run', + '--metadata=1', '--level', '1', '--raid-devices', 3, + '/dev/sda1', '/dev/sdb1', '/dev/sdc1'), + mock.call('mdadm', '--create', '/dev/md1', '--force', '--run', + '--metadata=1', '--level', '5', '--raid-devices', 3, + '/dev/sda2', '/dev/sdb2', '/dev/sdc2')]) + self.assertEqual(raid_config, result) + + @mock.patch.object(utils, 'execute', autospec=True) + def test_create_configuration_raid_6(self, mocked_execute): + node = self.node + raid_config = { + "logical_disks": [ + { + "size_gb": "10", + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": "MAX", + "raid_level": "6", + "controller": "software", + }, + ] + } + node['target_raid_config'] = raid_config + device1 = hardware.BlockDevice('/dev/sda', 'sda', 107374182400, True) + device2 = hardware.BlockDevice('/dev/sdb', 'sdb', 107374182400, True) + device3 = hardware.BlockDevice('/dev/sdc', 'sdc', 107374182400, True) + device4 = hardware.BlockDevice('/dev/sdd', 'sdd', 107374182400, True) + self.hardware.list_block_devices = mock.Mock() + self.hardware.list_block_devices.return_value = [device1, device2, + device3, device4] + + mocked_execute.side_effect = [ + None, # mklabel sda + ('42', None), # sgdisk -F sda + None, # mklabel sdb + ('42', None), # sgdisk -F sdb + None, # mklabel sdc + ('42', None), # sgdisk -F sdc + None, # mklabel sdd + ('42', None), # sgdisk -F sdd + None, None, # parted + partx sda + None, None, # parted + partx sdb + None, None, # parted + partx sdc + None, None, # parted + partx sdd + None, None, # parted + partx sda + None, None, # parted + partx sdb + None, None, # parted + partx sdc + None, None, # parted + partx sdd + None, None # mdadms + ] + + result = self.hardware.create_configuration(node, []) + + mocked_execute.assert_has_calls([ + mock.call('parted', '/dev/sda', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sda'), + mock.call('parted', '/dev/sdb', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sdb'), + mock.call('parted', '/dev/sdc', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sdc'), + mock.call('parted', '/dev/sdd', '-s', '--', 'mklabel', + 'msdos'), + mock.call('sgdisk', '-F', '/dev/sdd'), + mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sda', check_exit_code=False), + mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sdb', check_exit_code=False), + mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sdc', check_exit_code=False), + mock.call('parted', '/dev/sdd', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '42s', '10GiB'), + mock.call('partx', '-u', '/dev/sdd', check_exit_code=False), + mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sda', check_exit_code=False), + mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sdb', check_exit_code=False), + mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sdc', check_exit_code=False), + mock.call('parted', '/dev/sdd', '-s', '-a', 'optimal', '--', + 'mkpart', 'primary', '10GiB', '-1'), + mock.call('partx', '-u', '/dev/sdd', check_exit_code=False), + mock.call('mdadm', '--create', '/dev/md0', '--force', '--run', + '--metadata=1', '--level', '1', '--raid-devices', 4, + '/dev/sda1', '/dev/sdb1', '/dev/sdc1', '/dev/sdd1'), + mock.call('mdadm', '--create', '/dev/md1', '--force', '--run', + '--metadata=1', '--level', '6', '--raid-devices', 4, + '/dev/sda2', '/dev/sdb2', '/dev/sdc2', '/dev/sdd2')]) + self.assertEqual(raid_config, result) + + @mock.patch.object(utils, 'execute', autospec=True) def test_create_configuration_no_max(self, mocked_execute): node = self.node raid_config = { @@ -2941,8 +3113,10 @@ class TestGenericHardwareManager(base.IronicAgentTest): partition1 = hardware.BlockDevice('/dev/sdb1', 'sdb1', 268435456, True) self.hardware.list_block_devices = mock.Mock() self.hardware.list_block_devices.side_effect = [ + [device1, device2], # pre-flight validation call [device1, device2], [device1, device2, partition1]] + self.assertRaises(errors.SoftwareRAIDError, self.hardware.create_configuration, self.node, []) @@ -2974,6 +3148,9 @@ class TestGenericHardwareManager(base.IronicAgentTest): [device1, device2], [device1, device2], [device1, device2], + [device1, device2], + [device1, device2], + [device1, device2], [device1, device2]] # partition table creation @@ -3009,6 +3186,74 @@ class TestGenericHardwareManager(base.IronicAgentTest): self.hardware.create_configuration, self.node, []) + @mock.patch.object(utils, 'execute', autospec=True) + def test_create_configuration_device_handling_failures_raid5( + self, mocked_execute): + raid_config = { + "logical_disks": [ + { + "size_gb": "100", + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": "MAX", + "raid_level": "5", + "controller": "software", + }, + ] + } + self.node['target_raid_config'] = raid_config + device1 = hardware.BlockDevice('/dev/sda', 'sda', 107374182400, True) + device2 = hardware.BlockDevice('/dev/sdb', 'sdb', 107374182400, True) + self.hardware.list_block_devices = mock.Mock() + self.hardware.list_block_devices.side_effect = [ + [device1, device2], + [device1, device2]] + + # validation configuration explicitly fails before any action + error_regex = ("Software RAID configuration is not possible for " + "RAID level 5 with only 2 block devices found.") + # Execute is actually called for listing_block_devices + self.assertFalse(mocked_execute.called) + self.assertRaisesRegex(errors.SoftwareRAIDError, error_regex, + self.hardware.create_configuration, + self.node, []) + + @mock.patch.object(utils, 'execute', autospec=True) + def test_create_configuration_device_handling_failures_raid6( + self, mocked_execute): + raid_config = { + "logical_disks": [ + { + "size_gb": "100", + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": "MAX", + "raid_level": "6", + "controller": "software", + }, + ] + } + self.node['target_raid_config'] = raid_config + device1 = hardware.BlockDevice('/dev/sda', 'sda', 107374182400, True) + device2 = hardware.BlockDevice('/dev/sdb', 'sdb', 107374182400, True) + device3 = hardware.BlockDevice('/dev/sdc', 'sdc', 107374182400, True) + self.hardware.list_block_devices = mock.Mock() + self.hardware.list_block_devices.side_effect = [ + [device1, device2, device3], + [device1, device2, device3]] + # pre-creation validation fails as insufficent number of devices found + error_regex = ("Software RAID configuration is not possible for " + "RAID level 6 with only 3 block devices found.") + # Execute is actually called for listing_block_devices + self.assertFalse(mocked_execute.called) + self.assertRaisesRegex(errors.SoftwareRAIDError, error_regex, + self.hardware.create_configuration, + self.node, []) + def test_create_configuration_empty_target_raid_config(self): self.node['target_raid_config'] = {} result = self.hardware.create_configuration(self.node, []) @@ -3107,6 +3352,9 @@ class TestGenericHardwareManager(base.IronicAgentTest): [device1, device2], [device1, device2], [device1, device2], + [device1, device2], + [device1, device2], + [device1, device2], [device1, device2]] # partition table creation |
