SoftwareRAID initial implementation.
This commit is contained in:
parent
9e3386a53b
commit
998b1292c0
|
@ -23,6 +23,7 @@ set(CORE_SRC
|
|||
core/smartparser.cpp
|
||||
core/smartattributeparseddata.cpp
|
||||
core/smartdiskinformation.cpp
|
||||
core/softwareraid.cpp
|
||||
core/volumemanagerdevice.cpp
|
||||
)
|
||||
|
||||
|
@ -41,5 +42,6 @@ set(CORE_LIB_HDRS
|
|||
core/partitiontable.h
|
||||
core/smartattribute.h
|
||||
core/smartstatus.h
|
||||
core/softwareraid.h
|
||||
core/volumemanagerdevice.h
|
||||
)
|
||||
|
|
|
@ -54,7 +54,8 @@ public:
|
|||
Unknown_Device,
|
||||
Disk_Device,
|
||||
LVM_Device, /* VG */
|
||||
RAID_Device, /* software RAID device */
|
||||
SoftwareRAID_Device, /* software RAID device, i.e. mdraid */
|
||||
HardwareRAID_Device, /* hardware RAID device (or fake raid), i.e. dmraid */
|
||||
};
|
||||
|
||||
explicit Device(std::shared_ptr<DevicePrivate> d_ptr, const QString& name, const QString& deviceNode, const qint64 logicalSectorSize, const qint64 totalLogicalSectors, const QString& iconName = QString(), Device::Type type = Device::Type::Disk_Device);
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/*************************************************************************
|
||||
* Copyright (C) 2018 by Caio Carvalho <caiojcarvalho@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License as *
|
||||
* published by the Free Software Foundation; either version 3 of *
|
||||
* the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.*
|
||||
*************************************************************************/
|
||||
|
||||
#include "softwareraid.h"
|
||||
|
||||
#include "backend/corebackend.h"
|
||||
#include "backend/corebackendmanager.h"
|
||||
#include "core/partition.h"
|
||||
#include "core/volumemanagerdevice_p.h"
|
||||
#include "fs/filesystem.h"
|
||||
#include "fs/filesystemfactory.h"
|
||||
#include "util/externalcommand.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#define d_ptr std::static_pointer_cast<SoftwareRAIDPrivate>(d)
|
||||
|
||||
class SoftwareRAIDPrivate : public VolumeManagerDevicePrivate
|
||||
{
|
||||
public:
|
||||
qint32 m_raidLevel;
|
||||
qint64 m_chunkSize;
|
||||
qint64 m_totalChunk;
|
||||
qint64 m_arraySize;
|
||||
QString m_UUID;
|
||||
QStringList m_devicePathList;
|
||||
};
|
||||
|
||||
SoftwareRAID::SoftwareRAID(const QString& name, const QString& iconName)
|
||||
: VolumeManagerDevice(std::make_shared<SoftwareRAIDPrivate>(),
|
||||
name,
|
||||
(QStringLiteral("/dev/") + name),
|
||||
getChunkSize(QStringLiteral("/dev/") + name),
|
||||
getTotalChunk(QStringLiteral("/dev/") + name),
|
||||
iconName,
|
||||
Device::Type::SoftwareRAID_Device)
|
||||
{
|
||||
d_ptr->m_raidLevel = getRaidLevel(deviceNode());
|
||||
d_ptr->m_chunkSize = logicalSize();
|
||||
d_ptr->m_totalChunk = totalLogical();
|
||||
d_ptr->m_arraySize = getArraySize(deviceNode());
|
||||
d_ptr->m_UUID = getUUID(deviceNode());
|
||||
d_ptr->m_devicePathList = getDevicePathList(deviceNode());
|
||||
|
||||
initPartitions();
|
||||
}
|
||||
|
||||
const QStringList SoftwareRAID::deviceNodes() const
|
||||
{
|
||||
return d_ptr->m_devicePathList;
|
||||
}
|
||||
|
||||
const QStringList& SoftwareRAID::partitionNodes() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
qint64 SoftwareRAID::partitionSize(QString &partitionPath) const
|
||||
{
|
||||
Q_UNUSED(partitionPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SoftwareRAID::growArray(Report &report, const QStringList &devices)
|
||||
{
|
||||
Q_UNUSED(report);
|
||||
Q_UNUSED(devices);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoftwareRAID::shrinkArray(Report &report, const QStringList &devices)
|
||||
{
|
||||
Q_UNUSED(report);
|
||||
Q_UNUSED(devices);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoftwareRAID::scanSoftwareRAID(QList<Device*>& devices)
|
||||
{
|
||||
ExternalCommand scanRaid(QStringLiteral("cat"), { QStringLiteral("/proc/mdstat") });
|
||||
|
||||
if (scanRaid.run(-1) && scanRaid.exitCode() == 0) {
|
||||
QRegularExpression re(QStringLiteral("md(\\d+)\\s+:"));
|
||||
QRegularExpressionMatchIterator i = re.globalMatch(scanRaid.output());
|
||||
while (i.hasNext()) {
|
||||
QRegularExpressionMatch reMatch = i.next();
|
||||
|
||||
QString deviceNode = QStringLiteral("/dev/") + QStringLiteral("md") + reMatch.captured(1).trimmed();
|
||||
|
||||
Device* d = CoreBackendManager::self()->backend()->scanDevice(deviceNode);
|
||||
|
||||
if ( d )
|
||||
devices << d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qint32 SoftwareRAID::getRaidLevel(const QString &path)
|
||||
{
|
||||
QString output = getDetail(path);
|
||||
|
||||
if (!output.isEmpty()) {
|
||||
QRegularExpression re(QStringLiteral("Raid Level :\\s+\\w+(\\d+)"));
|
||||
QRegularExpressionMatch reMatch = re.match(output);
|
||||
if (reMatch.hasMatch())
|
||||
return reMatch.captured(1).toLongLong();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 SoftwareRAID::getChunkSize(const QString &path)
|
||||
{
|
||||
QString output = getDetail(path);
|
||||
if (!output.isEmpty()) {
|
||||
QRegularExpression re(QStringLiteral("Chunk Size :\\s+(\\d+)"));
|
||||
QRegularExpressionMatch reMatch = re.match(output);
|
||||
if (reMatch.hasMatch())
|
||||
return reMatch.captured(1).toLongLong() * 1024;
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
qint64 SoftwareRAID::getTotalChunk(const QString &path)
|
||||
{
|
||||
return getArraySize(path) / getChunkSize(path);
|
||||
}
|
||||
|
||||
qint64 SoftwareRAID::getArraySize(const QString &path)
|
||||
{
|
||||
QString output = getDetail(path);
|
||||
if (!output.isEmpty()) {
|
||||
QRegularExpression re(QStringLiteral("Array Size :\\s+(\\d+)"));
|
||||
QRegularExpressionMatch reMatch = re.match(output);
|
||||
if (reMatch.hasMatch())
|
||||
return reMatch.captured(1).toLongLong() * 1024;
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
QString SoftwareRAID::getUUID(const QString &path)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
return QStringLiteral();
|
||||
}
|
||||
|
||||
QStringList SoftwareRAID::getDevicePathList(const QString &path)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool SoftwareRAID::createSoftwareRAID(Report &report,
|
||||
const QString &name,
|
||||
const QStringList devicePathList,
|
||||
const qint32 raidLevel,
|
||||
const qint32 chunkSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoftwareRAID::deleteSoftwareRAID(Report &report,
|
||||
SoftwareRAID &raidDevice)
|
||||
{
|
||||
Q_UNUSED(report);
|
||||
Q_UNUSED(raidDevice);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoftwareRAID::assembleSoftwareRAID(Report &report, const SoftwareRAID &raidDevice)
|
||||
{
|
||||
Q_UNUSED(report);
|
||||
Q_UNUSED(raidDevice);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoftwareRAID::stopSoftwareRAID(Report &report, const SoftwareRAID &raidDevice)
|
||||
{
|
||||
Q_UNUSED(report);
|
||||
Q_UNUSED(raidDevice);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoftwareRAID::initPartitions()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
qint64 SoftwareRAID::mappedSector(const QString &partitionPath, qint64 sector) const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString SoftwareRAID::getDetail(const QString &path)
|
||||
{
|
||||
ExternalCommand cmd(QStringLiteral("mdadm"),
|
||||
{ QStringLiteral("--detail"),
|
||||
path });
|
||||
return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QStringLiteral();
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*************************************************************************
|
||||
* Copyright (C) 2018 by Caio Carvalho <caiojcarvalho@gmail.com> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License as *
|
||||
* published by the Free Software Foundation; either version 3 of *
|
||||
* the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.*
|
||||
*************************************************************************/
|
||||
|
||||
#if !defined(KPMCORE_SOFTWARERAID_H)
|
||||
#define KPMCORE_SOFTWARERAID_H
|
||||
|
||||
#include "core/volumemanagerdevice.h"
|
||||
#include "util/libpartitionmanagerexport.h"
|
||||
#include "util/report.h"
|
||||
|
||||
class LIBKPMCORE_EXPORT SoftwareRAID : public VolumeManagerDevice
|
||||
{
|
||||
Q_DISABLE_COPY(SoftwareRAID)
|
||||
|
||||
public:
|
||||
SoftwareRAID(const QString& name, const QString& iconName = QString());
|
||||
|
||||
const QStringList deviceNodes() const override;
|
||||
const QStringList& partitionNodes() const override;
|
||||
qint64 partitionSize(QString &partitionPath) const override;
|
||||
|
||||
virtual bool growArray(Report& report, const QStringList& devices);
|
||||
|
||||
virtual bool shrinkArray(Report& report, const QStringList& devices);
|
||||
|
||||
public:
|
||||
static void scanSoftwareRAID(QList<Device*>& devices);
|
||||
|
||||
static qint32 getRaidLevel(const QString& path);
|
||||
static qint64 getChunkSize(const QString& path);
|
||||
static qint64 getTotalChunk(const QString& path);
|
||||
static qint64 getArraySize(const QString& path);
|
||||
static QString getUUID(const QString& path);
|
||||
static QStringList getDevicePathList(const QString& path);
|
||||
|
||||
static bool createSoftwareRAID(Report& report,
|
||||
const QString& name,
|
||||
const QStringList devicePathList,
|
||||
const qint32 raidLevel,
|
||||
const qint32 chunkSize);
|
||||
|
||||
static bool deleteSoftwareRAID(Report& report,
|
||||
SoftwareRAID& raidDevice);
|
||||
|
||||
static bool assembleSoftwareRAID(Report& report,
|
||||
const SoftwareRAID& raidDevice);
|
||||
|
||||
static bool stopSoftwareRAID(Report& report,
|
||||
const SoftwareRAID& raidDevice);
|
||||
|
||||
protected:
|
||||
void initPartitions() override;
|
||||
|
||||
qint64 mappedSector(const QString &partitionPath, qint64 sector) const override;
|
||||
|
||||
private:
|
||||
static QString getDetail(const QString& path);
|
||||
};
|
||||
|
||||
#endif // SOFTWARERAID_H
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "core/diskdevice.h"
|
||||
#include "core/lvmdevice.h"
|
||||
#include "core/softwareraid.h"
|
||||
#include "core/partitiontable.h"
|
||||
#include "core/partitionalignment.h"
|
||||
|
||||
|
@ -106,6 +107,7 @@ QList<Device*> SfdiskBackend::scanDevices(bool excludeReadOnly)
|
|||
}
|
||||
|
||||
LvmDevice::scanSystemLVM(result);
|
||||
SoftwareRAID::scanSoftwareRAID(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -126,61 +128,50 @@ Device* SfdiskBackend::scanDevice(const QString& deviceNode)
|
|||
ExternalCommand sizeCommand2(QStringLiteral("blockdev"), { QStringLiteral("--getss"), deviceNode });
|
||||
ExternalCommand jsonCommand(QStringLiteral("sfdisk"), { QStringLiteral("--json"), deviceNode } );
|
||||
|
||||
if ( modelCommand.run(-1) && modelCommand.exitCode() == 0
|
||||
&& sizeCommand.run(-1) && sizeCommand.exitCode() == 0
|
||||
&& sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0
|
||||
&& jsonCommand.run(-1) )
|
||||
if ( sizeCommand.run(-1) && sizeCommand.exitCode() == 0
|
||||
&& sizeCommand2.run(-1) && sizeCommand2.exitCode() == 0
|
||||
&& jsonCommand.run(-1) )
|
||||
{
|
||||
QString modelName = modelCommand.output();
|
||||
modelName = modelName.left(modelName.length() - 1);
|
||||
Device* d = nullptr;
|
||||
qint64 deviceSize = sizeCommand.output().trimmed().toLongLong();
|
||||
|
||||
Log(Log::Level::information) << xi18nc("@info:status", "Device found: %1", modelName);
|
||||
int logicalSectorSize = sizeCommand2.output().trimmed().toLongLong();
|
||||
DiskDevice* d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize);
|
||||
|
||||
if (jsonCommand.exitCode() != 0)
|
||||
return d;
|
||||
|
||||
const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object();
|
||||
const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject();
|
||||
|
||||
QString tableType = partitionTable[QLatin1String("label")].toString();
|
||||
const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType);
|
||||
|
||||
qint64 firstUsableSector = 0, lastUsableSector = d->totalSectors();
|
||||
if (type == PartitionTable::gpt) {
|
||||
firstUsableSector = partitionTable[QLatin1String("firstlba")].toVariant().toLongLong();
|
||||
lastUsableSector = partitionTable[QLatin1String("lastlba")].toVariant().toLongLong();
|
||||
}
|
||||
if (lastUsableSector < firstUsableSector) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
setPartitionTableForDevice(*d, new PartitionTable(type, firstUsableSector, lastUsableSector));
|
||||
switch (type) {
|
||||
case PartitionTable::gpt:
|
||||
if ( modelCommand.run(-1) && modelCommand.exitCode() == 0 && !modelCommand.output().trimmed().isEmpty() )
|
||||
{
|
||||
// Read the maximum number of GPT partitions
|
||||
qint32 maxEntries;
|
||||
ExternalCommand ddCommand(QStringLiteral("dd"), { QStringLiteral("skip=1"), QStringLiteral("count=1"), QStringLiteral("if=") + deviceNode}, QProcess::SeparateChannels);
|
||||
if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) {
|
||||
QByteArray gptHeader = ddCommand.rawOutput();
|
||||
QByteArray gptMaxEntries = gptHeader.mid(80, 4);
|
||||
QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream >> maxEntries;
|
||||
}
|
||||
else
|
||||
maxEntries = 128;
|
||||
CoreBackend::setPartitionTableMaxPrimaries(*d->partitionTable(), maxEntries);
|
||||
QString modelName = modelCommand.output();
|
||||
modelName = modelName.left(modelName.length() - 1);
|
||||
|
||||
Log(Log::Level::information) << xi18nc("@info:status", "Disk Device found: %1", modelName);
|
||||
|
||||
d = new DiskDevice(modelName, deviceNode, 255, 63, deviceSize / logicalSectorSize / 255 / 63, logicalSectorSize);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
else // check for software raid
|
||||
{
|
||||
ExternalCommand softwareRaidCommand(QStringLiteral("mdadm"), { QStringLiteral("--detail"), deviceNode });
|
||||
|
||||
if ( softwareRaidCommand.run(-1) && softwareRaidCommand.exitCode() == 0 )
|
||||
{
|
||||
Log(Log::Level::information) << xi18nc("@info:status", "Software RAID Device found: %1", deviceNode);
|
||||
|
||||
QString deviceName = deviceNode.mid(5);
|
||||
|
||||
d = new SoftwareRAID( deviceName );
|
||||
}
|
||||
}
|
||||
|
||||
scanDevicePartitions(*d, partitionTable[QLatin1String("partitions")].toArray());
|
||||
return d;
|
||||
if ( d )
|
||||
{
|
||||
if (jsonCommand.exitCode() != 0)
|
||||
return d;
|
||||
|
||||
const QJsonObject jsonObject = QJsonDocument::fromJson(jsonCommand.rawOutput()).object();
|
||||
const QJsonObject partitionTable = jsonObject[QLatin1String("partitiontable")].toObject();
|
||||
|
||||
if (!updateDevicePartitionTable(*d, partitionTable))
|
||||
return nullptr;
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -291,6 +282,64 @@ void SfdiskBackend::scanDevicePartitions(Device& d, const QJsonArray& jsonPartit
|
|||
PartitionAlignment::isAligned(d, *part);
|
||||
}
|
||||
|
||||
bool SfdiskBackend::updateDevicePartitionTable(Device &d, const QJsonObject &jsonPartitionTable)
|
||||
{
|
||||
QString tableType = jsonPartitionTable[QLatin1String("label")].toString();
|
||||
const PartitionTable::TableType type = PartitionTable::nameToTableType(tableType);
|
||||
|
||||
qint64 firstUsableSector = 0, lastUsableSector;
|
||||
|
||||
if ( d.type() == Device::Type::Disk_Device )
|
||||
{
|
||||
const DiskDevice* diskDevice = static_cast<const DiskDevice*>(&d);
|
||||
|
||||
lastUsableSector = diskDevice->totalSectors();
|
||||
}
|
||||
else if ( d.type() == Device::Type::SoftwareRAID_Device )
|
||||
{
|
||||
const SoftwareRAID* raidDevice = static_cast<const SoftwareRAID*>(&d);
|
||||
|
||||
lastUsableSector = raidDevice->totalLogical() - 1;
|
||||
}
|
||||
|
||||
if (type == PartitionTable::gpt) {
|
||||
firstUsableSector = jsonPartitionTable[QLatin1String("firstlba")].toVariant().toLongLong();
|
||||
lastUsableSector = jsonPartitionTable[QLatin1String("lastlba")].toVariant().toLongLong();
|
||||
}
|
||||
|
||||
if (lastUsableSector < firstUsableSector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setPartitionTableForDevice(d, new PartitionTable(type, firstUsableSector, lastUsableSector));
|
||||
switch (type) {
|
||||
case PartitionTable::gpt:
|
||||
{
|
||||
// Read the maximum number of GPT partitions
|
||||
qint32 maxEntries;
|
||||
ExternalCommand ddCommand(QStringLiteral("dd"),
|
||||
{ QStringLiteral("skip=1"), QStringLiteral("count=1"), (QStringLiteral("if=") + d.deviceNode()) },
|
||||
QProcess::SeparateChannels);
|
||||
if (ddCommand.run(-1) && ddCommand.exitCode() == 0 ) {
|
||||
QByteArray gptHeader = ddCommand.rawOutput();
|
||||
QByteArray gptMaxEntries = gptHeader.mid(80, 4);
|
||||
QDataStream stream(&gptMaxEntries, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream >> maxEntries;
|
||||
}
|
||||
else
|
||||
maxEntries = 128;
|
||||
CoreBackend::setPartitionTableMaxPrimaries(*d.partitionTable(), maxEntries);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
scanDevicePartitions(d, jsonPartitionTable[QLatin1String("partitions")].toArray());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object.
|
||||
@param p the Partition the FileSystem is on
|
||||
@param mountPoint mount point of the partition in question
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
private:
|
||||
static void readSectorsUsed(const Device& d, Partition& p, const QString& mountPoint);
|
||||
void scanDevicePartitions(Device& d, const QJsonArray& jsonPartitions);
|
||||
bool updateDevicePartitionTable(Device& d, const QJsonObject& jsonPartitionTable);
|
||||
static PartitionTable::Flags availableFlags(PartitionTable::TableType type);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue