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 ++++++

Reply via email to