Skip to content

Commit

Permalink
Merge pull request #435 from yast/iscsi-ui
Browse files Browse the repository at this point in the history
iSCSI UI
  • Loading branch information
joseivanlopez authored Mar 16, 2023
2 parents 2b893fb + fed9904 commit 817ebd9
Show file tree
Hide file tree
Showing 28 changed files with 1,400 additions and 110 deletions.
18 changes: 16 additions & 2 deletions service/lib/dinstaller/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ def initiator_name=(value)
backend.iscsi.initiator.name = value
end

# Whether the initiator name was set via iBFT
#
# @return [Boolean]
def ibft
backend.iscsi.initiator.ibft_name?
end

# Performs an iSCSI discovery
#
# @param address [String] IP address of the iSCSI server
Expand Down Expand Up @@ -199,6 +206,8 @@ def iscsi_delete(path)
dbus_interface ISCSI_INITIATOR_INTERFACE do
dbus_accessor :initiator_name, "s"

dbus_reader :ibft, "b", dbus_name: "IBFT"

dbus_method :Discover,
"in address:s, in port:u, in options:a{sv}, out result:u" do |address, port, options|
busy_while { iscsi_discover(address, port, options) }
Expand All @@ -223,18 +232,23 @@ def proposal
def register_proposal_callbacks
proposal.on_calculate do
export_proposal
properties_changed
proposal_properties_changed
update_validation
end
end

def register_iscsi_callbacks
backend.iscsi.on_activate do
properties = interfaces_and_properties[ISCSI_INITIATOR_INTERFACE]
dbus_properties_changed(ISCSI_INITIATOR_INTERFACE, properties, [])
end

backend.iscsi.on_probe do
refresh_iscsi_nodes
end
end

def properties_changed
def proposal_properties_changed
properties = interfaces_and_properties[PROPOSAL_CALCULATOR_INTERFACE]
dbus_properties_changed(PROPOSAL_CALCULATOR_INTERFACE, properties, [])
end
Expand Down
11 changes: 10 additions & 1 deletion service/lib/dinstaller/storage/iscsi/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def initialize(logger: nil)
@logger = logger || ::Logger.new($stdout)
@initiator = ISCSI::Initiator.new

@on_activate_callbacks = []
@on_probe_callbacks = []
end

Expand All @@ -71,7 +72,8 @@ def activate

Yast::IscsiClientLib.getConfig
Yast::IscsiClientLib.autoLogOn
sleep(sl)

@on_activate_callbacks.each(&:call)
end

# Probes iSCSI
Expand Down Expand Up @@ -171,6 +173,13 @@ def update(node, startup:)
end
end

# Registers a callback to be called after performing iSCSI activation
#
# @param block [Proc]
def on_activate(&block)
@on_activate_callbacks << block
end

# Registers a callback to be called when the nodes are probed
#
# @param block [Proc]
Expand Down
4 changes: 3 additions & 1 deletion service/test/dinstaller/dbus/storage/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@

let(:settings) { nil }

let(:iscsi) { instance_double(DInstaller::Storage::ISCSI::Manager, on_probe: nil) }
let(:iscsi) do
instance_double(DInstaller::Storage::ISCSI::Manager, on_activate: nil, on_probe: nil)
end

before do
allow(Yast::Arch).to receive(:s390).and_return false
Expand Down
2 changes: 2 additions & 0 deletions web/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@
"localdomain",
"luks",
"mgmt",
"onboot",
"partitioner",
"patternfly",
"rfkill",
"screenreader",
"ssid",
"ssids",
"startup",
"subprogress",
"subvol",
"subvolume",
Expand Down
4 changes: 4 additions & 0 deletions web/src/assets/styles/blocks.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ section {
gap: var(--spacer-small);
}

section:not(:last-child) {
margin-block-end: var(--spacer-large);
}

section > svg {
grid-area: icon;
}
Expand Down
11 changes: 11 additions & 0 deletions web/src/assets/styles/patternfly-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,14 @@ table td > .pf-c-empty-state {
.pf-c-data-list {
--pf-c-data-list--BorderTopWidth: 2px;
}

// Toolbar content aligned to the right (alignment prop seems to be ignored)
.pf-c-toolbar__content-section {
justify-content: flex-end;
}

section .pf-c-toolbar {
--stack-gutter: 0;
--pf-c-toolbar--PaddingBottom: 0;
--pf-c-toolbar--PaddingTop: 0;
}
153 changes: 83 additions & 70 deletions web/src/client/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
import DBusClient from "./dbus";
import { WithStatus, WithValidation } from "./mixins";

const PROPOSAL_CALCULATOR_IFACE = "org.opensuse.DInstaller.Storage1.Proposal.Calculator";
const ISCSI_NODE_IFACE = "org.opensuse.DInstaller.Storage1.ISCSI.Node";
const SERVICE = "org.opensuse.DInstaller.Storage";
const STORAGE_OBJECT = "/org/opensuse/DInstaller/Storage1";
const ISCSI_NODES_NAMESPACE = "/org/opensuse/DInstaller/Storage1/iscsi_nodes";
const PROPOSAL_CALCULATOR_IFACE = "org.opensuse.DInstaller.Storage1.Proposal.Calculator";
const ISCSI_INITIATOR_IFACE = "org.opensuse.DInstaller.Storage1.ISCSI.Initiator";
const PROPOSAL_IFACE = "org.opensuse.DInstaller.Storage1.Proposal";
const STORAGE_OBJECT = "/org/opensuse/DInstaller/Storage1";
const ISCSI_NODE_IFACE = "org.opensuse.DInstaller.Storage1.ISCSI.Node";

/**
* Removes properties with undefined value
Expand Down Expand Up @@ -62,6 +63,12 @@ class ProposalManager {
this.proxies = {};
}

async setUp() {
const proposalCalculator = await this.client.proxy(PROPOSAL_CALCULATOR_IFACE, STORAGE_OBJECT);

this.proxies = { proposalCalculator };
}

/**
* Gets data associated to the proposal
*
Expand Down Expand Up @@ -95,8 +102,7 @@ class ProposalManager {
};
};

const proxy = await this.proposalCalculatorProxy();
return proxy.AvailableDevices.map(buildDevice);
return this.proxies.proposalCalculator.AvailableDevices.map(buildDevice);
}

/**
Expand Down Expand Up @@ -214,21 +220,7 @@ class ProposalManager {
Volumes: { t: "aa{sv}", v: volumes?.map(dbusVolume) }
});

const proxy = await this.proposalCalculatorProxy();
return proxy.Calculate(settings);
}

/**
* @private
* Proxy for org.opensuse.DInstaller.Storage1.Proposal.Calculator iface
*
* @returns {Promise<object>}
*/
async proposalCalculatorProxy() {
if (!this.proxies.proposalCalculator)
this.proxies.proposalCalculator = await this.client.proxy(PROPOSAL_CALCULATOR_IFACE, STORAGE_OBJECT);

return this.proxies.proposalCalculator;
return this.proxies.proposalCalculator.Calculate(settings);
}

/**
Expand Down Expand Up @@ -260,14 +252,24 @@ class ISCSIManager {
this.proxies = {};
}

async setUp() {
const initiator = await this.client.proxy(ISCSI_INITIATOR_IFACE, STORAGE_OBJECT);
const nodes = await this.client.proxies(ISCSI_NODE_IFACE, ISCSI_NODES_NAMESPACE);

this.proxies = { initiator, nodes };
}

async getInitiatorIbft() {
return this.proxies.initiator.IBFT;
}

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

/**
Expand All @@ -276,8 +278,7 @@ class ISCSIManager {
* @param {string} value
*/
async setInitiatorName(value) {
const proxy = await this.iscsiInitiatorProxy();
proxy.InitiatorName = value;
this.proxies.initiator.InitiatorName = value;
}

/**
Expand All @@ -296,23 +297,7 @@ class ISCSIManager {
* @property {string} startup
*/
async getNodes() {
const buildNode = (iscsiProxy) => {
const id = path => path.split("/").slice(-1)[0];

return {
id: id(iscsiProxy.path),
target: iscsiProxy.Target,
address: iscsiProxy.Address,
port: iscsiProxy.Port,
interface: iscsiProxy.Interface,
ibft: iscsiProxy.IBFT,
connected: iscsiProxy.Connected,
startup: iscsiProxy.Startup
};
};

const proxy = await this.iscsiNodesProxy();
return Object.values(proxy).map(p => buildNode(p));
return Object.values(this.proxies.nodes).map(this.buildNode);
}

/**
Expand All @@ -338,8 +323,20 @@ class ISCSIManager {
ReversePassword: { t: "s", v: options.reversePassword }
});

const proxy = await this.iscsiInitiatorProxy();
return proxy.Discover(address, port, auth);
return this.proxies.initiator.Discover(address, port, auth);
}

/**
* Sets the startup status of the connection
*
* @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;
}

/**
Expand All @@ -352,8 +349,7 @@ class ISCSIManager {
async delete(node) {
const path = this.nodePath(node);

const proxy = await this.iscsiInitiatorProxy();
return proxy.Delete(path);
return this.proxies.initiator.Delete(path);
}

/**
Expand Down Expand Up @@ -401,32 +397,43 @@ class ISCSIManager {
return proxy.Logout();
}

/**
* @private
* Proxy for org.opensuse.DInstaller.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);
onInitiatorChanged(handler) {
return this.client.onObjectChanged(STORAGE_OBJECT, ISCSI_INITIATOR_IFACE, (changes) => {
const data = {
name: changes.InitiatorName?.v,
ibft: changes.IBFT?.v
};

return this.proxies.iscsiInitiator;
const filtered = Object.entries(data).filter(([, v]) => v !== undefined);
return handler(Object.fromEntries(filtered));
});
}

/**
* @private
* Proxy for objects implementing org.opensuse.DInstaller.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);
async onNodeAdded(handler) {
this.proxies.nodes.addEventListener("added", (_, proxy) => handler(this.buildNode(proxy)));
}

async onNodeChanged(handler) {
this.proxies.nodes.addEventListener("changed", (_, proxy) => handler(this.buildNode(proxy)));
}

async onNodeRemoved(handler) {
this.proxies.nodes.addEventListener("removed", (_, proxy) => handler(this.buildNode(proxy)));
}

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

return {
id: id(proxy.path),
target: proxy.Target,
address: proxy.Address,
port: proxy.Port,
interface: proxy.Interface,
ibft: proxy.IBFT,
connected: proxy.Connected,
startup: proxy.Startup
};
}

/**
Expand All @@ -447,16 +454,22 @@ class ISCSIManager {
* @ignore
*/
class StorageBaseClient {
static SERVICE = "org.opensuse.DInstaller.Storage";

/**
* @param {string|undefined} address - D-Bus address; if it is undefined, it uses the system bus.
*/
constructor(address = undefined) {
this.client = new DBusClient(StorageBaseClient.SERVICE, address);
this.client = new DBusClient(SERVICE, address);
this.proposal = new ProposalManager(this.client);
this.iscsi = new ISCSIManager(this.client);
}

/**
* Initializes the proxies
*/
async setUp() {
await this.proposal.setUp();
await this.iscsi.setUp();
}
}

/**
Expand Down
Loading

0 comments on commit 817ebd9

Please sign in to comment.