LCOV - code coverage report
Current view: top level - pkg/storaged/stratis - stopped-pool.jsx Coverage Total Hit
Test: cockpit Lines: 51.9 % 129 67
Test Date: 2026-07-17 07:33:01

            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 { CardHeader, 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              : import { List, ListItem } from "@patternfly/react-core/dist/esm/components/List/index.js";
      13              : 
      14              : import { VolumeIcon } from "../icons/gnome-icons.jsx";
      15              : import {
      16              :     StorageCard, StorageDescription, PageTable,
      17              :     new_page, new_card, PAGE_CATEGORY_VIRTUAL,
      18              :     get_crossrefs
      19              : } from "../pages.jsx";
      20              : import { dialog_open, PassInput } from "../dialog.jsx";
      21              : import { std_reply, with_stored_passphrase } from "./utils.jsx";
      22              : import { get_stored_keydescs } from "./pool";
      23              : 
      24              : import * as python from "python.js";
      25              : import stratis3_start_pool_py from "./stratis3-start-pool.py";
      26              : 
      27          113 : const _ = cockpit.gettext;
      28              : 
      29              : /* Starting a pool has to deal with a number of possibilities:
      30              : 
      31              :    API REVISION:
      32              : 
      33              :    r6: Can only start V1 pools, takes unlock method as argument and
      34              :        passphrase in keyring.
      35              : 
      36              :    r8: Can start both V1 and V2 pools, takes passphrase via FD,
      37              :        unlock method determined by whether or not there is a
      38              :        FD.
      39              : 
      40              :    METADATA FORMAT:
      41              : 
      42              :    V1: Can have at most one passphrase and/or at most one clevis info,
      43              :        which are communicated in the StoppedPools manager property of both
      44              :        the r6 and r8 API.
      45              : 
      46              :    V2: Can have zero or more passphrases and clevis infos, a summary of which is
      47              :        communicated in the StoppedPools property.
      48              :  */
      49              : 
      50            2 : function is_v1_pool(uuid) {
      51            2 :     if (client.stratis_interface_revision < 8)
      52            0 :         return true;
      53              : 
      54            2 :     const stopped_info = client.stratis_manager.StoppedPools[uuid];
      55            2 :     return !stopped_info.metadata_version.v[0] || stopped_info.metadata_version.v[1] == 1;
      56            2 : }
      57              : 
      58            2 : function stratis_r8_manager_start_pool(uuid, slot, passphrase) {
      59            2 :     const p = python.spawn(
      60            2 :         stratis3_start_pool_py,
      61            0 :         [uuid, slot, passphrase ? 0 : "-"],
      62            2 :         { superuser: "require" });
      63            2 :     if (passphrase)
      64            0 :         p.input(passphrase);
      65            2 :     return p;
      66            2 : }
      67              : 
      68            0 : function get_v1_keydesc(uuid) {
      69            0 :     const stopped_info = client.stratis_manager.StoppedPools[uuid];
      70            0 :     const kd = stopped_info.key_description;
      71            0 :     return kd.v[1].v[1];
      72            0 : }
      73              : 
      74            2 : async function manager_start_pool(uuid, unlock_method, passphrase) {
      75            0 :     if (client.stratis_interface_revision < 8) {
      76            0 :         const start = () => {
      77            0 :             return client.stratis_manager.StartPool(
      78            0 :                 uuid, "uuid",
      79            0 :                 unlock_method ? [true, unlock_method] : [false, ""])
      80            0 :                     .then(std_reply);
      81            0 :         };
      82            0 :         if (passphrase) {
      83            0 :             const keydesc = get_v1_keydesc(uuid);
      84            0 :             return with_stored_passphrase(client, keydesc, passphrase, start);
      85            0 :         } else {
      86            0 :             return start();
      87            0 :         }
      88            0 :     } else {
      89            0 :         return await stratis_r8_manager_start_pool(uuid, unlock_method ? "any" : "-", passphrase);
      90            2 :     }
      91            2 : }
      92              : 
      93            2 : function start_pool(uuid, show_devs) {
      94            2 :     const stopped_info = client.stratis_manager.StoppedPools[uuid];
      95            2 :     const devs = client.stratis_stopped_pool_devnodes(uuid);
      96              : 
      97            0 :     async function prompt_for_passphrase() {
      98            0 :         if (client.stratis_interface_revision < 8) {
      99            0 :             const key_desc = get_v1_keydesc(uuid);
     100            0 :             const stored_keydescs = await get_stored_keydescs();
     101            0 :             if (stored_keydescs.includes(key_desc))
     102            0 :                 return await client.stratis_manager.StartPool(uuid, "uuid", [true, "keyring"]).then(std_reply);
     103            0 :         }
     104              : 
     105            0 :         dialog_open({
     106            0 :             Title: _("Unlock encrypted Stratis pool"),
     107            0 :             Body: (show_devs &&
     108            0 :             <>
     109            0 :                 <p>{_("Provide the passphrase for the pool on these block devices:")}</p>
     110            0 :                 <List>{devs.map(d => <ListItem key={d}>{d}</ListItem>)}</List>
     111            0 :                 <br />
     112            0 :             </>),
     113            0 :             Fields: [
     114            0 :                 PassInput("passphrase", _("Passphrase"), { })
     115            0 :             ],
     116            0 :             Action: {
     117            0 :                 Title: _("Unlock"),
     118            0 :                 action: function(vals) {
     119            0 :                     return manager_start_pool(uuid, "keyring", vals.passphrase);
     120            0 :                 }
     121            0 :             }
     122            0 :         });
     123            0 :     }
     124              : 
     125            2 :     let have_passphrase;
     126            2 :     let have_clevis;
     127              : 
     128            1 :     if (is_v1_pool(uuid)) {
     129            1 :         const kd = stopped_info.key_description;
     130            0 :         have_passphrase = kd && kd.v[0] && kd.v[1].v[0];
     131            1 :         const ci = stopped_info.clevis_info;
     132            0 :         have_clevis = ci && ci.v[0] && ci.v[1].v[0];
     133            0 :     } else {
     134            0 :         const features = stopped_info.features.v[0] ? stopped_info.features.v[1] : { };
     135            1 :         const encrypted = features.encryption;
     136            1 :         have_passphrase = features.key_description_present;
     137            1 :         have_clevis = features.clevis_present;
     138              : 
     139              :         // stratisd 3.8.0 never sets the "key_description_present"
     140              :         // or "clevis_present" flags, so we have to try everything
     141              :         // when the pool is encrypted. (stratisd 3.8.1 and younger
     142              :         // will set at least one of them for encrypted pools.)
     143              :         //
     144            0 :         if (encrypted && !have_passphrase && !have_clevis)
     145            0 :             have_passphrase = have_clevis = true;
     146            1 :     }
     147              : 
     148            2 :     if (!have_passphrase && !have_clevis) {
     149              :         // Not an encrypted pool, just start it
     150            2 :         return manager_start_pool(uuid, null, null);
     151            0 :     } else if (have_passphrase && have_clevis) {
     152            0 :         return manager_start_pool(uuid, "clevis", null).catch(prompt_for_passphrase);
     153            0 :     } else if (!have_passphrase && have_clevis) {
     154            0 :         return manager_start_pool(uuid, "clevis", null);
     155            0 :     } else if (have_passphrase && !have_clevis) {
     156            0 :         return prompt_for_passphrase();
     157            0 :     }
     158            2 : }
     159              : 
     160            9 : export function make_stratis_stopped_pool_page(parent, uuid) {
     161            9 :     if (client.in_anaconda_mode())
     162            9 :         return;
     163              : 
     164            9 :     const pool_card = new_card({
     165            9 :         title: _("Stratis pool"),
     166            9 :         type_extra: _("stopped"),
     167            9 :         next: null,
     168            9 :         page_location: ["pool", uuid],
     169            2 :         page_name: client.stratis_manager.StoppedPools[uuid]?.name?.v || uuid,
     170            9 :         page_icon: VolumeIcon,
     171            9 :         page_category: PAGE_CATEGORY_VIRTUAL,
     172            9 :         component: StoppedStratisPoolCard,
     173            9 :         props: { uuid },
     174            9 :         actions: [
     175            2 :             { title: _("Start"), action: from_menu => start_pool(uuid, from_menu), spinner: true },
     176            9 :         ],
     177            9 :     });
     178              : 
     179            9 :     new_page(parent, pool_card);
     180            9 : }
     181              : 
     182            5 : const StoppedStratisPoolCard = ({ card, uuid }) => {
     183            5 :     return (
     184            5 :         <StorageCard card={card}>
     185            5 :             <CardBody>
     186            5 :                 <DescriptionList className="pf-m-horizontal-on-sm">
     187            5 :                     <StorageDescription title={_("UUID")} value={uuid} />
     188            5 :                 </DescriptionList>
     189            5 :             </CardBody>
     190            5 :             <CardHeader><strong>{_("Block devices")}</strong></CardHeader>
     191            5 :             <PageTable
     192            5 :                 emptyCaption={_("No block devices found")}
     193            5 :                 aria-label={_("Stratis block devices")}
     194            5 :                 crossrefs={get_crossrefs(uuid)} />
     195            5 :         </StorageCard>
     196              :     );
     197            5 : };
        

Generated by: LCOV version 2.0-1