Hello community,

here is the log from the commit of package yast2-network for openSUSE:Factory 
checked in at 2017-08-04 11:57:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yast2-network (Old)
 and      /work/SRC/openSUSE:Factory/.yast2-network.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yast2-network"

Fri Aug  4 11:57:29 2017 rev:375 rq:514057 version:3.3.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/yast2-network/yast2-network.changes      
2017-07-17 10:30:57.661344932 +0200
+++ /work/SRC/openSUSE:Factory/.yast2-network.new/yast2-network.changes 
2017-08-04 11:57:31.939275787 +0200
@@ -1,0 +2,38 @@
+Wed Aug  2 13:01:48 UTC 2017 - [email protected]
+
+- bsc#1051624
+  - Fix default values for not existent sysconfig network variables
+    when read.
+- 3.3.5
+
+-------------------------------------------------------------------
+Fri Jul 28 08:46:43 UTC 2017 - [email protected]
+
+- bnc#1050986
+  - fix udev rules export when more than one device is configured
+- bnc#1037727
+  - dhclient configuration warning message does not block AutoYaST
+- 3.3.4
+
+-------------------------------------------------------------------
+Thu Jul 27 15:13:28 UTC 2017 - [email protected]
+
+- bsc#1039851
+  - Host: Load /etc/hosts entries before import the ones defined in
+    a given AutoYaST profile making it backward compatible.
+
+-------------------------------------------------------------------
+Wed Jul 12 05:37:30 UTC 2017 - [email protected]
+
+- bnc#1038717
+  - avoid creating duplicate udev rules in AutoYaST installation
+
+-------------------------------------------------------------------
+Fri Jul 11 09:40:11 UTC 2017 - [email protected]
+
+- bnc#1049814
+  - Moving network setup in AY into first stage completely when
+    the second stage is disabled.
+- 3.3.3
+
+-------------------------------------------------------------------

Old:
----
  yast2-network-3.3.2.tar.bz2

New:
----
  yast2-network-3.3.5.tar.bz2

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

Other differences:
------------------
++++++ yast2-network.spec ++++++
--- /var/tmp/diff_new_pack.JEykcN/_old  2017-08-04 11:57:32.707167403 +0200
+++ /var/tmp/diff_new_pack.JEykcN/_new  2017-08-04 11:57:32.715166273 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-network
-Version:        3.3.2
+Version:        3.3.5
 Release:        0
 BuildArch:      noarch
 

++++++ yast2-network-3.3.2.tar.bz2 -> yast2-network-3.3.5.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/.travis.yml 
new/yast2-network-3.3.5/.travis.yml
--- old/yast2-network-3.3.2/.travis.yml 2017-07-11 10:33:21.247935590 +0200
+++ new/yast2-network-3.3.5/.travis.yml 2017-08-02 15:32:11.173221713 +0200
@@ -5,6 +5,9 @@
 
 before_install:
   - docker build -t yast-network-image .
+  # list the installed packages (just for easier debugging)
+  - docker run --rm -it yast-network-image rpm -qa | sort
+
 script:
   # the "yast-travis-ruby" script is included in the base yastdevel/ruby image
   # see https://github.com/yast/docker-yast-ruby/blob/master/yast-travis-ruby
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/package/yast2-network.changes 
new/yast2-network-3.3.5/package/yast2-network.changes
--- old/yast2-network-3.3.2/package/yast2-network.changes       2017-07-11 
10:33:21.255935590 +0200
+++ new/yast2-network-3.3.5/package/yast2-network.changes       2017-08-02 
15:32:11.181221713 +0200
@@ -1,4 +1,42 @@
 -------------------------------------------------------------------
+Wed Aug  2 13:01:48 UTC 2017 - [email protected]
+
+- bsc#1051624
+  - Fix default values for not existent sysconfig network variables
+    when read.
+- 3.3.5
+
+-------------------------------------------------------------------
+Fri Jul 28 08:46:43 UTC 2017 - [email protected]
+
+- bnc#1050986
+  - fix udev rules export when more than one device is configured
+- bnc#1037727
+  - dhclient configuration warning message does not block AutoYaST
+- 3.3.4
+
+-------------------------------------------------------------------
+Thu Jul 27 15:13:28 UTC 2017 - [email protected]
+
+- bsc#1039851
+  - Host: Load /etc/hosts entries before import the ones defined in
+    a given AutoYaST profile making it backward compatible.
+
+-------------------------------------------------------------------
+Wed Jul 12 05:37:30 UTC 2017 - [email protected]
+
+- bnc#1038717
+  - avoid creating duplicate udev rules in AutoYaST installation
+
+-------------------------------------------------------------------
+Fri Jul 11 09:40:11 UTC 2017 - [email protected]
+
+- bnc#1049814
+  - Moving network setup in AY into first stage completely when
+    the second stage is disabled.
+- 3.3.3
+
+-------------------------------------------------------------------
 Mon Jul 10 15:03:09 UTC 2017 - [email protected]
 
 - Fix crash during write if Host.Read and Host.Import is called
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/package/yast2-network.spec 
new/yast2-network-3.3.5/package/yast2-network.spec
--- old/yast2-network-3.3.2/package/yast2-network.spec  2017-07-11 
10:33:21.255935590 +0200
+++ new/yast2-network-3.3.5/package/yast2-network.spec  2017-08-02 
15:32:11.181221713 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-network
-Version:        3.3.2
+Version:        3.3.5
 Release:        0
 BuildArch:      noarch
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/clients/lan_auto.rb 
new/yast2-network-3.3.5/src/clients/lan_auto.rb
--- old/yast2-network-3.3.2/src/clients/lan_auto.rb     2017-07-11 
10:33:21.259935590 +0200
+++ new/yast2-network-3.3.5/src/clients/lan_auto.rb     2017-08-02 
15:32:11.185221713 +0200
@@ -66,7 +66,7 @@
       elsif @func == "Change"
         @ret = LanAutoSequence("")
       elsif @func == "Import"
-        @new = FromAY(@param)
+        @new = Lan.FromAY(@param)
         # see bnc#498993
         # in case keep_install_network is set to true (in AY)
         # we'll keep values from installation
@@ -124,131 +124,6 @@
       @ret
     end
 
-    # If there's key in m, upcase key and assign the value to ret
-    # @return ret
-    def UpcaseCondSet(ret, m, key)
-      ret = deep_copy(ret)
-      m = deep_copy(m)
-      if Builtins.haskey(m, key)
-        Ops.set(ret, Builtins.toupper(key), Ops.get(m, key))
-      end
-      deep_copy(ret)
-    end
-
-    # Convert data from autoyast to structure used by module.
-    # @param [Hash] input autoyast settings
-    # @return native network settings
-    def FromAY(input)
-      input = deep_copy(input)
-      Builtins.y2debug("input %1", input)
-
-      ifaces = []
-      Builtins.foreach(Ops.get_list(input, "interfaces", [])) do |interface|
-        iface = {}
-        Builtins.foreach(interface) do |key, value|
-          if key == "aliases"
-            Builtins.foreach(
-              Convert.convert(
-                value,
-                from: "any",
-                to:   "map <string, map <string, any>>"
-              )
-            ) do |k, v|
-              # replace "alias0" to "0" (bnc#372687)
-              t = Convert.convert(
-                value,
-                from: "any",
-                to:   "map <string, any>"
-              )
-              Ops.set(t, Ops.get_string(v, "LABEL", ""), Ops.get_map(t, k, {}))
-              t = Builtins.remove(t, k)
-              value = deep_copy(t)
-            end
-          end
-          Ops.set(iface, key, value)
-        end
-        ifaces = Builtins.add(ifaces, iface)
-      end
-      Ops.set(input, "interfaces", ifaces)
-
-      interfaces = Builtins.listmap(Ops.get_list(input, "interfaces", [])) do 
|interface|
-        # input: list of items $[ "device": "d", "foo": "f", "bar": "b"]
-        # output: map of items  "d": $["FOO": "f", "BAR": "b"]
-        new_interface = {}
-        # uppercase map keys
-        newk = nil
-        interface = Builtins.mapmap(interface) do |k, v|
-          newk = if k == "aliases"
-            "_aliases"
-          else
-            Builtins.toupper(k)
-          end
-          { newk => v }
-        end
-        Builtins.foreach(interface) do |k, v|
-          Ops.set(new_interface, k, v) if v != "" && k != "DEVICE"
-        end
-        new_device = Ops.get_string(interface, "DEVICE", "")
-        { new_device => new_interface }
-      end
-
-      # split to a two level map like NetworkInterfaces
-      devices = {}
-
-      Builtins.foreach(interfaces) do |devname, if_data|
-        # devname can be in old-style fashion (eth-bus-<pci_id>). So, convert 
it
-        devname = LanItems.getDeviceName(devname)
-        type = NetworkInterfaces.GetType(devname)
-        d = Ops.get(devices, type, {})
-        Ops.set(d, devname, if_data)
-        Ops.set(devices, type, d)
-      end
-
-      hwcfg = {}
-      if Ops.greater_than(Builtins.size(Ops.get_list(input, "modules", [])), 0)
-        hwcfg = Builtins.listmap(Ops.get_list(input, "modules", [])) do |mod|
-          options = Ops.get_string(mod, "options", "")
-          module_name = Ops.get_string(mod, "module", "")
-          start_mode = Ops.get_string(mod, "startmode", "auto")
-          device_name = Ops.get_string(mod, "device", "")
-          module_data = {
-            "MODULE"         => module_name,
-            "MODULE_OPTIONS" => options,
-            "STARTMODE"      => start_mode
-          }
-          { device_name => module_data }
-        end
-      end
-
-      Ops.set(input, "devices", devices)
-      Ops.set(input, "hwcfg", hwcfg)
-
-      # DHCP:: config: some of it is in the DNS part of the profile
-      dhcp = {}
-      dhcpopts = Ops.get_map(input, "dhcp_options", {})
-      dns = Ops.get_map(input, "dns", {})
-
-      if Builtins.haskey(dns, "dhcp_hostname")
-        Ops.set(
-          dhcp,
-          "DHCLIENT_SET_HOSTNAME",
-          Ops.get_boolean(dns, "dhcp_hostname", false)
-        )
-      end
-
-      dhcp = UpcaseCondSet(dhcp, dhcpopts, "dhclient_client_id")
-      dhcp = UpcaseCondSet(dhcp, dhcpopts, "dhclient_additional_options")
-      dhcp = UpcaseCondSet(dhcp, dhcpopts, "dhclient_hostname_option")
-
-      Ops.set(input, "config", "dhcp" => dhcp)
-      if !Ops.get(input, "strict_IP_check_timeout").nil?
-        Ops.set(input, ["config", "config"], "CHECK_DUPLICATE_IP" => true)
-      end
-
-      Builtins.y2milestone("input=%1", input)
-      deep_copy(input)
-    end
-
     # Convert data from native network to autoyast for XML
     # @param [Hash] settings native network settings
     # @return [Hash] autoyast network settings
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/clients/save_network.rb 
new/yast2-network-3.3.5/src/clients/save_network.rb
--- old/yast2-network-3.3.2/src/clients/save_network.rb 2017-07-11 
10:33:21.259935590 +0200
+++ new/yast2-network-3.3.5/src/clients/save_network.rb 2017-08-02 
15:32:11.185221713 +0200
@@ -259,16 +259,36 @@
       DNS.create_hostname_link
     end
 
+    # Creates target's /etc/hosts configuration
+    #
+    # It uses hosts' configuration as defined in AY profile (if any) or
+    # proceedes according the proposal
+    def configure_hosts
+      configured = false
+      configured = NetworkAutoYast.instance.configure_hosts if Mode.autoinst
+      NetworkAutoconfiguration.instance.configure_hosts if !configured
+    end
+
+    # Invokes configuration of parts which are in charge of Lan module
+    #
+    # Currently it handles just AutoYaST installation. It just exits in case
+    # of common installation as there currently is nothing to do.
+    def configure_lan
+      return if !Mode.autoinst
+
+      NetworkAutoYast.instance.configure_lan
+    end
+
     # It does an automatic configuration of installed system
     #
     # Basically, it runs several proposals.
     def configure_target
       NetworkAutoconfiguration.instance.configure_virtuals
 
-      configure_dns
+      configure_lan
 
       # this depends on DNS configuration
-      NetworkAutoconfiguration.instance.configure_hosts
+      configure_hosts
 
       set_network_service
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/include/network/routines.rb 
new/yast2-network-3.3.5/src/include/network/routines.rb
--- old/yast2-network-3.3.2/src/include/network/routines.rb     2017-07-11 
10:33:21.267935590 +0200
+++ new/yast2-network-3.3.5/src/include/network/routines.rb     2017-08-02 
15:32:11.189221713 +0200
@@ -51,6 +51,7 @@
       Yast.import "TypeRepository"
       Yast.import "Stage"
       Yast.import "PackagesProposal"
+      Yast.import "Report"
     end
 
     # Abort function
@@ -978,7 +979,7 @@
     # @param [Array<String>] list of incorrectly configured devices
     # @return [void]
     def fix_dhclient_warning(devs)
-      Popup.Warning(fix_dhclient_msg(devs))
+      Report.Warning(fix_dhclient_msg(devs))
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-network-3.3.2/src/lib/network/network_autoyast.rb 
new/yast2-network-3.3.5/src/lib/network/network_autoyast.rb
--- old/yast2-network-3.3.2/src/lib/network/network_autoyast.rb 2017-07-11 
10:33:21.279935590 +0200
+++ new/yast2-network-3.3.5/src/lib/network/network_autoyast.rb 2017-08-02 
15:32:11.193221713 +0200
@@ -20,6 +20,8 @@
       Yast.import "Lan"
       Yast.import "LanItems"
       Yast.import "Linuxrc"
+      Yast.import "Host"
+      Yast.import "Routing"
     end
 
     # Merges existing config from system into given configuration map
@@ -104,26 +106,39 @@
       NetworkService.EnableDisableNow
     end
 
-    # Initializates DNS setup according AY profile
+    # Initializates NICs setup according AY profile
     #
-    # FIXME: it currently doesn't write DNS configuration. It is used for 
initialization
-    # of DNS setup according AY profile in 1st stage as part of network setup 
was moved
-    # here already and some parts of network configuration needs to know it. 
DNS write
-    # is still done in 2nd stage.
+    # If the installer is running in 1st stage mode only, then the 
configuration
+    # is also written
     #
+    # @param [Boolean] write forces instant writing of the configuration
     # @return [Boolean] true when configuration was present and loaded from 
the profile
-    def configure_dns
-      ay_dns_config = ay_networking_section["dns"]
+    def configure_lan(write: false)
+      log.info("NetworkAutoYast: Lan configuration")
 
-      return false if !ay_dns_config
+      ay_configuration = Lan.FromAY(ay_networking_section)
+      ay_configuration = 
NetworkAutoYast.instance.merge_configs(ay_configuration) if keep_net_config?
 
-      DNS.Import(ay_dns_config)
+      configure_submodule(Lan, ay_configuration, write: write)
+    end
+
+    # Initializates /etc/hosts according AY profile
+    #
+    # If the installer is running in 1st stage mode only, then the 
configuration
+    # is also written
+    #
+    # @param [Boolean] write forces instant writing of the configuration
+    # @return [Boolean] true when configuration was present and loaded from 
the profile
+    def configure_hosts(write: false)
+      log.info("NetworkAutoYast: Hosts configuration")
 
-      log.info("NetworkAutoYast: DNS / Hostname configuration")
-      log.info("dhcp hostname: #{DNS.dhcp_hostname}")
-      log.info("write hostname: #{DNS.write_hostname}")
+      hosts_config = (ay_host_section["hosts"] || {}).map do |host|
+        # we need to guarantee order of the items here
+        [host["host_address"] || "", host["names"] || []]
+      end
+      hosts_config = hosts_config.to_h.delete_if { |k, v| k.empty? || v.empty? 
}
 
-      true
+      configure_submodule(Host, "hosts" => hosts_config, write: write)
     end
 
     # Checks if the profile asks for keeping installation network configuration
@@ -206,16 +221,53 @@
       instsys_routing.merge(ay_routing)
     end
 
-    # Returns networking section of current AY profile
-    def ay_networking_section
+    # Returns current AY profile in the internal representation
+    #
+    # @return [Hash] hash representing current profile or empty hash
+    def ay_current_profile
       Yast.import "Profile"
 
       ay_profile = Profile.current
 
       return {} if ay_profile.nil? || ay_profile.empty?
-      return {} if ay_profile["networking"].nil?
+      ay_profile
+    end
 
-      ay_profile["networking"]
+    # Returns networking section of current AY profile
+    def ay_networking_section
+      return {} if ay_current_profile["networking"].nil?
+
+      ay_current_profile["networking"]
+    end
+
+    # Returns global section of current AY profile
+    def ay_general_section
+      return {} if ay_current_profile["general"].nil?
+
+      ay_current_profile["general"]
+    end
+
+    # Returns host section of the current AY profile
+    #
+    # Note that autoyast transforms the host's subsection
+    # into:
+    # {
+    #   hosts => [
+    #     # first <host_entry>
+    #     {
+    #       "host_address" => <ip>,
+    #       "names" => [list, of, names]
+    #     }
+    #     # second <host_entry>
+    #     ...
+    #   ]
+    # }
+    #
+    # return <Hash> with hosts configuration
+    def ay_host_section
+      return {} if ay_current_profile["host"].nil?
+
+      ay_current_profile["host"]
     end
 
     # Checks if the udev rule is valid for renaming a NIC
@@ -227,6 +279,38 @@
       true
     end
 
+    # Renames a network device represented by given item.
+    #
+    # @param [Integer] item is an item id. See LanItems for detail
+    # @param [String] name_to new device name
+    # @param [String] attr an udev attribute usable in NIC's rule. Currently 
just
+    #  "KERNELS" or "ATTR{address}" makes sense. This parameter is optional
+    # @param [String] value for the given udev attribute. Optional parameter.
+    def rename_lan_item(item, name_to, attr = nil, key = nil)
+      return if item.nil? || item < 0 || item >= LanItems.Items.size
+      return if name_to.nil? || name_to.empty?
+
+      # selecting according device name is unreliable (selects only in between 
configured devices)
+      LanItems.current = item
+
+      if !attr.nil? && !key.nil?
+        # find out what attribude is currently used for setting device name and
+        # change it if needed. Currently mac is used by default. So, we check 
is it is
+        # the other one (busid). If no we defaults to mac.
+        bus_attr = LanItems.GetItemUdev("KERNELS")
+        current_attr = bus_attr.empty? ? "ATTR{address}" : "KERNELS"
+
+        # make sure that we base renaming on defined attribute with value 
given in AY profile
+        LanItems.ReplaceItemUdev(current_attr, attr, key)
+      elsif attr.nil? ^ key.nil? # xor
+        raise ArgumentError, "Not enough data for udev rule definition"
+      end
+
+      LanItems.rename(name_to)
+
+      nil
+    end
+
     # Takes a list of udev rules and assignes them to corresponding devices.
     #
     # If a device has an udev rule defined already, it is overwritten by new 
one.
@@ -242,28 +326,47 @@
         next if !valid_rename_udev_rule?(rule)
         key.downcase!
 
+        # find item which matches to the given rule definition
         item, matching_item = LanItems.Items.find do |_, i|
           i["hwinfo"]["busid"].downcase == key || i["hwinfo"]["mac"].downcase 
== key
         end
         next if !matching_item
 
-        # for logging only
         name_from = matching_item["ifcfg"] || matching_item["dev_name"]
         log.info("Matching device found - renaming <#{name_from}> -> 
<#{name_to}>")
 
-        # selecting according device name is unreliable (selects only in 
between configured devices)
-        LanItems.current = item
+        # find rule in collision
+        colliding_item, _item_map = LanItems.Items.find do |i, _|
+          LanItems.GetDeviceName(i) == name_to
+        end
 
-        # find out what attribude is currently used for setting device name and
-        # change it if needed. Currently mac is used by default. So, we check 
is it is
-        # the other one (busid). If no we defaults to mac.
-        bus_attr = LanItems.GetItemUdev("KERNELS")
-        current_attr = bus_attr.empty? ? "ATTR{address}" : "KERNELS"
+        # rename item in collision
+        rename_lan_item(colliding_item, name_from)
 
-        # make sure that we base renaming on defined attribute with value 
given in AY profile
-        LanItems.ReplaceItemUdev(current_attr, attr, key)
-        LanItems.rename(name_to)
+        # rename matching item
+        rename_lan_item(item, name_to, attr, key)
       end
     end
+
+    # Configures given yast submodule according AY configuration
+    #
+    # It takes data from AY profile transformed into a format expected by the 
YaST
+    # sub module's Import method.
+    #
+    # It imports the profile, configures the module and writes the 
configuration.
+    # Writing the configuration is optional when second stage is available and 
mandatory
+    # when running autoyast installation with first stage only.
+    def configure_submodule(yast_module, ay_config, write: false)
+      return false if !ay_config
+
+      yast_module.Import(ay_config)
+
+      write ||= !ay_general_section.fetch("mode", "second_stage" => 
true)["second_stage"]
+      log.info("Write configuration instantly: #{write}")
+
+      yast_module.Write(gui: false) if write
+
+      true
+    end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/modules/DNS.rb 
new/yast2-network-3.3.5/src/modules/DNS.rb
--- old/yast2-network-3.3.2/src/modules/DNS.rb  2017-07-11 10:33:21.279935590 
+0200
+++ new/yast2-network-3.3.5/src/modules/DNS.rb  2017-08-02 15:32:11.193221713 
+0200
@@ -280,16 +280,12 @@
     # Includes Host,NetworkConfig::Read
     # @return true if success
     def Read
-      return true if @initialized == true
+      return true if @initialized
 
-      tmp1 = Convert.to_string(
-        SCR.Read(path(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"))
-      )
-      @dhcp_hostname = tmp1 == "yes"
-      tmp2 = Convert.to_string(
-        SCR.Read(path(".sysconfig.network.dhcp.WRITE_HOSTNAME_TO_HOSTS"))
-      )
-      @write_hostname = tmp2 == "yes"
+      # Used false as "no" is the default value in sysconfig for both
+      # attributes (bsc#bug_1051624)
+      @dhcp_hostname = dhclient_set_hostname || false
+      @write_hostname = get_write_hostname_to_hosts || false
 
       @resolv_conf_policy = Convert.to_string(
         SCR.Read(path(".sysconfig.network.config.NETCONFIG_DNS_POLICY"))
@@ -325,59 +321,13 @@
       Builtins.y2milestone("domain=%1", @domain)
 
       @initialized = true
-      true
     end
 
     # Write new DNS and hostname settings
     # Includes Host,NetworkConfig::Write
     # @return true if success
-    def Write
-      # build FQ hostname
-      fqhostname = Hostname.MergeFQ(@hostname, @domain)
-
-      # We do not collect static IP addresses here, as hostnames
-      # are defined for each static IP separately in address dialog
-      # FaTE #2202
-
-      @oldhostname = fqhostname # #49634
-
-      # ensure that nothing is saved in case old values are the same, as it 
makes
-      # rcnetwork reload restart all interfaces (even 'touch 
/etc/sysconfig/network/dhcp'
-      # is sufficient)
-      tmp = SCR.Read(path(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"))
-      old_dhcp_hostname = tmp == "yes"
-
-      tmp = SCR.Read(path(".sysconfig.network.dhcp.WRITE_HOSTNAME_TO_HOSTS"))
-      old_write_hostname = tmp == "yes"
-
-      if old_dhcp_hostname != dhcp_hostname || old_write_hostname != 
write_hostname
-        SCR.Write(
-          path(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"),
-          @dhcp_hostname ? "yes" : "no"
-        )
-        SCR.Write(
-          path(".sysconfig.network.dhcp.WRITE_HOSTNAME_TO_HOSTS"),
-          @write_hostname ? "yes" : "no"
-        )
-        SCR.Write(path(".sysconfig.network.dhcp"), nil)
-      end
-
-      Builtins.y2milestone("Writing configuration")
-      if !@modified
-        Builtins.y2milestone("No changes to DNS -> nothing to write")
-        return true
-      end
-
-      Builtins.y2milestone("nameservers=%1", @nameservers)
-      Builtins.y2milestone("searchlist=%1", @searchlist)
-      Builtins.y2milestone("hostname=%1", @hostname)
-      Builtins.y2milestone("domain=%1", @domain)
-      Builtins.y2milestone(
-        "dhcp_hostname=%1, write_hostname=%2",
-        @dhcp_hostname,
-        @write_hostname
-      )
-
+    def Write(gui: true)
+      # Write process description labels
       steps = [
         # Progress stage 1
         _("Write hostname"),
@@ -387,65 +337,37 @@
         _("Update /etc/resolv.conf")
       ]
 
+      # ensure that nothing is saved in case old values are the same, as it 
makes
+      # rcnetwork reload restart all interfaces (even 'touch 
/etc/sysconfig/network/dhcp'
+      # is sufficient)
+      update_sysconfig_dhcp
+
+      log.info("DNS: Writing configuration")
+      if !@modified
+        log.info("No changes to DNS -> nothing to write")
+        return true
+      end
+
       # Write dialog caption
       caption = _("Saving Hostname and DNS Configuration")
-      sl = 0 # 100; for testing
-
-      Progress.New(caption, " ", Builtins.size(steps), steps, [], "")
 
-      # Allow to set hostname even if it's modified by DHCP (#13427)
-      # if(NetworkConfig::DHCP["DHCLIENT_SET_HOSTNAME"]:false != true) {
+      Progress.New(caption, " ", Builtins.size(steps), steps, [], "") if gui
 
       # Progress step 1/3
-      ProgressNextStage(_("Writing hostname..."))
-
-      # change the hostname
-      SCR.Execute(path(".target.bash"), Ops.add("/bin/hostname ", @hostname))
-
-      # write hostname
-      SCR.Write(
-        path(".target.string"),
-        HOSTNAME_PATH,
-        Ops.add(fqhostname, "\n")
-      )
-
-      create_hostname_link
-
-      Builtins.sleep(sl)
+      ProgressNextStage(_("Writing hostname...")) if gui
+      update_hostname
 
       # Progress step 2/3
-      ProgressNextStage(_("Updating configuration..."))
-
-      # Finish him
+      ProgressNextStage(_("Updating configuration...")) if gui
       update_mta_config
-      Builtins.sleep(sl)
-
-      #     if(SCR::Read(.target.size, resolv_conf) < 0)
-      # SCR::Write(.target.string, resolv_conf, "");
 
       # Progress step 3/3
-      ProgressNextStage(_("Updating /etc/resolv.conf ..."))
-
-      SCR.Write(
-        path(".sysconfig.network.config.NETCONFIG_DNS_POLICY"),
-        @resolv_conf_policy
-      )
-      SCR.Write(
-        path(".sysconfig.network.config.NETCONFIG_DNS_STATIC_SEARCHLIST"),
-        Builtins.mergestring(@searchlist, " ")
-      )
-      SCR.Write(
-        path(".sysconfig.network.config.NETCONFIG_DNS_STATIC_SERVERS"),
-        Builtins.mergestring(@nameservers, " ")
-      )
-      SCR.Write(path(".sysconfig.network.config"), nil)
-
-      SCR.Execute(path(".target.bash"), "/sbin/netconfig update")
-
-      Builtins.sleep(sl)
+      ProgressNextStage(_("Updating /etc/resolv.conf ...")) if gui
+      update_sysconfig_config
 
-      Progress.NextStage
+      Progress.NextStage if gui
       @modified = false
+
       true
     end
 
@@ -720,6 +642,90 @@
       @nameservers.empty? && @searchlist.empty? && @hostname.empty? && 
@domain.empty?
     end
 
+    # Updates /etc/sysconfig/network/dhcp
+    def update_sysconfig_dhcp
+      if dhclient_set_hostname != @dhcp_hostname || 
get_write_hostname_to_hosts != @write_hostname
+        log.info("dhcp_hostname=#{@dhcp_hostname}")
+        log.info("write_hostname=#{@write_hostname}")
+
+        # @dhcp_hostname and @wrote_hostname can currently be nil only when
+        # not present in original file. So, do not add it in such case.
+        SCR.Write(
+          path(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"),
+          @dhcp_hostname ? "yes" : "no"
+        ) if !@dhcp_hostname.nil?
+        SCR.Write(
+          path(".sysconfig.network.dhcp.WRITE_HOSTNAME_TO_HOSTS"),
+          @write_hostname ? "yes" : "no"
+        ) if !@write_hostname.nil?
+        SCR.Write(path(".sysconfig.network.dhcp"), nil)
+      else
+        log.info("No update for /etc/sysconfig/network/dhcp")
+      end
+    end
+
+    # Updates system with new hostname
+    def update_hostname
+      log.info("hostname=#{@hostname}")
+      log.info("domain=#{@domain}")
+
+      # change the hostname
+      SCR.Execute(path(".target.bash"), Ops.add("/bin/hostname ", @hostname))
+
+      # build and write FQDN hostname
+      fqhostname = Hostname.MergeFQ(@hostname, @domain)
+      @oldhostname = fqhostname # #49634
+
+      SCR.Write(
+        path(".target.string"),
+        HOSTNAME_PATH,
+        Ops.add(fqhostname, "\n")
+      )
+
+      create_hostname_link
+    end
+
+    # Updates /etc/sysconfig/network/config
+    def update_sysconfig_config
+      log.info("nameservers=#{@nameservers}")
+      log.info("searchlist=#{@searchlist}")
+
+      SCR.Write(
+        path(".sysconfig.network.config.NETCONFIG_DNS_POLICY"),
+        @resolv_conf_policy
+      )
+      SCR.Write(
+        path(".sysconfig.network.config.NETCONFIG_DNS_STATIC_SEARCHLIST"),
+        Builtins.mergestring(@searchlist, " ")
+      )
+      SCR.Write(
+        path(".sysconfig.network.config.NETCONFIG_DNS_STATIC_SERVERS"),
+        Builtins.mergestring(@nameservers, " ")
+      )
+      SCR.Write(path(".sysconfig.network.config"), nil)
+
+      SCR.Execute(path(".target.bash"), "/sbin/netconfig update")
+    end
+
+    # A constant for translating sysconfig's yes/no values into boolean
+    SYSCFG_TO_BOOL = { "yes" => true, "no" => false }.freeze
+
+    # Reads value of DHCLIENT_SET_HOSTNAME and translates it to boolean
+    #
+    # return {true, false, nil} "yes" => true, "no" => false, otherwise or not
+    # present => nil
+    def dhclient_set_hostname
+      
SYSCFG_TO_BOOL[SCR.Read(path(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"))]
+    end
+
+    # Reads value of WRITE_HOSTNAME_TO_HOSTS and translates it to boolean
+    #
+    # return {true, false, nil} "yes" => true, "no" => false, otherwise or not
+    # present => nil
+    def get_write_hostname_to_hosts
+      
SYSCFG_TO_BOOL[SCR.Read(path(".sysconfig.network.dhcp.WRITE_HOSTNAME_TO_HOSTS"))]
+    end
+
     publish variable: :proposal_valid, type: "boolean"
     publish variable: :hostname, type: "string"
     publish variable: :domain, type: "string"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/modules/Host.rb 
new/yast2-network-3.3.5/src/modules/Host.rb
--- old/yast2-network-3.3.2/src/modules/Host.rb 2017-07-11 10:33:21.279935590 
+0200
+++ new/yast2-network-3.3.5/src/modules/Host.rb 2017-08-02 15:32:11.193221713 
+0200
@@ -127,7 +127,7 @@
 
     # Write hosts settings and apply changes
     # @return true if success
-    def Write
+    def Write(gui: false)
       Builtins.y2milestone("Writing hosts configuration")
 
       if !@modified
@@ -141,13 +141,15 @@
         return true
       end
 
-      steps = [_("Update /etc/hosts")]
+      if gui
+        steps = [_("Update /etc/hosts")]
 
-      caption = _("Saving Hostname Configuration")
+        caption = _("Saving Hostname Configuration")
 
-      Progress.New(caption, " ", steps.size, steps, [], "")
+        Progress.New(caption, " ", steps.size, steps, [], "")
 
-      ProgressNextStage(_("Updating /etc/hosts ..."))
+        ProgressNextStage(_("Updating /etc/hosts ..."))
+      end
 
       # backup if exists
       if SCR.Read(path(".target.size"), CFA::Hosts::PATH) >= 0
@@ -156,7 +158,7 @@
 
       @hosts.save
 
-      Progress.NextStage
+      Progress.NextStage if gui
 
       true
     end
@@ -164,13 +166,18 @@
     # Get all the Hosts configuration from a map.
     # When called by hosts_auto (preparing autoinstallation data)
     # the map may be empty.
+    #
     # @param [Hash] settings autoinstallation settings
+    #               expected format of settings["hosts"] is { "ip" => [list, 
of, names] }
     # @return true if success
     def Import(settings)
       @modified = true # trigger Write
       @initialized = true # don't let Read discard our data
 
       @hosts = CFA::Hosts.new
+      # Load default entries (bsc#1039851)
+      @hosts.load
+
       imported_hosts = Builtins.eval(Ops.get_map(settings, "hosts", {}))
 
       # convert from old format to the new one
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/modules/Lan.rb 
new/yast2-network-3.3.5/src/modules/Lan.rb
--- old/yast2-network-3.3.2/src/modules/Lan.rb  2017-07-11 10:33:21.283935590 
+0200
+++ new/yast2-network-3.3.5/src/modules/Lan.rb  2017-08-02 15:32:11.193221713 
+0200
@@ -473,7 +473,7 @@
 
     # Update the SCR according to network settings
     # @return true on success
-    def Write
+    def Write(gui: true)
       Builtins.y2milestone("Writing configuration")
 
       # Query modified flag in all components, not just LanItems - DNS,
@@ -547,7 +547,7 @@
       # Progress step 5
       ProgressNextStage(_("Writing routing configuration..."))
       orig = Progress.set(false)
-      Routing.Write
+      Routing.Write(gui: gui)
       Progress.set(orig)
       Builtins.sleep(sl)
 
@@ -557,9 +557,9 @@
       # write resolv.conf after change from dhcp to static (#327074)
       # reload/restart network before this to put correct resolv.conf from 
dhcp-backup
       orig = Progress.set(false)
-      DNS.Write
+      DNS.Write(gui: gui)
       Host.EnsureHostnameResolvable
-      Host.Write
+      Host.Write(gui: gui)
       Progress.set(orig)
 
       Builtins.sleep(sl)
@@ -644,6 +644,132 @@
       Write()
     end
 
+    # If there's key in m, upcase key and assign the value to ret
+    # @return ret
+    def UpcaseCondSet(ret, m, key)
+      ret = deep_copy(ret)
+      m = deep_copy(m)
+      if Builtins.haskey(m, key)
+        Ops.set(ret, Builtins.toupper(key), Ops.get(m, key))
+      end
+      deep_copy(ret)
+    end
+
+    # Convert data from autoyast to structure used by module.
+    # @param [Hash] input autoyast settings
+    # @return native network settings
+    # FIXME: massive refactoring required
+    def FromAY(input)
+      input = deep_copy(input)
+      Builtins.y2debug("input %1", input)
+
+      ifaces = []
+      Builtins.foreach(Ops.get_list(input, "interfaces", [])) do |interface|
+        iface = {}
+        Builtins.foreach(interface) do |key, value|
+          if key == "aliases"
+            Builtins.foreach(
+              Convert.convert(
+                value,
+                from: "any",
+                to:   "map <string, map <string, any>>"
+              )
+            ) do |k, v|
+              # replace "alias0" to "0" (bnc#372687)
+              t = Convert.convert(
+                value,
+                from: "any",
+                to:   "map <string, any>"
+              )
+              Ops.set(t, Ops.get_string(v, "LABEL", ""), Ops.get_map(t, k, {}))
+              t = Builtins.remove(t, k)
+              value = deep_copy(t)
+            end
+          end
+          Ops.set(iface, key, value)
+        end
+        ifaces = Builtins.add(ifaces, iface)
+      end
+      Ops.set(input, "interfaces", ifaces)
+
+      interfaces = Builtins.listmap(Ops.get_list(input, "interfaces", [])) do 
|interface|
+        # input: list of items $[ "device": "d", "foo": "f", "bar": "b"]
+        # output: map of items  "d": $["FOO": "f", "BAR": "b"]
+        new_interface = {}
+        # uppercase map keys
+        newk = nil
+        interface = Builtins.mapmap(interface) do |k, v|
+          newk = if k == "aliases"
+            "_aliases"
+          else
+            Builtins.toupper(k)
+          end
+          { newk => v }
+        end
+        Builtins.foreach(interface) do |k, v|
+          Ops.set(new_interface, k, v) if v != "" && k != "DEVICE"
+        end
+        new_device = Ops.get_string(interface, "DEVICE", "")
+        { new_device => new_interface }
+      end
+
+      # split to a two level map like NetworkInterfaces
+      devices = {}
+
+      Builtins.foreach(interfaces) do |devname, if_data|
+        # devname can be in old-style fashion (eth-bus-<pci_id>). So, convert 
it
+        devname = LanItems.getDeviceName(devname)
+        type = NetworkInterfaces.GetType(devname)
+        d = Ops.get(devices, type, {})
+        Ops.set(d, devname, if_data)
+        Ops.set(devices, type, d)
+      end
+
+      hwcfg = {}
+      if Ops.greater_than(Builtins.size(Ops.get_list(input, "modules", [])), 0)
+        hwcfg = Builtins.listmap(Ops.get_list(input, "modules", [])) do |mod|
+          options = Ops.get_string(mod, "options", "")
+          module_name = Ops.get_string(mod, "module", "")
+          start_mode = Ops.get_string(mod, "startmode", "auto")
+          device_name = Ops.get_string(mod, "device", "")
+          module_data = {
+            "MODULE"         => module_name,
+            "MODULE_OPTIONS" => options,
+            "STARTMODE"      => start_mode
+          }
+          { device_name => module_data }
+        end
+      end
+
+      Ops.set(input, "devices", devices)
+      Ops.set(input, "hwcfg", hwcfg)
+
+      # DHCP:: config: some of it is in the DNS part of the profile
+      dhcp = {}
+      dhcpopts = Ops.get_map(input, "dhcp_options", {})
+      dns = Ops.get_map(input, "dns", {})
+
+      if Builtins.haskey(dns, "dhcp_hostname")
+        Ops.set(
+          dhcp,
+          "DHCLIENT_SET_HOSTNAME",
+          Ops.get_boolean(dns, "dhcp_hostname", false)
+        )
+      end
+
+      dhcp = UpcaseCondSet(dhcp, dhcpopts, "dhclient_client_id")
+      dhcp = UpcaseCondSet(dhcp, dhcpopts, "dhclient_additional_options")
+      dhcp = UpcaseCondSet(dhcp, dhcpopts, "dhclient_hostname_option")
+
+      Ops.set(input, "config", "dhcp" => dhcp)
+      if !Ops.get(input, "strict_IP_check_timeout").nil?
+        Ops.set(input, ["config", "config"], "CHECK_DUPLICATE_IP" => true)
+      end
+
+      Builtins.y2milestone("input=%1", input)
+      deep_copy(input)
+    end
+
     # Import data.
     # It expects data described networking.rnc
     # and then passed through {LanAutoClient#FromAY}.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/modules/LanItems.rb 
new/yast2-network-3.3.5/src/modules/LanItems.rb
--- old/yast2-network-3.3.2/src/modules/LanItems.rb     2017-07-11 
10:33:21.283935590 +0200
+++ new/yast2-network-3.3.5/src/modules/LanItems.rb     2017-08-02 
15:32:11.193221713 +0200
@@ -2726,7 +2726,7 @@
         end
       else
         configured = Items().select { |i, _| IsItemConfigured(i) }
-        configured.each do |id, _|
+        ay["net-udev"] = configured.keys.each_with_object({}) do |id, udev|
           @current = id # for GetItemUdev
 
           name = GetItemUdev("NAME").to_s
@@ -2734,12 +2734,10 @@
 
           next if !rule || name.empty?
 
-          ay["net-udev"] = {
-            name => {
-              "rule"  => rule,
-              "name"  => name,
-              "value" => GetItemUdev(rule)
-            }
+          udev[name] = {
+            "rule"  => rule,
+            "name"  => name,
+            "value" => GetItemUdev(rule)
           }
         end
       end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/src/modules/Routing.rb 
new/yast2-network-3.3.5/src/modules/Routing.rb
--- old/yast2-network-3.3.2/src/modules/Routing.rb      2017-07-11 
10:33:21.283935590 +0200
+++ new/yast2-network-3.3.5/src/modules/Routing.rb      2017-08-02 
15:32:11.197221713 +0200
@@ -300,7 +300,7 @@
 
     # Write routing settings and apply changes
     # @return true if success
-    def Write
+    def Write(gui: false)
       Builtins.y2milestone("Writing configuration")
       if !Modified()
         Builtins.y2milestone("No changes to routing -> nothing to write")
@@ -316,10 +316,12 @@
 
       caption = _("Saving Routing Configuration")
 
-      Progress.New(caption, " ", Builtins.size(steps), steps, [], "")
+      if gui
+        Progress.New(caption, " ", Builtins.size(steps), steps, [], "")
 
-      # Progress stage 1/2
-      ProgressNextStage(_("Writing IP forwarding settings..."))
+        # Progress stage 1/2
+        ProgressNextStage(_("Writing IP forwarding settings..."))
+      end
 
       WriteIPForwarding()
 
@@ -329,11 +331,11 @@
       # so we let our caller do it together with other things
 
       # Progress stage 2/2
-      ProgressNextStage(_("Writing routing settings..."))
+      ProgressNextStage(_("Writing routing settings...")) if gui
 
       ret = write_routes(@Routes)
 
-      Progress.NextStage
+      Progress.NextStage if gui
 
       ret
     end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/test/data/default_hosts 
new/yast2-network-3.3.5/test/data/default_hosts
--- old/yast2-network-3.3.2/test/data/default_hosts     1970-01-01 
01:00:00.000000000 +0100
+++ new/yast2-network-3.3.5/test/data/default_hosts     2017-08-02 
15:32:11.197221713 +0200
@@ -0,0 +1,22 @@
+#
+# hosts         This file describes a number of hostname-to-address
+#               mappings for the TCP/IP subsystem.  It is mostly
+#               used at boot time, when no name servers are running.
+#               On small systems, this file can be used instead of a
+#               "named" name server.
+# Syntax:
+#
+# IP-Address  Full-Qualified-Hostname  Short-Hostname
+#
+
+127.0.0.1       localhost
+
+# special IPv6 addresses
+::1             localhost ipv6-localhost ipv6-loopback
+
+fe00::0         ipv6-localnet
+
+ff00::0         ipv6-mcastprefix
+ff02::1         ipv6-allnodes
+ff02::2         ipv6-allrouters
+ff02::3         ipv6-allhosts
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/test/host_test.rb 
new/yast2-network-3.3.5/test/host_test.rb
--- old/yast2-network-3.3.2/test/host_test.rb   2017-07-11 10:33:21.291935590 
+0200
+++ new/yast2-network-3.3.5/test/host_test.rb   2017-08-02 15:32:11.201221713 
+0200
@@ -114,10 +114,52 @@
     end
   end
 
+  describe ".Import" do
+    let(:file) do
+      file_path = File.expand_path("../data/default_hosts", __FILE__)
+      CFA::MemoryFile.new(File.read(file_path))
+    end
+
+    let(:etc_hosts) do
+      {
+        "127.0.0.1" => ["localhost"],
+        "::1"       => ["localhost ipv6-localhost ipv6-loopback"],
+        "fe00::0"   => ["ipv6-localnet"],
+        "ff00::0"   => ["ipv6-mcastprefix"],
+        "ff02::1"   => ["ipv6-allnodes"],
+        "ff02::2"   => ["ipv6-allrouters"],
+        "ff02::3"   => ["ipv6-allhosts"]
+      }
+    end
+
+    it "loads the current '/etc/hosts' entries" do
+      Yast::Host.Import("hosts" => {})
+
+      expect(Yast::Host.name_map).to eql(etc_hosts)
+    end
+
+    it "merges current entries with the given ones" do
+      Yast::Host.Import("hosts" => { "10.20.1.29" => ["beholder"] })
+
+      expect(Yast::Host.name_map).to eql(etc_hosts.merge("10.20.1.29" => 
["beholder"]))
+    end
+  end
+
   describe ".Export" do
+    let(:file) do
+      file_path = File.expand_path("../data/default_hosts", __FILE__)
+      CFA::MemoryFile.new(File.read(file_path))
+    end
+
     let(:etc_hosts) do
       {
         "127.0.0.1"  => ["localhost localhost.localdomain"],
+        "::1"        => ["localhost ipv6-localhost ipv6-loopback"],
+        "fe00::0"    => ["ipv6-localnet"],
+        "ff00::0"    => ["ipv6-mcastprefix"],
+        "ff02::1"    => ["ipv6-allnodes"],
+        "ff02::2"    => ["ipv6-allrouters"],
+        "ff02::3"    => ["ipv6-allhosts"],
         "10.20.1.29" => ["beholder"]
       }
     end
@@ -128,12 +170,24 @@
     end
 
     it "removes empty name lists" do
-      Yast::Host.Import("hosts" => { "127.0.0.1" => ["localhost"], "10.0.0.1" 
=> [] })
-      expect(Yast::Host.Export).to eql("hosts" => { "127.0.0.1" => 
["localhost"] })
+      Yast::Host.Import("hosts" => { "127.0.0.1" => ["localhost 
localhost.localdomain"], "10.20.1.29" => [] })
+      etc_hosts.delete("10.20.1.29")
+
+      expect(Yast::Host.Export).to eql("hosts" => etc_hosts)
     end
 
     it "exports empty hash when no mapping is defined" do
-      Yast::Host.Import("hosts" => {})
+      Yast::Host.Import(
+        "hosts" => {
+          "127.0.0.1" => [],
+          "::1"       => [],
+          "fe00::0"   => [],
+          "ff00::0"   => [],
+          "ff02::1"   => [],
+          "ff02::2"   => [],
+          "ff02::3"   => []
+        }
+      )
       expect(Yast::Host.Export).to be_empty
     end
   end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/test/lan_items_export_test.rb 
new/yast2-network-3.3.5/test/lan_items_export_test.rb
--- old/yast2-network-3.3.2/test/lan_items_export_test.rb       2017-07-11 
10:33:21.291935590 +0200
+++ new/yast2-network-3.3.5/test/lan_items_export_test.rb       2017-08-02 
15:32:11.201221713 +0200
@@ -20,26 +20,73 @@
 
   let(:scr) { Yast::SCR }
 
+  let(:is_s390) { false }
+
+  let(:eth0) do
+    {
+      "hwinfo" => { "dev_name" => "eth0" },
+      "udev"   => {
+        "net" => [
+          "SUBSYSTEM==\"net\"", "ACTION==\"add\"", "DRIVERS==\"?*\"", 
"ATTR{type}==\"1\"",
+          "ATTR{address}==\"00:50:56:12:34:56\"", "NAME=\"eth0\""
+        ]
+      }
+    }
+  end
+
+  let(:eth1) do
+    {
+      "hwinfo" => { "dev_name" => "eth1" },
+      "udev"   => {
+        "net" => [
+          "SUBSYSTEM==\"net\"", "ACTION==\"add\"", "DRIVERS==\"?*\"",
+          "KERNELS==\"0000:00:1f.6\"", "NAME=\"eth1\""
+        ]
+      }
+    }
+  end
+
+  let(:items) { { 0 => eth0, 1 => eth1 } }
+
   before(:each) do
     # mock SCR to not touch system
     allow(scr).to receive(:Read).and_return("")
     allow(scr).to receive(:Execute).and_return("exit" => -1, "stdout" => "", 
"stderr" => "")
+    allow(subject).to receive(:IsItemConfigured).and_return(true)
+    allow(subject).to receive(:Items).and_return(items)
   end
 
-  def path(p)
-    Yast::Path.new(p)
+  before(:each) do
+    allow(Yast::Arch).to receive(:s390).and_return(is_s390)
   end
 
-  context "When running on s390" do
-    before(:each) do
-      allow(Yast::Arch).to receive(:s390).and_return(true)
+  it "exports udev rules" do
+    ay = subject.send(:export_udevs, devices)
+    expect(ay["net-udev"]).to eq(
+      "eth0" => { "rule" => "ATTR{address}", "name" => "eth0", "value" => 
"00:50:56:12:34:56" },
+      "eth1" => { "rule" => "KERNELS", "name" => "eth1", "value" => 
"0000:00:1f.6" }
+    )
+  end
+
+  context "when an interface is not configured" do
+    before do
+      allow(subject).to receive(:IsItemConfigured).with(1).and_return(false)
+    end
+
+    it "does not include an udev rule for that interface" do
+      ay = subject.send(:export_udevs, devices)
+      expect(ay["net-udev"].keys).to eq(["eth0"])
     end
+  end
+
+  context "When running on s390" do
+    let(:is_s390) { true }
 
     # kind of smoke test
     it "produces s390 specific content in exported AY profile" do
       allow(scr)
         .to receive(:Execute)
-        .with(path(".target.bash_output"), /^driver=.*/)
+        .with(Yast::Path.new(".target.bash_output"), /^driver=.*/)
         .and_return("exit" => 0, "stdout" => "qeth", "stderr" => "")
 
       ay = subject.send(:export_udevs, devices)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/test/lan_test.rb 
new/yast2-network-3.3.5/test/lan_test.rb
--- old/yast2-network-3.3.2/test/lan_test.rb    2017-07-11 10:33:21.291935590 
+0200
+++ new/yast2-network-3.3.5/test/lan_test.rb    2017-08-02 15:32:11.201221713 
+0200
@@ -389,3 +389,71 @@
     end
   end
 end
+
+describe "LanClass#FromAY" do
+  it "makes a minimal structure from an empty input" do
+    expected = {
+      "config"     => { "dhcp"=>{} },
+      "devices"    => {},
+      "hwcfg"      => {},
+      "interfaces" => []
+    }
+    expect(Yast::Lan.FromAY({})).to eq(expected)
+  end
+
+  it "converts 'interfaces' into nested 'devices'" do
+    input = {
+      "interfaces" => [
+        {
+          "bootproto"   => "static",
+          "device"      => "eth1",
+          "ipaddr"      => "10.1.1.1",
+          "name"        => "Ethernet Card 0",
+          "prefixlen"   => "24",
+          "startmode"   => "auto",
+          "usercontrol" => "no"
+        }
+      ]
+    }
+    expected = {
+      "eth" => {
+        "eth1" => {
+          "BOOTPROTO"   => "static",
+          "IPADDR"      => "10.1.1.1",
+          "NAME"        => "Ethernet Card 0",
+          "PREFIXLEN"   => "24",
+          "STARTMODE"   => "auto",
+          "USERCONTROL" => "no"
+        }
+      }
+    }
+
+    expect(Yast::Lan.FromAY(input)["devices"]).to eq(expected)
+  end
+
+  it "converts DHCP options" do
+    input = {
+      "dhcp_options" => {
+        "dhclient_hostname_option" => "AUTO"
+      },
+      "dns"          => {
+        "dhcp_hostname"      => false,
+        "domain"             => "example.com",
+        "hostname"           => "eg",
+        "nameservers"        => ["10.10.0.100"],
+        "resolv_conf_policy" => "auto",
+        "searchlist"         => ["example.com"],
+        "write_hostname"     => false
+      }
+    }
+    expected_config = {
+      "dhcp" => {
+        "DHCLIENT_HOSTNAME_OPTION" => "AUTO",
+        "DHCLIENT_SET_HOSTNAME"    => false
+      }
+    }
+
+    actual = Yast::Lan.FromAY(input)
+    expect(actual["config"]).to eq(expected_config)
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-network-3.3.2/test/network_autoyast_test.rb 
new/yast2-network-3.3.5/test/network_autoyast_test.rb
--- old/yast2-network-3.3.2/test/network_autoyast_test.rb       2017-07-11 
10:33:21.295935590 +0200
+++ new/yast2-network-3.3.5/test/network_autoyast_test.rb       2017-08-02 
15:32:11.205221713 +0200
@@ -210,22 +210,6 @@
     end
   end
 
-  describe "#configure_dns" do
-    let(:network_autoyast) { Yast::NetworkAutoYast.instance }
-
-    it "imports DNS configuration when available in profile" do
-      Yast.import "DNS"
-
-      allow(network_autoyast)
-        .to receive(:ay_networking_section)
-        .and_return("dns" => { "dhcp_hostname" => false })
-
-      expect(Yast::DNS).to receive(:Import)
-
-      network_autoyast.configure_dns
-    end
-  end
-
   describe "#keep_net_config?" do
     let(:network_autoyast) { Yast::NetworkAutoYast.instance }
 
@@ -347,4 +331,62 @@
       expect(network_autoyast.send(:valid_rename_udev_rule?, 
complete_rule)).to be true
     end
   end
+
+  describe "#rename_lan_item" do
+    before(:each) do
+      allow(Yast::LanItems)
+        .to receive(:Items)
+        .and_return(0 => { "ifcfg" => "eth0" })
+    end
+
+    context "valid arguments given" do
+      it "renames the item with no udev attribute change" do
+        expect(Yast::LanItems)
+          .to receive(:rename)
+          .with("new_name")
+        expect(Yast::LanItems)
+          .not_to receive(:ReplaceItemUdev)
+
+        network_autoyast.send(:rename_lan_item, 0, "new_name")
+      end
+
+      it "renames the item with udev attribute change" do
+        expect(Yast::LanItems)
+          .to receive(:rename)
+          .with("new_name")
+        expect(Yast::LanItems)
+          .to receive(:ReplaceItemUdev)
+
+        network_autoyast.send(:rename_lan_item, 0, "new_name", "KERNELS", 
"0000:00:03.0")
+      end
+    end
+
+    context "invalid arguments given" do
+      it "do not try to rename an item when missing new name" do
+        expect(Yast::LanItems)
+          .not_to receive(:rename)
+
+        network_autoyast.send(:rename_lan_item, 0, nil)
+        network_autoyast.send(:rename_lan_item, 0, "")
+      end
+
+      it "do not try to rename an item when given item id is invalid" do
+        expect(Yast::LanItems)
+          .not_to receive(:rename)
+
+        network_autoyast.send(:rename_lan_item, nil, "new_name")
+        network_autoyast.send(:rename_lan_item, -1, "new_name")
+        network_autoyast.send(:rename_lan_item, 100, "new_name")
+      end
+
+      it "raise an exception when udev definition is incomplete" do
+        expect do
+          network_autoyast.send(:rename_lan_item, 0, "new_name", "KERNELS", 
nil)
+        end.to raise_error(ArgumentError)
+        expect do
+          network_autoyast.send(:rename_lan_item, 0, "new_name", nil, 
"0000:00:03.0")
+        end.to raise_error(ArgumentError)
+      end
+    end
+  end
 end


Reply via email to