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 :
29 37 : const _ = cockpit.gettext;
30 :
31 37 : const App = () => {
32 37 : const nmService = useObject(() => service.proxy("NetworkManager"),
33 37 : null,
34 37 : []);
35 37 : useEvent(nmService, "changed");
36 :
37 37 : const model = useObject(() => new NetworkManagerModel(), null, []);
38 37 : useEvent(model, "changed");
39 :
40 37 : const nmRunning_ref = useRef(undefined);
41 37 : useEvent(model.client, "owner", (event, owner) => { nmRunning_ref.current = owner !== null });
42 :
43 37 : const { path } = usePageLocation();
44 :
45 37 : useEvent(superuser, "changed");
46 :
47 37 : const usage_monitor = useObject(() => new UsageMonitor(), null, []);
48 37 : const plot_state_main = useObject(() => new PlotState(), null, []);
49 37 : const plot_state_iface = useObject(() => new PlotState(), null, []);
50 :
51 10 : if (model.curtain == 'testing' || model.curtain == 'restoring') {
52 10 : return <EmptyStatePanel loading title={model.curtain == 'testing' ? _("Testing connection") : _("Restoring connection")} />;
53 10 : }
54 :
55 37 : if (model.ready === undefined)
56 37 : return <EmptyStatePanel loading />;
57 :
58 : /* Show EmptyStatePanel when nm is not running */
59 8 : if (!nmRunning_ref.current) {
60 8 : if (nmService.enabled) {
61 8 : return (
62 8 : <div id="networking-nm-crashed">
63 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
64 8 : title={ _("NetworkManager is not running") }
65 7 : action={nmService.exists ? _("Start service") : null}
66 8 : onAction={ nmService.start }
67 8 : secondary={
68 8 : <Button component="a"
69 8 : variant="secondary"
70 1 : onClick={() => cockpit.jump("/system/services#/NetworkManager.service", cockpit.transport.host)}>
71 8 : {_("Troubleshoot…")}
72 8 : </Button>
73 8 : } />
74 8 : </div>
75 : );
76 8 : } else if (!nmService.exists) {
77 8 : return (
78 8 : <div id="networking-nm-not-found">
79 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
80 8 : title={ _("NetworkManager is not installed") } />
81 :
82 8 : </div>
83 : );
84 8 : } else {
85 8 : return (
86 8 : <div id="networking-nm-disabled">
87 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
88 8 : title={ _("Network devices and graphs require NetworkManager") }
89 7 : action={nmService.exists ? _("Enable service") : null}
90 1 : onAction={() => {
91 1 : nmService.enable();
92 1 : nmService.start();
93 1 : }} />
94 :
95 8 : </div>
96 : );
97 8 : }
98 8 : }
99 :
100 37 : const interfaces = model.list_interfaces();
101 :
102 : /* At this point NM is running and the model is ready */
103 36 : if (path.length == 0) {
104 36 : return (
105 36 : <ModelContext.Provider value={model}>
106 36 : <WithDialogs key="1">
107 36 : <NetworkPage privileged={superuser.allowed}
108 36 : operationInProgress={model.operationInProgress}
109 36 : usage_monitor={usage_monitor}
110 36 : plot_state={plot_state_main}
111 36 : interfaces={interfaces} />
112 36 : </WithDialogs>
113 36 : </ModelContext.Provider>
114 : );
115 33 : } else if (path.length == 1) {
116 31 : const iface = interfaces.find(iface => iface.Name == path[0]);
117 :
118 34 : if (iface) {
119 34 : return (
120 34 : <ModelContext.Provider value={model}>
121 34 : <WithDialogs key="2">
122 34 : <NetworkInterfacePage privileged={superuser.allowed}
123 34 : operationInProgress={model.operationInProgress}
124 34 : usage_monitor={usage_monitor}
125 34 : plot_state={plot_state_iface}
126 34 : interfaces={interfaces}
127 34 : iface={iface} />
128 34 : </WithDialogs>
129 34 : </ModelContext.Provider>
130 : );
131 8 : } else {
132 8 : return (
133 8 : <EmptyStatePanel icon={ ExclamationCircleIcon }
134 8 : title={cockpit.format(_("Network interface '$0' not found"), path[0])}
135 8 : action={_("Return to overview page")}
136 8 : actionVariant="link"
137 0 : onAction={() => cockpit.location.go("/")}
138 8 : />
139 : );
140 8 : }
141 34 : }
142 :
143 7 : return null;
144 37 : };
145 :
146 37 : function init() {
147 37 : initDialogs();
148 37 : const root = createRoot(document.getElementById("network-page"));
149 37 : root.render(<App />);
150 37 : }
151 :
152 37 : document.addEventListener("DOMContentLoaded", init);
|