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 17, 2023
1 parent 8d63669 commit a5fefa4
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 20 deletions.
31 changes: 24 additions & 7 deletions pkg/storaged/content-views.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,21 @@ function create_tabs(client, target, options) {
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 });
if (options.unified) {
tab_menu_actions.push({ title, func });
} 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 +172,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 Down Expand Up @@ -513,6 +526,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 @@ -578,7 +594,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 Down Expand Up @@ -680,7 +696,7 @@ 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, 0, block, { go: () => utils.go_to_block(client, block.path), ...options });
return rows;
}

Expand Down Expand Up @@ -849,12 +865,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, 0, lvol, { go, ...options });
});
return rows;
}
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
35 changes: 33 additions & 2 deletions pkg/storaged/iscsi-panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { CheckIcon, EditIcon, PlusIcon, TrashIcon } from "@patternfly/react-icon

import { SidePanel } from "./side-panel.jsx";
import { } from "./utils.js";
import { StorageButton } from "./storage-controls.jsx";
import { StorageButton, StorageMenuItem } from "./storage-controls.jsx";
import { dialog_open, TextInput, PassInput, SelectRow } from "./dialog.jsx";

const _ = cockpit.gettext;
Expand Down Expand Up @@ -186,6 +186,22 @@ function iscsi_change_name(client) {
});
}

export function iscsi_menu_items(client, options) {
if (!client.features.iscsi)
return [];

return [
<StorageMenuItem key="edit"
onClick={() => iscsi_change_name(client)}>
{_("Change iSCSI initiator name")}
</StorageMenuItem>,
<StorageMenuItem key="add"
onClick={() => iscsi_discover(client)}>
{_("Add iSCSI portal")}
</StorageMenuItem>,
];
}

export function iscsi_rows(client, options) {
function cmp_session(path_a, path_b) {
const a = client.iscsi_sessions[path_a];
Expand Down Expand Up @@ -218,15 +234,30 @@ export function iscsi_rows(client, options) {
actions,
kind: "array",
name: session.data.target_name || "",
type: _("iSCSI portal"),
key: path,
detail: session.data.persistent_address + ":" + session.data.persistent_port
detail: session.data.persistent_address + ":" + session.data.persistent_port,
location: session.data.persistent_address + ":" + session.data.persistent_port,
portal: session
};
}

return Object.keys(client.iscsi_sessions).sort(cmp_session)
.map(make_session);
}

export function portal_menu_items(client, session, options) {
function iscsi_remove() {
return session.Logout({ 'node.startup': { t: 's', v: "manual" } });
}

return [
<StorageMenuItem danger onClick={iscsi_remove}>
{_("Disconnect")}
</StorageMenuItem>
];
}

export class IscsiPanel extends React.Component {
constructor() {
super();
Expand Down
5 changes: 4 additions & 1 deletion pkg/storaged/mdraids-panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ function mdraid_row(client, path) {
name: mdraid_name(mdraid),
devname: block && block_name(block),
detail: fmt_size(mdraid.Size) + " " + _("RAID device"),
type: _("RAID device"),
size: mdraid.Size,
job_path: path,
key: path,
go: () => cockpit.location.go(["mdraid", mdraid.UUID])
go: () => cockpit.location.go(["mdraid", mdraid.UUID]),
block
};
}

Expand Down
145 changes: 145 additions & 0 deletions pkg/storaged/overviewx.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/

import cockpit from "cockpit";
import React from "react";

import { Page, PageSection } from "@patternfly/react-core/dist/esm/components/Page/index.js";
import { Card, CardHeader, CardTitle, CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js";
import { Stack } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js";
import { ListingTable } from "cockpit-components-table.jsx";

import { StorageBarMenu } from "./storage-controls.jsx";

import { thing_menu_items, thing_rows } from "./things-panel.jsx";
import { drive_rows } from "./drives-panel.jsx";
import { iscsi_menu_items, iscsi_rows, portal_menu_items } from "./iscsi-panel.jsx";
import { other_rows } from "./others-panel.jsx";
import { block_content_rows, vgroup_content_rows, block_menu_items, vgroup_menu_items } from "./content-views.jsx";
import { stratis_content_rows, pool_menu_items } from "./stratis-details.jsx";

import { StoragePlots } from "./plot.jsx";
import { JobsPanel } from "./jobs-panel.jsx";
import { StorageLogsPanel } from "./logs-panel.jsx";
import { fmt_size } from "./utils.js";

const _ = cockpit.gettext;

// XXX - all this needs to be added:
//
// <LockedCryptoPanel client={client} />
// <NFSPanel client={client} />

export const OverviewX = ({ client, plot_state }) => {
const menu_items = [].concat(
thing_menu_items(client, { unified: true }),
iscsi_menu_items(client, { unified: true }));

const actions = <StorageBarMenu id="devices-menu"
isKebab
label={_("Create devices")}
menuItems={menu_items} />;

const top_rows = [].concat(
drive_rows(client, { unified: true }),
thing_rows(client, { unified: true }),
iscsi_rows(client, { unified: true }),
other_rows(client, { unified: true }));

let rows = [];
top_rows.forEach(t => {
let m = [];
if (t.block)
m = block_menu_items(client, t.block, { unified: true });
if (t.vgroup)
m = vgroup_menu_items(client, t.vgroup, { unified: true });
if (t.pool)
m = pool_menu_items(client, t.pool, { unified: true });
if (t.portal)
m = portal_menu_items(client, t.portal, { unified: true });
const actions = (m.length > 0
? <StorageBarMenu isKebab label={_("Create")} menuItems={m} />
: null);
rows.push({
props: { key: t.path, go: t.go },
columns: [
{ title: t.name }, // XXX - use "ID", name is taken.
{ title: t.type },
{ title: t.location || t.devname },
{ title: fmt_size(t.size), props: { className: "pf-v5-u-text-align-right" } },
{ title: actions, props: { className: "pf-v5-c-table__action content-action" } },
]
});
if (t.block)
rows = rows.concat(block_content_rows(client, t.block, { unified: true }));
if (t.vgroup)
rows = rows.concat(vgroup_content_rows(client, t.vgroup, { unified: true }));
if (t.pool)
rows = rows.concat(stratis_content_rows(client, t.pool, { unified: true }));
});

function onRowClick(event, row) {
if (!event || event.button !== 0)
return;

// StorageBarMenu sets this to tell us not to navigate when
// the kebabs are opened.
if (event.defaultPrevented)
return;

console.log("CLICK", event, row.props);
if (row.props.go)
row.props.go();
}

return (
<Page id="main-storage">
<PageSection>
<Stack hasGutter>
<Card>
<CardBody>
<StoragePlots plot_state={plot_state} />
</CardBody>
</Card>
<Card>
<CardHeader actions={{ actions }}>
<CardTitle component="h2">{_("Storage")}</CardTitle>
</CardHeader>
<CardBody className="contains-list">
<ListingTable
id="unified"
variant="compact"
aria-label={_("Storage")}
onRowClick={onRowClick}
columns={[
{ title: _("ID") },
{ title: _("Type") },
{ title: _("Location") },
{ title: _("Size") },
]}
rows={rows} />
</CardBody>
</Card>
<JobsPanel client={client} />
<StorageLogsPanel />
</Stack>
</PageSection>
</Page>
);
};
5 changes: 4 additions & 1 deletion pkg/storaged/storage-controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,10 @@ export const StorageBarMenu = ({ label, isKebab, onlyNarrow, menuItems }) => {
return null;

function onToggle(event, isOpen) {
event.stopPropagation();
// Tell Overview that we handled this event. We can't use
// stopPrevention() since the Toggles depend on seeing other
// Togglers events at the top level to close themselves.
event.preventDefault();
setIsOpen(isOpen);
}

Expand Down
12 changes: 11 additions & 1 deletion pkg/storaged/storage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,17 @@ tr[class*="content-level-"] {
}
}

@for $i from 1 through 10 {
#unified tr[class*="content-level-"] {
--multiplier: 0;
--offset: calc(var(--pf-v5-global--spacer--lg) * (var(--multiplier) + 1));

> td:first-child {
position: relative;
inset-inline-start: var(--offset);
}
}

@for $i from 0 through 10 {
tr.content-level-#{$i} {
--multiplier: #{$i};
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/storaged/storaged.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { PlotState } from "plot.js";
import client from "./client";
import { MultipathAlert } from "./multipath.jsx";
import { Overview } from "./overview.jsx";
import { OverviewX } from "./overviewx.jsx";
import { Details } from "./details.jsx";
import { update_plot_state } from "./plot.jsx";

Expand Down Expand Up @@ -80,6 +81,8 @@ class StoragePage extends React.Component {
let detail;
if (path.length === 0)
detail = null;
else if (path.length == 1 && path[0] == "X")
detail = <OverviewX client={client} plot_state={this.plot_state} />;
else if (path.length == 1)
detail = <Details client={client} type="block" name={path[0]} />;
else
Expand Down
Loading

0 comments on commit a5fefa4

Please sign in to comment.