From 7a8fcd060a6f6d72e7de88d8d1649dc74e843deb Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Wed, 22 Nov 2023 15:33:32 +0200 Subject: [PATCH] WIP - anaconda mode --- doc/anaconda.md | 84 +++++++++++++++++++++ pkg/storaged/anaconda.jsx | 42 +++++++++++ pkg/storaged/block/create-pages.jsx | 3 +- pkg/storaged/block/format-dialog.jsx | 31 +++++--- pkg/storaged/block/other.jsx | 4 + pkg/storaged/client.js | 68 +++++++++++++++++ pkg/storaged/dialog.jsx | 11 ++- pkg/storaged/drive/drive.jsx | 3 + pkg/storaged/filesystem/filesystem.jsx | 12 +-- pkg/storaged/filesystem/mounting-dialog.jsx | 20 +++-- pkg/storaged/filesystem/utils.jsx | 10 ++- pkg/storaged/lvm2/volume-group.jsx | 3 + pkg/storaged/mdraid/mdraid.jsx | 6 ++ pkg/storaged/nfs/nfs.jsx | 3 + pkg/storaged/overview/overview.jsx | 6 +- pkg/storaged/pages.jsx | 2 + pkg/storaged/stratis/filesystem.jsx | 19 +++-- pkg/storaged/stratis/pool.jsx | 10 ++- pkg/storaged/stratis/stopped-pool.jsx | 3 + pkg/storaged/stratis/utils.jsx | 1 + pkg/storaged/utils.js | 8 +- 21 files changed, 310 insertions(+), 39 deletions(-) create mode 100644 doc/anaconda.md create mode 100644 pkg/storaged/anaconda.jsx diff --git a/doc/anaconda.md b/doc/anaconda.md new file mode 100644 index 00000000000..d0554f5e1fa --- /dev/null +++ b/doc/anaconda.md @@ -0,0 +1,84 @@ +Cockpit Storage in Anaconda Mode +================================ + +Anaconda (the OS Installer) can open the Cockpit "storaged" page for +advanced setup of the target storage devices. When this is done, +storaged is in a special "Anaconda mode" and behaves significantly +different. + +In essence, the storaged page restricts itself to working with the +target environment. It will hide the real root filesystem (on the USB +stick that the Live environment was booted from, say), but let the +user create a "fake" root filesystem on some block device. + +Entering Anaconda mode +---------------------- + +The "storaged" page is put into Anaconda mode by storing a +"cockpit_anaconda" item in its `window.localStorage`. The value +should be a JSON encoded object, the details of which are explained +below. + +Since both Anaconda and the storaged page are served from the same +origin, Anaconda can just execute something like this: + +``` + window.localStorage.setItem("cockpit_anaconda", + JSON.stringify({ + "mount_point_prefix": "/sysroot", + "ignore_devices": [ "/dev/sr0", "/dev/loop0" ] + })); + window.open("/cockpit/@localhost/storage/index.html", "storage-tab"); +``` + +Ignoring storage devices +------------------------ + +Anaconda needs to tell Cockpit which devices can not be used to +install the OS on. This is done with the "ignore_devices" entry, which +is a array of strings. + +``` +{ + "ignore_devices": [ "/dev/sda" ] +} +``` + +Entries in that array can contain pathname of block devices, names of +LVM2 volume groups, and names of Stratis pools. + +Mount point prefix +------------------ + +Cockpit can be put into a kind of "chroot" environment by giving it a +mount point prefix like so: + +``` +{ + "mount_point_prefix": "/sysroot" +} +``` + +This works at the UI level: filesystems that have mount points outside +of "/sysroot" are hidden from the user, and when letting the user work +with mount points below "/sysroot", the "/sysroot" prefix is +omitted. So when the user says to create a filesystem on "/var", they +are actually creating one on "/sysroot/var". + +However, Cockpit (via UDisks2) will still write the new mount point +configuration into the real /etc/fstab (_not_ /sysroot/etc/fstab). + +In addition to that, Cockpit will also store the mount points in the +`"cockpit_mount_points"` item in `window.localStorage`, as a JSON +encoded object, for the benefit of Anaconda. + +This is a simple map from mount point to block device, like + +``` +{ + "/boot": "/dev/vda1", + "/": "/dev/vda2" +} +``` + +The mount points do not include the mount point prefix. diff --git a/pkg/storaged/anaconda.jsx b/pkg/storaged/anaconda.jsx new file mode 100644 index 00000000000..edd25998600 --- /dev/null +++ b/pkg/storaged/anaconda.jsx @@ -0,0 +1,42 @@ +/* + * This file is part of Cockpit. + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Cockpit is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * Cockpit 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Cockpit; If not, see . + */ + +import cockpit from "cockpit"; +import React from "react"; +import client from "./client.js"; + +import { StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js"; +import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js"; + +const _ = cockpit.gettext; + +export const AnacondaAdvice = () => { + if (!client.in_anaconda_mode()) + return null; + + return ( + + +

Anaconda will tell us here what is wrong with the current config.

+
+
+ ); +}; diff --git a/pkg/storaged/block/create-pages.jsx b/pkg/storaged/block/create-pages.jsx index eb56b420dbf..13efdb0a3e0 100644 --- a/pkg/storaged/block/create-pages.jsx +++ b/pkg/storaged/block/create-pages.jsx @@ -103,5 +103,6 @@ export function make_block_page(parent, block, card) { } } - new_page(parent, card); + if (card) + new_page(parent, card); } diff --git a/pkg/storaged/block/format-dialog.jsx b/pkg/storaged/block/format-dialog.jsx index 4b21aba617b..e94f26f7f51 100644 --- a/pkg/storaged/block/format-dialog.jsx +++ b/pkg/storaged/block/format-dialog.jsx @@ -47,14 +47,19 @@ const _ = cockpit.gettext; export function initial_tab_options(client, block, for_fstab) { const options = { }; - get_parent_blocks(client, block.path).forEach(p => { - // "nofail" is the default for new filesystems with Cockpit so - // that a failure to mount one of them will not prevent - // Cockpit from starting. This allows people to debug and fix - // these failures with Cockpit itself. - // + // "nofail" is the default for new filesystems with Cockpit so + // that a failure to mount one of them will not prevent + // Cockpit from starting. This allows people to debug and fix + // these failures with Cockpit itself. + // + // In Anaconda mode however, we don't make "nofail" the + // default since people will be creating the core filesystems + // like "/", "/var", etc. + + if (!client.in_anaconda_mode()) options.nofail = true; + get_parent_blocks(client, block.path).forEach(p => { if (is_netdev(client, p)) { options._netdev = true; } @@ -142,10 +147,10 @@ export function format_dialog(client, path, start, size, enable_dos_extended) { return false; }) .then(version => { - format_dialog_internal(client, path, start, size, enable_dos_extended, version); + return format_dialog_internal(client, path, start, size, enable_dos_extended, version); }); } else { - format_dialog_internal(client, path, start, size, enable_dos_extended); + return format_dialog_internal(client, path, start, size, enable_dos_extended); } } @@ -242,6 +247,10 @@ function format_dialog_internal(client, path, start, size, enable_dos_extended, if (old_opts == undefined) old_opts = initial_mount_options(client, block); + old_dir = client.strip_mount_point_prefix(old_dir); + if (old_dir === false) + return Promise.reject(_("This device can not be used for the installation target.")); + const split_options = parse_options(old_opts); extract_option(split_options, "noauto"); const opt_ro = extract_option(split_options, "ro"); @@ -279,7 +288,10 @@ function format_dialog_internal(client, path, start, size, enable_dos_extended, visible: is_filesystem, value: old_dir || "", validate: (val, values, variant) => { - return is_valid_mount_point(client, block, val, variant == "nomount"); + return is_valid_mount_point(client, + block, + client.add_mount_point_prefix(val), + variant == "nomount"); } }), SelectOne("type", _("Type"), @@ -474,6 +486,7 @@ function format_dialog_internal(client, path, start, size, enable_dos_extended, if (mount_point != "") { if (mount_point[0] != "/") mount_point = "/" + mount_point; + mount_point = client.add_mount_point_prefix(mount_point); config_items.push(["fstab", { dir: { t: 'ay', v: encode_filename(mount_point) }, diff --git a/pkg/storaged/block/other.jsx b/pkg/storaged/block/other.jsx index 52a92ea4e09..8f83e60a27d 100644 --- a/pkg/storaged/block/other.jsx +++ b/pkg/storaged/block/other.jsx @@ -19,6 +19,7 @@ import cockpit from "cockpit"; import React from "react"; +import client from "../client.js"; import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js"; import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js"; @@ -32,6 +33,9 @@ import { make_block_page } from "../block/create-pages.jsx"; const _ = cockpit.gettext; export function make_other_page(parent, block) { + if (client.should_ignore_block(block)) + return; + const other_card = new_card({ title: _("Block device"), next: null, diff --git a/pkg/storaged/client.js b/pkg/storaged/client.js index 9772851f7fe..0e58e1a3b8a 100644 --- a/pkg/storaged/client.js +++ b/pkg/storaged/client.js @@ -609,6 +609,7 @@ client.update = (first_time) => { client.ready = true; if (client.ready) { update_indices(); + client.export_mount_point_mapping(); reset_pages(); make_overview_page(); client.dispatchEvent("changed"); @@ -741,6 +742,21 @@ function init_model(callback) { ).then(() => info); } + try { + client.anaconda = JSON.parse(window.localStorage.getItem("cockpit_anaconda")); + } catch { + console.warn("Can't parse cockpit_anaconda configuration as JSON"); + client.anaconda = null; + } + + // XXX - debug + client.anaconda = { + mount_point_prefix: "/sysroot", + ignore_devices: ["/dev/vda", "/dev/sr0", "fedora"], + }; + + console.log("ANACONDA", client.anaconda); + pull_time().then(() => { read_os_release().then(os_release => { client.os_release = os_release; @@ -1484,4 +1500,56 @@ client.get_config = (name, def) => { } }; +client.in_anaconda_mode = () => !!client.anaconda; + +client.strip_mount_point_prefix = (dir) => { + const mpp = client.anaconda?.mount_point_prefix; + + if (dir && mpp) { + if (dir.indexOf(mpp) != 0) + return false; + + dir = dir.substr(mpp.length); + if (dir == "") + dir = "/"; + } + + return dir; +}; + +client.add_mount_point_prefix = (dir) => { + const mpp = client.anaconda?.mount_point_prefix; + if (mpp) { + if (dir == "/") + dir = mpp; + else + dir = mpp + dir; + } + return dir; +}; + +client.should_ignore_device = (devname) => { + return client.anaconda?.ignore_devices && client.anaconda.ignore_devices.indexOf(devname) != -1; +}; + +client.should_ignore_block = (block) => { + return client.should_ignore_device(utils.decode_filename(block.PreferredDevice)); +}; + +client.export_mount_point_mapping = () => { + const mpm = { }; + for (const p in client.blocks) { + const b = client.blocks[p]; + for (const c of b.Configuration) { + if (c[0] == "fstab") { + const dir = client.strip_mount_point_prefix(utils.decode_filename(c[1].dir.v)); + if (dir) + mpm[dir] = utils.decode_filename(b.PreferredDevice); + } + } + } + + window.localStorage.setItem("cockpit_mount_points", JSON.stringify(mpm)); +}; + export default client; diff --git a/pkg/storaged/dialog.jsx b/pkg/storaged/dialog.jsx index c1a262adf15..e95c19bd2fe 100644 --- a/pkg/storaged/dialog.jsx +++ b/pkg/storaged/dialog.jsx @@ -1103,7 +1103,8 @@ export const BlockingMessage = (usage) => { pvol: _("physical volume of LVM2 volume group"), "mdraid-member": _("member of MDRAID device"), vdo: _("backing device for VDO device"), - "stratis-pool-member": _("member of Stratis pool") + "stratis-pool-member": _("member of Stratis pool"), + mounted: _("Filesystem outside the target"), }; const rows = []; @@ -1193,9 +1194,15 @@ export const TeardownMessage = (usage, expect_single_unmount) => { const name = (fsys ? fsys.Devnode : block_name(client.blocks[use.block.CryptoBackingDevice] || use.block)); + let location = use.location; + if (use.usage == "mounted") { + location = client.strip_mount_point_prefix(location); + if (location === false) + location = _("(Not part of target)"); + } rows.push({ columns: [name, - use.location || "-", + location || "-", use.actions.length ? use.actions.join(", ") : "-", { title: , diff --git a/pkg/storaged/drive/drive.jsx b/pkg/storaged/drive/drive.jsx index 57d71414585..f89428a1486 100644 --- a/pkg/storaged/drive/drive.jsx +++ b/pkg/storaged/drive/drive.jsx @@ -46,6 +46,9 @@ export function make_drive_page(parent, drive) { if (!block) return; + if (client.should_ignore_block(block)) + return; + const drive_card = new_card({ title: _("Drive"), next: null, diff --git a/pkg/storaged/filesystem/filesystem.jsx b/pkg/storaged/filesystem/filesystem.jsx index fd119e3f95a..dfedb95236a 100644 --- a/pkg/storaged/filesystem/filesystem.jsx +++ b/pkg/storaged/filesystem/filesystem.jsx @@ -84,11 +84,13 @@ export function make_filesystem_card(next, backing_block, content_block, fstab_c const mounted = content_block && is_mounted(client, content_block); let mp_text; - if (mount_point && mounted) - mp_text = mount_point; - else if (mount_point && !mounted) - mp_text = mount_point + " " + _("(not mounted)"); - else + if (mount_point) { + mp_text = client.strip_mount_point_prefix(mount_point); + if (mp_text == false) + return null; + if (!mounted) + mp_text = mp_text + " " + _("(not mounted)"); + } else mp_text = _("(not mounted)"); return new_card({ diff --git a/pkg/storaged/filesystem/mounting-dialog.jsx b/pkg/storaged/filesystem/mounting-dialog.jsx index 6b01fb56180..b4663b27151 100644 --- a/pkg/storaged/filesystem/mounting-dialog.jsx +++ b/pkg/storaged/filesystem/mounting-dialog.jsx @@ -47,6 +47,10 @@ export function mounting_dialog(client, block, mode, forced_options) { const [old_config, old_dir, old_opts, old_parents] = get_fstab_config(block, true); const options = old_config ? old_opts : initial_tab_options(client, block, true); + const old_dir_for_display = client.strip_mount_point_prefix(old_dir); + if (old_dir_for_display === false) + return Promise.reject(_("This device can not be used for the installation target.")); + const split_options = parse_options(options); extract_option(split_options, "noauto"); const opt_never_auto = extract_option(split_options, "x-cockpit-never-auto"); @@ -198,8 +202,12 @@ export function mounting_dialog(client, block, mode, forced_options) { fields = [ TextInput("mount_point", _("Mount point"), { - value: old_dir, - validate: val => is_valid_mount_point(client, block, val, mode == "update" && !is_filesystem_mounted, true) + value: old_dir_for_display, + validate: val => is_valid_mount_point(client, + block, + client.add_mount_point_prefix(val), + mode == "update" && !is_filesystem_mounted, + true) }), CheckBoxes("mount_options", _("Mount options"), { @@ -292,7 +300,7 @@ export function mounting_dialog(client, block, mode, forced_options) { const usage = get_active_usage(client, block.path); const dlg = dialog_open({ - Title: cockpit.format(mode_title[mode], old_dir), + Title: cockpit.format(mode_title[mode], old_dir_for_display), Fields: fields, Teardown: TeardownMessage(usage, old_dir), update: function (dlg, vals, trigger) { @@ -321,8 +329,10 @@ export function mounting_dialog(client, block, mode, forced_options) { opts = opts.concat(forced_options); if (vals.mount_options.extra !== false) opts = opts.concat(parse_options(vals.mount_options.extra)); - return (maybe_update_config(vals.mount_point, unparse_options(opts), - vals.passphrase, passphrase_type) + return (maybe_update_config(client.add_mount_point_prefix(vals.mount_point), + unparse_options(opts), + vals.passphrase, + passphrase_type) .then(() => maybe_set_crypto_options(vals.mount_options.ro, opts.indexOf("noauto") == -1, vals.at_boot == "nofail", diff --git a/pkg/storaged/filesystem/utils.jsx b/pkg/storaged/filesystem/utils.jsx index 65fabfb75b9..1656410c0d7 100644 --- a/pkg/storaged/filesystem/utils.jsx +++ b/pkg/storaged/filesystem/utils.jsx @@ -90,7 +90,10 @@ export function is_valid_mount_point(client, block, val, format_only, for_fstab) if (Object.keys(children).length > 0) return <> {_("Filesystems are already mounted below this mountpoint.")} - {Object.keys(children).map(m =>
{cockpit.format("• $0 on $1", nice_block_name(children[m]), m)}
)} + {Object.keys(children).map(m =>
+ {cockpit.format("• $0 on $1", nice_block_name(children[m]), + client.strip_mount_point_prefix(m))} +
)} {_("Please unmount them first.")} ; } @@ -125,6 +128,7 @@ export const MountPoint = ({ fstab_config, forced_options, backing_block, conten let mount_point_text = null; if (old_dir) { + mount_point_text = client.strip_mount_point_prefix(old_dir); let opt_texts = []; if (opt_ro) opt_texts.push(_("read only")); @@ -138,9 +142,7 @@ export const MountPoint = ({ fstab_config, forced_options, backing_block, conten opt_texts.push(_("stop boot on failure")); opt_texts = opt_texts.concat(split_options); if (opt_texts.length) { - mount_point_text = cockpit.format("$0 ($1)", old_dir, opt_texts.join(", ")); - } else { - mount_point_text = old_dir; + mount_point_text = cockpit.format("$0 ($1)", mount_point_text, opt_texts.join(", ")); } } diff --git a/pkg/storaged/lvm2/volume-group.jsx b/pkg/storaged/lvm2/volume-group.jsx index 6396ffff837..cd9cd9f1fd7 100644 --- a/pkg/storaged/lvm2/volume-group.jsx +++ b/pkg/storaged/lvm2/volume-group.jsx @@ -221,6 +221,9 @@ export function make_lvm2_volume_group_page(parent, vgroup) { else if (vgroup.FreeSize == 0) lvol_excuse = _("No free space"); + if (client.should_ignore_device(vgroup.Name)) + return; + const pvols_card = new_card({ title: _("LVM2 physical volumes"), next: null, diff --git a/pkg/storaged/mdraid/mdraid.jsx b/pkg/storaged/mdraid/mdraid.jsx index 26efa334063..84df1689427 100644 --- a/pkg/storaged/mdraid/mdraid.jsx +++ b/pkg/storaged/mdraid/mdraid.jsx @@ -188,6 +188,12 @@ function missing_bitmap(mdraid) { export function make_mdraid_page(parent, mdraid) { const block = client.mdraids_block[mdraid.path]; + if (block && client.should_ignore_block(block)) + return; + + if (!block && client.in_anaconda_mode()) + return; + let add_excuse = false; if (!block) add_excuse = _("The MDRAID device must be running in order to add spare disks."); diff --git a/pkg/storaged/nfs/nfs.jsx b/pkg/storaged/nfs/nfs.jsx index 8e76f73fcca..6e0f227eb92 100644 --- a/pkg/storaged/nfs/nfs.jsx +++ b/pkg/storaged/nfs/nfs.jsx @@ -279,6 +279,9 @@ const NfsEntryUsageBar = ({ entry, not_mounted_text, short }) => { }; export function make_nfs_page(parent, entry) { + if (client.in_anaconda_mode()) + return; + const remote = entry.fields[0]; const local = entry.fields[1]; let mount_point = local; diff --git a/pkg/storaged/overview/overview.jsx b/pkg/storaged/overview/overview.jsx index e8922246081..d087c27ba59 100644 --- a/pkg/storaged/overview/overview.jsx +++ b/pkg/storaged/overview/overview.jsx @@ -146,7 +146,7 @@ const OverviewCard = ({ card, plot_state }) => { ].filter(item => item !== null); const net_menu_items = [ - menu_item(nfs_feature, _("New NFS mount"), () => nfs_fstab_dialog(null, null)), + !client.in_anaconda_mode() && 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 !== null); @@ -173,6 +173,7 @@ const OverviewCard = ({ card, plot_state }) => { return ( + { !client.in_anaconda_mode() && @@ -180,6 +181,7 @@ const OverviewCard = ({ card, plot_state }) => { + } @@ -189,8 +191,10 @@ const OverviewCard = ({ card, plot_state }) => { + { !client.in_anaconda_mode() && + } ); }; diff --git a/pkg/storaged/pages.jsx b/pkg/storaged/pages.jsx index 9d7a4c64c45..9c3facc27f3 100644 --- a/pkg/storaged/pages.jsx +++ b/pkg/storaged/pages.jsx @@ -42,6 +42,7 @@ import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/ind import { decode_filename, block_name, fmt_size } from "./utils.js"; import { StorageButton, StorageBarMenu, StorageMenuItem, StorageSize } from "./storage-controls.jsx"; import { MultipathAlert } from "./multipath.jsx"; +import { AnacondaAdvice } from "./anaconda.jsx"; import { JobsPanel } from "./jobs-panel.jsx"; const _ = cockpit.gettext; @@ -681,6 +682,7 @@ export const StoragePage = ({ location, plot_state }) => { + diff --git a/pkg/storaged/stratis/filesystem.jsx b/pkg/storaged/stratis/filesystem.jsx index e8c414853a2..9ca376bc872 100644 --- a/pkg/storaged/stratis/filesystem.jsx +++ b/pkg/storaged/stratis/filesystem.jsx @@ -19,7 +19,7 @@ import cockpit from "cockpit"; import React from "react"; -import client from "../client"; +import client from "../client.js"; import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js"; import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js"; @@ -91,7 +91,10 @@ export function make_stratis_filesystem_page(parent, pool, fsys, TextInput("mount_point", _("Mount point"), { validate: (val, values, variant) => { - return is_valid_mount_point(client, null, val, variant == "nomount"); + return is_valid_mount_point(client, + null, + client.add_mount_point_prefix(val), + variant == "nomount"); } }), CheckBoxes("mount_options", _("Mount options"), @@ -181,11 +184,13 @@ export function make_stratis_filesystem_page(parent, pool, fsys, } let mp_text; - if (mount_point && fs_is_mounted) - mp_text = mount_point; - else if (mount_point && !fs_is_mounted) - mp_text = mount_point + " " + _("(not mounted)"); - else + if (mount_point) { + mp_text = client.strip_mount_point_prefix(mount_point); + if (mp_text == false) + return; + if (!fs_is_mounted) + mp_text = mp_text + " " + _("(not mounted)"); + } else mp_text = _("(not mounted)"); const fsys_card = new_card({ diff --git a/pkg/storaged/stratis/pool.jsx b/pkg/storaged/stratis/pool.jsx index 2ea48db7c52..930151e4ac7 100644 --- a/pkg/storaged/stratis/pool.jsx +++ b/pkg/storaged/stratis/pool.jsx @@ -89,7 +89,10 @@ function create_fs(pool) { TextInput("mount_point", _("Mount point"), { validate: (val, values, variant) => { - return is_valid_mount_point(client, null, val, variant == "nomount"); + return is_valid_mount_point(client, + null, + client.add_mount_point_prefix(val), + variant == "nomount"); } }), CheckBoxes("mount_options", _("Mount options"), @@ -105,7 +108,7 @@ function create_fs(pool) { }), SelectOne("at_boot", _("At boot"), { - value: "nofail", + value: client.in_anaconda_mode() ? "local" : "nofail", explanation: mount_explanation.nofail, choices: [ { @@ -283,6 +286,9 @@ export function make_stratis_pool_page(parent, pool) { const use = pool.TotalPhysicalUsed[0] && [Number(pool.TotalPhysicalUsed[1]), Number(pool.TotalPhysicalSize)]; + if (client.should_ignore_device(pool.Name)) + return; + const blockdevs_card = new_card({ title: _("Stratis block devices"), next: null, diff --git a/pkg/storaged/stratis/stopped-pool.jsx b/pkg/storaged/stratis/stopped-pool.jsx index 38bfc19f8ce..d803e1c6b17 100644 --- a/pkg/storaged/stratis/stopped-pool.jsx +++ b/pkg/storaged/stratis/stopped-pool.jsx @@ -86,6 +86,9 @@ function start_pool(uuid, show_devs) { } export function make_stratis_stopped_pool_page(parent, uuid) { + if (client.in_anaconda_mode()) + return; + const blockdevs_card = new_card({ title: _("Stratis block devices"), next: null, diff --git a/pkg/storaged/stratis/utils.jsx b/pkg/storaged/stratis/utils.jsx index 7fd76b0ebe7..f19e0742e2c 100644 --- a/pkg/storaged/stratis/utils.jsx +++ b/pkg/storaged/stratis/utils.jsx @@ -148,6 +148,7 @@ export function set_mount_options(path, vals, forced_options) { return Promise.resolve(); if (mount_point[0] != "/") mount_point = "/" + mount_point; + mount_point = client.add_mount_point_prefix(mount_point); const config = ["fstab", diff --git a/pkg/storaged/utils.js b/pkg/storaged/utils.js index 829d56bfd14..5da3fbf1fef 100644 --- a/pkg/storaged/utils.js +++ b/pkg/storaged/utils.js @@ -496,7 +496,8 @@ export function is_available_block(client, block, honor_ignore_hint) { !is_vdo_backing_dev() && !is_swap() && !block_ptable && - !(block_part && block_part.IsContainer)); + !(block_part && block_part.IsContainer) && + !client.should_ignore_device(decode_filename(block.PreferredDevice))); } export function get_available_spaces(client) { @@ -569,7 +570,8 @@ export function get_other_devices(client) { block.Size > 0 && !client.legacy_vdo_overlay.find_by_block(block) && !client.blocks_stratis_fsys[block.path] && - !is_snap(client, block)); + !is_snap(client, block) && + !client.should_ignore_block(block)); }); } @@ -834,7 +836,7 @@ export function get_active_usage(client, path, top_action, child_action, is_temp has_fstab_entry, set_noauto: !is_top && !is_temporary, actions: (is_top ? get_actions(_("unmount")) : [_("unmount")]).concat(has_fstab_entry ? [_("mount")] : []), - blocking: false + blocking: client.strip_mount_point_prefix(location) === false, }); }