From 208d58bcd93edfa31e80e8d1a99425573f929de5 Mon Sep 17 00:00:00 2001 From: Caio Date: Thu, 7 Jun 2018 17:22:22 -0300 Subject: [PATCH] [partition] Including CreateVolumeGroupDialog and fixing some of its GUI issues. --- src/modules/partition/CMakeLists.txt | 1 + src/modules/partition/core/DeviceModel.cpp | 16 ++ src/modules/partition/core/DeviceModel.h | 2 + .../partition/core/PartitionCoreModule.cpp | 37 ++++- .../partition/core/PartitionCoreModule.h | 9 +- .../partition/gui/CreateVolumeGroupDialog.cpp | 53 +++++++ .../partition/gui/CreateVolumeGroupDialog.h | 39 +++++ src/modules/partition/gui/PartitionPage.cpp | 47 +++++- .../partition/gui/VolumeGroupBaseDialog.cpp | 148 +++++++++++++++++- .../partition/gui/VolumeGroupBaseDialog.h | 49 +++++- .../partition/gui/VolumeGroupBaseDialog.ui | 41 +++-- .../partition/jobs/CreateVolumeGroupJob.cpp | 4 - 12 files changed, 409 insertions(+), 37 deletions(-) create mode 100644 src/modules/partition/gui/CreateVolumeGroupDialog.cpp create mode 100644 src/modules/partition/gui/CreateVolumeGroupDialog.h diff --git a/src/modules/partition/CMakeLists.txt b/src/modules/partition/CMakeLists.txt index 2f0e46bea..279eaf0ef 100644 --- a/src/modules/partition/CMakeLists.txt +++ b/src/modules/partition/CMakeLists.txt @@ -35,6 +35,7 @@ if ( KPMcore_FOUND ) gui/BootInfoWidget.cpp gui/ChoicePage.cpp gui/CreatePartitionDialog.cpp + gui/CreateVolumeGroupDialog.cpp gui/DeviceInfoWidget.cpp gui/EditExistingPartitionDialog.cpp gui/EncryptWidget.cpp diff --git a/src/modules/partition/core/DeviceModel.cpp b/src/modules/partition/core/DeviceModel.cpp index 00058bac4..95d7ddb72 100644 --- a/src/modules/partition/core/DeviceModel.cpp +++ b/src/modules/partition/core/DeviceModel.cpp @@ -25,10 +25,12 @@ // KPMcore #include +#include // KF5 #include +#include #include // STL @@ -116,3 +118,17 @@ DeviceModel::swapDevice( Device* oldDevice, Device* newDevice ) emit dataChanged( index( indexOfOldDevice ), index( indexOfOldDevice ) ); } + +void +DeviceModel::addDevice( Device *device ) +{ + beginResetModel(); + + m_devices << device; + std::sort( m_devices.begin(), m_devices.end(), []( const Device* dev1, const Device* dev2 ) + { + return dev1->deviceNode() < dev2->deviceNode(); + } ); + + endResetModel(); +} diff --git a/src/modules/partition/core/DeviceModel.h b/src/modules/partition/core/DeviceModel.h index ccca426bd..84f1c3c9d 100644 --- a/src/modules/partition/core/DeviceModel.h +++ b/src/modules/partition/core/DeviceModel.h @@ -49,6 +49,8 @@ public: void swapDevice( Device* oldDevice, Device* newDevice ); + void addDevice( Device* device ); + private: QList< Device* > m_devices; }; diff --git a/src/modules/partition/core/PartitionCoreModule.cpp b/src/modules/partition/core/PartitionCoreModule.cpp index 7229c8305..710f85378 100644 --- a/src/modules/partition/core/PartitionCoreModule.cpp +++ b/src/modules/partition/core/PartitionCoreModule.cpp @@ -273,6 +273,10 @@ PartitionCoreModule::createVolumeGroup( QString &vgName, QVector< const Partition* > pvList, qint32 peSize ) { + // Appending '_' character in case of repeated VG name + while ( hasVGwithThisName( vgName ) ) + vgName.append('_'); + CreateVolumeGroupJob* job = new CreateVolumeGroupJob( vgName, pvList, peSize ); job->updatePreview(); @@ -283,6 +287,10 @@ PartitionCoreModule::createVolumeGroup( QString &vgName, DeviceInfo* deviceInfo = new DeviceInfo( device ); + deviceInfo->partitionModel->init( device, osproberEntries() ); + + m_deviceModel->addDevice( device ); + m_deviceInfos << deviceInfo; deviceInfo->jobs << Calamares::job_ptr( job ); @@ -458,12 +466,37 @@ PartitionCoreModule::efiSystemPartitions() const return m_efiSystemPartitions; } -QList< const Partition* > +QVector< const Partition* > PartitionCoreModule::lvmPVs() const { return m_lvmPVs; } +bool +PartitionCoreModule::hasVGwithThisName( const QString& name ) const +{ + for ( DeviceInfo* d : m_deviceInfos ) + if ( dynamic_cast(d->device.data()) && + d->device.data()->name() == name) + return true; + + return false; +} + +bool +PartitionCoreModule::isInVG( const Partition *partition ) const +{ + for ( DeviceInfo* d : m_deviceInfos ) + { + LvmDevice* vg = dynamic_cast( d->device.data() ); + + if ( vg && vg->physicalVolumes().contains( partition )) + return true; + } + + return false; +} + void PartitionCoreModule::dumpQueue() const { @@ -503,6 +536,8 @@ PartitionCoreModule::refresh() updateIsDirty(); m_bootLoaderModel->update(); + scanForLVMPVs(); + //FIXME: this should be removed in favor of // proper KPM support for EFI if ( PartUtils::isEfiSystem() ) diff --git a/src/modules/partition/core/PartitionCoreModule.h b/src/modules/partition/core/PartitionCoreModule.h index 05c029381..cedda391d 100644 --- a/src/modules/partition/core/PartitionCoreModule.h +++ b/src/modules/partition/core/PartitionCoreModule.h @@ -24,6 +24,7 @@ #include "Typedefs.h" // KPMcore +#include #include // Qt @@ -134,7 +135,11 @@ public: QList< Partition* > efiSystemPartitions() const; - QList< const Partition* > lvmPVs() const; + QVector< const Partition* > lvmPVs() const; + + bool hasVGwithThisName( const QString& name ) const; + + bool isInVG( const Partition* partition ) const; /** * @brief findPartitionByMountPoint returns a Partition* for a given mount point. @@ -198,7 +203,7 @@ private: }; QList< DeviceInfo* > m_deviceInfos; QList< Partition* > m_efiSystemPartitions; - QList< const Partition* > m_lvmPVs; + QVector< const Partition* > m_lvmPVs; DeviceModel* m_deviceModel; BootLoaderModel* m_bootLoaderModel; diff --git a/src/modules/partition/gui/CreateVolumeGroupDialog.cpp b/src/modules/partition/gui/CreateVolumeGroupDialog.cpp new file mode 100644 index 000000000..f1ed32551 --- /dev/null +++ b/src/modules/partition/gui/CreateVolumeGroupDialog.cpp @@ -0,0 +1,53 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Caio Jordão Carvalho + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + */ + +#include "CreateVolumeGroupDialog.h" + +#include +#include + +#include +#include +#include + +CreateVolumeGroupDialog::CreateVolumeGroupDialog( QString& vgName, + QVector< const Partition* >& selectedPVs, + QVector< const Partition* > pvList, + qint32& peSize, + QWidget* parent ) + : VolumeGroupBaseDialog( vgName, pvList, peSize, parent ) + , m_selectedPVs( selectedPVs ) +{ + setWindowTitle( "Create Volume Group" ); + + vgType()->setEnabled( false ); +} + +void +CreateVolumeGroupDialog::accept() +{ + QString& name = vgNameValue(); + name = vgName()->text(); + + m_selectedPVs << checkedItems(); + + qint32& pe = peSizeValue(); + pe = peSize()->value(); + + QDialog::accept(); +} diff --git a/src/modules/partition/gui/CreateVolumeGroupDialog.h b/src/modules/partition/gui/CreateVolumeGroupDialog.h new file mode 100644 index 000000000..eb2ced137 --- /dev/null +++ b/src/modules/partition/gui/CreateVolumeGroupDialog.h @@ -0,0 +1,39 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Caio Jordão Carvalho + * + * Calamares 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. + * + * Calamares 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 Calamares. If not, see . + */ + +#ifndef CREATEVOLUMEGROUPDIALOG_H +#define CREATEVOLUMEGROUPDIALOG_H + +#include "gui/VolumeGroupBaseDialog.h" + +class CreateVolumeGroupDialog : public VolumeGroupBaseDialog +{ +public: + CreateVolumeGroupDialog( QString& vgName, + QVector< const Partition* >& selectedPVs, + QVector< const Partition* > pvList, + qint32& peSize, + QWidget* parent ); + + void accept() override; + +private: + QVector< const Partition* >& m_selectedPVs; +}; + +#endif // CREATEVOLUMEGROUPDIALOG_H diff --git a/src/modules/partition/gui/PartitionPage.cpp b/src/modules/partition/gui/PartitionPage.cpp index adebb3418..503a4e2ea 100644 --- a/src/modules/partition/gui/PartitionPage.cpp +++ b/src/modules/partition/gui/PartitionPage.cpp @@ -28,9 +28,9 @@ #include "core/PartUtils.h" #include "core/KPMHelpers.h" #include "gui/CreatePartitionDialog.h" +#include "gui/CreateVolumeGroupDialog.h" #include "gui/EditExistingPartitionDialog.h" #include "gui/ScanningDialog.h" -#include "gui/VolumeGroupBaseDialog.h" #include "ui_PartitionPage.h" #include "ui_CreatePartitionTableDialog.h" @@ -133,6 +133,8 @@ PartitionPage::updateButtons() bool isFree = KPMHelpers::isPartitionFreeSpace( partition ); bool isExtended = partition->roles().has( PartitionRole::Extended ); + bool isInVG = m_core->isInVG( partition ); + create = isFree; // Keep it simple for now: do not support editing extended partitions as // it does not work with our current edit implementation which is @@ -140,8 +142,9 @@ PartitionPage::updateButtons() // because they need to be created *before* creating logical partitions // inside them, so an edit must be applied without altering the job // order. + // TODO: See if LVM PVs can be edited in Calamares edit = !isFree && !isExtended; - del = !isFree; + del = !isFree && !isInVG; } if ( m_ui->deviceComboBox->currentIndex() >= 0 ) @@ -184,13 +187,51 @@ void PartitionPage::onNewVolumeGroupClicked() { QString vgName; + QVector< const Partition* > selectedPVs; qint32 peSize = 4; - QPointer< VolumeGroupBaseDialog > dlg = new VolumeGroupBaseDialog( vgName, m_core->lvmPVs(), peSize, this ); + QVector< const Partition* > availablePVs; + + for ( const Partition* p : m_core->lvmPVs() ) + if ( !m_core->isInVG( p ) ) + availablePVs << p; + + QPointer< CreateVolumeGroupDialog > dlg = new CreateVolumeGroupDialog( vgName, + selectedPVs, + availablePVs, + peSize, + this ); if ( dlg->exec() == QDialog::Accepted ) { + QModelIndex partitionIndex = m_ui->partitionTreeView->currentIndex(); + if ( partitionIndex.isValid() ) + { + const PartitionModel* model = static_cast< const PartitionModel* >( partitionIndex.model() ); + Q_ASSERT( model ); + Partition* partition = model->partitionForIndex( partitionIndex ); + Q_ASSERT( partition ); + + // Disable delete button if current partition was selected to be in VG + // TODO: Should Calamares edit LVM PVs which are in VGs? + if ( selectedPVs.contains( partition ) ) + m_ui->deleteButton->setEnabled( false ); + } + + QModelIndex deviceIndex = m_core->deviceModel()->index( m_ui->deviceComboBox->currentIndex(), 0 ); + Q_ASSERT( deviceIndex.isValid() ); + + QVariant previousDeviceData = m_core->deviceModel()->data( deviceIndex, Qt::ToolTipRole ); + + m_core->createVolumeGroup( vgName, selectedPVs, peSize ); + + // As createVolumeGroup method call resets deviceModel, + // is needed to set the current index in deviceComboBox as the previous one + int previousIndex = m_ui->deviceComboBox->findData( previousDeviceData, Qt::ToolTipRole ); + + if ( previousIndex != -1 ) + m_ui->deviceComboBox->setCurrentIndex( previousIndex ); } delete dlg; diff --git a/src/modules/partition/gui/VolumeGroupBaseDialog.cpp b/src/modules/partition/gui/VolumeGroupBaseDialog.cpp index b3535a004..e5da2c0ad 100644 --- a/src/modules/partition/gui/VolumeGroupBaseDialog.cpp +++ b/src/modules/partition/gui/VolumeGroupBaseDialog.cpp @@ -21,22 +21,160 @@ #include "gui/ListPhysicalVolumeWidgetItem.h" +#include + +#include +#include +#include +#include +#include +#include + VolumeGroupBaseDialog::VolumeGroupBaseDialog( QString& vgName, - QList< const Partition* > pvList, + QVector< const Partition* > pvList, qint32& peSize, - QWidget *parent) + QWidget *parent ) : QDialog(parent) , ui(new Ui::VolumeGroupBaseDialog) - , m_vgName(vgName) - , m_peSize(peSize) + , m_vgNameValue(vgName) + , m_peSizeValue(peSize) + , m_totalSizeValue(0) + , m_usedSizeValue(0) { ui->setupUi(this); for ( const Partition* p : pvList ) - ui->pvList->addItem( new ListPhysicalVolumeWidgetItem(p, false) ); + ui->pvList->addItem( new ListPhysicalVolumeWidgetItem( p, false ) ); + + ui->vgType->addItems( QStringList() << "LVM" << "RAID" ); + ui->vgType->setCurrentIndex(0); + + QRegularExpression re(R"(^(?!_|\.)[\w\-.+]+)"); + ui->vgName->setValidator( new QRegularExpressionValidator( re, this ) ); + ui->vgName->setText( m_vgNameValue ); + + ui->peSize->setValue( m_peSizeValue ); + + updateOkButton(); + updateTotalSize(); + + connect( ui->pvList, &QListWidget::itemChanged, this, + [&](QListWidgetItem*) { + updateTotalSize(); + updateOkButton(); + } ); + + connect( ui->peSize, qOverload(&QSpinBox::valueChanged), this, + [&](int) { + updateTotalSectors(); + updateOkButton(); + }); + + connect( ui->vgName, &QLineEdit::textChanged, this, + [&](const QString&) { + updateOkButton(); + }); } VolumeGroupBaseDialog::~VolumeGroupBaseDialog() { delete ui; } + +QVector< const Partition* > +VolumeGroupBaseDialog::checkedItems() const +{ + QVector< const Partition* > items; + + for ( int i = 0; i < ui->pvList->count(); i++) { + ListPhysicalVolumeWidgetItem* item = dynamic_cast< ListPhysicalVolumeWidgetItem* >( ui->pvList->item(i) ); + + if ( item && item->checkState() == Qt::Checked ) + items << item->partition(); + } + + return items; +} + +bool +VolumeGroupBaseDialog::isSizeValid() const +{ + return m_totalSizeValue >= m_usedSizeValue; +} + +void +VolumeGroupBaseDialog::updateOkButton() +{ + okButton()->setEnabled(isSizeValid() && + !checkedItems().empty() && + !ui->vgName->text().isEmpty() && + ui->peSize->value() > 0); +} + +void +VolumeGroupBaseDialog::updateTotalSize() +{ + m_totalSizeValue = 0; + + for ( const Partition *p : checkedItems()) + m_totalSizeValue += p->capacity() - p->capacity() % (ui->peSize->value() * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB)); + + ui->totalSize->setText(Capacity::formatByteSize(m_totalSizeValue)); + + updateTotalSectors(); +} + +void +VolumeGroupBaseDialog::updateTotalSectors() +{ + qint32 totalSectors = 0; + + qint32 extentSize = ui->peSize->value() * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB); + + if ( extentSize > 0 ) + totalSectors = m_totalSizeValue / extentSize; + + ui->totalSectors->setText( QString::number( totalSectors ) ); +} + +QString& +VolumeGroupBaseDialog::vgNameValue() const +{ + return m_vgNameValue; +} + +qint32& +VolumeGroupBaseDialog::peSizeValue() const +{ + return m_peSizeValue; +} + +QLineEdit* +VolumeGroupBaseDialog::vgName() const +{ + return ui->vgName; +} + +QComboBox* +VolumeGroupBaseDialog::vgType() const +{ + return ui->vgType; +} + +QSpinBox* +VolumeGroupBaseDialog::peSize() const +{ + return ui->peSize; +} + +QListWidget* +VolumeGroupBaseDialog::pvList() const +{ + return ui->pvList; +} + +QPushButton* +VolumeGroupBaseDialog::okButton() const +{ + return ui->buttonBox->button( QDialogButtonBox::StandardButton::Ok ); +} diff --git a/src/modules/partition/gui/VolumeGroupBaseDialog.h b/src/modules/partition/gui/VolumeGroupBaseDialog.h index 294effb1d..0b4a48372 100644 --- a/src/modules/partition/gui/VolumeGroupBaseDialog.h +++ b/src/modules/partition/gui/VolumeGroupBaseDialog.h @@ -27,22 +27,55 @@ namespace Ui { class VolumeGroupBaseDialog; } +class QComboBox; +class QLineEdit; +class QListWidget; +class QSpinBox; + class VolumeGroupBaseDialog : public QDialog { Q_OBJECT public: - explicit VolumeGroupBaseDialog(QString& vgName, - QList< const Partition* > pvList, - qint32& peSize, - QWidget* parent = 0); + explicit VolumeGroupBaseDialog( QString& vgName, + QVector< const Partition* > pvList, + qint32& peSize, + QWidget* parent = nullptr ); ~VolumeGroupBaseDialog(); -private: - Ui::VolumeGroupBaseDialog *ui; +protected: + virtual void updateOkButton(); - QString &m_vgName; - qint32 &m_peSize; + void updateTotalSize(); + + void updateTotalSectors(); + + QVector< const Partition* > checkedItems() const; + + bool isSizeValid() const; + + QString& vgNameValue() const; + + qint32& peSizeValue() const; + + QLineEdit* vgName() const; + + QComboBox* vgType() const; + + QSpinBox* peSize() const; + + QListWidget* pvList() const; + + QPushButton* okButton() const; + +private: + Ui::VolumeGroupBaseDialog* ui; + + QString& m_vgNameValue; + qint32& m_peSizeValue; + + qint64 m_totalSizeValue; + qint64 m_usedSizeValue; }; #endif // VOLUMEGROUPBASEDIALOG_H diff --git a/src/modules/partition/gui/VolumeGroupBaseDialog.ui b/src/modules/partition/gui/VolumeGroupBaseDialog.ui index c9cb853b8..b45d204e2 100644 --- a/src/modules/partition/gui/VolumeGroupBaseDialog.ui +++ b/src/modules/partition/gui/VolumeGroupBaseDialog.ui @@ -25,7 +25,7 @@ - + Volume Group Name: @@ -35,10 +35,10 @@ - + - + Volume Group Type: @@ -48,10 +48,10 @@ - + - + Physical Extent Size: @@ -61,10 +61,23 @@ - + + + MiB + + + 1 + + + 999 + + + 4 + + - + Total Size: @@ -74,7 +87,7 @@ - + --- @@ -84,7 +97,7 @@ - + Used Size: @@ -94,7 +107,7 @@ - + --- @@ -104,7 +117,7 @@ - + Total Sectors: @@ -114,7 +127,7 @@ - + --- @@ -124,7 +137,7 @@ - + Quantity of LVs: @@ -134,7 +147,7 @@ - + --- diff --git a/src/modules/partition/jobs/CreateVolumeGroupJob.cpp b/src/modules/partition/jobs/CreateVolumeGroupJob.cpp index 3b05b684a..7debd9475 100644 --- a/src/modules/partition/jobs/CreateVolumeGroupJob.cpp +++ b/src/modules/partition/jobs/CreateVolumeGroupJob.cpp @@ -58,10 +58,6 @@ CreateVolumeGroupJob::exec() { Report report( nullptr ); - QVector< const Partition* > pvList; - - pvList << m_pvList; - CreateVolumeGroupOperation op( m_vgName, m_pvList, m_peSize ); op.setStatus( Operation::StatusRunning );