Skip to content

Commit

Permalink
storage: Allow Anaconda to override feature detection
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollmer committed Oct 23, 2024
1 parent 21cfc3a commit 94addba
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 27 deletions.
43 changes: 43 additions & 0 deletions doc/anaconda.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,49 @@ 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
}
```

NOTE: A feature can not really be forced on when the code for it is
not installed. Setting a feature to "true" in the Anconda config means
that Cockpit will run its normal feature detection for it.

Exported information
--------------------

Expand Down
2 changes: 1 addition & 1 deletion pkg/storaged/block/create-pages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand Down
109 changes: 84 additions & 25 deletions pkg/storaged/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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.
Expand All @@ -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();
}
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -1460,6 +1497,28 @@ 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;

return val;
};

client.strip_mount_point_prefix = (dir) => {
const mpp = client.anaconda?.mount_point_prefix;

Expand Down
2 changes: 1 addition & 1 deletion pkg/storaged/overview/overview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 94addba

Please sign in to comment.