184 lines
5.2 KiB
Python
184 lines
5.2 KiB
Python
#
|
|
# Copyright(c) 2020-2021 Intel Corporation
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
import threading
|
|
from enum import IntEnum, Enum
|
|
|
|
from core.test_run import TestRun
|
|
from storage_devices.device import Device
|
|
from storage_devices.disk import Disk
|
|
from test_tools.fs_tools import readlink
|
|
from test_tools.mdadm import Mdadm
|
|
from test_tools.disk_finder import resolve_to_by_id_link
|
|
from type_def.size import Size, Unit
|
|
|
|
|
|
def get_devices_paths_string(devices: [Device]):
|
|
return " ".join([d.path for d in devices])
|
|
|
|
|
|
class Level(IntEnum):
|
|
Raid0 = 0
|
|
Raid1 = 1
|
|
Raid4 = 4
|
|
Raid5 = 5
|
|
Raid6 = 6
|
|
Raid10 = 10
|
|
|
|
|
|
class StripSize(IntEnum):
|
|
Strip4K = 4
|
|
Strip8K = 8
|
|
Strip16K = 16
|
|
Strip32K = 32
|
|
Strip64K = 64
|
|
Strip128K = 128
|
|
Strip256K = 256
|
|
Strip1M = 1024
|
|
|
|
|
|
class MetadataVariant(Enum):
|
|
Legacy = "legacy"
|
|
Imsm = "imsm"
|
|
|
|
|
|
class RaidConfiguration:
|
|
def __init__(
|
|
self,
|
|
level: Level = None,
|
|
metadata: MetadataVariant = MetadataVariant.Imsm,
|
|
number_of_devices: int = 0,
|
|
size: Size = None,
|
|
strip_size: StripSize = None,
|
|
name: str = None,
|
|
):
|
|
self.level = level
|
|
self.metadata = metadata
|
|
self.number_of_devices = number_of_devices
|
|
self.size = size
|
|
self.strip_size = strip_size
|
|
self.name = name
|
|
|
|
|
|
class Raid(Disk):
|
|
__unique_id = 0
|
|
__lock = threading.Lock()
|
|
|
|
def __init__(
|
|
self,
|
|
path: str,
|
|
level: Level,
|
|
uuid: str,
|
|
container_uuid: str = None,
|
|
container_path: str = None,
|
|
metadata: MetadataVariant = MetadataVariant.Imsm,
|
|
array_devices: [Device] = [],
|
|
volume_devices: [Device] = [],
|
|
):
|
|
Device.__init__(self, resolve_to_by_id_link(path.replace("/dev/", "")))
|
|
self.device_name = path.split('/')[-1]
|
|
self.level = level
|
|
self.uuid = uuid
|
|
self.container_uuid = container_uuid
|
|
self.container_path = container_path
|
|
self.metadata = metadata
|
|
self.array_devices = array_devices if array_devices else volume_devices.copy()
|
|
self.volume_devices = volume_devices
|
|
self.partitions = []
|
|
self.__block_size = None
|
|
|
|
def __eq__(self, other):
|
|
try:
|
|
return self.uuid == other.uuid
|
|
except AttributeError:
|
|
return False
|
|
|
|
@property
|
|
def block_size(self):
|
|
if not self.__block_size:
|
|
self.__block_size = Unit(int(self.get_sysfs_property("logical_block_size")))
|
|
return self.__block_size
|
|
|
|
def stop(self):
|
|
Mdadm.stop(self.path)
|
|
if self.container_path:
|
|
Mdadm.stop(self.container_path)
|
|
|
|
@classmethod
|
|
def discover(cls):
|
|
TestRun.LOGGER.info("Discover RAIDs in system...")
|
|
raids = []
|
|
for raid in Mdadm.examine_result():
|
|
raids.append(
|
|
cls(
|
|
raid["path"],
|
|
Level[raid["level"]],
|
|
raid["uuid"],
|
|
raid["container"]["uuid"] if "container" in raid else None,
|
|
raid["container"]["path"] if "container" in raid else None,
|
|
MetadataVariant(raid["metadata"]),
|
|
[Device(d) for d in raid["array_devices"]],
|
|
[Device(d) for d in raid["devices"]]
|
|
)
|
|
)
|
|
|
|
return raids
|
|
|
|
@classmethod
|
|
def create(
|
|
cls,
|
|
raid_configuration: RaidConfiguration,
|
|
devices: [Device]
|
|
):
|
|
import copy
|
|
raid_conf = copy.deepcopy(raid_configuration)
|
|
|
|
if not raid_conf.number_of_devices:
|
|
raid_conf.number_of_devices = len(devices)
|
|
elif len(devices) < raid_conf.number_of_devices:
|
|
raise ValueError("RAID configuration requires at least "
|
|
f"{raid_conf.number_of_devices} devices")
|
|
|
|
md_dir_path = "/dev/md/"
|
|
array_devices = devices
|
|
volume_devices = devices[:raid_conf.number_of_devices]
|
|
|
|
if raid_conf.metadata != MetadataVariant.Legacy:
|
|
container_conf = RaidConfiguration(
|
|
name=cls.__get_unique_name(raid_conf.metadata.value),
|
|
metadata=raid_conf.metadata,
|
|
number_of_devices=len(array_devices)
|
|
)
|
|
Mdadm.create(container_conf, get_devices_paths_string(array_devices))
|
|
|
|
if not raid_conf.name:
|
|
raid_conf.name = cls.__get_unique_name()
|
|
|
|
Mdadm.create(raid_conf, get_devices_paths_string(volume_devices))
|
|
|
|
raid_link = md_dir_path + raid_conf.name
|
|
raid = [r for r in Mdadm.examine_result() if readlink(r["path"]) == readlink(raid_link)][0]
|
|
|
|
return cls(
|
|
raid["path"],
|
|
raid_conf.level,
|
|
raid["uuid"],
|
|
raid["container"]["uuid"] if "container" in raid else None,
|
|
raid["container"]["path"] if "container" in raid else None,
|
|
raid_conf.metadata,
|
|
array_devices,
|
|
volume_devices
|
|
)
|
|
|
|
@staticmethod
|
|
def remove_all():
|
|
Mdadm.stop()
|
|
|
|
@classmethod
|
|
def __get_unique_name(cls, prefix: str = "Raid"):
|
|
with cls.__lock:
|
|
cls.__unique_id += 1
|
|
return f"{prefix}{cls.__unique_id}"
|