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 113 : import React from "react";
8 : import client from "../client";
9 :
10 : import { install_dialog } from "cockpit-components-install-dialog.jsx";
11 :
12 : import { Card, CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js";
13 : import { Stack, StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js";
14 : import { DropdownGroup, DropdownList } from '@patternfly/react-core/dist/esm/components/Dropdown/index.js';
15 :
16 : import { StoragePlots } from "../plot.jsx";
17 : import { StorageMenuItem, StorageBarMenu } from "../storage-controls.jsx";
18 : import { dialog_open } from "../dialog.jsx";
19 : import { StorageLogsPanel } from "../logs-panel.jsx";
20 :
21 : import { create_mdraid } from "../mdraid/create-dialog.jsx";
22 : import { create_vgroup } from "../lvm2/create-dialog.jsx";
23 : import { create_stratis_pool } from "../stratis/create-dialog.jsx";
24 : import { iscsi_change_name, iscsi_discover } from "../iscsi/create-dialog.jsx";
25 : import { get_other_devices } from "../utils.js";
26 :
27 : import { new_page, new_card, StorageCard, ChildrenTable } from "../pages.jsx";
28 : import { make_drive_page } from "../drive/drive.jsx";
29 : import { make_lvm2_volume_group_page } from "../lvm2/volume-group.jsx";
30 : import { make_mdraid_page } from "../mdraid/mdraid.jsx";
31 : import { make_stratis_pool_page } from "../stratis/pool.jsx";
32 : import { make_stratis_stopped_pool_page } from "../stratis/stopped-pool.jsx";
33 : import { make_nfs_page, nfs_fstab_dialog } from "../nfs/nfs.jsx";
34 : import { make_iscsi_session_page } from "../iscsi/session.jsx";
35 : import { make_other_page } from "../block/other.jsx";
36 : import { make_btrfs_volume_page } from "../btrfs/volume.jsx";
37 :
38 113 : const _ = cockpit.gettext;
39 :
40 112 : export function make_overview_page() {
41 112 : const overview_card = new_card({
42 112 : title: _("Storage"),
43 112 : page_location: [],
44 112 : page_name: _("Storage"),
45 112 : component: OverviewCard
46 112 : });
47 :
48 112 : const overview_page = new_page(null, overview_card);
49 :
50 1 : Object.keys(client.iscsi_sessions).forEach(p => make_iscsi_session_page(overview_page, client.iscsi_sessions[p]));
51 112 : Object.keys(client.drives).forEach(p => {
52 112 : if (!client.drives_iscsi_session[p])
53 112 : make_drive_page(overview_page, client.drives[p]);
54 112 : });
55 25 : Object.keys(client.vgroups).forEach(p => make_lvm2_volume_group_page(overview_page, client.vgroups[p]));
56 7 : Object.keys(client.mdraids).forEach(p => make_mdraid_page(overview_page, client.mdraids[p]));
57 18 : Object.keys(client.stratis_pools).map(p => make_stratis_pool_page(overview_page, client.stratis_pools[p]));
58 9 : Object.keys(client.stratis_manager.StoppedPools).map(uuid => make_stratis_stopped_pool_page(overview_page, uuid));
59 3 : client.nfs.entries.forEach(e => make_nfs_page(overview_page, e));
60 51 : get_other_devices(client).map(p => make_other_page(overview_page, client.blocks[p]));
61 112 : Object.keys(client.uuids_btrfs_volume).forEach(uuid => make_btrfs_volume_page(overview_page, uuid));
62 112 : }
63 :
64 106 : const OverviewCard = ({ card, plot_state }) => {
65 106 : function menu_item(feature, title, action) {
66 106 : const feature_enabled = !feature || feature.is_enabled();
67 106 : const required_package = feature && feature.package;
68 :
69 23 : if (!feature_enabled && !(required_package && client.features.packagekit))
70 8 : return null;
71 :
72 32 : function install_then_action() {
73 2 : if (!feature_enabled) {
74 2 : install_dialog(required_package, feature.dialog_options).then(
75 2 : () => {
76 2 : feature.enable()
77 2 : .then(action)
78 0 : .catch(error => {
79 0 : dialog_open({
80 0 : Title: _("Error"),
81 0 : Body: error.toString()
82 0 : });
83 0 : });
84 2 : },
85 1 : () => null /* ignore cancel */);
86 1 : } else {
87 31 : action();
88 31 : }
89 32 : }
90 :
91 106 : return <StorageMenuItem key={title} onClick={install_then_action}>{title}</StorageMenuItem>;
92 106 : }
93 :
94 106 : const lvm2_feature = {
95 106 : is_enabled: () => client.features.lvm2
96 106 : };
97 :
98 106 : const stratis_feature = {
99 106 : is_enabled: () => client.features.stratis,
100 106 : package: client.get_config("stratis_package", false),
101 1 : enable: () => {
102 1 : return cockpit.spawn(["systemctl", "start", "stratisd"], { superuser: "require" })
103 1 : .then(() => client.stratis_start());
104 1 : },
105 :
106 106 : dialog_options: {
107 106 : title: _("Install Stratis support"),
108 106 : text: _("The $0 package must be installed to create Stratis pools.")
109 106 : }
110 106 : };
111 :
112 106 : const nfs_feature = {
113 95 : is_enabled: () => client.features.nfs,
114 106 : package: client.get_config("nfs_client_package", false),
115 1 : enable: () => {
116 1 : client.features.nfs = true;
117 1 : client.nfs.start();
118 1 : return Promise.resolve();
119 1 : },
120 :
121 106 : dialog_options: {
122 106 : title: _("Install NFS support")
123 106 : }
124 106 : };
125 :
126 106 : const iscsi_feature = {
127 106 : is_enabled: () => client.features.iscsi,
128 106 : };
129 :
130 106 : const local_menu_items = [
131 5 : menu_item(null, _("Create MDRAID device"), () => create_mdraid()),
132 12 : menu_item(lvm2_feature, _("Create LVM2 volume group"), () => create_vgroup()),
133 9 : menu_item(stratis_feature, _("Create Stratis pool"), () => create_stratis_pool()),
134 106 : ].filter(item => !!item);
135 :
136 106 : const net_menu_items = [
137 5 : !client.in_anaconda_mode() && menu_item(nfs_feature, _("New NFS mount"), () => nfs_fstab_dialog(null, null)),
138 1 : menu_item(iscsi_feature, _("Change iSCSI initiator name"), () => iscsi_change_name()),
139 1 : menu_item(iscsi_feature, _("Add iSCSI portal"), () => iscsi_discover()),
140 106 : ].filter(item => !!item);
141 :
142 106 : const groups = [];
143 :
144 106 : if (local_menu_items.length > 0)
145 106 : groups.push(
146 106 : <DropdownGroup key="local" label={_("Local storage")}>
147 106 : <DropdownList>
148 106 : {local_menu_items}
149 106 : </DropdownList>
150 106 : </DropdownGroup>);
151 :
152 106 : if (net_menu_items.length > 0)
153 106 : groups.push(
154 106 : <DropdownGroup key="net" label={_("Networked storage")}>
155 106 : <DropdownList>
156 106 : {net_menu_items}
157 106 : </DropdownList>
158 106 : </DropdownGroup>);
159 :
160 106 : const actions = <StorageBarMenu label={_("Create storage device")} menuItems={groups} />;
161 :
162 106 : return (
163 106 : <Stack hasGutter>
164 106 : { !client.in_anaconda_mode() &&
165 96 : <StackItem>
166 96 : <Card isPlain>
167 96 : <CardBody>
168 96 : <StoragePlots plot_state={plot_state} />
169 96 : </CardBody>
170 96 : </Card>
171 96 : </StackItem>
172 : }
173 106 : <StackItem>
174 106 : <StorageCard card={card} actions={actions}>
175 106 : <ChildrenTable
176 106 : emptyCaption={_("No storage found")}
177 106 : aria-label={_("Storage")}
178 106 : show_icons
179 106 : page={card.page}
180 106 : />
181 106 : </StorageCard>
182 106 : </StackItem>
183 106 : { !client.in_anaconda_mode() &&
184 96 : <StackItem>
185 96 : <StorageLogsPanel />
186 96 : </StackItem>
187 : }
188 106 : </Stack>);
189 106 : };
|