From e24d14c5126f50c0776c8a9da6000763bfb67453 Mon Sep 17 00:00:00 2001 From: dalto Date: Sat, 6 Nov 2021 09:44:27 -0500 Subject: [PATCH] [zfs] Initial commit for zfs module --- src/modules/zfs/CMakeLists.txt | 13 ++++ src/modules/zfs/ZfsJob.cpp | 118 ++++++++++++++++++++++++++++++++ src/modules/zfs/ZfsJob.h | 50 ++++++++++++++ src/modules/zfs/zfs.conf | 38 ++++++++++ src/modules/zfs/zfs.schema.yaml | 22 ++++++ 5 files changed, 241 insertions(+) create mode 100644 src/modules/zfs/CMakeLists.txt create mode 100644 src/modules/zfs/ZfsJob.cpp create mode 100644 src/modules/zfs/ZfsJob.h create mode 100644 src/modules/zfs/zfs.conf create mode 100644 src/modules/zfs/zfs.schema.yaml diff --git a/src/modules/zfs/CMakeLists.txt b/src/modules/zfs/CMakeLists.txt new file mode 100644 index 000000000..2feb911d0 --- /dev/null +++ b/src/modules/zfs/CMakeLists.txt @@ -0,0 +1,13 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +calamares_add_plugin( zfs + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + ZfsJob.cpp + SHARED_LIB +) + diff --git a/src/modules/zfs/ZfsJob.cpp b/src/modules/zfs/ZfsJob.cpp new file mode 100644 index 000000000..2602c5417 --- /dev/null +++ b/src/modules/zfs/ZfsJob.cpp @@ -0,0 +1,118 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Evan James + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "ZfsJob.h" + +#include "utils/CalamaresUtilsSystem.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" + +ZfsJob::ZfsJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + +ZfsJob::~ZfsJob() {} + +QString +ZfsJob::prettyName() const +{ + return tr( "Create ZFS pools and datasets" ); +} + +Calamares::JobResult +ZfsJob::exec() +{ + QList< QVariant > partitions; + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + if ( gs && gs->contains( "partitions" ) && gs->value( "partitions" ).canConvert( QVariant::List ) ) + { + partitions = gs->value( "partitions" ).toList(); + } + else + { + cWarning() << "No *partitions* defined."; + return Calamares::JobResult::internalError( tr( "Configuration Error" ), + tr( "No partitions are available for Zfs." ), + Calamares::JobResult::InvalidConfiguration ); + } + + const CalamaresUtils::System* system = CalamaresUtils::System::instance(); + + for ( auto& partition : qAsConst( partitions ) ) + { + QVariantMap pMap; + if ( partition.canConvert( QVariant::Map ) ) + pMap = partition.toMap(); + + // If it isn't a zfs partition, ignore it + if ( pMap[ "fsName" ] != "zfs" ) + continue; + + // Find the best device identifier, if one isn't available, skip this partition + QString deviceName; + if ( pMap[ "partuuid" ].toString() != "" ) + deviceName = "/dev/disk/by-partuuid/" + pMap[ "partuuid" ].toString().toLower(); + else if ( pMap[ "device" ].toString() != "" ) + deviceName = pMap[ "device" ].toString().toLower(); + else + continue; + + // Create the zpool + auto r + = system->runCommand( { "sh", "-c", "zpool create " + m_poolOptions + " " + m_poolName + " " + deviceName }, + std::chrono::seconds( 10 ) ); + if ( r.getExitCode() != 0 ) + return Calamares::JobResult::error( "message", "Failed to create zpool on " + deviceName ); + + // Create the datasets + for ( const auto& dataset : qAsConst( m_datasets ) ) + { + QVariantMap dsMap = dataset.toMap(); + + // Make sure all values are valid + if ( dsMap[ "dsName" ].toString().isEmpty() || dsMap[ "mountpoint" ].toString().isEmpty() + || dsMap[ "canMount" ].toString().isEmpty() ) + { + cWarning() << "Bad dataset entry"; + continue; + } + + // Create the dataset. We set canmount=no regardless of the setting for now. + // It is modified to the correct value in the mount module to ensure mount order is maintained + r = system->runCommand( { "sh", + "-c", + "zfs create " + m_datasetOptions + + " -o canmount=off -o mountpoint=" + dsMap[ "mountpoint" ].toString() + " " + + m_poolName + "/" + dsMap[ "dsName" ].toString() }, + std::chrono::seconds( 10 ) ); + if ( r.getExitCode() != 0 ) + cWarning() << "Failed to create dataset" << dsMap[ "dsName" ].toString(); + } + } + + return Calamares::JobResult::ok(); +} + + +void +ZfsJob::setConfigurationMap( const QVariantMap& map ) +{ + m_poolName = CalamaresUtils::getString( map, "poolName" ); + m_poolOptions = CalamaresUtils::getString( map, "poolOptions" ); + m_datasetOptions = CalamaresUtils::getString( map, "datasetOptions" ); + + m_datasets = CalamaresUtils::getList( map, "datasets" ); +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( ZfsJobFactory, registerPlugin< ZfsJob >(); ) diff --git a/src/modules/zfs/ZfsJob.h b/src/modules/zfs/ZfsJob.h new file mode 100644 index 000000000..87646a227 --- /dev/null +++ b/src/modules/zfs/ZfsJob.h @@ -0,0 +1,50 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Evan James + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef ZFSJOB_H +#define ZFSJOB_H + +#include +#include +#include + +#include "CppJob.h" + +#include "utils/PluginFactory.h" + +#include "DllMacro.h" + +/** @brief Create zpools and zfs datasets + * + */ +class PLUGINDLLEXPORT ZfsJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit ZfsJob( QObject* parent = nullptr ); + ~ZfsJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private: + QString m_poolName; + QString m_poolOptions; + QString m_datasetOptions; + + QList m_datasets; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( ZfsJobFactory ) + +#endif // ZFSJOB_H diff --git a/src/modules/zfs/zfs.conf b/src/modules/zfs/zfs.conf new file mode 100644 index 000000000..f2f8f52b0 --- /dev/null +++ b/src/modules/zfs/zfs.conf @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The zfs module creates the zfs pools and datasets +# +# +# +--- +# The name to be used for the zpool +poolName: zpcala + +# A list of options that will be passed to zpool create +poolOptions: "-f -o ashift=12 -O mountpoint=none -O acltype=posixacl -O relatime=on" + +# A list of options that will be passed to zfs create when creating each dataset +# Do not include "canmount" or "mountpoint" as those are set below in the datasets array +datasetOptions: "-o compression=lz4 -o atime=off -o xattr=sa" + +# An array of datasets that will be created on the zpool mounted at / +datasets: + - dsName: ROOT + mountpoint: none + canMount: off + - dsName: ROOT/distro + mountpoint: none + canMount: off + - dsName: ROOT/distro/root + mountpoint: / + canMount: noauto + - dsName: ROOT/distro/home + mountpoint: /home + canMount: on + - dsName: ROOT/distro/varcache + mountpoint: /var/cache + canMount: on + - dsName: ROOT/distro/varlog + mountpoint: /var/log + canMount: on diff --git a/src/modules/zfs/zfs.schema.yaml b/src/modules/zfs/zfs.schema.yaml new file mode 100644 index 000000000..fb83778ad --- /dev/null +++ b/src/modules/zfs/zfs.schema.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/zfs +additionalProperties: false +type: object +properties: + poolName: { type: string } + poolOptions: { type: string } + datasetOptions: { type: string } + datasets: + type: array + items: + type: object + additionalProperties: false + properties: + dsName: { type: string } + mountpoint: { type: string } + canMount: { type: string } + required: [ dsName, mountpoint, canmount ] +required: [ poolName, datasets ]