LCOV - code coverage report
Current view: top level - pkg/storaged/stratis - create-dialog.jsx Coverage Total Hit
Test: cockpit Lines: 86.7 % 165 143
Test Date: 2026-08-04 16:34:20

            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              : import client from "../client.js";
       8              : 
       9              : import { dialog_open, TextInput, CheckBoxes, PassInput, SelectSpaces } from "../dialog.jsx";
      10              : import { decode_filename, get_available_spaces, prepare_available_spaces } from "../utils.js";
      11              : 
      12              : import { validate_pool_name, std_reply, get_unused_keydesc, with_stored_passphrase, confirm_tang_trust } from "./utils.jsx";
      13              : import { remember_passphrase } from "../anaconda.jsx";
      14              : import { validate_url, get_tang_adv } from "../crypto/tang.jsx";
      15              : 
      16          113 : const _ = cockpit.gettext;
      17              : 
      18            8 : function manager_create_pool(name, devs, key_desc, clevis_info) {
      19              :     // "clevis_info" is either falsy or an array with two elements:
      20              :     //
      21              :     //   [ pin, pin_config ]
      22              :     //
      23              :     // This array can be used directly with the (b(ss)) signature of
      24              :     // the r6 CreatePool call, but needs to be taken apart for the
      25              :     // a((bu)ss) signature of the r8 CreatePool call.
      26              : 
      27            0 :     if (client.stratis_interface_revision < 8) {
      28            0 :         let key_desc_arg = [false, ""];
      29            0 :         if (key_desc)
      30            0 :             key_desc_arg = [true, key_desc];
      31            0 :         let clevis_info_arg = [false, ["", ""]];
      32            0 :         if (clevis_info)
      33            0 :             clevis_info_arg = [true, clevis_info];
      34            0 :         return client.stratis_manager.CreatePool(name,
      35            0 :                                                  devs,
      36            0 :                                                  key_desc_arg,
      37            0 :                                                  clevis_info_arg);
      38            0 :     } else {
      39            8 :         const any_slot = [false, 0];
      40            8 :         const key_descs_arg = [];
      41            8 :         if (key_desc)
      42            2 :             key_descs_arg.push([any_slot, key_desc]);
      43            8 :         const clevis_infos_arg = [];
      44            8 :         if (clevis_info)
      45            0 :             clevis_infos_arg.push([any_slot, clevis_info[0], clevis_info[1]]);
      46            8 :         return client.stratis_manager.CreatePool(name,
      47            8 :                                                  devs,
      48            8 :                                                  key_descs_arg,
      49            8 :                                                  clevis_infos_arg,
      50            8 :                                                  [false, 0], // journal_size
      51            8 :                                                  [false, ""], // tag_spec
      52            8 :                                                  [false, false]); // allocate_superblock
      53            8 :     }
      54            8 : }
      55              : 
      56            9 : export function create_stratis_pool() {
      57            9 :     function find_pool(name) {
      58            2 :         for (const p in client.stratis_pools) {
      59            2 :             if (client.stratis_pools[p].Name == name)
      60            2 :                 return client.stratis_pools[p];
      61            2 :         }
      62            9 :         return null;
      63            9 :     }
      64              : 
      65            9 :     let name;
      66            9 :     for (let i = 0; i < 1000; i++) {
      67            9 :         name = "pool" + i.toFixed();
      68            9 :         if (!find_pool(name))
      69            9 :             break;
      70            9 :     }
      71              : 
      72            9 :     dialog_open({
      73            9 :         Title: _("Create Stratis pool"),
      74            9 :         Fields: [
      75            9 :             TextInput("name", _("Name"),
      76            9 :                       {
      77            9 :                           value: name,
      78            8 :                           validate: name => validate_pool_name(null, name)
      79            9 :                       }),
      80            9 :             SelectSpaces("disks", _("Block devices"),
      81            9 :                          {
      82            9 :                              empty_warning: _("No block devices are available."),
      83            8 :                              validate: function (disks) {
      84            8 :                                  if (disks.length === 0)
      85            0 :                                      return _("At least one block device is needed.");
      86            8 :                              },
      87            9 :                              spaces: get_available_spaces()
      88            9 :                          }),
      89            9 :             CheckBoxes("encrypt_pass", _("Options"),
      90            9 :                        {
      91            9 :                            fields: [
      92            9 :                                {
      93            9 :                                    tag: "on",
      94            9 :                                    title: _("Encrypt data with a passphrase"),
      95            9 :                                }
      96            9 :                            ],
      97            9 :                            nested_fields: [
      98            9 :                                PassInput("passphrase", _("Passphrase"),
      99            9 :                                          {
     100            2 :                                              validate: function (phrase) {
     101            2 :                                                  if (phrase === "")
     102            0 :                                                      return _("Passphrase cannot be empty");
     103            2 :                                              },
     104            9 :                                              visible: vals => vals.encrypt_pass.on,
     105            9 :                                              new_password: true
     106            9 :                                          }),
     107            9 :                                PassInput("passphrase2", _("Confirm"),
     108            9 :                                          {
     109            2 :                                              validate: function (phrase2, vals) {
     110            2 :                                                  if (phrase2 != vals.passphrase)
     111            0 :                                                      return _("Passphrases do not match");
     112            2 :                                              },
     113            9 :                                              visible: vals => vals.encrypt_pass.on,
     114            9 :                                              new_password: true
     115            9 :                                          })
     116            9 :                            ]
     117            9 :                        }),
     118            9 :             CheckBoxes("encrypt_tang", "",
     119            9 :                        {
     120            9 :                            fields: [
     121            9 :                                { tag: "on", title: _("Encrypt data with a Tang keyserver") }
     122            9 :                            ],
     123            9 :                            nested_fields: [
     124            9 :                                TextInput("tang_url", _("Keyserver address"),
     125            9 :                                          {
     126            9 :                                              validate: validate_url,
     127            9 :                                              visible: vals => vals.encrypt_tang && vals.encrypt_tang.on
     128            9 :                                          }),
     129            9 :                            ]
     130            9 :                        }),
     131            9 :             CheckBoxes("overprov", "",
     132            9 :                        {
     133            9 :                            value: { on: true },
     134            9 :                            fields: [
     135            9 :                                {
     136            9 :                                    tag: "on",
     137            9 :                                    title: _("Overprovisioning"),
     138            9 :                                }
     139            9 :                            ]
     140            9 :                        })
     141            9 :         ],
     142            9 :         Action: {
     143            9 :             Title: _("Create"),
     144            8 :             action: function (vals) {
     145            8 :                 return prepare_available_spaces(client, vals.disks).then(function (paths) {
     146            8 :                     const devs = paths.map(p => decode_filename(client.blocks[p].PreferredDevice));
     147              : 
     148            8 :                     function create(key_desc, adv) {
     149            8 :                         let clevis_info = null;
     150            8 :                         if (adv)
     151            0 :                             clevis_info = ["tang", JSON.stringify({ url: vals.tang_url, adv })];
     152            8 :                         return manager_create_pool(vals.name, devs, key_desc, clevis_info)
     153            8 :                                 .then(std_reply)
     154            8 :                                 .then(result => {
     155            2 :                                     if (key_desc && vals.passphrase) {
     156            2 :                                         for (const p of paths) {
     157            2 :                                             const block = client.blocks[p];
     158            2 :                                             if (block)
     159            2 :                                                 remember_passphrase(block, vals.passphrase);
     160            2 :                                         }
     161            2 :                                     }
     162            1 :                                     if (vals.overprov && !vals.overprov.on && result[0]) {
     163            1 :                                         const path = result[1][0];
     164            1 :                                         return client.wait_for(() => client.stratis_pools[path])
     165            1 :                                                 .then(pool => {
     166            1 :                                                     return client.stratis_set_property(pool,
     167            1 :                                                                                        "Overprovisioning",
     168            1 :                                                                                        "b", false);
     169            1 :                                                 });
     170            1 :                                     }
     171            8 :                                 });
     172            8 :                     }
     173              : 
     174            8 :                     function create2(adv) {
     175            2 :                         if (vals.encrypt_pass.on) {
     176            2 :                             return get_unused_keydesc(client, vals.name)
     177            2 :                                     .then(keydesc => with_stored_passphrase(client, keydesc, vals.passphrase,
     178            2 :                                                                             () => create(keydesc, adv)));
     179            0 :                         } else {
     180            6 :                             return create(false, adv);
     181            6 :                         }
     182            8 :                     }
     183              : 
     184            0 :                     if (vals.encrypt_tang && vals.encrypt_tang.on) {
     185            0 :                         return get_tang_adv(vals.tang_url)
     186            0 :                                 .then(adv => confirm_tang_trust(vals.tang_url, adv, () => create2(adv)));
     187            0 :                     } else {
     188            8 :                         return create2(false);
     189            8 :                     }
     190            8 :                 });
     191            8 :             }
     192            9 :         }
     193            9 :     });
     194            9 : }
        

Generated by: LCOV version 2.0-1