Line data Source code
1 : /*
2 : * Copyright (C) 2021 Red Hat, Inc.
3 : * SPDX-License-Identifier: LGPL-2.1-or-later
4 : */
5 : import '../lib/patternfly/patternfly-6-cockpit.scss';
6 : import "./network-anaconda.scss";
7 37 : import cockpit from "cockpit";
8 : import 'cockpit-dark-theme'; // once per page
9 37 : import React, { useRef } from 'react';
10 37 : import { createRoot } from "react-dom/client";
11 :
12 : import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
13 : import { ExclamationCircleIcon } from "@patternfly/react-icons";
14 :
15 : import { EmptyStatePanel } from "cockpit-components-empty-state.jsx";
16 : import { ModelContext } from './model-context.jsx';
17 : import { NetworkInterfacePage } from './network-interface.jsx';
18 : import { NetworkPage } from './network-main.jsx';
19 : import { UsageMonitor } from './helpers.js';
20 :
21 : import * as service from 'service.js';
22 : import { init as initDialogs, NetworkManagerModel } from './interfaces.js';
23 : import { superuser } from 'superuser';
24 : import { PlotState } from 'plot';
25 :
26 : import { useObject, useEvent, usePageLocation } from "hooks";
27 : import { WithDialogs } from "dialogs.jsx";
28 : import { AnacondaNetworkPage } from './anaconda-main';
29 :
30 37 : const _ = cockpit.gettext;
31 :
32 37 : const App = () => {
33 37 : const nmService = useObject(() => service.proxy("NetworkManager"),
34 37 : null,
35 37 : []);
36 37 : useEvent(nmService, "changed");
37 :
38 37 : const model = useObject(() => new NetworkManagerModel(), null, []);
39 37 : useEvent(model, "changed");
40 :
41 37 : const nmRunning_ref = useRef(undefined);
42 37 : useEvent(model.client, "owner", (event, owner) => { nmRunning_ref.current = owner !== null });
43 :
44 37 : const { path } = usePageLocation();
45 :
46 37 : useEvent(superuser, "changed");
47 :
48 37 : const usage_monitor = useObject(() => new UsageMonitor(), null, []);
49 37 : const plot_state_main = useObject(() => new PlotState(), null, []);
50 37 : const plot_state_iface = useObject(() => new PlotState(), null, []);
51 :
52 10 : if (model.curtain == 'testing' || model.curtain == 'restoring') {
53 10 : return <EmptyStatePanel loading title={model.curtain == 'testing' ? _("Testing connection") : _("Restoring connection")} />;
54 10 : }
55 :
56 37 : if (model.ready === undefined)
57 37 : return <EmptyStatePanel loading />;
58 :
59 : /* Show EmptyStatePanel when nm is not running */
60 8 : if (!nmRunning_ref.current) {
61 8 : if (nmService.enabled) {
62 8 : return (
63 8 : <div id="networking-nm-crashed">
64 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
65 8 : title={ _("NetworkManager is not running") }
66 7 : action={nmService.exists ? _("Start service") : null}
67 8 : onAction={ nmService.start }
68 8 : secondary={
69 8 : <Button component="a"
70 8 : variant="secondary"
71 1 : onClick={() => cockpit.jump("/system/services#/NetworkManager.service", cockpit.transport.host)}>
72 8 : {_("Troubleshoot…")}
73 8 : </Button>
74 8 : } />
75 8 : </div>
76 : );
77 8 : } else if (!nmService.exists) {
78 8 : return (
79 8 : <div id="networking-nm-not-found">
80 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
81 8 : title={ _("NetworkManager is not installed") } />
82 :
83 8 : </div>
84 : );
85 8 : } else {
86 8 : return (
87 8 : <div id="networking-nm-disabled">
88 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
89 8 : title={ _("Network devices and graphs require NetworkManager") }
90 7 : action={nmService.exists ? _("Enable service") : null}
91 1 : onAction={() => {
92 1 : nmService.enable();
93 1 : nmService.start();
94 1 : }} />
95 :
96 8 : </div>
97 : );
98 8 : }
99 8 : }
100 :
101 37 : const interfaces = model.list_interfaces();
102 :
103 37 : const anaconda_mode = JSON.parse(window.sessionStorage.getItem("cockpit_anaconda"));
104 :
105 7 : if (anaconda_mode) {
106 0 : const iface = path.length == 1 ? interfaces.find(iface => iface.Name == path[0]) : undefined;
107 :
108 7 : return (
109 7 : <ModelContext.Provider value={model}>
110 7 : <WithDialogs key="networking-anaconda">
111 7 : <AnacondaNetworkPage privileged={superuser.allowed}
112 7 : operationInProgress={model.operationInProgress}
113 7 : usage_monitor={usage_monitor}
114 7 : interfaces={interfaces}
115 7 : iface={iface} />
116 7 : </WithDialogs>
117 7 : </ModelContext.Provider>
118 : );
119 7 : }
120 :
121 : /* At this point NM is running and the model is ready */
122 36 : if (path.length == 0) {
123 36 : return (
124 36 : <ModelContext.Provider value={model}>
125 36 : <WithDialogs key="networking">
126 36 : <NetworkPage privileged={superuser.allowed}
127 36 : operationInProgress={model.operationInProgress}
128 36 : usage_monitor={usage_monitor}
129 36 : plot_state={plot_state_main}
130 36 : interfaces={interfaces} />
131 36 : </WithDialogs>
132 36 : </ModelContext.Provider>
133 : );
134 33 : } else if (path.length == 1) {
135 31 : const iface = interfaces.find(iface => iface.Name == path[0]);
136 :
137 34 : if (iface) {
138 34 : return (
139 34 : <ModelContext.Provider value={model}>
140 34 : <WithDialogs key="networking-interface">
141 34 : <NetworkInterfacePage privileged={superuser.allowed}
142 34 : operationInProgress={model.operationInProgress}
143 34 : usage_monitor={usage_monitor}
144 34 : plot_state={plot_state_iface}
145 34 : interfaces={interfaces}
146 34 : iface={iface} />
147 34 : </WithDialogs>
148 34 : </ModelContext.Provider>
149 : );
150 8 : } else {
151 8 : return (
152 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
153 8 : title={cockpit.format(_("Network interface '$0' not found"), path[0])}
154 8 : action={_("Return to overview page")}
155 8 : actionVariant="link"
156 0 : onAction={() => cockpit.location.go("/")}
157 8 : />
158 : );
159 8 : }
160 34 : }
161 :
162 7 : return null;
163 37 : };
164 :
165 37 : function init() {
166 37 : initDialogs();
167 37 : const root = createRoot(document.getElementById("network-page"));
168 37 : root.render(<App />);
169 37 : }
170 :
171 37 : document.addEventListener("DOMContentLoaded", init);
|