summaryrefslogtreecommitdiff
path: root/ironic_python_agent
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2019-11-28 11:18:02 +0000
committerGerrit Code Review <review@openstack.org>2019-11-28 11:18:02 +0000
commit8e246f4482151ebbce4de552a6bad403aa0f37ea (patch)
treeed38dbf57d519ffdb105df2ffe967dc97424fa22 /ironic_python_agent
parentcdee7bb6e15b46901b2a9cf6b9cda57847c0e936 (diff)
parente4659c94cfffae88c8cc7879cd7130c8ed24e757 (diff)
downloadironic-python-agent-8e246f4482151ebbce4de552a6bad403aa0f37ea.tar.gz
Merge "RAID 5/6"
Diffstat (limited to 'ironic_python_agent')
-rw-r--r--ironic_python_agent/hardware.py14
-rw-r--r--ironic_python_agent/tests/unit/test_hardware.py248
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