From f0ea95062b97fb26cce649657bf4b569f8e66c9e Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Tue, 22 Oct 2024 15:45:57 +0300 Subject: [PATCH] storage: Allow Anaconda to override feature detection --- doc/anaconda.md | 39 ++++++++++ pkg/storaged/block/create-pages.jsx | 2 +- pkg/storaged/client.js | 111 +++++++++++++++++++++------- pkg/storaged/overview/overview.jsx | 2 +- 4 files changed, 127 insertions(+), 27 deletions(-) diff --git a/doc/anaconda.md b/doc/anaconda.md index 0ef6fb2ae016..77eb46a0dc62 100644 --- a/doc/anaconda.md +++ b/doc/anaconda.md @@ -89,6 +89,45 @@ case, Cockpit will use the type from "default_fsys_type". } ``` +Overriding Cockpit feature detection +------------------------------------ + +Cockpit Storage performs feature detection when it loads and disables +parts of its UI accordingly. For example, the "Create LVM2 volume +group" menu item will only be shown when the LVM2 support for UDisks2 +is installed. + +Anaconda can override this feature detection and force LVM2 to be off +even if all necessary software is installed. + +This is done with the `features` entry: + +```json +{ + "features": { + "lvm2": false, + "stratis": false, + } +} +``` + +The defaults for this, when in Anaconda mode, are as follows. They +make sense for offline operation. + +``` +{ + "btrfs": true, + "lvm2": true, + "vdo": true, + "legacy_vdo": true, + "stratis": true, + "nfs": false, + "iscsi": false, + "clevis": false, + "packagekit": false +} +``` + Exported information -------------------- diff --git a/pkg/storaged/block/create-pages.jsx b/pkg/storaged/block/create-pages.jsx index 555e672bb710..7f34bbca0c0a 100644 --- a/pkg/storaged/block/create-pages.jsx +++ b/pkg/storaged/block/create-pages.jsx @@ -58,7 +58,7 @@ export function make_block_page(parent, block, card) { const is_btrfs = (fstab_config.length > 0 && (fstab_config[2].indexOf("subvol=") >= 0 || fstab_config[2].indexOf("subvolid=") >= 0)); - const block_btrfs_blockdev = content_block && client.blocks_fsys_btrfs[content_block.path]; + const block_btrfs_blockdev = client.features.btrfs && content_block && client.blocks_fsys_btrfs[content_block.path]; const single_device_volume = block_btrfs_blockdev && block_btrfs_blockdev.data.num_devices === 1; if (client.blocks_ptable[block.path]) { diff --git a/pkg/storaged/client.js b/pkg/storaged/client.js index b247f9f00248..7eaf6554fa70 100644 --- a/pkg/storaged/client.js +++ b/pkg/storaged/client.js @@ -859,43 +859,64 @@ function init_model(callback) { if (!client.manager.valid) return; - try { - await client.manager.EnableModule("btrfs", true); - client.manager_btrfs = proxy("Manager.BTRFS", "Manager"); - await client.manager_btrfs.wait(); - client.features.btrfs = client.manager_btrfs.valid; - if (client.features.btrfs) - btrfs_start_polling(); - } catch (error) { - console.warn("Can't enable storaged btrfs module", error.toString()); + if (!client.anaconda_feature("btrfs")) { + client.features.btrfs = false; + } else { + try { + await client.manager.EnableModule("btrfs", true); + client.manager_btrfs = proxy("Manager.BTRFS", "Manager"); + await client.manager_btrfs.wait(); + client.features.btrfs = client.manager_btrfs.valid; + if (client.features.btrfs) + btrfs_start_polling(); + } catch (error) { + console.warn("Can't enable storaged btrfs module", error.toString()); + } } - try { - await client.manager.EnableModule("iscsi", true); - client.manager_iscsi = proxy("Manager.ISCSI.Initiator", "Manager"); - await client.manager_iscsi.wait(); - client.features.iscsi = (client.manager_iscsi.valid && client.manager_iscsi.SessionsSupported !== false); - } catch (error) { - console.warn("Can't enable storaged iscsi module", error.toString()); + if (!client.anaconda_feature("iscsi")) { + client.features.iscsi = false; + } else { + try { + await client.manager.EnableModule("iscsi", true); + client.manager_iscsi = proxy("Manager.ISCSI.Initiator", "Manager"); + await client.manager_iscsi.wait(); + client.features.iscsi = (client.manager_iscsi.valid + && client.manager_iscsi.SessionsSupported !== false); + } catch (error) { + console.warn("Can't enable storaged iscsi module", error.toString()); + } } - try { - await client.manager.EnableModule("lvm2", true); - client.manager_lvm2 = proxy("Manager.LVM2", "Manager"); - await client.manager_lvm2.wait(); - client.features.lvm2 = client.manager_lvm2.valid; - } catch (error) { - console.warn("Can't enable storaged lvm2 module", error.toString()); + if (!client.anaconda_feature("lvm2")) { + client.features.iscsi = false; + } else { + try { + await client.manager.EnableModule("lvm2", true); + client.manager_lvm2 = proxy("Manager.LVM2", "Manager"); + await client.manager_lvm2.wait(); + client.features.lvm2 = client.manager_lvm2.valid; + } catch (error) { + console.warn("Can't enable storaged lvm2 module", error.toString()); + } } } function enable_lvm_create_vdo_feature() { + if (!client.anaconda_feature("vdo")) { + client.features.lvm_create_vdo = false; + return Promise.resolve(); + } return cockpit.spawn(["vdoformat", "--version"], { err: "ignore" }) .then(() => { client.features.lvm_create_vdo = true; return Promise.resolve() }) .catch(() => Promise.resolve()); } function enable_legacy_vdo_features() { + if (!client.anaconda_feature("legacy-vdo")) { + client.features.legacy_vdo = false; + return Promise.resolve(); + } return client.legacy_vdo_overlay.start().then( function (success) { // hack here @@ -908,6 +929,10 @@ function init_model(callback) { } function enable_clevis_features() { + if (!client.anaconda_feature("clevis")) { + client.features.clevis = false; + return Promise.resolve(); + } return cockpit.script("type clevis-luks-bind", { err: "ignore" }).then( function () { client.features.clevis = true; @@ -919,6 +944,10 @@ function init_model(callback) { } function enable_nfs_features() { + if (!client.anaconda_feature("nfs")) { + client.features.nfs = false; + return Promise.resolve(); + } // mount.nfs might be in */sbin but that isn't always in // $PATH, such as when connecting from CentOS to another // machine via SSH as non-root. @@ -935,7 +964,7 @@ function init_model(callback) { } function enable_pk_features() { - if (client.in_anaconda_mode()) { + if (!client.anaconda_feature("packagekit")) { client.features.packagekit = false; return Promise.resolve(); } @@ -1366,7 +1395,15 @@ client.stratis_start = () => { const stratis3_interface_revision = "r6"; function stratis3_start() { - const stratis = cockpit.dbus("org.storage.stratis3", { superuser: "try" }); + let stratis_service = "org.storage.stratis3"; + + if (!client.anaconda_feature("stratis")) { + // HACK - There is no real clean way to switch off Stratis in + // Cockpit except by making it look for a bogus name... + stratis_service = "does.not.exist"; + } + + const stratis = cockpit.dbus(stratis_service, { superuser: "try" }); client.stratis_manager = stratis.proxy("org.storage.stratis3.Manager." + stratis3_interface_revision, "/org/storage/stratis3"); @@ -1460,6 +1497,30 @@ client.get_config = (name, def) => client.in_anaconda_mode = () => !!client.anaconda; +client.anaconda_feature = (tag) => { + if (!client.anaconda) + return true; + + const default_anaconda_features = { + nfs: false, + iscsi: false, + clevis: false, + packagekit: false, + }; + + let val = undefined; + if (client.anaconda.features) + val = client.anaconda.features[tag]; + if (val === undefined) + val = default_anaconda_features[tag]; + if (val === undefined) + val = true; + + console.log("AF", tag, val); + + return val; +}; + client.strip_mount_point_prefix = (dir) => { const mpp = client.anaconda?.mount_point_prefix; diff --git a/pkg/storaged/overview/overview.jsx b/pkg/storaged/overview/overview.jsx index d6cb2f379beb..9db5fb86e1d9 100644 --- a/pkg/storaged/overview/overview.jsx +++ b/pkg/storaged/overview/overview.jsx @@ -148,7 +148,7 @@ const OverviewCard = ({ card, plot_state }) => { ].filter(item => !!item); const net_menu_items = [ - !client.in_anaconda_mode() && menu_item(nfs_feature, _("New NFS mount"), () => nfs_fstab_dialog(null, null)), + menu_item(nfs_feature, _("New NFS mount"), () => nfs_fstab_dialog(null, null)), menu_item(iscsi_feature, _("Change iSCSI initiater name"), () => iscsi_change_name()), menu_item(iscsi_feature, _("Add iSCSI portal"), () => iscsi_discover()), ].filter(item => !!item);