Skip to content

Commit

Permalink
WIP - unified overview
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollmer committed Aug 28, 2023
1 parent bf64b22 commit 17d1082
Show file tree
Hide file tree
Showing 17 changed files with 500 additions and 79 deletions.
22 changes: 21 additions & 1 deletion pkg/storaged/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function is_multipath_master(block) {
}

function update_indices() {
let path, block, mdraid, vgroup, pvol, lvol, pool, blockdev, fsys, part, i;
let path, block, mdraid, vgroup, pvol, lvol, pool, blockdev, fsys, part, session, i;

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused variable session.

client.broken_multipath_present = false;
client.drives_multipath_blocks = { };
Expand Down Expand Up @@ -457,6 +457,26 @@ function update_indices() {
client.blocks_partitions[path].sort(function (a, b) { return a.Offset - b.Offset });
}

client.iscsi_sessions_drives = { };
client.drives_iscsi_session = { };
for (path in client.drives) {
const block = client.drives_block[path];
if (!block)
continue;
for (const session_path in client.iscsi_sessions) {
const session = client.iscsi_sessions[session_path];
for (i = 0; i < block.Symlinks.length; i++) {
console.log("??", block.Symlinks[i], session.data["target_name"]);
if (utils.decode_filename(block.Symlinks[i]).includes(session.data["target_name"])) {
client.drives_iscsi_session[path] = session;
if (!client.iscsi_sessions_drives[session_path])
client.iscsi_sessions_drives[session_path] = [];
client.iscsi_sessions_drives[session_path].push(client.drives[path]);
}
}
}
}

client.path_jobs = { };
function enter_job(job) {
if (!job.Objects || !job.Objects.length)
Expand Down
153 changes: 116 additions & 37 deletions pkg/storaged/content-views.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { BlockVolTab, PoolVolTab, VDOPoolTab } from "./lvol-tabs.jsx";
import { PartitionTab } from "./part-tab.jsx";
import { SwapTab } from "./swap-tab.jsx";
import { UnrecognizedTab } from "./unrecognized-tab.jsx";
import { vgroup_rename, vgroup_delete } from "./vgroup-details.jsx";

const _ = cockpit.gettext;

Expand Down Expand Up @@ -131,6 +132,8 @@ function create_tabs(client, target, options) {
(block_stratis_blockdev && client.stratis_pools[block_stratis_blockdev.Pool]) ||
block_stratis_stopped_pool);

const target_name = lvol ? utils.lvol_name(lvol) : block ? utils.block_name(block) : null;

// Adjust for encryption leaking out of Stratis
if (is_crypto && is_stratis)
is_crypto = false;
Expand All @@ -140,17 +143,28 @@ function create_tabs(client, target, options) {
warnings = warnings.concat(client.path_warnings[content_block.path] || []);

const tab_actions = [];
const tab_hints = [];
const tab_menu_actions = [];
const tab_menu_danger_actions = [];

function add_action(title, func) {
tab_actions.push(<StorageButton onlyWide key={title} onClick={func}>{title}</StorageButton>);
tab_menu_actions.push({ title, func, only_narrow: true });
function add_action(title, func, unified_hint) {
if (options.unified) {
tab_menu_actions.push({ title, func });
if (unified_hint)
tab_hints.push(unified_hint);
} else {
tab_actions.push(<StorageButton onlyWide key={title} onClick={func}>{title}</StorageButton>);
tab_menu_actions.push({ title, func, only_narrow: true });
}
}

function add_danger_action(title, func) {
tab_actions.push(<StorageButton onlyWide key={title} onClick={func}>{title}</StorageButton>);
tab_menu_danger_actions.push({ title, func, only_narrow: true });
if (options.unified) {
tab_menu_danger_actions.push({ title, func });
} else {
tab_actions.push(<StorageButton onlyWide key={title} onClick={func}>{title}</StorageButton>);
tab_menu_danger_actions.push({ title, func, only_narrow: true });
}
}

function add_menu_action(title, func) {
Expand All @@ -164,6 +178,11 @@ function create_tabs(client, target, options) {
const tabs = [];

function add_tab(name, renderer, for_content, associated_warnings) {
// No tabs on the unified overview
// XXX - what about warnings?
if (options.unified)
return;

let tab_warnings = [];
if (associated_warnings)
tab_warnings = warnings.filter(w => associated_warnings.indexOf(w.warning) >= 0);
Expand All @@ -188,7 +207,7 @@ function create_tabs(client, target, options) {
return;

dialog_open({
Title: _("Create thin volume"),
Title: cockpit.format(_("Create thin volume in $0/$1"), vgroup.Name, lvol.Name),
Fields: [
TextInput("name", _("Name"),
{
Expand Down Expand Up @@ -275,7 +294,7 @@ function create_tabs(client, target, options) {
return;

dialog_open({
Title: _("Unlock"),
Title: _("Unlock $0", target_name),
Fields: [
PassInput("passphrase", _("Passphrase"), {})
],
Expand All @@ -296,9 +315,9 @@ function create_tabs(client, target, options) {
} else {
const config = client.blocks_crypto[block.path]?.ChildConfiguration.find(c => c[0] == "fstab");
if (config && !content_block)
add_action(_("Mount"), () => mounting_dialog(client, block, "mount"));
add_action(_("Mount"), () => mounting_dialog(client, block, "mount"), _("not mounted"));
else
add_action(_("Unlock"), unlock);
add_action(_("Unlock"), unlock, _("locked"));
}
}

Expand All @@ -312,7 +331,7 @@ function create_tabs(client, target, options) {

function create_snapshot() {
dialog_open({
Title: _("Create snapshot"),
Title: cockpit.format(_("Create snapshot of $0/$1"), vgroup.Name, lvol.Name),
Fields: [
TextInput("name", _("Name"),
{ validate: utils.validate_lvm2_name }),
Expand All @@ -331,7 +350,7 @@ function create_tabs(client, target, options) {
if (lvol.Active) {
add_menu_action(_("Deactivate"), deactivate);
} else {
add_action(_("Activate"), activate);
add_action(_("Activate"), activate, _("not active"));
}
}
if (client.lvols[lvol.ThinPool]) {
Expand Down Expand Up @@ -363,29 +382,27 @@ function create_tabs(client, target, options) {
if (block)
block_part = client.blocks_part[block.path];

let name, danger;
let danger;

if (lvol) {
name = utils.lvol_name(lvol);
danger = _("Deleting a logical volume will delete all data in it.");
} else if (block_part) {
name = utils.block_name(block);
danger = _("Deleting a partition will delete all data in it.");
}

if (name) {
if (target_name) {
const usage = utils.get_active_usage(client, target.path, _("delete"));

if (usage.Blocking) {
dialog_open({
Title: cockpit.format(_("$0 is in use"), name),
Title: cockpit.format(_("$0 is in use"), target_name),
Body: BlockingMessage(usage)
});
return;
}

dialog_open({
Title: cockpit.format(_("Permanently delete $0?"), name),
Title: cockpit.format(_("Permanently delete $0?"), target_name),
Teardown: TeardownMessage(usage),
Action: {
Danger: danger,
Expand Down Expand Up @@ -423,12 +440,13 @@ function create_tabs(client, target, options) {
if (is_mounted(client, content_block))
add_menu_action(_("Unmount"), () => mounting_dialog(client, content_block, "unmount"));
else
add_action(_("Mount"), () => mounting_dialog(client, content_block, "mount"));
add_action(_("Mount"), () => mounting_dialog(client, content_block, "mount"), _("not mounted"));
}

return {
renderers: tabs,
actions: tab_actions,
hints: tab_hints,
menu_actions: tab_menu_actions,
menu_danger_actions: tab_menu_danger_actions,
has_warnings: warnings.length > 0
Expand Down Expand Up @@ -513,6 +531,9 @@ function block_description(client, block, options) {
if (cleartext && !omit_encrypted_label)
type = cockpit.format(_("$0 (encrypted)"), type);

if (options.unified)
link = null;

return {
type,
used_for,
Expand Down Expand Up @@ -558,6 +579,15 @@ function append_row(client, rows, level, key, name, desc, tabs, job_object, opti
if (info)
info = <>{"\n"}{info}</>;

let location = desc.used_for;
if (tabs.hints.length > 0) {
const hints = "(" + tabs.hints.join(", ") + ")";
if (location)
location += " " + hints;
else
location = hints;
}

const cols = [
{
title: (
Expand All @@ -567,7 +597,7 @@ function append_row(client, rows, level, key, name, desc, tabs, job_object, opti
</span>)
},
{ title: desc.type },
{ title: desc.link ? <StorageLink onClick={() => cockpit.location.go(desc.link)}>{desc.used_for}</StorageLink> : desc.used_for },
{ title: desc.link ? <StorageLink onClick={() => cockpit.location.go(location)}>{desc.used_for}</StorageLink> : location },
{
title: desc.size.length
? <StorageUsageBar stats={desc.size} critical={desc.critical_size || 0.95} block={name} />
Expand All @@ -578,7 +608,7 @@ function append_row(client, rows, level, key, name, desc, tabs, job_object, opti
];

rows.push({
props: { key, className: "content-level-" + level },
props: { key, className: "content-level-" + level, go: options.go },
columns: cols,
expandedContent: tabs.renderers.length > 0 ? <ListingPanel tabRenderers={tabs.renderers} /> : null
});
Expand All @@ -602,20 +632,29 @@ function append_partitions(client, rows, level, block, options) {
format_dialog(client, block.path, start, size, is_dos_partitioned && level <= device_level);
}

const btn = (
<StorageButton onlyWide onClick={create_partition}>
{_("Create partition")}
</StorageButton>
);

const item = (
<StorageMenuItem key="create"
onlyNarrow
onClick={create_partition}>
{_("Create partition")}
</StorageMenuItem>);
let btn, item, menu;

const menu = <StorageBarMenu onlyNarrow menuItems={[item]} isKebab />;
if (options.unified) {
btn = null;
item = (
<StorageMenuItem key="create"
onClick={create_partition}>
{_("Create partition")}
</StorageMenuItem>);
menu = <StorageBarMenu menuItems={[item]} isKebab />;
} else {
btn = (
<StorageButton onlyWide onClick={create_partition}>
{_("Create partition")}
</StorageButton>);
item = (
<StorageMenuItem key="create"
onlyNarrow
onClick={create_partition}>
{_("Create partition")}
</StorageMenuItem>);
menu = <StorageBarMenu onlyNarrow menuItems={[item]} isKebab />;
}

const cols = [
_("Free space"),
Expand All @@ -627,7 +666,11 @@ function append_partitions(client, rows, level, block, options) {

rows.push({
columns: cols,
props: { key: "free-space-" + rows.length.toString(), className: "content-level-" + level }
props: {
key: "free-space-" + rows.length.toString(),
className: "content-level-" + level,
go: options.go,
}
});
}

Expand Down Expand Up @@ -667,7 +710,8 @@ function append_device(client, rows, level, block, options) {

export function block_content_rows(client, block, options) {
const rows = [];
append_device(client, rows, 0, block, options);
append_device(client, rows, options.level || 0, block,
{ go: () => utils.go_to_block(client, block.path), ...options });
return rows;
}

Expand Down Expand Up @@ -728,6 +772,20 @@ function format_disk(client, block) {
});
}

export function block_menu_items(client, block, options) {
function onClick() {
if (block.ReadOnly)
return Promise.reject(_("Device is read-only"));
format_disk(client, block);
}

return [
<StorageMenuItem danger key="disk-format" onClick={onClick}>
{_("Create partition table")}
</StorageMenuItem>
];
}

const BlockContent = ({ client, block, allow_partitions }) => {
if (!block)
return null;
Expand Down Expand Up @@ -822,12 +880,13 @@ function append_logical_volume(client, rows, level, lvol, options) {
export function vgroup_content_rows(client, vgroup, options) {
const rows = [];

const go = () => cockpit.location.go(["vg", vgroup.Name]);
const isVDOPool = lvol => Object.keys(client.vdo_vols).some(v => client.vdo_vols[v].VDOPool == lvol.path);

(client.vgroups_lvols[vgroup.path] || []).forEach(lvol => {
// Don't display VDO pool volumes as separate entities; they are an internal implementation detail and have no actions
if (lvol.ThinPool == "/" && lvol.Origin == "/" && !isVDOPool(lvol))
append_logical_volume(client, rows, 0, lvol, options);
append_logical_volume(client, rows, options.level || 0, lvol, { go, ...options });
});
return rows;
}
Expand Down Expand Up @@ -869,7 +928,7 @@ function create_logical_volume(client, vgroup) {
purposes.push({ value: "vdo", title: _("VDO filesystem volume (compression/deduplication)") });

dialog_open({
Title: _("Create logical volume"),
Title: cockpit.format(_("Create logical volume in $0"), vgroup.Name),
Fields: [
TextInput("name", _("Name"),
{
Expand Down Expand Up @@ -986,6 +1045,26 @@ function create_logical_volume(client, vgroup) {
});
}

export function vgroup_menu_items(client, vgroup, options) {
function onClick() {
if (vgroup.FreeSize == 0)
return Promise.reject(_("No free space"));
create_logical_volume(client, vgroup);
}

return [
<StorageMenuItem key="vgroup-create" onClick={onClick}>
{_("Create logical volume")}
</StorageMenuItem>,
<StorageMenuItem key="vgroup-rename" onClick={() => vgroup_rename(client, vgroup)}>
{_("Rename volume group")}
</StorageMenuItem>,
<StorageMenuItem key="vgroup-rename" danger onClick={() => vgroup_delete(client, vgroup)}>
{_("Delete volume group")}
</StorageMenuItem>,
];
}

export class VGroup extends React.Component {
constructor () {
super();
Expand Down
2 changes: 1 addition & 1 deletion pkg/storaged/drives-panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { fmt_size, drive_name, decode_filename, block_name } from "./utils.js";
const _ = cockpit.gettext;
const C_ = cockpit.gettext;

export function drive_rows(client) {
export function drive_rows(client, options) {
function cmp_drive(path_a, path_b) {
return client.drives[path_a].SortKey.localeCompare(client.drives[path_b].SortKey);
}
Expand Down
Loading

0 comments on commit 17d1082

Please sign in to comment.