diff --git a/package/yast2-registration.changes b/package/yast2-registration.changes
index 5ba595bb4..0acf7f17a 100644
--- a/package/yast2-registration.changes
+++ b/package/yast2-registration.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Tue Aug 25 13:54:46 UTC 2015 - lslezak@suse.cz
+
+- Handle not available products when using a SMT server for running
+ online migration (bsc#942892)
+
-------------------------------------------------------------------
Fri Aug 21 11:38:22 UTC 2015 - lslezak@suse.cz
diff --git a/src/lib/registration/ui/migration_selection_dialog.rb b/src/lib/registration/ui/migration_selection_dialog.rb
index 789ed49d7..16b68b874 100644
--- a/src/lib/registration/ui/migration_selection_dialog.rb
+++ b/src/lib/registration/ui/migration_selection_dialog.rb
@@ -17,6 +17,7 @@
require "yast"
require "registration/migration_sorter"
require "registration/sw_mgmt"
+require "registration/url_helpers"
module Registration
module UI
@@ -28,6 +29,8 @@ class MigrationSelectionDialog
Yast.import "UI"
Yast.import "Wizard"
+ Yast.import "Report"
+ Yast.import "HTML"
attr_accessor :selected_migration, :manual_repo_selection, :installed_products
@@ -71,18 +74,43 @@ def run
update_details
+ handle_user_input
+ end
+
+ private
+
+ attr_accessor :migrations
+
+ # the main loop for handling the user inut
+ # @return [Symbol] the UI symbol
+ def handle_user_input
loop do
ret = Yast::UI.UserInput
- update_details if ret == :migration_targets
- store_values if ret == :next
+
+ case ret
+ when :migration_targets
+ update_details
+ when :next
+ if valid_migration?
+ store_values
+ else
+ report_unavailable_migration
+ next
+ end
+ end
return ret if [:next, :back, :cancel, :abort].include?(ret)
end
end
- private
-
- attr_accessor :migrations
+ # is the current selected migration valid? (a migration is selected and
+ # all products are available)
+ # @return [Boolean] true if the migration can be used
+ def valid_migration?
+ current = current_migration
+ # available is nil (not set) or true
+ current && current.all? { |p| p.available.nil? || p.available }
+ end
def add_registered_addons
extra = Addon.registered_not_installed.map { |addon| SwMgmt.remote_product(addon) }
@@ -156,8 +184,31 @@ def migration_details(idx)
"
" + _("Migration Summary") + "
"
end
+ # create a product summary for the details widget
+ # @return [String] product summary
def product_summary(product, installed_product)
product_name = CGI.escapeHTML(product.friendly_name)
+ log.info "creating summary for #{product} and #{installed_product}"
+
+ # explicitly check for false, the flag is not returned by SCC, this is
+ # a SMT specific check (in SCC all products are implicitly available)
+ if product.available == false
+ # a product can be unavailable only when using SMT, the default
+ # SCC URL should be never used
+ url = UrlHelpers.registration_url || SUSE::Connect::YaST::DEFAULT_URL
+
+ # TRANSLATORS: An error message displayed in the migration details.
+ # The product has not been mirrored to the SMT server and cannot be used
+ # for migration. The SMT admin has to mirror the product to allow
+ # using the selected migration.
+ # %{url} is the URL of the registration server (SMT)
+ # %{product} is a full product name, e.g. "SUSE Linux Enterprise Server 12"
+ return Yast::HTML.Colorize(
+ _("ERROR: Product %{product} is not available at the " \
+ "registration server (%{url}). Make the product available " \
+ "to allow using this migration.") % { product: product_name,
+ url: url }, "red")
+ end
if !installed_product
# this is rather a theoretical case, but anyway....
@@ -166,46 +217,70 @@ def product_summary(product, installed_product)
return _("%s will be installed.") % product_name
end
- installed_version = installed_product["version_version"]
+ product_change_summary(installed_product, product)
+ end
- if installed_version == product.version
+ # create a summary for changed product
+ # @param [Hash] old_product the old installed libzypp product
+ # @param [OpenStruct] the new target product
+ # @return [String] RichText summary
+ def product_change_summary(old_product, new_product)
+ new_product_name = CGI.escapeHTML(new_product.friendly_name)
+ installed_version = old_product["version_version"]
+
+ if installed_version == new_product.version
# TRANSLATORS: Summary message, rich text format
# %s is a product name, e.g. "SUSE Linux Enterprise Server 12"
- return _("%s stays unchanged.") % product_name
+ return _("%s stays unchanged.") % new_product_name
end
- old_product_name = SwMgmt.product_label(installed_product)
+ old_product_name = SwMgmt.product_label(old_product)
# use Gem::Version for version compare
- if Gem::Version.new(installed_version) < Gem::Version.new(product.version)
+ if Gem::Version.new(installed_version) < Gem::Version.new(new_product.version)
# TRANSLATORS: Summary message, rich text format
# %{old_product} is a product name, e.g. "SUSE Linux Enterprise Server 12"
# %{new_product} is a product name, e.g. "SUSE Linux Enterprise Server 12 SP1 x86_64"
return _("%{old_product} will be upgraded to %{new_product}.") \
- % { old_product: old_product_name, new_product: product_name }
+ % { old_product: old_product_name, new_product: new_product_name }
else
# TRANSLATORS: Summary message, rich text format
# %{old_product} and %{new_product} are product names
return _("%{old_product} will be downgraded to %{new_product}.") \
- % { old_product: old_product_name, new_product: product_name }
+ % { old_product: old_product_name, new_product: new_product_name }
end
end
# store the current UI values
def store_values
- selected = Yast::UI.QueryWidget(:migration_targets, :CurrentItem)
- self.selected_migration = migrations[selected]
- log.info "Selected migration: #{selected_migration}"
-
+ self.selected_migration = current_migration
self.manual_repo_selection = Yast::UI.QueryWidget(:manual_repos, :Value)
end
+ # return the currently selected migration
+ # @return [Array] the selected migration target
+ def current_migration
+ current_item = Yast::UI.QueryWidget(:migration_targets, :CurrentItem)
+ migration = migrations[current_item]
+ log.info "Selected migration: #{migration}"
+ migration
+ end
+
def sorted_migrations
# sort the products in each migration
migrations.map do |migration|
migration.sort(&::Registration::MIGRATION_SORTER)
end
end
+
+ # display an error popup
+ def report_unavailable_migration
+ # TRANSLATORS: an error popup message
+ Yast::Report.Error(_("The selected migration contains a product\n" \
+ "which is not available at the registration server.\n\n" \
+ "Select a different migration target or make the missing products\n" \
+ "available at the registration server."))
+ end
end
end
end
diff --git a/test/migration_selection_dialog_test.rb b/test/migration_selection_dialog_test.rb
index f717cecbf..6a49ddb0a 100755
--- a/test/migration_selection_dialog_test.rb
+++ b/test/migration_selection_dialog_test.rb
@@ -29,10 +29,9 @@
end
it "saves the entered values when clicking Next" do
- # user pressed the "Abort" button
expect(Yast::UI).to receive(:UserInput).and_return(:next)
expect(Yast::UI).to receive(:QueryWidget).with(:migration_targets, :CurrentItem)
- .and_return(0).twice
+ .and_return(0).at_least(1)
expect(Yast::UI).to receive(:QueryWidget).with(:manual_repos, :Value).and_return(true)
dialog = subject.new(migration_products, [])
@@ -42,5 +41,36 @@
expect(dialog.selected_migration).to eq(migration_products.first)
expect(dialog.manual_repo_selection).to eq(true)
end
+
+ it "displays an error when the selected migration contains unavailable product" do
+ # user pressed the "Abort" button after displaying the error message
+ expect(Yast::UI).to receive(:UserInput).and_return(:next, :abort)
+ expect(Yast::UI).to receive(:QueryWidget).with(:migration_targets, :CurrentItem)
+ .and_return(0).at_least(1)
+ expect(Yast::Report).to receive(:Error)
+ .with(/is not available at the registration server/)
+ allow(Registration::UrlHelpers).to receive(:registration_url)
+ .and_return("http://example.com")
+
+ migrations = migration_products
+ # make one product not available
+ migrations.first.first.available = false
+
+ dialog = subject.new(migrations, [])
+ expect(dialog.run).to eq(:abort)
+ end
+
+ it "displays a product summary" do
+ expect(Yast::UI).to receive(:UserInput).and_return(:next)
+ expect(Yast::UI).to receive(:QueryWidget).with(:migration_targets, :CurrentItem)
+ .and_return(0).at_least(1)
+ expect(Yast::UI).to receive(:QueryWidget).with(:manual_repos, :Value).and_return(true)
+
+ # load just the SLES12 product from that file
+ installed = load_yaml_fixture("products_legacy_installation.yml")[1]
+
+ dialog = subject.new(migration_products, [installed])
+ expect(dialog.run).to eq(:next)
+ end
end
end