Skip to content

Commit

Permalink
Merge de2ba10 into c4e540a
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Jun 14, 2022
2 parents c4e540a + de2ba10 commit bae7344
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 14 deletions.
4 changes: 4 additions & 0 deletions web/src/App.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jest.mock("./InstallationProgress", () => () => "InstallationProgress Mock");
jest.mock("./InstallationFinished", () => () => "InstallationFinished Mock");
jest.mock("./Overview", () => () => "Overview Mock");
jest.mock("./Questions", () => () => <div>Questions Mock</div>);
jest.mock("./TargetIpsPopup", () => () => "Target IPs Mock");

const callbacks = {};
const initialStatusMock = null;
Expand All @@ -56,6 +57,9 @@ beforeEach(() => {
},
monitor: {
onDisconnect: jest.fn()
},
network: {
config: jest.fn()
}
};
});
Expand Down
19 changes: 8 additions & 11 deletions web/src/DBusError.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,22 @@
import React from "react";

import { screen } from "@testing-library/react";
import { installerRender } from "./test-utils";
import { installerRender, plainRender } from "./test-utils";

import DBusError from "./DBusError";

describe("DBusError", () => {
it("includes a generic D-Bus connection problem message", async () => {
installerRender(<DBusError />);

await screen.findByText(/Could not connect to the D-Bus service/i);
});
jest.mock("./TargetIpsPopup", () => () => "IP Mock");

it("includes a button for reloading", async () => {
installerRender(<DBusError />);
describe("DBusError", () => {
it("includes a generic D-Bus connection problem message", () => {
plainRender(<DBusError />);

await screen.findByRole("button", { name: /Reload/i });
expect(screen.getByText(/Could not connect to the D-Bus service/i))
.toBeInTheDocument()
});

it("calls location.reload when user clicks on 'Reload'", async () => {
const { user } = installerRender(<DBusError />);
const { user } = plainRender(<DBusError />);

const reloadButton = await screen.findByRole("button", { name: /Reload/i });

Expand Down
3 changes: 3 additions & 0 deletions web/src/InstallationFinished.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ describe("InstallationFinished", () => {
return {
manager: {
rebootSystem: rebootSystemFn
},
network: {
config: () => Promise.resolve({ addresses: [], hostname: "example.net" })
}
};
});
Expand Down
2 changes: 2 additions & 0 deletions web/src/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import "./layout.scss";
import logo from "./assets/suse-horizontal-logo.svg";

import About from "./About";
import TargetIpsPopup from "./TargetIpsPopup";

/**
* D-Installer main layout component.
Expand Down Expand Up @@ -85,6 +86,7 @@ function Layout({ MenuIcon, sectionTitle, SectionIcon, FooterActions, children }
<div className="layout__footer-info-area">
<img src={logo} alt="Logo of SUSE" className="company-logo" />
<About />
<TargetIpsPopup />
</div>
{ FooterActions &&
<div
Expand Down
7 changes: 7 additions & 0 deletions web/src/Overview.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const languages = [{ id: "en_US", name: "English" }];
const products = [{ id: "openSUSE", name: "openSUSE Tumbleweed" }];
const startInstallationFn = jest.fn();
const fakeUser = { fullName: "Fake User", userName: "fake_user", autologin: true };
const ipData = {
addresses: [],
hostname: "example.net"
}

beforeEach(() => {
createClient.mockImplementation(() => {
Expand All @@ -63,6 +67,9 @@ beforeEach(() => {
manager: {
startInstallation: startInstallationFn
},
network: {
config: () => Promise.resolve(ipData)
},
users: {
getUser: () => Promise.resolve(fakeUser),
isRootPasswordSet: () => Promise.resolve(true),
Expand Down
70 changes: 70 additions & 0 deletions web/src/TargetIpsPopup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) [2022] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import React, { useEffect, useState } from "react";
import { useInstallerClient } from "./context/installer";
import Popup from "./Popup";
import { Button, Text } from "@patternfly/react-core";

const initIpData = {
addresses: [],
hostname: ""
};

function formatIp(address, prefix) {
return address + "/" + prefix;
}

export default function TargetIpsPopup() {
const [isOpen, setIsOpen] = useState(false);

const open = () => setIsOpen(true);
const close = () => setIsOpen(false);
const client = useInstallerClient();
const [state, setState] = useState(initIpData);
const ips = state.addresses.map((addr) => formatIp(addr.address, addr.prefix));
const firstIp = ips.length > 0 ? ips[0] : "";

useEffect(() => {
client.network.config().then((data) => {
setState(data);
});
}, [client.network]);

return (
<>
<Button variant="link" onClick={open}>
{ firstIp } ({state.hostname})
</Button>

<Popup
isOpen={isOpen}
title={state.hostname}
aria-label="IP Addresses"
>
{ ips.map((ip) => <Text key={ip}> {ip} </Text>) }
<Popup.Actions>
<Popup.Confirm onClick={close} autoFocus>Close</Popup.Confirm>
</Popup.Actions>
</Popup>
</>
);
}
9 changes: 8 additions & 1 deletion web/src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,22 @@ import StorageClient from "./storage";
import UsersClient from "./users";
import status from "./status";
import QuestionsClient from "./questions";
import NetworkClient from "./network";

import cockpit from "../lib/cockpit";

const SERVICE_NAME = "org.opensuse.DInstaller";
const NM_SERVICE_NAME = "org.freedesktop.NetworkManager";

const createClient = () => {
const client = cockpit.dbus(SERVICE_NAME, {
bus: "system",
superuser: "try"
});
const nmClient = cockpit.dbus(NM_SERVICE_NAME, {
bus: "system",
superuser: "try"
});

return {
language: new LanguageClient(client),
Expand All @@ -45,7 +51,8 @@ const createClient = () => {
software: new SoftwareClient(client),
storage: new StorageClient(client),
users: new UsersClient(client),
questions: new QuestionsClient(client)
questions: new QuestionsClient(client),
network: new NetworkClient(nmClient)
};
};

Expand Down
6 changes: 5 additions & 1 deletion web/src/client/mixins.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ const withDBus = {

const proxy = this._client.proxy(iface, path, { watch: true });
await proxy.wait();
_proxies[iface] = proxy;

if (!path) {
_proxies[iface] = proxy;
}

return proxy;
},

Expand Down
118 changes: 118 additions & 0 deletions web/src/client/network.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) [2022] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { applyMixin, withDBus } from "./mixins";

const NM_IFACE = "org.freedesktop.NetworkManager";

export default class NetworkClient {
constructor(dbusClient) {
this._client = dbusClient;
}

/**
* Returns IP config overview - addresses and hostname
*
* @return {Promise.<Objects>} address key stores list of addresses,
* hostname stores target's hostname
*/
async config() {
const data = await this.#addresses();
const addresses = data.map(a => {
return {
address: a.address.v,
prefix: a.prefix.v
};
});

return {
addresses,
hostname: await this.hostname()
};
}

/**
* Returns the computer's hostname
*
* https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Settings.html
*
* @return {Promise.<String>}
*/
async hostname() {
const proxy = await this.proxy(NM_IFACE + ".Settings");

return proxy.Hostname;
}

/*
* Returns list of active NM connections
*
* Private method.
* See NM API documentation for details.
* https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html
*
* @return {Promis.<Array>}
*/
async #connections() {
const proxy = await this.proxy(NM_IFACE);

return proxy.ActiveConnections;
}

/*
* Returns NM IP config for the particular connection
*
* Private method.
* See NM API documentation for details
* https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Connection.Active.html
* https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.IP4Config.html
*
* @return {Promise.<Map>}
*/
async #address(connection) {
const configPath = await this.proxy(NM_IFACE + ".Connection.Active", connection);
const ipConfigs = await this.proxy(NM_IFACE + ".IP4Config", configPath.Ip4Config);

return ipConfigs.AddressData;
}

/*
* Returns list of IP addresses for all active NM connections
*
* Private method.
*
* @return {Promise.<Array>}
*/
async #addresses() {
const conns = await this.#connections();

let result = [];

for (const i in conns) {
const addr = await this.#address(conns[i]);
result = [...result, ...addr];
}

return result;
}
}

applyMixin(NetworkClient, withDBus);
7 changes: 6 additions & 1 deletion web/src/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ const installerRender = (ui, options = {}) => ({
...render(ui, { wrapper: InstallerProvider, ...options })
});

export { installerRender };
const plainRender = (ui, options = {}) => ({
user: userEvent.setup(),
...render(ui, options)
});

export { installerRender, plainRender };

0 comments on commit bae7344

Please sign in to comment.