diff --git a/src/core/raid/softwareraid.cpp b/src/core/raid/softwareraid.cpp index 0b3907b..85dd9d5 100644 --- a/src/core/raid/softwareraid.cpp +++ b/src/core/raid/softwareraid.cpp @@ -1,576 +1,584 @@ /************************************************************************* * Copyright (C) 2018 by Caio Carvalho * * * * 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 .* *************************************************************************/ #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 "ops/createpartitiontableoperation.h" #include "util/externalcommand.h" #include #include #include #define d_ptr std::static_pointer_cast(d) QString SoftwareRAID::s_raidConfigurationFile = QString(); class SoftwareRAIDPrivate : public VolumeManagerDevicePrivate { public: qint32 m_raidLevel; qint64 m_chunkSize; qint64 m_totalChunk; qint64 m_arraySize; QString m_UUID; QStringList m_devicePathList; QStringList m_partitionPathList; SoftwareRAID::Status m_status; }; SoftwareRAID::SoftwareRAID(const QString& name, SoftwareRAID::Status status, const QString& iconName) : VolumeManagerDevice(std::make_shared(), name, (QStringLiteral("/dev/") + name), status == SoftwareRAID::Status::Inactive ? 0 : getChunkSize(QStringLiteral("/dev/") + name), status == SoftwareRAID::Status::Inactive ? 0 : 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()); d_ptr->m_status = status; initPartitions(); } const QStringList SoftwareRAID::deviceNodes() const { return d_ptr->m_devicePathList; } const QStringList& SoftwareRAID::partitionNodes() const { return d_ptr->m_partitionPathList; } 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; } QString SoftwareRAID::prettyName() const { QString raidInfo; if (status() == SoftwareRAID::Status::Active) raidInfo = xi18nc("@item:inlistbox [RAID level]", " [RAID %1]", raidLevel()); else if (status() == SoftwareRAID::Status::Recovery) raidInfo = xi18nc("@item:inlistbox [RAID level - Recovering]", " [RAID %1 - Recovering]", raidLevel()); else if (status() == SoftwareRAID::Status::Resync) raidInfo = xi18nc("@item:inlistbox [RAID level - Resyncing]", " [RAID %1 - Resyncing]", raidLevel()); else raidInfo = QStringLiteral(" [RAID]"); return VolumeManagerDevice::prettyName() + raidInfo; } 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 { return d_ptr->m_raidLevel; } qint64 SoftwareRAID::chunkSize() const { return d_ptr->m_chunkSize; } qint64 SoftwareRAID::totalChunk() const { return d_ptr->m_totalChunk; } qint64 SoftwareRAID::arraySize() const { return d_ptr->m_arraySize; } QString SoftwareRAID::uuid() const { return d_ptr->m_UUID; } SoftwareRAID::Status SoftwareRAID::status() const { return d_ptr->m_status; } void SoftwareRAID::setStatus(SoftwareRAID::Status status) { d_ptr->m_status = status; } void SoftwareRAID::scanSoftwareRAID(QList& devices) { QStringList availableInConf; QString config = getRAIDConfiguration(); if (!config.isEmpty()) { QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/([\\/\\w-]+)")); QRegularExpressionMatchIterator i = re.globalMatch(config); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString deviceName = reMatch.captured(2).trimmed(); availableInConf << deviceName; } } QFile mdstat(QStringLiteral("/proc/mdstat")); if (mdstat.open(QIODevice::ReadOnly)) { QTextStream stream(&mdstat); QString content = stream.readAll(); mdstat.close(); QRegularExpression re(QStringLiteral("md([\\/\\w]+)\\s+:\\s+([\\w]+)")); QRegularExpressionMatchIterator i = re.globalMatch(content); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(1).trimmed(); QString status = reMatch.captured(2).trimmed(); SoftwareRAID* d = static_cast(CoreBackendManager::self()->backend()->scanDevice(deviceNode)); // Just to prevent segfault in some case if (d == nullptr) continue; const QStringList constAvailableInConf = availableInConf; for (const QString& path : constAvailableInConf) if (getUUID(QStringLiteral("/dev/") + path) == d->uuid()) availableInConf.removeAll(path); QStringList partitionNodes; if (d->partitionTable() != nullptr) for (const Partition *p : d->partitionTable()->children()) partitionNodes << p->partitionPath(); d->setPartitionNodes(partitionNodes); + for (const Device* dev : qAsConst(devices)) { + if (dev->partitionTable()) { + for (const Partition* p : dev->partitionTable()->children()) + if (getRaidArrayName(p->deviceNode()) == d->deviceNode()) + d->physicalVolumes() << p; + } + } + devices << d; if (status == QStringLiteral("inactive")) d->setStatus(SoftwareRAID::Status::Inactive); if (d->raidLevel() > 0) { QRegularExpression reMirrorStatus(d->name() + QStringLiteral("\\s+:\\s+(.*\\n\\s+)+\\[[=>.]+\\]\\s+(resync|recovery)")); QRegularExpressionMatch reMirrorStatusMatch = reMirrorStatus.match(content); if (reMirrorStatusMatch.hasMatch()) { if (reMirrorStatusMatch.captured(2) == QStringLiteral("resync")) d->setStatus(SoftwareRAID::Status::Resync); else if (reMirrorStatusMatch.captured(2) == QStringLiteral("recovery")) d->setStatus(SoftwareRAID::Status::Recovery); } } } } for (const QString& name : qAsConst(availableInConf)) { SoftwareRAID *raidDevice = new SoftwareRAID(name, SoftwareRAID::Status::Inactive); devices << raidDevice; } } 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).toInt(); } return -1; } qint64 SoftwareRAID::getChunkSize(const QString &path) { if (getRaidLevel(path) == 1) { QStringList devices = getDevicePathList(path); if (!devices.isEmpty()) { QString device = devices[0]; // Look sector size for the first device/partition on the list, as RAID 1 is composed by mirrored devices ExternalCommand sectorSize(QStringLiteral("blockdev"), { QStringLiteral("--getss"), device }); if (sectorSize.run(-1) && sectorSize.exitCode() == 0) return sectorSize.output().trimmed().toLongLong(); } } else { 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(); } } 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) { 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 QString config = getRAIDConfiguration(); if (!config.isEmpty()) { QRegularExpression re(QStringLiteral("([\\t\\r\\n\\f\\s]|INACTIVE-)ARRAY \\/dev\\/md([\\/\\w-]+)(.*)")); QRegularExpressionMatchIterator i = re.globalMatch(config); while (i.hasNext()) { QRegularExpressionMatch reMatch = i.next(); QString deviceNode = QStringLiteral("/dev/md") + reMatch.captured(2).trimmed(); QString otherInfo = reMatch.captured(3).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) { QStringList result; QString detail = getDetail(path); if (!detail.isEmpty()) { QRegularExpression re(QStringLiteral("\\s+\\/dev\\/(\\w+)")); QRegularExpressionMatchIterator i = re.globalMatch(detail); while (i.hasNext()) { QRegularExpressionMatch match = i.next(); QString device = QStringLiteral("/dev/") + match.captured(1); if (device != path) result << device; } } return result; } bool SoftwareRAID::isRaidPath(const QString &devicePath) { return !getDetail(devicePath).isEmpty(); } bool SoftwareRAID::createSoftwareRAID(Report &report, const QString &name, const QStringList devicePathList, const qint32 raidLevel, const qint32 chunkSize) { QString path = QStringLiteral("/dev/") + name; QStringList args = { QStringLiteral("--create"), path, QStringLiteral("--level=") + QString::number(raidLevel), QStringLiteral("--chunk=") + QString::number(chunkSize), QStringLiteral("--raid-devices=") + QString::number(devicePathList.size()) }; for (const QString &p : qAsConst(devicePathList)) { eraseDeviceMDSuperblock(p); args << p; } ExternalCommand cmd(report, QStringLiteral("mdadm"), args); if (!cmd.run(-1) || cmd.exitCode() != 0) return false; if (updateConfigurationFile(path)) qDebug() << QStringLiteral("Updated RAID config: ") + path; return true; } bool SoftwareRAID::deleteSoftwareRAID(Report &report, SoftwareRAID &raidDevice) { // We need raid activated to erase its partition table if (assembleSoftwareRAID(raidDevice.deviceNode())) raidDevice.setStatus(SoftwareRAID::Status::Active); // check this behaviour on mirror devices during resync if (raidDevice.status() == SoftwareRAID::Status::Active) { // Erasing device's partition table if (raidDevice.partitionTable() != nullptr) { CreatePartitionTableOperation updatePartitionTable(raidDevice, raidDevice.partitionTable()->type()); if (!updatePartitionTable.execute(report)) return false; } stopSoftwareRAID(report, raidDevice.deviceNode()); } for (const QString& path : raidDevice.deviceNodes()) eraseDeviceMDSuperblock(path); QString config = getRAIDConfiguration(); QStringList lines = config.split(QLatin1Char('\n')); QString contentUpdated = QStringLiteral("\""); for (const QString line : lines) if (!line.isEmpty() && !line.contains(raidDevice.uuid())) contentUpdated += line + QLatin1Char('\n'); contentUpdated += QLatin1Char('\"'); ExternalCommand cmd(QStringLiteral("/usr/") + QStringLiteral(LIBEXECDIRPATH) + QStringLiteral("/kpmcore_mdadmupdateconf"), { QStringLiteral("--write"), contentUpdated, raidConfigurationFilePath() }); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::assembleSoftwareRAID(const QString& deviceNode) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--assemble"), QStringLiteral("--scan"), deviceNode, QStringLiteral("--config=") + raidConfigurationFilePath() }); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::stopSoftwareRAID(Report& report, const QString& deviceNode) { if (!isRaidPath(deviceNode)) return false; ExternalCommand cmd(report, QStringLiteral("mdadm"), { QStringLiteral("--manage"), QStringLiteral("--stop"), deviceNode }); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::reassembleSoftwareRAID(Report& report, const QString &deviceNode) { // TODO: Include report return stopSoftwareRAID(report, deviceNode) && assembleSoftwareRAID(deviceNode); } QString SoftwareRAID::getRaidArrayName(const QString &partitionPath) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--query"), partitionPath }); if (cmd.run(-1) && cmd.exitCode() == 0) { QRegularExpression ex(QStringLiteral("device active raid\\d+\\s([\\/\\w]+).")); QRegularExpressionMatch match = ex.match(cmd.output()); if (match.hasMatch()) return match.captured(1); } return QString(); } void SoftwareRAID::initPartitions() { } qint64 SoftwareRAID::mappedSector(const QString &partitionPath, qint64 sector) const { Q_UNUSED(partitionPath); Q_UNUSED(sector); return -1; } bool SoftwareRAID::eraseDeviceMDSuperblock(const QString &path) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--zero-superblock"), path}); return cmd.run(-1) && cmd.exitCode() == 0; } bool SoftwareRAID::updateConfigurationFile(const QString &path) { ExternalCommand cmd(QStringLiteral("/usr/") + QStringLiteral(LIBEXECDIRPATH) + QStringLiteral("/kpmcore_mdadmupdateconf"), { QStringLiteral("--append"), path, raidConfigurationFilePath() }); return cmd.run(-1) && cmd.exitCode() == 0; } QString SoftwareRAID::getDetail(const QString &path) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--detail"), path }); return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); } QString SoftwareRAID::getRAIDConfiguration() { QFile config(raidConfigurationFilePath()); if (!config.open(QIODevice::ReadOnly)) return QString(); QTextStream stream(&config); QString result = stream.readAll(); config.close(); return result; } QString SoftwareRAID::getDeviceInformation(const QString &deviceName) { ExternalCommand cmd(QStringLiteral("mdadm"), { QStringLiteral("--misc"), QStringLiteral("--detail"), QStringLiteral("--scan"), deviceName }); // TODO: Get only information about the device line. // Because if there is any error on config file, it will print more information than needed. return (cmd.run(-1) && cmd.exitCode() == 0) ? cmd.output() : QString(); } void SoftwareRAID::setRaidConfigurationFilePath(const QString &filePath) { s_raidConfigurationFile = filePath; } QString SoftwareRAID::raidConfigurationFilePath() { if (s_raidConfigurationFile.isEmpty()) s_raidConfigurationFile = getDefaultRaidConfigFile(); return s_raidConfigurationFile; } QString SoftwareRAID::getDefaultRaidConfigFile() { if (QFile::exists(QStringLiteral("/etc/mdadm.conf"))) return QStringLiteral("/etc/mdadm.conf"); else if (QFile::exists(QStringLiteral("/etc/mdadm/mdadm.conf"))) return QStringLiteral("/etc/mdadm/mdadm.conf"); return QString(); } void SoftwareRAID::setPartitionNodes(const QStringList& partitionNodes) { d_ptr->m_partitionPathList = partitionNodes; } diff --git a/src/jobs/movephysicalvolumejob.cpp b/src/jobs/movephysicalvolumejob.cpp index c961b8d..ee10d32 100644 --- a/src/jobs/movephysicalvolumejob.cpp +++ b/src/jobs/movephysicalvolumejob.cpp @@ -1,69 +1,70 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * 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 .* *************************************************************************/ #include "jobs/movephysicalvolumejob.h" #include "core/lvmdevice.h" +#include "core/volumemanagerdevice.h" #include "util/report.h" #include /** Creates a new MovePhysicalVolumeJob * @param d Device representing LVM Volume Group */ -MovePhysicalVolumeJob::MovePhysicalVolumeJob(LvmDevice& d, const QList & partList) : +MovePhysicalVolumeJob::MovePhysicalVolumeJob(VolumeManagerDevice& d, const QList & partList) : Job(), m_Device(d), m_PartList(partList) { } bool MovePhysicalVolumeJob::run(Report& parent) { bool rval = false; Report* report = jobStarted(parent); QStringList destinations = device().deviceNodes(); for (const auto &p : partList()) { if (destinations.contains(p->partitionPath())) { destinations.removeAll(p->partitionPath()); } } for (const auto &p : partList()) { rval = LvmDevice::movePV(*report, p->partitionPath(), destinations); if (rval == false) { break; } } jobFinished(*report, rval); return rval; } QString MovePhysicalVolumeJob::description() const { QString movedPartitions = QString(); for (const auto &p : partList()) movedPartitions += p->deviceNode() + QStringLiteral(", "); movedPartitions.chop(2); return xi18nc("@info/plain", "Move used PE in %1 on %2 to other available Physical Volumes", movedPartitions, device().name()); } diff --git a/src/jobs/movephysicalvolumejob.h b/src/jobs/movephysicalvolumejob.h index 452e8ab..1ae5eb3 100644 --- a/src/jobs/movephysicalvolumejob.h +++ b/src/jobs/movephysicalvolumejob.h @@ -1,56 +1,56 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * 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 .* *************************************************************************/ #if !defined(KPMCORE_MOVEPHYSICALVOLUMEJOB_H) #define KPMCORE_MOVEPHYSICALVOLUMEJOB_H #include "core/partition.h" #include "jobs/job.h" -class LvmDevice; +class VolumeManagerDevice; class Report; class QString; class MovePhysicalVolumeJob : public Job { public: - MovePhysicalVolumeJob(LvmDevice& dev, const QList & partlist); + MovePhysicalVolumeJob(VolumeManagerDevice& dev, const QList & partlist); public: bool run(Report& parent) override; QString description() const override; protected: - LvmDevice& device() { + VolumeManagerDevice& device() { return m_Device; } - const LvmDevice& device() const { + const VolumeManagerDevice& device() const { return m_Device; } const QList & partList() const { return m_PartList; } private: - LvmDevice& m_Device; + VolumeManagerDevice& m_Device; const QList m_PartList; }; #endif diff --git a/src/jobs/resizevolumegroupjob.cpp b/src/jobs/resizevolumegroupjob.cpp index 3dce190..0642b55 100644 --- a/src/jobs/resizevolumegroupjob.cpp +++ b/src/jobs/resizevolumegroupjob.cpp @@ -1,76 +1,80 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * 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 .* *************************************************************************/ #include "jobs/resizevolumegroupjob.h" #include "core/lvmdevice.h" +#include "core/volumemanagerdevice.h" #include "fs/luks.h" #include "util/report.h" #include /** Creates a new ResizeVolumeGroupJob */ -ResizeVolumeGroupJob::ResizeVolumeGroupJob(LvmDevice& dev, const QList & partlist, const Type type) : +ResizeVolumeGroupJob::ResizeVolumeGroupJob(VolumeManagerDevice& dev, const QList & partlist, const Type type) : Job(), m_Device(dev), m_PartList(partlist), m_Type(type) { } bool ResizeVolumeGroupJob::run(Report& parent) { bool rval = false; Report* report = jobStarted(parent); - for (const auto &p : partList()) { - const QString deviceNode = p->roles().has(PartitionRole::Luks) ? static_cast(&p->fileSystem())->mapperName() : p->partitionPath(); - if (type() == ResizeVolumeGroupJob::Type::Grow) - rval = LvmDevice::insertPV(*report, device(), deviceNode); - else if (type() == ResizeVolumeGroupJob::Type::Shrink) - rval = LvmDevice::removePV(*report, device(), deviceNode); + if (device().type() == Device::Type::LVM_Device) { + LvmDevice& lvm = static_cast(device()); + for (const auto &p : partList()) { + const QString deviceNode = p->roles().has(PartitionRole::Luks) ? static_cast(&p->fileSystem())->mapperName() : p->partitionPath(); + if (type() == ResizeVolumeGroupJob::Type::Grow) + rval = LvmDevice::insertPV(*report, lvm, deviceNode); + else if (type() == ResizeVolumeGroupJob::Type::Shrink) + rval = LvmDevice::removePV(*report, lvm, deviceNode); - if (rval == false) - break; + if (rval == false) + break; + } } jobFinished(*report, rval); return rval; } QString ResizeVolumeGroupJob::description() const { QString partitionList = QString(); for (const auto &p : partList()) { partitionList += p->deviceNode() + QStringLiteral(", "); } partitionList.chop(2); const qint32 count = partList().count(); if (type() == ResizeVolumeGroupJob::Type::Grow) { return xi18ncp("@info/plain", "Adding LVM Physical Volume %2 to %3.", "Adding LVM Physical Volumes %2 to %3.", count, partitionList, device().name()); } if (type() == ResizeVolumeGroupJob::Type::Shrink) { return xi18ncp("@info/plain", "Removing LVM Physical Volume %2 from %3.", "Removing LVM Physical Volumes %2 from %3.", count, partitionList, device().name()); } return xi18nc("@info/plain", "Resizing Volume Group %1 to %2.", device().name(), partitionList); } diff --git a/src/jobs/resizevolumegroupjob.h b/src/jobs/resizevolumegroupjob.h index adb2a1d..fc0e944 100644 --- a/src/jobs/resizevolumegroupjob.h +++ b/src/jobs/resizevolumegroupjob.h @@ -1,68 +1,68 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * * * 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 .* *************************************************************************/ #if !defined(KPMCORE_RESIZEVOLUMEGROUPJOB_H) #define KPMCORE_RESIZEVOLUMEGROUPJOB_H #include "core/partition.h" #include "jobs/job.h" -class LvmDevice; +class VolumeManagerDevice; class Report; class QString; class ResizeVolumeGroupJob : public Job { public: enum class Type { Grow, Shrink }; public: - ResizeVolumeGroupJob(LvmDevice& dev, const QList & partlist, const Type type); + ResizeVolumeGroupJob(VolumeManagerDevice& dev, const QList & partlist, const Type type); public: bool run(Report& parent) override; QString description() const override; protected: - LvmDevice& device() { + VolumeManagerDevice& device() { return m_Device; } - const LvmDevice& device() const { + const VolumeManagerDevice& device() const { return m_Device; } const QList & partList() const { return m_PartList; } ResizeVolumeGroupJob::Type type() const { return m_Type; } private: - LvmDevice& m_Device; + VolumeManagerDevice& m_Device; const QList m_PartList; ResizeVolumeGroupJob::Type m_Type; }; #endif diff --git a/src/ops/resizevolumegroupoperation.cpp b/src/ops/resizevolumegroupoperation.cpp index 6996899..65b219a 100644 --- a/src/ops/resizevolumegroupoperation.cpp +++ b/src/ops/resizevolumegroupoperation.cpp @@ -1,150 +1,122 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * 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 .* *************************************************************************/ #include "ops/resizevolumegroupoperation.h" #include "core/lvmdevice.h" #include "core/partition.h" #include "fs/lvm2_pv.h" #include "jobs/resizevolumegroupjob.h" #include "jobs/movephysicalvolumejob.h" #include "util/helpers.h" +#include #include #include /** Creates a new ResizeVolumeGroupOperation. @param d the Device to create the new PartitionTable on @param partList list of LVM Physical Volumes that should be in LVM Volume Group */ -ResizeVolumeGroupOperation::ResizeVolumeGroupOperation(LvmDevice& d, const QVector& partList) +ResizeVolumeGroupOperation::ResizeVolumeGroupOperation(VolumeManagerDevice& d, const QVector& partList) : Operation() , m_Device(d) , m_TargetList(partList) , m_CurrentList(d.physicalVolumes()) , m_TargetSize(0) , m_CurrentSize(0) , m_GrowVolumeGroupJob(nullptr) , m_ShrinkVolumeGroupJob(nullptr) , m_MovePhysicalVolumeJob(nullptr) { for (const auto &p : targetList()) m_TargetSize += p->capacity(); for (const auto &p : currentList()) m_CurrentSize += p->capacity(); QList toRemoveList; for (const auto &p : currentList()) if (!targetList().contains(p)) toRemoveList.append(p); QList toInsertList; for (const auto &p : targetList()) if (!currentList().contains(p)) toInsertList.append(p); - qint64 currentFreePE = 0; - for (const auto &p : currentList()) { - FS::lvm2_pv *lvm2PVFs; - innerFS(p, lvm2PVFs); - currentFreePE += lvm2PVFs->freePE(); - } - qint64 removedFreePE = 0; - for (const auto &p : qAsConst(toRemoveList)) { - FS::lvm2_pv *lvm2PVFs; - innerFS(p, lvm2PVFs); - removedFreePE += lvm2PVFs->freePE(); - } - qint64 freePE = currentFreePE - removedFreePE; - qint64 movePE = 0; - for (const auto &p : qAsConst(toRemoveList)) { - FS::lvm2_pv *lvm2PVFs; - innerFS(p, lvm2PVFs); - movePE += lvm2PVFs->allocatedPE(); - } - qint64 growPE = 0; - for (const auto &p : qAsConst(toInsertList)) { - growPE += p->capacity() / device().peSize(); + if (!toInsertList.isEmpty()) { + m_GrowVolumeGroupJob = new ResizeVolumeGroupJob(d, toInsertList, ResizeVolumeGroupJob::Type::Grow); + addJob(growVolumeGroupJob()); } - if ( movePE > (freePE + growPE)) { - // *ABORT* can't move - } else if (partList == currentList()) { - // *DO NOTHING* - } else { - if (!toInsertList.isEmpty()) { - m_GrowVolumeGroupJob = new ResizeVolumeGroupJob(d, toInsertList, ResizeVolumeGroupJob::Type::Grow); - addJob(growVolumeGroupJob()); - } - if (!toRemoveList.isEmpty()) { - m_MovePhysicalVolumeJob = new MovePhysicalVolumeJob(d, toRemoveList); - m_ShrinkVolumeGroupJob = new ResizeVolumeGroupJob(d, toRemoveList, ResizeVolumeGroupJob::Type::Shrink); - addJob(movePhysicalVolumeJob()); - addJob(shrinkvolumegroupjob()); - } + if (!toRemoveList.isEmpty()) { + m_MovePhysicalVolumeJob = new MovePhysicalVolumeJob(d, toRemoveList); + m_ShrinkVolumeGroupJob = new ResizeVolumeGroupJob(d, toRemoveList, ResizeVolumeGroupJob::Type::Shrink); + addJob(movePhysicalVolumeJob()); + addJob(shrinkvolumegroupjob()); } } QString ResizeVolumeGroupOperation::description() const { QString tList = QString(); for (const auto &p : targetList()) { tList += p->deviceNode() + QStringLiteral(", "); } tList.chop(2); QString curList = QString(); for (const auto &p : currentList()) { curList += p->deviceNode() + QStringLiteral(", "); } curList.chop(2); return xi18nc("@info/plain", "Resize volume %1 from %2 to %3", device().name(), curList, tList); } bool ResizeVolumeGroupOperation::targets(const Device& d) const { return d == device(); } bool ResizeVolumeGroupOperation::targets(const Partition& p) const { for (const auto &partition : targetList()) { if (partition->partitionPath() == p.partitionPath()) { return true; } } return false; } void ResizeVolumeGroupOperation::preview() { //assuming that targetSize is larger than the allocated space. device().setTotalLogical(targetSize() / device().logicalSize()); device().partitionTable()->setFirstUsableSector(PartitionTable::defaultFirstUsable(device(), PartitionTable::vmd)); device().partitionTable()->setLastUsableSector(PartitionTable::defaultLastUsable(device(), PartitionTable::vmd)); device().partitionTable()->updateUnallocated(device()); } void ResizeVolumeGroupOperation::undo() { device().setTotalLogical(currentSize() / device().logicalSize()); device().partitionTable()->setFirstUsableSector(PartitionTable::defaultFirstUsable(device(), PartitionTable::vmd)); device().partitionTable()->setLastUsableSector(PartitionTable::defaultLastUsable(device(), PartitionTable::vmd)); device().partitionTable()->updateUnallocated(device()); } diff --git a/src/ops/resizevolumegroupoperation.h b/src/ops/resizevolumegroupoperation.h index c46364c..6ae0285 100644 --- a/src/ops/resizevolumegroupoperation.h +++ b/src/ops/resizevolumegroupoperation.h @@ -1,109 +1,110 @@ /************************************************************************* * Copyright (C) 2016 by Chantara Tith * * Copyright (C) 2016 by Andrius Štikonas * * * * 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 .* *************************************************************************/ #if !defined(KPMCORE_RESIZEVOLUMEGROUPOPERATION_H) #define KPMCORE_RESIZEVOLUMEGROUPOPERATION_H #include "util/libpartitionmanagerexport.h" #include "ops/operation.h" -#include "core/lvmdevice.h" +#include "core/volumemanagerdevice.h" #include +#include class ResizeVolumeGroupJob; class MovePhysicalVolumeJob; class OperationStack; class LvmDevice; class LIBKPMCORE_EXPORT ResizeVolumeGroupOperation : public Operation { Q_DISABLE_COPY(ResizeVolumeGroupOperation) friend class OperationStack; public: - ResizeVolumeGroupOperation(LvmDevice& dev, const QVector& partlist); + ResizeVolumeGroupOperation(VolumeManagerDevice& dev, const QVector& partlist); public: QString iconName() const override { return QStringLiteral("arrow-right-double"); } QString description() const override; virtual bool targets(const Device&) const override; virtual bool targets(const Partition&) const override; virtual void preview() override; virtual void undo() override; QStringList getToRemoveList(); QStringList getToInsertList(); protected: - LvmDevice& device() { + VolumeManagerDevice& device() { return m_Device; } - const LvmDevice& device() const { + const VolumeManagerDevice& device() const { return m_Device; } const QVector& targetList() const { return m_TargetList; } const QVector& currentList() const { return m_CurrentList; } qint64 targetSize() const { return m_TargetSize; } qint64 currentSize() const { return m_CurrentSize; } ResizeVolumeGroupJob* growVolumeGroupJob() { return m_GrowVolumeGroupJob; } ResizeVolumeGroupJob* shrinkvolumegroupjob() { return m_ShrinkVolumeGroupJob; } MovePhysicalVolumeJob* movePhysicalVolumeJob() { return m_MovePhysicalVolumeJob; } private: - LvmDevice& m_Device; + VolumeManagerDevice& m_Device; QVector m_TargetList; QVector m_CurrentList; qint64 m_TargetSize; qint64 m_CurrentSize; ResizeVolumeGroupJob *m_GrowVolumeGroupJob; ResizeVolumeGroupJob *m_ShrinkVolumeGroupJob; MovePhysicalVolumeJob *m_MovePhysicalVolumeJob; }; #endif