Skip to content
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
38 changes: 27 additions & 11 deletions service/lib/agama/storage/finisher.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) [2023] SUSE LLC
# Copyright (c) [2023-2025] SUSE LLC
#
# All Rights Reserved.
#
Expand All @@ -20,6 +20,7 @@
# find current contact information at www.suse.com.

require "yast"
require "yast/i18n"
require "yast2/execute"
require "yast2/systemd/service"
require "bootloader/finish_client"
Expand Down Expand Up @@ -82,6 +83,7 @@ def possible_steps
SecurityStep.new(logger, security),
CopyFilesStep.new(logger),
StorageStep.new(logger),
IscsiStep.new(logger),
BootloaderStep.new(logger),
IguanaStep.new(logger),
SnapshotsStep.new(logger),
Expand All @@ -94,8 +96,11 @@ def possible_steps

# Base class for the Finisher steps containing some shared logic
class Step
include Yast::I18n

# Base constructor
def initialize(logger)
textdomain "agama"
@logger = logger
end

Expand Down Expand Up @@ -137,7 +142,7 @@ class CopyFilesStep < Step
].freeze

def label
"Copying important installation files to the target system"
_("Copying important installation files to the target system")
end

def run?
Expand Down Expand Up @@ -178,7 +183,7 @@ def initialize(logger, security)
end

def label
"Writing Linux Security Modules configuration"
_("Writing Linux Security Modules configuration")
end

def run
Expand All @@ -189,7 +194,7 @@ def run
# Step to write the bootloader configuration
class BootloaderStep < Step
def label
"Installing bootloader"
_("Installing bootloader")
end

def run
Expand All @@ -206,18 +211,29 @@ def cio_ignore_finish
# Step to finish the Y2Storage configuration
class StorageStep < Step
def label
"Adjusting storage configuration"
_("Adjusting storage configuration")
end

def run
wfm_write("storage_finish")
end
end

# Step to finish the iSCSI configuration
class IscsiStep < Step
def label
_("Adjusting iSCSI configuration")
end

def run
wfm_write("iscsi-client_finish")
end
end

# Step to configure the file-system snapshots
class SnapshotsStep < Step
def label
"Configuring file systems snapshots"
_("Configuring file systems snapshots")
end

def run
Expand All @@ -230,7 +246,7 @@ class CopyLogsStep < Step
SCRIPTS_DIR = "/run/agama/scripts"

def label
"Copying logs"
_("Copying logs")
end

def run
Expand Down Expand Up @@ -264,7 +280,7 @@ def logs_dir
# Executes post-installation scripts
class PostScripts < Step
def label
"Running user-defined scripts"
_("Running user-defined scripts")
end

def run
Expand Down Expand Up @@ -303,7 +319,7 @@ def enable_init_scripts
# Executes post-installation scripts
class FilesStep < Step
def label
"Deploying user-defined files"
_("Deploying user-defined files")
end

def run
Expand All @@ -322,7 +338,7 @@ def deploy_files
# Step to unmount the target file-systems
class UnmountStep < Step
def label
"Unmounting storage devices"
_("Unmounting storage devices")
end

def run
Expand All @@ -338,7 +354,7 @@ class IguanaStep < Step
private_constant :IGUANA_MOUNTLIST

def label
"Configuring Iguana"
_("Configuring Iguana")
end

def run?
Expand Down
9 changes: 9 additions & 0 deletions service/lib/agama/storage/iscsi/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
require "y2iscsi_client/authentication"

Yast.import "IscsiClientLib"
Yast.import "Service"

module Agama
module Storage
Expand All @@ -33,6 +34,7 @@ module ISCSI
class Adapter
# Performs actions for activating iSCSI.
def activate
start_services
Yast::IscsiClientLib.getiBFT
# Check initiator name, creating one if missing.
return false unless Yast::IscsiClientLib.checkInitiatorName(silent: true)
Expand Down Expand Up @@ -137,6 +139,13 @@ def update_node(node, startup:)

private

# Starts the iSCSI services.
def start_services
Yast::Service.start("iscsi")
Yast::Service.start("iscsid")
Yast::Service.start("iscsiuio")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to have here comment why we need all three services

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why. We are replicating what YaST does. The three services are always available in the live media.

end

# Creates an iSCSI authentication object.
#
# @param credentials [Hash<Symbole, String>]
Expand Down
11 changes: 10 additions & 1 deletion service/lib/agama/storage/iscsi/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Manager
include Yast::I18n

STARTUP_OPTIONS = ["onboot", "manual", "automatic"].freeze
PACKAGES = ["open-iscsi", "iscsiuio"].freeze

# Config according to the JSON schema.
#
Expand All @@ -61,7 +62,15 @@ def initialize(progress_manager: nil, logger: nil)
@nodes = []
@on_activate_callbacks = []
@on_probe_callbacks = []
@on_sessions_change_callbacks = []
# Sets iSCSI as configured after any change on the sessions.
@on_sessions_change_callbacks = [proc { @configured = true }]
end

# Whether iSCSI was configured.
#
# @return [Boolean]
def configured?
!!@configured
end

# Performs actions for activating iSCSI.
Expand Down
16 changes: 15 additions & 1 deletion service/lib/agama/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,28 @@ def probe_devices

# Adds the required packages to the list of resolvables to install
def add_packages
devicegraph = Y2Storage::StorageManager.instance.staging
packages = devicegraph.used_features.pkg_list
packages += ISCSI::Manager::PACKAGES if need_iscsi?
return if packages.empty?

logger.info "Selecting these packages for installation: #{packages}"
Yast::PackagesProposal.SetResolvables(PROPOSAL_ID, :package, packages)
end

# Whether iSCSI is needed in the target system.
#
# @return [Boolean]
def need_iscsi?
iscsi.configured? || devicegraph.used_features.any? { |f| f.id == :UF_ISCSI }
end

# Staging devicegraph
#
# @return [Y2Storage::Devicegraph]
def devicegraph
Y2Storage::StorageManager.instance.staging
end

# Prepares the storage devices for installation
#
# @return [Boolean] true if the all actions were successful
Expand Down
6 changes: 6 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Thu Jun 19 10:21:13 UTC 2025 - José Iván López González <[email protected]>

- Add missing steps for iSCSI installation
(gh#agama-project/agama#2473).

-------------------------------------------------------------------
Wed Jun 18 12:28:14 UTC 2025 - Ladislav Slezák <[email protected]>

Expand Down
68 changes: 59 additions & 9 deletions service/test/agama/storage/iscsi/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
allow(Yast::IscsiClientLib).to receive(:iBFT?)
allow(Yast::IscsiClientLib).to receive(:find_session)
allow(Yast::IscsiClientLib).to receive(:getStartupStatus)
allow(Yast::Service).to receive(:start)
allow(subject).to receive(:adapter).and_return(adapter)
allow(subject).to receive(:sleep)
end
Expand All @@ -69,6 +70,14 @@
subject.activate
end

it "enables the iSCSI services" do
expect(Yast::Service).to receive(:start).with("iscsi").ordered
expect(Yast::Service).to receive(:start).with("iscsid").ordered
expect(Yast::Service).to receive(:start).with("iscsiuio").ordered

subject.activate
end

it "runs the callbacks" do
callback = proc {}
subject.on_activate(&callback)
Expand Down Expand Up @@ -230,25 +239,20 @@
end

describe "#login" do
let(:node) { Agama::Storage::ISCSI::Node.new }

before do
allow(Yast::IscsiClientLib).to receive(:default_startup_status).and_return("onboot")
allow(Yast::IscsiClientLib).to receive(:login_into_current)
allow(Yast::IscsiClientLib).to receive(:setStartupStatus)
end

let(:startup) { "automatic" }

before do
allow(Yast::IscsiClientLib).to receive(:login_into_current).and_return(login_success)
allow(Yast::IscsiClientLib).to receive(:setStartupStatus).and_return(startup_success)
end

let(:node) { Agama::Storage::ISCSI::Node.new }

let(:login_success) { nil }

let(:startup_success) { nil }

let(:startup) { "automatic" }

it "tries to login without credentials" do
expect(Yast::IscsiClientLib).to receive(:login_into_current) do |auth, _|
expect(auth).to be_a(Y2IscsiClient::Authentication)
Expand Down Expand Up @@ -685,4 +689,50 @@
end
end
end

describe "#configured?" do
context "if no session has been configured yet" do
it "returns false" do
expect(subject.configured?).to eq(false)
end
end

context "if a session was configured by loading a config" do
let(:config_json) do
{
targets: [
{
address: "192.168.100.152",
port: 3260,
name: "iqn.2025-01.com.example:becda24e8804c6580bd1",
interface: "default"
}
]
}
end

before do
allow(adapter).to receive(:login).and_return(true)
subject.apply_config_json(config_json)
end

it "returns true" do
expect(subject.configured?).to eq(true)
end
end

context "if a session was manually configured" do
before do
allow(Yast::IscsiClientLib).to receive(:default_startup_status).and_return("onboot")
allow(Yast::IscsiClientLib).to receive(:login_into_current).and_return(true)
allow(Yast::IscsiClientLib).to receive(:setStartupStatus).and_return(true)
node = Agama::Storage::ISCSI::Node.new
subject.login(node)
end

it "returns true" do
expect(subject.configured?).to eq(true)
end
end
end
end
Loading