LCOV - code coverage report
Current view: top level - pkg/storaged/stratis - filesystem.jsx Coverage Total Hit
Test: cockpit Lines: 94.8 % 211 200
Test Date: 2026-06-25 09:20:42

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2023 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6          113 : import cockpit from "cockpit";
       7          113 : import React from "react";
       8              : import client from "../client";
       9              : 
      10              : import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js";
      11              : import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js";
      12              : 
      13              : import {
      14              :     dialog_open, TextInput, CheckBoxes, SizeSlider, BlockingMessage, TeardownMessage,
      15              :     init_teardown_usage,
      16              : } from "../dialog.jsx";
      17              : import { StorageUsageBar, StorageLink } from "../storage-controls.jsx";
      18              : import {
      19              :     StorageCard, StorageDescription,
      20              :     new_page, new_card,
      21              :     navigate_away_from_card, navigate_to_new_card_location,
      22              : } from "../pages.jsx";
      23              : import {
      24              :     MountPoint, edit_mount_point,
      25              :     is_valid_mount_point, is_mounted,
      26              :     get_fstab_config, mount_point_text,
      27              : } from "../filesystem/utils.jsx";
      28              : import { MismountAlert, check_mismounted_fsys } from "../filesystem/mismounting.jsx";
      29              : import { mounting_dialog, at_boot_input, update_at_boot_input, mount_options } from "../filesystem/mounting-dialog.jsx";
      30              : import { fmt_size, get_active_usage, teardown_active_usage } from "../utils.js";
      31              : import { std_reply, validate_fs_name, set_mount_options, destroy_filesystem } from "./utils.jsx";
      32              : 
      33          113 : const _ = cockpit.gettext;
      34              : 
      35           11 : export function make_stratis_filesystem_page(parent, pool, fsys,
      36           11 :     offset, forced_options) {
      37           11 :     const filesystems = client.stratis_pool_filesystems[pool.path];
      38           11 :     const stats = client.stratis_pool_stats[pool.path];
      39           11 :     const block = client.slashdevs_block[fsys.Devnode];
      40              : 
      41           11 :     if (!block)
      42           11 :         return;
      43              : 
      44           11 :     const fstab_config = get_fstab_config(block);
      45           11 :     const [, mount_point] = fstab_config;
      46           11 :     const fs_is_mounted = is_mounted(client, block);
      47              : 
      48           11 :     const mismount_warning = check_mismounted_fsys(block, block, fstab_config);
      49              : 
      50            2 :     function mount() {
      51            2 :         return mounting_dialog(client, block, "mount", forced_options);
      52            2 :     }
      53              : 
      54            3 :     function unmount() {
      55            3 :         return mounting_dialog(client, block, "unmount", forced_options);
      56            3 :     }
      57              : 
      58            4 :     function snapshot_fsys() {
      59            4 :         dialog_open({
      60            4 :             Title: cockpit.format(_("Create a snapshot of filesystem $0"), fsys.Name),
      61            4 :             Fields: [
      62            4 :                 TextInput("name", _("Name"),
      63            4 :                           {
      64            4 :                               value: "",
      65            4 :                               validate: name => validate_fs_name(null, name, filesystems)
      66            4 :                           }),
      67            4 :                 TextInput("mount_point", _("Mount point"),
      68            4 :                           {
      69            4 :                               validate: (val, values, variant) => {
      70            4 :                                   return is_valid_mount_point(client,
      71            4 :                                                               null,
      72            4 :                                                               client.add_mount_point_prefix(val),
      73            4 :                                                               variant == "nomount");
      74            4 :                               }
      75            4 :                           }),
      76            4 :                 mount_options(false, false),
      77            4 :                 at_boot_input(),
      78            4 :             ],
      79            4 :             update: update_at_boot_input,
      80            4 :             Action: {
      81            4 :                 Title: _("Create snapshot and mount"),
      82            4 :                 Variants: [{ tag: "nomount", Title: _("Create snapshot only") }],
      83            4 :                 action: function (vals) {
      84            4 :                     return pool.SnapshotFilesystem(fsys.path, vals.name)
      85            4 :                             .then(std_reply)
      86            4 :                             .then(result => {
      87            4 :                                 if (result[0])
      88            0 :                                     return set_mount_options(result[1], vals, forced_options);
      89              :                                 else
      90            0 :                                     return Promise.resolve();
      91            4 :                             });
      92            4 :                 }
      93            4 :             }
      94            4 :         });
      95            4 :     }
      96              : 
      97            5 :     function delete_fsys() {
      98            5 :         const usage = get_active_usage(client, block.path, _("delete"));
      99              : 
     100            0 :         if (usage.Blocking) {
     101            0 :             dialog_open({
     102            0 :                 Title: cockpit.format(_("$0 is in use"),
     103            0 :                                       fsys.Name),
     104            0 :                 Body: BlockingMessage(usage)
     105            0 :             });
     106            0 :             return;
     107            0 :         }
     108              : 
     109            5 :         dialog_open({
     110            5 :             Title: cockpit.format(_("Confirm deletion of $0"), fsys.Name),
     111            5 :             Teardown: TeardownMessage(usage),
     112            5 :             Action: {
     113            5 :                 Danger: _("Deleting a filesystem will delete all data in it."),
     114            5 :                 Title: _("Delete"),
     115            5 :                 action: async function () {
     116            5 :                     await teardown_active_usage(client, usage);
     117            5 :                     await destroy_filesystem(fsys);
     118            5 :                     navigate_away_from_card(fsys_card);
     119            5 :                 }
     120            5 :             },
     121            5 :             Inits: [
     122            5 :                 init_teardown_usage(client, usage)
     123            5 :             ]
     124            5 :         });
     125            5 :     }
     126              : 
     127           11 :     const mp_text = mount_point_text(mount_point, fs_is_mounted);
     128           11 :     if (mp_text == null)
     129            2 :         return null;
     130              : 
     131           11 :     const fsys_card = new_card({
     132           11 :         title: _("Stratis filesystem"),
     133           11 :         location: mp_text,
     134           11 :         next: null,
     135           11 :         page_location: ["pool", pool.Name, fsys.Name],
     136           11 :         page_name: fsys.Name,
     137           11 :         page_size: <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), stats.pool_total]}
     138           11 :                                     critical={1} total={stats.fsys_total_used} offset={offset} short />,
     139           11 :         has_warning: !!mismount_warning,
     140           11 :         component: StratisFilesystemCard,
     141           11 :         props: { pool, fsys, fstab_config, forced_options, mismount_warning, offset },
     142           11 :         actions: [
     143           11 :             client.in_anaconda_mode() &&
     144            1 :                 { title: _("Edit mount point"), action: () => edit_mount_point(block, forced_options) },
     145           11 :             (fs_is_mounted
     146            7 :                 ? { title: _("Unmount"), action: unmount }
     147           11 :                 : null),
     148           11 :             (!fs_is_mounted && !client.in_anaconda_mode()
     149           10 :                 ? { title: _("Mount"), action: mount }
     150            7 :                 : null),
     151           11 :             {
     152           11 :                 title: _("Snapshot"),
     153           11 :                 action: snapshot_fsys,
     154            4 :                 excuse: ((!pool.Overprovisioning && stats.pool_free < Number(fsys.Size))
     155            4 :                     ? _("Not enough free space")
     156           11 :                     : null),
     157           11 :             },
     158           11 :             { title: _("Delete"), action: delete_fsys, danger: true },
     159           11 :         ]
     160           11 :     });
     161              : 
     162           11 :     new_page(parent, fsys_card);
     163           11 : }
     164              : 
     165            7 : const StratisFilesystemCard = ({
     166            7 :     card, pool, fsys, fstab_config, forced_options, mismount_warning, offset,
     167            7 : }) => {
     168            7 :     const filesystems = client.stratis_pool_filesystems[pool.path];
     169            7 :     const stats = client.stratis_pool_stats[pool.path];
     170            7 :     const block = client.slashdevs_block[fsys.Devnode];
     171              : 
     172            2 :     function rename_fsys() {
     173            2 :         dialog_open({
     174            2 :             Title: _("Rename filesystem"),
     175            2 :             Fields: [
     176            2 :                 TextInput("name", _("Name"),
     177            2 :                           {
     178            2 :                               value: fsys.Name,
     179            2 :                               validate: name => validate_fs_name(fsys, name, filesystems)
     180            2 :                           })
     181            2 :             ],
     182            2 :             Action: {
     183            2 :                 Title: _("Rename"),
     184            2 :                 action: async function (vals) {
     185            2 :                     await fsys.SetName(vals.name).then(std_reply);
     186            2 :                     navigate_to_new_card_location(card, ["pool", pool.Name, vals.name]);
     187            2 :                 }
     188            2 :             }
     189            2 :         });
     190            2 :     }
     191              : 
     192            2 :     function set_limit() {
     193            2 :         dialog_open({
     194            2 :             Title: _("Set limit of virtual filesystem size"),
     195            2 :             Fields: [
     196            2 :                 CheckBoxes("size_options", _("Options"),
     197            2 :                            {
     198            2 :                                value: {
     199            2 :                                    custom_limit: fsys.SizeLimit[0],
     200            2 :                                },
     201            2 :                                fields: [
     202            2 :                                    { tag: "custom_limit", title: _("Limit virtual filesystem size") },
     203            2 :                                ]
     204            2 :                            }),
     205            2 :                 SizeSlider("limit", _("Virtual size limit"),
     206            2 :                            {
     207            2 :                                visible: vals => vals.size_options.custom_limit,
     208            2 :                                value: fsys.SizeLimit[0] && Number(fsys.SizeLimit[1]),
     209            2 :                                min: Number(fsys.Size),
     210            0 :                                max: pool.Overprovisioning ? stats.pool_total : stats.pool_free + Number(fsys.Size),
     211            2 :                                allow_infinite: true,
     212            2 :                                round: 512
     213            2 :                            }),
     214            2 :             ],
     215            2 :             Action: {
     216            2 :                 Title: _("Set"),
     217            2 :                 action: async function (vals) {
     218            2 :                     await client.stratis_set_property(fsys,
     219            2 :                                                       "SizeLimit",
     220            2 :                                                       "(bs)", (vals.size_options.custom_limit
     221            2 :                                                           ? [true, vals.limit.toString()]
     222            2 :                                                           : [false, ""]));
     223            2 :                 }
     224            2 :             }
     225            2 :         });
     226            2 :     }
     227              : 
     228            7 :     return (
     229            7 :         <StorageCard card={card}
     230            7 :                      alert={mismount_warning &&
     231            1 :                      <MismountAlert warning={mismount_warning}
     232            1 :                                            fstab_config={fstab_config} forced_options={forced_options}
     233            1 :                                            backing_block={block} content_block={block} />}>
     234            7 :             <CardBody>
     235            7 :                 <DescriptionList className="pf-m-horizontal-on-sm">
     236            7 :                     <StorageDescription title={_("Name")}
     237            7 :                                    value={fsys.Name}
     238            7 :                            action={<StorageLink onClick={rename_fsys}>
     239            7 :                                {_("edit")}
     240            7 :                            </StorageLink>} />
     241            7 :                     <StorageDescription title={_("Mount point")}>
     242            7 :                         <MountPoint fstab_config={fstab_config} forced_options={forced_options}
     243            7 :                                     backing_block={block} content_block={block} />
     244            7 :                     </StorageDescription>
     245            7 :                     <StorageDescription title={_("Usage")}>
     246            7 :                         <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), stats.pool_total]}
     247            7 :                                              critical={1} total={stats.fsys_total_used} offset={offset} />
     248            7 :                     </StorageDescription>
     249            7 :                     <StorageDescription title={_("Virtual size")}
     250            7 :                                         value={fmt_size(Number(fsys.Size))} />
     251            7 :                     <StorageDescription title={_("Virtual size limit")}
     252            2 :                                         value={fsys.SizeLimit[0] ? fmt_size(Number(fsys.SizeLimit[1])) : _("none")}
     253            7 :                                         action={<StorageLink onClick={set_limit}>
     254            7 :                                             {_("edit")}
     255            7 :                                         </StorageLink>} />
     256            7 :                 </DescriptionList>
     257            7 :             </CardBody>
     258            7 :         </StorageCard>
     259              :     );
     260            7 : };
        

Generated by: LCOV version 2.0-1