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