diff --git a/src/lib/y2s390.rb b/src/lib/y2s390.rb new file mode 100644 index 00000000..51ac23fd --- /dev/null +++ b/src/lib/y2s390.rb @@ -0,0 +1,4 @@ +require "y2s390/dasd" +require "y2s390/dasds_reader" +require "y2s390/dasds_collection" +require "y2s390/hwinfo_reader" diff --git a/src/lib/y2s390/base_collection.rb b/src/lib/y2s390/base_collection.rb new file mode 100644 index 00000000..a89b70d7 --- /dev/null +++ b/src/lib/y2s390/base_collection.rb @@ -0,0 +1,88 @@ +# Copyright (c) [2021] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "forwardable" + +module Y2S390 + # Base class for collection of config elements (e.g., {Dasd}). + class BaseCollection + extend Forwardable + + def_delegators :@elements, :each, :each_with_index, :select, :find, :reject, :map, + :any?, :size, :empty?, :first + + # Constructor + # + # @param elements [Array] + def initialize(elements = []) + @elements = elements + end + + # Adds an element to the collection + # + # @param element [Object] + # @return [self] + def add(element) + @elements << element + + self + end + + # Deletes the element with the given id from the collection + # + # @param id [String] + # @return [self] + def delete(id) + @elements.reject! { |e| e.id == id } + + self + end + + # List with all the elements + # + # @return [Array] + def all + @elements.dup + end + + alias_method :to_a, :all + + # Element with the given id + # + # @return [Object, nil] nil if the collection does not include an element with + # such an id. + def by_id(value) + @elements.find { |e| e.id == value } + end + + # Elements included in the given id's list + # + # @return [BaseCollection] A new collection with the elements included in the list given + def by_ids(ids) + self.class.new(@elements.select { |d| ids.include?(d.id) }) + end + + # All element ids + # + # @return [Array] + def ids + map(&:id) + end + end +end diff --git a/src/lib/y2s390/dasd.rb b/src/lib/y2s390/dasd.rb new file mode 100644 index 00000000..e56fbc57 --- /dev/null +++ b/src/lib/y2s390/dasd.rb @@ -0,0 +1,170 @@ +# Copyright (c) [2021] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "yast2/execute" +require "y2s390/formatting_status" + +module Y2S390 + # This class represents a direct-access storage device (DASD) + class Dasd + # Command for configuring z Systems specific devices + CONFIGURE_CMD = "/sbin/dasd_configure".freeze + # # Command for displaying configuration of z Systems DASD devices + LIST_CMD = "/sbin/lsdasd".freeze + + # @return [String] dasd type (EKCD, FBA) + attr_accessor :type + + # @return [String] dasd device type, cpu model... + attr_accessor :device_type + + # @return [String] the device id or channel + attr_accessor :id + + # @return [String, nil) associated device name + attr_accessor :device_name + + # @return [Symbol] device status (:offline, :active) + attr_reader :status + + # @return [Integer] number of cylinders + attr_accessor :cylinders + + # @return [FormattingStatus] status of the last formatting process + attr_accessor :formatting_status + + # @return [Boolean] + attr_accessor :format_wanted + + # @return [Boolean] + attr_accessor :diag_wanted + + # @return [Boolean] + attr_accessor :formatted + + # @return [Boolean] + attr_accessor :use_diag + + KNOWN_STATUS = { + "offline" => :offline, "active" => :active, "active(ro)" => :read_only, "n/f" => :no_format + }.freeze + + # Constructor + # + # @param id [String] + # @param status [String] + # @param device_name [String] + # @param type [String] + def initialize(id, status: nil, device_name: nil, type: nil) + @id = id + @device_name = device_name + @type = type + self.status = status + end + + def hex_id + @id.gsub(".", "").hex + end + + # Sets the device status if known or :unknown if not + # + # @param value [String] device current status according to lsdasd output + # @return [Symbol] device status (:active, :read_only, :offline or :unknown) + def status=(value) + @status = KNOWN_STATUS[value.to_s.downcase] || :unknown + end + + # @return [Boolean] whether the DASD device is active or not + def active? + status == :active || status == :read_only + end + + # @return [Boolean] whether the DASD device is formatted or not + def offline? + status == :offline + end + + # @return [Boolean] whether the DASD device is formatted or not + def formatted? + @formatted || false + end + + # Return the partitions information + # + # @return [String] + def partition_info + return "/dev/#{device_name}1" if type != "ECKD" + + out = Yast::Execute.stdout.on_target!("/sbin/fdasd", "-p", "/dev/#{device_name}") + return out if out.empty? + + regexp = Regexp.new("^[ \t]*([^ \t]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)" \ + "[ \t]+([^ \t]+)[ \t]+([^ \t]+([ \t]+[^ \t]+))*[ \t]*$") + + lines = out.split("\n").select { |s| s.match?(regexp) } + lines.map { |line| r = line.match(regexp); "#{r[1]} (#{r[6]})" }.join(", ") + end + + def hwinfo + Y2S390::HwinfoReader.instance.for_device(id) + end + + # Returns whether the device can be formatted or not + # + # @return [Boolean] + def can_be_formatted? + active? && type == "ECKD" + end + + # Returns whether the device is active according to the IO hwinfo + # + # @return [Boolean] true if it is active; false otherwise + def io_active? + hwinfo&.resource&.io&.first&.active + end + + # Returns the access type ('rw', 'ro') according to the hwinfo + # + # @return [Boolean] true if it is active; false otherwise + def access_type + hwinfo&.resource&.io&.first&.mode + end + + # @return [Integer] + def sysfs_id + hwinfo&.sysfs_id + end + + def sys_device_name + cmd = ["ls", "/sys/bus/ccw/devices/#{id}/block/"] + disk = Yast::Execute.stdout.on_target!(cmd).strip + disk.to_s.empty? ? nil : "/dev/#{disk}" + end + + def refresh_data! + cmd = [LIST_CMD, id] + data = Yast::Execute.stdout.locally!(*cmd).split("\n").find { |l| l.start_with? /\d/ } + + return if data.to_s.empty? + + _id, @status, @device_name, _, @type, = data.split(" ") + end + end +end diff --git a/src/lib/y2s390/dasd_actions.rb b/src/lib/y2s390/dasd_actions.rb new file mode 100644 index 00000000..4631bc80 --- /dev/null +++ b/src/lib/y2s390/dasd_actions.rb @@ -0,0 +1,4 @@ +require "y2s390/dasd_actions/activate" +require "y2s390/dasd_actions/deactivate" +require "y2s390/dasd_actions/format" +require "y2s390/dasd_actions/diag" diff --git a/src/lib/y2s390/dasd_actions/activate.rb b/src/lib/y2s390/dasd_actions/activate.rb new file mode 100644 index 00000000..cb3d917c --- /dev/null +++ b/src/lib/y2s390/dasd_actions/activate.rb @@ -0,0 +1,61 @@ +require "y2s390/dasd_actions/base" + +module Y2S390 + module DasdActions + class Activate < Base + def run + unformatted_disks = [] + + selected.each do |dasd| + unformatted_disks << dasd if activate(dasd) == 8 # 8 means disk is not formatted + end + + # for autoinst, format unformatted disks later + if format_now?(unformatted_disks) + devices = unformatted_disks.each_with_object([]) { |d, arr| arr << d if device_for!(d) } + controller.FormatDisks(devices) + devices.each { |d| activate(d) } + end + + controller.ProbeDisks + + return true + end + + private + + # Convenience method for checking if the activated DASD without format need to be formatted + # now or not. + # + # @param unformatted_disks [Array] + def format_now?(unformatted_disks) + return false if auto_mode? || unformatted_disks.empty? + + popup = if unformatted_disks.size == 1 + format(_("Device %s is not formatted. Format device now?"), unformatted_disks[0]) + else + format(_("There are %s unformatted devices. Format them now?"), + unformatted_disks.size) + end + + Yast::Popup.ContinueCancel(popup) + end + + def device_for!(dasd) + # We need to set it before format the disk + name = dasd.device_name = dasd.sys_device_name + Yast::Popup.Error(format(_("Couldn't find device for channel %s."), dasd.id)) if !name + + name + end + + # Convenience method for activating a DASD device + # + # @param dasd [Dasd] device to be activated + # @return [Boolean] whether the device was activated or not + def activate(dasd) + controller.ActivateDisk(dasd.id, !!dasd.diag_wanted) + end + end + end +end diff --git a/src/lib/y2s390/dasd_actions/base.rb b/src/lib/y2s390/dasd_actions/base.rb new file mode 100644 index 00000000..591a0c25 --- /dev/null +++ b/src/lib/y2s390/dasd_actions/base.rb @@ -0,0 +1,39 @@ +require "yast" +require "abstract_method" + +Yast.import "DASDController" +Yast.import "Mode" +Yast.import "Popup" + +module Y2S390 + module DasdActions + class Base + include Yast::I18n + + # @return [Boolean] + abstract_method :run + attr_accessor :selected + + def initialize(selected) + textdomain "s390" + @selected = selected + end + + def self.run(selected) + new(selected).run + end + + def controller + Yast::DASDController + end + + def config_mode? + Yast::Mode.config + end + + def auto_mode? + Yast::Mode.autoinst + end + end + end +end diff --git a/src/lib/y2s390/dasd_actions/deactivate.rb b/src/lib/y2s390/dasd_actions/deactivate.rb new file mode 100644 index 00000000..26db206e --- /dev/null +++ b/src/lib/y2s390/dasd_actions/deactivate.rb @@ -0,0 +1,20 @@ +require "y2s390/dasd_actions/base" + +module Y2S390 + module DasdActions + class Deactivate < Base + def run + selected.each { |d| deactivate(d) } + controller.ProbeDisks + + return true + end + + private + + def deactivate(dasd) + controller.DeactivateDisk(dasd.id, dasd.diag_wanted) + end + end + end +end diff --git a/src/lib/y2s390/dasd_actions/diag.rb b/src/lib/y2s390/dasd_actions/diag.rb new file mode 100644 index 00000000..a82bfeb9 --- /dev/null +++ b/src/lib/y2s390/dasd_actions/diag.rb @@ -0,0 +1,38 @@ +require "y2s390/dasd_actions/base" + +module Y2S390 + module DasdActions + class Diag < Base + attr_accessor :use_diag + + def run + if Yast::Mode.config + selected.each { |dasd| dasd.diag_wanted = !!use_diag } + else + selected.each do |dasd| + controller.ActivateDiag(dasd.id, !!use_diag) if dasd.io_active? + dasd.diag_wanted = !!use_diag + end + + controller.ProbeDisks + end + + true + end + end + + class DiagOff < Diag + def initialize(selected) + super(selected) + @use_diag = false + end + end + + class DiagOn < Diag + def initialize(selected) + super(selected) + @use_diag = true + end + end + end +end diff --git a/src/lib/y2s390/dasd_actions/format.rb b/src/lib/y2s390/dasd_actions/format.rb new file mode 100644 index 00000000..cb7f9854 --- /dev/null +++ b/src/lib/y2s390/dasd_actions/format.rb @@ -0,0 +1,85 @@ +require "y2s390/dasd_actions/base" + +module Y2S390 + module DasdActions + class FormatOff < Base + def run + selected.each { |dasd| dasd.format_wanted = false } + return true + end + end + + class FormatOn < Base + def run + selected.each { |dasd| dasd.format_wanted = true } + return true + end + end + + class Format < Base + def run + return false unless can_be_formatted? + return false unless really_format? + + controller.FormatDisks(selected) + + # We used to explicitly activate the DASD devices here, don't do + # it - see bsc#1187012. + + controller.ProbeDisks + + true + end + + private + + def can_be_formatted? + # check if disks are R/W and active + problem = nil + + selected.each do |dasd| + if !dasd.io_active? + # error report, %s is device identification + problem = format(_("Disk %s is not active."), dasd.id) + elsif dasd.access_type != "rw" + # error report, %s is device identification + problem = format(_("Disk %s is not accessible for writing."), dasd.id) + elsif !dasd.can_be_formatted? + problem = + # TRANSLATORS %s is device idetification + format(_("Disk %s cannot be formatted. Only ECKD disks can be formatted."), dasd.id) + end + end + + if problem + Yast::Popup.Message(problem) + return false + end + + true + end + + def really_format? + channels_str = selected.map(&:id).join(", ") + + if !Yast::Popup.AnyQuestionRichText( + Yast::Popup.NoHeadline, + # popup question + format( + _( + "Formatting these disks destroys all data on them.
\n" \ + "Really format the following disks?
\n" \ + "%s"), channels_str + ), + 60, + 20, + Yast::Label.YesButton, + Yast::Label.NoButton, + :focus_no + ) + return false + end + end + end + end +end diff --git a/src/lib/y2s390/dasds_collection.rb b/src/lib/y2s390/dasds_collection.rb new file mode 100644 index 00000000..c42bec0f --- /dev/null +++ b/src/lib/y2s390/dasds_collection.rb @@ -0,0 +1,13 @@ +require "y2s390/base_collection" + +module Y2S390 + class DasdsCollection < BaseCollection + def filter(&block) + self.class.new(@elements.select(&block)) + end + + def active + filter(&:active?) + end + end +end diff --git a/src/lib/y2s390/dasds_reader.rb b/src/lib/y2s390/dasds_reader.rb new file mode 100644 index 00000000..2a17d13a --- /dev/null +++ b/src/lib/y2s390/dasds_reader.rb @@ -0,0 +1,165 @@ +# Copyright (c) [2021] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2s390/dasd" +require "yast2/execute" +require "y2s390/dasds_collection" +require "y2s390/hwinfo_reader" + +Yast.import "Mode" + +module Y2S390 + # This class is reponsible of reading the information about DASD devices + class DasdsReader + attr_accessor :disks + + # Command for displaying configuration of z Systems DASD devices + LIST_CMD = "/sbin/lsdasd".freeze + + # Initializes a collection of DASDs based on the information read using lsdasd output and also + # the hardware information (hwinfo). + # + # @param offline [Boolean] whether it should obtain the offline devices too or not + # @param force_probing [Boolean] in case of probing it will fetch the hwinfo if not will use the + # information cached when exist + # @return [Array] a collection of Dasds read from the system using a + # comination + def list(offline: true, force_probing: false) + HwinfoReader.instance.reset if force_probing + + a = dasd_entries(offline: offline).each_with_object([]) do |entry, arr| + next unless entry.start_with?(/\d/) + + id, offline, name, _, type, = entry.split(" ") + attrs = Yast::Mode.config ? {} : { status: offline, device_name: name, type: type } + dasd = Y2S390::Dasd.new(id, **attrs).tap do |d| + if Yast::Mode.config + d.diag = d.use_diag = use_diag?(d) + d.format = false + else + update_additional_info(d) + end + end + + arr << dasd + end + + Y2S390::DasdsCollection.new(a) + end + + def update_info(dasd, extended: false) + data = dasd_entries(dasd: dasd).find { |e| e.start_with?(/\d/) } + return false if data.to_s.empty? + + _, status, name, _, type, = data.split(" ") + dasd.status = status + dasd.device_name = name + dasd.type = type + update_additional_info(dasd) if extended + end + + def update_additional_info(dasd) + refresh_extended_data!(dasd) + end + + private + + # In production, call SCR.Read(.probe.disk). + # For testing, point YAST2_S390_PROBE_DISK to a YAML file + # with the mock value. + # Suggesstion: + # YAST2_S390_PROBE_DISK=test/data/probe_disk_dasd.yml rake run"[dasd]" + # @return [Array] .probe.disk output + def dasd_entries(offline: true, dasd: nil) + if ENV["S390_MOCKING"] + File.read("test/data/lsdasd.txt") + else + cmd = cmd_for(offline: offline) + Yast::Execute.stdout.locally!(cmd) + end.split("\n") + end + + def cmd_for(offline: true, dasd: nil) + cmd = [LIST_CMD] + cmd << "-a" if offline + cmd << dasd if dasd + cmd + end + + def refresh_extended_data!(dasd) + reset_extended_data!(dasd) if dasd.offline? + dasd.use_diag = use_diag?(dasd) + dasd.formatted = formatted?(dasd) + dasd.device_type = device_type_for(dasd) + end + + def reset_extended_data!(dasd) + dasd.cylinders = nil + dasd.formatting_status = nil + end + + # Determines whether a given DASD is formatted or not + # + # @param dasd [DASD] + # @return [Boolean] + def formatted?(dasd) + return false if dasd.offline? + + fmt_out = Yast::Execute.stdout.locally!( + ["/sbin/dasdview", "--extended", "/dev/#{dasd.device_name}"], ["grep", "formatted"] + ) + fmt_out.empty? ? false : !fmt_out.upcase.match?(/NOT[ \t]*FORMATTED/) + end + + # Determines whether the given DASD has the DIAG access enabled or not + # + # @param dasd [DASD] + # @return [Boolean] + def use_diag?(dasd) + diag_file = "/sys/#{dasd.sysfs_id}/device/use_diag" + use_diag = Yast::SCR.Read(Yast.path(".target.string"), diag_file) if File.exist?(diag_file) + use_diag.to_i == 1 + end + + # Get partitioninfo + # @param [String] disk string Disk to read info from + # @return GetPartitionInfo string The info + def partition_info(dasd) + return "#{dasd.device_name}1" if dasd.type != "ECKD" + + out = Yast::Execute.stdout.on_target!("/sbin/fdasd", "-p", "/dev/#{dasd.device_name}") + return out if out.empty? + + regexp = Regexp.new("^[ \t]*([^ \t]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)" \ + "[ \t]+([^ \t]+)[ \t]+([^ \t]+([ \t]+[^ \t]+))*[ \t]*$") + + lines = out.split("\n").select { |s| s.match?(regexp) } + lines.map { |line| r = line.match(regexp); "#{r[1]} (#{r[6]})" }.join(", ") + end + + def device_type_for(dasd) + device_type = (dasd&.hwinfo&.device_id.to_i & 65535).to_s(16) + cu_model = dasd&.hwinfo&.detail&.cu_model.to_i.to_s(16).rjust(2, "0") + sub_device_id = (dasd&.hwinfo&.sub_device_id.to_i & 65535).to_s(16) + dev_model = dasd&.hwinfo&.detail&.dev_model.to_i.to_s(16).rjust(2, "0") + + "#{device_type}/#{cu_model} #{sub_device_id}/#{dev_model}".upcase + end + end +end diff --git a/src/lib/y2s390/format_process.rb b/src/lib/y2s390/format_process.rb new file mode 100644 index 00000000..327d06e3 --- /dev/null +++ b/src/lib/y2s390/format_process.rb @@ -0,0 +1,129 @@ +require "yast" +require "yast2/execute" + +module Y2S390 + class FormatStatus + attr_accessor :progress, :cylinders, :dasd + # Constructor + # + # @param dasd [Dasd] + # @param cylinders [Ingeger] + # @param format_size [Integer] + def initialize(dasd, cylinders, format_size = 10) + @dasd = dasd + @cylinders = cylinders + @progress = 0 + @size = format_size + end + + def update_progress + @progress += @size + end + + def done? + @cylinders <= @progress + end + + def step! + update_progress + end + end + + # This class is responsible of formatting a set of DASD volumes maintaining also the status of the + # progress. + class FormatProcess + include Yast::Logger + attr_accessor :id, :dasds, :summary, :updated + + SIZE = 10 + + # Constructor + # + # @param dasds [Integer] + def initialize(dasds) + @dasds = dasds + @summary = {} + @updated = {} + end + + def disks_params + dasds.map { |d| "-f /dev/#{d.device_name}" }.join(" ") + end + + # Convenience method to start with the formatting process + def start + cmd = "/sbin/dasdfmt -Y -P #{dasds.size} -b 4096 -y -r #{SIZE} -m #{SIZE} #{disks_params}" + + @id = Yast::SCR.Execute(Yast.path(".process.start_shell"), cmd) + end + + # Checks whether the formatting process is still running or not + # + # @return [Boolean] true when running; false otherwise + def running? + return false unless @id + + Yast::SCR.Read(Yast.path(".process.running"), @id) + end + + def read_line + return "" unless @id + + Yast::SCR.Read(Yast.path(".process.read_line"), @id) + end + + def read + Yast::SCR.Read(Yast.path(".process.read"), @id) + end + + def status + Yast::SCR.Read(Yast.path(".process.status"), @id) + end + + def error + stderr = "" + loop do + line = Yast::SCR.Read(Yast.path(".process.read_line_stderr")) + break unless line + + stderr << line + end + + stderr + end + + def initialize_summary + dasds.each_with_index { |d, i| @summary[i] = FormatStatus.new(d, read_line.to_i) } + end + + def update_summary + @updated = {} + + line = read + + return unless line + + log.info "Updating Summary" + progress = line.split("|") + progress.each do |d| + next if d.to_s.empty? + + summary[d.to_i]&.update_progress + @updated[d.to_i] = summary[d.to_i] + end + log.info("The summary is #{summary.inspect}") + log.info("Updated #{updated.inspect}") + + updated + end + + def cylinders + log.info("The summary is #{summary.values.inspect}") + summary.values.inject(0) { |sum, v| sum + v.cylinders } + end + + def progress + @summary.values.inject(0) { |sum, v| sum + v.progress } + end + end +end diff --git a/src/lib/y2s390/formatting_status.rb b/src/lib/y2s390/formatting_status.rb new file mode 100644 index 00000000..a351649b --- /dev/null +++ b/src/lib/y2s390/formatting_status.rb @@ -0,0 +1,44 @@ +module Y2S390 + class FormattingStatus + # @return [Symbol] + attr_accessor :status + # return [Integer, nil] + attr_accessor :cylinders + + # return [Integer] + attr_accessor :cylinders_formatted + + # return [String] + attr_accessor :error_details + + # Constructor + def initialize(cylinders = nil) + @cylinders = cylinders + end + + def format! + @cylinders_formatted = 0 + @status = :formatting + end + + def step!(cyl = 10) + return if done? + + @cylinders_formatted += cyl + done! if done? + end + + def done? + @cylinders_formatted >= @cylinders + end + + def done! + @status = :done + end + + def error!(error) + @status = :error + @error_details = error + end + end +end diff --git a/src/lib/y2s390/hwinfo_reader.rb b/src/lib/y2s390/hwinfo_reader.rb new file mode 100644 index 00000000..b54563c9 --- /dev/null +++ b/src/lib/y2s390/hwinfo_reader.rb @@ -0,0 +1,54 @@ +require "yaml" + +module Y2S390 + class HwinfoReader + include Singleton + + def for_device(id, force_probing: false) + reset if force_probing + + data[id] + end + + def reset + @data = nil + end + + def data + @data ||= data_from_hwinfo + end + + private + + def disks + if ENV["S390_MOCKING"] + load_data("test/data/probe_disk_dasd.yml") + else + Yast::SCR.Read(Yast.path(".probe.disk")) || [] + end + end + + def data_from_hwinfo + disks.each_with_object({}) do |disk, hash| + hw_data = struct_for(disk) + hash[hw_data.sysfs_bus_id] = hw_data + end + end + + def struct_for(hash_value) + hash_value.each_with_object(OpenStruct.new) do |(k, v), data| + value = if k == "io" && v.is_a?(Array) + v.first.is_a?(Hash) ? v.map { |e| struct_for(e) } : v + else + v.is_a?(Hash) ? struct_for(v) : v + end + + data.public_send("#{k}=", value) + end + end + + def load_data(name) + YAML.safe_load(File.read(name)) + end + end +end diff --git a/test/dasd_controller_test.rb b/test/dasd_controller_test.rb index c84ff073..18b7a74c 100755 --- a/test/dasd_controller_test.rb +++ b/test/dasd_controller_test.rb @@ -7,6 +7,20 @@ describe Yast::DASDController do subject { Yast::DASDController } + describe "#GetDeviceName" do + let(:channel) { "0.0.0150" } + let(:device_block_path) { "/sys/bus/ccw/devices/#{channel}/block/" } + + before do + allow(Yast::SCR).to receive(:Read).with(Yast.path(".target.dir"), device_block_path) + .once.and_return(["dasda"]) + end + + it "returns the associated device name" do + expect(subject.GetDeviceName(channel)).to eq("/dev/dasda") + end + end + describe "#ActivateDisk" do let(:exit_code) { 8 } let(:channel) { "0.0.0100" } @@ -436,7 +450,7 @@ end before do - allow(Yast::FileUtils).to receive(:Exists).and_return(false) + allow(File).to receive(:exist?).and_return(false) end it "is added to devices with formatted info" do @@ -454,7 +468,7 @@ let(:diag_path) { "/sys//class/block/dasda/device/use_diag" } before do - allow(Yast::FileUtils).to receive(:Exists).with(diag_path) + allow(File).to receive(:exist?).with(diag_path) .and_return(true) allow(Yast::SCR).to receive(:Read) .with(Yast::Path.new(".target.string"), diag_path) diff --git a/test/data/lsdasd.txt b/test/data/lsdasd.txt new file mode 100644 index 00000000..422b8f05 --- /dev/null +++ b/test/data/lsdasd.txt @@ -0,0 +1,10 @@ +Bus-ID Status Name Device Type BlkSz Size Blocks +================================================================================ +0.0.0190 offline +0.0.0191 offline +0.0.0192 offline +0.0.019d offline +0.0.0160 active dasda 94:0 ECKD 4096 21127MB 5408640 +0.0.0150 active dasdb 94:4 ECKD 4096 7042MB 1802880 +0.0.0592 active(ro) dasdc 94:8 ECKD 4096 168MB 43200 +0.0.019e active(ro) dasdd 94:12 ECKD 4096 351MB 90000 diff --git a/test/data/probe_disk.yml b/test/data/probe_disk.yml index 31ca9f3e..6419c8bc 100644 --- a/test/data/probe_disk.yml +++ b/test/data/probe_disk.yml @@ -129,3 +129,55 @@ sysfs_id: "/class/block/sdc" unique_key: _R2u.eVSycZhnQ60 vendor: IET +- bus: None + bus_hwcfg: none + class_id: 262 + detail: + cu_model: 233 + dev_model: 12 + lcss: 0 + dev_name: \"/dev/dasda\" + dev_names: + - "/dev/dasda" + - "/dev/disk/by-path/ccw-0.0.0160" + - "/dev/disk/by-id/ccw-IBM.750000000FRK11.1192.4b.00000001000075600000000000000000" + - "/dev/disk/by-id/ccw-0X0160" + - "/dev/disk/by-id/ccw-IBM.750000000FRK11.1192.4b" + dev_num: + major: 94 + minor: 0 + range: 4 + type: b + device: DASD + device_id: 276880 + driver: io_subchannel + drivers: + - active: true + modprobe: true + modules: + - - dasd_eckd_mod + - '' + model: IBM DASD + old_unique_key: N5EP.J4gQoPlOYuE + prog_if: 1 + resource: + disk_log_geo: + - cylinders: 30048 + heads: 15 + sectors: 12 + io: + - active: true + length: 1 + mode: rw + start: 352 + size: + - unit: sectors + x: 5408640 + y: 4096 + sub_class_id: 0 + sub_device_id: 275344 + sysfs_bus_id: 0.0.0160 + sysfs_id: "/class/block/dasda" + unique_key: 5czR.ALFATSt_U8F + vendor: IBM + vendor_id: 286721 diff --git a/test/y2s390/base_collection_examples.rb b/test/y2s390/base_collection_examples.rb new file mode 100644 index 00000000..967fba2f --- /dev/null +++ b/test/y2s390/base_collection_examples.rb @@ -0,0 +1,126 @@ +# Copyright (c) [2021] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2s390" + +shared_examples "config base collection" do + subject { described_class.new(elements) } + + let(:elements) { [element1, element2, element3] } + + let(:element1) { element_class.new("test1") } + + let(:element2) { element_class.new("test2") } + + let(:element3) { element_class.new("test3") } + + let(:element_class) { Y2S390::Dasd } + + describe "#add" do + let(:element) { element_class.new("test") } + + it "adds the given element to the collection" do + expect(subject.any? { |e| e.id == element.id }).to eq(false) + + size = subject.size + subject.add(element) + + expect(subject.size).to eq(size + 1) + expect(subject.any? { |e| e.id == element.id }).to eq(true) + end + + it "returns the collection" do + expect(subject.add(element)).to eq(subject) + end + end + + describe "#delete" do + context "if the collection includes an element with the given id" do + let(:id) { elements.first.id } + + it "deletes the element from the collection" do + expect(subject.any? { |e| e.id == id }).to eq(true) + + size = subject.size + subject.delete(id) + + expect(subject.size).to eq(size - 1) + expect(subject.any? { |e| e.id == id }).to eq(false) + end + + it "returns the collection" do + expect(subject.delete(id)).to eq(subject) + end + end + + context "if the collection does not include an element with the given id" do + let(:id) { element_class.new("test").id } + + it "does not modify the collection" do + size = subject.size + subject.delete(id) + + expect(subject.size).to eq(size) + end + + it "returns the collection" do + expect(subject.delete(id)).to eq(subject) + end + end + + end + + describe "#all" do + it "returns the list of elements" do + all = subject.all + + expect(all).to eq(elements) + end + end + + describe "#by_id" do + context "if the collection contains an element with the given id" do + let(:id) { element2.id } + + it "returns the element" do + result = subject.by_id(id) + + expect(result).to be_a(element_class) + expect(result.id).to eq(element2.id) + end + end + + context "if the collection does not contain an element with the given id" do + let(:id) { element_class.new("test").id } + + it "returns nil" do + expect(subject.by_id(id)).to be_nil + end + end + end + + describe "#ids" do + it "returns the ids of all the elements" do + ids = elements.map(&:id) + + expect(subject.ids).to contain_exactly(*ids) + end + end +end diff --git a/test/y2s390/dasd_actions/activate_test.rb b/test/y2s390/dasd_actions/activate_test.rb new file mode 100644 index 00000000..28e0a003 --- /dev/null +++ b/test/y2s390/dasd_actions/activate_test.rb @@ -0,0 +1,70 @@ +require_relative "../../test_helper.rb" +require "y2s390/dasd_actions/activate" + +describe Y2S390::DasdActions::Activate do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0160, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "offline") } + let(:dasd_0160) { Y2S390::Dasd.new("0.0.0160", status: "offline") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "offline") } + let(:controller) { Yast::DASDController } + + describe "#run" do + let(:format_now) { false } + + before do + allow(controller).to receive(:ProbeDisks) + allow(controller).to receive(:FormatDisks) + allow(action).to receive(:format_now?).and_return(format_now) + allow(action).to receive(:activate) + end + + it "iterates over the the selected DASDs trying to active them" do + expect(action).to receive(:activate).with(dasd_0150) + expect(action).to receive(:activate).with(dasd_0160) + expect(action).to receive(:activate).with(dasd_0fff) + + action.run + end + + context "when some of the the activated DASD is unformatted" do + let(:to_format) { [dasd_0150] } + + before do + allow(action).to receive(:activate).with(dasd_0150).and_return(8) + allow(action).to receive(:activate).with(dasd_0160).and_return(0) + allow(action).to receive(:activate).with(dasd_0fff).and_return(0) + end + + it "asks for formatting the unformatted devices" do + expect(action).to receive(:format_now?).with(to_format) + action.run + end + + context "when the user approve to format the unformatted disks" do + let(:format_now) { true } + + before do + allow(action).to receive(:device_for!).with(dasd_0150).and_return("dasda") + end + + it "formats the unformatted disks wich has a device name and activates them" do + expect(controller).to receive(:FormatDisks).with(to_format) + expect(action).to receive(:activate).with(dasd_0150) + action.run + end + end + + context "when the user does not approve to format the unformatted disks" do + it "reloads the DASDs configuration forcing a probe of the disks" do + expect(controller).to receive(:ProbeDisks) + action.run + end + end + + it "returns true" do + expect(action.run).to eql(true) + end + end + end +end diff --git a/test/y2s390/dasd_actions/deactivate_test.rb b/test/y2s390/dasd_actions/deactivate_test.rb new file mode 100644 index 00000000..13017c89 --- /dev/null +++ b/test/y2s390/dasd_actions/deactivate_test.rb @@ -0,0 +1,35 @@ +require_relative "../../test_helper.rb" +require "y2s390/dasd_actions/deactivate" + +describe Y2S390::DasdActions::Deactivate do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "offline") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "offline") } + let(:controller) { Yast::DASDController } + + describe "#run" do + let(:format_now) { false } + + before do + allow(controller).to receive(:ProbeDisks) + allow(action).to receive(:deactivate) + end + + it "iterates over the the selected DASDs deactivating them" do + expect(action).to receive(:deactivate).with(dasd_0150) + expect(action).to receive(:deactivate).with(dasd_0fff) + + action.run + end + + it "reloads the DASDs configuration forcing a probe of the disks" do + expect(controller).to receive(:ProbeDisks) + action.run + end + + it "returns true" do + expect(action.run).to eql(true) + end + end +end diff --git a/test/y2s390/dasd_actions/diag_test.rb b/test/y2s390/dasd_actions/diag_test.rb new file mode 100644 index 00000000..90eb958b --- /dev/null +++ b/test/y2s390/dasd_actions/diag_test.rb @@ -0,0 +1,116 @@ +require_relative "../../test_helper.rb" +require "y2s390/dasd_actions/diag" + +describe Y2S390::DasdActions::DiagOn do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "offline") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "offline") } + let(:controller) { Yast::DASDController } + + describe "#run" do + let(:format_now) { false } + let(:config_mode) { false } + + before do + allow(controller).to receive(:ProbeDisks) + allow(Yast::Mode).to receive(:config).and_return(config_mode) + allow(dasd_0150).to receive(:io_active?).and_return(false) + allow(dasd_0fff).to receive(:io_active?).and_return(false) + end + + context "when running in config Mode" do + let(:config_mode) { true } + + it "iterates over the the selected DASDs setting the diag wanted to true" do + expect { action.run }.to change { dasd_0150.diag_wanted }.from(nil).to(true) + .and change { dasd_0fff.diag_wanted }.from(nil).to(true) + end + end + + context "when not running in config Mode" do + it "iterates over the selected DASDs setting the diag wanted to true" do + expect { action.run }.to change { dasd_0150.diag_wanted }.from(nil).to(true) + .and change { dasd_0fff.diag_wanted }.from(nil).to(true) + end + + context "when the DASD is active according to sysfs" do + before do + allow(dasd_0150).to receive(:io_active?).and_return(true) + end + + it "activates the DIAG" do + expect(controller).to receive(:ActivateDiag).with("0.0.0150", true) + + action.run + end + end + + it "reloads the DASDs configuration forcing a probe of the disks" do + expect(controller).to receive(:ProbeDisks) + action.run + end + end + + it "returns true" do + expect(action.run).to eql(true) + end + end +end + +describe Y2S390::DasdActions::DiagOff do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "offline") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "offline") } + let(:controller) { Yast::DASDController } + + describe "#run" do + let(:format_now) { false } + let(:config_mode) { false } + + before do + allow(controller).to receive(:ProbeDisks) + allow(Yast::Mode).to receive(:config).and_return(config_mode) + allow(dasd_0150).to receive(:io_active?).and_return(false) + allow(dasd_0fff).to receive(:io_active?).and_return(false) + end + + context "when running in config Mode" do + let(:config_mode) { true } + + it "iterates over the the selected DASDs setting the diag wanted to false" do + expect { action.run }.to change { dasd_0150.diag_wanted }.from(nil).to(false) + .and change { dasd_0fff.diag_wanted }.from(nil).to(false) + end + end + + context "when not running in config Mode" do + it "iterates over the selected DASDs setting the diag wanted to false" do + expect { action.run }.to change { dasd_0150.diag_wanted }.from(nil).to(false) + .and change { dasd_0fff.diag_wanted }.from(nil).to(false) + end + + context "when the DASD is active according to sysfs" do + before do + allow(dasd_0150).to receive(:io_active?).and_return(true) + end + + it "deactivates the DIAG" do + expect(controller).to receive(:ActivateDiag).with("0.0.0150", false) + + action.run + end + end + + it "reloads the DASDs configuration forcing a probe of the disks" do + expect(controller).to receive(:ProbeDisks) + action.run + end + end + + it "returns true" do + expect(action.run).to eql(true) + end + end +end diff --git a/test/y2s390/dasd_actions/format_test.rb b/test/y2s390/dasd_actions/format_test.rb new file mode 100644 index 00000000..f85d57ac --- /dev/null +++ b/test/y2s390/dasd_actions/format_test.rb @@ -0,0 +1,152 @@ +require_relative "../../test_helper.rb" +require "y2s390/dasd_actions/format" + +describe Y2S390::DasdActions::Format do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "active", type: "ECKD") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "active(ro)", type: "FBA") } + let(:controller) { Yast::DASDController } + + describe "#run" do + let(:format_now) { false } + let(:config_mode) { false } + let(:io_active) { false } + + before do + allow(controller).to receive(:ProbeDisks) + allow(controller).to receive(:FormatDisks) + allow(Yast::Mode).to receive(:config).and_return(config_mode) + allow(dasd_0150).to receive(:io_active?).and_return(io_active) + allow(dasd_0fff).to receive(:io_active?).and_return(io_active) + allow(dasd_0150).to receive(:access_type).and_return("rw") + allow(dasd_0fff).to receive(:access_type).and_return("ro") + end + + context "when at least one of the DASD devices is not active" do + it "reports the problem" do + expect(Yast::Popup).to receive(:Message).with(/Disk 0.0.0fff is not active/) + + action.run + end + + it "does not format any disk" do + expect(controller).to_not receive(:FormatDisks) + action.run + end + + it "returns false" do + expect(action.run).to eql(false) + end + end + + context "when at least one of the DASD devices is not accessible for writing" do + let(:io_active) { true } + + it "reports the problem" do + expect(Yast::Popup).to receive(:Message).with(/Disk 0.0.0fff is not accessible for writing/) + + action.run + end + + it "does not format any disk" do + expect(controller).to_not receive(:FormatDisks) + action.run + end + + it "returns false" do + expect(action.run).to eql(false) + end + end + + context "when at least one of the DASD devices is not a ECKD one" do + let(:io_active) { true } + before do + dasd_0fff.status = "active" + allow(dasd_0fff).to receive(:access_type).and_return("rw") + end + + it "reports the problem" do + expect(Yast::Popup).to receive(:Message).with(/Disk 0.0.0fff cannot be formatted/) + + action.run + end + + it "does not format any disk" do + expect(controller).to_not receive(:FormatDisks) + action.run + end + + it "returns false" do + expect(action.run).to eql(false) + end + end + + context "when all the DASDs can be formatted" do + before do + allow(action).to receive(:can_be_formatted?).and_return(true) + allow(action).to receive(:really_format?).and_return(true) + end + + it "asks the user to confirm the format of the selected disks" do + allow(action).to receive(:really_format?).and_call_original + expect(Yast::Popup).to receive(:AnyQuestionRichText) + action.run + end + + context "and the user accepts to procceed with the format of the selected disks" do + it "formats the selected DASDs" do + expect(controller).to receive(:FormatDisks).with(selected) + action.run + end + + it "reloads the DASDs configuration forcing a probe of the disks" do + expect(controller).to receive(:ProbeDisks) + action.run + end + + it "returns true" do + expect(action.run).to eql(true) + end + end + + context "and the user does not accept to procceed with the format of the selected disks" do + it "returns false" do + allow(action).to receive(:really_format?).and_return(false) + expect(action.run).to eql(false) + end + end + + end + end +end + +describe Y2S390::DasdActions::FormatOn do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "offline") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "offline") } + let(:controller) { Yast::DASDController } + + describe "#run" do + it "iterates over the the selected DASDs setting the format wanted to true" do + expect { action.run }.to change { dasd_0150.format_wanted }.from(nil).to(true) + .and change { dasd_0fff.format_wanted }.from(nil).to(true) + end + end +end + +describe Y2S390::DasdActions::FormatOff do + let(:action) { described_class.new(selected) } + let(:selected) { Y2S390::DasdsCollection.new([dasd_0150, dasd_0fff]) } + let(:dasd_0150) { Y2S390::Dasd.new("0.0.0150", status: "offline") } + let(:dasd_0fff) { Y2S390::Dasd.new("0.0.0fff", status: "offline") } + let(:controller) { Yast::DASDController } + + describe "#run" do + it "iterates over the the selected DASDs setting the format wanted to true" do + expect { action.run }.to change { dasd_0150.format_wanted }.from(nil).to(false) + .and change { dasd_0fff.format_wanted }.from(nil).to(false) + end + end +end diff --git a/test/y2s390/dasd_test.rb b/test/y2s390/dasd_test.rb new file mode 100644 index 00000000..cfe0d7a4 --- /dev/null +++ b/test/y2s390/dasd_test.rb @@ -0,0 +1,72 @@ +require_relative "../test_helper" +require "y2s390" + +describe Y2S390::Dasd do + subject { described_class.new("0.0.0150") } + let(:dasda) { described_class.new("0.0.0150", status: "active", device_name: "dasda") } + + describe "#hex_id" do + it "returns an integer representation of the channel ID" do + expect(subject.hex_id).to be_a(Integer) + expect(subject.hex_id).to eql("000150".hex) + end + end + + describe "#active?" do + it "returns true if the DASD status is :active or :read_only" do + expect(dasda.active?).to eql(true) + end + + it "returns false if it is offline" do + expect(subject.active?).to eql(false) + end + end + + describe "#offline?" do + subject { described_class.new("0.0.0190", status: "offline") } + + it "returns true if the DASD status is offline" do + expect(subject.offline?).to eql(true) + end + + it "returns false if the DASD status is not offline" do + expect(dasda.offline?).to eql(false) + end + end + + describe "#status=" do + context "when given a known status" do + it "sets the corresponding one" do + expect { subject.status = "offline" }.to change { subject.status } + .from(:unknown).to(:offline) + expect { subject.status = "active" }.to change { subject.status } + .from(:offline).to(:active) + expect { subject.status = "active(ro)" }.to change { subject.status } + .from(:active).to(:read_only) + end + end + + context "when the given status is not known" do + subject { described_class.new("0.0.0190", status: "offline") } + + it "sets the status as :unknown" do + expect { subject.status = "another" }.to change {subject.status} + .from(:offline).to(:unknown) + end + end + end + + describe "#formatted?" do + it "returns true if the DASD device is formmated according to internal state" do + subject.formatted = true + expect(subject.formatted?).to eql(true) + end + + it "returns false otherwise" do + subject.formatted = nil + expect(subject.formatted?).to eql(false) + subject.formatted = false + expect(subject.formatted?).to eql(false) + end + end +end diff --git a/test/y2s390/dasds_collection_test.rb b/test/y2s390/dasds_collection_test.rb new file mode 100644 index 00000000..1174d7e1 --- /dev/null +++ b/test/y2s390/dasds_collection_test.rb @@ -0,0 +1,32 @@ +require_relative "../test_helper" +require_relative "base_collection_examples" +require "y2s390" + +describe Y2S390::DasdsCollection do + include_examples "config base collection" + + describe "#filter" do + let(:elements) { [element1, element2, element3] } + let(:element1) { Y2S390::Dasd.new("0.0.0150", status: "active", type: "EKCD") } + let(:element2) { Y2S390::Dasd.new("0.0.0160", status: "offline", type: "EKCD") } + let(:element3) { Y2S390::Dasd.new("0.0.0190", status: "active(ro)", type: "EKCD") } + + it "returns a new collection with the elements which the block returns true" do + expect(subject.size).to eq(3) + collection = subject.filter { |d| d.offline? } + expect(collection.size).to eq(1) + expect(collection.by_id("0.0.0160")).to eq(element2) + end + end + + describe "#active" do + let(:elements) { [element1, element2, element3] } + let(:element1) { Y2S390::Dasd.new("0.0.0150", status: "active", type: "EKCD") } + let(:element2) { Y2S390::Dasd.new("0.0.0160", status: "offline", type: "EKCD") } + let(:element3) { Y2S390::Dasd.new("0.0.0190", status: "active(ro)", type: "EKCD") } + + it "returns a new collection with only the active elements" do + expect(subject.active.ids).to eql(["0.0.0150", "0.0.0190"]) + end + end +end diff --git a/test/y2s390/dasds_reader_test.rb b/test/y2s390/dasds_reader_test.rb new file mode 100644 index 00000000..3769b2e7 --- /dev/null +++ b/test/y2s390/dasds_reader_test.rb @@ -0,0 +1,59 @@ +require_relative "../test_helper" + +require "y2s390/dasds_reader" + +describe Y2S390::DasdsReader do + let(:reader) { described_class.new } + let(:config_mode) { true } + let(:execute) { instance_double("Yast::Execute", on_target: true) } + + before do + allow(reader).to receive(:dasd_entries).and_return(load_file("lsdasd.txt").split("\n")) + allow(ENV).to receive(:[]).with('S390_MOCKING').and_return(true) + end + + describe "#list" do + before do + allow(Yast::Mode).to receive(:config).and_return(config_mode) + end + + it "reads and parses the DASD devices using lsdasd" do + allow(ENV).to receive(:[]).with('S390_MOCKING').and_return(false) + allow(Yast::Execute).to receive(:stdout).and_return(execute) + expect(reader).to receive(:dasd_entries).and_call_original + expect(execute).to receive(:locally!).with(["/sbin/lsdasd", "-a"]).and_return("") + + reader.list + end + + it "returns a collection of DASD devices" do + expect(reader.list).to be_a(Y2S390::DasdsCollection) + end + + + context "when called in 'normal' Mode" do + let(:config_mode) { false } + + it "it fetchs all the DASD information" do + devices = reader.list + dasd = devices.by_id("0.0.0160") + expect(dasd.type).to eq("ECKD") + expect(dasd.device_name).to eq("dasda") + expect(dasd.offline?).to eq(false) + expect(dasd.device_type).to eq("3990/E9 3390/0A") + end + end + + context "when called in 'config' Mode" do + it "it fetchs only the 'id' and 'diag' information and initializes 'format' as false" do + expect(reader).to_not receive(:update_additional_info) + devices = reader.list + dasd = devices.by_id("0.0.0150") + expect(dasd.format).to eq(false) + expect(dasd.use_diag).to eq(false) + expect(dasd.type).to be_nil + expect(dasd.device_name).to be_nil + end + end + end +end