Hello community,

here is the log from the commit of package yast2-installation for 
openSUSE:Factory checked in at 2016-05-19 12:04:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yast2-installation (Old)
 and      /work/SRC/openSUSE:Factory/.yast2-installation.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yast2-installation"

Changes:
--------
--- /work/SRC/openSUSE:Factory/yast2-installation/yast2-installation.changes    
2016-05-10 09:26:39.000000000 +0200
+++ 
/work/SRC/openSUSE:Factory/.yast2-installation.new/yast2-installation.changes   
    2016-05-19 12:04:33.000000000 +0200
@@ -1,0 +2,32 @@
+Tue May 17 08:17:51 UTC 2016 - [email protected]
+
+- Visual improvement in the SSH keys importing proposal summary
+
+-------------------------------------------------------------------
+Mon May 16 16:39:34 UTC 2016 - [email protected]
+
+- The user is now informed about SSH keys to be reused (copied
+  from a previous system) during system installation.
+- The user can select a different partition (or none) to read the
+  keys from and whether to also copy config files.
+- SSH import functionality not longer depending from
+  "copy_to_system" feature.
+- Fate#319624
+- 3.1.187
+
+-------------------------------------------------------------------
+Mon May 16 08:29:25 UTC 2016 - [email protected]
+
+- SSH installation: handle closing the initial installation screen
+  by the window manager close button (bsc#979499)
+- 3.1.186
+
+-------------------------------------------------------------------
+Mon May  9 15:14:22 CEST 2016 - [email protected]
+
+- Do not copy licenses from inst-sys to target system.
+  Showing EULA location in the installed system.
+  (fate#219341)
+- 3.1.185
+
+-------------------------------------------------------------------

Old:
----
  yast2-installation-3.1.184.tar.bz2

New:
----
  yast2-installation-3.1.187.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ yast2-installation.spec ++++++
--- /var/tmp/diff_new_pack.OmButz/_old  2016-05-19 12:04:34.000000000 +0200
+++ /var/tmp/diff_new_pack.OmButz/_new  2016-05-19 12:04:34.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-installation
-Version:        3.1.184
+Version:        3.1.187
 Release:        0
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build

++++++ yast2-installation-3.1.184.tar.bz2 -> yast2-installation-3.1.187.tar.bz2 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/package/yast2-installation.changes 
new/yast2-installation-3.1.187/package/yast2-installation.changes
--- old/yast2-installation-3.1.184/package/yast2-installation.changes   
2016-05-09 14:24:55.000000000 +0200
+++ new/yast2-installation-3.1.187/package/yast2-installation.changes   
2016-05-18 11:28:09.000000000 +0200
@@ -1,4 +1,36 @@
 -------------------------------------------------------------------
+Tue May 17 08:17:51 UTC 2016 - [email protected]
+
+- Visual improvement in the SSH keys importing proposal summary
+
+-------------------------------------------------------------------
+Mon May 16 16:39:34 UTC 2016 - [email protected]
+
+- The user is now informed about SSH keys to be reused (copied
+  from a previous system) during system installation.
+- The user can select a different partition (or none) to read the
+  keys from and whether to also copy config files.
+- SSH import functionality not longer depending from
+  "copy_to_system" feature.
+- Fate#319624
+- 3.1.187
+
+-------------------------------------------------------------------
+Mon May 16 08:29:25 UTC 2016 - [email protected]
+
+- SSH installation: handle closing the initial installation screen
+  by the window manager close button (bsc#979499)
+- 3.1.186
+
+-------------------------------------------------------------------
+Mon May  9 15:14:22 CEST 2016 - [email protected]
+
+- Do not copy licenses from inst-sys to target system.
+  Showing EULA location in the installed system.
+  (fate#219341)
+- 3.1.185
+
+-------------------------------------------------------------------
 Fri May  6 11:09:28 UTC 2016 - [email protected]
 
 - get more texts for roles dialog from control file, allow
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/package/yast2-installation.spec 
new/yast2-installation-3.1.187/package/yast2-installation.spec
--- old/yast2-installation-3.1.184/package/yast2-installation.spec      
2016-05-09 14:24:55.000000000 +0200
+++ new/yast2-installation-3.1.187/package/yast2-installation.spec      
2016-05-18 11:28:09.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-installation
-Version:        3.1.184
+Version:        3.1.187
 Release:        0
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-3.1.184/src/Makefile.am 
new/yast2-installation-3.1.187/src/Makefile.am
--- old/yast2-installation-3.1.184/src/Makefile.am      2016-05-09 
14:24:55.000000000 +0200
+++ new/yast2-installation-3.1.187/src/Makefile.am      2016-05-18 
11:28:09.000000000 +0200
@@ -49,6 +49,7 @@
   clients/inst_rpmcopy_secondstage.rb \
   clients/inst_save_hardware_status.rb \
   clients/inst_scenarios.rb \
+  clients/inst_ssh_import.rb \
   clients/inst_system_analysis.rb \
   clients/inst_system_role.rb \
   clients/inst_upgrade_urls.rb \
@@ -66,6 +67,7 @@
   clients/save_config_finish.rb \
   clients/save_hw_status_finish.rb \
   clients/snapshots_finish.rb \
+  clients/ssh_import_proposal.rb \
   clients/ssh_settings_finish.rb \
   clients/stroj-casu.rb \
   clients/switch_scr_finish.rb \
@@ -116,7 +118,11 @@
   lib/installation/select_system_role.rb \
   lib/installation/snapshots_finish.rb \
   lib/installation/updates_manager.rb \
-  lib/installation/update_repository.rb
+  lib/installation/update_repository.rb \
+  lib/installation/ssh_config.rb \
+  lib/installation/ssh_key.rb \
+  lib/installation/ssh_config_file.rb \
+  lib/installation/ssh_importer.rb
 
 ylibtransferdir = "${yast2dir}/lib/transfer"
 ylibtransfer_DATA = \
@@ -174,6 +180,7 @@
   lib/installation/clients/proxy_finish.rb \
   lib/installation/clients/save_config_finish.rb \
   lib/installation/clients/save_hw_status_finish.rb \
+  lib/installation/clients/ssh_import_proposal.rb \
   lib/installation/clients/ssh_settings_finish.rb \
   lib/installation/clients/stroj-casu.rb \
   lib/installation/clients/switch_scr_finish.rb \
@@ -184,6 +191,10 @@
   lib/installation/clients/x11_finish.rb \
   lib/installation/clients/yast_inf_finish.rb
 
-EXTRA_DIST = $(module_DATA) $(client_DATA) $(ynclude_DATA) $(scrconf_DATA) 
$(schemafiles_DATA) $(desktop_DATA) $(fillup_DATA) $(ylib_DATA) 
$(ylibtransfer_DATA)
+ylibdialogdir = "${yast2dir}/lib/installation/dialogs"
+ylibdialog_DATA = \
+  lib/installation/dialogs/ssh_import.rb
+
+EXTRA_DIST = $(module_DATA) $(client_DATA) $(ynclude_DATA) $(scrconf_DATA) 
$(schemafiles_DATA) $(desktop_DATA) $(fillup_DATA) $(ylibdialog_DATA) 
$(ylib_DATA) $(ylibtransfer_DATA)
 
 include $(top_srcdir)/Makefile.am.common
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/clients/inst_ssh_import.rb 
new/yast2-installation-3.1.187/src/clients/inst_ssh_import.rb
--- old/yast2-installation-3.1.184/src/clients/inst_ssh_import.rb       
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/clients/inst_ssh_import.rb       
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,22 @@
+# encoding: utf-8
+
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require "installation/dialogs/ssh_import"
+Yast::SshImportDialog.new.run
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/clients/ssh_import_proposal.rb 
new/yast2-installation-3.1.187/src/clients/ssh_import_proposal.rb
--- old/yast2-installation-3.1.184/src/clients/ssh_import_proposal.rb   
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/clients/ssh_import_proposal.rb   
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,2 @@
+require "installation/clients/ssh_import_proposal"
+Yast::SshImportProposalClient.run
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/clients/copy_files_finish.rb
 
new/yast2-installation-3.1.187/src/lib/installation/clients/copy_files_finish.rb
--- 
old/yast2-installation-3.1.184/src/lib/installation/clients/copy_files_finish.rb
    2016-05-09 14:24:55.000000000 +0200
+++ 
new/yast2-installation-3.1.187/src/lib/installation/clients/copy_files_finish.rb
    2016-05-18 11:28:09.000000000 +0200
@@ -29,6 +29,7 @@
 #  Jiri Srain <[email protected]>
 #
 require "fileutils"
+require "installation/ssh_importer"
 
 module Yast
   class CopyFilesFinishClient < Client
@@ -199,82 +200,12 @@
         # FATE #301937, items are defined in the control file
         SystemFilesCopy.SaveInstSysContent
 
-        # Remove old eula.txt
-        # bugzilla #208908
-        @eula_txt = Builtins.sformat(
-          "%1%2/eula.txt",
-          Installation.destdir,
-          Directory.etcdir
-        )
-        if FileUtils.Exists(@eula_txt)
-          SCR.Execute(path(".target.remove"), @eula_txt)
-        end
-
-        # FATE #304865: Enhance YaST Modules to cooperate better handling the 
product licenses
-        @license_dir = ProductFeatures.GetStringFeature(
-          "globals",
-          "base_product_license_directory"
-        )
-        if @license_dir.nil? || @license_dir == ""
-          @license_dir = Builtins.sformat(
-            "%1%2/licenses/base/",
-            Installation.destdir,
-            Directory.etcdir
-          )
-          Builtins.y2warning(
-            "No 'base_product_license_directory' set, using %1",
-            @license_dir
-          )
-        else
-          @license_dir = Builtins.sformat(
-            "%1/%2",
-            Installation.destdir,
-            @license_dir
-          )
-          Builtins.y2milestone("Using license dir: %1", @license_dir)
-        end
-
-        # BNC #594042: Multiple license locations
-        @license_locations = ["/usr/share/doc/licenses/", "/"]
-
-        Builtins.foreach(@license_locations) do |license_location|
-          license_location = Builtins.sformat(
-            "%1/license.tar.gz",
-            license_location
-          )
-          next if !FileUtils.Exists(license_location)
-          # Copy licenses so it can be used in firstboot later
-          # bnc #396976
-          cmd = Convert.to_map(
-            WFM.Execute(
-              path(".local.bash_output"),
-              Builtins.sformat(
-                "mkdir -p '%1' && cd '%1' && rm -rf license*.*; cd '%1' && tar 
-xf '%2'",
-                String.Quote(@license_dir),
-                String.Quote(license_location)
-              )
-            )
-          )
-          if Ops.get_integer(cmd, "exit", -1) == 0
-            Builtins.y2milestone(
-              "Copying %1 to %2 was successful",
-              license_location,
-              @license_dir
-            )
-          else
-            Builtins.y2error(
-              "Copying %1 to %2 has failed: %3",
-              license_location,
-              @license_dir,
-              cmd
-            )
-          end
-          raise Break
-        end
-
         # bugzila #328126
         # Copy 70-persistent-cd.rules ... if not updating
         CopyHardwareUdevRules() if !Mode.update
+
+        # fate#319624
+        copy_ssh_files
       else
         Builtins.y2error("unknown function: %1", @func)
         @ret = nil
@@ -408,6 +339,11 @@
       nil
     end
 
+    def copy_ssh_files
+      log.info "Copying SSH keys and config files"
+      ::Installation::SshImporter.instance.write(Installation.destdir)
+    end
+
     # Prevent from re-defining client class
     # Re-defining would produce warnings that constants were already 
initialized
   end unless defined? CopyFilesFinishClient
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/clients/inst_complex_welcome.rb
 
new/yast2-installation-3.1.187/src/lib/installation/clients/inst_complex_welcome.rb
--- 
old/yast2-installation-3.1.184/src/lib/installation/clients/inst_complex_welcome.rb
 2016-05-09 14:24:55.000000000 +0200
+++ 
new/yast2-installation-3.1.187/src/lib/installation/clients/inst_complex_welcome.rb
 2016-05-18 11:28:09.000000000 +0200
@@ -79,6 +79,7 @@
       Yast.import "Timezone"
       Yast.import "UI"
       Yast.import "Wizard"
+      Yast.import "ProductFeatures"
     end
 
   private
@@ -91,10 +92,10 @@
         case ret
         when :back
           return ret
-        when :abort
+        when :abort, :cancel
           next unless Popup.ConfirmAbort(:painless)
           Wizard.RestoreNextButton
-          return ret
+          return :abort
         when :keyboard
           read_ui_state
           Keyboard.Set(@keyboard)
@@ -388,6 +389,30 @@
       @text_mode = UI.TextMode
     end
 
+    # Showing where the EULA will be installed
+    def license_location
+      file_location = ProductFeatures.GetStringFeature(
+        "globals",
+        "base_product_license_directory"
+      )
+      if file_location.nil?
+        Empty()
+      else
+        Left(
+          ReplacePoint(
+            Id(:license_location),
+            Label(
+              # TRANSLATORS: addition license information
+              # %1 is replaced with the filename. Please keep
+              # the translation VERY short.
+              _("EULA location in the installed system: %s") %
+                file_location
+            )
+          )
+        )
+      end
+    end
+
     def dialog_content
       # this type of contents will be shown only for initial installation 
dialog
       VBox(
@@ -423,6 +448,10 @@
                     Left(ReplacePoint(Id(:base_license_rp), Empty()))
                   )
                 ),
+                VSpacing(text_mode? ? 0.5 : 1),
+                HBox(
+                  license_location
+                ),
                 VSpacing(text_mode? ? 0.1 : 0.5),
                 MinHeight(
                   1,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/clients/inst_pre_install.rb 
new/yast2-installation-3.1.187/src/lib/installation/clients/inst_pre_install.rb
--- 
old/yast2-installation-3.1.184/src/lib/installation/clients/inst_pre_install.rb 
    2016-05-09 14:24:55.000000000 +0200
+++ 
new/yast2-installation-3.1.187/src/lib/installation/clients/inst_pre_install.rb 
    2016-05-18 11:28:09.000000000 +0200
@@ -18,6 +18,9 @@
 # To contact Novell about this file by physical or electronic mail, you may 
find
 # current contact information at www.novell.com.
 # 
------------------------------------------------------------------------------
+
+require "installation/ssh_importer"
+
 module Yast
   class InstPreInstallClient < Client
     include Yast::Logger
@@ -55,6 +58,7 @@
           from: "any",
           to:   "list <map>"
         )
+        @copy_items ||= []
 
         @copy_items.each do |one_copy_item|
           item_id = one_copy_item["id"]
@@ -85,7 +89,10 @@
         end
       end
 
-      read_users
+      each_mounted_partition do |device, mount_point|
+        read_users(device, mount_point) if can_read_users?
+        read_ssh_info(device, mount_point)
+      end
 
       # free the memory
       @useful_partitions = nil
@@ -205,23 +212,6 @@
       nil
     end
 
-    # Stores all found user databases (/etc/passwd and friends) into
-    # UsersDatabase.all, so it can be used during the users import step
-    def read_users
-      require_users_database
-      return unless defined? Users::UsersDatabase
-      each_mounted_partition do |device, mount_point|
-        log.info "Reading users information from #{device}"
-        Users::UsersDatabase.import(mount_point)
-      end
-    end
-
-    def require_users_database
-      require "users/users_database"
-    rescue LoadError
-      log.error "UsersDatabase not found. YaST2-users is missing, old or 
broken."
-    end
-
     def Initialize
       Builtins.y2milestone("Evaluating all current partitions")
 
@@ -300,6 +290,41 @@
 
   protected
 
+    # Checks whether it's possible to read the existing users databases
+    def can_read_users?
+      @can_read_users ||= begin
+        require_users_database
+        defined? Users::UsersDatabase
+      end
+    end
+
+    # Requires users_database if possible, not failing otherwise
+    def require_users_database
+      require "users/users_database"
+    rescue LoadError
+      log.error "UsersDatabase not found. YaST2-users is missing, old or 
broken."
+    end
+
+    # Stores the users database (/etc/passwd and friends) of a given filesystem
+    # in UsersDatabase.all, so it can be used during the users import step
+    #
+    # @param device [String] device name of the filesystem
+    # @param mount_point [String] path where the filesystem is mounted
+    def read_users(device, mount_point)
+      log.info "Reading users information from #{device}"
+      Users::UsersDatabase.import(mount_point)
+    end
+
+    # Stores the SSH configuration of a given partition in the SSH importer
+    # @see CopyFilesFinishClient and SshImportProposalClient
+    #
+    # @param device [String] device name of the filesystem
+    # @param mount_point [String] path where the filesystem is mounted
+    def read_ssh_info(device, mount_point)
+      log.info "Reading SSH information from #{device}"
+      ::Installation::SshImporter.instance.add_config(mount_point, device)
+    end
+
     def each_mounted_partition(&block)
       mnt_tmpdir = "#{Directory.tmpdir}/tmp_mnt_for_check"
       mnt_tmpdir = SystemFilesCopy.CreateDirectoryIfMissing(mnt_tmpdir)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/clients/ssh_import_proposal.rb
 
new/yast2-installation-3.1.187/src/lib/installation/clients/ssh_import_proposal.rb
--- 
old/yast2-installation-3.1.184/src/lib/installation/clients/ssh_import_proposal.rb
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-3.1.187/src/lib/installation/clients/ssh_import_proposal.rb
  2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,82 @@
+require "installation/proposal_client"
+require "installation/ssh_importer"
+
+module Yast
+  # Proposal client for SSH keys import
+  class SshImportProposalClient < ::Installation::ProposalClient
+    include Yast::I18n
+    include Yast::Logger
+
+    def initialize
+      Yast.import "UI"
+      textdomain "installation"
+    end
+
+  protected
+
+    def description
+      {
+        # proposal part - bootloader label
+        "rich_text_title" => _("Import SSH Host Keys and Configuration"),
+        # menubutton entry
+        "menu_title"      => _("&Import SSH Host Keys and Configuration"),
+        "id"              => "ssh_import"
+      }
+    end
+
+    def make_proposal(attrs)
+      importer.reset if attrs["force_reset"]
+      {
+        "preformatted_proposal" => preformatted_proposal,
+        "links"                 => ["ssh_import"]
+      }
+    end
+
+    def importer
+      ::Installation::SshImporter.instance
+    end
+
+    def preformatted_proposal
+      if importer.configurations.empty?
+        return _("No previous Linux installation found - not importing any SSH 
Key")
+      end
+      if importer.device.nil?
+        res = _("No existing SSH host keys will be copied")
+      else
+        ssh_config = importer.configurations[importer.device]
+        partition = ssh_config.system_name
+        if importer.copy_config?
+          # TRANSLATORS: %s is the name of a Linux system found in the hard
+          # disk, like 'openSUSE 13.2'
+          res = _("SSH host keys and configuration will be copied from %s") % 
partition
+        else
+          # TRANSLATORS: %s is the name of a Linux system found in the hard
+          # disk, like 'openSUSE 13.2'
+          res = _("SSH host keys will be copied from %s") % partition
+        end
+      end
+      # TRANSLATORS: link to change the proposal
+      res += " " + _("(<a href=%s>change</a>)") % '"ssh_import"'
+      Yast::HTML.List([res])
+    end
+
+    def ask_user(param)
+      args = {
+        "enable_back" => true,
+        "enable_next" => param.fetch("has_next", false),
+        "going_back"  => true
+      }
+
+      log.info "Asking user which SSH keys to import"
+      begin
+        Yast::Wizard.OpenAcceptDialog
+        result = WFM.CallFunction("inst_ssh_import", [args])
+      ensure
+        Yast::Wizard.CloseDialog
+      end
+      log.info "Returning from ssh_import ask_user with #{result}"
+
+      { "workflow_sequence" => result }
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/dialogs/ssh_import.rb 
new/yast2-installation-3.1.187/src/lib/installation/dialogs/ssh_import.rb
--- old/yast2-installation-3.1.184/src/lib/installation/dialogs/ssh_import.rb   
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/lib/installation/dialogs/ssh_import.rb   
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,118 @@
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require "yast"
+require "ui/installation_dialog"
+require "installation/ssh_importer"
+
+module Yast
+  class SshImportDialog < ::UI::InstallationDialog
+    def initialize
+      super
+
+      Yast.import "UI"
+      Yast.import "Label"
+
+      textdomain "installation"
+    end
+
+    # Event callback for the 'ok' button
+    def next_handler
+      partition = UI.QueryWidget(Id(:device), :Value)
+      partition = nil if partition == :none
+      copy_config = UI.QueryWidget(Id(:copy_config), :Value)
+      log.info "SshImportDialog partition => #{partition} copy_config => 
#{copy_config}"
+      importer.device = partition
+      importer.copy_config = copy_config
+      super
+    end
+
+  private
+
+    def importer
+      @importer ||= ::Installation::SshImporter.instance
+    end
+
+    def partitions
+      @partitions ||= importer.configurations
+    end
+
+    def device
+      @device ||= importer.device
+    end
+
+    def copy_config
+      @copy_config ||= importer.copy_config
+    end
+
+    def dialog_content
+      HSquash(
+        VBox(
+          Left(Label(_("System to Import SSH Host Keys from"))),
+          partitions_list_widget,
+          VSpacing(1),
+          Left(copy_config_widget)
+        )
+      )
+    end
+
+    def dialog_title
+      _("Import SSH Host Keys and Configuration")
+    end
+
+    def help_text
+      _(
+        "<p>Every SSH server is identified by one or several public host keys. 
" \
+        "Choose an existing Linux installation to reuse the host keys -and " \
+        "thus the identity- of its SSH server. The key files found in /etc/ssh 
" \
+        "(one pair of files per host key) will be copied to the new system " \
+        "being installed.</p>" \
+        "<p>Check <b>Copy Whole SSH Configuration</b> to also copy other files 
" \
+        "found in /etc/ssh, in addition to the keys.</p>"
+      )
+    end
+
+    def partitions_list_widget
+      sorted_partitions = partitions.to_a.sort_by(&:first)
+      part_widgets = sorted_partitions.map do |device, partition|
+        Left(partition_widget(device, partition))
+      end
+
+      RadioButtonGroup(
+        Id(:device),
+        VBox(
+          # TRANSLATORS: option to select no partition for SSH keys import
+          Left(RadioButton(Id(:none), _("None"), device.nil?)),
+          *part_widgets
+        )
+      )
+    end
+
+    def partition_widget(dev, partition)
+      strings = { system_name: partition.system_name, device: dev }
+      # TRANSLATORS: %{system_name} is a string like "openSUSE 13.2", %{device}
+      # is a string like /dev/sda1
+      name = _("%{system_name} at %{device}") % strings
+      RadioButton(Id(dev), name, device == dev)
+    end
+
+    def copy_config_widget
+      CheckBox(Id(:copy_config), _("Copy Whole SSH Configuration"), 
copy_config)
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/ssh_config.rb 
new/yast2-installation-3.1.187/src/lib/installation/ssh_config.rb
--- old/yast2-installation-3.1.184/src/lib/installation/ssh_config.rb   
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/lib/installation/ssh_config.rb   
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,166 @@
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require "yast"
+require "installation/ssh_key"
+require "installation/ssh_config_file"
+require "fileutils"
+
+module Installation
+  # Class that allows to memorize the list of SSH keys and config files found 
in
+  # a filesystem (i.e. the content of the /etc/ssh directory)
+  #
+  # Used by the SSH keys importing functionality.
+  class SshConfig
+    extend Yast::I18n
+    textdomain "installation"
+
+    class << self
+      # Creates a new object with the information read from a filesystem
+      #
+      # @param root_dir [String] Path where the original "/" is mounted
+      def from_dir(root_dir)
+        config = SshConfig.new(name_for(root_dir))
+        dir = ssh_dir(root_dir)
+        config.read_files(dir)
+        config
+      end
+
+      def ssh_dir(root_dir)
+        File.join(root_dir, "etc", "ssh")
+      end
+
+    protected
+
+      def os_release_file(root_dir)
+        File.join(root_dir, "etc", "os-release")
+      end
+
+      # Find out the name for a previous Linux installation.
+      # This uses /etc/os-release which is specified in
+      # https://www.freedesktop.org/software/systemd/man/os-release.html
+      #
+      # @param mount_point [String] Path where the original "/" is mounted
+      # @return [String] Speaking name of the Linux installation
+      def name_for(mount_point)
+        # TRANSLATORS: default name for a found Linux system (we don't know if
+        # it's an openSUSE, Ubuntu...)
+        default_name = _("Linux")
+        os_release = parse_ini_file(os_release_file(mount_point))
+
+        name = os_release["PRETTY_NAME"]
+        if name.empty?
+          name = os_release[NAME] || default_name
+          name += " #{os_release[VERSION]}"
+        end
+        name
+      rescue Errno::ENOENT # No /etc/os-release found
+        default_name
+      end
+
+      # Parse a simple .ini file and return the content in a hash.
+      #
+      # @param filename [String] Name of the file to parse
+      # @return [Hash<String, String>] file content as hash
+      #
+      def parse_ini_file(filename)
+        content = {}
+        File.readlines(filename).each do |line|
+          line = line.lstrip.chomp
+          next if line.empty? || line.start_with?("#")
+          (key, value) = line.split("=")
+          value.gsub!(/^\s*"/, "")
+          value.gsub!(/"\s*$/, "")
+          content[key] = value
+        end
+        content
+      end
+    end
+
+    # @return [String] name to help the user identify the configuration
+    attr_accessor :system_name
+    # @return [Array<SshKey>] keys found in the partition
+    attr_accessor :keys
+    # @return [Array<SshConfigFile>] configuration files found in the partition
+    attr_accessor :config_files
+
+    def initialize(system_name)
+      self.system_name = system_name
+      self.keys = []
+      self.config_files = []
+    end
+
+    # Populates the list of keys and config files from a ssh configuration
+    # directory
+    #
+    # @param dir [String] path of the SSH configuration directory
+    def read_files(dir)
+      filenames = Dir.glob("#{dir}/*")
+
+      # Let's process keys first, pairs of files like "xyz" & "xyz.pub"
+      pub_key_filenames = filenames.select { |f| 
f.end_with?(SshKey::PUBLIC_FILE_SUFFIX) }
+      pub_key_filenames.each do |pub_file|
+        # Remove the .pub suffix
+        priv_file = pub_file.chomp(SshKey::PUBLIC_FILE_SUFFIX)
+        add_key(priv_file)
+        filenames.delete(pub_file)
+        filenames.delete(priv_file)
+      end
+
+      filenames.each do |name|
+        add_config_file(name)
+      end
+    end
+
+    # Writes keys and/or configuration files to a filesystem
+    #
+    # @param root_dir [String] path where the target filesystem (/) is mounted
+    # @param write_keys [Boolean] whether to copy the keys
+    # @param write_config_files [Boolean] whether to copy the config files
+    def write_files(root_dir, write_keys: true, write_config_files: true)
+      dir = self.class.ssh_dir(root_dir)
+      ::FileUtils.mkdir_p(dir)
+
+      keys.each { |k| k.write_files(dir) } if write_keys
+      config_files.each { |f| f.write(dir) } if write_config_files
+    end
+
+    # Access time of the most recently accessed SSH key.
+    #
+    # Needed to keep the default behavior backward compatible.
+    #
+    # @return [Time]
+    def keys_atime
+      keys.map(&:atime).max
+    end
+
+  protected
+
+    def add_key(priv_filename)
+      key = SshKey.new(File.basename(priv_filename))
+      key.read_files(priv_filename)
+      keys << key
+    end
+
+    def add_config_file(filename)
+      file = SshConfigFile.new(File.basename(filename))
+      file.read(filename)
+      config_files << file
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/ssh_config_file.rb 
new/yast2-installation-3.1.187/src/lib/installation/ssh_config_file.rb
--- old/yast2-installation-3.1.184/src/lib/installation/ssh_config_file.rb      
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/lib/installation/ssh_config_file.rb      
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,61 @@
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require "fileutils"
+
+module Installation
+  # Class that allows to memorize a particular SSH config file found in a
+  # partition.
+  #
+  # Used by the SSH configuration importing functionality.
+  class SshConfigFile
+    BACKUP_SUFFIX = ".yast.orig"
+
+    # @return [String] file name
+    attr_accessor :name
+    # @return [Time] access time of the original file
+    attr_accessor :atime
+    # @return [String] content of the file
+    attr_accessor :content
+    # @return [Fixmum] mode of the original file. @see File.chmod
+    attr_accessor :permissions
+
+    def initialize(name)
+      @name = name
+    end
+
+    def read(path)
+      self.content = IO.read(path)
+      self.atime = File.atime(path)
+      self.permissions = File.stat(path).mode
+    end
+
+    def write(dir)
+      path = File.join(dir, name)
+      backup(path)
+      IO.write(path, content)
+      File.chmod(permissions, path)
+    end
+
+  protected
+
+    def backup(filename)
+      ::FileUtils.mv(filename, filename + BACKUP_SUFFIX) if 
File.exist?(filename)
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/ssh_importer.rb 
new/yast2-installation-3.1.187/src/lib/installation/ssh_importer.rb
--- old/yast2-installation-3.1.184/src/lib/installation/ssh_importer.rb 
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/lib/installation/ssh_importer.rb 
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,106 @@
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require "installation/ssh_config"
+
+module Installation
+  # Entry point for the SSH keys importing functionality.
+  #
+  # This singleton class provides methods to hold a list of configurations 
found
+  # in the hard disk and to copy its files to the target system
+  class SshImporter
+    include Singleton
+
+    # @return [String] device name of the source filesystem (i.e. the
+    # SshConfig to copy the keys from)
+    attr_accessor :device
+    # @return [boolean] whether to copy also the config files in addition to 
the
+    # keys
+    attr_accessor :copy_config
+    # @return [Hash{String => SshConfig}] found configurations, indexed by 
device
+    # name
+    attr_reader :configurations
+
+    alias_method :copy_config?, :copy_config
+
+    def initialize
+      @configurations = {}
+      reset
+    end
+
+    # Set default settings (#device and #copy_config?)
+    #
+    # To ensure backwards compatibility, the default behavior is to copy the 
SSH
+    # keys, but not other config files, from the most recently accessed config
+    def reset
+      set_device
+      @copy_config = false
+    end
+
+    # Reads ssh keys and config files from a given root directory, stores the
+    # information in #configurations and updates #device according to the
+    # default behavior.
+    #
+    # Directories without keys in /etc/ssh are ignored.
+    #
+    # @param root_dir [String] Path where the original "/" is mounted
+    # @param device [String] Name of the mounted device
+    def add_config(root_dir, device)
+      config = SshConfig.from_dir(root_dir)
+      return if config.keys.empty?
+
+      configurations[device] = config
+      set_device
+    end
+
+    # Writes the SSH keys from the selected device (and also other 
configuration
+    # files if #copy_config? is true) in the target filesystem
+    #
+    # @param root_dir [String] Path to use as "/" to locate the ssh directory
+    def write(root_dir)
+      return unless device
+      configurations[device].write_files(
+        root_dir,
+        write_keys:         true,
+        write_config_files: copy_config
+      )
+    end
+
+  protected
+
+    # Sets #device according to the logic implemented in the old
+    # "copy_to_system" feature, to ensure backwards compatibility. That means
+    # selecting the device which contains the most recently accessed (atime)
+    # key file.
+    #
+    # For some background, see fate#300421, fate#305019, fate#319624
+    def set_device
+      if configurations.empty?
+        @device = nil
+      else
+        with_atime = configurations.to_a.select { |_dev, config| 
config.keys_atime }
+        if with_atime.empty?
+          @device = configurations.keys.first
+        else
+          recent = with_atime.max_by { |_dev, config| config.keys_atime }
+          @device = recent.first
+        end
+      end
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/src/lib/installation/ssh_key.rb 
new/yast2-installation-3.1.187/src/lib/installation/ssh_key.rb
--- old/yast2-installation-3.1.184/src/lib/installation/ssh_key.rb      
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/src/lib/installation/ssh_key.rb      
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1,64 @@
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+module Installation
+  # Class that allows to memorize a particular SSH keys found in a partition.
+  #
+  # Used to implement the SSH keys importing functionality.
+  class SshKey
+    PUBLIC_FILE_SUFFIX = ".pub"
+
+    # @return [String] name for the user to identify the key
+    attr_accessor :name
+    # @return [Time] access time of the most recently accessed file
+    attr_accessor :atime
+    # @return [Array<Keyfile>] list of files associated to the key
+    attr_accessor :files
+
+    def initialize(name)
+      @name = name
+      @files = []
+    end
+
+    def read_files(priv_filename)
+      add_file(priv_filename) if File.exist?(priv_filename)
+      pub_filename = priv_filename + PUBLIC_FILE_SUFFIX
+      add_file(pub_filename) if File.exist?(pub_filename)
+    end
+
+    def write_files(dir)
+      files.each do |file|
+        path = File.join(dir, file.filename)
+        IO.write(path, file.content)
+        File.chmod(file.permissions, path)
+      end
+    end
+
+  protected
+
+    KeyFile = Struct.new(:filename, :content, :permissions)
+
+    def add_file(path)
+      content = IO.read(path)
+      permissions = File.stat(path).mode
+      files << KeyFile.new(File.basename(path), content, permissions)
+      atime = File.atime(path)
+      self.atime = atime unless self.atime && self.atime > atime
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/driver_update_test.rb 
new/yast2-installation-3.1.187/test/driver_update_test.rb
--- old/yast2-installation-3.1.184/test/driver_update_test.rb   2016-05-09 
14:24:55.000000000 +0200
+++ new/yast2-installation-3.1.187/test/driver_update_test.rb   2016-05-18 
11:28:09.000000000 +0200
@@ -7,8 +7,6 @@
 Yast.import "Linuxrc"
 
 describe Installation::DriverUpdate do
-  FIXTURES_DIR = Pathname.new(__FILE__).dirname.join("fixtures")
-
   subject(:update) { Installation::DriverUpdate.new(update_path) }
 
   let(:update_path) { FIXTURES_DIR.join("updates", "dud_000") }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/os-release 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/os-release
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/os-release   
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/os-release   
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+PRETTY_NAME="Operating system 1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/moduli 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/moduli
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/moduli   
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/moduli   
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of moduli file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_config 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_config
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_config       
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_config       
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of ssh_config file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_dsa_key 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_dsa_key
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_dsa_key 
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_dsa_key 
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of ssh_host_dsa_key file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_dsa_key.pub 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_dsa_key.pub
--- 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_dsa_key.pub 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_dsa_key.pub 
    2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of ssh_host_dsa_key.pub file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_key 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_key
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_key     
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_key     
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of ssh_host_key file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_key.pub 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_key.pub
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/ssh_host_key.pub 
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/ssh_host_key.pub 
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of ssh_host_key.pub file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/sshd_config 
new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/sshd_config
--- old/yast2-installation-3.1.184/test/fixtures/root1/etc/ssh/sshd_config      
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root1/etc/ssh/sshd_config      
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root1: content of sshd_config file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/known_hosts 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/known_hosts
--- old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/known_hosts      
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/known_hosts      
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of known_hosts file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_config 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_config
--- old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_config       
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_config       
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of ssh_config file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key
--- 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key 
    2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of ssh_host_ed25519_key file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key.pub
 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key.pub
--- 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key.pub
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_ed25519_key.pub
 2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of ssh_host_ed25519_key.pub file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_key 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_key
--- old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_key     
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_key     
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of ssh_host_key file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_key.pub 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_key.pub
--- old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/ssh_host_key.pub 
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/ssh_host_key.pub 
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of ssh_host_key.pub file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/sshd_config 
new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/sshd_config
--- old/yast2-installation-3.1.184/test/fixtures/root2/etc/ssh/sshd_config      
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root2/etc/ssh/sshd_config      
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root2: content of sshd_config file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-3.1.184/test/fixtures/root3/etc/ssh/ssh_config 
new/yast2-installation-3.1.187/test/fixtures/root3/etc/ssh/ssh_config
--- old/yast2-installation-3.1.184/test/fixtures/root3/etc/ssh/ssh_config       
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/fixtures/root3/etc/ssh/ssh_config       
2016-05-18 11:28:09.000000000 +0200
@@ -0,0 +1 @@
+root3: content of ssh_config file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-3.1.184/test/ssh_config_test.rb 
new/yast2-installation-3.1.187/test/ssh_config_test.rb
--- old/yast2-installation-3.1.184/test/ssh_config_test.rb      1970-01-01 
01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/ssh_config_test.rb      2016-05-18 
11:28:09.000000000 +0200
@@ -0,0 +1,222 @@
+#! /usr/bin/rspec
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require_relative "./test_helper"
+require "installation/ssh_config"
+require "tmpdir"
+require "fileutils"
+
+describe Installation::SshConfig do
+  describe ".from_dir" do
+    textdomain "installation"
+
+    let(:recent_root1_atime) { Time.now }
+    let(:old_root1_atime) { Time.now - 60 }
+    let(:root1_dir) { FIXTURES_DIR.join("root1") }
+    let(:root2_dir) { FIXTURES_DIR.join("root2") }
+
+    before do
+      # The ssh_host private key file is more recent than any other file
+      allow(File).to receive(:atime) do |path|
+        path =~ /ssh_host_key$/ ? recent_root1_atime : old_root1_atime
+      end
+    end
+
+    it "reads the name of the systems with /etc/os-release" do
+      root1 = described_class.from_dir(root1_dir)
+      expect(root1.system_name).to eq "Operating system 1"
+    end
+
+    it "uses 'Linux' as name for systems without /etc/os-release" do
+      root2 = described_class.from_dir(root2_dir)
+      expect(root2.system_name).to eq _("Linux")
+    end
+
+    it "stores all the keys and files with their names" do
+      root1 = described_class.from_dir(root1_dir)
+      root2 = described_class.from_dir(root2_dir)
+
+      expect(root1.config_files.map(&:name)).to contain_exactly(
+        "moduli", "ssh_config", "sshd_config"
+      )
+      expect(root1.keys.map(&:name)).to contain_exactly(
+        "ssh_host_dsa_key", "ssh_host_key"
+      )
+      expect(root2.config_files.map(&:name)).to contain_exactly(
+        "known_hosts", "ssh_config", "sshd_config"
+      )
+      expect(root2.keys.map(&:name)).to contain_exactly(
+        "ssh_host_ed25519_key", "ssh_host_key"
+      )
+    end
+
+    it "stores the content of the config files" do
+      root1 = described_class.from_dir(root1_dir)
+      expect(root1.config_files.map(&:content)).to contain_exactly(
+        "root1: content of moduli file\n",
+        "root1: content of ssh_config file\n",
+        "root1: content of sshd_config file\n"
+      )
+    end
+
+    it "stores the content of both files for the keys" do
+      root1 = described_class.from_dir(root1_dir)
+      contents = root1.keys.map { |k| k.files.map(&:content) }
+      expect(contents).to contain_exactly(
+        ["root1: content of ssh_host_dsa_key file\n", "root1: content of 
ssh_host_dsa_key.pub file\n"],
+        ["root1: content of ssh_host_key file\n", "root1: content of 
ssh_host_key.pub file\n"]
+      )
+    end
+
+    it "uses the most recent file of each key to set #atime" do
+      root1 = described_class.from_dir(root1_dir)
+      host_key = root1.keys.detect { |k| k.name == "ssh_host_key" }
+      host_dsa_key = root1.keys.detect { |k| k.name == "ssh_host_dsa_key" }
+
+      expect(host_key.atime).to eq recent_root1_atime
+      expect(host_dsa_key.atime).to eq old_root1_atime
+    end
+  end
+
+  describe ".write_files" do
+    def permissions(file)
+      format("%o", File.stat(file).mode)[-3..-1]
+    end
+
+    around do |example|
+      # Git does not preserve file permissions (only the executable bit),
+      # so let's copy test/fixtures to a temporal directory and ensure
+      # sensible permissions there
+      Dir.mktmpdir do |dir|
+        ::FileUtils.cp_r(FIXTURES_DIR.join("root1"), dir)
+        Dir.glob("#{dir}/root1/etc/ssh/*").each do |file|
+          if file.end_with?("_key") || file.end_with?("sshd_config") || 
file.end_with?("moduli")
+            File.chmod(0600, file)
+          else
+            File.chmod(0644, file)
+          end
+        end
+        @config = Installation::SshConfig.from_dir(File.join(dir, "root1"))
+      end
+
+      Dir.mktmpdir do |dir|
+        @target_dir = dir
+        example.run
+      end
+    end
+
+    let(:ssh_dir) { File.join(@target_dir, "etc", "ssh") }
+
+    it "creates /etc/ssh/ if it does not exist" do
+      @config.write_files(@target_dir)
+      expect(Dir.glob("#{@target_dir}/etc/*")).to eq ["#{@target_dir}/etc/ssh"]
+    end
+
+    it "reuses /etc/ssh if it's already there" do
+      ::FileUtils.mkdir_p(ssh_dir)
+      ::FileUtils.touch(File.join(ssh_dir, "preexisting_file"))
+
+      @config.write_files(@target_dir, write_keys: false)
+
+      files = Dir.glob("#{ssh_dir}/*")
+      expect(files.size).to eq(@config.config_files.size + 1)
+      expect(files).to include "#{ssh_dir}/preexisting_file"
+    end
+
+    it "writes all the files by default" do
+      @config.write_files(@target_dir)
+
+      target_content = Dir.glob("#{ssh_dir}/*")
+      expect(target_content).to contain_exactly(
+        "#{ssh_dir}/ssh_host_key", "#{ssh_dir}/ssh_host_key.pub",
+        "#{ssh_dir}/ssh_host_dsa_key", "#{ssh_dir}/ssh_host_dsa_key.pub",
+        "#{ssh_dir}/moduli", "#{ssh_dir}/ssh_config", "#{ssh_dir}/sshd_config"
+      )
+    end
+
+    it "writes only the key files if write_config_files is false" do
+      @config.write_files(@target_dir, write_config_files: false)
+
+      target_content = Dir.glob("#{ssh_dir}/*")
+      expect(target_content).to contain_exactly(
+        "#{ssh_dir}/ssh_host_key", "#{ssh_dir}/ssh_host_key.pub",
+        "#{ssh_dir}/ssh_host_dsa_key", "#{ssh_dir}/ssh_host_dsa_key.pub"
+      )
+    end
+
+    it "writes only the config files if write_keys is false" do
+      @config.write_files(@target_dir, write_keys: false)
+
+      target_content = Dir.glob("#{ssh_dir}/*")
+      expect(target_content).to contain_exactly(
+        "#{ssh_dir}/moduli", "#{ssh_dir}/ssh_config", "#{ssh_dir}/sshd_config"
+      )
+    end
+
+    it "preserves original permissions for files and keys" do
+      @config.write_files(@target_dir)
+
+      expect(permissions("#{ssh_dir}/moduli")).to eq "600"
+      expect(permissions("#{ssh_dir}/ssh_config")).to eq "644"
+      expect(permissions("#{ssh_dir}/ssh_host_key")).to eq "600"
+      expect(permissions("#{ssh_dir}/ssh_host_key.pub")).to eq "644"
+    end
+
+    it "backups config files found in the target directory" do
+      ::FileUtils.mkdir_p(ssh_dir)
+      ::FileUtils.touch(File.join(ssh_dir, "moduli"))
+
+      @config.write_files(@target_dir)
+
+      expect(File.exist?(File.join(ssh_dir, "moduli.yast.orig"))).to eq true
+    end
+
+    it "writes the original content for each file" do
+      @config.write_files(@target_dir)
+
+      expect(IO.read("#{ssh_dir}/moduli")).to eq(
+        "root1: content of moduli file\n"
+      )
+      expect(IO.read("#{ssh_dir}/ssh_host_key")).to eq(
+        "root1: content of ssh_host_key file\n"
+      )
+      expect(IO.read("#{ssh_dir}/ssh_host_key.pub")).to eq(
+        "root1: content of ssh_host_key.pub file\n"
+      )
+    end
+  end
+
+  describe "#keys_atime" do
+    subject(:config) { ::Installation::SshConfig.new("name") }
+    let(:now) { Time.now }
+
+    it "returns the access time of the most recently accessed key" do
+      config.keys = [
+        instance_double("Installation::SshKey", atime: now),
+        instance_double("Installation::SshKey", atime: now + 1200),
+        instance_double("Installation::SshKey", atime: now - 1200)
+      ]
+      expect(config.keys_atime).to eq(now + 1200)
+    end
+
+    it "returns nil if no keys has been read" do
+      expect(config.keys_atime).to be_nil
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-3.1.184/test/ssh_importer_test.rb 
new/yast2-installation-3.1.187/test/ssh_importer_test.rb
--- old/yast2-installation-3.1.184/test/ssh_importer_test.rb    1970-01-01 
01:00:00.000000000 +0100
+++ new/yast2-installation-3.1.187/test/ssh_importer_test.rb    2016-05-18 
11:28:09.000000000 +0200
@@ -0,0 +1,178 @@
+#! /usr/bin/rspec
+# Copyright (c) 2016 SUSE LLC.
+#  All Rights Reserved.
+
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of version 2 or 3 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 about this file by physical or electronic mail,
+#  you may find current contact information at www.suse.com
+
+require_relative "./test_helper"
+require "installation/ssh_importer"
+require "installation/ssh_config"
+
+describe Installation::SshImporter do
+  subject(:importer) { Installation::SshImporter.instance }
+
+  describe "#add_config" do
+    before do
+      importer.configurations.clear
+      importer.reset
+    end
+
+    it "stores the configuration if /etc/ssh contains keys" do
+      importer.add_config(FIXTURES_DIR.join("root1"), "dev")
+      expect(importer.configurations).to_not be_empty
+    end
+
+    it "does nothing if there are no keys in /etc/ssh" do
+      importer.add_config(FIXTURES_DIR.join("root3"), "dev")
+      expect(importer.configurations).to be_empty
+    end
+
+    it "does nothing if there is no /etc/ssh directory" do
+      importer.add_config(FIXTURES_DIR.join("root1/etc"), "dev")
+      expect(importer.configurations).to be_empty
+    end
+
+    it "does nothing if the root directory does not exist" do
+      importer.add_config("/non-existent", "dev")
+      expect(importer.configurations).to be_empty
+    end
+
+    context "reading several valid directories" do
+      let(:now) { Time.now }
+      let(:root2_atime) { Time.now - 60 }
+      # We just want all config to have some keys, no matter which
+      let(:keys) { [instance_double(Installation::SshKey)] }
+      let(:root1) { instance_double("Installation::SshConfig", keys_atime: now 
- 1200, keys: keys) }
+      let(:root2) { instance_double("Installation::SshConfig", keys_atime: 
now, keys: keys) }
+      let(:root3) { instance_double("Installation::SshConfig", keys_atime: now 
- 60, keys: keys) }
+
+      before do
+        allow(Installation::SshConfig).to 
receive(:from_dir).with("root1_dir").and_return root1
+        allow(Installation::SshConfig).to 
receive(:from_dir).with("root2_dir").and_return root2
+        allow(Installation::SshConfig).to 
receive(:from_dir).with("root3_dir").and_return root3
+      end
+
+      it "stores the device name and configuration for all systems" do
+        importer.add_config("root1_dir", "/dev/root1")
+        importer.add_config("root2_dir", "/dev/root2")
+        importer.add_config("root3_dir", "/dev/root3")
+
+        expect(importer.configurations).to eq(
+          "/dev/root1" => root1,
+          "/dev/root2" => root2,
+          "/dev/root3" => root3
+        )
+      end
+
+      it "sets #device to the most recently accessed configuration" do
+        expect(importer.device).to be_nil
+        importer.add_config("root1_dir", "/dev/root1")
+        expect(importer.device).to eq "/dev/root1"
+        importer.add_config("root2_dir", "/dev/root2")
+        expect(importer.device).to eq "/dev/root2"
+        importer.add_config("root3_dir", "/dev/root3")
+        expect(importer.device).to eq "/dev/root2"
+      end
+    end
+  end
+
+  describe ".write" do
+    let(:root1) { instance_double("Installation::SshConfig") }
+    let(:root2) { instance_double("Installation::SshConfig") }
+
+    before do
+      allow(importer).to receive(:configurations).and_return(
+        "/dev/root1" => root1,
+        "/dev/root2" => root2
+      )
+    end
+
+    context "if no device is selected" do
+      it "writes nothing" do
+        importer.device = nil
+
+        expect(root1).to_not receive(:write_files)
+        expect(root2).to_not receive(:write_files)
+
+        importer.write("/somewhere")
+      end
+    end
+
+    context "if a device is selected" do
+      before do
+        importer.device = "/dev/root2"
+      end
+
+      context "if #copy_config? is true" do
+        before do
+          importer.copy_config = true
+        end
+
+        it "writes the ssh keys of the choosen device" do
+          expect(root2).to receive(:write_files) do |_dir, flags|
+            expect(flags[:write_keys]).to eq true
+          end
+
+          importer.write("/somewhere")
+        end
+
+        it "writes the config files of the choosen device" do
+          expect(root2).to receive(:write_files) do |_dir, flags|
+            expect(flags[:write_config_files]).to eq true
+          end
+
+          importer.write("/somewhere")
+        end
+
+        it "does not write files from other devices" do
+          allow(root2).to receive(:write_files)
+          expect(root1).to_not receive(:write_files)
+
+          importer.write("/somewhere")
+        end
+      end
+
+      context "if #copy_config? is false" do
+        before do
+          importer.copy_config = false
+        end
+
+        it "writes the ssh keys of the choosen device" do
+          expect(root2).to receive(:write_files) do |_dir, flags|
+            expect(flags[:write_keys]).to eq true
+          end
+
+          importer.write("/somewhere")
+        end
+
+        it "does not write the config files of the choosen device" do
+          expect(root2).to receive(:write_files) do |_dir, flags|
+            expect(flags[:write_config_files]).to eq false
+          end
+
+          importer.write("/somewhere")
+        end
+
+        it "does not write files from other devices" do
+          allow(root2).to receive(:write_files)
+          expect(root1).to_not receive(:write_files)
+
+          importer.write("/somewhere")
+        end
+      end
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-3.1.184/test/test_helper.rb 
new/yast2-installation-3.1.187/test/test_helper.rb
--- old/yast2-installation-3.1.184/test/test_helper.rb  2016-05-09 
14:24:55.000000000 +0200
+++ new/yast2-installation-3.1.187/test/test_helper.rb  2016-05-18 
11:28:09.000000000 +0200
@@ -4,6 +4,9 @@
 
 require "yast"
 require "yast/rspec"
+require "pathname"
+
+FIXTURES_DIR = Pathname.new(__FILE__).dirname.join("fixtures")
 
 # fake AutoinstConfigClass class which is not supported by Ubuntu
 module Yast
@@ -20,11 +23,11 @@
   AutoinstConfig = AutoinstConfigClass.new
 
   # Faked Profile module
-  class Profile
+  class ProfileClass
     def current
     end
   end
-  Profile = Profile.new
+  Profile = ProfileClass.new
 end
 
 if ENV["COVERAGE"]


Reply via email to