Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Sle15] AY setup written in first stage #520

Merged
merged 23 commits into from
Jul 21, 2017
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions package/yast2-network.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
-------------------------------------------------------------------
Fri Jul 11 09:40:11 UTC 2017 - mfilka@suse.com

- Moving network setup in AY into first stage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is a major thing we should have a public bug for this. I could not find one in https://trello.com/c/dxyLJ9c8/956-8-sle-15-%E2%9A%A1-autoyast-configure-the-whole-network-in-the-first-stage so should I create one?
Also, /etc/hosts should be mentioned.

- DNS configuration is written when second stage is disabled in
the profile
- 3.3.3

-------------------------------------------------------------------
Mon Jul 10 15:03:09 UTC 2017 - jreidinger@suse.com

Expand Down
2 changes: 1 addition & 1 deletion package/yast2-network.spec
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


Name: yast2-network
Version: 3.3.2
Version: 3.3.3
Release: 0
BuildArch: noarch

Expand Down
127 changes: 1 addition & 126 deletions src/clients/lan_auto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def main
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
Expand Down Expand Up @@ -124,131 +124,6 @@ def main
@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
Expand Down
24 changes: 22 additions & 2 deletions src/clients/save_network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -259,16 +259,36 @@ def configure_dns
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK formerly this was not associated with AutoYaST. So we should explicitly point this out:

It works for both values of Mode.autoinst

BTW, what about autoupgrade? http://www.rubydoc.info/github/yast/yast-yast2/Yast/ModeClass

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgrade scenario ignores network setup ... but i'm not sure about autoupgrade - will check

#
# 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

Expand Down
108 changes: 91 additions & 17 deletions src/lib/network/network_autoyast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def initialize
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
Expand Down Expand Up @@ -104,26 +106,40 @@ def set_network_service
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

log.info("NetworkAutoYast: DNS / Hostname configuration")
log.info("dhcp hostname: #{DNS.dhcp_hostname}")
log.info("write hostname: #{DNS.write_hostname}")
# 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")

true
# expected format for Hosts.Import is { "ip" => [list, of, names] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, and this must be documented there, in the API docs for Host.Import (not Hosts).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

hosts_config = (ay_host_section["hosts"] || {}).map do |host|
# we neet 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? }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This non-trivial FromAY-like section should go into its own method (class?) and be covered with tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is almost everything what configure_hosts does

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, now that the other configure_* methods are gone it makes less sense to split it off. Still, with all the "or empty" logic in here, tests are needed.

Also, how does this code relate to Import in host_auto? Do we need to "guarantee item order" also there?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item order is most probably bad description here. I refer to "host_address" and "names". Each of these hash keys refer to a xml node. And this nodes can be ordered differently in the profile. We must guarantee that "host_address" is at first place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this duplicates Host.Import . Is that one obsolete then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, configure_submodule does call Import


configure_submodule(Host, "hosts" => hosts_config, write: write)
end

# Checks if the profile asks for keeping installation network configuration
Expand Down Expand Up @@ -206,16 +222,53 @@ def merge_routing(instsys_routing, ay_routing)
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

# Returns networking section of current AY profile
def ay_networking_section
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO it would be more clear to replace this method by inlining ay_current_profile.fetch("networking", {})

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regarding Host.Import see bellow

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my POV "networking" section is used quite often so it deserved own method as a shortcut.

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_profile["networking"]
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>
# {
# "hosts_address" => <ip>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. "host_address".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

# "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
Expand Down Expand Up @@ -265,5 +318,26 @@ def assign_udevs_to_devs(udev_rules)
LanItems.rename(name_to)
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
Loading