From a76777fb60826e0963f0442d2485f51a7f928bfc Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 11 Oct 2016 15:09:05 +0200 Subject: [PATCH 01/27] use cfa instead of ini agent --- src/include/network/lan/address.rb | 2 +- src/lib/cfa/hosts.rb | 147 +++++++++++++++++++++++ src/modules/Host.rb | 186 +++++++++++------------------ test/data/hosts | 24 ++++ test/host_test.rb | 156 +++++++++++++++++++++--- 5 files changed, 383 insertions(+), 132 deletions(-) create mode 100644 src/lib/cfa/hosts.rb create mode 100644 test/data/hosts diff --git a/src/include/network/lan/address.rb b/src/include/network/lan/address.rb index 467118dae..1694b60a4 100644 --- a/src/include/network/lan/address.rb +++ b/src/include/network/lan/address.rb @@ -1546,7 +1546,7 @@ def AddressDialog Host.Update( @hostname_initial, Ops.get_string(@settings, "HOSTNAME", ""), - [Ops.get_string(@settings, "IPADDR", "")] + Ops.get_string(@settings, "IPADDR", "") ) end end diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb new file mode 100644 index 000000000..563638b08 --- /dev/null +++ b/src/lib/cfa/hosts.rb @@ -0,0 +1,147 @@ +require "yast" +require "yast2/target_file" + +require "cfa/base_model" +require "cfa/matcher" +require "cfa/augeas_parser" + +module CFA + class Hosts < BaseModel + PARSER = AugeasParser.new("hosts.lns") + PATH = "/etc/hosts".freeze + include Yast::Logger + + def initialize(file_handler: nil) + super(PARSER, PATH, file_handler: file_handler) + end + + def hosts + matcher = Matcher.new { |k,v| k =~ /^\d*$/ } + data.select(matcher).each_with_object({}) do |host, result| + entry = host[:value] + result[entry["ipaddr"]] ||= [] + result[entry["ipaddr"]] << single_host_entry(entry) + end + end + + def host(ip) + hosts = data.select(ip_matcher(ip)) + + hosts.map do |host| + single_host_entry(host[:value]) + end + end + + def delete_host(ip) + entries = data.select(ip_matcher(ip)) + if entries.empty? + log.info "no entry to delete for ip #{ip}" + return + end + + if entries.size > 1 + log.info "delete host with ip '#{ip}' removes more then one entry" + end + + entries.each do |e| + log.info "deleting record #{e.inspect}" + data.delete(e[:key]) + end + end + + # replaces or adds new host entry. If more then one entry with given ip exists + # then replaces the last instance + def set_host(ip, canonical, aliases = []) + entries = data.select(ip_matcher(ip)) + if entries.empty? + log.info "adding new entry for ip #{ip}" + entry_line = AugeasTree.new + entry_line["ipaddr"] = ip + entry_line["canonical"] = canonical + aliases_col = entry_line.collection("alias") + aliases.each do |a| + aliases_col.add(a) + end + data.add(unique_id, entry_line) + return + end + + if entries.size > 1 + log.info "more then one entry with ip '#{ip}'. Replacing last one." + end + + entry = entries.last[:value] + entry["ipaddr"] = ip + entry["canonical"] = canonical + # clear previous aliases + entry.delete("alias") + entry.delete("alias[]") + aliases_col = entry.collection("alias") + aliases.each do |a| + aliases_col.add(a) + end + end + + # adds new entry, even if it exists + def add_host(ip, canonical, aliases = []) + log.info "adding new entry for ip #{ip}" + entry_line = AugeasTree.new + entry_line["ipaddr"] = ip + entry_line["canonical"] = canonical + aliases_col = entry_line.collection("alias") + aliases.each do |a| + aliases_col.add(a) + end + data.add(unique_id, entry_line) + end + + def remove_hostname(hostname) + entries = data.select(hostname_matcher(hostname)) + entries.each do |entry| + entry = entry[:value] + if entry["canonical"] == hostname + aliases = aliases_for(entry) + if aliases.empty? + delete_host(entry["ipaddr"]) + else + set_host(entry["ipaddr"], aliases.first, aliases[1..-1]) + end + else + reduced_aliases = aliases_for(entry) + reduced_aliases.delete(hostname) + set_host(entry["ipaddr"], entry["canonical"], reduced_aliases) + end + end + end + + private + + def ip_matcher(ip) + Matcher.new { |k, v| v["ipaddr"] == ip } + end + + def hostname_matcher(hostname) + Matcher.new do |k, v| + v["canonical"] == hostname || aliases_for(v).include?(hostname) + end + end + + def aliases_for(entry) + entry["alias[]"] ? entry.collection("alias").map{ |a| a } : [entry["alias"]].compact + end + + def single_host_entry(entry) + result = [entry["canonical"]] + result.concat(aliases_for(entry)) + result.join(" ") + end + + def unique_id + id = 1 + loop do + return id.to_s unless data[id.to_s] + id += 1 + end + end + end +end diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 0904da7d0..8e307fd41 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -27,6 +27,8 @@ # Authors: Michal Svec # require "yast" +require "yast2/execute" +require "cfa/hosts" module Yast class HostClass < Module @@ -42,50 +44,45 @@ def main Yast.include self, "network/routines.rb" - # All hosts - # See hosts(5) - # keys: IPs, (But #35671 suggests that repeating IPs is valid) - # values: names, the first one is the canonical one - @hosts = {} - # Data was modified? @modified = false - # All hosts read at the start - @hosts_init = {} - - # "hosts" file location - @hosts_file = "/etc/hosts" - @initialized = false end # Remove all entries from the host table. def clear - @hosts = {} + @hosts.hosts.keys.each do |ip| + @hosts.delete_host(ip) + end @modified = true end # @return [hash] address->list of names def name_map - @hosts + @hosts.hosts end # @return [array] names for that address def names(address) - @hosts[address] || [] + @hosts.host(address) || [] end # Give address a new list of names. def set_names(address, names) - @hosts[address] = names + @hosts.delete_host(address) + names.each do |name| + canonical, *aliases = name.split(" ") + @hosts.add_host(address, canonical, aliases) + end @modified = true end # Add another name to the list for address (which may be empty so far) + # FIXME: used only in one place, which looks wrong def add_name(address, name) - @hosts[address] ||= [] - @hosts[address] << name + canonical, *aliases = name.split(" ") + @hosts.add_host(address, canonical, aliases) @modified = true end @@ -103,15 +100,12 @@ def EnsureHostnameResolvable # being unable to resolve hostname (bnc#304632) fqhostname = Hostname.MergeFQ(DNS.hostname, DNS.domain) - Ops.set( - @hosts, - local_ip, - [Ops.add(Ops.add(fqhostname, " "), DNS.hostname)] - ) + set_names(local_ip, ["#{fqhostname} #{DNS.hostname}"]) elsif Builtins.haskey(@hosts, local_ip) # Do not add it if product default says no # and remove 127.0.02 entry if it exists - Ops.set(@hosts, local_ip, []) + + @hosts.delete_host(local_ip) end @modified = true @@ -121,27 +115,20 @@ def EnsureHostnameResolvable # Read hosts settings # @return true if success def Read - return true if @initialized == true + return true if @initialized + # read /etc/hosts - if Ops.greater_than(SCR.Read(path(".target.size"), @hosts_file), 0) - hostlist = SCR.Dir(path(".etc.hosts")) - @hosts = Builtins.listmap(hostlist) do |host| - names = Convert.convert( - SCR.Read( - Builtins.topath(Builtins.sformat(".etc.hosts.\"%1\"", host)) - ), - from: "any", - to: "list " - ) - next { host => names } if names != [] - end + if Ops.greater_than(SCR.Read(path(".target.size"), CFA::Hosts::PATH), 0) + @hosts = CFA::Hosts.new + @hosts.load end # save hosts to check for changes later - @hosts_init = deep_copy(@hosts) + @hosts_init = CFA::Hosts.new + @hosts_init.load - Builtins.y2debug("hosts=%1", @hosts) + Builtins.y2debug("hosts=#{@hosts.inspect}") @initialized = true true end @@ -165,48 +152,21 @@ def Write steps = [_("Update /etc/hosts")] caption = _("Saving Hostname Configuration") - sl = 500 # sleep for longer time, so that progress does not disappear right afterwards - Progress.New(caption, " ", Builtins.size(steps), steps, [], "") + Progress.New(caption, " ", steps.size, steps, [], "") ProgressNextStage(_("Updating /etc/hosts ...")) - # Create if not exists, otherwise backup - if Ops.less_than(SCR.Read(path(".target.size"), @hosts_file), 0) - SCR.Write(path(".target.string"), @hosts_file, "") - else - SCR.Execute( - path(".target.bash"), - Ops.add( - Ops.add(Ops.add(Ops.add("/bin/cp ", @hosts_file), " "), @hosts_file), - ".YaST2save" - ) - ) + # backup if exists + if SCR.Read(path(".target.size"), CFA::Hosts::PATH) >= 0 + Yast::Execute.on_target("cp", CFA::Hosts::PATH, "#{CFA::Hosts::PATH}.YaST2save") end - ret = false - if @hosts == {} || @hosts.nil? - # Workaround bug [#4476] - ret = SCR.Write(path(".target.string"), @hosts_file, "") - else - # Update the hosts config - Builtins.y2milestone("hosts=%1", @hosts) - Builtins.maplist(@hosts) do |ho, names| - Builtins.y2milestone( - "%1 (%2:%3)", - ho, - names, - Ops.get(@hosts_init, ho) - ) - SCR.Write(Builtins.add(path(".etc.hosts"), ho), names) - end - ret = true - end + @hosts.save - SCR.Write(path(".etc.hosts"), nil) - Builtins.sleep(sl) Progress.NextStage - ret == true + + true end # Get all the Hosts configuration from a map. @@ -215,34 +175,41 @@ def Write # @param [Hash] settings autoinstallation settings # @return true if success def Import(settings) - settings = deep_copy(settings) @modified = true # trigger Write @initialized = true # don't let Read discard our data - @hosts = Builtins.eval(Ops.get_map(settings, "hosts", {})) + @hosts = CFA::Hosts.new + imported_hosts = Builtins.eval(Ops.get_map(settings, "hosts", {})) + # convert from old format to the new one # use ::1 entry as a reference - if Ops.greater_than(Builtins.size(Ops.get(@hosts, "::1", [])), 1) - Builtins.foreach(@hosts) do |ip, hn| - Ops.set(@hosts, ip, [Builtins.mergestring(hn, " ")]) + if (imported_hosts["::1"] || []).size > 1 + imported_hosts.each_pair do |k, v| + imported_hosts[k] = v.join(" ") end end + + imported_hosts.each_pair do |ip, names| + set_names(ip, names) + end + true end # Dump the Hosts settings to a map, for autoinstallation use. # @return autoinstallation settings def Export - return {} if @hosts.empty? + exported_hosts = @hosts.hosts + return {} if exported_hosts.empty? # Filter out IPs with empty hostname (so that valid autoyast # profile is created)(#335120) # FIXME: this also removes records with empty names from @hosts. Such # side effect is unexpected and should be removed. - @hosts.keep_if { |_, names| !names.empty? } + exported_hosts.keep_if { |_, names| !names.empty? } - { "hosts" => @hosts } + { "hosts" => exported_hosts } end # Return "system" predefined hosts (should be present all the time) @@ -260,21 +227,17 @@ def GetSystemHosts end # Update hosts according to the current hostname - # (only one hostname, assigned to all IPs) + # (only one hostname, assigned to all IP) # @param hostname current hostname # @param domain current domain name - # @param [Array] iplist localhost IP addresses + # @param String ip to assign # @return true if success - def Update(oldhn, newhn, iplist) - iplist = deep_copy(iplist) - ips = Builtins.filter(iplist) { |ip| ip != "127.0.0.1" } - - Builtins.y2milestone("Hosts: %1", @hosts) + def Update(oldhn, newhn, ip) Builtins.y2milestone( "Updating /etc/hosts: %1 -> %2: %3", oldhn, newhn, - ips + ip ) @modified = true @@ -282,38 +245,27 @@ def Update(oldhn, newhn, iplist) # Remove old hostname from hosts if !oldhn.empty? - Builtins.foreach(@hosts) do |ip, hs| - wrk = Builtins.maplist(hs) { |s| Builtins.splitstring(s, " ") } - wrk = Builtins.filter(wrk) { |lst| !Builtins.contains(lst, oldhn) } - Ops.set(@hosts, ip, Builtins.maplist(wrk) do |lst| - Builtins.mergestring(lst, " ") - end) - end + @hosts.remove_hostname(oldhn) end - # Resurect the rest of oldhnlist without old hostname - # FIXME: maybe - # Add localhost if missing - if !Builtins.haskey(@hosts, "127.0.0.1") - Ops.set(@hosts, "127.0.0.1", ["localhost"]) + if @hosts.host("127.0.0.1").empty? + @hosts.add_host("127.0.0.1", "localhost") end - # Add hostname/ip for all ips - nickadded = false - Builtins.maplist(ips) do |ip| - # Omit some IP addresses - next if ip == "" || ip.nil? || ip == "127.0.0.1" - name = newhn - # Add nick for the first one - if !nickadded && name != "" - nickadded = true - name = Ops.add(Ops.add(newhn, " "), nick) - end - Ops.set(@hosts, ip, Builtins.add(Ops.get(@hosts, ip, []), name)) - end + # Omit some IP addresses + return true if ["127.0.0.1", "", nil].include?(ip) + # Omit invalid newhn + return true if [nil, ""].include?(newhn) - Builtins.y2milestone("Hosts: %1", @hosts) + hosts = @hosts.host(ip) + if hosts.empty? + @hosts.add_host(ip, newhn) + else + canonical, *aliases = hosts.last + aliases << newhn + @hosts.set_host(ip, canonical, aliases) + end true end @@ -322,10 +274,10 @@ def Update(oldhn, newhn, iplist) # @return summary text def Summary summary = "" - return Summary.NotConfigured if @hosts == {} + return Summary.NotConfigured if @hosts.hosts.empty? summary = Summary.OpenList(summary) - Builtins.foreach(@hosts) do |k, v| + @hosts.hosts.each do |k, v| Builtins.foreach(v) do |hn| summary = Summary.AddListItem(summary, Ops.add(Ops.add(k, " - "), hn)) end if !Builtins.contains( diff --git a/test/data/hosts b/test/data/hosts new file mode 100644 index 000000000..28bfa8e34 --- /dev/null +++ b/test/data/hosts @@ -0,0 +1,24 @@ +# +# 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 +10.100.128.72 pepa.labs.suse.cz pepa pepa2 +10.254.128.01 pepa1.labs.suse.cz pepa1 diff --git a/test/host_test.rb b/test/host_test.rb index d83c43a0e..55ab91790 100755 --- a/test/host_test.rb +++ b/test/host_test.rb @@ -3,13 +3,134 @@ require_relative "test_helper" require "yast" +require "cfa/memory_file" +require "cfa/base_model" +require "cfa/hosts" Yast.import "Host" -describe "Host" do - subject(:host) { Yast::Host } +describe Yast::Host do + let(:file) do + file_path = File.expand_path("../data/hosts", __FILE__) + CFA::MemoryFile.new(File.read(file_path)) + end + + before do + # use only testing file + CFA::BaseModel.default_file_handler = file + + allow(Yast::SCR).to receive(:Read).with(path(".target.size"), "/etc/hosts").and_return(50) + + # reset internal caches + Yast::Host.instance_variable_set(:"@modified", false) + Yast::Host.instance_variable_set(:"@initialized", false) + + # do nothing on system + allow(Yast::Execute).to receive(:on_target) + end + + describe ".Read" do + it "reads hosts configuration from system" do + Yast::Host.Read + + expect(Yast::Host.name_map).to_not be_empty + end + end + + describe ".clear" do + it "removes all entries from host table" do + Yast::Host.Read + Yast::Host.clear + + expect(Yast::Host.name_map).to be_empty + end + end + + describe ".name_map" do + # FIXME make value API better + it "returns hash with ip as key and hostnames as value" do + Yast::Host.Read + + name_map = Yast::Host.name_map + expect(name_map["10.100.128.72"]).to eq(["pepa.labs.suse.cz pepa pepa2"]) + end + end + + describe ".names" do + it "returns empty array if given ip is not is hosts table" do + Yast::Host.Read + + expect(Yast::Host.names("1.1.1.1")).to eq [] + end + + # FIXME: better API + it "returns single element array with string containing canonical name and aliases separated by space" do + Yast::Host.Read + + expect(Yast::Host.names("10.100.128.72")).to eq(["pepa.labs.suse.cz pepa pepa2"]) + end + end + + describe ".set_names" do + it "adds new adress with names if address is not yet in hosts table" do + Yast::Host.Read + Yast::Host.set_names("1.1.1.1", ["test test2.suse.cz"]) - describe "#Export" do + expect(Yast::Host.names("1.1.1.1")).to eq(["test test2.suse.cz"]) + end + + it "replaces entry with given address if already used" do + Yast::Host.Read + Yast::Host.set_names("10.100.128.72", ["test test2.suse.cz"]) + + expect(Yast::Host.names("10.100.128.72")).to eq(["test test2.suse.cz"]) + end + end + + describe ".add_name" do + it "adds host to hosts entry even if it is already there" do + Yast::Host.Read + Yast::Host.add_name("10.100.128.72", "test test2.suse.cz") + Yast::Host.add_name("10.100.128.72", "test3 test3.suse.cz") + + expect(Yast::Host.names("10.100.128.72")).to eq([ + "pepa.labs.suse.cz pepa pepa2", + "test test2.suse.cz", + "test3 test3.suse.cz" + ]) + end + end + + describe ".Write" do + it "do nothing if not modified" do + expect(file).to_not receive(:write) + Yast::Host.Read + Yast::Host.Write + end + + it "writes content of file" do + Yast::Host.Read + Yast::Host.add_name("10.100.128.72", "test test2.suse.cz") + Yast::Host.add_name("10.100.128.72", "test3 test3.suse.cz") + Yast::Host.Write + + content = file.content + + expect(content.lines).to include("10.100.128.72\ttest test2.suse.cz\n") + expect(content.lines).to include("10.100.128.72\ttest3 test3.suse.cz\n") + end + + it "creates backup of file" do + expect(Yast::Execute).to receive(:on_target).with("cp", "/etc/hosts", "/etc/hosts.YaST2save") + + Yast::Host.Read + Yast::Host.add_name("10.100.128.72", "test test2.suse.cz") + Yast::Host.add_name("10.100.128.72", "test3 test3.suse.cz") + Yast::Host.Write + end + end + + describe ".Export" do let(:etc_hosts) do { "127.0.0.1" => ["localhost localhost.localdomain"], @@ -18,22 +139,22 @@ end it "Successfully exports stored mapping" do - host.Import("hosts" => etc_hosts) - expect(host.Export).to eql("hosts" => etc_hosts) + Yast::Host.Import("hosts" => etc_hosts) + expect(Yast::Host.Export).to eql("hosts" => etc_hosts) end it "removes empty name lists" do - host.Import("hosts" => { "127.0.0.1" => ["localhost"], "10.0.0.1" => [] }) - expect(host.Export).to eql("hosts" => { "127.0.0.1" => ["localhost"] }) + 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"] }) end it "exports empty hash when no mapping is defined" do - host.Import("hosts" => {}) - expect(host.Export).to be_empty + Yast::Host.Import("hosts" => {}) + expect(Yast::Host.Export).to be_empty end end - describe "#Update" do + describe ".Update" do let(:etc_hosts) do { "127.0.0.1" => ["localhost localhost.localdomain"], @@ -41,12 +162,19 @@ } end - it "doesn't drop records with two spaces" do - host.Import("hosts" => etc_hosts) - host.Update("", "newname", ["10.0.0.42"]) + let(:etc_hosts_new) do + { + "127.0.0.1" => ["localhost localhost.localdomain"], + "10.0.0.1" => ["somehost.example.com notice-two-spaces"] + } + end + + it "doesn't drop records with two spaces but make it single space" do + Yast::Host.Import("hosts" => etc_hosts) + Yast::Host.Update("", "newname", "10.0.0.42") tested_ip = "10.0.0.1" - expect(host.name_map[tested_ip]).to eql etc_hosts[tested_ip] + expect(Yast::Host.name_map[tested_ip]).to eql etc_hosts_new[tested_ip] end end end From 671bc1c8cae6d4158be6eb4670c8bda55d72949a Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Tue, 11 Oct 2016 15:30:36 +0200 Subject: [PATCH 02/27] mark private internal api and present new useful api call --- src/clients/host.rb | 10 ++-------- src/include/network/lan/address.rb | 4 ++-- src/include/network/services/host.rb | 4 ---- src/modules/Host.rb | 21 ++++++++++++++------- src/modules/LanItems.rb | 2 +- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/clients/host.rb b/src/clients/host.rb index 56565b1db..8269d172d 100644 --- a/src/clients/host.rb +++ b/src/clients/host.rb @@ -109,15 +109,9 @@ def HostGUI def ListHandler(_options) # Command line output Headline # configuration of hosts - summary = Ops.add( - Ops.add( - "\n" + _("Host Configuration Summary:") + "\n\n", - RichText.Rich2Plain(Host.Summary) - ), - "\n" - ) + summary = "\n" + _("Host Configuration Summary:") + "\n\n" + + RichText.Rich2Plain(Host.Summary) + "\n" - Builtins.y2debug("%1", summary) CommandLine.Print(summary) true end diff --git a/src/include/network/lan/address.rb b/src/include/network/lan/address.rb index 1694b60a4..6b8a20e1b 100644 --- a/src/include/network/lan/address.rb +++ b/src/include/network/lan/address.rb @@ -1530,7 +1530,7 @@ def AddressDialog ip_changed = LanItems.ipaddr != Ops.get_string(@settings, "IPADDR", "") if ip_changed - Host.set_names(LanItems.ipaddr, []) + Host.remove_ip(LanItems.ipaddr) Builtins.y2milestone("IP has changed") end @@ -1541,7 +1541,7 @@ def AddressDialog if @hostname_initial != Ops.get_string(@settings, "HOSTNAME", "") || ip_changed if Ops.get_string(@settings, "HOSTNAME", "") == "" - Host.set_names(LanItems.ipaddr, []) + Host.remove_ip(LanItems.ipaddr) else Host.Update( @hostname_initial, diff --git a/src/include/network/services/host.rb b/src/include/network/services/host.rb index fc38ba462..31770d511 100644 --- a/src/include/network/services/host.rb +++ b/src/include/network/services/host.rb @@ -292,10 +292,6 @@ def HostsMainDialog(standalone) key = Ops.get_string(row, 1, "") Host.add_name(key, value) end - # deleted entries need to be set to [], - # so that ini-agent does not keep them in - # config file (#455862) - Builtins.foreach(deleted_items) { |d| Host.set_names(d, []) } end break else diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 8e307fd41..498c431e9 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -68,14 +68,9 @@ def names(address) @hosts.host(address) || [] end - # Give address a new list of names. - def set_names(address, names) + # remove all instances of ip in hosts table + def remove_ip(address) @hosts.delete_host(address) - names.each do |name| - canonical, *aliases = name.split(" ") - @hosts.add_host(address, canonical, aliases) - end - @modified = true end # Add another name to the list for address (which may be empty so far) @@ -338,6 +333,18 @@ def SetModified publish function: :ResolveHostnameToStaticIPs, type: "void ()" publish function: :GetModified, type: "boolean ()" publish function: :SetModified, type: "void ()" + + private + + # Give address a new list of names. + def set_names(address, names) + @hosts.delete_host(address) + names.each do |name| + canonical, *aliases = name.split(" ") + @hosts.add_host(address, canonical, aliases) + end + @modified = true + end end Host = HostClass.new diff --git a/src/modules/LanItems.rb b/src/modules/LanItems.rb index d92197335..9eb683180 100644 --- a/src/modules/LanItems.rb +++ b/src/modules/LanItems.rb @@ -2396,7 +2396,7 @@ def s390_correct_lladdr(lladdr) # Removes all records connected to the ip from /etc/hosts def drop_hosts(ip) log.info("Deleting hostnames assigned to #{ip} from /etc/hosts") - Host.set_names(ip, []) + Host.remove_ip(ip) end # Exports udev rules for AY profile From 183af41b01394311f856943d4d1149f9ec8fc14b Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 09:07:47 +0200 Subject: [PATCH 03/27] fix adding alias in update and test it --- src/modules/Host.rb | 13 +++++-------- test/host_test.rb | 24 ++++++++---------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 498c431e9..d43e8e6c6 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -112,7 +112,6 @@ def EnsureHostnameResolvable def Read return true if @initialized - # read /etc/hosts if Ops.greater_than(SCR.Read(path(".target.size"), CFA::Hosts::PATH), 0) @hosts = CFA::Hosts.new @@ -176,7 +175,6 @@ def Import(settings) @hosts = CFA::Hosts.new imported_hosts = Builtins.eval(Ops.get_map(settings, "hosts", {})) - # convert from old format to the new one # use ::1 entry as a reference if (imported_hosts["::1"] || []).size > 1 @@ -236,12 +234,8 @@ def Update(oldhn, newhn, ip) ) @modified = true - nick = Ops.get(Hostname.SplitFQ(newhn), 0, "") - # Remove old hostname from hosts - if !oldhn.empty? - @hosts.remove_hostname(oldhn) - end + @hosts.remove_hostname(oldhn) if !oldhn.empty? # Add localhost if missing if @hosts.host("127.0.0.1").empty? @@ -253,12 +247,15 @@ def Update(oldhn, newhn, ip) # Omit invalid newhn return true if [nil, ""].include?(newhn) + nick = Hostname.SplitFQ(newhn)[0] || "" + nick = nick.empty? ? [] : [nick] hosts = @hosts.host(ip) if hosts.empty? - @hosts.add_host(ip, newhn) + @hosts.add_host(ip, newhn, nick) else canonical, *aliases = hosts.last aliases << newhn + aliases.concat(nick) @hosts.set_host(ip, canonical, aliases) end diff --git a/test/host_test.rb b/test/host_test.rb index 55ab91790..96781095a 100755 --- a/test/host_test.rb +++ b/test/host_test.rb @@ -71,22 +71,6 @@ end end - describe ".set_names" do - it "adds new adress with names if address is not yet in hosts table" do - Yast::Host.Read - Yast::Host.set_names("1.1.1.1", ["test test2.suse.cz"]) - - expect(Yast::Host.names("1.1.1.1")).to eq(["test test2.suse.cz"]) - end - - it "replaces entry with given address if already used" do - Yast::Host.Read - Yast::Host.set_names("10.100.128.72", ["test test2.suse.cz"]) - - expect(Yast::Host.names("10.100.128.72")).to eq(["test test2.suse.cz"]) - end - end - describe ".add_name" do it "adds host to hosts entry even if it is already there" do Yast::Host.Read @@ -176,5 +160,13 @@ tested_ip = "10.0.0.1" expect(Yast::Host.name_map[tested_ip]).to eql etc_hosts_new[tested_ip] end + + it "adds alias for added hostname" do + Yast::Host.Import("hosts" => etc_hosts) + Yast::Host.Update("", "newname.suse.cz", "10.0.0.42") + + tested_ip = "10.0.0.42" + expect(Yast::Host.name_map[tested_ip]).to eql ["newname.suse.cz newname"] + end end end From 69fb68e07b6f2c70498d0ccc4b5a849922ebc7de Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 09:42:45 +0200 Subject: [PATCH 04/27] changes from review --- src/lib/cfa/hosts.rb | 29 +++++++++++++++++++++++------ src/modules/Host.rb | 4 ---- test/host_test.rb | 10 +++++----- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index 563638b08..0449d83fa 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -15,8 +15,13 @@ def initialize(file_handler: nil) super(PARSER, PATH, file_handler: file_handler) end + # returns old format of hosts used in Host module. + # @return [Hash>] format is hash where key is ip and value + # is array which contain strings and each string represent one line in hosts + # table with comma separated hostnames, where first is canonical and rest + # are aliases def hosts - matcher = Matcher.new { |k,v| k =~ /^\d*$/ } + matcher = Matcher.new { |k, _v| k =~ /^\d*$/ } data.select(matcher).each_with_object({}) do |host, result| entry = host[:value] result[entry["ipaddr"]] ||= [] @@ -24,6 +29,8 @@ def hosts end end + # Returns single entry from hosts for given ip or empty array if not found + # @see {#hosts} def host(ip) hosts = data.select(ip_matcher(ip)) @@ -32,6 +39,7 @@ def host(ip) end end + # deletes all occurences of given ip in host table def delete_host(ip) entries = data.select(ip_matcher(ip)) if entries.empty? @@ -95,10 +103,13 @@ def add_host(ip, canonical, aliases = []) data.add(unique_id, entry_line) end + # removes hostname from all entries in hosts table. + # if it is only hostname for given ip, ip is removed + # if it is canonical part, then first alias is used as canonical hostname def remove_hostname(hostname) entries = data.select(hostname_matcher(hostname)) - entries.each do |entry| - entry = entry[:value] + entries.each do |pair| + entry = pair[:value] if entry["canonical"] == hostname aliases = aliases_for(entry) if aliases.empty? @@ -116,26 +127,32 @@ def remove_hostname(hostname) private + # returns matcher for cfa to find entries with given ip def ip_matcher(ip) - Matcher.new { |k, v| v["ipaddr"] == ip } + Matcher.new { |_k, v| v["ipaddr"] == ip } end + # returns matcher for cfa to find entries with given hostname def hostname_matcher(hostname) - Matcher.new do |k, v| + Matcher.new do |_k, v| v["canonical"] == hostname || aliases_for(v).include?(hostname) end end + # returns aliases as array even if there is only one def aliases_for(entry) - entry["alias[]"] ? entry.collection("alias").map{ |a| a } : [entry["alias"]].compact + entry["alias[]"] ? entry.collection("alias").map { |a| a } : [entry["alias"]].compact end + # generate old format string with first canonical and then aliases + # all separated by space def single_host_entry(entry) result = [entry["canonical"]] result.concat(aliases_for(entry)) result.join(" ") end + # helper to generate unique id for cfa entry def unique_id id = 1 loop do diff --git a/src/modules/Host.rb b/src/modules/Host.rb index d43e8e6c6..5ed138838 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -21,10 +21,6 @@ # you may find current contact information at www.novell.com # # ************************************************************************** -# File: modules/Host.ycp -# Package: Network configuration -# Summary: Hosts data (/etc/hosts) -# Authors: Michal Svec # require "yast" require "yast2/execute" diff --git a/test/host_test.rb b/test/host_test.rb index 96781095a..b6f58db36 100755 --- a/test/host_test.rb +++ b/test/host_test.rb @@ -47,7 +47,7 @@ end describe ".name_map" do - # FIXME make value API better + # FIXME: make value API better it "returns hash with ip as key and hostnames as value" do Yast::Host.Read @@ -78,10 +78,10 @@ Yast::Host.add_name("10.100.128.72", "test3 test3.suse.cz") expect(Yast::Host.names("10.100.128.72")).to eq([ - "pepa.labs.suse.cz pepa pepa2", - "test test2.suse.cz", - "test3 test3.suse.cz" - ]) + "pepa.labs.suse.cz pepa pepa2", + "test test2.suse.cz", + "test3 test3.suse.cz" + ]) end end From 2814e511d45b9fbae5b818582ec8d70ed5467a50 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 12:05:40 +0200 Subject: [PATCH 05/27] chagnes from review --- .travis.yml | 2 +- src/modules/Host.rb | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1ebcfbd7..3d0053672 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls" + - sh ./travis_setup.sh -p "rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa" script: - rubocop - rake check:syntax diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 5ed138838..520a18aa9 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -194,8 +194,6 @@ def Export # Filter out IPs with empty hostname (so that valid autoyast # profile is created)(#335120) - # FIXME: this also removes records with empty names from @hosts. Such - # side effect is unexpected and should be removed. exported_hosts.keep_if { |_, names| !names.empty? } { "hosts" => exported_hosts } From 0fea43bc2e77e765e05cbbd2e60adb148cde2bc1 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 12:11:44 +0200 Subject: [PATCH 06/27] update build dependencies --- .travis.yml | 2 +- package/yast2-network.spec | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3d0053672..7c43a7155 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa" + - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa" script: - rubocop - rake check:syntax diff --git a/package/yast2-network.spec b/package/yast2-network.spec index 76d4f41dd..2c1a71188 100644 --- a/package/yast2-network.spec +++ b/package/yast2-network.spec @@ -51,6 +51,9 @@ Requires: yast2-storage >= 2.21.11 # Packages::vnc_packages BuildRequires: yast2-packager >= 3.1.47 Requires: yast2-packager >= 3.1.47 +# cfa for parsing hosts +BuildRequires: rubygem(%rb_default_ruby_abi:cfa) +Requires: rubygem(%rb_default_ruby_abi:cfa) # testsuite BuildRequires: rubygem(rspec) From c9b506e012d4ac35fb512a0cb0f40d1fae17db89 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 12:52:11 +0200 Subject: [PATCH 07/27] add comment --- src/lib/cfa/hosts.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index 0449d83fa..056e85a9e 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -6,6 +6,10 @@ require "cfa/augeas_parser" module CFA + # class representings /etc/hosts file model. It provides helper to manipulate + # with file. It uses CFA framework and Augeas parser. + # @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/BaseModel + # @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/AugeasParser class Hosts < BaseModel PARSER = AugeasParser.new("hosts.lns") PATH = "/etc/hosts".freeze From 9cc559f5397001fa3c01b6ef7ea2f74c87d9d138 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 13:07:46 +0200 Subject: [PATCH 08/27] debug travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7c43a7155..7406e895b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,3 +11,4 @@ script: - rake check:syntax - rake check:pot - COVERAGE=1 rake test:unit + - cat ~/.y2log From 2891ea769bcab33d9c383efb4361152e69110226 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 14:06:04 +0200 Subject: [PATCH 09/27] Revert "adapt Rakefile to submit to correct build service project in maintenance branch SLE-12-SP2" This reverts commit 6a761c7ff0c082cb9c4e2ee9723b5d0e45839196. --- Rakefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Rakefile b/Rakefile index 5b4852746..2432dd52a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,5 @@ require "yast/rake" -Yast::Tasks.submit_to :sle12sp2 - Yast::Tasks.configuration do |conf| # lets ignore license check for now conf.skip_license_check << /.*/ From 329459d2703da88dad7dc15476001390a5f51cf4 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 14:11:21 +0200 Subject: [PATCH 10/27] more travis debugging --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7406e895b..7a6dd9814 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,6 @@ script: - rubocop - rake check:syntax - rake check:pot + - rake test:unit - COVERAGE=1 rake test:unit - cat ~/.y2log From 8f87a1dacbf9af27dbbfd2dd84e813e19628bccf Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 15:22:34 +0200 Subject: [PATCH 11/27] fix travis --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a6dd9814..5fbf005f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,9 @@ before_install: # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa" + - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" script: - rubocop - rake check:syntax - rake check:pot - - rake test:unit - COVERAGE=1 rake test:unit - - cat ~/.y2log From 1fc89e689a1c512406c8a3e7ba8659472ac8fe2c Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 15:43:35 +0200 Subject: [PATCH 12/27] more fixes to travis and jenkins --- .travis.yml | 2 +- package/yast2-network.spec | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5fbf005f4..6386fe197 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" + - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev libaugeas rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" script: - rubocop - rake check:syntax diff --git a/package/yast2-network.spec b/package/yast2-network.spec index 2c1a71188..f63cb1cd5 100644 --- a/package/yast2-network.spec +++ b/package/yast2-network.spec @@ -54,6 +54,8 @@ Requires: yast2-packager >= 3.1.47 # cfa for parsing hosts BuildRequires: rubygem(%rb_default_ruby_abi:cfa) Requires: rubygem(%rb_default_ruby_abi:cfa) +# lenses are needed to use cfa +Requires: augeas-lenses # testsuite BuildRequires: rubygem(rspec) From 10a186a8ead561cd8541187fa4da5372b2a5c783 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 15:50:08 +0200 Subject: [PATCH 13/27] more fixes to travis and jenkins --- package/yast2-network.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/package/yast2-network.spec b/package/yast2-network.spec index f63cb1cd5..d5a4c8ecd 100644 --- a/package/yast2-network.spec +++ b/package/yast2-network.spec @@ -55,6 +55,7 @@ Requires: yast2-packager >= 3.1.47 BuildRequires: rubygem(%rb_default_ruby_abi:cfa) Requires: rubygem(%rb_default_ruby_abi:cfa) # lenses are needed to use cfa +BuildRequires: augeas-lenses Requires: augeas-lenses # testsuite From ce70f39943dac59ea2f00fcdd7ac37a3c3011cbd Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 15:51:16 +0200 Subject: [PATCH 14/27] more fixes to travis and jenkins --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6386fe197..d6bcb0954 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev libaugeas rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" + - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev augeas rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" script: - rubocop - rake check:syntax From 447cc1bddab9c66d709877a99b2ad4ab15f92488 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 15:57:53 +0200 Subject: [PATCH 15/27] fix ip addr --- test/data/hosts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data/hosts b/test/data/hosts index 28bfa8e34..cd5c1450c 100644 --- a/test/data/hosts +++ b/test/data/hosts @@ -21,4 +21,4 @@ ff02::1 ipv6-allnodes ff02::2 ipv6-allrouters ff02::3 ipv6-allhosts 10.100.128.72 pepa.labs.suse.cz pepa pepa2 -10.254.128.01 pepa1.labs.suse.cz pepa1 +10.254.128.1 pepa1.labs.suse.cz pepa1 From 8d9d6b8dc61e3da2f0f29609840bcc58cad73a04 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 16:00:26 +0200 Subject: [PATCH 16/27] more fixes to travis and jenkins --- .travis.yml | 2 +- package/yast2-network.spec | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d6bcb0954..be239b1bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: # disable rvm, use system Ruby - rvm reset - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev augeas rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" + - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev augeas-lenses libaugeas0 rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" script: - rubocop - rake check:syntax diff --git a/package/yast2-network.spec b/package/yast2-network.spec index d5a4c8ecd..aaeaf92a9 100644 --- a/package/yast2-network.spec +++ b/package/yast2-network.spec @@ -97,6 +97,8 @@ rake install DESTDIR="%{buildroot}" %{yast_schemadir}/autoyast/rnc/networking.rnc %{yast_schemadir}/autoyast/rnc/host.rnc %{yast_libdir}/network +%dir %{yast_libdir}/cfa/ +%{yast_libdir}/cfa/host.rb %{yast_ydatadir}/network %dir %{yast_docdir} From b5fd99874c7103697d5931538e076e41972e2de1 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 16:12:17 +0200 Subject: [PATCH 17/27] ensure that hosts are not nil --- package/yast2-network.spec | 2 +- src/modules/Host.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package/yast2-network.spec b/package/yast2-network.spec index aaeaf92a9..237072dc3 100644 --- a/package/yast2-network.spec +++ b/package/yast2-network.spec @@ -98,7 +98,7 @@ rake install DESTDIR="%{buildroot}" %{yast_schemadir}/autoyast/rnc/host.rnc %{yast_libdir}/network %dir %{yast_libdir}/cfa/ -%{yast_libdir}/cfa/host.rb +%{yast_libdir}/cfa/hosts.rb %{yast_ydatadir}/network %dir %{yast_docdir} diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 520a18aa9..41a62a7a3 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -44,6 +44,8 @@ def main @modified = false @initialized = false + + @hosts = CFA::Hosts.new end # Remove all entries from the host table. From daa9755684fde01292fab6319f4c8be37711598f Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Wed, 12 Oct 2016 16:28:02 +0200 Subject: [PATCH 18/27] DRY code (thanks @mvidner) --- src/lib/cfa/hosts.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index 056e85a9e..f875e1c1c 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -66,15 +66,7 @@ def delete_host(ip) def set_host(ip, canonical, aliases = []) entries = data.select(ip_matcher(ip)) if entries.empty? - log.info "adding new entry for ip #{ip}" - entry_line = AugeasTree.new - entry_line["ipaddr"] = ip - entry_line["canonical"] = canonical - aliases_col = entry_line.collection("alias") - aliases.each do |a| - aliases_col.add(a) - end - data.add(unique_id, entry_line) + add_host(ip, canonical, aliases) return end From c460b5b0b252df1eb9587ba58ca279f0c878f7bb Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Wed, 12 Oct 2016 16:45:49 +0200 Subject: [PATCH 19/27] Improved API docs --- src/lib/cfa/hosts.rb | 53 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index f875e1c1c..8ce989273 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -19,11 +19,27 @@ def initialize(file_handler: nil) super(PARSER, PATH, file_handler: file_handler) end - # returns old format of hosts used in Host module. - # @return [Hash>] format is hash where key is ip and value - # is array which contain strings and each string represent one line in hosts - # table with comma separated hostnames, where first is canonical and rest - # are aliases + # The old format used by {Yast::HostClass}. + # @return [Hash{String => Array}] keys are IPs, + # values are lists of lines in /etc/hosts (not names!) + # with whitespace separated hostnames, where the first one is canonical + # and the rest are aliases + # + # For example, the file contents + # + # 1.2.3.4 www.example.org www + # 1.2.3.7 log.example.org log + # 1.2.3.7 sql.example.org sql + # + # is returned as + # + # { + # "1.2.3.4" => "www.example.org www" + # "1.2.3.7" => [ + # "log.example.org log", + # "sql.example.org sql" + # ] + # } def hosts matcher = Matcher.new { |k, _v| k =~ /^\d*$/ } data.select(matcher).each_with_object({}) do |host, result| @@ -34,7 +50,8 @@ def hosts end # Returns single entry from hosts for given ip or empty array if not found - # @see {#hosts} + # @see #hosts + # @return [Array] def host(ip) hosts = data.select(ip_matcher(ip)) @@ -44,6 +61,7 @@ def host(ip) end # deletes all occurences of given ip in host table + # @return [void] def delete_host(ip) entries = data.select(ip_matcher(ip)) if entries.empty? @@ -61,8 +79,13 @@ def delete_host(ip) end end - # replaces or adds new host entry. If more then one entry with given ip exists - # then replaces the last instance + # Replaces or adds a new host entry. + # If more than one entry with the given ip exists + # then it replaces the last instance. + # @param [String] ip + # @param [String] canonical + # @param [Array] aliases + # @return [void] def set_host(ip, canonical, aliases = []) entries = data.select(ip_matcher(ip)) if entries.empty? @@ -86,7 +109,11 @@ def set_host(ip, canonical, aliases = []) end end - # adds new entry, even if it exists + # Adds new entry, even if it exists + # @param [String] ip + # @param [String] canonical + # @param [Array] aliases + # @return [void] def add_host(ip, canonical, aliases = []) log.info "adding new entry for ip #{ip}" entry_line = AugeasTree.new @@ -99,9 +126,11 @@ def add_host(ip, canonical, aliases = []) data.add(unique_id, entry_line) end - # removes hostname from all entries in hosts table. - # if it is only hostname for given ip, ip is removed - # if it is canonical part, then first alias is used as canonical hostname + # Removes hostname from all entries in hosts table. + # If it is the only hostname for a given ip, the ip is removed + # If it is canonical name, then the first alias becomes the canonical hostname + # @param [String] hostname + # @return [void] def remove_hostname(hostname) entries = data.select(hostname_matcher(hostname)) entries.each do |pair| From 43ebe40889d51b526c35219edffeb18f9f6814e5 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 13 Oct 2016 09:24:29 +0200 Subject: [PATCH 20/27] use better names for some API calls (thanks @mvidner) --- src/lib/cfa/hosts.rb | 8 ++++---- src/modules/Host.rb | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index 8ce989273..8bd8d3681 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -62,7 +62,7 @@ def host(ip) # deletes all occurences of given ip in host table # @return [void] - def delete_host(ip) + def delete_by_ip(ip) entries = data.select(ip_matcher(ip)) if entries.empty? log.info "no entry to delete for ip #{ip}" @@ -86,10 +86,10 @@ def delete_host(ip) # @param [String] canonical # @param [Array] aliases # @return [void] - def set_host(ip, canonical, aliases = []) + def set_entry(ip, canonical, aliases = []) entries = data.select(ip_matcher(ip)) if entries.empty? - add_host(ip, canonical, aliases) + add_entry(ip, canonical, aliases) return end @@ -114,7 +114,7 @@ def set_host(ip, canonical, aliases = []) # @param [String] canonical # @param [Array] aliases # @return [void] - def add_host(ip, canonical, aliases = []) + def add_entry(ip, canonical, aliases = []) log.info "adding new entry for ip #{ip}" entry_line = AugeasTree.new entry_line["ipaddr"] = ip diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 41a62a7a3..b74172fd3 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -51,7 +51,7 @@ def main # Remove all entries from the host table. def clear @hosts.hosts.keys.each do |ip| - @hosts.delete_host(ip) + @hosts.delete_by_ip(ip) end @modified = true end @@ -68,14 +68,14 @@ def names(address) # remove all instances of ip in hosts table def remove_ip(address) - @hosts.delete_host(address) + @hosts.delete_by_ip(address) end # Add another name to the list for address (which may be empty so far) # FIXME: used only in one place, which looks wrong def add_name(address, name) canonical, *aliases = name.split(" ") - @hosts.add_host(address, canonical, aliases) + @hosts.add_entry(address, canonical, aliases) @modified = true end @@ -98,7 +98,7 @@ def EnsureHostnameResolvable # Do not add it if product default says no # and remove 127.0.02 entry if it exists - @hosts.delete_host(local_ip) + @hosts.delete_by_ip(local_ip) end @modified = true @@ -235,7 +235,7 @@ def Update(oldhn, newhn, ip) # Add localhost if missing if @hosts.host("127.0.0.1").empty? - @hosts.add_host("127.0.0.1", "localhost") + @hosts.add_entry("127.0.0.1", "localhost") end # Omit some IP addresses @@ -247,12 +247,12 @@ def Update(oldhn, newhn, ip) nick = nick.empty? ? [] : [nick] hosts = @hosts.host(ip) if hosts.empty? - @hosts.add_host(ip, newhn, nick) + @hosts.add_entry(ip, newhn, nick) else canonical, *aliases = hosts.last aliases << newhn aliases.concat(nick) - @hosts.set_host(ip, canonical, aliases) + @hosts.set_entry(ip, canonical, aliases) end true @@ -331,10 +331,10 @@ def SetModified # Give address a new list of names. def set_names(address, names) - @hosts.delete_host(address) + @hosts.delete_by_ip(address) names.each do |name| canonical, *aliases = name.split(" ") - @hosts.add_host(address, canonical, aliases) + @hosts.add_entry(address, canonical, aliases) end @modified = true end From 5d4bfaa193cdea0fa203645b7a308bebb74482c0 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Thu, 13 Oct 2016 13:51:34 +0200 Subject: [PATCH 21/27] consistent delete keyword --- src/lib/cfa/hosts.rb | 2 +- src/modules/Host.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index 8bd8d3681..89e7680d4 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -131,7 +131,7 @@ def add_entry(ip, canonical, aliases = []) # If it is canonical name, then the first alias becomes the canonical hostname # @param [String] hostname # @return [void] - def remove_hostname(hostname) + def delete_hostname(hostname) entries = data.select(hostname_matcher(hostname)) entries.each do |pair| entry = pair[:value] diff --git a/src/modules/Host.rb b/src/modules/Host.rb index b74172fd3..8412d4294 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -231,7 +231,7 @@ def Update(oldhn, newhn, ip) @modified = true # Remove old hostname from hosts - @hosts.remove_hostname(oldhn) if !oldhn.empty? + @hosts.delete_hostname(oldhn) if !oldhn.empty? # Add localhost if missing if @hosts.host("127.0.0.1").empty? From 8eb9bfa68077742d253560660dfef89fcfe3a9c4 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 14 Oct 2016 15:07:02 +0200 Subject: [PATCH 22/27] fix removing hostname with test --- src/lib/cfa/hosts.rb | 15 +++++++++++++-- test/host_test.rb | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index 89e7680d4..35a9f40e5 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -140,12 +140,23 @@ def delete_hostname(hostname) if aliases.empty? delete_host(entry["ipaddr"]) else - set_host(entry["ipaddr"], aliases.first, aliases[1..-1]) + entry["canonical"] = aliases.first + entry.delete("alias") + entry.delete("alias[]") + aliases_col = entry.collection("alias") + aliases[1..-1].each do |a| + aliases_col.add(a) + end end else reduced_aliases = aliases_for(entry) reduced_aliases.delete(hostname) - set_host(entry["ipaddr"], entry["canonical"], reduced_aliases) + entry.delete("alias") + entry.delete("alias[]") + aliases_col = entry.collection("alias") + aliases[1..-1].each do |a| + aliases_col.add(a) + end end end end diff --git a/test/host_test.rb b/test/host_test.rb index b6f58db36..468c41b91 100755 --- a/test/host_test.rb +++ b/test/host_test.rb @@ -168,5 +168,16 @@ tested_ip = "10.0.0.42" expect(Yast::Host.name_map[tested_ip]).to eql ["newname.suse.cz newname"] end + + it "deletes old hostnames passed as first parameter" do + Yast::Host.Read + Yast::Host.Update("pepa.labs.suse.cz", "newname.suse.cz", "10.0.0.42") + Yast::Host.Write + + content = file.content + + expect(content.lines).to include("10.100.128.72 pepa pepa2\n") + expect(content.lines).to include("10.0.0.42\tnewname.suse.cz newname\n") + end end end From 72281b175b9c2db9c732daa97cbd2338e8bf6112 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 14 Oct 2016 15:13:39 +0200 Subject: [PATCH 23/27] fix appending hostname in Host#Update --- src/modules/Host.rb | 2 +- test/host_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/Host.rb b/src/modules/Host.rb index 8412d4294..d64d4371d 100644 --- a/src/modules/Host.rb +++ b/src/modules/Host.rb @@ -249,7 +249,7 @@ def Update(oldhn, newhn, ip) if hosts.empty? @hosts.add_entry(ip, newhn, nick) else - canonical, *aliases = hosts.last + canonical, *aliases = hosts.last.split(" ") aliases << newhn aliases.concat(nick) @hosts.set_entry(ip, canonical, aliases) diff --git a/test/host_test.rb b/test/host_test.rb index 468c41b91..d53904940 100755 --- a/test/host_test.rb +++ b/test/host_test.rb @@ -179,5 +179,15 @@ expect(content.lines).to include("10.100.128.72 pepa pepa2\n") expect(content.lines).to include("10.0.0.42\tnewname.suse.cz newname\n") end + + it "adds hostname as alias if ip have already its entry" do + Yast::Host.Read + Yast::Host.Update("pepa.labs.suse.cz", "newname.suse.cz", "10.100.128.72") + Yast::Host.Write + + content = file.content + + expect(content.lines).to include("10.100.128.72 pepa pepa2 newname.suse.cz newname\n") + end end end From 9bdd9af81c4ced656b92f1d389ff28e910fe3a4f Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 14 Oct 2016 15:16:14 +0200 Subject: [PATCH 24/27] try to fix travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index be239b1bb..bcf123337 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,12 @@ compiler: before_install: # disable rvm, use system Ruby - rvm reset + - sudo add-apt-repository -y ppa:raphink/augeas + - sudo apt-get update + - sudo apt-get install libaugeas-dev libxml2-dev - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev augeas-lenses libaugeas0 rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" + # get newer augeas script: - rubocop - rake check:syntax From b508d83f3c8924b8ceb95fc4b42351c92c375e01 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 14 Oct 2016 15:26:14 +0200 Subject: [PATCH 25/27] try smaller travis fix --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index bcf123337..5d4526298 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,12 +4,10 @@ compiler: before_install: # disable rvm, use system Ruby - rvm reset + # get repo with newer augeas - sudo add-apt-repository -y ppa:raphink/augeas - - sudo apt-get update - - sudo apt-get install libaugeas-dev libxml2-dev - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev augeas-lenses libaugeas0 rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" - # get newer augeas script: - rubocop - rake check:syntax From e6b65e0eb3e3bc4a4e5e9920f1bee7bc387bdeef Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 14 Oct 2016 15:35:01 +0200 Subject: [PATCH 26/27] revert back smaller travis fix --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d4526298..468ef8b8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,13 @@ compiler: before_install: # disable rvm, use system Ruby - rvm reset - # get repo with newer augeas + # install newer augeasget repo with newer augeas, otherwise ruby-augeas fails (see https://github.com/yast/yast-network/pull/454#issuecomment-253795507) - sudo add-apt-repository -y ppa:raphink/augeas + - sudo apt-get update + - sudo apt-get install libaugeas-dev libxml2-dev + # end of augeas install - wget https://raw.githubusercontent.com/yast/yast-devtools/master/travis-tools/travis_setup.sh - - sh ./travis_setup.sh -p "ruby2.1-dev libaugeas-dev augeas-lenses libaugeas0 rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" + - sh ./travis_setup.sh -p "ruby2.1-dev augeas-lenses libaugeas0 rake yast2-devtools yast2-testsuite yast2 yast2-storage yast2-proxy yast2-country yast2-packager" -g "rspec:3.3.0 yast-rake gettext rubocop:0.41.2 simplecov:0.10.0 coveralls cfa cheetah" script: - rubocop - rake check:syntax From ed6a634d88705c0a5f40b3bb5e1ddccee59b7b41 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 14 Oct 2016 16:09:31 +0200 Subject: [PATCH 27/27] changes --- package/yast2-network.changes | 7 +++++++ package/yast2-network.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2-network.changes b/package/yast2-network.changes index a57d5f3c1..eb5f0a09a 100644 --- a/package/yast2-network.changes +++ b/package/yast2-network.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Oct 14 14:08:31 UTC 2016 - jreidinger@suse.com + +- optimize loading /etc/hosts if there is a lot of entries + (bsc#877047) +- 3.2.0 + ------------------------------------------------------------------- Wed Aug 24 10:54:55 UTC 2016 - mfilka@suse.com diff --git a/package/yast2-network.spec b/package/yast2-network.spec index 237072dc3..b57b10afa 100644 --- a/package/yast2-network.spec +++ b/package/yast2-network.spec @@ -17,7 +17,7 @@ Name: yast2-network -Version: 3.1.168 +Version: 3.2.0 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build