Add LVM based image support to MD scenario

FIXME: Currently matches LVM and partitions.

Change-Id: Ie56d8cc7800ca64859c4e9cdf7511fd78b6f3a51
This commit is contained in:
Michal Nasiadka 2021-11-04 14:03:03 +01:00
parent c303bd971b
commit c95da0a8c3
4 changed files with 83 additions and 9 deletions

View File

@ -431,6 +431,13 @@ def md_restart(raid_device):
"""
try:
LOG.debug('Restarting software RAID device %s', raid_device)
# NOTE(mnasiadka): If the image is LVM based we need to clean
# dmsetup devices, because mdadm --stop will fail.
dmsetup_stdout, _ = il_utils.execute('dmsetup', 'table',
use_standard_locale=True)
if 'No devices found' not in dmsetup_stdout:
LOG.debug('Deactivating LVM')
il_utils.execute('dmsetup', 'remove_all')
component_devices = get_component_devices(raid_device)
il_utils.execute('mdadm', '--stop', raid_device)
il_utils.execute('mdadm', '--assemble', raid_device,
@ -554,6 +561,7 @@ def list_all_block_devices(block_type='disk',
report_json = json.loads(report)
except json.decoder.JSONDecodeError as ex:
LOG.error("Unable to decode lsblk output, invalid JSON: %s", ex)
raise
context = pyudev.Context()
devices_raw = report_json['blockdevices']
@ -609,11 +617,11 @@ def list_all_block_devices(block_type='disk',
{'device_raw': device_raw})
elif (devtype == 'md'
and (block_type == 'part'
or block_type == 'md')):
# NOTE(dszumski): Partitions on software RAID devices have type
# 'md'. This may also contain RAID devices in a broken state in
# rare occasions. See https://review.opendev.org/#/c/670807 for
# more detail.
or block_type == 'lvm')):
# NOTE(dszumski): Partitions and LVM volumes on software RAID
# devices have type 'md'. This may also contain RAID devices in
# a broken state in rare occasions. See
# https://review.opendev.org/#/c/670807 for more detail.
LOG.debug(
"TYPE detected to contain 'md', signifying a "
"RAID partition. Found: %(device_raw)s",

View File

@ -201,7 +201,9 @@ RAID_BLK_DEVICE_TEMPLATE = ("""
{"kname":"md0", "model":"RAID", "size":1765517033470, "rota":false,
"type":"raid1", "serial":null, "uuid":null, "partuuid":null, "wwn":"33"},
{"kname":"md1", "model":"RAID", "size":0, "rota":false, "type":"raid1",
"serial":null, "uuid":null, "partuuid":null, "wwn":null}
"serial":null, "uuid":null, "partuuid":null, "wwn":null},
{"kname":"dm-0", "model":"RAID", "size":1765517033470, "rota":false,
"type":"md", "uuid":null, "partuuid":null}
]
}
""")

View File

@ -1241,9 +1241,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
@mock.patch.object(hardware, '_get_device_info', autospec=True)
@mock.patch.object(pyudev.Devices, 'from_device_file', autospec=False)
@mock.patch.object(il_utils, 'execute', autospec=True)
def test_list_all_block_device(self, mocked_execute, mocked_udev,
mocked_dev_vendor, mock_listdir,
mock_readlink):
def test_list_all_block_devices(self, mocked_execute, mocked_udev,
mocked_dev_vendor, mock_listdir,
mock_readlink):
by_path_map = {
'/dev/disk/by-path/1:0:0:0': '../../dev/sda',
'/dev/disk/by-path/1:0:0:1': '../../dev/sdb',
@ -5439,6 +5439,66 @@ class TestModuleFunctions(base.IronicAgentTest):
@mock.patch.object(hardware, '_get_device_info',
lambda x, y, z: 'FooTastic')
@mock.patch.object(disk_utils, 'udev_settle', autospec=True)
@mock.patch.object(hardware.pyudev.Devices, "from_device_file",
autospec=False)
def test_list_all_block_devices_success_raid_lvm(self, mocked_fromdevfile,
mocked_udev,
mocked_readlink,
mocked_mpath,
mocked_execute):
mocked_readlink.return_value = '../../sda'
mocked_fromdevfile.return_value = {}
mocked_mpath.return_value = True
mocked_execute.side_effect = [
(hws.RAID_BLK_DEVICE_TEMPLATE, ''),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sda'),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sda1'),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sdb'),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sdb1'),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sda'),
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # md0p1
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # md0
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # md0
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # md1
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # dm-0
]
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-c', '/dev/sda1'),
mock.call('multipath', '-c', '/dev/sdb'),
mock.call('multipath', '-c', '/dev/sdb'),
mock.call('multipath', '-c', '/dev/sdb1'),
mock.call('multipath', '-c', '/dev/md0p1'),
mock.call('multipath', '-c', '/dev/md0'),
mock.call('multipath', '-c', '/dev/md0'),
mock.call('multipath', '-c', '/dev/md1'),
mock.call('multipath', '-c', '/dev/dm-0'),
]
result = hardware.list_all_block_devices(block_type='lvm',
ignore_empty=False)
mocked_execute.assert_has_calls(expected_calls)
hardware.LOG.error(result[0].__dict__)
self.assertEqual(RAID_BLK_DEVICE_TEMPLATE_DEVICES[3:4], result)
mocked_udev.assert_called_once_with()
@mock.patch.object(hardware, 'get_multipath_status', autospec=True)
@mock.patch.object(os, 'readlink', autospec=True)
@mock.patch.object(hardware, '_get_device_info',
lambda x, y, z: 'FooTastic')
@mock.patch.object(hardware, '_udev_settle', autospec=True)
@mock.patch.object(hardware.pyudev.Devices, "from_device_file",
autospec=False)
def test_list_all_block_devices_partuuid_success(

View File

@ -0,0 +1,4 @@
---
features:
- |
Add support for LVM based images in MD (software RAID) scenario.