Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iSCSI UI #435

Merged
merged 19 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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