From a79512bf8e2affbec4af5477a05c7114f545fbfb Mon Sep 17 00:00:00 2001 From: Matheus Guilhermino Date: Thu, 5 Jan 2023 16:38:13 -0300 Subject: [PATCH] Refactor inventory items for multipath support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor multipath related items to properly detect and parse multipath disks and partitions. Persistent multipath names use the /dev/disk/by-id/wwn-* paths to specify disks while HDD/SSD/NVMe disks use /dev/disk/by-path persistent names. Test Plan: PASS - AIO-SX: HPE multipath install/bootstrap/unlock PASS - AIO-SX: Qemu virtual multipath install/bootstrap/unlock PASS - AIO-DX: Qemu virtual multipath install/bootstrap/unlock PASS - AIO-DX+: Qemu virtual multipath install/bootstrap/unlock PASS - 2+2 (controller storage): Qemu virtual multipath install/bootstrap/unlock PASS - 2+2+2 (dedicated storage): Qemu virtual multipath install/bootstrap/unlock PASS - Add OSD ceph storage configuration (AIO-SX) PASS - Expand CGTS volume group using extra disk (Partition) (AIO-SX) PASS - Expand CGTS volume group using extra disk (disk) (AIO-SX) PASS - Add nova local volume group using extra disk (AIO-SX) PASS - App pod that alocates and writes into a PVC (AIO-SX) PASS - Local disk Commands (Disk API) - AIO-SX/DX - host-disk-list - host-disk-show - host-disk-partition-list - host-disk-partition-show - host-pv-list - host-pv-show - host-stor-list - host-stor-show - host-lvg-list - host-lvg-show - host-pv-add PASS - Create nova-local volume group PASS - Local disk Commands on AIO-DX after swact Regression: PASS - AIO-SX: Non-multipath install/bootstrap/unlock (NVME) PASS - AIO-DX: Non-multipath install/bootstrap/unlock (SSD) PASS - 2+2: Non-multipath install/bootstrap/unlock (SSD) PASS - 2+2+2 : Non-multipath install/bootstrap/unlock (SSD and HD) PASS - Distributed cloud: Non-multipath install/bootstrap/unlock Change-Id: I40b7b14d50879a74035af55b001a1e6d5a9e8272 Depends-On: https://review.opendev.org/c/starlingx/tools/+/860590 Story: 2010046 Task: 47119 Co-Authored-By: Robert Church Signed-off-by: Matheus Guilhermino Signed-off-by: Robert Church --- .../sysinv/sysinv/scripts/manage-partitions | 12 ++----- .../sysinv/sysinv/scripts/partition_info.sh | 4 +-- sysinv/sysinv/sysinv/sysinv/agent/disk.py | 6 ++-- .../sysinv/sysinv/sysinv/agent/partition.py | 11 +++--- .../sysinv/api/controllers/v1/partition.py | 10 ++++-- .../sysinv/api/controllers/v1/storage.py | 6 +++- sysinv/sysinv/sysinv/sysinv/common/utils.py | 36 ++++++------------- 7 files changed, 39 insertions(+), 46 deletions(-) diff --git a/sysinv/sysinv/sysinv/scripts/manage-partitions b/sysinv/sysinv/sysinv/scripts/manage-partitions index 56144bfe4c..fdfdd39f41 100755 --- a/sysinv/sysinv/sysinv/scripts/manage-partitions +++ b/sysinv/sysinv/sysinv/scripts/manage-partitions @@ -3,7 +3,7 @@ # # vim: tabstop=4 shiftwidth=4 softtabstop=4 # -# Copyright (c) 2017-2018 Wind River Systems, Inc. +# Copyright (c) 2017-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -170,15 +170,9 @@ def _get_disk_device_path(part_device_path): :returns the device path of the disk on which the partition resides """ disk_device_path = "" - if 'by-path' in part_device_path: - disk_device_path = re.match('(/dev/disk/by-path/(.+))-part([0-9]+)', + if 'by-path' in part_device_path or 'by-id' in part_device_path: + disk_device_path = re.match('(/dev/disk/by-(path|id)/(.+))-part([0-9]+)', part_device_path).group(1) - if constants.DEVICE_NAME_MPATH in part_device_path: - match_path = re.match('(/dev/disk/by-id/.+)-part([0-9]+)(-mpath.*)', - part_device_path) - if match_path: - disk_device_path = match_path.group(1) + match_path.group(3) - return disk_device_path diff --git a/sysinv/sysinv/sysinv/scripts/partition_info.sh b/sysinv/sysinv/sysinv/scripts/partition_info.sh index efc1da6dc8..d6c6898025 100644 --- a/sysinv/sysinv/sysinv/scripts/partition_info.sh +++ b/sysinv/sysinv/sysinv/scripts/partition_info.sh @@ -3,7 +3,7 @@ # # vim: tabstop=4 shiftwidth=4 softtabstop=4 # -# Copyright (c) 2017 Wind River Systems, Inc. +# Copyright (c) 2017-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -47,7 +47,7 @@ device_nodes=( `echo "$sfdisk_part_info" | awk '$1 == "Device" {i=1; next}; i {p sector_size=$(blockdev --getss $device_path) for device in "${device_nodes[@]}"; do - part_number=$(udevadm info $device | grep -oP 'E: PARTN=\K.*') + part_number=$(udevadm info $device | grep -oP -m1 'E: PARTN=\K.*|E: DM_PART=\K.*') # Parse the output and put it in the right return format. part_type_guid=$(sfdisk --part-type $device_path $part_number) part_type_name=$(echo "$sfdisk_part_info" | grep -w $device | awk '{print substr($0, index($0, $6))}' | tr ' ' '.') diff --git a/sysinv/sysinv/sysinv/sysinv/agent/disk.py b/sysinv/sysinv/sysinv/sysinv/agent/disk.py index c1016f2919..4c11be6bfb 100644 --- a/sysinv/sysinv/sysinv/sysinv/agent/disk.py +++ b/sysinv/sysinv/sysinv/sysinv/agent/disk.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -247,8 +247,8 @@ class DiskOperator(object): device_path = "/dev/disk/by-path/" + device['ID_PATH'] LOG.debug("[DiskEnum] device_path: %s ", device_path) elif (constants.DEVICE_NAME_MPATH in device.get("DM_NAME", "") - and 'DM_UUID' in device): - device_path = "/dev/disk/by-id/dm-uuid-" + device['DM_UUID'] + and 'DM_WWN' in device): + device_path = "/dev/disk/by-id/wwn-" + device['DM_WWN'] LOG.debug("[DiskEnum] device_path: %s ", device_path) device_node = utils.get_mpath_from_dm(device.device_node) LOG.debug("[DiskEnum] device_node: %s ", device_node) diff --git a/sysinv/sysinv/sysinv/sysinv/agent/partition.py b/sysinv/sysinv/sysinv/sysinv/agent/partition.py index affa8f3192..c125c346b0 100644 --- a/sysinv/sysinv/sysinv/sysinv/agent/partition.py +++ b/sysinv/sysinv/sysinv/sysinv/agent/partition.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2017 Wind River Systems, Inc. +# Copyright (c) 2017-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -88,9 +88,12 @@ class PartitionOperator(object): for partition in sfdisk_partitions: partition_number = partition.get('part_number') size_mib = partition.get('size_mib') - if 'nvme' in device_node: + if constants.DEVICE_NAME_NVME in device_node: part_device_node = '{}p{}'.format(device_node, partition_number) + elif constants.DEVICE_NAME_MPATH in device_node: + part_device_node = '{}-part{}'.format(device_node, + partition_number) else: part_device_node = '{}{}'.format(device_node, partition_number) @@ -137,8 +140,8 @@ class PartitionOperator(object): device_path = "/dev/disk/by-path/" + device['ID_PATH'] device_node = device.device_node elif (constants.DEVICE_NAME_MPATH in device.get("DM_NAME", "") - and 'DM_UUID' in device): - device_path = "/dev/disk/by-id/dm-uuid-" + device['DM_UUID'] + and 'DM_WWN' in device): + device_path = "/dev/disk/by-id/wwn-" + device['DM_WWN'] device_node = utils.get_mpath_from_dm(device.device_node) try: diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/partition.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/partition.py index 1e09f35773..b4a1d1015a 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/partition.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/partition.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2022 Wind River Systems, Inc. +# Copyright (c) 2017-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -204,7 +204,8 @@ class PartitionController(rest.RestController): # Only return user created partitions. partitions = [ p for p in partitions - if p.type_guid == constants.USER_PARTITION_PHYSICAL_VOLUME] + if (p.type_guid == constants.USER_PARTITION_PHYSICAL_VOLUME or + p.type_name == "Linux LVM")] return PartitionCollection.convert_with_links(partitions, limit, url=resource_url, @@ -409,6 +410,9 @@ def _build_device_node_path(partition): if constants.DEVICE_NAME_NVME in idisk.device_node: device_node = "%sp%s" %\ (idisk.device_node, len(partitions) + 1) + elif constants.DEVICE_NAME_MPATH in idisk.device_node: + device_node = "%s-part%s" %\ + (idisk.device_node, len(partitions) + 1) else: device_node = "%s%s" % (idisk.device_node, len(partitions) + 1) device_path = cutils.get_part_device_path(idisk.device_path, @@ -416,6 +420,8 @@ def _build_device_node_path(partition): else: if constants.DEVICE_NAME_NVME in idisk.device_node: device_node = idisk.device_node + "p1" + elif constants.DEVICE_NAME_MPATH in idisk.device_node: + device_node = idisk.device_node + "-part1" else: device_node = idisk.device_node + '1' device_path = cutils.get_part_device_path(idisk.device_path, "1") diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/storage.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/storage.py index 0037cc6387..2200f1ac57 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/storage.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/storage.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2021 Wind River Systems, Inc. +# Copyright (c) 2013-2023 Wind River Systems, Inc. # from eventlet.green import subprocess @@ -169,6 +169,10 @@ class Storage(base.APIBase): constants.DEVICE_NAME_NVME in d.device_node): stor.journal_node = "{}p{}".format(d.device_node, part_number) + elif (d.device_node is not None and + constants.DEVICE_NAME_MPATH in d.device_node): + stor.journal_node = "{}-part{}".format(d.device_node, + part_number) else: stor.journal_node = "{}{}".format(d.device_node, part_number) diff --git a/sysinv/sysinv/sysinv/sysinv/common/utils.py b/sysinv/sysinv/sysinv/sysinv/common/utils.py index 65b32853ec..5177b9c4af 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/common/utils.py @@ -568,8 +568,7 @@ def is_system_usable_block_device(pydev_device): if pydev_device.get("DM_VG_NAME") or pydev_device.get("DM_LV_NAME"): # Skip LVM devices return False - if (constants.DEVICE_NAME_MPATH in pydev_device.get("DM_NAME", "") - and "part" in pydev_device.get("DM_UUID", "").split("-")[0]): + if constants.DEVICE_NAME_MPATH in pydev_device.get("DM_NAME", "") and pydev_device.get("DM_PART", ""): # Skip mpath partition devices return False if pydev_device.get("ID_FS_TYPE") == constants.DEVICE_FS_TYPE_MPATH: @@ -3737,13 +3736,15 @@ def get_mpath_from_dm(dm_device): context = pyudev.Context() - pydev_device = pyudev.Device.from_device_file(context, dm_device) + pydev_device = pyudev.Devices.from_device_file(context, dm_device) - if constants.DEVICE_NAME_MPATH in pydev_device.get("DM_NAME", ""): - re_line = re.compile(r'^(\D*)') - match = re_line.search(pydev_device.get("DM_NAME")) - if match: - mpath_device = os.path.join("/dev/mapper", match.group(1)) + device_mapper_name = pydev_device.get("DM_NAME", "") + if constants.DEVICE_NAME_MPATH in device_mapper_name: + device_mapper_mpath = pydev_device.get("DM_MPATH", None) + if device_mapper_mpath: + mpath_device = os.path.join("/dev/mapper", device_mapper_mpath) + else: + mpath_device = os.path.join("/dev/mapper", device_mapper_name) return mpath_device @@ -3755,15 +3756,7 @@ def get_part_device_path(disk_device_path, part_number): :param part_number: the partition number :returns the partition device path """ - if constants.DEVICE_NAME_MPATH in disk_device_path: - path_split = disk_device_path.split(constants.DEVICE_NAME_MPATH) - part_device_path = '{}part{}-{}{}'.format(path_split[0], - part_number, - constants.DEVICE_NAME_MPATH, - path_split[1]) - else: - part_device_path = '{}-part{}'.format(disk_device_path, part_number) - + part_device_path = '{}-part{}'.format(disk_device_path, part_number) return part_device_path @@ -3773,15 +3766,8 @@ def get_part_number(part_device_path): :returns the partition's number """ part_num = "" - if 'by-path' in part_device_path: + if 'by-path' in part_device_path or 'by-id' in part_device_path: part_num = re.match('.*?([0-9]+)$', part_device_path).group(1) - - if constants.DEVICE_NAME_MPATH in part_device_path: - match_path = re.match('(/dev/disk/by-id/.+)-part([0-9]+)(-mpath.*)', - part_device_path) - if match_path: - part_num = match_path.group(2) - return part_num