Skip to content

Commit 6c219f5

Browse files
mvollmerVenefilyn
authored andcommitted
storage: Collect "available spaces" during page creation
Currently, get_available_spaces implements its own logic for finding empty block devices and unused space in partition tables. This should match the logic used during creation of the page tree, and currently needs to be done with careful coding. Instead, let's collect the available spaces as a side effect of creating the page tree. This has made two edge more consistent with the rule that only empty block devices (aka "Unformatted data") should be offered as available: - LVM2 physical volumes that are not associated with any known volume group are no longer considered "available" since they are not unformatted. - Devices that can be accessed via multiple pathes but that don't have a "mpath" device to manage them, might now be offered as available via one path.
1 parent c9d0b95 commit 6c219f5

File tree

15 files changed

+57
-116
lines changed

15 files changed

+57
-116
lines changed

pkg/storaged/block/create-pages.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ export function make_block_page(parent, block, card) {
113113
} else if (block_swap ||
114114
(content_block.IdUsage == "other" && content_block.IdType == "swap")) {
115115
card = make_swap_card(card, block, content_block);
116-
} else if (client.blocks_available[content_block.path]) {
116+
} else if (!content_block.IdUsage) {
117+
if (!block.HintIgnore)
118+
register_available_block_space(client, content_block);
117119
card = make_unformatted_data_card(card, block, content_block);
118120
} else {
119121
card = make_unrecognized_data_card(card, block, content_block);

pkg/storaged/block/resize.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ function lvol_or_part_and_fsys_resize(client, lvol_or_part, size, offline, passp
188188
return Promise.reject(_("Stratis blockdevs can not be made smaller")); // not-covered: safety check
189189
else
190190
return Promise.resolve();
191-
} else if (client.blocks_available[block.path]) {
191+
} else if (!block.IdUsage) {
192192
// Growing or shrinking unformatted data, nothing to do
193193
return Promise.resolve();
194194
} else if (size < orig_size) {
@@ -293,7 +293,7 @@ export function get_resize_info(client, block, to_fit) {
293293
can_grow: false,
294294
};
295295
shrink_excuse = grow_excuse = _("Swap can not be resized here");
296-
} else if (client.blocks_available[block.path]) {
296+
} else if (!block.IdUsage) {
297297
info = {
298298
can_shrink: true,
299299
can_grow: true,

pkg/storaged/client.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -825,13 +825,6 @@ function update_indices() {
825825
}
826826
}
827827

828-
client.blocks_available = { };
829-
for (path in client.blocks) {
830-
block = client.blocks[path];
831-
if (utils.is_available_block(client, block))
832-
client.blocks_available[path] = true;
833-
}
834-
835828
client.path_jobs = { };
836829
function enter_job(job) {
837830
if (!job.Objects || !job.Objects.length)

pkg/storaged/dialog.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ import { FormHelper } from "cockpit-components-form-helper";
243243

244244
import {
245245
decode_filename, fmt_size, block_name, format_size_and_text, format_delay, for_each_async, get_byte_units,
246-
is_available_block, BTRFS_TOOL_MOUNT_PATH
246+
BTRFS_TOOL_MOUNT_PATH
247247
} from "./utils.js";
248248
import { fmt_to_fragments } from "utils.jsx";
249249
import client from "./client.js";
@@ -1391,8 +1391,7 @@ export function init_teardown_usage(client, usage, expect_single_unmount) {
13911391
} else if (u.block.IdUsage == "crypto" && !client.blocks_cleartext[u.block.path]) {
13921392
u.data_warning = _("Locked encrypted device might contain data");
13931393
} else if (!client.blocks_ptable[u.block.path] &&
1394-
u.block.IdUsage != "raid" &&
1395-
!is_available_block(client, u.block)) {
1394+
u.block.IdUsage && u.block.IdUsage != "raid") {
13961395
u.data_warning = _("Device contains unrecognized data");
13971396
}
13981397
if (u.data_warning)

pkg/storaged/lvm2/create-dialog.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function create_vgroup() {
5656
if (disks.length === 0)
5757
return _("At least one disk is needed.");
5858
},
59-
spaces: get_available_spaces(client)
59+
spaces: get_available_spaces()
6060
})
6161
],
6262
Action: {

pkg/storaged/lvm2/volume-group.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ function add_disk(vgroup) {
204204
if (disks.length === 0)
205205
return _("At least one disk is needed.");
206206
},
207-
spaces: get_available_spaces(client).filter(filter_inside_vgroup)
207+
spaces: get_available_spaces().filter(filter_inside_vgroup)
208208
})
209209
],
210210
Action: {

pkg/storaged/mdraid/create-dialog.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export function create_mdraid() {
105105
return cockpit.format(cockpit.ngettext("At least $0 disk is needed.", "At least $0 disks are needed.", disks_needed),
106106
disks_needed);
107107
},
108-
spaces: get_available_spaces(client)
108+
spaces: get_available_spaces()
109109
})
110110
],
111111
Action: {

pkg/storaged/mdraid/mdraid.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ function add_disk(mdraid) {
167167
if (disks.length === 0)
168168
return _("At least one disk is needed.");
169169
},
170-
spaces: get_available_spaces(client).filter(filter_inside_mdraid)
170+
spaces: get_available_spaces().filter(filter_inside_mdraid)
171171
})
172172
],
173173
Action: {

pkg/storaged/pages.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner/inde
3939
import { DescriptionListDescription, DescriptionListGroup, DescriptionListTerm } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js";
4040
import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
4141

42-
import { decode_filename, block_short_name, fmt_size } from "./utils.js";
42+
import {
43+
decode_filename, block_short_name, fmt_size,
44+
reset_available_spaces
45+
} from "./utils.js";
4346
import { StorageButton, StorageBarMenu, StorageMenuItem, StorageSize } from "./storage-controls.jsx";
4447
import { MultipathAlert } from "./multipath.jsx";
4548
import { JobsPanel } from "./jobs-panel.jsx";
@@ -142,6 +145,7 @@ export const PAGE_CATEGORY_NETWORK = 3;
142145
export function reset_pages() {
143146
pages = new Map();
144147
crossrefs = new Map();
148+
reset_available_spaces();
145149
}
146150

147151
function name_from_card(card) {

pkg/storaged/partitions/partition-table.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import cockpit from "cockpit";
2121
import React from "react";
2222
import client from "../client";
2323

24-
import { get_partitions } from "../utils.js";
24+
import { get_partitions, register_available_free_space } from "../utils.js";
2525
import { StorageCard, ChildrenTable, new_page, new_card } from "../pages.jsx";
2626
import { format_dialog } from "../block/format-dialog.jsx";
2727
import { make_block_page } from "../block/create-pages.jsx";
@@ -68,9 +68,10 @@ function make_partition_pages(parent, block) {
6868
let p;
6969
for (i = 0; i < partitions.length; i++) {
7070
p = partitions[i];
71-
if (p.type == 'free')
71+
if (p.type == 'free') {
72+
register_available_free_space(client, block, p);
7273
make_free_space_page(parent, p.start, p.size, enable_dos_extended);
73-
else if (p.type == 'container')
74+
} else if (p.type == 'container')
7475
make_extended_partition_page(parent, p);
7576
else {
7677
const card = make_partition_card(null, p.block);

pkg/storaged/stratis/create-dialog.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function create_stratis_pool() {
5959
if (disks.length === 0)
6060
return _("At least one block device is needed.");
6161
},
62-
spaces: get_available_spaces(client)
62+
spaces: get_available_spaces()
6363
}),
6464
CheckBoxes("encrypt_pass", _("Options"),
6565
{

pkg/storaged/stratis/pool.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ function add_disks(pool) {
237237
if (disks.length === 0)
238238
return _("At least one disk is needed.");
239239
},
240-
spaces: get_available_spaces(client)
240+
spaces: get_available_spaces()
241241
})
242242
],
243243
Action: {

pkg/storaged/utils.js

Lines changed: 21 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -462,93 +462,32 @@ export function get_partitions(client, block) {
462462
return process_level(0, 0, block.Size);
463463
}
464464

465-
export function is_available_block(client, block, honor_ignore_hint) {
466-
const block_ptable = client.blocks_ptable[block.path];
467-
const block_part = client.blocks_part[block.path];
468-
const block_pvol = client.blocks_pvol[block.path];
465+
let available_spaces = [];
469466

470-
function has_fs_label() {
471-
if (!block.IdUsage)
472-
return false;
473-
// Devices with a LVM2_member label need to actually be
474-
// associated with a volume group.
475-
if (block.IdType == 'LVM2_member' && (!block_pvol || !client.vgroups[block_pvol.VolumeGroup]))
476-
return false;
477-
return true;
478-
}
479-
480-
function is_mpath_member() {
481-
if (!client.drives[block.Drive])
482-
return false;
483-
if (!client.drives_block[block.Drive]) {
484-
// Broken multipath drive
485-
return true;
486-
}
487-
const members = client.drives_multipath_blocks[block.Drive];
488-
for (let i = 0; i < members.length; i++) {
489-
if (members[i] == block)
490-
return true;
491-
}
492-
return false;
493-
}
494-
495-
function is_vdo_backing_dev() {
496-
return !!client.legacy_vdo_overlay.find_by_backing_block(block);
497-
}
498-
499-
function is_swap() {
500-
return !!block && client.blocks_swap[block.path];
501-
}
502-
503-
return (!(block.HintIgnore && honor_ignore_hint) &&
504-
block.Size > 0 &&
505-
!has_fs_label() &&
506-
!is_mpath_member() &&
507-
!is_vdo_backing_dev() &&
508-
!is_swap() &&
509-
!block_ptable &&
510-
!(block_part && block_part.IsContainer) &&
511-
!should_ignore(client, block.path));
467+
export function get_available_spaces() {
468+
return available_spaces.sort((a, b) => block_cmp(a.block, b.block));
512469
}
513470

514-
export function get_available_spaces(client) {
515-
function make(path) {
516-
const block = client.blocks[path];
517-
const parts = get_block_link_parts(client, path);
518-
const text = cockpit.format(parts.format, parts.link);
519-
return { type: 'block', block, size: block.Size, desc: text };
520-
}
521-
522-
const spaces = Object.keys(client.blocks).filter(p => is_available_block(client, client.blocks[p], true))
523-
.sort(make_block_path_cmp(client))
524-
.map(make);
525-
526-
function add_free_spaces(block) {
527-
const parts = get_partitions(client, block);
528-
let i;
529-
let p;
530-
let link_parts;
531-
let text;
532-
for (i in parts) {
533-
p = parts[i];
534-
if (p.type == 'free') {
535-
link_parts = get_block_link_parts(client, block.path);
536-
text = cockpit.format(link_parts.format, link_parts.link);
537-
spaces.push({
538-
type: 'free',
539-
block,
540-
start: p.start,
541-
size: p.size,
542-
desc: cockpit.format(_("unpartitioned space on $0"), text)
543-
});
544-
}
545-
}
546-
}
471+
export function reset_available_spaces() {
472+
available_spaces = [];
473+
}
547474

548-
for (const p in client.blocks_ptable)
549-
add_free_spaces(client.blocks[p]);
475+
export function register_available_block_space(client, block) {
476+
const parts = get_block_link_parts(client, block.path);
477+
const text = cockpit.format(parts.format, parts.link);
478+
available_spaces.push({ type: 'block', block, size: block.Size, desc: text });
479+
}
550480

551-
return spaces;
481+
export function register_available_free_space(client, block, partition) {
482+
const link_parts = get_block_link_parts(client, block.path);
483+
const text = cockpit.format(link_parts.format, link_parts.link);
484+
available_spaces.push({
485+
type: 'free',
486+
block,
487+
start: partition.start,
488+
size: partition.size,
489+
desc: cockpit.format(_("unpartitioned space on $0"), text)
490+
});
552491
}
553492

554493
export function prepare_available_spaces(client, spcs) {

test/verify/check-storage-lvm2

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,6 @@ class TestStorageLvm2(storagelib.StorageCase):
8686
b.wait_visible(self.dropdown_action("Remove") + "[disabled]")
8787
b.wait_text(self.dropdown_description("Remove"), "Last cannot be removed")
8888

89-
# Wipe the disk and make sure lvmetad forgets about it. This
90-
# might help with reusing it in the second half of this test.
91-
#
92-
# HACK - the pvscan is necessary because of
93-
# https://bugzilla.redhat.com/show_bug.cgi?id=1063813
94-
#
95-
m.execute(f"wipefs -a {dev_2}")
96-
m.execute(f"pvscan --cache {dev_2}")
97-
9889
# Thin volumes
9990
m.execute("lvcreate TEST1 --thinpool pool -L 20m")
10091
b.wait_visible(self.card_row("LVM2 logical volumes", name="pool"))
@@ -114,6 +105,15 @@ class TestStorageLvm2(storagelib.StorageCase):
114105
b.wait_visible(self.card("Storage"))
115106
b.wait_not_present(self.card_row("Storage", name="TEST1"))
116107

108+
# Wipe the disks and make sure lvmetad forgets about them. This
109+
# might help with reusing it in the second half of this test.
110+
#
111+
# HACK - the pvscan is necessary because of
112+
# https://bugzilla.redhat.com/show_bug.cgi?id=1063813
113+
#
114+
m.execute(f"wipefs -a {dev_1} {dev_2}")
115+
m.execute(f"pvscan --cache {dev_1} {dev_2}")
116+
117117
# create volume group in the UI
118118

119119
self.dialog_with_retry(trigger=lambda: self.click_devices_dropdown("Create LVM2 volume group"),
@@ -194,6 +194,9 @@ class TestStorageLvm2(storagelib.StorageCase):
194194
b.wait_not_present(self.card_row("LVM2 volume group", name=dev_2))
195195
b.wait_in_text(self.card_desc("LVM2 volume group", "Capacity"), "50.3 MB")
196196

197+
# loopback needs a explicit trigger...
198+
m.execute("udevadm trigger")
199+
197200
# create thin pool and volume
198201
# the pool will be maximum size, 50.3 MB
199202
b.click(self.card_button("LVM2 logical volumes", "Create new logical volume"))

test/verify/check-storage-multipath

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ class TestStorageMultipath(storagelib.StorageCase):
7878
b.wait_in_text(self.card_desc("Hard Disk Drive", "Multipathed devices"), dev1)
7979
b.wait_in_text(self.card_desc("Hard Disk Drive", "Multipathed devices"), dev2)
8080

81-
# Check that neither is offered as a free block device
81+
# Check that only one is offered as a free block device
8282
b.go("#/")
8383
self.click_dropdown(self.card_header("Storage"), "Create LVM2 volume group")
8484
self.dialog_wait_open()
85-
check_free_block_devices([])
85+
check_free_block_devices(["/dev/sda"])
8686
self.dialog_cancel()
8787
self.dialog_wait_close()
8888

0 commit comments

Comments
 (0)