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
53 changes: 41 additions & 12 deletions service/lib/agama/storage/zfcp/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

require "yast"
require "y2s390/zfcp"
require "y2storage/storage_manager"
require "agama/storage/zfcp/controller"
require "agama/storage/zfcp/disk"

Expand Down Expand Up @@ -54,14 +55,20 @@ def on_disks_change(&block)

# Probes zFCP
#
# Callbacks are called at the end, see {#on_probe}.
# Callbacks {#on_probe} are called after probing, and callbacks {#on_disks_change} are
# called if there is any change in the disks.
def probe
logger.info "Probing zFCP"

previous_disks = disks
yast_zfcp.probe_controllers
yast_zfcp.probe_disks

@on_probe_callbacks&.each { |c| c.call(controllers, disks) }

return unless disks_changed?(previous_disks)

@on_disks_change_callbacks&.each { |c| c.call(disks) }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

np: Is it possible that @on_disks_change_callbacks is nil? Not something to change in this PR, but I rather prefer to have an empty array.

Copy link
Copy Markdown
Contributor Author

@joseivanlopez joseivanlopez Jul 6, 2023

Choose a reason for hiding this comment

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

Yes, because it is initialize on assignement, see https://github.com/openSUSE/agama/blob/master/service/lib/agama/storage/zfcp/manager.rb#L51. We could avoid it by initializing the list in the constructor or by adding a #on_disk_change_callbacks method.

end

# zFCP controllers
Expand Down Expand Up @@ -91,13 +98,22 @@ def allow_lun_scan?

# Activates the controller with the given channel id
#
# @note: If "allow_lun_scan" is active, then all LUNs are automatically activated.
# @note: If "allow_lun_scan" is active, then all its LUNs are automatically activated.
#
# @param channel [String]
# @return [Integer] Exit code of the chzdev command (0 on success)
def activate_controller(channel)
output = yast_zfcp.activate_controller(channel)
update_disks if output["exit"] == 0
if output["exit"] == 0
# LUNs activation could delay after activating the controller. This usually happens when
# activating a controller for first time because some SCSI initialization. Probing the
# disks should be done after all disks are activated.
#
# FIXME: waiting 2 seconds should be enough, but there is no guarantee that all the
# disks are actually activated.
sleep(2)
probe
end
output["exit"]
end

Expand All @@ -110,7 +126,7 @@ def activate_controller(channel)
# @return [Integer] Exit code of the chzdev command (0 on success)
def activate_disk(channel, wwpn, lun)
output = yast_zfcp.activate_disk(channel, wwpn, lun)
update_disks if output["exit"] == 0
probe if output["exit"] == 0
output["exit"]
end

Expand All @@ -125,7 +141,7 @@ def activate_disk(channel, wwpn, lun)
# @return [Integer] Exit code of the chzdev command (0 on success)
def deactivate_disk(channel, wwpn, lun)
output = yast_zfcp.deactivate_disk(channel, wwpn, lun)
update_disks if output["exit"] == 0
probe if output["exit"] == 0
output["exit"]
end

Expand Down Expand Up @@ -154,15 +170,28 @@ def find_luns(channel, wwpn)
# @return [Logger]
attr_reader :logger

# Probes and runs the on_disk_change callbacsk if disks have changed
def update_disks
disk_records = yast_zfcp.disks.sort_by { |d| d["dev_name"] }
probe
new_disk_records = yast_zfcp.disks.sort_by { |d| d["dev_name"] }
# Whether threre is any change in the disks
#
# Checks whether any of the removed disks is still probed or whether any of the current
# disks is not probed yet.
#
# @param previous_disks [Array<Disk>] Previously activated disks (before zFCP (re)probing)
# @return [Booelan]
def disks_changed?(previous_disks)
removed_disks = previous_disks - disks

return true if removed_disks.any? { |d| exist_disk?(d) }
return true if disks.any? { |d| !exist_disk?(d) }

return if disk_records == new_disk_records
false
end

@on_disks_change_callbacks&.each { |c| c.call(disks) }
# Whether the given disk is probed
#
# @param disk [Disk]
# @return [Booelan]
def exist_disk?(disk)
!Y2Storage::StorageManager.instance.probed.find_by_name(disk.name).nil?
end

# Creates a zFCP controller from YaST data
Expand Down
7 changes: 7 additions & 0 deletions service/package/rubygem-agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Wed Jul 5 14:02:23 UTC 2023 - José Iván López González <jlopez@suse.com>

- Delay zFCP probing after activating a controller and ensure the
system is marked as deprecated if needed after probing zFCP
(gh#openSUSE/agama#650).

-------------------------------------------------------------------
Wed Jun 14 15:11:56 UTC 2023 - José Iván López González <jlopez@suse.com>

Expand Down
147 changes: 55 additions & 92 deletions service/test/agama/storage/zfcp/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
require "agama/storage/zfcp/manager"
require "agama/storage/zfcp/controller"
require "agama/storage/zfcp/disk"
require "y2storage/storage_manager"
require "y2storage/devicegraph"
require "y2storage/disk"

# yast2-s390 is not available for all architectures. Defining Y2S390::ZFCP constant here in order to
# make possible to mock that class independently on the architecture. Note that Y2S390 classes are
Expand All @@ -38,6 +41,9 @@ module Y2S390

before do
allow(Y2S390::ZFCP).to receive(:new).and_return(yast_zfcp)
allow(Y2Storage::StorageManager).to receive(:instance).and_return(storage_manager)
allow(storage_manager).to receive(:probed).and_return(devicegraph)
allow(devicegraph).to receive(:find_by_name).and_return(storage_disk)
allow(yast_zfcp).to receive(:probe_controllers)
allow(yast_zfcp).to receive(:probe_disks)
allow(yast_zfcp).to receive(:controllers).and_return(*controller_records)
Expand All @@ -46,6 +52,12 @@ module Y2S390

let(:yast_zfcp) { double(Y2S390::ZFCP) }

let(:storage_manager) { instance_double(Y2Storage::StorageManager) }

let(:devicegraph) { instance_double(Y2Storage::Devicegraph) }

let(:storage_disk) { instance_double(Y2Storage::Disk) }

let(:controller_records) { [[]] }

let(:disk_records) { [[]] }
Expand Down Expand Up @@ -80,15 +92,55 @@ module Y2S390
subject.probe
end

let(:callback) { proc { |_, _| } }

it "runs the on_probe callbacks" do
callback = proc { |_, _| }
subject.on_probe(&callback)

expect(callback).to receive(:call).with([], [])

subject.probe
end

context "if any zFCP disk was deactivated" do
let(:disk_records) { [[sda_record, sdb_record], [sda_record]] }

it "runs the on_disks_change callbacks" do
callback = proc { |_| }
subject.on_disks_change(&callback)

expect(callback).to receive(:call)

subject.probe
end
end

context "if any new zFCP disk was activated" do
let(:disk_records) { [[sda_record], [sda_record, sdb_record]] }

let(:storage_disk) { nil }

it "runs the on_disks_change callbacks" do
callback = proc { |_| }
subject.on_disks_change(&callback)

expect(callback).to receive(:call)

subject.probe
end
end

context "if the zFCP disks have not changed" do
let(:disk_records) { [[sda_record, sdb_record], [sda_record, sdb_record]] }

it "does not run the on_disks_change callbacks" do
callback = proc { |_| }
subject.on_disks_change(&callback)

expect(callback).to_not receive(:call)

subject.probe
end
end
end

describe "#controllers" do
Expand Down Expand Up @@ -179,14 +231,11 @@ module Y2S390
describe "#activate_controller" do
before do
allow(yast_zfcp).to receive(:activate_controller).and_return(output)

subject.on_disks_change(&callback)
allow(subject).to receive(:sleep)
end

let(:output) { { "exit" => 3 } }

let(:callback) { proc { |_| } }

it "tries to activate the controller with the given channel id" do
expect(yast_zfcp).to receive(:activate_controller).with("0.0.fa00")

Expand All @@ -207,26 +256,6 @@ module Y2S390

subject.activate_controller("0.0.fa00")
end

context "and the zFCP disks have changed" do
let(:disk_records) { [[sda_record], [sdb_record]] }

it "runs the on_disks_change callbacks" do
expect(callback).to receive(:call)

subject.activate_controller("0.0.fa00")
end
end

context "and the zFCP disks have not changed" do
let(:disk_records) { [[sda_record], [sda_record]] }

it "does not run the on_disks_change callbacks" do
expect(callback).to_not receive(:call)

subject.activate_controller("0.0.fa00")
end
end
end

context "if the controller was not activated" do
Expand All @@ -237,26 +266,16 @@ module Y2S390

subject.activate_controller("0.0.fa00")
end

it "does not run the on_disks_change callbacks" do
expect(callback).to_not receive(:call)

subject.activate_controller("0.0.fa00")
end
end
end

describe "#activate_disk" do
before do
allow(yast_zfcp).to receive(:activate_disk).and_return(output)

subject.on_disks_change(&callback)
end

let(:output) { { "exit" => 3 } }

let(:callback) { proc { |_| } }

it "tries to activate the zFCP diks with the given channel id, WWPN and LUN" do
expect(yast_zfcp)
.to receive(:activate_disk).with("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
Expand All @@ -278,26 +297,6 @@ module Y2S390

subject.activate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end

context "and the zFCP disks have changed" do
let(:disk_records) { [[sda_record], [sdb_record]] }

it "runs the on_disks_change callbacks" do
expect(callback).to receive(:call)

subject.activate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end
end

context "and the zFCP disks have not changed" do
let(:disk_records) { [[sda_record], [sda_record]] }

it "does not run the on_disks_change callbacks" do
expect(callback).to_not receive(:call)

subject.activate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end
end
end

context "if the zFCP disk was not activated" do
Expand All @@ -308,26 +307,16 @@ module Y2S390

subject.activate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end

it "does not run the on_disks_change callbacks" do
expect(callback).to_not receive(:call)

subject.activate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end
end
end

describe "#deactivate_disk" do
before do
allow(yast_zfcp).to receive(:deactivate_disk).and_return(output)

subject.on_disks_change(&callback)
end

let(:output) { { "exit" => 3 } }

let(:callback) { proc { |_| } }

it "tries to deactivate the zFCP diks with the given channel id, WWPN and LUN" do
expect(yast_zfcp)
.to receive(:deactivate_disk).with("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
Expand All @@ -349,26 +338,6 @@ module Y2S390

subject.deactivate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end

context "and the zFCP disks have changed" do
let(:disk_records) { [[sda_record], [sdb_record]] }

it "runs the on_disks_change callbacks" do
expect(callback).to receive(:call)

subject.deactivate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end
end

context "and the zFCP disks have not changed" do
let(:disk_records) { [[sda_record], [sda_record]] }

it "does not run the on_disks_change callbacks" do
expect(callback).to_not receive(:call)

subject.deactivate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end
end
end

context "if the zFCP disk was not deactivated" do
Expand All @@ -379,12 +348,6 @@ module Y2S390

subject.deactivate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end

it "does not run the on_disks_change callbacks" do
expect(callback).to_not receive(:call)

subject.deactivate_disk("0.0.fa00", "0x500507630708d3b3", "0x0013000000000000")
end
end
end

Expand Down
6 changes: 6 additions & 0 deletions web/package/cockpit-agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Wed Jul 5 13:59:58 UTC 2023 - José Iván López González <jlopez@suse.com>

- Add info about deactivated zFCP auto_lun_scan and sort members in
device selector (gh#openSUSE/agama#650).

-------------------------------------------------------------------
Mon Jul 3 10:18:32 UTC 2023 - José Iván López González <jlopez@suse.com>

Expand Down
Loading