Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-agama-yast for openSUSE:Factory checked in at 2026-03-18 16:49:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-agama-yast (Old) and /work/SRC/openSUSE:Factory/.rubygem-agama-yast.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-agama-yast" Wed Mar 18 16:49:21 2026 rev:39 rq:1340719 version:19 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-agama-yast/rubygem-agama-yast.changes 2026-03-11 20:50:27.813961527 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-agama-yast.new.8177/rubygem-agama-yast.changes 2026-03-18 16:49:57.679661846 +0100 @@ -1,0 +2,28 @@ +Tue Mar 17 12:15:21 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Version 19 + +------------------------------------------------------------------- +Mon Mar 16 16:54:19 UTC 2026 - José Iván López González <[email protected]> + +- Report an error if storage needs to be probed meanwhile DASD is + formatting (related to bsc#1259354). + +------------------------------------------------------------------- +Fri Mar 13 15:50:31 UTC 2026 - Ancor Gonzalez Sosa <[email protected]> + +- Fix the id reported to identify the encryption method used for + TPM-based unlocking (bsc#1258486). + +------------------------------------------------------------------- +Thu Mar 12 13:03:36 UTC 2026 - Imobach Gonzalez Sosa <[email protected]> + +- Add errors to the output of agama-autoyast (related to bsc#1259434). + +------------------------------------------------------------------- +Thu Mar 12 08:35:23 UTC 2026 - Ancor Gonzalez Sosa <[email protected]> + +- Infer the boot device, if omitted in the user configuration, when + reusing a pre-existing LVM or RAID (bsc#1248504). + +------------------------------------------------------------------- Old: ---- agama-yast-19.pre.devel1979.15594b321.gem New: ---- agama-yast-19.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ agama-yast.spec ++++++ --- /var/tmp/diff_new_pack.krIJXX/_old 2026-03-18 16:49:58.279686980 +0100 +++ /var/tmp/diff_new_pack.krIJXX/_new 2026-03-18 16:49:58.279686980 +0100 @@ -17,7 +17,7 @@ Name: agama-yast -Version: 19.pre.devel1979.15594b321 +Version: 19 Release: 0 %define mod_name agama-yast %define mod_full_name %{mod_name}-%{version} ++++++ rubygem-agama-yast.spec ++++++ --- /var/tmp/diff_new_pack.krIJXX/_old 2026-03-18 16:49:58.319688656 +0100 +++ /var/tmp/diff_new_pack.krIJXX/_new 2026-03-18 16:49:58.323688823 +0100 @@ -24,7 +24,7 @@ # Name: rubygem-agama-yast -Version: 19.pre.devel1979.15594b321 +Version: 19 Release: 0 %define mod_name agama-yast %define mod_full_name %{mod_name}-%{version} ++++++ agama-yast-19.pre.devel1979.15594b321.gem -> agama-yast-19.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bin/agama-autoyast new/bin/agama-autoyast --- old/bin/agama-autoyast 1980-01-02 01:00:00.000000000 +0100 +++ new/bin/agama-autoyast 1980-01-02 01:00:00.000000000 +0100 @@ -51,10 +51,12 @@ warn "Did not convert the profile (canceled by the user)." exit 2 end -rescue Agama::Commands::CouldNotFetchProfile - warn "Could not fetch the AutoYaST profile." +rescue Agama::Commands::CouldNotFetchProfile => e + warn "Could not fetch the AutoYaST profile:\n\n" + warn e.full_message exit 3 -rescue Agama::Commands::CouldNotWriteAgamaConfig - warn "Could not write the Agama configuration." +rescue Agama::Commands::CouldNotWriteAgamaConfig => e + warn "Could not write the Agama configuration:\n\n" + warn e.full_message exit 4 end Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/autoyast/profile_reporter.rb new/lib/agama/autoyast/profile_reporter.rb --- old/lib/agama/autoyast/profile_reporter.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/autoyast/profile_reporter.rb 1980-01-02 01:00:00.000000000 +0100 @@ -62,7 +62,7 @@ ) questions_client.ask(question) do |answer| - answer == :continue + answer.action == :continue end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/autoyast/root_reader.rb new/lib/agama/autoyast/root_reader.rb --- old/lib/agama/autoyast/root_reader.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/autoyast/root_reader.rb 1980-01-02 01:00:00.000000000 +0100 @@ -38,11 +38,18 @@ root_user = config.users.find { |u| u.name == "root" } return {} unless root_user - hsh = { "password" => root_user.password.value.to_s } - hsh["hashedPassword"] = true if root_user.password.value.encrypted? + hsh = {} + password = root_user.password + + if password + hsh["password"] = password.value.to_s + hsh["hashedPassword"] = true if password.value.encrypted? + end + + hsh = hsh.merge(setup_ssh(root_user)) + + return {} if hsh.empty? - public_key = root_user.authorized_keys.first - hsh["sshPublicKey"] = public_key if public_key { "root" => hsh } end @@ -58,6 +65,17 @@ result = reader.read @config = result.config end + + def setup_ssh(root_user) + hsh = {} + + public_key = root_user.authorized_keys.first + + hsh["sshPublicKey"] = public_key if public_key + hsh["sshPublicKeys"] = root_user.authorized_keys unless root_user.authorized_keys.empty? + + hsh + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/autoyast/user_reader.rb new/lib/agama/autoyast/user_reader.rb --- old/lib/agama/autoyast/user_reader.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/autoyast/user_reader.rb 1980-01-02 01:00:00.000000000 +0100 @@ -37,13 +37,15 @@ user = config.users.find { |u| !u.system? && !u.root? } return {} unless user - hsh = { - "userName" => user.name, - "fullName" => user.gecos.first.to_s, - "password" => user.password.value.to_s - } + hsh = basic_user_info(user) - hsh["hashedPassword"] = true if user.password.value.encrypted? + password = user.password + if password + hsh["password"] = password.value.to_s + hsh["hashedPassword"] = true if password.value.encrypted? + end + + hsh["sshPublicKeys"] = user.authorized_keys unless user.authorized_keys.empty? { "user" => hsh } end @@ -60,6 +62,13 @@ result = reader.read @config = result.config end + + def basic_user_info(user) + { + "userName" => user.name, + "fullName" => user.gecos.first.to_s + } + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/dbus/storage/dasd.rb new/lib/agama/dbus/storage/dasd.rb --- old/lib/agama/dbus/storage/dasd.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/dbus/storage/dasd.rb 1980-01-02 01:00:00.000000000 +0100 @@ -37,11 +37,13 @@ private_constant :PATH # @param manager [Agama::Storage::DASD::Manager] + # @param task_runner [Agama::TaskRunner] # @param logger [Logger, nil] - def initialize(manager, logger: nil) + def initialize(manager, task_runner, logger: nil) textdomain "agama" super(PATH, logger: logger) @manager = manager + @task_runner = task_runner @serialized_system = serialize_system @serialized_config = serialize_config register_callbacks @@ -72,6 +74,7 @@ # Applies the given serialized DASD config. # # @todo Raise error if the config is not valid. + # @raise [Agama::TaskRunner::BusyError] If an async task is running, see {TaskRunner}. # # @param serialized_config [String] Serialized DASD config according to the JSON schema. def configure(serialized_config) @@ -83,7 +86,19 @@ # Do not configure if there is nothing to change. return if manager.configured?(config_json) - perform_configuration(config_json) + # The configuration could take long time (e.g., formatting devices). It is important to + # not block the service in order to make possible to attend other requests. + task_runner.async_run("Configure DASD") do + logger.info("Configuring DASD") + + start_progress(1, _("Configuring DASD")) + manager.configure(config_json) + + update_serialized_system + update_serialized_config + + finish_progress + end end private @@ -91,6 +106,9 @@ # @return [Agama::Storage::DASD::Manager] attr_reader :manager + # @return [Agama::TaskRunner] + attr_reader :task_runner + def register_callbacks on_progress_change { self.ProgressChanged(serialize_progress) } on_progress_finish { self.ProgressFinished } @@ -102,28 +120,6 @@ manager.on_format_finish { |process_status| self.FormatFinished(process_status.to_s) } end - # Performs the configuration process in a separate thread. - # - # The configuration could take long time (e.g., formatting devices). It is important to not - # block the service in order to make possible to attend other requests. - # - # @raise if there is an unfinished configuration. - # - # @param config_json [Hash] - def perform_configuration(config_json) - raise "Previous configuration is not finished yet" if @configuration_thread&.alive? - - logger.info("Configuring DASD") - - @configuration_thread = Thread.new do - start_progress(1, _("Configuring DASD")) - manager.configure(config_json) - update_serialized_system - update_serialized_config - finish_progress - end - end - # Updates the system info if needed. def update_serialized_system serialized_system = serialize_system diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/dbus/storage/manager.rb new/lib/agama/dbus/storage/manager.rb --- old/lib/agama/dbus/storage/manager.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/dbus/storage/manager.rb 1980-01-02 01:00:00.000000000 +0100 @@ -45,12 +45,14 @@ PATH = "/org/opensuse/Agama/Storage1" private_constant :PATH - # @param backend [Agama::Storage::Manager] + # @param manager [Agama::Storage::Manager] + # @param task_runner [Agama::TaskRunner] # @param logger [Logger, nil] - def initialize(backend, logger: nil) + def initialize(manager, task_runner, logger: nil) textdomain "agama" super(PATH, logger: logger) - @backend = backend + @manager = manager + @task_runner = task_runner @serialized_system = serialize_system @serialized_config = serialize_config @serialized_config_model = serialize_config_model @@ -71,7 +73,7 @@ dbus_method(:Install) { install } dbus_method(:Finish) { finish } dbus_method(:Umount) { umount } - dbus_method(:SetLocale, "in locale:s") { |locale| backend.configure_locale(locale) } + dbus_method(:SetLocale, "in locale:s") { |locale| manager.configure_locale(locale) } dbus_method( :SetConfig, "in serialized_product_config:s, in serialized_config:s" ) { |p, c| configure(p, c) } @@ -88,36 +90,44 @@ end # Implementation for the API method #Activate. + # + # @raise [Agama::TaskRunner::BusyError] If an async task is running, see {TaskRunner}. def activate - logger.info("Activating storage") + task_runner.run("Activate storage") do + logger.info("Activating storage") - start_progress(3, ACTIVATING_STEP) - backend.reset_activation if backend.activated? - backend.activate + start_progress(3, ACTIVATING_STEP) + manager.reset_activation if manager.activated? + manager.activate - next_progress_step(PROBING_STEP) - perform_probe + next_progress_step(PROBING_STEP) + perform_probe - next_progress_step(CONFIGURING_STEP) - configure_with_current + next_progress_step(CONFIGURING_STEP) + configure_with_current - finish_progress + finish_progress + end end # Implementation for the API method #Probe. + # + # @raise [Agama::TaskRunner::BusyError] If an async task is running, see {TaskRunner}. def probe - logger.info("Probing storage") + task_runner.run("Probe storage") do + logger.info("Probing storage") - start_progress(3, ACTIVATING_STEP) - backend.activate unless backend.activated? + start_progress(3, ACTIVATING_STEP) + manager.activate unless manager.activated? - next_progress_step(PROBING_STEP) - perform_probe + next_progress_step(PROBING_STEP) + perform_probe - next_progress_step(CONFIGURING_STEP) - configure_with_current + next_progress_step(CONFIGURING_STEP) + configure_with_current - finish_progress + finish_progress + end end # Configures storage. @@ -126,6 +136,8 @@ # { "storage": ... } or { "legacyAutoyastStorage": ... }. # # @raise If the config is not valid. + # @raise [Agama::TaskRunner::BusyError] If an async task is running and the system needs to + # be probed, see {TaskRunner}. # # @param serialized_product_config [String] Serialized product config. # @param serialized_config [String] Serialized storage config. @@ -134,24 +146,13 @@ config_json = JSON.parse(serialized_config, symbolize_names: true) # Do not configure if there is nothing to change. - return if backend.configured?(product_config_json, config_json) - - logger.info("Configuring storage") - product_config = Agama::Config.new(product_config_json) - backend.update_product_config(product_config) if backend.product_config != product_config + return if manager.configured?(product_config_json, config_json) - start_progress(3, ACTIVATING_STEP) - backend.activate unless backend.activated? + # It is safe to run the task if the system was already probed. + return configure_task(product_config_json, config_json) if manager.probed? - next_progress_step(PROBING_STEP) - backend.probe unless backend.probed? - - update_serialized_system - - next_progress_step(CONFIGURING_STEP) - calculate_proposal(config_json) - - finish_progress + # Prevent to probe the system if there is an async task running (e.g., formatting DASD). + task_runner.run("Configure storage") { configure_task(product_config_json, config_json) } end # Converts the given serialized config model to a config. @@ -184,13 +185,13 @@ # Implementation for the API method #Install. def install start_progress(3, _("Preparing bootloader proposal")) - backend.bootloader.configure + manager.bootloader.configure next_progress_step(_("Preparing the storage devices")) - backend.install + manager.install next_progress_step(_("Writing bootloader sysconfig")) - backend.bootloader.install + manager.bootloader.install finish_progress end @@ -198,14 +199,14 @@ # Implementation for the API method #Finish. def finish start_progress(1, _("Finishing installation")) - backend.finish + manager.finish finish_progress end # Implementation for the API method #Umount. def umount start_progress(1, _("Unmounting devices")) - backend.umount + manager.umount finish_progress end @@ -224,7 +225,7 @@ # @return [Integer] 0 success; 1 error def configure_bootloader(serialized_config) logger.info("Setting bootloader config: #{serialized_config}") - backend.bootloader.config.load_json(serialized_config) + manager.bootloader.config.load_json(serialized_config) # after loading config try to apply it, so proper packages can be requested # TODO: generate also new issue from configuration calculate_bootloader @@ -243,18 +244,45 @@ private_constant :CONFIGURING_STEP # @return [Agama::Storage::Manager] - attr_reader :backend + attr_reader :manager + + # @return [Agama::TaskRunner] + attr_reader :task_runner def register_progress_callbacks on_progress_change { self.ProgressChanged(serialize_progress) } on_progress_finish { self.ProgressFinished } end + # Performs the configuration task. + # + # @param product_config_json [Hash, nil] + # @param config_json [Hash, nil] + def configure_task(product_config_json, config_json) + logger.info("Configuring storage") + + product_config = Agama::Config.new(product_config_json) + manager.update_product_config(product_config) if manager.product_config != product_config + + start_progress(3, ACTIVATING_STEP) + manager.activate unless manager.activated? + + next_progress_step(PROBING_STEP) + manager.probe unless manager.probed? + + update_serialized_system + + next_progress_step(CONFIGURING_STEP) + calculate_proposal(config_json) + + finish_progress + end + # Probes storage and updates the associated info. # # @see #update_system_info def perform_probe - backend.probe + manager.probe update_serialized_system end @@ -264,7 +292,7 @@ def configure_with_current return unless proposal.storage_json - calculate_proposal(backend.config_json) + calculate_proposal(manager.config_json) # The storage proposal with the current settings is not explicitly requested. It is # automatically calculated as side effect of calling to probe or activate. All the # dependant steps has to be automatically done too, for example, reconfiguring bootloader. @@ -277,8 +305,8 @@ # # @param config_json [Hash, nil] def calculate_proposal(config_json = nil) - backend.configure(config_json) - backend.add_packages if backend.proposal.success? + manager.configure(config_json) + manager.add_packages if manager.proposal.success? update_serialized_config update_serialized_config_model @@ -289,7 +317,7 @@ # Performs the bootloader configuration applying the current config. def calculate_bootloader logger.info("Configuring bootloader") - backend.bootloader.configure + manager.bootloader.configure update_serialized_bootloader_config end @@ -353,7 +381,7 @@ # # @return [String] def serialize_system - return serialize_nil unless backend.probed? + return serialize_nil unless manager.probed? json = { devices: devices_json(:probed), @@ -390,7 +418,7 @@ # # @return [String] def serialize_proposal - return serialize_nil unless backend.proposal.success? + return serialize_nil unless manager.proposal.success? json = { devices: devices_json(:staging), @@ -403,14 +431,14 @@ # # @return [String] def serialize_issues - super(backend.issues) + super(manager.issues) end # Generates the serialized JSON of the bootloader config. # # @return [String] def serialize_bootloader_config - backend.bootloader.config.to_json + manager.bootloader.config.to_json end # Representation of the null JSON. @@ -438,7 +466,7 @@ # * :delete [Boolean] # * :resize [Boolean] def actions_json - backend.actions.map do |action| + manager.actions.map do |action| { device: action.device_sid, text: action.text, @@ -455,7 +483,7 @@ # # @return [Array<Hash>] def system_issues_json - backend.system_issues.map { |i| issue_json(i) } + manager.system_issues.map { |i| issue_json(i) } end # @see Storage::System#available_drives @@ -498,7 +526,7 @@ def encryption_methods Agama::Storage::EncryptionSettings .available_methods - .map { |m| m.id.to_s } + .map { |m| Agama::Storage::EncryptionSettings.method_id(m) } end # Default volumes to be used as templates @@ -515,12 +543,12 @@ # @return [Agama::Storage::Proposal] def proposal - backend.proposal + manager.proposal end # @return [Agama::Config] def product_config - backend.product_config + manager.product_config end # @return [Agama::VolumeTemplatesBuilder] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/dbus/storage_service.rb new/lib/agama/dbus/storage_service.rb --- old/lib/agama/dbus/storage_service.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/dbus/storage_service.rb 1980-01-02 01:00:00.000000000 +0100 @@ -25,6 +25,7 @@ require "agama/dbus/storage/manager" require "agama/storage/manager" require "agama/storage/iscsi/adapter" +require "agama/task_runner" require "yast" require "y2storage/inhibitors" @@ -112,7 +113,7 @@ # @return [Agama::DBus::Storage::Manager] def manager_object - @manager_object ||= Agama::DBus::Storage::Manager.new(manager, logger: logger) + @manager_object ||= Agama::DBus::Storage::Manager.new(manager, task_runner, logger: logger) end # @return [Agama::DBus::Storage::ISCSI] @@ -129,7 +130,7 @@ require "agama/storage/dasd/manager" require "agama/dbus/storage/dasd" manager = Agama::Storage::DASD::Manager.new(logger: logger) - @dasd_object = Agama::DBus::Storage::DASD.new(manager, logger: logger) + @dasd_object = Agama::DBus::Storage::DASD.new(manager, task_runner, logger: logger) end # @return [Agama::DBus::Storage::ZFCP, nil] @@ -148,6 +149,11 @@ def manager @manager ||= Agama::Storage::Manager.new(logger: logger) end + + # @return [Agama::TaskRunner] + def task_runner + @task_runner ||= Agama::TaskRunner.new + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/storage/config_solvers/boot.rb new/lib/agama/storage/config_solvers/boot.rb --- old/lib/agama/storage/config_solvers/boot.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/storage/config_solvers/boot.rb 1980-01-02 01:00:00.000000000 +0100 @@ -48,7 +48,12 @@ # @return [Storage::System] attr_reader :storage_system - # Finds a device for booting and sets its alias, if needed. + # Finds a device for booting. + # + # If there is already an entry pointing to that device, it may set an alias for that config + # entry if needed. + # + # If there is no Drive or MdRaid entry, it may add it to the config. # # A boot device cannot be automatically inferred in the following scenarios: # * The root partition or logical volume is missing. @@ -69,12 +74,15 @@ # Config of the device used for allocating root, directly or indirectly. # - # The boot device has to be a partitioned drive. If root is not directly created as a - # partition of a drive (e.g., as logical volume, as partition of a MD RAID, etc), then the - # first partitioned drive used for allocating the device (physical volume or MD member - # device) is considered as boot device. + # The boot device has to be a partitioned drive or hardware RAID. If root is not directly + # created as a partition of a drive (e.g., as logical volume, as partition of a MD RAID, + # etc), then the first partitioned drive used for allocating the device (physical volume + # or MD member device) is considered as boot device. + # + # The boot device is recursively searched until reaching a drive or a hardware RAID. # - # The boot device is recursively searched until reaching a drive. + # For reused LVMs or RAIDs, the result may be a new config entry created to point to the + # appropriate boot device. # # @return [Configs::Drive, Configs::MdRaid, nil] nil if the boot device cannot be inferred # from the config. @@ -130,14 +138,14 @@ # Recursively looks for the first partitioned config from the given MD RAID. # + # If no config is found, it may create and return a new Drive or MdRaid config. + # # @param md_raid [Configs::MdRaid] # @return [Configs::Drive, Configs::MdRaid, nil] def partitionable_from_found_md_raid(md_raid) return md_raid if storage_system.candidate?(md_raid.found_device) - # TODO: find the correct underlying disk devices for the MD RAID (note they may lack - # a corresponding drive entry at the configuration) - nil + partitionable_from_found(md_raid.found_device) end # Recursively looks for the first partitioned drive from the given MD RAID. @@ -151,9 +159,20 @@ # Recursively looks for the first partitioned config from the given volume group. # + # If no config is found, it may create and return a new Drive or MdRaid config. + # # @param volume_group [Configs::VolumeGroup] # @return [Configs::Drive, Configs::MdRaid, nil] def partitionable_from_volume_group(volume_group) + partitionable_from_volume_group_pvs(volume_group) || + (volume_group.found_device && partitionable_from_found(volume_group.found_device)) + end + + # Recursively looks for the first partitioned config from the given volume group. + # + # @param volume_group [Configs::VolumeGroup] + # @return [Configs::Drive, Configs::MdRaid, nil] + def partitionable_from_volume_group_pvs(volume_group) pv_devices = find_devices(volume_group.physical_volumes_devices, is_target: true) pvs = find_devices(volume_group.physical_volumes) @@ -212,6 +231,94 @@ def find_volume_group(device_alias) config.volume_groups.find { |v| v.logical_volume?(device_alias) } end + + # Finds or creates a config pointing to the bootable device corresponding to the given + # RAID or volume group. + # + # @param device [Y2Storage::Md, Y2Storage::LvmVg] + # @return [Configs::Drive, Configs::MdRaid, nil] + def partitionable_from_found(device) + disks = bootable_devices(device) + return if disks.empty? + + config_entry(disks) + end + + # Finds all devices that could be used to boot into the given RAID or volume group + # + # @see #partitionable_from_found + # + # @param device [Y2Storage::Md, Y2Storage::LvmVg] + # @return [Array<Y2Storage::Partitionable>] + def bootable_devices(device) + device.ancestors.select do |dev| + dev.is?(:disk_device) && dev.partition_table? && storage_system.candidate?(dev) + end + end + + # @see #partitionable_from_found + # + # @param devices [Array<Y2Storage::Partitionable>] list of candidate RAIDs or disk devices + # @return [Configs::Drive, Configs::MdRaid] + def config_entry(devices) + find_config_entry(devices) || create_config_entry(devices) + end + + # Find the first entry in the current configuration that corresponds to any of the given + # devices + # + # @param devices [Array<Y2Storage::Partitionable>] list of candidate RAIDs or disk devices + # @return [Configs::Drive, Configs::MdRaid, nil] + def find_config_entry(devices) + sids = devices.map(&:sid) + raid = config.md_raids.find { |d| sids.include?(d.found_device&.sid) } + return raid if raid + + config.drives.find { |d| sids.include?(d.found_device.sid) } + end + + # Creates a new entry in the config to point to one of the given devices + # + # @param devices [Array<Y2Storage::Partitionable>] list of candidate RAIDs or disk devices + # @return [Configs::Drive, Configs::MdRaid] + def create_config_entry(devices) + device = preferred_device_to_create_entry(devices) + device.is?(:raid) ? create_raid_entry(device) : create_drive_entry(device) + end + + # @see #create_config_entry + # + # @param devices [Array<Y2Storage::Partitionable>] + # @return [Y2Storage::Partitionable] + def preferred_device_to_create_entry(devices) + devices = devices.select { |d| d.is?(:raid) } if devices.any? { |d| d.is?(:raid) } + devices.min_by(&:name) + end + + # @see #create_config_entry + # + # @param device [<Y2Storage::Partitionable>] disk device + # @return [Configs::Drive] + def create_drive_entry(device) + config.drives << Configs::Drive.new.tap do |drive| + drive.search.name = device.name + drive.search.solve(device) + end + config.drives.last + end + + # @see #create_config_entry + # + # @param device [<Y2Storage::Md>] RAID device + # @return [Configs::MdRaid] + def create_raid_entry(device) + config.md_raids << Configs::MdRaid.new.tap do |md| + md.search = Configs::Search.new + md.search.name = device.name + md.search.solve(device) + end + config.md_raids.last + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/storage/encryption_settings.rb new/lib/agama/storage/encryption_settings.rb --- old/lib/agama/storage/encryption_settings.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/agama/storage/encryption_settings.rb 1980-01-02 01:00:00.000000000 +0100 @@ -65,6 +65,20 @@ encryption_methods.reject { |m| m.respond_to?(:possible?) && !m.possible? } end + # Identifier used for the given encryption method in the system API. + # + # This method may disappear in the future after normalizing the ids for encryption methods. + # Those ids currently use inconsistent format (camelCase vs snake_case) and are not unified + # between the Agama configuration and the system API. + # + # @param method [Y2Storage::EncryptionMethod::Base] + # @return [String] + def self.method_id(method) + return "tpmFde" if method.is?(:tpm_fde) + + method.id.to_s + end + # Constructor def initialize # LUKS2 with PBKDF2 is the most sensible option nowadays: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/agama/task_runner.rb new/lib/agama/task_runner.rb --- old/lib/agama/task_runner.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/agama/task_runner.rb 1980-01-02 01:00:00.000000000 +0100 @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +# Copyright (c) [2026] 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. + +module Agama + # Class for running tasks in a sync or async way, preventing the execution of several tasks at the + # same time. + class TaskRunner + # Error when an async task is already running. + class BusyError < StandardError + def initialize(running_task = nil, requested_task = nil) + message = "Cannot start a new task while another is in progress: " \ + "requested: '#{requested_task || "unknown"}', " \ + "running: '#{running_task || "unknown"}'" + super(message) + end + end + + def initialize + @running_task = nil + @running_thread = nil + end + + # Runs the given block in a new thread. + # + # @raise [BusyError] If a previous async task is already running. + # + # @param task [String, nil] Description of the task to run. + # @param block [Proc] Code to run in a separate thread. + # @return [Thread] The new thread. + def async_run(task = nil, &block) + # Queue to safely communicate between threads. It is used to indicate to the main thread that + # the task has started. + ready = Queue.new + perform_run(task) do + @running_task = task + @running_thread = Thread.new do + ready.push(true) # Signaling to indicate the task has started. + block.call + end + end + ready.pop # Ensures the task has started. + @running_thread + end + + # Runs the given block in the main thread. + # + # @raise [BusyError] If a async task is running. + # + # @param task [String, nil] Description of the task to run. + # @param block [Proc] Code to run. + def run(task = nil, &block) + perform_run(task, &block) + end + + # Whether there is a task running in a separate thread. + # + # @return [Boolean] + def busy? + @running_thread&.alive? || false + end + + private + + # Runs the given block. + # + # @raise [BusyError] If a async task is running. + # + # @param task [String, nil] Description of the task to run. + # @param block [Proc] Code to run. + def perform_run(task = nil, &block) + raise BusyError.new(@running_task, task) if busy? + + block.call + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 1980-01-02 01:00:00.000000000 +0100 +++ new/metadata 1980-01-02 01:00:00.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: agama-yast version: !ruby/object:Gem::Version - version: 19.pre.devel1979.15594b321 + version: '19' platform: ruby authors: - YaST Team @@ -581,6 +581,7 @@ - lib/agama/storage/zfcp/controller.rb - lib/agama/storage/zfcp/device.rb - lib/agama/storage/zfcp/manager.rb +- lib/agama/task_runner.rb - lib/agama/with_locale.rb - lib/tasks/autoyast.rb - lib/y2storage/agama_proposal.rb ++++++ po.tar.bz2 ++++++
