Skip to content

Commit

Permalink
web: adapt the ISCSI client to the new HTTP API
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed May 8, 2024
1 parent fb191cc commit 77cea2b
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 152 deletions.
4 changes: 2 additions & 2 deletions web/src/client/software.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class ProductBaseClient {

return {
success: response.ok, // still we can fail 400 due to dbus issue or 500 if backend stop working. maybe some message for this case?
message: ""
message: "",
};
}

Expand All @@ -318,7 +318,7 @@ class ProductBaseClient {

return {
success: response.ok, // still we can fail 400 due to dbus issue or 500 if backend stop working. maybe some message for this case?
message: ""
message: "",
};
}

Expand Down
194 changes: 74 additions & 120 deletions web/src/client/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
// cspell:ignore ptable

import { compact, hex, uniq } from "~/utils";
import { WithIssues, WithStatus, WithProgress } from "./mixins";
import { WithIssues, WithProgress, WithStatus } from "./mixins";
import { HTTPClient } from "./http";

const STORAGE_OBJECT = "/org/opensuse/Agama/Storage1";
const STORAGE_JOBS_NAMESPACE = "/org/opensuse/Agama/Storage1/jobs";
const STORAGE_JOB_IFACE = "org.opensuse.Agama.Storage1.Job";
const PROPOSAL_IFACE = "org.opensuse.Agama.Storage1.Proposal";
const ISCSI_INITIATOR_IFACE = "org.opensuse.Agama.Storage1.ISCSI.Initiator";
const ISCSI_NODES_NAMESPACE = "/org/opensuse/Agama/Storage1/iscsi_nodes";
const ISCSI_NODES_NAMESPACE = "/storage/iscsi/nodes";
const ISCSI_NODE_IFACE = "org.opensuse.Agama.Storage1.ISCSI.Node";
const DASD_MANAGER_IFACE = "org.opensuse.Agama.Storage1.DASD.Manager";
const DASD_DEVICES_NAMESPACE = "/org/opensuse/Agama/Storage1/dasds";
Expand Down Expand Up @@ -1194,50 +1194,37 @@ class ZFCPManager {
*/
class ISCSIManager {
/**
* @param {string} service - D-Bus service name
* @param {string} address - D-Bus address
* @param {import("./http").HTTPClient} client - HTTP client.
*/
constructor(service, address) {
this.service = service;
this.address = address;
this.proxies = {};
constructor(client) {
this.client = client;
}

/**
* @return {DBusClient} client
* Gets the iSCSI initiator
*
* @return {Promise<ISCSIInitiator>}
*
* @typedef {object} ISCSIInitiator
* @property {string} name
* @property {boolean} ibft
*/
client() {
// return this.assigned_client;
if (!this._client) {
this._client = new DBusClient(this.service, this.address);
async getInitiator() {
const response = await this.client.get("/storage/iscsi/initiator");
if (!response.ok) {
console.error("Failed to get the iSCSI initiator", response);
}

return this._client;
}

async getInitiatorIbft() {
const proxy = await this.iscsiInitiatorProxy();
return proxy.IBFT;
}

/**
* Gets the iSCSI initiator name
*
* @returns {Promise<string>}
*/
async getInitiatorName() {
const proxy = await this.iscsiInitiatorProxy();
return proxy.InitiatorName;
return response.json();
}

/**
* Sets the iSCSI initiator name
*
* @param {string} value
*/
async setInitiatorName(value) {
const proxy = await this.iscsiInitiatorProxy();
proxy.InitiatorName = value;
setInitiatorName(value) {
return this.client.patch("/storage/iscsi/initiator", { name: value });
}

/**
Expand All @@ -1256,8 +1243,12 @@ class ISCSIManager {
* @property {string} startup
*/
async getNodes() {
const proxy = await this.iscsiNodesProxy();
return Object.values(proxy).map(this.buildNode);
const response = await this.client.get("/storage/iscsi/nodes");
if (!response.ok) {
console.error("Failed to get the list of iSCSI nodes", response);
}

return response.json();
}

/**
Expand All @@ -1273,18 +1264,16 @@ class ISCSIManager {
* @property {string} [reverseUsername] - Username for authentication by initiator
* @property {string} [reversePassword] - Password for authentication by initiator
*
* @returns {Promise<number>} 0 on success, 1 on failure
* @returns {Promise<boolean>} true on success, false on failure
*/
async discover(address, port, options = {}) {
const auth = removeUndefinedCockpitProperties({
Username: { t: "s", v: options.username },
Password: { t: "s", v: options.password },
ReverseUsername: { t: "s", v: options.reverseUsername },
ReversePassword: { t: "s", v: options.reversePassword }
});

const proxy = await this.iscsiInitiatorProxy();
return proxy.Discover(address, port, auth);
const data = {
address,
port,
options,
};
const response = await this.client.post("/storage/iscsi/discover", data);
return response.ok;
}

/**
Expand All @@ -1293,25 +1282,21 @@ class ISCSIManager {
* @param {ISCSINode} node
* @param {String} startup
*/
async setStartup(node, startup) {
const path = this.nodePath(node);

const proxy = await this.client().proxy(ISCSI_NODE_IFACE, path);
proxy.Startup = startup;
setStartup(node, startup) {
this.client.patch(this.nodePath(node), { startup });
}

/**
* Deletes the given iSCSI node
*
* @param {ISCSINode} node
* @returns {Promise<number>} 0 on success, 1 on failure if the given path is not exported, 2 on
* @returns {Promise<boolean>} 0 on success, 1 on failure if the given path is not exported, 2 on
* failure because any other reason.
*/
async delete(node) {
const path = this.nodePath(node);

const proxy = await this.iscsiInitiatorProxy();
return proxy.Delete(path);
// FIXME: return the proper error code
const response = await this.client.delete(this.nodePath(node));
return response.ok;
}

/**
Expand All @@ -1331,63 +1316,61 @@ class ISCSIManager {
* valid, and 2 on failure because any other reason
*/
async login(node, options = {}) {
const path = this.nodePath(node);

const dbusOptions = removeUndefinedCockpitProperties({
Username: { t: "s", v: options.username },
Password: { t: "s", v: options.password },
ReverseUsername: { t: "s", v: options.reverseUsername },
ReversePassword: { t: "s", v: options.reversePassword },
Startup: { t: "s", v: options.startup }
});
const path = this.nodePath(node) + "/login";
const response = await this.client.post(path, options);
if (!response.ok) {
console.error("Could not login into the iSCSI node", response);
return response.json();
}

const proxy = await this.client().proxy(ISCSI_NODE_IFACE, path);
return proxy.Login(dbusOptions);
return 0;
}

/**
* Closes an iSCSI session
*
* @param {ISCSINode} node
* @returns {Promise<number>} 0 on success, 1 on failure
* @returns {Promise<boolean>} true on success, false on failure
*/
async logout(node) {
const path = this.nodePath(node);
// const iscsiNode = new ISCSINodeObject(this.client, path);
// return await iscsiNode.iface.logout();
const proxy = await this.client().proxy(ISCSI_NODE_IFACE, path);
return proxy.Logout();
const path = this.nodePath(node) + "/logout";
const response = await this.client.post(path);
if (!response.ok) {
console.error("Could not logout from the iSCSI node", response);
return false;
}

return true;
}

onInitiatorChanged(handler) {
return this.client().onObjectChanged(STORAGE_OBJECT, ISCSI_INITIATOR_IFACE, (changes) => {
const data = {
name: changes.InitiatorName?.v,
ibft: changes.IBFT?.v
};
return this.client.onEvent("ISCSIInitiatorChanged", handler);
}

const filtered = Object.entries(data).filter(([, v]) => v !== undefined);
return handler(Object.fromEntries(filtered));
});
onNodeAdded(handler) {
return this.onNodeEvent("ISCSINodeAdded", handler);
}

async onNodeAdded(handler) {
const proxy = await this.iscsiNodesProxy();
proxy.addEventListener("added", (_, proxy) => handler(this.buildNode(proxy)));
onNodeChanged(handler) {
return this.onNodeEvent("ISCSINodeChanged", handler);
}

async onNodeChanged(handler) {
const proxy = await this.iscsiNodesProxy();
proxy.addEventListener("changed", (_, proxy) => handler(this.buildNode(proxy)));
onNodeRemoved(handler) {
return this.onNodeEvent("ISCSINodeRemoved", handler);
}

async onNodeRemoved(handler) {
const proxy = await this.iscsiNodesProxy();
proxy.addEventListener("removed", (_, proxy) => handler(this.buildNode(proxy)));
/**
* @private
* Registers a handler for the given iSCSI node event.
*
* @param {string} eventName - Event name
*/
onNodeEvent(eventName, handler) {
return this.client.onEvent(eventName, ({ node }) => handler(node));
}

buildNode(proxy) {
const id = path => path.split("/").slice(-1)[0];
const id = (path) => path.split("/").slice(-1)[0];

return {
id: id(proxy.path),
Expand All @@ -1397,39 +1380,10 @@ class ISCSIManager {
interface: proxy.Interface,
ibft: proxy.IBFT,
connected: proxy.Connected,
startup: proxy.Startup
startup: proxy.Startup,
};
}

/**
* @private
* Proxy for org.opensuse.Agama.Storage1.ISCSI.Initiator iface
*
* @returns {Promise<object>}
*/
async iscsiInitiatorProxy() {
if (!this.proxies.iscsiInitiator) {
this.proxies.iscsiInitiator = await this.client().proxy(ISCSI_INITIATOR_IFACE, STORAGE_OBJECT);
}

return this.proxies.iscsiInitiator;
}

/**
* @private
* Proxy for objects implementing org.opensuse.Agama.Storage1.ISCSI.Node iface
*
* @note The ISCSI nodes are dynamically exported.
*
* @returns {Promise<object>}
*/
async iscsiNodesProxy() {
if (!this.proxies.iscsiNodes)
this.proxies.iscsiNodes = await this.client().proxies(ISCSI_NODE_IFACE, ISCSI_NODES_NAMESPACE);

return this.proxies.iscsiNodes;
}

/**
* @private
* Builds the D-Bus path for the given iSCSI node
Expand Down Expand Up @@ -1458,7 +1412,7 @@ class StorageBaseClient {
this.system = new DevicesManager(this.client, "system");
this.staging = new DevicesManager(this.client, "staging");
this.proposal = new ProposalManager(this.client, this.system);
this.iscsi = new ISCSIManager(StorageBaseClient.SERVICE, client);
this.iscsi = new ISCSIManager(this.client);
this.dasd = new DASDManager(StorageBaseClient.SERVICE, client);
this.zfcp = new ZFCPManager(StorageBaseClient.SERVICE, client);
}
Expand Down
31 changes: 17 additions & 14 deletions web/src/components/storage/iscsi/DiscoverForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ export default function DiscoverForm({ onSubmit: onSubmitProp, onCancel }) {

useEffect(() => {
// Scroll the alert into view
if (isFailed)
if (isFailed) {
alertRef.current.scrollIntoView({
behavior: "smooth",
block: "start"
block: "start",
});
}
});

const updateData = (key, value) => setData({ ...data, [key]: value });
Expand All @@ -72,9 +73,9 @@ export default function DiscoverForm({ onSubmit: onSubmitProp, onCancel }) {
setIsLoading(true);
setSavedData(data);

const result = await onSubmitProp(data);
const success = await onSubmitProp(data);

if (result !== 0) {
if (!success) {
setIsFailed(true);
setIsLoading(false);
}
Expand All @@ -100,16 +101,18 @@ export default function DiscoverForm({ onSubmit: onSubmitProp, onCancel }) {
// TRANSLATORS: popup title
<Popup isOpen title={_("Discover iSCSI Targets")}>
<Form id={id} onSubmit={onSubmit}>
{ isFailed &&
<div ref={alertRef}>
<Alert
variant="warning"
isInline
title={_("Something went wrong")}
>
<p>{_("Make sure you provide the correct values")}</p>
</Alert>
</div> }
{isFailed &&
(
<div ref={alertRef}>
<Alert
variant="warning"
isInline
title={_("Something went wrong")}
>
<p>{_("Make sure you provide the correct values")}</p>
</Alert>
</div>
)}
<FormGroup
fieldId="address"
label={_("IP address")}
Expand Down
Loading

0 comments on commit 77cea2b

Please sign in to comment.