[yast-commit] [ci_new_pac] JFYI yast2-registration -> sle12
Script 'mail_helper' called by ro Hello packager, This is just FYI. Your package was checked in in distribution "sle12" by autobuild-member: ro. Here comes the log... ---------------------------%<------------------------------ Hi, here is the log from ci_new_pac /mounts/work_src_done/SLE12/yast2-registration -> sle12 Changes: -------- --- /work/SRC/SUSE:SLE-12:GA/yast2-registration/yast2-registration.changes 2014-03-10 16:14:03.000000000 +0100 +++ /mounts/work_src_done/SLE12/yast2-registration/yast2-registration.changes 2014-03-18 10:47:09.000000000 +0100 @@ -1,0 +2,7 @@ +Tue Mar 18 09:31:39 UTC 2014 - lslezak@suse.cz + +- Display and allow registering online add-ons from SCC during + installation (FATE#312925, FATE#315685) +- 3.1.19 + +------------------------------------------------------------------- calling whatdependson for sle12-i586 Packages directly triggered for rebuild: - yast2-registration ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/SUSE:SLE-12:GA/yast2-registration (Old) and /mounts/work_src_done/SLE12/yast2-registration (BS:build ID:34529 MAIL:yast-commit@opensuse.org) (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "yast2-registration", Maintainer is "yast-commit@opensuse.org" Old: ---- yast2-registration-3.1.18.tar.bz2 New: ---- yast2-registration-3.1.19.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-registration.spec ++++++ --- /var/tmp/diff_new_pack.hxjloI/_old 2014-03-18 13:07:43.000000000 +0100 +++ /var/tmp/diff_new_pack.hxjloI/_new 2014-03-18 13:07:43.000000000 +0100 @@ -17,7 +17,7 @@ Name: yast2-registration -Version: 3.1.18 +Version: 3.1.19 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -31,7 +31,7 @@ # N_() method Requires: yast2-ruby-bindings >= 3.1.12 # SCC API library -Requires: rubygem-scc_api >= 0.2.4 +Requires: rubygem-scc_api >= 0.2.7 Requires: yast2-slp >= 3.1.2 BuildRequires: yast2 >= 2.23.13 ++++++ yast2-registration-3.1.18.tar.bz2 -> yast2-registration-3.1.19.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/package/yast2-registration.changes new/yast2-registration-3.1.19/package/yast2-registration.changes --- old/yast2-registration-3.1.18/package/yast2-registration.changes 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/package/yast2-registration.changes 2014-03-18 10:44:23.000000000 +0100 @@ -1,4 +1,11 @@ ------------------------------------------------------------------- +Tue Mar 18 09:31:39 UTC 2014 - lslezak@suse.cz + +- Display and allow registering online add-ons from SCC during + installation (FATE#312925, FATE#315685) +- 3.1.19 + +------------------------------------------------------------------- Thu Mar 6 20:31:05 UTC 2014 - lslezak@suse.cz - display a popup for selecting/deselecting registered repositories diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/package/yast2-registration.spec new/yast2-registration-3.1.19/package/yast2-registration.spec --- old/yast2-registration-3.1.18/package/yast2-registration.spec 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/package/yast2-registration.spec 2014-03-18 10:44:23.000000000 +0100 @@ -17,7 +17,7 @@ Name: yast2-registration -Version: 3.1.18 +Version: 3.1.19 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -31,7 +31,7 @@ # N_() method Requires: yast2-ruby-bindings >= 3.1.12 # SCC API library -Requires: rubygem-scc_api >= 0.2.4 +Requires: rubygem-scc_api >= 0.2.7 Requires: yast2-slp >= 3.1.2 BuildRequires: yast2 >= 2.23.13 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/Makefile.am new/yast2-registration-3.1.19/src/Makefile.am --- old/yast2-registration-3.1.18/src/Makefile.am 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/src/Makefile.am 2014-03-18 10:44:23.000000000 +0100 @@ -11,8 +11,10 @@ ylibdir = @ylibdir@/registration ylib_DATA = \ + lib/registration/addon.rb \ lib/registration/exceptions.rb \ lib/registration/sw_mgmt.rb \ + lib/registration/storage.rb \ lib/registration/repo_state.rb \ lib/registration/helpers.rb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/clients/inst_scc.rb new/yast2-registration-3.1.19/src/clients/inst_scc.rb --- old/yast2-registration-3.1.18/src/clients/inst_scc.rb 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/src/clients/inst_scc.rb 2014-03-18 10:44:23.000000000 +0100 @@ -25,15 +25,22 @@ # use external rubygem for SCC communication require "scc_api" +require "cgi" + require "registration/exceptions" require "registration/helpers" require "registration/sw_mgmt" require "registration/repo_state" +require "registration/storage" module Yast class InstSccClient < Client include Yast::Logger + # the maximum number of reg. codes displayed vertically, + # this is the limit for 80x25 textmode UI + MAX_REGCODES_PER_COLUMN = 9 + def main Yast.import "UI" @@ -45,10 +52,46 @@ Yast.import "Report" Yast.import "Mode" Yast.import "Label" + Yast.import "Sequencer" + Yast.import "Installation" # redirect the scc_api log to y2log SccApi::GlobalLogger.instance.log = Y2Logger.instance + @selected_addons = [] + + initialize_regkeys + + start_workflow + end + + private + + # initialize known reg. keys + def initialize_regkeys + @known_reg_keys = ::Registration::Storage::RegKeys.instance.reg_keys + if @known_reg_keys + log.info "Known reg keys: #{@known_reg_keys.size} keys" + return + end + + if !Mode.normal + # read registration keys from USB media + log.info "Reading keys from USB media..." + # TODO FIXME: placeholder for FATE#316796 (https://fate.suse.com/316796) + # read the keys here, return Hash with mapping product_name => reg_key + @known_reg_keys = {} + log.info "Found #{@known_reg_keys.size} keys" + else + log.info "Initializing empty known reg keys" + @known_reg_keys = {} + end + + # cache the values + ::Registration::Storage::RegKeys.instance.reg_keys = @known_reg_keys + end + + def register_base_system show_scc_credentials_dialog ret = nil @@ -58,116 +101,128 @@ ret = UI.UserInput if ret == :next + email = UI.QueryWidget(:email, :Value) reg_code = UI.QueryWidget(:reg_code, :Value) # reset the user input in case an exception is raised ret = nil - begin + catch_registration_errors do register(email, reg_code) return :next - rescue SccApi::NoNetworkError - # Error popup - Report.Error(_("Network is not configured, the registration server cannot be reached.")) - rescue SccApi::NotAuthorized - # Error popup - Report.Error(_("The email address or the registration\ncode is not valid.")) - rescue Timeout::Error - # Error popup - Report.Error(_("Connection time out.")) - rescue SccApi::ErrorResponse => e - # TODO FIXME: display error details from the response - Report.Error(_("Registration server error.\n\nRetry registration later.")) - rescue SccApi::HttpError => e - case e.response - when Net::HTTPClientError - Report.Error(_("Registration client error.")) - when Net::HTTPServerError - Report.Error(_("Registration server error.\n\nRetry registration later.")) - else - Report.Error(_("Registration failed.")) - end - rescue ::Registration::ServiceError => e - log.error("Service error: #{e.message % e.service}") - Report.Error(_(e.message) % e.service) - rescue ::Registration::PkgError => e - log.error("Pkg error: #{e.message}") - Report.Error(_(e.message)) - rescue Exception => e - log.error("SCC registration failed: #{e}, #{e.backtrace}") - Report.Error(_("Registration failed.")) end + end - return :next if ret == :skip && confirm_skipping + return ret if ret == :skip && confirm_skipping end return ret end - - private + def catch_registration_errors(&block) + begin + yield + rescue SccApi::NoNetworkError + # Error popup + Report.Error(_("Network is not configured, the registration server cannot be reached.")) + rescue SccApi::NotAuthorized + # Error popup + Report.Error(_("The email address or the registration\ncode is not valid.")) + rescue Timeout::Error + # Error popup + Report.Error(_("Connection time out.")) + rescue SccApi::ErrorResponse => e + # TODO FIXME: display error details from the response + Report.Error(_("Registration server error.\n\nRetry registration later.")) + rescue SccApi::HttpError => e + case e.response + when Net::HTTPClientError + Report.Error(_("Registration client error.")) + when Net::HTTPServerError + Report.Error(_("Registration server error.\n\nRetry registration later.")) + else + Report.Error(_("Registration failed.")) + end + rescue ::Registration::ServiceError => e + log.error("Service error: #{e.message % e.service}") + Report.Error(_(e.message) % e.service) + rescue ::Registration::PkgError => e + log.error("Pkg error: #{e.message}") + Report.Error(_(e.message)) + rescue Exception => e + log.error("SCC registration failed: #{e}, #{e.backtrace}") + Report.Error(_("Registration failed.")) + end + end def register(email, reg_code) - scc = SccApi::Connection.new(email, reg_code) + @scc = SccApi::Connection.new(email, reg_code) # set the current language to receive translated error messages - scc.language = ::Registration::Helpers.language + @scc.language = ::Registration::Helpers.language reg_url = ::Registration::Helpers.registration_url if reg_url log.info "Using custom registration URL: #{reg_url.inspect}" - scc.url = reg_url + @scc.url = reg_url end # announce (register the system) first - credentials = run_with_feedback(_("Registering the System..."), _("Contacting the SUSE Customer Center server")) do - scc.announce + @credentials = run_with_feedback(_("Registering the System..."), _("Contacting the SUSE Customer Center server")) do + @scc.announce end # ensure the zypp config directories are writable in inst-sys ::Registration::SwMgmt.zypp_config_writable! # write the global credentials - credentials.write + @credentials.write # then register the product(s) - product_services = run_with_feedback(_("Registering the Product..."), _("Contacting the SUSE Customer Center server")) do - # there will be just one base product, but theoretically there can be more... - ::Registration::SwMgmt.products_to_register.map do |base_product| - log.info("Registering base product: #{base_product.inspect}") - scc.register(base_product) - end - end + products = ::Registration::SwMgmt.products_to_register + register_products(products) - log.info "product_services: #{product_services.inspect}" + # remember the base products for later (to get the respective addons) + ::Registration::Storage::BaseProducts.instance.products = products + end - if !product_services.empty? - Progress.New( - # TRANSLATORS: dialog caption - _("Adding Registered Software Repositories"), - " ", - product_services.size, - [ _("Add Services") ], - [ _("Adding Services") ], - # TRANSLATORS: dialog help - _("<p>The repository manager is downloading registered repositories...</p>") - ) + def register_products(products) + product_services = run_with_feedback(n_("Registering Product...", "Registering Products...", products.size), _("Contacting the SUSE Customer Center server")) do + products.map do |product| + log.info("Registering product: #{product["name"]}") - Progress.NextStage + begin + orig_reg_code = @scc.reg_code + # use product specific reg. key (e.g. for addons) + @scc.reg_code = product["reg_key"] if product["reg_key"] + + ret = @scc.register(product) + ensure + # restore the original base product key + @scc.reg_code = orig_reg_code + end - begin - ::Registration::SwMgmt.add_services(product_services, credentials) - ensure - Progress.Finish + ret end + end + + log.info "registered product_services: #{product_services.inspect}" + + if !product_services.empty? + add_product_services(product_services) # select repositories to use in installation (e.g. enable/disable Updates) select_repositories(product_services) if Mode.installation end end + def add_product_services(product_services) + ::Registration::SwMgmt.add_services(product_services, @credentials) + end + + # content for the main registration dialog def scc_credentials_dialog VBox( HBox( @@ -188,11 +243,13 @@ ) end + # help text for the main registration dialog def scc_help_text # TODO: improve the help text _("Enter SUSE Customer Center credentials here to register the system to get updates and add-on products.") end + # display the main registration dialog def show_scc_credentials_dialog Wizard.SetContents( _("SUSE Customer Center Registration"), @@ -264,6 +321,329 @@ Popup.ClearFeedback end + # create item list (available addons items) + def addon_selection_items(addons) + addons.map{|a| Item(Id(a.product_ident), a.short_name, @selected_addons.include?(a))} + end + + # create content fot the addon selection dialog + def addon_selection_dialog_content(addons) + media_checkbox = Empty() + + # the media check box is displayed only at installation + # to modify the installation workflow (display extra add-on dialog) + if Mode.installation || true + media_checkbox = VBox( + VSpacing(0.4), + HBox( + HSpacing(1), + Left(CheckBox(Id(:media), _("In&clude Add-on Products from Separate Media"), + Installation.add_on_selected)), + ) + ) + end + + VBox( + VWeight(75, MultiSelectionBox(Id(:addons), Opt(:notify), "", + addon_selection_items(addons))), + MinHeight(8, + VWeight(25, RichText(Id(:details), Opt(:disabled), "<small>" + + _("Select an addon to show details here") + "<small>")), + ), + media_checkbox, + VSpacing(0.4) + ) + end + + # update addon details after changing the current addon in the UI + def show_addon_details(addon) + details = "<p><big><b>#{CGI.escape_html(addon.long_name)}</b></big></p>" + + "<p>#{CGI.escape_html(addon.description)}</p>" + + # TODO FIXME: SCC does not support dependencies yet + # + # if !addon.depends_on.empty? + # # rich text content: list of required (dependent) addons, + # # %s is a list of product names + # details << (_("<p><b>Required Add-ons:</b> %s</p>") % + # CGI.escape_html(addon.depends_on.map(&:label).join(", "))) + # end + + if !addon.free + # rich text content: the selected addon requires a registration key + details << _("<p><b>A Registration Key is Required</b></p>") + end + + UI.ChangeWidget(Id(:details), :Value, details) + UI.ChangeWidget(Id(:details), :Enabled, true) + end + + # check addon dependencies and automatically select required addons + def check_addon_dependencies(addons) + selected = UI.QueryWidget(Id(:addons), :SelectedItems) + selected_addons = addons.select{|a| selected.include?(a.name)} + + selected_addons.each do |a| + missing = a.required_addons - selected_addons + + if !missing.empty? + # popup message, %s are product names + Popup.Message((_("Automatically selecting '%s'\ndependencies:\n\n%s") % + [a.label, missing.map(&:label).join("\n")])) + # select the missing entries + UI.ChangeWidget(Id(:addons), :SelectedItems, selected + missing.map(&:name)) + end + end + end + + # check for the maximum amount of reg. codes supported by Yast + def supported_addon_count(addons, selected) + # maximum number or reg codes which can be displayed in two column layout + max_supported = 2*MAX_REGCODES_PER_COLUMN + + if addons.select{|a| selected.include?(a.product_ident) && !a.free}.size > max_supported + Report.Error(_("YaST allows to select at most %s addons.") % max_supported) + return false + end + + return true + end + + # read the addon media checkbox and adapt the installation workflow accordingly + def set_media_addons + if Mode.installation + # the widget exists only at installation + Installation.add_on_selected = UI.QueryWidget(Id(:media), :Value) + log.info "Add-on media selected: #{Installation.add_on_selected}" + + # lazy include, the file is part of yast2-installation + # avoid yast2-installation runtime dependency by including it only here, + # not at the global level + Yast.include self, "installation/misc.rb" + AdjustStepsAccordingToInstallationSettings() + end + end + + # handle user input in the addon selection dialog + def handle_addon_selection_dialog(addons) + ret = nil + continue_buttons = [:next, :back, :close, :abort, :skip] + + while !continue_buttons.include?(ret) do + ret = UI.UserInput + + # current item has been changed, refresh details, check dependencies + case ret + when :addons + current_addon = UI.QueryWidget(Id(:addons), :CurrentItem) + + if current_addon + show_addon_details(addons.find{|addon| addon.product_ident == current_addon}) + # TODO FIXME: SCC does not support dependencies yet + # check_addon_dependencies(addons) + end + when :next + selected = UI.QueryWidget(Id(:addons), :SelectedItems) + + if !supported_addon_count(addons, selected) + ret = nil + next + end + + @selected_addons = addons.select{|a| selected.include?(a.product_ident)} + log.info "Selected addons: #{@selected_addons.map(&:short_name)}" + + set_media_addons + + ret = :skip if @selected_addons.empty? + end + end + + ret + end + + # run the addon selection dialog + def select_addons + addons = get_available_addons + Wizard.SetContents( + # dialog title + _("Available Products and Extensions"), + addon_selection_dialog_content(addons), + # FIXME: help text + "", + GetInstArgs.enable_back || Mode.normal, + GetInstArgs.enable_next || Mode.normal + ) + + handle_addon_selection_dialog(addons) + end + + + # create widgets for entering the addon reg keys + def addon_regkey_items(addons) + textmode = UI.TextMode + box = VBox() + + addons.each do |addon| + label = addon.short_name + label << " (#{addon.long_name})" if !addon.long_name.empty? + + box[box.size] = MinWidth(32, InputField(Id(addon.product_ident), label, + @known_reg_keys.fetch(addon.product_ident, ""))) + # add extra spacing when there are just few addons, in GUI always + box[box.size] = VSpacing(1) if (addons.size < 5) || !textmode + end + + box + end + + # create content for the addon reg keys dialog + def addon_regkeys_dialog_content(addons) + # display the second column if needed + if addons.size > MAX_REGCODES_PER_COLUMN + # display only the addons which fit two column layout + display_addons = addons[0..2*MAX_REGCODES_PER_COLUMN - 1] + + # round the half up (more items in the first column for odd number of items) + half = (display_addons.size + 1) / 2 + + box1 = addon_regkey_items(display_addons[0..half - 1]) + box2 = HBox( + HSpacing(2), + addon_regkey_items(display_addons[half..-1]) + ) + else + box1 = addon_regkey_items(addons) + end + + HBox( + HSpacing(Opt(:hstretch), 3), + box1, + box2 ? box2 : Empty(), + HSpacing(Opt(:hstretch), 3) + ) + end + + # load available addons from SCC server + # the result is cached to avoid reloading when going back and forth in the + # installation workflow + def get_available_addons + # cache the available addons + return @available_addons if @available_addons + + @available_addons = run_with_feedback(_("Loading Available Add-on Products and Extensions..."), + _("Contacting the SUSE Customer Center server")) do + + # extensions for base product + ::Registration::Storage::BaseProducts.instance.products.reduce([]) do |acc, product| + acc.concat(@scc.extensions_for(product["name"]).extensions) + end + end + + log.info "Received product extensions: #{@available_addons}" + @available_addons + end + + # handle user input in the addon reg keys dialog + def handle_register_addons_dialog(addons_with_keys) + continue_buttons = [:next, :back, :close, :abort] + + ret = nil + while !continue_buttons.include?(ret) do + ret = UI.UserInput + + if ret == :next + collect_addon_regkeys(addons_with_keys) + + # register the add-ons + ret = nil unless register_selected_addons + end + end + + return ret + end + + # collect the entered reg keys from UI + # @return [Hash<Addon,String>] addon => reg. code mapping + def collect_addon_regkeys(addons_with_keys) + pairs = addons_with_keys.map do |a| + [a.product_ident, UI.QueryWidget(Id(a.product_ident), :Value)] + end + @known_reg_keys.merge!(Hash[pairs]) + end + + # register all selected addons + def register_selected_addons + registration_order = @selected_addons + # TODO FIXME: SCC does not report dependoencies + # begin + # # compute the registration order according to the dependencies + # registration_order = Registration::AddonSorter.registration_order(@selected_addons) + # rescue KeyError + # # Continuew/Cancel dialog: missing dependency error + # if Popup.ContinueCancel(_("Addon dependencies cannot be solved.\n" + + # "Register without solving dependencies?")) + # # just try the current order it might work + # else + # return false + # end + # end + # + # log.info "Addon registration order: #{registration_order.map(&:short_name)}" + + products = registration_order.map do |a| + { + "name" => a.product_ident, + "reg_key" => @known_reg_keys[a.product_ident], + # TODO FIXME: not handled by SCC yet + "arch" => nil, + "version" => nil + } + end + + catch_registration_errors do + register_products(products) + return true + end + + return false + end + + # run the addon reg keys dialog + def register_addons + missing_regkeys = @selected_addons.reject(&:free) + + # if registering only add-ons which do not need a reg. key (like SDK) + # then simply start the registration + if missing_regkeys.empty? + Wizard.SetContents( + # dialog title + _("Registering Selected Add-on Products and Extensions"), + # display only the products which need a registration key + Empty(), + # FIXME: help text + "", + false, + false + ) + # when registration fails go back + return register_selected_addons ? :next : :back + else + Wizard.SetContents( + # dialog title + _("Enter Registration Keys for Selected Add-on Products and Extensions"), + # display only the products which need a registration key + addon_regkeys_dialog_content(missing_regkeys), + # FIXME: help text + "", + GetInstArgs.enable_back || Mode.normal, + GetInstArgs.enable_next || Mode.normal + ) + + return handle_register_addons_dialog(missing_regkeys) + end + end + def confirm_skipping # Popup question: confirm skipping the registration confirmation = _("If you do not register your system we will not be able\n" + @@ -275,6 +655,35 @@ Popup.YesNo(confirmation) end + # UI workflow definition + def start_workflow + aliases = { + "register" => lambda { register_base_system() }, + "select_addons" => lambda { select_addons() }, + "register_addons" => lambda { register_addons() } + } + + sequence = { + "ws_start" => "register", + "register" => { + :abort => :abort, + :skip => :next, + :next => "select_addons" + }, + "select_addons" => { + :abort => :abort, + :skip => :next, + :next => "register_addons" + }, + "register_addons" => { + :abort => :abort, + :next => :next + } + } + + log.info "Starting scc sequence" + Sequencer.Run(aliases, sequence) + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/clients/scc.rb new/yast2-registration-3.1.19/src/clients/scc.rb --- old/yast2-registration-3.1.18/src/clients/scc.rb 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/src/clients/scc.rb 2014-03-18 10:44:23.000000000 +0100 @@ -26,12 +26,29 @@ require "registration/sw_mgmt" module Yast - import "Wizard" + class SccClient < Client + Yast.import "Wizard" + Yast.import "CommandLine" - Wizard.CreateDialog - ::Registration::SwMgmt.init + def main + if WFM.Args.include?("help") + cmdline_description = { + "id" => "scc", + # Command line help text for the repository module, %1 is "SUSEconnect" + "help" => _("Use '%s' instead of this YaST module.") % "SUSEconnect", + } - WFM.call("inst_scc") + CommandLine.Run(cmdline_description) + else + Wizard.CreateDialog + ::Registration::SwMgmt.init - Wizard.CloseDialog + WFM.call("inst_scc") + + Wizard.CloseDialog + end + end + end end + +Yast::SccClient.new.main diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/lib/registration/addon.rb new/yast2-registration-3.1.19/src/lib/registration/addon.rb --- old/yast2-registration-3.1.18/src/lib/registration/addon.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-registration-3.1.19/src/lib/registration/addon.rb 2014-03-18 10:44:23.000000000 +0100 @@ -0,0 +1,84 @@ +# encoding: utf-8 + +# ------------------------------------------------------------------------------ +# Copyright (c) 2014 SUSE LLC +# +# 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 Novell, Inc. +# +# To contact Novell about this file by physical or electronic mail, you may find +# current contact information at www.novell.com. +# ------------------------------------------------------------------------------ +# + +require 'tsort' + +module Registration + class Addon + # product data needed for registration + attr_reader :name, :version, :arch + # additional data: UI labels, dependencies on other add-ons and + # a flag indicating required registration key + attr_reader :label, :description, :depends_on, :regkey_needed + + def initialize(name, version, arch, label: "", description: "", + depends_on: [], regkey_needed: true) + @name = name + @version = version + @arch = arch + @label = label + @description = description + @depends_on = depends_on + @regkey_needed = regkey_needed + end + + # recursively collect all addon dependecies and create a flat list + # @return [Array<Addon>] + def required_addons + # this addon dependencies plus their dependencies + depends_on.inject(depends_on.dup) do |acc, dep| + acc.concat(dep.required_addons) + # remove duplicates + acc.uniq + end + end + end + + # class for sorting Addons according to their dependencies + # SCC requires to register addons in their dependency order + # @see TSort example http://ruby-doc.org/stdlib-2.1.0/libdoc/tsort/rdoc/TSort.html#module-TSort-l... + class AddonSorter < Hash + include TSort + + alias tsort_each_node each_key + + def tsort_each_child(node, &block) + fetch(node).each(&block) + end + + # computes registration order of add-ons acording to their dependencies + # raises KeyError on missing dependency + # @param addons [Array<Addons>] input list with addons + # @return [Array<Addons>] input list sorted according to Addon dependencies + def self.registration_order(addons) + solver = AddonSorter.new + + # fill the solver with addon dependencies + addons.each do |a| + solver[a] = a.depends_on + end + + # compute the order using tsort + solver.tsort + end + end + +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/lib/registration/helpers.rb new/yast2-registration-3.1.19/src/lib/registration/helpers.rb --- old/yast2-registration-3.1.18/src/lib/registration/helpers.rb 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/src/lib/registration/helpers.rb 2014-03-18 10:44:23.000000000 +0100 @@ -22,6 +22,7 @@ # require "yast" +require "uri" module Registration @@ -87,6 +88,16 @@ service.sub(/\Aservice:[^:]+:/, "") end + # return "credentials" parameter from URL + # raises URI::InvalidURIError if URL is invalid + # @param url [String] URL as string + def self.credentials_from_url(url) + parsed_url = URI(url) + params = Hash[URI.decode_www_form(parsed_url.query)] + + params["credentials"] + end + # Create radio button label for a SLP service # @param service [Yast::SlpServiceClass::Service] SLP service # @return [String] label diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/lib/registration/storage.rb new/yast2-registration-3.1.19/src/lib/registration/storage.rb --- old/yast2-registration-3.1.18/src/lib/registration/storage.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-registration-3.1.19/src/lib/registration/storage.rb 2014-03-18 10:44:23.000000000 +0100 @@ -0,0 +1,40 @@ + +# encoding: utf-8 + +# ------------------------------------------------------------------------------ +# Copyright (c) 2014 Novell, Inc. 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 Novell, Inc. +# +# To contact Novell about this file by physical or electronic mail, you may find +# current contact information at www.novell.com. +# ------------------------------------------------------------------------------ +# +# + +require "singleton" + +module Registration + module Storage + + # storage for changed repositories + class RegKeys < Struct.new(:reg_keys) + include Singleton + end + + # remember the registered base product + class BaseProducts < Struct.new(:products) + include Singleton + end + end +end \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/src/lib/registration/sw_mgmt.rb new/yast2-registration-3.1.19/src/lib/registration/sw_mgmt.rb --- old/yast2-registration-3.1.18/src/lib/registration/sw_mgmt.rb 2014-03-10 14:49:36.000000000 +0100 +++ new/yast2-registration-3.1.19/src/lib/registration/sw_mgmt.rb 2014-03-18 10:44:23.000000000 +0100 @@ -33,7 +33,6 @@ Yast.import "Pkg" Yast.import "Installation" Yast.import "PackageCallbacksInit" - Yast.import "Progress" class SwMgmt include Yast @@ -49,7 +48,6 @@ PackageCallbacksInit.InitPackageCallbacks Pkg.TargetInitialize(Installation.destdir) Pkg.TargetLoad - Pkg.SourceStartManager(true) end # during installation /etc/zypp directory is not writable (mounted on @@ -73,7 +71,7 @@ def self.products_to_register # just for debugging: - # return [{"name" => "SUSE_SLES", "arch" => "x86_64", "version" => "12-"}] + # return [{"name" => "SLES", "arch" => "x86_64", "version" => "12-1.47"}] # during installation the products are :selected, # on a running system the products are :installed @@ -105,13 +103,14 @@ product_services.map(&:services).flatten.each do |service| log.info "Adding service #{service.name.inspect} (#{service.url})" - # progress bar label - Progress.Title(_("Adding service %s...") % service.name) + credentials_file = Helpers.credentials_from_url(service.url) - # TODO FIXME: SCC currenly does not return credentials for the service, - # just reuse the global credentials and save to a different file - credentials.file = service.name + "_credentials" - credentials.write + if credentials_file + # TODO FIXME: SCC currenly does not return credentials for the service, + # just reuse the global credentials and save to a different file + credentials.file = credentials_file + credentials.write + end if !Pkg.ServiceAdd(service.name, service.url.to_s) # error message @@ -127,8 +126,6 @@ # error message raise Registration::ServiceError.new(N_("Refreshing service '%s' failed."), service.name) end - - Progress.NextStep end ensure Pkg.SourceSaveAll diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/test/Makefile.am new/yast2-registration-3.1.19/test/Makefile.am --- old/yast2-registration-3.1.18/test/Makefile.am 2014-03-10 14:49:37.000000000 +0100 +++ new/yast2-registration-3.1.19/test/Makefile.am 2014-03-18 10:44:23.000000000 +0100 @@ -1,4 +1,5 @@ TESTS = \ + addon_spec.rb \ discover_registration_services_spec.rb \ helpers_spec.rb \ scc_finish_test.rb \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/test/addon_spec.rb new/yast2-registration-3.1.19/test/addon_spec.rb --- old/yast2-registration-3.1.18/test/addon_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-registration-3.1.19/test/addon_spec.rb 2014-03-18 10:44:23.000000000 +0100 @@ -0,0 +1,93 @@ +#! /usr/bin/env rspec + +require_relative "spec_helper" + +require "registration/addon" + +describe Registration::Addon do + + describe ".required_addons" do + it "returns empty list if there are no dependencies" do + addon = Registration::Addon.new("SUSE_SLES", "12", "x86_64") + + expect(addon.required_addons).to be_empty + end + + it "returns an array containing the dependency name" do + addon1 = Registration::Addon.new("SUSE_ADDON1", "12", "x86_64") + addon2 = Registration::Addon.new("SUSE_ADDON2", "12", "x86_64", depends_on: [addon1]) + + expect(addon2.required_addons).to eq([addon1]) + end + + it "removes duplicates" do + addon1 = Registration::Addon.new("SUSE_ADDON1", "12", "x86_64") + addon2 = Registration::Addon.new("SUSE_ADDON2", "12", "x86_64", depends_on: [addon1]) + addon3 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon1]) + addon4 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon1]) + addon5 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon2, addon3, addon4]) + + required_addons = addon5.required_addons + + expect(required_addons).to eq(required_addons.uniq) + expect(required_addons).to include(addon1, addon2, addon3, addon4) + end + + it "returns transitive dependencies" do + addon1 = Registration::Addon.new("SUSE_ADDON1", "12", "x86_64") + addon2 = Registration::Addon.new("SUSE_ADDON2", "12", "x86_64", depends_on: [addon1]) + addon3 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon2]) + + expect(addon3.required_addons).to include(addon1, addon2) + end + + it "returns multiple transitive dependencies" do + addon1 = Registration::Addon.new("SUSE_ADDON1", "12", "x86_64") + addon2 = Registration::Addon.new("SUSE_ADDON2", "12", "x86_64") + addon3 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon1, addon2]) + addon4 = Registration::Addon.new("SUSE_ADDON4", "12", "x86_64") + addon5 = Registration::Addon.new("SUSE_ADDON5", "12", "x86_64") + addon6 = Registration::Addon.new("SUSE_ADDON6", "12", "x86_64", depends_on: [addon4, addon5]) + addon7 = Registration::Addon.new("SUSE_ADDON7", "12", "x86_64", depends_on: [addon3, addon6]) + + required_addons = addon7.required_addons + expect(required_addons.size).to eq(6) + expect(required_addons).to include(addon1, addon2, addon3, addon4, addon5, addon6) + end + end + +end + +describe Registration::AddonSorter do + describe ".registration_order" do + it "returns registration order according to the dependencies" do + addon1 = Registration::Addon.new("SUSE_ADDON1", "12", "x86_64") + addon2 = Registration::Addon.new("SUSE_ADDON2", "12", "x86_64") + addon3 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon1, addon2]) + addon4 = Registration::Addon.new("SUSE_ADDON4", "12", "x86_64") + addon5 = Registration::Addon.new("SUSE_ADDON5", "12", "x86_64") + addon6 = Registration::Addon.new("SUSE_ADDON6", "12", "x86_64", depends_on: [addon4, addon5]) + addon7 = Registration::Addon.new("SUSE_ADDON7", "12", "x86_64", depends_on: [addon3, addon6]) + + # deliberately use an order which does not follow dependencies to make sure it is changed + addons = [addon7, addon2, addon3, addon5, addon4, addon6, addon1] + + solved = Registration::AddonSorter.registration_order(addons) + + # check the order, iterate over the list and check for missing dependencies + registered = [] + solved.each do |a| + # check that all dependendent add-ons are already registered + expect(a.depends_on - registered).to be_empty + registered << a + end + end + + it "raises KeyError exception when there is an unresolved dependency" do + addon1 = Registration::Addon.new("SUSE_ADDON1", "12", "x86_64") + addon2 = Registration::Addon.new("SUSE_ADDON3", "12", "x86_64", depends_on: [addon1]) + + expect{Registration::AddonSorter.registration_order([addon2])}.to raise_error(KeyError) + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-registration-3.1.18/test/helpers_spec.rb new/yast2-registration-3.1.19/test/helpers_spec.rb --- old/yast2-registration-3.1.18/test/helpers_spec.rb 2014-03-10 14:49:37.000000000 +0100 +++ new/yast2-registration-3.1.19/test/helpers_spec.rb 2014-03-18 10:44:23.000000000 +0100 @@ -108,4 +108,21 @@ end end + describe ".credentials_from_url" do + it "returns credentials parameter from URL" do + url = "https://scc.suse.com/service/repo/repoindex.xml?credentials=SLES_credentials" + expect(Registration::Helpers.credentials_from_url(url)).to eq("SLES_credentials") + end + + it "returns nil if the URL misses credentials parameter" do + url = "https://scc.suse.com/service/repo/repoindex.xml?nocredentials=SLES_credentia..." + expect(Registration::Helpers.credentials_from_url(url)).to eq(nil) + end + + it "raises URI::InvalidURIError when URL is invalid" do + url = ":foo:" + expect{Registration::Helpers.credentials_from_url(url)}.to raise_error(URI::InvalidURIError) + end + end + end continue with "q"... Checked in at Tue Mar 18 13:07:54 CET 2014 by ro Remember to have fun... -- To unsubscribe, e-mail: yast-commit+unsubscribe@opensuse.org For additional commands, e-mail: yast-commit+help@opensuse.org
participants (1)
-
ro