diff --git a/src/core/raid/softwareraid.cpp b/src/core/raid/softwareraid.cpp index cb22899..6cb0eaa 100644 --- a/src/core/raid/softwareraid.cpp +++ b/src/core/raid/softwareraid.cpp @@ -39,9 +39,10 @@ public: qint64 m_arraySize; QString m_UUID; QStringList m_devicePathList; + bool m_active; }; -SoftwareRAID::SoftwareRAID(const QString& name, const QString& iconName) +SoftwareRAID::SoftwareRAID(const QString& name, bool active, const QString& iconName) : VolumeManagerDevice(std::make_shared(), name, (QStringLiteral("/dev/") + name), @@ -56,6 +57,7 @@ SoftwareRAID::SoftwareRAID(const QString& name, const QString& iconName) d_ptr->m_arraySize = getArraySize(deviceNode()); d_ptr->m_UUID = getUUID(deviceNode()); d_ptr->m_devicePathList = getDevicePathList(deviceNode()); + d_ptr->m_active = active; initPartitions(); } @@ -92,7 +94,23 @@ bool SoftwareRAID::shrinkArray(Report &report, const QStringList &devices) QString SoftwareRAID::prettyName() const { - return VolumeManagerDevice::prettyName() + xi18nc("@item:inlistbox [RAID level]", " [RAID %1]", raidLevel()); + return VolumeManagerDevice::prettyName() + + (isActive() ? xi18nc("@item:inlistbox [RAID level]", " [RAID %1]", raidLevel()) : + QStringLiteral(" [RAID]")); +} + +bool SoftwareRAID::operator ==(const Device& other) const +{ + bool equalDeviceNode = Device::operator ==(other); + + if (other.type() == Device::Type::SoftwareRAID_Device) { + const SoftwareRAID& raid = static_cast(other); + + if (!equalDeviceNode) + return raid.uuid() == uuid(); + } + + return equalDeviceNode; } qint32 SoftwareRAID::raidLevel() const @@ -125,9 +143,37 @@ QStringList SoftwareRAID::devicePathList() const return d_ptr->m_devicePathList; } +bool SoftwareRAID::isActive() const +{ + return d_ptr->m_active; +} + +void SoftwareRAID::setActive(bool active) +{ + d_ptr->m_active = active; +} + void SoftwareRAID::scanSoftwareRAID(QList& devices) { - // TODO: Check configuration file and load all the devices that aren't in /proc/mdstat as innactive + QList scannedRaid; + + // TODO: Support custom config files. + QString config = getRAIDConfiguration(QStringLiteral("/etc/mdadm.conf")); + + if (!config.isEmpty()) { + QRegularExpression re(QStringLiteral("[\\t\\r\\n\\f\\s]ARRAY \\/dev\\/([\\/\\w-]+)")); + QRegularExpressionMatchIterator i = re.globalMatch(config); + + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + QString deviceName = reMatch.captured(1).trimmed(); + + SoftwareRAID *raidDevice = new SoftwareRAID(deviceName, false); + + scannedRaid << raidDevice; + } + } + ExternalCommand scanRaid(QStringLiteral("cat"), { QStringLiteral("/proc/mdstat") }); if (scanRaid.run(-1) && scanRaid.exitCode() == 0) { @@ -136,14 +182,18 @@ void SoftwareRAID::scanSoftwareRAID(QList& devices) while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); - QString deviceNode = QStringLiteral("/dev/") + QStringLiteral("md") + reMatch.captured(1).trimmed(); + QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(1).trimmed(); - Device* d = CoreBackendManager::self()->backend()->scanDevice(deviceNode); + SoftwareRAID* d = static_cast(CoreBackendManager::self()->backend()->scanDevice(deviceNode)); - if ( d ) - devices << d; + if (scannedRaid.contains(d)) + d->setActive(true); + else + scannedRaid << d; } } + + devices << scannedRaid; } qint32 SoftwareRAID::getRaidLevel(const QString &path) @@ -193,8 +243,51 @@ qint64 SoftwareRAID::getArraySize(const QString &path) QString SoftwareRAID::getUUID(const QString &path) { - Q_UNUSED(path) - return QStringLiteral(); + QString output = getDetail(path); + + if (!output.isEmpty()) { + QRegularExpression re(QStringLiteral("UUID :\\s+([\\w:]+)")); + QRegularExpressionMatch reMatch = re.match(output); + + if (reMatch.hasMatch()) + return reMatch.captured(1); + } + + // If UUID was not found in detail output, it should be searched in config file + + // TODO: Support custom config files. + QString config = getRAIDConfiguration(QStringLiteral("/etc/mdadm.conf")); + + if (!config.isEmpty()) { + QRegularExpression re(QStringLiteral("[\\t\\r\\n\\f\\s]ARRAY \\/dev\\/md([\\/\\w-]+)(.*)")); + QRegularExpressionMatchIterator i = re.globalMatch(config); + + while (i.hasNext()) { + QRegularExpressionMatch reMatch = i.next(); + QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(1).trimmed(); + QString otherInfo = reMatch.captured(2).trimmed(); + + // Consider device node as name=host:deviceNode when the captured device node string has '-' character + // It happens when user have included the device to config file using 'mdadm --examine --scan' + if (deviceNode.contains(QLatin1Char('-'))) { + QRegularExpression reName(QStringLiteral("name=[\\w:]+\\/dev\\/md\\/([\\/\\w]+)")); + QRegularExpressionMatch nameMatch = reName.match(otherInfo); + + if (nameMatch.hasMatch()) + deviceNode = nameMatch.captured(1); + } + + if (deviceNode == path) { + QRegularExpression reUUID(QStringLiteral("(UUID=|uuid=)([\\w:]+)")); + QRegularExpressionMatch uuidMatch = reUUID.match(otherInfo); + + if (uuidMatch.hasMatch()) + return uuidMatch.captured(2); + } + } + } + + return QString(); } QStringList SoftwareRAID::getDevicePathList(const QString &path) @@ -266,5 +359,12 @@ QString SoftwareRAID::getDetail(const QString &path) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--detail"), path }); - return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QStringLiteral(); + return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); +} + +QString SoftwareRAID::getRAIDConfiguration(const QString &configurationPath) +{ + ExternalCommand cmd(QStringLiteral("cat"), { configurationPath }); + + return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); } diff --git a/src/core/raid/softwareraid.h b/src/core/raid/softwareraid.h index 0951d37..6fd56ee 100644 --- a/src/core/raid/softwareraid.h +++ b/src/core/raid/softwareraid.h @@ -27,7 +27,7 @@ class LIBKPMCORE_EXPORT SoftwareRAID : public VolumeManagerDevice Q_DISABLE_COPY(SoftwareRAID) public: - SoftwareRAID(const QString& name, const QString& iconName = QString()); + SoftwareRAID(const QString& name, bool active = true, const QString& iconName = QString()); const QStringList deviceNodes() const override; const QStringList& partitionNodes() const override; @@ -39,12 +39,17 @@ public: virtual QString prettyName() const override; + virtual bool operator==(const Device& other) const override; + qint32 raidLevel() const; qint64 chunkSize() const; qint64 totalChunk() const; qint64 arraySize() const; QString uuid() const; QStringList devicePathList() const; + bool isActive() const; + + void setActive(bool active); public: static void scanSoftwareRAID(QList& devices); @@ -80,6 +85,8 @@ protected: private: static QString getDetail(const QString& path); + + static QString getRAIDConfiguration(const QString& configurationPath); }; #endif // SOFTWARERAID_H diff --git a/src/ops/createpartitiontableoperation.cpp b/src/ops/createpartitiontableoperation.cpp index f3ed270..57b5738 100644 --- a/src/ops/createpartitiontableoperation.cpp +++ b/src/ops/createpartitiontableoperation.cpp @@ -21,6 +21,7 @@ #include "core/device.h" #include "core/partitiontable.h" #include "core/partition.h" +#include "core/raid/softwareraid.h" #include "jobs/createpartitiontablejob.h" @@ -93,7 +94,18 @@ bool CreatePartitionTableOperation::execute(Report& parent) */ bool CreatePartitionTableOperation::canCreate(const Device* device) { - return (device != nullptr) && (device->partitionTable() == nullptr || !device->partitionTable()->isChildMounted()) && (device->type() != Device::Type::LVM_Device); + if (device == nullptr) + return false; + + if (device->type() == Device::Type::SoftwareRAID_Device) { + const SoftwareRAID* raid = static_cast(device); + + if (!raid->isActive()) + return false; + } + + return (device->partitionTable() == nullptr || !device->partitionTable()->isChildMounted()) + && (device->type() != Device::Type::LVM_Device); } QString CreatePartitionTableOperation::description() const