summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRiccardo Pittau <elfosardo@gmail.com>2021-02-10 13:48:29 +0100
committerRiccardo Pittau <elfosardo@gmail.com>2021-02-17 10:11:13 +0100
commit7d7940d90401df59258dc1963d51c7c53b3e3cf0 (patch)
tree131dad17ba5be5db2d1788d77bc3a302c6da2767
parenta35761c21fa8ad7856723b0ec68ddc87db39c709 (diff)
downloadironic-python-agent-7d7940d90401df59258dc1963d51c7c53b3e3cf0.tar.gz
Move some raid specific functions to raid_utils
To reduce size of the hardware module and separate the raid specific code in raid_utils, we move some functions and adapt the tests. Change-Id: I73f6cf118575b627e66727d88d5567377c1999a0
-rw-r--r--ironic_python_agent/hardware.py81
-rw-r--r--ironic_python_agent/raid_utils.py87
-rw-r--r--ironic_python_agent/tests/unit/test_hardware.py34
-rw-r--r--ironic_python_agent/tests/unit/test_raid_utils.py114
4 files changed, 213 insertions, 103 deletions
diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py
index 8d50e354..12d90ea0 100644
--- a/ironic_python_agent/hardware.py
+++ b/ironic_python_agent/hardware.py
@@ -209,35 +209,6 @@ def _get_component_devices(raid_device):
return component_devices
-def _get_actual_component_devices(raid_device):
- """Get the component devices of a Software RAID device.
-
- Examine an md device and return its constituent devices.
-
- :param raid_device: A Software RAID block device name.
- :returns: A list of the component devices.
- """
- if not raid_device:
- return []
-
- try:
- out, _ = utils.execute('mdadm', '--detail', raid_device,
- use_standard_locale=True)
- except processutils.ProcessExecutionError as e:
- LOG.warning('Could not get component devices of %(dev)s: %(err)s',
- {'dev': raid_device, 'err': e})
- return []
-
- component_devices = []
- lines = out.splitlines()
- # the first line contains the md device itself
- for line in lines[1:]:
- device = re.findall(r'/dev/\w+', line)
- component_devices += device
-
- return component_devices
-
-
def _calc_memory(sys_dict):
physical = 0
for sys_child in sys_dict['children']:
@@ -1834,12 +1805,6 @@ class GenericHardwareManager(HardwareManager):
return self._do_create_configuration(node, ports, raid_config)
def _do_create_configuration(self, node, ports, raid_config):
- # incr starts to 1
- # It means md0 is on the partition 1, md1 on 2...
- # incr could be incremented if we ever decide, for example to create
- # some additional partitions here (boot partitions)
- incr = 1
-
# No 'software' controller: do nothing. If 'controller' is
# set to 'software' on only one of the drives, the validation
# code will catch it.
@@ -1948,45 +1913,7 @@ class GenericHardwareManager(HardwareManager):
# Create the RAID devices.
for index, logical_disk in enumerate(logical_disks):
- md_device = '/dev/md%d' % index
- component_devices = []
- for device in logical_disk['block_devices']:
- # The partition delimiter for all common harddrives (sd[a-z]+)
- part_delimiter = ''
- if 'nvme' in device:
- part_delimiter = 'p'
- component_devices.append(
- device + part_delimiter + str(index + incr))
- raid_level = logical_disk['raid_level']
- # The schema check allows '1+0', but mdadm knows it as '10'.
- if raid_level == '1+0':
- raid_level = '10'
- try:
- LOG.debug("Creating md device %(dev)s on %(comp)s",
- {'dev': md_device, 'comp': component_devices})
- utils.execute('mdadm', '--create', md_device, '--force',
- '--run', '--metadata=1', '--level', raid_level,
- '--raid-devices', len(component_devices),
- *component_devices)
- except processutils.ProcessExecutionError as e:
- msg = "Failed to create md device {} on {}: {}".format(
- md_device, ' '.join(component_devices), e)
- raise errors.SoftwareRAIDError(msg)
-
- # check for missing devices and re-add them
- actual_components = _get_actual_component_devices(md_device)
- missing = set(component_devices) - set(actual_components)
- for dev in missing:
- try:
- LOG.warning('Found %(device)s to be missing from %(md)s '
- '... re-adding!',
- {'device': dev, 'md': md_device})
- utils.execute('mdadm', '--add', md_device, dev,
- attempts=3, delay_on_retry=True)
- except processutils.ProcessExecutionError as e:
- msg = "Failed re-add {} to {}: {}".format(
- dev, md_device, e)
- raise errors.SoftwareRAIDError(msg)
+ raid_utils.create_raid_device(index, logical_disk)
LOG.info("Successfully created Software RAID")
@@ -2380,7 +2307,7 @@ def dispatch_to_managers(method, *args, **kwargs):
if getattr(manager, method, None):
try:
return getattr(manager, method)(*args, **kwargs)
- except(errors.IncompatibleHardwareMethodError):
+ except errors.IncompatibleHardwareMethodError:
LOG.debug('HardwareManager %(manager)s does not '
'support %(method)s',
{'manager': manager, 'method': method})
@@ -2424,10 +2351,6 @@ def cache_node(node):
expected root device to appear.
:param node: Ironic node object
- :param wait_for_disks: Default True switch to wait for disk setup to be
- completed so the node information can be aligned
- with the physical storage devices of the host.
- This is likely to be used in unit testing.
"""
global NODE
new_node = NODE is None or NODE['uuid'] != node['uuid']
diff --git a/ironic_python_agent/raid_utils.py b/ironic_python_agent/raid_utils.py
index d62b4280..962a6e9a 100644
--- a/ironic_python_agent/raid_utils.py
+++ b/ironic_python_agent/raid_utils.py
@@ -11,8 +11,10 @@
# limitations under the License.
import copy
+import re
from ironic_lib import utils as il_utils
+from oslo_concurrency import processutils
from oslo_log import log as logging
from ironic_python_agent import errors
@@ -26,6 +28,12 @@ LOG = logging.getLogger(__name__)
# https://www.rodsbooks.com/efi-bootloaders/principles.html
ESP_SIZE_MIB = 550
+# NOTE(rpittau) The partition number used to create a raid device.
+# Could be changed to variable if we ever decide, for example to create
+# some additional partitions (e.g. boot partitions), so md0 is on the
+# partition 1, md1 on the partition 2, and so on.
+RAID_PARTITION = 1
+
def get_block_devices_for_raid(block_devices, logical_disks):
"""Get block devices that are involved in the RAID configuration.
@@ -161,3 +169,82 @@ def create_raid_partition_tables(block_devices, partition_table_type,
parted_start_dict[dev_name] = calculate_raid_start(
target_boot_mode, partition_table_type, dev_name)
return parted_start_dict
+
+
+def _get_actual_component_devices(raid_device):
+ """Get the component devices of a Software RAID device.
+
+ Examine an md device and return its constituent devices.
+
+ :param raid_device: A Software RAID block device name.
+ :returns: A list of the component devices.
+ """
+ if not raid_device:
+ return []
+
+ try:
+ out, _ = utils.execute('mdadm', '--detail', raid_device,
+ use_standard_locale=True)
+ except processutils.ProcessExecutionError as e:
+ LOG.warning('Could not get component devices of %(dev)s: %(err)s',
+ {'dev': raid_device, 'err': e})
+ return []
+
+ component_devices = []
+ lines = out.splitlines()
+ # the first line contains the md device itself
+ for line in lines[1:]:
+ device = re.findall(r'/dev/\w+', line)
+ component_devices += device
+
+ return component_devices
+
+
+def create_raid_device(index, logical_disk):
+ """Create a raid device.
+
+ :param index: the index of the resulting md device.
+ :param logical_disk: the logical disk containing the devices used to
+ crete the raid.
+ :raise: errors.SoftwareRAIDError if not able to create the raid device
+ or fails to re-add a device to a raid.
+ """
+ md_device = '/dev/md%d' % index
+ component_devices = []
+ for device in logical_disk['block_devices']:
+ # The partition delimiter for all common harddrives (sd[a-z]+)
+ part_delimiter = ''
+ if 'nvme' in device:
+ part_delimiter = 'p'
+ component_devices.append(
+ device + part_delimiter + str(index + RAID_PARTITION))
+ raid_level = logical_disk['raid_level']
+ # The schema check allows '1+0', but mdadm knows it as '10'.
+ if raid_level == '1+0':
+ raid_level = '10'
+ try:
+ LOG.debug("Creating md device %(dev)s on %(comp)s",
+ {'dev': md_device, 'comp': component_devices})
+ utils.execute('mdadm', '--create', md_device, '--force',
+ '--run', '--metadata=1', '--level', raid_level,
+ '--raid-devices', len(component_devices),
+ *component_devices)
+ except processutils.ProcessExecutionError as e:
+ msg = "Failed to create md device {} on {}: {}".format(
+ md_device, ' '.join(component_devices), e)
+ raise errors.SoftwareRAIDError(msg)
+
+ # check for missing devices and re-add them
+ actual_components = _get_actual_component_devices(md_device)
+ missing = set(component_devices) - set(actual_components)
+ for dev in missing:
+ try:
+ LOG.warning('Found %(device)s to be missing from %(md)s '
+ '... re-adding!',
+ {'device': dev, 'md': md_device})
+ utils.execute('mdadm', '--add', md_device, dev,
+ attempts=3, delay_on_retry=True)
+ except processutils.ProcessExecutionError as e:
+ msg = "Failed re-add {} to {}: {}".format(
+ dev, md_device, e)
+ raise errors.SoftwareRAIDError(msg)
diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py
index 77281558..19c33a62 100644
--- a/ironic_python_agent/tests/unit/test_hardware.py
+++ b/ironic_python_agent/tests/unit/test_hardware.py
@@ -29,6 +29,7 @@ from stevedore import extension
from ironic_python_agent import errors
from ironic_python_agent import hardware
from ironic_python_agent import netutils
+from ironic_python_agent import raid_utils
from ironic_python_agent.tests.unit import base
from ironic_python_agent.tests.unit.samples import hardware_samples as hws
from ironic_python_agent import utils
@@ -2400,7 +2401,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mocked_create.assert_called_once_with(self.hardware, self.node, [],
raid_config)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
@@ -2484,7 +2485,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call(x) for x in ['/dev/sda', '/dev/sdb']
])
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(utils, 'get_node_boot_mode', lambda node: 'bios')
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
@@ -2574,7 +2575,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'/dev/sda2', '/dev/sdb2', '/dev/sdc2')])
self.assertEqual(raid_config, result)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(utils, 'get_node_boot_mode', lambda node: 'bios')
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
@@ -2678,7 +2679,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'/dev/sda2', '/dev/sdb2', '/dev/sdc2', '/dev/sdd2')])
self.assertEqual(raid_config, result)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
return_value=[])
@@ -2751,7 +2752,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'/dev/sda2', '/dev/sdb2')])
self.assertEqual(raid_config, result)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
return_value=[])
@@ -2830,7 +2831,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'/dev/sda2', '/dev/sdb2')])
self.assertEqual(raid_config, result)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
return_value=[])
@@ -2904,7 +2905,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'/dev/sda2', '/dev/sdb2')])
self.assertEqual(raid_config, result)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
return_value=[])
@@ -2980,7 +2981,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'/dev/sda2', '/dev/sdb2')])
self.assertEqual(raid_config, result)
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(utils, 'get_node_boot_mode', lambda node: 'bios')
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
@@ -3334,7 +3335,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
result = self.hardware.create_configuration(self.node, [])
self.assertEqual(result, {})
- @mock.patch.object(hardware, '_get_actual_component_devices',
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions', autospec=True,
return_value=[])
@@ -3476,21 +3477,6 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.node, [])
@mock.patch.object(utils, 'execute', autospec=True)
- def test__get_actual_component_devices(self, mocked_execute):
- mocked_execute.side_effect = [(hws.MDADM_DETAIL_OUTPUT, '')]
- component_devices = hardware._get_actual_component_devices(
- '/dev/md0')
- self.assertEqual(['/dev/vde1', '/dev/vdf1'], component_devices)
-
- @mock.patch.object(utils, 'execute', autospec=True)
- def test__get_actual_component_devices_broken_raid0(self, mocked_execute):
- mocked_execute.side_effect = [(hws.MDADM_DETAIL_OUTPUT_BROKEN_RAID0,
- '')]
- component_devices = hardware._get_actual_component_devices(
- '/dev/md126')
- self.assertEqual(['/dev/sda2'], component_devices)
-
- @mock.patch.object(utils, 'execute', autospec=True)
def test__get_md_uuid(self, mocked_execute):
mocked_execute.side_effect = [(hws.MDADM_DETAIL_OUTPUT, '')]
md_uuid = hardware._get_md_uuid('/dev/md0')
diff --git a/ironic_python_agent/tests/unit/test_raid_utils.py b/ironic_python_agent/tests/unit/test_raid_utils.py
new file mode 100644
index 00000000..30a13065
--- /dev/null
+++ b/ironic_python_agent/tests/unit/test_raid_utils.py
@@ -0,0 +1,114 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from unittest import mock
+
+from oslo_concurrency import processutils
+
+from ironic_python_agent import errors
+from ironic_python_agent import raid_utils
+from ironic_python_agent.tests.unit import base
+from ironic_python_agent.tests.unit.samples import hardware_samples as hws
+from ironic_python_agent import utils
+
+
+class TestRaidUtils(base.IronicAgentTest):
+
+ @mock.patch.object(utils, 'execute', autospec=True)
+ def test__get_actual_component_devices(self, mock_execute):
+ mock_execute.side_effect = [(hws.MDADM_DETAIL_OUTPUT, '')]
+ component_devices = raid_utils._get_actual_component_devices(
+ '/dev/md0')
+ self.assertEqual(['/dev/vde1', '/dev/vdf1'], component_devices)
+
+ @mock.patch.object(utils, 'execute', autospec=True)
+ def test__get_actual_component_devices_broken_raid0(self, mock_execute):
+ mock_execute.side_effect = [(hws.MDADM_DETAIL_OUTPUT_BROKEN_RAID0, '')]
+ component_devices = raid_utils._get_actual_component_devices(
+ '/dev/md126')
+ self.assertEqual(['/dev/sda2'], component_devices)
+
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
+ autospec=True)
+ @mock.patch.object(utils, 'execute', autospec=True)
+ def test_create_raid_device(self, mock_execute, mocked_components):
+ logical_disk = {
+ "block_devices": ['/dev/sda', '/dev/sdb', '/dev/sdc'],
+ "raid_level": "1",
+ }
+ mocked_components.return_value = ['/dev/sda1',
+ '/dev/sdb1',
+ '/dev/sdc1']
+
+ raid_utils.create_raid_device(0, logical_disk)
+
+ mock_execute.assert_called_once_with(
+ 'mdadm', '--create', '/dev/md0', '--force', '--run',
+ '--metadata=1', '--level', '1', '--raid-devices', 3,
+ '/dev/sda1', '/dev/sdb1', '/dev/sdc1')
+
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
+ autospec=True)
+ @mock.patch.object(utils, 'execute', autospec=True)
+ def test_create_raid_device_missing_device(self, mock_execute,
+ mocked_components):
+ logical_disk = {
+ "block_devices": ['/dev/sda', '/dev/sdb', '/dev/sdc'],
+ "raid_level": "1",
+ }
+ mocked_components.return_value = ['/dev/sda1',
+ '/dev/sdc1']
+
+ raid_utils.create_raid_device(0, logical_disk)
+
+ expected_calls = [
+ mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
+ '--metadata=1', '--level', '1', '--raid-devices', 3,
+ '/dev/sda1', '/dev/sdb1', '/dev/sdc1'),
+ mock.call('mdadm', '--add', '/dev/md0', '/dev/sdb1',
+ attempts=3, delay_on_retry=True)
+ ]
+ self.assertEqual(mock_execute.call_count, 2)
+ mock_execute.assert_has_calls(expected_calls)
+
+ @mock.patch.object(utils, 'execute', autospec=True)
+ def test_create_raid_device_fail_create_device(self, mock_execute):
+ logical_disk = {
+ "block_devices": ['/dev/sda', '/dev/sdb', '/dev/sdc'],
+ "raid_level": "1",
+ }
+ mock_execute.side_effect = processutils.ProcessExecutionError()
+
+ self.assertRaisesRegex(errors.SoftwareRAIDError,
+ "Failed to create md device /dev/md0",
+ raid_utils.create_raid_device, 0,
+ logical_disk)
+
+ @mock.patch.object(raid_utils, '_get_actual_component_devices',
+ autospec=True)
+ @mock.patch.object(utils, 'execute', autospec=True)
+ def test_create_raid_device_fail_read_device(self, mock_execute,
+ mocked_components):
+ logical_disk = {
+ "block_devices": ['/dev/sda', '/dev/sdb', '/dev/sdc'],
+ "raid_level": "1",
+ }
+ mock_execute.side_effect = [mock.Mock,
+ processutils.ProcessExecutionError()]
+
+ mocked_components.return_value = ['/dev/sda1',
+ '/dev/sdc1']
+
+ self.assertRaisesRegex(errors.SoftwareRAIDError,
+ "Failed re-add /dev/sdb1 to /dev/md0",
+ raid_utils.create_raid_device, 0,
+ logical_disk)