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 : }
|