diff --git a/Rakefile b/Rakefile index 2432dd52a..230638706 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,29 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast/rake" Yast::Tasks.configuration do |conf| - # lets ignore license check for now - conf.skip_license_check << /.*/ + conf.skip_license_check << /doc\// + conf.skip_license_check << /test\/data/ + conf.skip_license_check << /\.desktop$/ + conf.skip_license_check << /\.rnc$/ + # ensure we are not getting worse with documentation + conf.documentation_minimal = 61 if conf.respond_to?(:documentation_minimal=) end diff --git a/doc/network-ng.md b/doc/network-ng.md index 6516894b6..c928513b8 100644 --- a/doc/network-ng.md +++ b/doc/network-ng.md @@ -3,7 +3,9 @@ This document tries to describe the details of the network-ng data model and how all the pieces are combined. -## Overall Approach +## Data Model + +### Overall Approach {Y2Network::Config} and other associated classes represent the network configuration in a backend agnostic way. It should not matter whether you are using `sysconfig` files, NetworkManager or @@ -25,7 +27,7 @@ filesystem by using a *configuration writer*. Obviously, we should implement configuration readers and writers for whathever backend we would like to support. At this point of time, only `Sysconfig` and `Autoinst` are supported. -## The Configuration Classes +### The Configuration Classes {Y2Network::Config} offers and API to deal with network configuration, but it collaborates with other classes. @@ -38,31 +40,77 @@ other classes. Currently it is bound to an interface name, but we plan to provide more advanced matchers. * {Y2Network::Routing}: holds routing information, including IP forwarding settings, routing tables, etc. -## Backend Support +### Multi-Backend Support + +As mentioned above, Y2Network is designed to support different backends. It is expected to implement +a reader and a writer for each backend (except for AutoYaST, which is an special case). The reader +will be responsible for checking the system's configuration and building a {Y2Network::Config} +object, containing interfaces, configurations, routes, etc. On the other hand, the writer will be +responsible for updating the system using that configuration object. + +As a developer, you rarely will need to access to readers/writers because `Yast::Lan` already offers +an API to read and write the configuration. See the [Accessing the +Configuration](#accessing-the-configuration) section for further details. -In order to support a new backend, we need to implement a configuration readers and writers. The -{Y2Network::Sysconfig} module implements support to deal with `sysconfig` files. +#### Sysconfig + +The sysconfig backend support is composed by these files: src/lib/y2network/sysconfig ├── config_reader.rb <- READER ├── config_writer.rb <- WRITER - ├── connection_config_reader_handlers - │ ├── eth.rb - │ ├── wlan.rb - │ └── ... ├── connection_config_reader.rb + ├── connection_config_readers + │   ├── ethernet.rb + │   ├── wireless.rb + │ └── ... + ├── connection_config_writer.rb + ├── connection_config_writers + │   ├── ethernet.rb + │   ├── wireless.rb + │ └── ... ├── dns_reader.rb ├── dns_writer.rb ├── interface_file.rb ├── interfaces_reader.rb - ├── interfaces_writer.rb └── routes_file.rb -As you can see, there are many classes, but the relevant ones are just `ConfigReader` and `ConfigWriter`. +{Y2Network::Sysconfig::ConfigReader} and {Y2Network::Sysconfig::ConfigWriter} are the reader and +writer classes. Each of them cooperates with a set of ancillary classes in order to get the job +done. + +{Y2Network::Sysconfig::DNSReader}, {Y2Network::Sysconfig::InterfacesReader} and +{Y2Network::Sysconfig::ConnectionConfigReader} are involved in reading the configuration. The logic +to read the configuration for a connection (e.g., `ifcfg-eth0`, `ifcfg-wlan0`, etc.) is implemented +in a set of smaller classes (one for each time of connection) under +{Y2Network::Sysconfig::ConnectionConfigReaders}. + +{Y2Network::Sysconfig::DNSWriter} and {Y2Network::Sysconfig::ConnectionConfigWriter}, including +smaller classes under {Y2Network::Sysconfig::ConnectionConfigWriters}, are involved in writing the +configuration. In this case, it does not exist a `InterfacesWriter` class, because when it comes to +interfaces the configuration is handled through connection configuration files. -## Accessing the Configuration +Last but not least, there are additional classes like {Y2Network::Sysconfig::RoutesFile} and +{Y2Network::Sysconfig::InterfaceFile} which abstract the details of reading/writing `ifcfg` files. -The `Yast::Lan` module is still the entry point to read and write the network configuration. Basically, it keeps two configuration objects, one for the running system and another want for the wanted configuration. +#### AutoYaST + +AutoYaST is a special case in the sense that it reads the information from a profile, instead of +using the running system as reference. Additionally, it does not implement a writer because the +configuration will be written using a different backend (like sysconfig). + + src/lib/y2network/autoinst/ + ├── config_reader.rb + ├── dns_reader.rb + └── routing_reader.rb + +For the time being, it only implements support to read DNS and routing information. + +### Accessing the Configuration + +The `Yast::Lan` module is still the entry point to read and write the network configuration. +Basically, it keeps two configuration objects, one for the running system and another want for the +wanted configuration. Yast.import "Lan" Yast::Lan.read(:cache) @@ -78,11 +126,32 @@ The `Yast::Lan` module is still the entry point to read and write the network co Any change you want to apply to the running system should be performed by modifying the `yast_config` and writing the changes. -## AutoYaST Support +## New UI layer + +### Interface Configuration Builders + +In the old code, there was no clear separation between UI and business logic. In order to improve +the situation, we introduced the concept of [interface configuration +builders](https://github.com/yast/yast-network/blob/network-ng/src/lib/y2network/interface_config_builder.rb). + +We already have implemented support for several interface types. You can find them under the +[Y2Network::InterfaceConfigBuilders +namespace](https://github.com/yast/yast-network/tree/843f75bfdb71d4026b3f97facf18eece479b8a0e/src/lib/y2network/interface_config_builders). -AutoYaST is somehow and special case, as the configuration is read from the profile instead of the -running system. So in this scenario, YaST2 Network will read the configuration using the `Autoinst` -reader and will write it to the final system using the one corresponding to the wanted backend. +### Widgets + +The user interaction is driven by a set of sequences, which determine which dialogs are shown to the +user. Each of those dialogs contain a set of widgets, usually grouped in tabs. The content of the +dialog depends on the interface type. + +Below you can find some pointers to relevant sequences, dialogs and widgets: + +* Sequences: + * [Sequences::Interface](https://github.com/yast/yast-network/blob/358bcd13b4e92e7c4e9c0e477c83196ca67b578e/src/lib/y2network/sequences/interface.rb) +* Dialogs: + * [Dialogs::AddInterface](https://github.com/yast/yast-network/blob/358bcd13b4e92e7c4e9c0e477c83196ca67b578e/src/lib/y2network/dialogs/add_interface.rb) + * [Dialogs::EditInterface](https://github.com/yast/yast-network/blob/358bcd13b4e92e7c4e9c0e477c83196ca67b578e/src/lib/y2network/dialogs/edit_interface.rb) +* [Y2Network::Widgets](https://github.com/yast/yast-network/tree/358bcd13b4e92e7c4e9c0e477c83196ca67b578e/src/lib/y2network/widgets) ## Current Status @@ -90,8 +159,8 @@ reader and will write it to the final system using the one corresponding to the | Interface type | read | write | |-----------------|------|-------| -| Ethernet | ✓ | ⌛ | -| Wireless | ✓ | ⌛ | +| Ethernet | ✓ | ✓ | +| Wireless | ✓ | ✓ | | InfiniBand | ⌛ | | | Bridge | ⌛ | | | Bonding | | | @@ -101,3 +170,9 @@ reader and will write it to the final system using the one corresponding to the | USB | | | | Dummy | | | | s390 types | | | + +## (Short Term) Plan + +- [ ] Finish reading/writing interfaces +- [ ] Move missing UI logic to interface configuration builders +- [ ] Replace LanItems with the new data model diff --git a/examples/connection.rb b/examples/connection.rb index f82ee9e7d..75009be9a 100644 --- a/examples/connection.rb +++ b/examples/connection.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "y2network/config" require "y2network/virtual_interface" diff --git a/src/autoyast-rnc/networking.rnc b/src/autoyast-rnc/networking.rnc index 7ab804e53..c3f19bfb0 100644 --- a/src/autoyast-rnc/networking.rnc +++ b/src/autoyast-rnc/networking.rnc @@ -100,6 +100,7 @@ device = element type { text }? & element layer2 { BOOLEAN}? & element chanids { text }? & + # ignored, present for backward compatibility element portname { text }? & element protocol { text }? & element router { text }? diff --git a/src/clients/lan_auto.rb b/src/clients/lan_auto.rb index 49fc03456..e406e29cd 100644 --- a/src/clients/lan_auto.rb +++ b/src/clients/lan_auto.rb @@ -86,9 +86,9 @@ def main elsif @func == "Packages" @ret = Lan.AutoPackages elsif @func == "SetModified" - @ret = LanItems.SetModified + @ret = Lan.SetModified elsif @func == "GetModified" - @ret = LanItems.GetModified + @ret = Lan.Modified elsif @func == "Export" @settings2 = Lan.Export Builtins.y2debug("settings: %1", @settings2) @@ -133,66 +133,17 @@ def main # @return [Hash] autoyast network settings def ToAY(settings) settings = deep_copy(settings) - interfaces = [] - discard = ["UDI", "_nm_name"] - Builtins.foreach(Ops.get_map(settings, "devices", {})) do |_type, devsmap| - Builtins.foreach( - Convert.convert(devsmap, from: "map", to: "map ") - ) do |device, devmap| - newmap = {} - Builtins.foreach( - Convert.convert(devmap, from: "map", to: "map ") - ) do |key, val| - Builtins.y2milestone("Adding: %1=%2", key, val) - if key != "_aliases" - if Ops.greater_than(Builtins.size(Convert.to_string(val)), 0) && - !Builtins.contains(discard, key) && - !Builtins.contains(discard, Builtins.tolower(key)) - Ops.set(newmap, Builtins.tolower(key), Convert.to_string(val)) - end - else - # handle aliases - Builtins.y2debug("val: %1", val) - # if aliases are empty, then ommit it - if Ops.greater_than(Builtins.size(Convert.to_map(val)), 0) - # replace key "0" into "alias0" (bnc#372678) - Builtins.foreach( - Convert.convert( - val, - from: "any", - to: "map >" - ) - ) do |k, v| - Ops.set( - newmap, - Builtins.tolower("aliases"), - Builtins.add( - Ops.get_map(newmap, Builtins.tolower("aliases"), {}), - Builtins.sformat("alias%1", k), - v - ) - ) - end - end - end - end - newmap["device"] = device - interfaces = Builtins.add(interfaces, newmap) - end - end + interfaces = settings["interfaces"] || [] + Builtins.y2milestone("interfaces: #{interfaces.inspect})") + net_udev = settings["net-udev"] || [] + Builtins.y2milestone("net-udev: #{net_udev.inspect})") # Modules - s390_devices = [] Builtins.foreach(Ops.get_map(settings, "s390-devices", {})) do |_device, mod| s390_devices = Builtins.add(s390_devices, mod) end - net_udev = [] - Builtins.foreach(Ops.get_map(settings, "net-udev", {})) do |_device, mod| - net_udev = Builtins.add(net_udev, mod) - end - modules = [] Builtins.foreach(Ops.get_map(settings, "hwcfg", {})) do |device, mod| newmap = {} diff --git a/src/include/network/summary.rb b/src/clients/lan_export.rb similarity index 54% rename from src/include/network/summary.rb rename to src/clients/lan_export.rb index c715253ef..c36c39970 100644 --- a/src/include/network/summary.rb +++ b/src/clients/lan_export.rb @@ -21,33 +21,28 @@ # you may find current contact information at www.novell.com # # ************************************************************************** -# File: include/network/summary.ycp -# Package: Network configuration -# Summary: Summary and overview functions -# Authors: Michal Svec -# -# -# All config settings are stored in a global variable Devices. -# All hardware settings are stored in a global variable Hardware. -# Deleted devices are in the global list DELETED. + +require "yaml" + module Yast - module NetworkSummaryInclude - def initialize_network_summary(_include_target) + # Client for testing autoyast export and writes result to /tmp/test.yaml + # DEVELOPMENT ONLY, not for production use + class LanExportClient < Client + def main + Yast.import "UI" + textdomain "network" - Yast.import "String" - Yast.import "NetworkInterfaces" - end + # Open Trivial UI to get error messages + Yast::UI.OpenDialog(Yast::Term.new(:PushButton, "Test")) + WFM.CallFunction("lan_auto", ["Read"]) + res = WFM.CallFunction("lan_auto", ["Export"]) + File.write("/tmp/test.yaml", res.to_yaml) + Yast::UI.CloseDialog - # Create list of Table items - # @param [Array] types list of types - # @param [String] cur current type - # @return Table items - def BuildTypesList(types, cur) - types = deep_copy(types) - Builtins.maplist(types) do |t| - Item(Id(t), NetworkInterfaces.GetDevTypeDescription(t, false), t == cur) - end + nil end end end + +Yast::LanExportClient.new.main diff --git a/src/data/network/s390_defaults.yml b/src/data/network/s390_defaults.yml deleted file mode 100644 index d51d54f44..000000000 --- a/src/data/network/s390_defaults.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- # s390 configuration options -CHAN_MODE: '0' -QETH_PORTNAME: '' -QETH_PORTNUMBER: '' -QETH_OPTIONS: '' -QETH_LAYER2: 'no' -QETH_CHANIDS: '' -IPA_TAKEOVER: 'no' -IUCV_USER: '' -LLADDR: '00:00:00:00:00:00' - diff --git a/src/data/network/sysconfig_defaults.yml b/src/data/network/sysconfig_defaults.yml deleted file mode 100644 index f96a2573c..000000000 --- a/src/data/network/sysconfig_defaults.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- # generic sysconfig options -BOOTPROTO: static -IPADDR: '' -PREFIXLEN: '' -REMOTE_IPADDR: '' -NETMASK: '' -MTU: '' -ETHTOOL_OPTIONS: '' -NAME: '' -STARTMODE: '' -IFPLUGD_PRIORITY: '0' -WIRELESS_MODE: Managed -WIRELESS_ESSID: '' -WIRELESS_NWID: '' -WIRELESS_AUTH_MODE: open -WIRELESS_WPA_PSK: '' -WIRELESS_KEY_LENGTH: '128' -WIRELESS_KEY: '' -WIRELESS_KEY_0: '' -WIRELESS_KEY_1: '' -WIRELESS_KEY_2: '' -WIRELESS_KEY_3: '' -WIRELESS_DEFAULT_KEY: '0' -WIRELESS_NICK: '' -WIRELESS_CHANNEL: '' -WIRELESS_FREQUENCY: '' -WIRELESS_BITRATE: auto -WIRELESS_AP: '' -WIRELESS_POWER: '' -WIRELESS_EAP_MODE: 'PEAP' -WIRELESS_WPA_IDENTITY: '' -WIRELESS_WPA_PASSWORD: '' -WIRELESS_WPA_ANONID: '' -WIRELESS_CLIENT_CERT: '' -WIRELESS_CLIENT_KEY: '' -WIRELESS_CLIENT_KEY_PASSWORD: '' -WIRELESS_CA_CERT: '' -WIRELESS_EAP_AUTH: 'MSCHAPV2' -WIRELESS_PEAP_VERSION: '' -WIRELESS_AP_SCANMODE: '1' -BONDING_MODULE_OPTS: mode=active-backup miimon=100 -TUNNEL_SET_OWNER: '' -TUNNEL_SET_GROUP: '' -BRIDGE_PORTS: '' -BRIDGE: 'yes' -BRIDGE_STP: 'off' -BRIDGE_FORWARDDELAY: '0' diff --git a/src/include/network/complex.rb b/src/include/network/complex.rb index 09381989a..35fa47327 100644 --- a/src/include/network/complex.rb +++ b/src/include/network/complex.rb @@ -42,7 +42,6 @@ def initialize_network_complex(include_target) Yast.import "Summary" Yast.include include_target, "network/routines.rb" - Yast.include include_target, "network/summary.rb" end # Used for initializing the description variable (ifcfg[NAME]) diff --git a/src/include/network/lan/address.rb b/src/include/network/lan/address.rb index f75472a0a..7bbb96646 100644 --- a/src/include/network/lan/address.rb +++ b/src/include/network/lan/address.rb @@ -28,6 +28,7 @@ # require "y2firewall/helpers/interfaces" require "y2network/dialogs/edit_interface" +require "y2network/boot_protocol" module Yast module NetworkLanAddressInclude @@ -40,17 +41,11 @@ def initialize_network_lan_address(include_target) textdomain "network" - Yast.import "Host" - Yast.import "Lan" - Yast.import "NetworkInterfaces" Yast.import "ProductFeatures" - Yast.import "String" - Yast.include include_target, "network/summary.rb" Yast.include include_target, "network/lan/help.rb" Yast.include include_target, "network/lan/hardware.rb" Yast.include include_target, "network/complex.rb" - Yast.include include_target, "network/lan/bridge.rb" Yast.include include_target, "network/lan/s390.rb" @force_static_ip = ProductFeatures.GetBooleanFeature( @@ -65,103 +60,30 @@ def AddressDialog(builder:) @builder = builder ret = Y2Network::Dialogs::EditInterface.run(builder) - log.info "ShowAndRun: #{ret}" if ret != :back && ret != :abort - bootproto = builder["BOOTPROTO"] - ipaddr = builder["IPADDR"] - - # IP is mandatory for static configuration. Makes no sense to write static - # configuration without that. - return ret if bootproto == "static" && ipaddr.empty? - - if bootproto == "static" - update_hostname(ipaddr, builder["HOSTNAME"] || "") - elsif LanItems.isCurrentDHCP && !LanItems.isCurrentHotplug + if LanItems.isCurrentDHCP && !LanItems.isCurrentHotplug # fixed bug #73739 - if dhcp is used, dont set default gw statically # but also: reset default gw only if DHCP* is used, this branch covers # "No IP address" case, then default gw must stay (#460262) # and also: don't delete default GW for usb/pcmcia devices (#307102) - if LanItems.isCurrentDHCP && !LanItems.isCurrentHotplug - yast_config = Y2Network::Config.find(:yast) - if yast_config && yast_config.routing && yast_config.routing.default_route - remove_gw = Popup.YesNo( - _( - "A static default route is defined.\n" \ - "It is suggested to remove the static default route definition \n" \ - "if one can be obtained also via DHCP.\n" \ - "Do you want to remove the static default route?" - ) + yast_config = Y2Network::Config.find(:yast) + if yast_config && yast_config.routing && yast_config.routing.default_route + remove_gw = Popup.YesNo( + _( + "A static default route is defined.\n" \ + "It is suggested to remove the static default route definition \n" \ + "if one can be obtained also via DHCP.\n" \ + "Do you want to remove the static default route?" ) - yast_config.routing.remove_default_routes if remove_gw - end + ) + yast_config.routing.remove_default_routes if remove_gw end end - - # When virtual interfaces are added the list of routing devices needs - # to be updated to offer them - LanItems.add_device_to_routing if LanItems.update_routing_devices? end - # rollback if changes are canceled, as still some widgets edit LanItems directly - LanItems.Rollback if ret != :next - # proceed with WLAN settings if appropriate, #42420 - ret = :wire if ret == :next && builder.type == "wlan" - log.info "AddressDialog res: #{ret.inspect}" ret end - - private - - # Performs hostname update - # - # This handles ip and hostname change when editing NIC properties. - # The method relies on old NIC's IP which is set globally at initialization - # of NIC edit dialog (@see LanItems#ipaddr) - # - # When hostname is empty, then old IP's record is cleared from /etc/hosts and - # new is not created. - # Otherwise the canonical name and all aliases in the record - # are replaced by new ones. - # - # @param ipaddr [String] ip address - # @param hostname [String] new hostname - def update_hostname(ipaddr, hostname) - ip_changed = LanItems.ipaddr != ipaddr - initial_hostname = initial_hostname(LanItems.ipaddr) - hostname_changed = initial_hostname != hostname - - return if !(ip_changed || hostname_changed || hostname.empty?) - - # store old names, remove the record - names = Host.names(LanItems.ipaddr).first - Host.remove_ip(LanItems.ipaddr) - - if ip_changed && !hostname_changed && !names.nil? - log.info("Dropping record for #{LanItems.ipaddr} from /etc/hosts") - - Host.add_name(ipaddr, names) - end - if !hostname.empty? && hostname_changed - log.info("Updating cannonical name for #{LanItems.ipaddr} in /etc/hosts") - - Host.Update(initial_hostname, hostname, ipaddr) - end - - nil - end - - # Returns canonical hostname for the given ip - def initial_hostname(ipaddr) - host_list = Host.names(ipaddr) - if Ops.greater_than(Builtins.size(host_list), 1) - Builtins.y2milestone( - "More than one hostname for single IP detected, using the first one only" - ) - end - - String.FirstChunk(Ops.get(host_list, 0, ""), " \t") - end end end diff --git a/src/include/network/lan/bridge.rb b/src/include/network/lan/bridge.rb deleted file mode 100644 index 9b075610b..000000000 --- a/src/include/network/lan/bridge.rb +++ /dev/null @@ -1,72 +0,0 @@ -# encoding: utf-8 - -# *************************************************************************** -# -# Copyright (c) 2012 Novell, Inc. -# All Rights Reserved. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of version 2 of the GNU General Public License as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, -# you may find current contact information at www.novell.com -# -# ************************************************************************** -# File: include/network/lan/address.ycp -# Package: Network configuration -# Summary: Network card adresss configuration dialogs -# Authors: Michal Svec -# -module Yast - module NetworkLanBridgeInclude - include Logger - - def initialize_network_lan_bridge(_include_target) - textdomain "network" - end - - # Immediately updates device's ifcfg to be usable as bridge port. - # - # It mainly setups suitable BOOTPROTO an IP related values - def configure_as_bridge_port(device) - selected_interface = NetworkInterfaces.Current - log.info("Adapt device #{device} as bridge port") - - # when using wicked every device which can be bridged - # can be set to BOOTPROTO=none. No workaround with - # BOOTPROTO=static required anymore - if NetworkInterfaces.Edit(device) - NetworkInterfaces.Current["IPADDR"] = "" - NetworkInterfaces.Current["NETMASK"] = "" - NetworkInterfaces.Current["BOOTPROTO"] = "none" - # take out PREFIXLEN from old configuration (BNC#735109) - NetworkInterfaces.Current["PREFIXLEN"] = "" - - # remove all aliases (bnc#590167) - aliases = NetworkInterfaces.Current["_aliases"] || {} - aliases.each do |alias_name, alias_ip| - NetworkInterfaces.DeleteAlias(device, alias_name) if alias_ip - end - NetworkInterfaces.Current["_aliases"] = {} - - NetworkInterfaces.Commit - NetworkInterfaces.Add - - NetworkInterfaces.Current = selected_interface - end - - Lan.autoconf_slaves += [device] unless Lan.autoconf_slaves.include? device - - true - end - end -end diff --git a/src/include/network/lan/cmdline.rb b/src/include/network/lan/cmdline.rb index 621feabe4..73e938758 100644 --- a/src/include/network/lan/cmdline.rb +++ b/src/include/network/lan/cmdline.rb @@ -29,6 +29,7 @@ require "shellwords" require "y2network/interface_config_builder" +require "y2network/boot_protocol" module Yast module NetworkLanCmdlineInclude @@ -183,17 +184,14 @@ def ListHandler(options) # Handler for action "add" # @param [Hash{String => String}] options action options def AddHandler(options) - LanItems.AddNew - Lan.Add - LanItems.Items[LanItems.current]["ifcfg"] = options.fetch("name", "") - LanItems.type = options.fetch("type", infered_type(options)) - if LanItems.type.empty? + type = options.fetch("type", infered_type(options)) + if type.empty? Report.Error(_("The device type is mandatory.")) return false end - builder = Y2Network::InterfaceConfigBuilder.for(LanItems.type) - builder.name = LanItems.GetCurrentName() + builder = Y2Network::InterfaceConfigBuilder.for(Y2Network::InterfaceType.from_short_name(type)) + builder.name = options.fetch("name") update_builder_from_options!(builder, options) return false unless validate_config(builder) @@ -212,7 +210,10 @@ def EditHandler(options) return false if validateId(options, config) == false LanItems.current = getItem(options, config) - builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetCurrentType()) + + config = Lan.yast_config.copy + connection_config = config.connections.by_name(LanItems.GetCurrentName) + builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetCurrentType(), config: connection_config) builder.name = LanItems.GetCurrentName() LanItems.SetItem(builder: builder) update_builder_from_options!(builder, options) @@ -271,19 +272,19 @@ def infered_type(options) # @param builder [Y2Network::InterfaceConfigBuilder] # @return [Boolean] true when the options are valid; false otherwise def validate_config(builder) - unless ["none", "static", "dhcp"].include? builder["BOOTPROTO"] + if builder.boot_protocol.nil? Report.Error(_("Impossible value for bootproto.")) return false end - if builder["BOOTPROTO"] == "static" && builder["IPADDR"].empty? + if builder.boot_protocol.name == "static" && builder.ip_address.empty? Report.Error( _("For static configuration, the \"ip\" option is needed.") ) return false end - unless ["auto", "ifplugd", "nfsroot"].include? builder["STARTMODE"] + unless builder.startmode Report.Error(_("Impossible value for startmode.")) return false end @@ -297,26 +298,25 @@ def validate_config(builder) # @param builder [Y2Network::InterfaceConfigBuilder] # @param options [Hash{String => String}] action options def update_builder_from_options!(builder, options) - case builder.type + case builder.type.short_name when "bond" - builder["BOND_SLAVES"] = options.fetch("slaves", "").split(" ") + builder.slaves = options.fetch("slaves", "").split(" ") when "vlan" - builder["ETHERDEVICE"] = options.fetch("ethdevice", "") + builder.etherdevice = options["ethdevice"] when "br" - builder["BRIDGE_PORTS"] = options.fetch("bridge_ports", "") + builder.ports = options["bridge_ports"] end default_bootproto = options.keys.include?("ip") ? "static" : "none" - builder["BOOTPROTO"] = options.fetch("bootproto", default_bootproto) - if builder["BOOTPROTO"] == "static" - builder["IPADDR"] = options.fetch("ip", "") - builder["PREFIX"] = options.fetch("prefix", "") - builder["NETMASK"] = options.fetch("netmask", "255.255.255.0") if builder["PREFIX"].empty? + builder.boot_protocol = options.fetch("bootproto", default_bootproto) + if builder.boot_protocol.name == "static" + builder.ip_address = options.fetch("ip", "") + builder.subnet_prefix = options.fetch("prefix", options.fetch("netmask", "255.255.255.0")) else - builder["IPADDR"] = "" - builder["NETMASK"] = "" + builder.ip_address = "" + builder.subnet_prefix = "" end - builder["STARTMODE"] = options.fetch("startmode", "auto") + builder.startmode = options.fetch("startmode", "auto") end end end diff --git a/src/include/network/lan/complex.rb b/src/include/network/lan/complex.rb index 8d550e2da..56cd57b43 100644 --- a/src/include/network/lan/complex.rb +++ b/src/include/network/lan/complex.rb @@ -1,4 +1,4 @@ -# , :gw6dev encoding: utf-8 +# encoding: utf-8 # *************************************************************************** # @@ -29,6 +29,11 @@ require "y2network/interface_config_builder" require "y2network/sequences/interface" +require "y2network/widgets/interfaces_table" +require "y2network/widgets/interface_description" +require "y2network/widgets/add_interface" +require "y2network/widgets/edit_interface" +require "y2network/widgets/delete_interface" module Yast module NetworkLanComplexInclude @@ -54,7 +59,6 @@ def initialize_network_lan_complex(include_target) Yast.import "LanItems" Yast.include include_target, "network/routines.rb" - Yast.include include_target, "network/summary.rb" Yast.include include_target, "network/lan/help.rb" Yast.include include_target, "network/services/routing.rb" Yast.include include_target, "network/services/dns.rb" @@ -68,36 +72,13 @@ def initialize_network_lan_complex(include_target) def wd return @wd if @wd @wd = { - "MANAGED" => managed_widget, - "IPV6" => ipv6_widget, - "OVERVIEW" => { - "widget" => :custom, - "custom_widget" => VBox( - VWeight( - 2, - Table( - Id(:_hw_items), - Opt(:notify, :immediate), - Header(_("Name"), _("IP Address"), _("Device"), _("Note")) - ) - ), - VWeight(1, RichText(Id(:_hw_sum), "")), - HBox( - *overview_buttons.map { |k, v| PushButton(Id(k), v) }, - HStretch() - ) - ), - "init" => fun_ref(method(:initOverview), "void (string)"), - "handle" => fun_ref( - method(:handleOverview), - "symbol (string, map)" - ), - "help" => Ops.get_string( - @help, - "overview", - "" - ) - } + "MANAGED" => managed_widget, + "IPV6" => ipv6_widget, + interfaces_table.widget_id => interfaces_table.cwm_definition, + interface_description.widget_id => interface_description.cwm_definition, + add_interface.widget_id => add_interface.cwm_definition, + edit_interface.widget_id => edit_interface.cwm_definition, + delete_interface.widget_id => delete_interface.cwm_definition } @wd = Convert.convert( @@ -133,14 +114,38 @@ def tabs_descr }, "overview" => { "header" => _("Overview"), - "contents" => VBox("OVERVIEW"), - "widget_names" => ["OVERVIEW"] + "contents" => VBox( + interfaces_table.widget_id, + interface_description.widget_id, + Left(HBox(add_interface.widget_id, edit_interface.widget_id, delete_interface.widget_id)) + ), + "widget_names" => [interfaces_table.widget_id, interface_description.widget_id, add_interface.widget_id, edit_interface.widget_id, delete_interface.widget_id] } } @tabs_descr = Builtins.union(@tabs_descr, route_td) @tabs_descr = Builtins.union(@tabs_descr, @dns_td) end + def interfaces_table + @interfaces_table ||= Y2Network::Widgets::InterfacesTable.new(interface_description) + end + + def interface_description + @interface_description ||= Y2Network::Widgets::InterfaceDescription.new + end + + def add_interface + @add_interface ||= Y2Network::Widgets::AddInterface.new + end + + def edit_interface + @edit_interface ||= Y2Network::Widgets::EditInterface.new(interfaces_table) + end + + def delete_interface + @delete_interface ||= Y2Network::Widgets::DeleteInterface.new(interfaces_table) + end + # Commit changes to internal structures # @return always `next def Commit(builder:) @@ -196,84 +201,6 @@ def WriteDialog ret ? :next : :abort end - def AddInterface - Lan.Add - LanItems.operation = :add - LanItems.SelectHWMap(Ops.get_map(LanItems.getCurrentItem, "hwinfo", {})) - Ops.set( - LanItems.Items, - [LanItems.current, "ifcfg"], - Ops.get_string(LanItems.getCurrentItem, ["hwinfo", "dev_name"], "") - ) - LanItems.operation = :edit - fw = "" - if LanItems.needFirmwareCurrentItem - fw = LanItems.GetFirmwareForCurrentItem - if fw != "" - if !Package.Installed(fw) && !Package.Available(fw) - Popup.Message( - Builtins.sformat( - _( - "Firmware is needed. Install it from \n" \ - "the add-on CD.\n" \ - "First add the add-on CD to your YaST software repositories then return \n" \ - "to this configuration dialog.\n" - ) - ) - ) - return false - elsif !Builtins.contains(LanItems.Requires, fw) - LanItems.Requires = Builtins.add(LanItems.Requires, fw) - end - else - return Popup.ContinueCancel( - _( - "The device needs a firmware to function properly. Usually, it can be\n" \ - "downloaded from your driver vendor's Web page. \n" \ - "If you have already downloaded and installed the firmware, click\n" \ - "Continue to configure the device. Otherwise click Cancel and\n" \ - "return to this dialog once you have installed the firmware.\n" - ) - ) - end - end - - # this is one of 2 places to install packages :-( - # - kernel modules (InstallKernel): before loaded - # - wlan firmware: here, just because it is copied from modems - # #45960 - if LanItems.Requires != [] && !LanItems.Requires.nil? - return false if PackagesInstall(LanItems.Requires) != :next - if fw == "b43-fwcutter" - if Popup.ContinueCancelHeadline( - _("Installing firmware"), - _( - "For successful firmware installation, the 'install_bcm43xx_firmware' script needs to be executed. Execute it now?" - ) - ) - command = Convert.convert( - SCR.Execute( - path(".target.bash_output"), - "/usr/sbin/install_bcm43xx_firmware" - ), - from: "any", - to: "map " - ) - if Ops.get_integer(command, "exit", -1) != 0 - Popup.ErrorDetails( - _("An error occurred during firmware installation."), - Ops.get_string(command, "stderr", "") - ) - else - Popup.Message("bcm43xx_firmware installed successfully") - end - end - end - end - # TODO: Refresh hwinfo in LanItems - true - end - # Returns true if the device can be used (means handled as normal linux device) # or false otherwise (it is used mainly at s390 based systems where a special # handling is needed to run linux device emulation) @@ -281,221 +208,6 @@ def DeviceReady(devname) !Arch.s390 || s390_DriverLoaded(devname) end - def enableDisableButtons - LanItems.current = Convert.to_integer( - UI.QueryWidget(Id(:_hw_items), :CurrentItem) - ) - - UI.ChangeWidget(:_hw_sum, :Value, LanItems.GetItemDescription) - if !LanItems.IsCurrentConfigured # unconfigured - UI.ChangeWidget(Id(:delete), :Enabled, false) - else - UI.ChangeWidget(Id(:delete), :Enabled, true) - end - - UI.ChangeWidget(Id(:edit), :Enabled, LanItems.enableCurrentEditButton) - - if !Mode.config && Lan.HaveXenBridge # #196479 - # #178848 - overview_buttons.keys.each { |b| UI.ChangeWidget(Id(b), :Enabled, false) } - end - - nil - end - - # Automatically configures slaves when user enslaves them into a bond or bridge device - def UpdateSlaves - current = LanItems.current - master_builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetCurrentType()) - master_builder.name = LanItems.GetCurrentName() - - Lan.autoconf_slaves.each do |dev| - if LanItems.FindAndSelect(dev) - builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetCurrentType()) - builder.name = LanItems.GetCurrentName() - LanItems.SetItem(builder: builder) - else - dev_index = LanItems.FindDeviceIndex(dev) - if dev_index < 0 - log.error("initOverview: invalid slave device name #{dev}") - next - end - LanItems.current = dev_index - - AddInterface() - builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetDeviceType(current)) - builder.name = LanItems.GetCurrentName - end - # clear defaults, some defaults are invalid for slaves and can cause troubles - # in related sysconfig scripts or makes no sence for slaves (e.g. ip configuration). - builder["NETMASK"] = "" - builder["BOOTPROTO"] = "none" - case master_builder.type - when "bond" - LanItems.startmode = "hotplug" - # If there is already a rule based on the bus_id, do not update it. - if LanItems.current_udev_rule.empty? || LanItems.GetItemUdev("KERNELS").empty? - LanItems.update_item_udev_rule!(:bus_id) - end - when "br" - builder["IPADDR"] = "" - else - raise "Adapting slaves for wrong type #{master_builder.type.inspect}" - end - - LanItems.Commit(builder) - end - - # Once the interfaces have been configured we should empty the list to - # avoid configure them again in case that some interface is removed from the - # master. - Lan.autoconf_slaves = [] - - LanItems.current = current - - nil - end - - # Automatically updates interfaces configuration according users input. - # - # Perform automatic configuration based on user input. E.g. when an interface is inserted - # into bond device and persistence based on bus id is required, then some configuration changes - # are required in ifcfg and udev. It used to be needed to do it by hand before. - def AutoUpdateOverview - # TODO: allow disabling. E.g. if bus id based persistency is not requested. - UpdateSlaves() - - nil - end - - def initOverview(_key) - # search for automatic updates - AutoUpdateOverview() - - # update table with device description - term_items = - LanItems.Overview.map do |i| - t = Item(Id(i["id"])) - (i["table_descr"] || []).each { |l| t << l } - t - end - - UI.ChangeWidget(Id(:_hw_items), :Items, term_items) - - if !@shown - disable_unconfigureable_items([:_hw_items, :_hw_sum] + overview_buttons.keys, true) - @shown = true - else - enableDisableButtons - end - - log.info("LanItems #{LanItems.Items}") - - nil - end - - def handleOverview(_key, event) - if !disable_unconfigureable_items([:_hw_items, :_hw_sum] + overview_buttons.keys, false) - enableDisableButtons - end - UI.ChangeWidget(:_hw_sum, :Value, LanItems.GetItemDescription) - - if Ops.get_string(event, "EventReason", "") == "Activated" - case Ops.get_symbol(event, "ID") - when :add - Y2Network::Sequences::Interface.new.add - return :redraw - when :edit - if LanItems.IsCurrentConfigured - - builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetCurrentType()) - builder.name = LanItems.GetCurrentName() - LanItems.SetItem(builder: builder) - - if LanItems.startmode == "managed" - # Continue-Cancel popup - if !Popup.ContinueCancel( - _( - "The interface is currently set to be managed\n" \ - "by the NetworkManager applet.\n" \ - "\n" \ - "If you edit the settings for this interface here,\n" \ - "the interface will no longer be managed by NetworkManager.\n" - ) - ) - # y2r: cannot break from middle of switch - # but in this function return will do - return nil # means cancel - end - - LanItems.startmode = "ifplugd" - end - else - if !AddInterface() - Builtins.y2error("handleOverview: AddInterface failed.") - # y2r: cannot break from middle of switch - # but in this function return will do - return nil - end - - type = LanItems.GetCurrentType() - # s390 does not have type yet set, but it can be read from hwinfo - type = LanItems.Items.dig(LanItems.current, "hwinfo", "type") if !type || type.empty? - builder = Y2Network::InterfaceConfigBuilder.for(type) - builder.name = LanItems.GetCurrentName() - - # FIXME: This is for backward compatibility only - # dhclient needs to set just one dhcp enabled interface to - # DHCLIENT_SET_DEFAULT_ROUTE=yes. Otherwise interface is selected more - # or less randomly (bnc#868187). However, UI is not ready for such change yet. - # As it could easily happen that all interfaces are set to "no" (and - # default route is unrecheable in such case) this explicite setup was - # added. - LanItems.set_default_route = true - - if !DeviceReady( - Ops.get_string( - LanItems.getCurrentItem, - ["hwinfo", "dev_name"], - "" - ) - ) - Y2Network::Sequences::Interface.new.init_s390(builder) - return :redraw - end - end - - Y2Network::Sequences::Interface.new.edit(builder) - return :redraw - - when :delete - # warn user when device to delete has STARTMODE=nfsroot (bnc#433867) - devmap = LanItems.GetCurrentMap - if devmap && devmap["STARTMODE"] == "nfsroot" - if !Popup.YesNoHeadline( - Label.WarningMsg, - _("Device you select has STARTMODE=nfsroot. Really delete?") - ) - # y2r: cannot break from middle of switch - # but in this function return will do - return nil - end - end - - LanItems.DeleteItem - initOverview("") - end - end - if Builtins.size(LanItems.Items) == 0 - UI.ChangeWidget(:_hw_sum, :Value, "") - UI.ChangeWidget(Id(:edit), :Enabled, false) - UI.ChangeWidget(Id(:delete), :Enabled, false) - return nil - end - - nil - end - def ManagedDialog contents = VBox(HSquash(VBox("MANAGED", VSpacing(0.5), "IPV6"))) @@ -535,9 +247,7 @@ def input_done?(ret) true end - def MainDialog(init_tab, builder:) - @builder = builder - + def MainDialog(init_tab) caption = _("Network Settings") widget_descr = { "tab" => CWMTab.CreateWidget( @@ -588,20 +298,6 @@ def MainDialog(init_tab, builder:) break if input_done?(ret) end - @builder = nil - - ret - end - - private - - def overview_buttons - ret = {} - - ret[:add] = Label.AddButton - ret[:edit] = Label.EditButton - ret[:delete] = Label.DeleteButton - ret end end diff --git a/src/include/network/lan/hardware.rb b/src/include/network/lan/hardware.rb index f9d41be4c..c1d71aa5a 100644 --- a/src/include/network/lan/hardware.rb +++ b/src/include/network/lan/hardware.rb @@ -29,6 +29,8 @@ include Yast::UIShortcuts +require "y2network/dialogs/s390_device_activation" + module Yast module NetworkLanHardwareInclude def initialize_network_lan_hardware(include_target) @@ -44,7 +46,6 @@ def initialize_network_lan_hardware(include_target) Yast.import "Popup" Yast.import "Wizard" Yast.import "LanItems" - Yast.include include_target, "network/summary.rb" Yast.include include_target, "network/routines.rb" Yast.include include_target, "network/lan/cards.rb" @@ -73,384 +74,8 @@ def initHelp # S/390 devices configuration dialog # @return dialog result def S390Dialog(builder:) - # S/390 dialog caption - caption = _("S/390 Network Card Configuration") - - drvtype = DriverType(builder.type) - - helptext = "" - contents = Empty() - - if Builtins.contains(["qeth", "hsi"], builder.type) - # CHANIDS - tmp_list = Builtins.splitstring(LanItems.qeth_chanids, " ") - chanids_map = { - "read" => Ops.get(tmp_list, 0, ""), - "write" => Ops.get(tmp_list, 1, ""), - "control" => Ops.get(tmp_list, 2, "") - } - contents = HBox( - HSpacing(6), - # Frame label - Frame( - _("S/390 Device Settings"), - HBox( - HSpacing(2), - VBox( - VSpacing(1), - HBox( - # TextEntry label - InputField( - Id(:qeth_portname), - Opt(:hstretch), - _("&Port Name"), - LanItems.qeth_portname - ), - ComboBox( - Id(:qeth_portnumber), - _("Port Number"), - [Item(Id("0"), "0", true), Item(Id("1"), "1")] - ) - ), - VSpacing(1), - # TextEntry label - InputField( - Id(:qeth_options), - Opt(:hstretch), - Label.Options, - LanItems.qeth_options - ), - VSpacing(1), - # CheckBox label - Left(CheckBox(Id(:ipa_takeover), _("&Enable IPA Takeover"))), - VSpacing(1), - # CheckBox label - Left( - CheckBox( - Id(:qeth_layer2), - Opt(:notify), - _("Enable &Layer 2 Support") - ) - ), - # TextEntry label - InputField( - Id(:qeth_macaddress), - Opt(:hstretch), - _("Layer2 &MAC Address"), - LanItems.qeth_macaddress - ), - VSpacing(1), - HBox( - InputField( - Id(:qeth_chan_read), - Opt(:hstretch), - _("Read Channel"), - Ops.get_string(chanids_map, "read", "") - ), - InputField( - Id(:qeth_chan_write), - Opt(:hstretch), - _("Write Channel"), - Ops.get_string(chanids_map, "write", "") - ), - InputField( - Id(:qeth_chan_control), - Opt(:hstretch), - _("Control Channel"), - Ops.get_string(chanids_map, "control", "") - ) - ) - ), - HSpacing(2) - ) - ), - HSpacing(6) - ) - # S/390 dialog help: QETH Port name - helptext = _( - "

Enter the Port Name for this interface (case-sensitive).

" - ) + - # S/390 dialog help: QETH Options - _( - "

Enter any additional Options for this interface (separated by spaces).

" - ) + - _( - "

Select Enable IPA Takeover if IP address takeover should be enabled for this interface.

" - ) + - _( - "

Select Enable Layer 2 Support if this card has been configured with layer 2 support.

" - ) + - _( - "

Enter the Layer 2 MAC Address if this card has been configured with layer 2 support.

" - ) - end - - if drvtype == "lcs" - tmp_list = Builtins.splitstring(LanItems.qeth_chanids, " ") - chanids_map = { - "read" => Ops.get(tmp_list, 0, ""), - "write" => Ops.get(tmp_list, 1, "") - } - contents = HBox( - HSpacing(6), - # Frame label - Frame( - _("S/390 Device Settings"), - HBox( - HSpacing(2), - VBox( - VSpacing(1), - # TextEntry label - InputField( - Id(:chan_mode), - Opt(:hstretch), - _("&Port Number"), - LanItems.chan_mode - ), - VSpacing(1), - # TextEntry label - InputField( - Id(:lcs_timeout), - Opt(:hstretch), - _("&LANCMD Time-Out"), - LanItems.lcs_timeout - ), - VSpacing(1), - HBox( - InputField( - Id(:qeth_chan_read), - Opt(:hstretch), - _("Read Channel"), - Ops.get_string(chanids_map, "read", "") - ), - InputField( - Id(:qeth_chan_write), - Opt(:hstretch), - _("Write Channel"), - Ops.get_string(chanids_map, "write", "") - ) - ) - ), - HSpacing(2) - ) - ), - HSpacing(6) - ) - # S/390 dialog help: LCS - helptext = _("

Choose the Port Number for this interface.

") + - _("

Specify the LANCMD Time-Out for this interface.

") - end - - ctcitems = [ - # ComboBox item: CTC device protocol - Item(Id("0"), _("Compatibility Mode")), - # ComboBox item: CTC device protocol - Item(Id("1"), _("Extended Mode")), - # ComboBox item: CTC device protocol - Item(Id("2"), _("CTC-Based tty (Linux to Linux Connections)")), - # ComboBox item: CTC device protocol - Item(Id("3"), _("Compatibility Mode with OS/390 and z/OS")) - ] - - if drvtype == "ctc" - tmp_list = Builtins.splitstring(LanItems.qeth_chanids, " ") - chanids_map = { - "read" => Ops.get(tmp_list, 0, ""), - "write" => Ops.get(tmp_list, 1, "") - } - contents = HBox( - HSpacing(6), - # Frame label - Frame( - _("S/390 Device Settings"), - HBox( - HSpacing(2), - VBox( - VSpacing(1), - # TextEntry label - ComboBox(Id(:chan_mode), _("&Protocol"), ctcitems), - VSpacing(1), - HBox( - InputField( - Id(:qeth_chan_read), - Opt(:hstretch), - _("Read Channel"), - Ops.get_string(chanids_map, "read", "") - ), - InputField( - Id(:qeth_chan_write), - Opt(:hstretch), - _("Write Channel"), - Ops.get_string(chanids_map, "write", "") - ) - ) - ), - HSpacing(2) - ) - ), - HSpacing(6) - ) - # S/390 dialog help: CTC - helptext = _("

Choose the Protocol for this interface.

") - end - - if drvtype == "iucv" - contents = HBox( - HSpacing(6), - # Frame label - Frame( - _("S/390 Device Settings"), - HBox( - HSpacing(2), - VBox( - VSpacing(1), - # TextEntry label, #42789 - InputField( - Id(:iucv_user), - Opt(:hstretch), - _("&Peer Name"), - LanItems.iucv_user - ), - VSpacing(1) - ), - HSpacing(2) - ) - ), - HSpacing(6) - ) - # S/390 dialog help: IUCV, #42789 - helptext = _( - "

Enter the name of the IUCV peer,\nfor example, the z/VM user name with which to connect (case-sensitive).

\n" - ) - end - - Wizard.SetContentsButtons( - caption, - contents, - helptext, - Label.BackButton, - Label.NextButton - ) - - if drvtype == "ctc" - UI.ChangeWidget(Id(:chan_mode), :Value, LanItems.chan_mode) - end - - if drvtype == "lcs" - UI.ChangeWidget(Id(:chan_mode), :Value, LanItems.chan_mode) - UI.ChangeWidget(Id(:lcs_timeout), :Value, LanItems.lcs_timeout) - end - - if drvtype == "qeth" - UI.ChangeWidget(Id(:ipa_takeover), :Value, LanItems.ipa_takeover) - UI.ChangeWidget(Id(:qeth_layer2), :Value, LanItems.qeth_layer2) - UI.ChangeWidget( - Id(:qeth_macaddress), - :ValidChars, - ":0123456789abcdefABCDEF" - ) - end - - id = case builder.type - when "hsi" then :qeth_options - when "qeth" then :qeth_portname - when "iucv" then :iucv_user - else :chan_mode - end - UI.SetFocus(Id(id)) - - ret = nil - loop do - if drvtype == "qeth" - mac_enabled = Convert.to_boolean( - UI.QueryWidget(Id(:qeth_layer2), :Value) - ) - UI.ChangeWidget(Id(:qeth_macaddress), :Enabled, mac_enabled) - end - - ret = UI.UserInput - - case ret - when :abort, :cancel - ReallyAbort() ? break : next - when :back - break - when :next - if builder.type == "iucv" - LanItems.device = Ops.add( - "id-", - Convert.to_string(UI.QueryWidget(Id(:iucv_user), :Value)) - ) - LanItems.iucv_user = Convert.to_string( - UI.QueryWidget(Id(:iucv_user), :Value) - ) - end - - if builder.type == "ctc" - LanItems.chan_mode = Convert.to_string( - UI.QueryWidget(Id(:chan_mode), :Value) - ) - end - if builder.type == "lcs" - LanItems.lcs_timeout = Convert.to_string( - UI.QueryWidget(Id(:lcs_timeout), :Value) - ) - LanItems.chan_mode = Convert.to_string( - UI.QueryWidget(Id(:chan_mode), :Value) - ) - end - if builder.type == "qeth" || builder.type == "hsi" - LanItems.qeth_options = Convert.to_string( - UI.QueryWidget(Id(:qeth_options), :Value) - ) - LanItems.ipa_takeover = Convert.to_boolean( - UI.QueryWidget(Id(:ipa_takeover), :Value) - ) - LanItems.qeth_layer2 = Convert.to_boolean( - UI.QueryWidget(Id(:qeth_layer2), :Value) - ) - LanItems.qeth_macaddress = Convert.to_string( - UI.QueryWidget(Id(:qeth_macaddress), :Value) - ) - LanItems.qeth_portnumber = Convert.to_string( - UI.QueryWidget(Id(:qeth_portnumber), :Value) - ) - LanItems.qeth_portname = Convert.to_string( - UI.QueryWidget(Id(:qeth_portname), :Value) - ) - end - read = Convert.to_string(UI.QueryWidget(Id(:qeth_chan_read), :Value)) - write = Convert.to_string( - UI.QueryWidget(Id(:qeth_chan_write), :Value) - ) - control = Convert.to_string( - UI.QueryWidget(Id(:qeth_chan_control), :Value) - ) - control = "" if control.nil? - LanItems.qeth_chanids = String.CutBlanks( - Builtins.sformat("%1 %2 %3", read, write, control) - ) - if LanItems.createS390Device - builder.name = LanItems.GetCurrentName - else - Popup.Error( - _( - "An error occurred while creating device.\nSee YaST log for details." - ) - ) - ret = nil - next - end - break - when :qeth_layer2 - next - else - Builtins.y2error("Unexpected return code: %1", ret) - next - end - end - - deep_copy(ret) + dialog = Y2Network::Dialogs::S390DeviceActivation.for(builder) + dialog ? dialog.run : :abort end end end diff --git a/src/include/network/lan/s390.rb b/src/include/network/lan/s390.rb index 7a3be4645..2c90ebb1f 100644 --- a/src/include/network/lan/s390.rb +++ b/src/include/network/lan/s390.rb @@ -1,12 +1,24 @@ -# encoding: utf-8 - -# File: include/network/lan/s390.ycp -# Package: Network configuration -# Summary: Network card adresss configuration dialogs -# Authors: Michal Filka +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. # -# Functions for accessing and handling s390 specific needs. +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + module Yast + # Functions for accessing and handling s390 specific needs. module NetworkLanS390Include SYS_DIR = "/sys/class/net".freeze @@ -46,7 +58,6 @@ def s390_ReadQethAttribute(devname, attrib) # # Currently loaded attributes are: # QETH_LAYER2 yes/no string. - # QETH_PORTNAME portname or empty string # QETH_PORTNUMBER portnumber or empty string # QETH_CHANIDS read/write/control channel ids separated by space (compatibility requirement) # @@ -62,9 +73,6 @@ def s390_ReadQethConfig(devname) qeth_layer2 = s390_ReadQethAttribute(devname, "layer2") == "1" ? "yes" : "no" result = Builtins.add(result, "QETH_LAYER2", qeth_layer2) - qeth_portname = s390_ReadQethAttribute(devname, "portname") - result = Builtins.add(result, "QETH_PORTNAME", qeth_portname) - qeth_portno = s390_ReadQethAttribute(devname, "portno") result = Builtins.add(result, "QETH_PORTNUMBER", qeth_portno) diff --git a/src/include/network/lan/udev.rb b/src/include/network/lan/udev.rb deleted file mode 100644 index 820cbd258..000000000 --- a/src/include/network/lan/udev.rb +++ /dev/null @@ -1,110 +0,0 @@ -# encoding: utf-8 - -# File: include/network/lan/udev.ycp -# Package: Network configuration -# Summary: udev helpers -# Authors: Michal Filka -# -# Functions for handling udev rules - -module Yast - module NetworkLanUdevInclude - # Creates default udev rule for given NIC. - # - # Udev rule is based on device's MAC. - # - # @return [Array] an udev rule - def GetDefaultUdevRule(dev_name, dev_mac) - [ - "SUBSYSTEM==\"net\"", - "ACTION==\"add\"", - "DRIVERS==\"?*\"", - "ATTR{address}==\"#{dev_mac}\"", - "ATTR{type}==\"1\"", - "NAME=\"#{dev_name}\"" - ] - end - - # Updates existing key in a rule to new value. - # Modifies rule and returns it. - # If key is not found, rule is unchanged. - def update_udev_rule_key(rule, key, value) - return rule if rule.nil? || rule.empty? - - raise ArgumentError if key.nil? - raise ArgumentError if value.nil? - - i = rule.find_index { |k| k =~ /^#{key}/ } - - if i - rule[i] = rule[i].gsub(/#{key}={1,2}"([^"]*)"/) do |m| - m.gsub(Regexp.last_match(1), value) - end - end - - rule - end - - # Returns a value of the particular key in the rule - # - # @param rule [array] an udev rule represented as a list of strings - # @param key [string] a key name which is asked for value - # @return [string] value corresponding to the key or empty string - def udev_key_value(rule, key) - raise ArgumentError, "Rule must not be nil when querying a key value" if rule.nil? - - rule.each do |tuple| - # note that when using =~ then named capture groups (?...) currently - # cannot be used together with interpolation (#{}) - # see http://stackoverflow.com/questions/15890729/why-does-capturing-named-groups-in-ruby-result-in-undefined-local-variable-or-m - matches = tuple.match(/#{key}={1,2}"?(?[^[:space:]"]*)/) - return matches[:value] if matches - end - - "" - end - - # Writes new persistent udev net rules and tells udevd to update its configuration - def write_update_udevd(udev_rules) - SCR.Write(path(".udev_persistent.rules"), udev_rules) - SCR.Write(path(".udev_persistent.nil"), []) - - update_udevd - end - - # Tells udevd to reload and update its configuration - # - # @return [boolean] false when new configuration cannot be activated - def update_udevd - SCR.Execute(path(".target.bash"), "/usr/bin/udevadm control --reload") - - # When configuring a new s390 card, we neglect to fill - # its Items[i, "udev", "net"], causing jumbled names (bnc#721520) - # The udev trigger will make udev write the persistent names - # (which it already has done, but we have overwritten them now). - ret = SCR.Execute( - path(".target.bash"), - "/usr/bin/udevadm trigger --subsystem-match=net --action=add" - ) - ret.zero? - end - - # Removes (key,operator,value) tripplet from given udev rule. - def RemoveKeyFromUdevRule(rule, key) - pattern = /#{key}={1,2}\S*/ - - rule.delete_if { |tripplet| tripplet =~ pattern } - end - - # Adds (key, operator, value) tripplet into given udev rule - # - # Tripplet is given as a string in form KEY="VALUE" or - # MATCHKEY=="MATCHVALUE" - def AddToUdevRule(rule, tripplet) - return rule unless tripplet =~ /.+={1,2}\".*\"/ - - rule ||= [] - rule + [tripplet] - end - end -end diff --git a/src/include/network/lan/wireless.rb b/src/include/network/lan/wireless.rb deleted file mode 100644 index 1d610bb63..000000000 --- a/src/include/network/lan/wireless.rb +++ /dev/null @@ -1,1420 +0,0 @@ -# encoding: utf-8 - -# *************************************************************************** -# -# Copyright (c) 2012 Novell, Inc. -# All Rights Reserved. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of version 2 of the GNU General Public License as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, -# you may find current contact information at www.novell.com -# -# ************************************************************************** -# File: include/network/lan/wizards.ycp -# Package: Network configuration -# Summary: Network cards configuration wizards -# Authors: Michal Svec -# - -require "shellwords" - -module Yast - module NetworkLanWirelessInclude - def initialize_network_lan_wireless(include_target) - textdomain "network" - - Yast.import "CWM" - Yast.import "FileUtils" - Yast.import "Label" - Yast.import "Lan" - Yast.import "LanItems" - Yast.import "Map" - Yast.import "Message" - Yast.import "Popup" - Yast.import "String" - Yast.import "Wizard" - - Yast.include include_target, "network/routines.rb" - Yast.include include_target, "network/lan/help.rb" - - # key input type buttons - @type_w = RadioButtonGroup( - Id(:type_g), - VBox( - # Translators: input type for a wireless key - # radio button group label - Left(Label(_("Key Input Type"))), - Left( - HBox( - # Translators: input type for a wireless key - RadioButton(Id("passphrase"), _("&Passphrase")), - HSpacing(1), - # Translators: input type for a wireless key - RadioButton(Id("ascii"), _("&ASCII")), - HSpacing(1), - # Translators: input type for a wireless key - # (Hexadecimal) - RadioButton(Id("hex"), _("&Hexadecimal")) - ) - ) - ) - ) - - @wpa_eap_widget_descr = { - "WPA_EAP_MODE" => { - "widget" => :combobox, - # combo box label - "label" => _("EAP &Mode"), - "opt" => [:notify], - "items" => [ - # combo box item, one of WPA EAP modes - ["PEAP", _("PEAP")], - # combo box item, one of WPA EAP modes - ["TLS", _("TLS")], - # combo box item, one of WPA EAP modes - ["TTLS", _("TTLS")] - ], - "help" => _( - "

WPA-EAP uses a RADIUS server to authenticate users. There\n" \ - "are different methods in EAP to connect to the server and\n" \ - "perform the authentication, namely TLS, TTLS, and PEAP.

\n" - ), - "init" => fun_ref(method(:InitEapMode), "void (string)"), - "handle" => fun_ref(method(:HandleEapMode), "symbol (string, map)") - }, - # the four WPA_EAP_* widgets come together, so the helps are - # dipersed a bit - "WPA_EAP_IDENTITY" => { - "widget" => :textentry, - # text entry label - "label" => _("&Identity"), - "opt" => [], - "help" => _( - "

For TTLS and PEAP, enter your Identity\n" \ - "and Password as configured on the server.\n" \ - "If you have special requirements to set the username used as\n" \ - "Anonymous Identity, you may set it here. This is usually not needed.

\n" - ) - }, - "WPA_EAP_ANONID" => { - "widget" => :textentry, - # text entry label - "label" => _("&Anonymous Identity"), - "opt" => [], - "help" => "" - }, - "WPA_EAP_PASSWORD" => { - "widget" => :password, - # or password? - # text entry label - "label" => _("&Password"), - "opt" => [], - "help" => "" - }, - "WPA_EAP_CLIENT_CERT" => { - "widget" => :textentry, - # text entry label - "label" => _("&Client Certificate"), - "opt" => [], - "help" => _( - "

TLS uses a Client Certificate instead of a username and\n" \ - "password combination for authentication. It uses a public and private key pair\n" \ - "to encrypt negotiation communication, therefore you will additionally need\n" \ - "a Client Key file that contains your private key and\n" \ - "the appropriate Client Key Password for that file.

\n" - ), - "validate_type" => :function, - "validate_function" => fun_ref( - method(:ValidateFileExists), - "boolean (string, map)" - ) - }, - "WPA_EAP_CLIENT_KEY" => { - "widget" => :textentry, - # text entry label - "label" => _("Client &Key"), - "opt" => [], - "help" => "", - "validate_type" => :function, - "validate_function" => fun_ref( - method(:ValidateFileExists), - "boolean (string, map)" - ) - }, - "WPA_EAP_CLIENT_KEY_PASSWORD" => { - "widget" => :textentry, - # or password? - # text entry label - "label" => _( - "Client Key Pass&word" - ), - "opt" => [], - "help" => "" - }, - "WPA_EAP_CA_CERT" => { - "widget" => :textentry, - # text entry label - # aka certificate of the CA (certification authority) - "label" => _( - "&Server Certificate" - ), - "opt" => [], - "help" => _( - "

To increase security, it is recommended to configure\n" \ - "a Server Certificate. It is used\n" \ - "to validate the server's authenticity.

\n" - ), - "validate_type" => :function, - "validate_function" => fun_ref( - method(:ValidateCaCertExists), - "boolean (string, map)" - ) - }, - "WPA_EAP_CLIENT_CERT_BROWSE" => { - "widget" => :push_button, - "label" => "...", - "opt" => [:autoShortcut], - "help" => "", - "init" => fun_ref(CWM.method(:InitNull), "void (string)"), - "store" => fun_ref(CWM.method(:StoreNull), "void (string, map)"), - "handle" => fun_ref(method(:HandleFileBrowse), "symbol (string, map)") - }, - "WPA_EAP_CLIENT_KEY_BROWSE" => { - "widget" => :push_button, - "label" => "...", - "opt" => [:autoShortcut], - "help" => "", - "init" => fun_ref(CWM.method(:InitNull), "void (string)"), - "store" => fun_ref(CWM.method(:StoreNull), "void (string, map)"), - "handle" => fun_ref(method(:HandleFileBrowse), "symbol (string, map)") - }, - "WPA_EAP_CA_CERT_BROWSE" => { - "widget" => :push_button, - "label" => "...", - "opt" => [:autoShortcut], - "help" => "", - "init" => fun_ref(CWM.method(:InitNull), "void (string)"), - "store" => fun_ref(CWM.method(:StoreNull), "void (string, map)"), - "handle" => fun_ref(method(:HandleFileBrowse), "symbol (string, map)") - }, - "DETAILS_B" => { - "widget" => :push_button, - # push button label - "label" => _("&Details"), - "opt" => [], - "help" => "", - "init" => fun_ref(CWM.method(:InitNull), "void (string)"), - "store" => fun_ref(CWM.method(:StoreNull), "void (string, map)"), - "handle" => fun_ref(method(:HandleDetails), "symbol (string, map)") - }, - "WPA_EAP_DUMMY" => { - "widget" => :empty, - "help" => _( - "If you do not know your ID and password or you do not have\nany certificate or key files, contact your system administrator.\n" - ), - "init" => fun_ref(CWM.method(:InitNull), "void (string)"), - "store" => fun_ref( - CWM.method(:StoreNull), - "void (string, map)" - ), - "validate_type" => :function, - "validate_function" => fun_ref( - method(:ValidateWpaEap), - "boolean (string, map)" - ) - }, - # Details dialog - "WPA_EAP_AUTH" => { - "widget" => :combobox, - # combo box label - "label" => _("&Authentication Method"), - "help" => _( - "

Here you can configure the inner authentication (also known as phase 2)\n" \ - "method. By default, all methods are allowed. If you want to restrict the\n" \ - "allowed methods or in case you have encountered difficulties regarding\n" \ - "authentication, choose your inner authentication method.

\n" - ) - }, - "WPA_EAP_PEAP_VERSION" => { - "widget" => :radio_buttons, - # radio button group label - "label" => _("&PEAP Version"), - "help" => _( - "

If you are using PEAP, you can also force the use of a specific PEAP\nimplementation (version 0 or 1). Normally this should not be necessary.

\n" - ), - "items" => [ - # radio button: any version of PEAP - ["", _("&Any")], - ["0", "&0"], - ["1", "&1"] - ], - "init" => fun_ref(method(:InitPeapVersion), "void (string)") - } - } - end - - # Compose a typed key into a single-string representation - # @param [String] type "passphrase", "ascii", "hex" - # @param [String] key - # @return prefixed key - def ComposeWepKey(type, key) - # prefixes for key types - prefix = { "ascii" => "s:", "passphrase" => "h:", "hex" => "" } - - # empty key - don't prepend a type (#40431) - key == "" ? "" : Ops.add(Ops.get(prefix, type, "?:"), key) - end - - def ParseWepKey(tkey) - if Builtins.substring(tkey, 0, 2) == "s:" - { "key" => Builtins.substring(tkey, 2), "type" => "ascii" } - elsif Builtins.substring(tkey, 0, 2) == "h:" - { "key" => Builtins.substring(tkey, 2), "type" => "passphrase" } - # make passphrase the default key type, #40431 - elsif tkey == "" - { "key" => tkey, "type" => "passphrase" } - else - { "key" => tkey, "type" => "hex" } - end - end - - # Is the entered key valid? - # TODO: check according to the selected key length - # (or better adjust the length?) - # @param [Array] lengths allowed real key lengths - def CheckWirelessKey(key, lengths) - lengths = deep_copy(lengths) - return false if key.nil? - - if Builtins.regexpmatch(key, "^s:.{5}$") && Builtins.contains(lengths, 40) || - Builtins.regexpmatch(key, "^s:.{6,13}$") && - Builtins.contains(lengths, 104) - return true - end - - if Builtins.regexpmatch(key, "^[0-9A-Fa-f-]*$") - key = Builtins.deletechars(key, "-") - actual_bits = Ops.multiply(Builtins.size(key), 4) # 4 bits per hex digit - return true if Builtins.contains(lengths, actual_bits) - Builtins.y2milestone( - "Key length: actual %1, allowed %2", - actual_bits, - lengths - ) - end - - return true if Builtins.regexpmatch(key, "^h:") - - false - end - - # Takes the WEP items from the list and returns the key lengths as integers - # Like the input, uses the real length which is 24 bits shorter - # than the marketing one. - # If the input is nil, return the default set of key lengths. - # @param [Array] enc_modes a subset of WEP40, WEP104, WEP128, WEP232, TKIP, CCMP - # @return [Array] of real key lengths - def ParseKeyLengths(enc_modes) - enc_modes = deep_copy(enc_modes) - return [40, 104] if enc_modes.nil? - - lengths = [] - Builtins.foreach(enc_modes) do |em| - if Builtins.substring(em, 0, 3) == "WEP" - lengths = Builtins.add( - lengths, - Builtins.tointeger(Builtins.substring(em, 3)) - ) - end - end - - Builtins.y2warning("empty set of key lengths") if lengths == [] - deep_copy(lengths) - end - - # Make a list of ComboBox items for authentication mode. - # We must translate WPA-PSK: it is "wpa-psk" in hwinfo but "psk" in syconfig - # (#74496). - # @param [Array] authmodes allowed modes as returned by hwinfo. nil == don't know. - # @return combo box items - def AuthModeItems(authmodes) - authmodes = deep_copy(authmodes) - names = { - # Wireless authentication modes: - # ComboBox item - "no-encryption" => _( - "No Encryption" - ), - # ComboBox item - "open" => _("WEP - Open"), - # ComboBox item - "sharedkey" => _("WEP - Shared Key"), - # ComboBox item - # Ask me what it means, I don't know yet - "wpa-psk" => _( - "WPA-PSK (WPA version 1 or 2)" - ), - # ComboBox item - "wpa-eap" => _("WPA-EAP (WPA version 1 or 2)") - } - ids = { "wpa-psk" => "psk", "wpa-eap" => "eap" } - authmodes = if authmodes.nil? - Convert.convert( - Map.Keys(names), - from: "list", - to: "list " - ) - else - # keep only those that we know how to handle - Builtins.filter(authmodes) do |am| - Builtins.haskey(names, am) - end - end - Builtins.maplist(authmodes) do |am| - Item(Id(Ops.get(ids, am, am)), Ops.get(names, am, am)) - end - end - - # Wireless devices configuration dialog - # @return dialog result - def WirelessDialog - # Wireless dialog caption - caption = _("Wireless Network Card Configuration") - mode = LanItems.wl_mode - essid = LanItems.wl_essid - authmode = LanItems.wl_auth_mode - # wpa or wep? - authmode_wpa = authmode == "psk" || authmode == "eap" # shortcut - key = nil - type = nil - if authmode == "psk" - key = LanItems.wl_wpa_psk - type = Builtins.size(key) == 64 ? "hex" : "passphrase" - elsif authmode != "eap" - wkey = ParseWepKey( - Ops.get(LanItems.wl_key, LanItems.wl_default_key, "") - ) - key = Ops.get(wkey, "key", "") - type = Ops.get(wkey, "type", "") - else - key = "" # and type is not used - end - - key_lengths = ParseKeyLengths(LanItems.wl_enc_modes) - - # Wireless dialog contents - contents = HBox( - HSpacing(4), - VBox( - VSpacing(0.5), - # Frame label - Frame( - _("Wireless Device Settings"), - HBox( - HSpacing(2), - VBox( - VSpacing(0.5), - # ComboBox label - ComboBox( - Id(:mode), - Opt(:hstretch), - _("O&perating Mode"), - [ - # ComboBox item - Item(Id("Ad-hoc"), _("Ad-Hoc"), mode == "Ad-hoc"), - # ComboBox item - Item(Id("Managed"), _("Managed"), mode == "Managed"), - # ComboBox item - Item(Id("Master"), _("Master"), mode == "Master") - ] - ), - VSpacing(0.2), - # Text entry label - HBox( - ComboBox( - Id(:essid), - Opt(:editable), - _("Ne&twork Name (ESSID)"), - [essid] - ), - PushButton(Id(:scan_for_networks), _("Scan Network")) - ), - VSpacing(0.2), - ComboBox( - Id(:authmode), - Opt(:hstretch, :notify), - # ComboBox label - _("&Authentication Mode"), - AuthModeItems(LanItems.wl_auth_modes) - ), - VSpacing(0.2), - @type_w, - VSpacing(0.2), - # Text entry label - Password(Id(:key), _("&Encryption Key"), key), - VSpacing(0.5) - ), - HSpacing(2) - ) - ), - VSpacing(0.5), - HBox( - # PushButton label - PushButton(Id(:expert), _("E&xpert Settings")), - HSpacing(0.5), - # PushButton label, keys for WEP encryption - PushButton(Id(:keys), _("&WEP Keys")) - ), - VSpacing(0.5) - ), - HSpacing(4) - ) - Wizard.SetContentsButtons( - caption, - contents, - Builtins.sformat( - "%1%2%3", - Ops.get_string(@help, "wireless", ""), - Ops.get_string(@help, "wep_key", ""), - Ops.get_string(@help, "wpa", "") - ), - Label.BackButton, - Label.NextButton - ) - - # - # Situation with (E)SSID is not as clear as it should be. - # According IEEE 802.11-2007 it should be between 0 and 32 octets (sometimes including trailing \0). - # - # However, vendors can have additional limits. - # According http://www.cisco.com/web/techdoc/wireless/access_points/online_help/eag/123-04.JA/1400br/h_ap_sec_ap-client-security.html - # characters ?, ", $, [, \, ], + are disallowed. Moreover !, #, : shouldn't be at beginning of the id. - # As this is only part of vendor specification and an APs which breaks that rule (see http://www.wirelessforums.org/alt-internet-wireless/ssid-33892.html) - # this is ignored. - # - # Eventually, as a note to bnc#118157 and bnc#750325 an ' (apostrophe) is valid character in ESSID. - # - UI.ChangeWidget(Id(:essid), :ValidChars, String.CPrint) - - UI.ChangeWidget(Id(:authmode), :Value, authmode) - UI.ChangeWidget(Id(:type_g), :CurrentButton, type) if authmode != "eap" - - ckey = nil - ret = nil - loop do - UI.ChangeWidget(Id(:mode), :Value, "Managed") if authmode_wpa - - UI.ChangeWidget( - Id(:type_g), - :Enabled, - authmode != "no-encryption" && authmode != "eap" - ) - UI.ChangeWidget( - Id(:key), - :Enabled, - authmode != "no-encryption" && authmode != "eap" - ) - UI.ChangeWidget( - Id(:keys), - :Enabled, - authmode != "no-encryption" && !authmode_wpa - ) - UI.ChangeWidget( - Id("ascii"), - :Enabled, - authmode != "no-encryption" && authmode != "psk" - ) - - ret = UI.UserInput - - authmode = Convert.to_string(UI.QueryWidget(Id(:authmode), :Value)) - authmode_wpa = authmode == "psk" || authmode == "eap" # shortcut - - case ret - when :abort, :cancel - if ReallyAbort() - LanItems.Rollback() - break - end - - next - when :back - break - when :next, :expert, :keys - mode = Convert.to_string(UI.QueryWidget(Id(:mode), :Value)) - # WPA-PSK and WPA-EAP are only allowed for Managed mode - if authmode_wpa && mode != "Managed" - UI.SetFocus(Id(:mode)) - # Popup text - Popup.Error( - _( - "WPA authentication mode is only possible in managed operating mode." - ) - ) - next - end - essid = Convert.to_string(UI.QueryWidget(Id(:essid), :Value)) - if essid == "" && (mode != "Managed" || authmode_wpa) - UI.SetFocus(Id(:essid)) - # Popup text - # modes: combination of operation and authentication - Popup.Error(_("Specify the network name for this mode.")) - next - end - if Ops.greater_than(Builtins.size(essid), 32) - UI.SetFocus(Id(:essid)) - # Popup text - Popup.Error( - _("The network name must be shorter than 32 characters.") - ) - next - end - - if authmode != "no-encryption" && authmode != "eap" - key = Convert.to_string(UI.QueryWidget(Id(:key), :Value)) - else - key = "" - Ops.set(LanItems.wl_key, LanItems.wl_default_key, "") - LanItems.wl_wpa_psk = "" - end - type = Convert.to_string(UI.QueryWidget(Id(:type_g), :CurrentButton)) - if authmode == "psk" - sz = Builtins.size(key) - if type == "passphrase" && - (Ops.less_than(sz, 8) || Ops.greater_than(sz, 63)) - UI.SetFocus(Id(:key)) - # Error popup - Popup.Error( - _( - "The passphrase must have between 8 and 63 characters (inclusively)." - ) - ) - next - elsif type == "hex" && - !Builtins.regexpmatch(key, "^[0-9A-Fa-f]{64}$") - UI.SetFocus(Id(:key)) - # Error popup - Popup.Error( - Builtins.sformat( - _("The key must have %1 hexadecimal digits."), - 64 - ) - ) - next - end - elsif !authmode_wpa - ckey = ComposeWepKey(type, key) - if ckey != "" - if !CheckWirelessKey(ckey, key_lengths) - UI.SetFocus(Id(:key)) - # Popup text - Popup.Error(_("The encryption key is invalid.")) - next - end - else - UI.SetFocus(Id(:key)) - if authmode == "sharedkey" # error - # Popup text - Popup.Error( - _( - "The encryption key must be specified for this authentication mode." - ) - ) - next - elsif ret != :keys # warning only - # Popup text - pop = _( - "Using no encryption is a security risk.\nReally continue?\n" - ) - next if !Popup.YesNo(pop) - end - end - end - break - when :scan_for_networks - if scan_supported? - command = Builtins.sformat( - "/usr/sbin/ip link set %1 up && /usr/sbin/iwlist %1 scan | " \ - "/usr/bin/grep ESSID | /usr/bin/cut -d':' -f2 | " \ - "/usr/bin/cut -d'\"' -f2 | /usr/bin/sort -u", - Ops.get_string(LanItems.Items, [LanItems.current, "ifcfg"], "").shellescape - ) - output = Convert.convert( - SCR.Execute(path(".target.bash_output"), command), - from: "any", - to: "map " - ) - - if Ops.get_integer(output, "exit", -1) == 0 - networks = Builtins.splitstring( - Ops.get_string(output, "stdout", ""), - "\n" - ) - Builtins.y2milestone("Found networks : %1", networks) - UI.ChangeWidget(:essid, :Items, networks) - end - end - when :authmode - # do nothing - else - Builtins.y2error("Unexpected return code: %1", ret) - next - end - end - - if ret == :next || ret == :expert || ret == :keys - LanItems.wl_essid = Convert.to_string( - UI.QueryWidget(Id(:essid), :Value) - ) - LanItems.wl_mode = mode - LanItems.wl_auth_mode = authmode - if authmode == "psk" - LanItems.wl_wpa_psk = key - Ops.set(LanItems.wl_key, LanItems.wl_default_key, "") - elsif !authmode_wpa && authmode != "no-encryption" - Ops.set(LanItems.wl_key, LanItems.wl_default_key, ckey) - LanItems.wl_wpa_psk = "" - end - end - - if ret == :next && authmode == "eap" - ret = :eap # continue by the WPA-EAP dialog - end - deep_copy(ret) - end - - # Wireless expert configuration dialog - # @return dialog result - def WirelessExpertDialog - # Wireless expert dialog caption - caption = _("Wireless Expert Settings") - - # Wireless expert dialog help 1/5 - helptext = _( - "

Here, set additional configuration parameters\n(rarely needed).

" - ) + - # Wireless expert dialog help 2/5 - _( - "

To use your wireless LAN card in master or ad-hoc mode,\n" \ - "set the Channel the card should use here. This is not needed\n" \ - "for managed mode--the card will hop through the channels searching for access\n" \ - "points in that case.

\n" - ) + - # Wireless expert dialog help 3/5 - _( - "

In some rare cases, you may want to set a transmission\nBit Rate explicitly. The default is to go as fast as possible.

" - ) + - # Wireless expert dialog help 4/5 - _( - "

In an environment with multiple Access Points, you may want to\ndefine the one to which to connect by entering its MAC address.

" - ) + - # Wireless expert dialog help 5/5 - _( - "

Use Power Management enables power saving mechanisms.\n" \ - "This is generally a good idea, especially if you are a laptop user and may\n" \ - "be disconnected from AC power.

\n" - ) - - channels = [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14" - ] - channels = deep_copy(LanItems.wl_channels) if !LanItems.wl_channels.nil? - if LanItems.wl_channel != "" && - !Builtins.contains(channels, LanItems.wl_channel) - channels = Builtins.prepend(channels, LanItems.wl_channel) - end - # Combobox item - channels = Builtins.prepend(channels, Item(Id(""), _("Automatic"))) - - bitrates = [ - "54", - "48", - "36", - "24", - "18", - "12", - "11", - "9", - "6", - "5.5", - "2", - "1" - ] - bitrates = deep_copy(LanItems.wl_bitrates) if !LanItems.wl_bitrates.nil? - if LanItems.wl_bitrate != "" && - !Builtins.contains(bitrates, LanItems.wl_bitrate) - bitrates = Builtins.prepend(bitrates, LanItems.wl_bitrate) - end - # Combobox item - bitrates = Builtins.prepend(bitrates, Item(Id(""), _("Automatic"))) - - # Wireless expert dialog contents - contents = HBox( - HSpacing(4), - VBox( - VSpacing(0.5), - # Frame label - Frame( - _("Wireless Expert Settings"), - HBox( - HSpacing(2), - VBox( - VSpacing(1), - # Combobox label - ComboBox(Id(:channel), Opt(:hstretch), _("&Channel"), channels), - VSpacing(0.2), - # Combobox label - ComboBox(Id(:bitrate), Opt(:hstretch), _("B&it Rate"), bitrates), - VSpacing(0.2), - # Text entry label - InputField( - Id(:accesspoint), - Opt(:hstretch), - _("&Access Point"), - LanItems.wl_accesspoint - ), - VSpacing(0.2), - # CheckBox label - Left( - CheckBox( - Id(:power), - _("Use &Power Management"), - LanItems.wl_power == true - ) - ), - VSpacing(0.2), - Left( - IntField( - Id(:ap_scanmode), - Opt(:hstretch), - _("AP ScanMode"), - 0, - 2, - Builtins.tointeger(LanItems.wl_ap_scanmode) - ) - ), - VSpacing(1) - ), - HSpacing(2) - ) - ), - VSpacing(0.5) - ), - HSpacing(4) - ) - - Wizard.SetContentsButtons( - caption, - contents, - helptext, - Label.BackButton, - Label.OKButton - ) - - UI.ChangeWidget(Id(:bitrate), :Value, LanItems.wl_bitrate) - UI.ChangeWidget(Id(:channel), :Value, LanItems.wl_channel) - # #88530 - channel_changeable = Builtins.contains( - ["Ad-hoc", "Master"], - LanItems.wl_mode - ) - UI.ChangeWidget(Id(:channel), :Enabled, channel_changeable) - - ret = nil - loop do - ret = UI.UserInput - - case ret - when :abort, :cancel - if ReallyAbort() - LanItems.Rollback() - break - end - - next - when :back - break - when :next - # Check - break - else - Builtins.y2error("Unexpected return code: %1", ret) - next - end - end - - if ret == :next - LanItems.wl_channel = Convert.to_string( - UI.QueryWidget(Id(:channel), :Value) - ) - # LanItems::wl_frequency = (string) UI::QueryWidget(`id(`frequency), `Value); - LanItems.wl_bitrate = Convert.to_string( - UI.QueryWidget(Id(:bitrate), :Value) - ) - LanItems.wl_accesspoint = Convert.to_string( - UI.QueryWidget(Id(:accesspoint), :Value) - ) - LanItems.wl_power = Convert.to_boolean( - UI.QueryWidget(Id(:power), :Value) - ) == true - LanItems.wl_ap_scanmode = Builtins.tostring( - UI.QueryWidget(Id(:ap_scanmode), :Value) - ) - end - - deep_copy(ret) - end - - # Used to add or edit a key - # @param [String] tkey has s: for ascii or h: for passphrase - # @param [Array] lengths allowed real key lengths - def WirelessKeyPopup(tkey, lengths) - lengths = deep_copy(lengths) - wkey = ParseWepKey(tkey) - key = Ops.get(wkey, "key", "") - type = Ops.get(wkey, "type", "") - - contents = HBox( - HSpacing(1), - VBox( - VSpacing(0.2), - # Translators: popup dialog heading - Heading(_("Enter Encryption Key")), - @type_w, # common with the main dialog - VSpacing(0.5), - # Translators: text entry label - Left(TextEntry(Id(:key), _("&Key"), key)), - VSpacing(0.2), - HBox( - PushButton(Id(:ok), Opt(:default, :key_F10), Label.OKButton), - PushButton(Id(:cancel), Opt(:key_F9), Label.CancelButton), - PushButton(Id(:help), Opt(:key_F1), Label.HelpButton) - ), - VSpacing(0.2) - ), - HSpacing(1) - ) - - UI.OpenDialog(Opt(:decorated), contents) - UI.ChangeWidget(Id(:type_g), :CurrentButton, type) - UI.SetFocus(Id(:key)) - - ret = nil - ckey = nil - loop do - ret = UI.UserInput - - if ret == :help - # Translators: popup title - Popup.LongText( - _("Help"), - RichText(Ops.get_string(@help, "wep_key", "")), - 50, - 18 - ) - elsif ret == :cancel - break - elsif ret == :ok - key = Convert.to_string(UI.QueryWidget(Id(:key), :Value)) - type = Convert.to_string(UI.QueryWidget(Id(:type_g), :CurrentButton)) - ckey = ComposeWepKey(type, key) - break if CheckWirelessKey(ckey, lengths) - UI.SetFocus(Id(:key)) - # Popup text - Popup.Error(_("The encryption key is invalid.")) - else - Builtins.y2error("Unexpected return code: %1", ret) - end - end - - tkey = ckey if ret == :ok - - UI.CloseDialog - - tkey - end - - # Generate items for the keys table - def WirelessKeysItems(keys, defaultk) - keys = deep_copy(keys) - Builtins.maplist([0, 1, 2, 3]) do |i| - Item(Id(i), i, Ops.get(keys, i, ""), i == defaultk ? "*" : "") - end - end - - # In case the current default key is empty, find a nonempty one - # or the first one. - def FindGoodDefault(keys, defaultk) - keys = deep_copy(keys) - return defaultk if Ops.get(keys, defaultk, "") != "" - defaultk = Builtins.find([0, 1, 2, 3]) { |i| Ops.get(keys, i, "") != "" } - defaultk = 0 if defaultk.nil? - defaultk - end - - # Wireless expert configuration dialog - # @return dialog result - def WirelessKeysDialog - # Wireless keys dialog caption - caption = _("Wireless Keys") - - # Wireless keys dialog help 1/3 - helptext = _( - "

In this dialog, define your WEP keys used\n" \ - "to encrypt your data before it is transmitted. You can have up to four keys,\n" \ - "although only one key is used to encrypt the data. This is the default key.\n" \ - "The other keys can be used to decrypt data. Usually you have only\n" \ - "one key.

" - ) + - # Wireless keys dialog help 2/3 - _( - "

Key Length defines the bit length of your WEP keys.\n" \ - "Possible are 64 and 128 bit, sometimes also referred to as 40 and 104 bit.\n" \ - "Some older hardware might not be able to handle 128 bit keys, so if your\n" \ - "wireless LAN connection does not establish, you may need to set this\n" \ - "value to 64.

" - ) + "" - - length = LanItems.wl_key_length - ui_key_lengths = Builtins.maplist(ParseKeyLengths(LanItems.wl_enc_modes)) do |kl| - Builtins.tostring(Ops.add(kl, 24)) - end - if !Builtins.contains(ui_key_lengths, length) - ui_key_lengths = Builtins.add(ui_key_lengths, length) - end - keys = deep_copy(LanItems.wl_key) - defaultk = FindGoodDefault(keys, LanItems.wl_default_key) - - # Wireless keys dialog contents - contents = HBox( - HSpacing(5), - VBox( - VSpacing(1), - # Frame label - Frame( - _("WEP Keys"), - HBox( - HSpacing(3), - VBox( - VSpacing(1), - # ComboBox label - Left(ComboBox(Id(:length), _("&Key Length"), ui_key_lengths)), - VSpacing(1), - Table( - Id(:table), - Opt(:notify), - Header( - # Table header label - # Abbreviation of Number - _("No."), - # Table header label - _("Key"), - # Table header label - Center(_("Default")) - ), - WirelessKeysItems(keys, defaultk) - ), - HBox( - # PushButton label - PushButton(Id(:edit), Label.EditButton), - # PushButton label - PushButton(Id(:delete), Label.DeleteButton), - # PushButton label - PushButton(Id(:default), _("&Set as Default")) - ), - VSpacing(1) - ), - HSpacing(3) - ) - ), - VSpacing(1) - ), - HSpacing(5) - ) - - Wizard.SetContentsButtons( - caption, - contents, - helptext, - Label.BackButton, - Label.OKButton - ) - - UI.ChangeWidget(Id(:length), :Value, length) - - current = Convert.to_integer(UI.QueryWidget(Id(:table), :CurrentItem)) - - ret = nil - loop do - Builtins.foreach([:edit, :delete, :default]) do |btn| - UI.ChangeWidget(Id(btn), :Enabled, !current.nil?) - end - - UI.SetFocus(Id(:table)) - ret = UI.UserInput - - current = Convert.to_integer(UI.QueryWidget(Id(:table), :CurrentItem)) - length = Convert.to_string(UI.QueryWidget(Id(:length), :Value)) - rlength = Ops.subtract(Builtins.tointeger(length), 24) - - case ret - when :abort, :cancel - if ReallyAbort() - LanItems.Rollback() - break - end - - next - when :table, :edit, :delete - Ops.set( - keys, - current, - if ret != :delete - WirelessKeyPopup(Ops.get(keys, current, ""), [rlength]) - else - "" - end - ) - defaultk = FindGoodDefault(keys, defaultk) - UI.ChangeWidget(Id(:table), :Items, WirelessKeysItems(keys, defaultk)) - when :default - defaultk = FindGoodDefault(keys, current) - UI.ChangeWidget(Id(:table), :Items, WirelessKeysItems(keys, defaultk)) - when :next, :back - break - else - Builtins.y2error("Unexpected return code: %1", ret) - next - end - end - - if ret == :next - LanItems.wl_key_length = length - LanItems.wl_key = deep_copy(keys) - LanItems.wl_default_key = defaultk - end - - deep_copy(ret) - end - - # -------------------- WPA EAP -------------------- - - # `RadioButtonGroup uses CurrentButton instead of Value, grrr - # @param [String] key widget id - # @return what property to ask for to get the widget value - def ValueProp(key) - if UI.QueryWidget(Id(key), :WidgetClass) == "YRadioButtonGroup" - return :CurrentButton - end - :Value - end - - # function to initialize widgets - # @param [String] key widget id - def InitializeWidget(key) - # the "" serves instead of a default constructor for wl_wpa_eap - value = Ops.get_string(LanItems.wl_wpa_eap, key, "") - UI.ChangeWidget(Id(key), ValueProp(key), value) - - nil - end - - # function to store data from widget - # @param [String] key widget id - # @param [Hash] _event unused - def StoreWidget(key, _event) - value = UI.QueryWidget(Id(key), ValueProp(key)) - Ops.set(LanItems.wl_wpa_eap, key, value) - - nil - end - - # Event handler for EAP mode: - # enable or disable appropriate widgets - # @param key [String] the widget receiving the event - # @param _event [Hash] the event being handled - # @return nil so that the dialog loops on - def HandleEapMode(key, _event) - tls = UI.QueryWidget(Id(key), :Value) == "TLS" - Builtins.foreach(["WPA_EAP_PASSWORD", "WPA_EAP_ANONID", "DETAILS_B"]) do |id| - UI.ChangeWidget(Id(id), :Enabled, !tls) - end - Builtins.foreach( - [ - "WPA_EAP_CLIENT_CERT", - "WPA_EAP_CLIENT_CERT_BROWSE", - "WPA_EAP_CLIENT_KEY", - "WPA_EAP_CLIENT_KEY_BROWSE", - "WPA_EAP_CLIENT_KEY_PASSWORD" - ] - ) { |id| UI.ChangeWidget(Id(id), :Enabled, tls) } - nil - end - - # function to initialize widgets - # @param [String] key widget id - def InitEapMode(key) - # inherited - InitializeWidget(key) - # enable/disable - HandleEapMode(key, "ID" => "_cwm_wakeup") - - nil - end - - # function to initialize widgets - # @param [String] key widget id - def InitPeapVersion(key) - # inherited - InitializeWidget(key) - # enable/disable - mode = Ops.get_string(LanItems.wl_wpa_eap, "WPA_EAP_MODE", "") - UI.ChangeWidget(Id(key), :Enabled, mode.casecmp("peap").zero?) - - nil - end - - # Called when one of the two file browser buttons is pressed - # @param [String] key widget id - # @param [Hash] event ? - # @return nil so that the dialog does not exit - def HandleFileBrowse(key, event) - event = deep_copy(event) - # only process our own events - return nil if Ops.get(event, "ID") != key - - # convert to the text entry widget we belong to - attached_to = { - "WPA_EAP_CLIENT_CERT_BROWSE" => "WPA_EAP_CLIENT_CERT", - "WPA_EAP_CLIENT_KEY_BROWSE" => "WPA_EAP_CLIENT_KEY", - "WPA_EAP_CA_CERT_BROWSE" => "WPA_EAP_CA_CERT" - } - key = Ops.get_string(attached_to, key, "") - - # get the file and its directory if already entered - file = Convert.to_string(UI.QueryWidget(Id(key), :Value)) - slashpos = Builtins.findlastof(file, "/") - defaultd = "." # "/etc/cert"; - dir = slashpos.nil? ? defaultd : Builtins.substring(file, 0, slashpos) - - # file browser dialog headline - file = UI.AskForExistingFile(dir, "*", _("Choose a Certificate")) - - if !file.nil? - # fill the value - UI.ChangeWidget(Id(key), :Value, file) - end - nil - end - - # Remap the buttons to their Wizard Sequencer values - # @param _key [String] the widget receiving the event - # @param event [Hash] the event being handled - # @return nil so that the dialog loops on - def HandleDetails(_key, event) - event = deep_copy(event) - return :details if Ops.get(event, "ID") == "DETAILS_B" - nil - end - - # Called to validate that the file entered exists - # @param key [String] widget id - # @param _event [Hash] the event being handled - # @return ok? - def ValidateFileExists(key, _event) - file = Convert.to_string(UI.QueryWidget(Id(key), :Value)) - - if file == "" - return true # validated in ValidateWpaEap - end - - return true if FileUtils.Exists(file) - - UI.SetFocus(Id(key)) - Popup.Error(Message.CannotOpenFile(file)) - false - end - - def ValidateCaCertExists(key, event) - event = deep_copy(event) - ret = true - if Builtins.size(Convert.to_string(UI.QueryWidget(Id(key), :Value))) == 0 || - !ValidateFileExists(key, event) - if !Popup.YesNo( - _( - "Not using a Certificate Authority (CA) certificate can result in connections\nto insecure, rogue wireless networks. Continue without CA ?" - ) - ) - ret = false - end - end - ret - end - - # Called to validate that the whole dialog makes sense together - # @param _key [String] widget id - # @param _event [Hash] the event being handled - # @return ok? - def ValidateWpaEap(_key, _event) - tmp = Builtins.listmap( - [ - "WPA_EAP_IDENTITY", - # "WPA_EAP_PASSWORD", - "WPA_EAP_CLIENT_CERT" - ] - ) { |key2| { key2 => UI.QueryWidget(Id(key2), :Value) } } - - if Ops.get_string(tmp, "WPA_EAP_CLIENT_CERT", "") == "" && - Ops.get_string(tmp, "WPA_EAP_IDENTITY", "") == "" - UI.SetFocus(Id("WPA_EAP_IDENTITY")) - # error popup text - Popup.Error( - _( - "Enter either the identity and password\nor the client certificate." - ) - ) - return false - else - return true - end - end - - # Lays out a text entry and a push button, with proper alignment - def AddButton(id, button_id) - # return `HBox (id, button_id); - # needs new CWM - VSquash(HBox(id, Bottom(button_id))) # only for old UI? - end - - # Settings for WPA-EAP - # @return dialog result - def WirelessWpaEapDialog - contents = VBox( - "WPA_EAP_MODE", - "WPA_EAP_DUMMY", - HBox("WPA_EAP_IDENTITY", HSpacing(1), "WPA_EAP_PASSWORD"), - "WPA_EAP_ANONID", - AddButton("WPA_EAP_CLIENT_CERT", "WPA_EAP_CLIENT_CERT_BROWSE"), - HBox( - AddButton("WPA_EAP_CLIENT_KEY", "WPA_EAP_CLIENT_KEY_BROWSE"), - HSpacing(1), - "WPA_EAP_CLIENT_KEY_PASSWORD" - ), - AddButton("WPA_EAP_CA_CERT", "WPA_EAP_CA_CERT_BROWSE"), - VSpacing(1), - Right("DETAILS_B") - ) - - functions = { - "init" => fun_ref(method(:InitializeWidget), "void (string)"), - "store" => fun_ref(method(:StoreWidget), "void (string, map)"), - :abort => fun_ref(method(:ReallyAbort), "boolean ()") - } # undocumented, FIXME - - CWM.ShowAndRun( - "widget_descr" => @wpa_eap_widget_descr, - "contents" => contents, - # dialog caption - "caption" => _("WPA-EAP"), - "back_button" => Label.BackButton, - "next_button" => Label.NextButton, - "fallback_functions" => functions - ) - end - - # Detailed settings for WPA-EAP - # @return dialog result - def WirelessWpaEapDetailsDialog - contents = HSquash( - VBox("WPA_EAP_AUTH", VSpacing(1), "WPA_EAP_PEAP_VERSION") - ) - - functions = { - "init" => fun_ref(method(:InitializeWidget), "void (string)"), - "store" => fun_ref(method(:StoreWidget), "void (string, map)"), - :abort => fun_ref(method(:ReallyAbort), "boolean ()") - } - - auth_names = { - # combo box item, any of EAP authentication methods - "" => _("Any"), - # combo box item, an EAP authentication method - "MD5" => _("MD5"), - # combo box item, an EAP authentication method - "GTC" => _("GTC"), - # combo box item, an EAP authentication method - "CHAP" => _("CHAP"), - # combo box item, an EAP authentication method - "PAP" => _("PAP"), - # combo box item, an EAP authentication method - "MSCHAP" => _("MSCHAPv1"), - # combo box item, an EAP authentication method - "MSCHAPV2" => _("MSCHAPv2") - } - auth_items = { - "TTLS" => ["", "MD5", "GTC", "CHAP", "PAP", "MSCHAP", "MSCHAPV2"], - "PEAP" => ["", "MD5", "GTC", "MSCHAPV2"] - } - mode = Ops.get_string(LanItems.wl_wpa_eap, "WPA_EAP_MODE", "") - - wd = deep_copy(@wpa_eap_widget_descr) - Ops.set( - wd, - ["WPA_EAP_AUTH", "items"], - Builtins.maplist(Ops.get(auth_items, mode, [])) do |i| - [i, Ops.get(auth_names, i, "")] - end - ) - - CWM.ShowAndRun( - "widget_descr" => wd, - "contents" => contents, - # dialog caption - "caption" => _("WPA-EAP Details"), - "back_button" => Label.BackButton, - "next_button" => Label.OKButton, - "fallback_functions" => functions - ) - end - - private - - IWLIST_PKG = "wireless-tools".freeze - - def scan_supported? - # Require wireless-tools installation in order to be able to scan the - # wlan network (bsc#1112952) - return true if Stage.initial || Package.Installed(IWLIST_PKG) || Package.Install(IWLIST_PKG) - - Popup.Error( - _("The package %s was not installed. It is needed in order to " \ - "be able to scan the network") % IWLIST_PKG - ) - false - end - end -end diff --git a/src/include/network/lan/wizards.rb b/src/include/network/lan/wizards.rb index a3fa97181..5eca2e1b4 100644 --- a/src/include/network/lan/wizards.rb +++ b/src/include/network/lan/wizards.rb @@ -47,7 +47,6 @@ def initialize_network_lan_wizards(include_target) Yast.include include_target, "network/lan/complex.rb" Yast.include include_target, "network/lan/dhcp.rb" Yast.include include_target, "network/lan/hardware.rb" - Yast.include include_target, "network/lan/wireless.rb" Yast.include include_target, "network/services/dns.rb" Yast.include include_target, "network/services/host.rb" end @@ -119,10 +118,9 @@ def LanAutoSequence(mode) end def MainSequence(mode) - iface_builder = Y2Network::InterfaceConfigBuilder.new aliases = { - "global" => -> { MainDialog("global", builder: iface_builder) }, - "overview" => -> { MainDialog("overview", builder: iface_builder) } + "global" => -> { MainDialog("global") }, + "overview" => -> { MainDialog("overview") } } start = "overview" @@ -176,49 +174,26 @@ def NetworkCardSequence(action, builder:) Sequencer.Run(aliases, sequence) end - def AddressSequence(which, builder:) + def AddressSequence(_which, builder:) # TODO: add builder wherever needed aliases = { - "address" => -> { AddressDialog(builder: builder) }, - "hosts" => -> { HostsMainDialog(false) }, - "s390" => -> { S390Dialog(builder: builder) }, - "wire" => -> { WirelessDialog() }, - "expert" => -> { WirelessExpertDialog() }, - "keys" => -> { WirelessKeysDialog() }, - "eap" => -> { WirelessWpaEapDialog() }, - "eap-details" => -> { WirelessWpaEapDetailsDialog() }, - "commit" => [-> { Commit(builder: builder) }, true] + "address" => -> { AddressDialog(builder: builder) }, + "hosts" => -> { HostsMainDialog(false) }, + "s390" => -> { S390Dialog(builder: builder) }, + "commit" => [-> { Commit(builder: builder) }, true] } - ws_start = which == "wire" ? "wire" : "address" sequence = { - "ws_start" => ws_start, - "address" => { - abort: :abort, - next: "commit", - wire: "wire", - hosts: "hosts", - s390: "s390", - hardware: :hardware + "ws_start" => "address", + "address" => { + abort: :abort, + next: "commit", + hosts: "hosts", + s390: "s390" }, - "s390" => { abort: :abort, next: "address" }, - "hosts" => { abort: :abort, next: "address" }, - "wire" => { - next: "commit", - expert: "expert", - keys: "keys", - eap: "eap", - abort: :abort - }, - "expert" => { next: "wire", abort: :abort }, - "keys" => { next: "wire", abort: :abort }, - "eap" => { - next: "commit", - details: "eap-details", - abort: :abort - }, - "eap-details" => { next: "eap", abort: :abort }, - "commit" => { next: :next } + "s390" => { abort: :abort, next: "address" }, + "hosts" => { abort: :abort, next: "address" }, + "commit" => { next: :next } } Sequencer.Run(aliases, sequence) diff --git a/src/include/network/services/dns.rb b/src/include/network/services/dns.rb index 4cdcce113..d902ddd3d 100644 --- a/src/include/network/services/dns.rb +++ b/src/include/network/services/dns.rb @@ -378,27 +378,20 @@ def use_dhcp_hostname? # Init handler for DHCP_HOSTNAME def InitDhcpHostname(_key) UI.ChangeWidget(Id("DHCP_HOSTNAME"), :Enabled, has_dhcp? && NetworkService.is_wicked) + dhcp_hostname = DNS.dhcp_hostname - hostname_ifaces = LanItems.find_set_hostname_ifaces - selected = DNS.dhcp_hostname ? ANY_LABEL : NONE_LABEL - items = [] - - if !LanItems.valid_dhcp_cfg? - fix_dhclient_warning(LanItems.invalid_dhcp_cfgs) - - selected = NO_CHANGE_LABEL - items << Item(Id(NO_CHANGE_LABEL), _("keep current settings"), true) - elsif hostname_ifaces.size == 1 - selected = hostname_ifaces.first - end - # translators: no device selected placeholder - items << Item(Id(NONE_LABEL), _("no"), selected == NONE_LABEL) - # translators: placeholder for "set hostname via any DHCP aware device" - items << Item(Id(ANY_LABEL), _("yes: any"), selected == ANY_LABEL) + items = [ + # translators: no device selected placeholder + Item(Id(:none), _("no"), dhcp_hostname == :none), + # translators: placeholder for "set hostname via any DHCP aware device" + Item(Id(:any), _("yes: any"), dhcp_hostname == :any) + ] - items += LanItems.find_dhcp_ifaces.sort.map do |iface| + config = Yast::Lan.yast_config + iface_names = config.connections.to_a.select { |c| c.bootproto.dhcp? }.map(&:interface) + items += iface_names.map do |iface| # translators: label is in form yes: - Item(Id(iface), format(_("yes: %s"), iface), iface == selected) + Item(Id(iface), format(_("yes: %s"), iface), dhcp_hostname == iface) end UI.ReplaceWidget( @@ -410,7 +403,7 @@ def InitDhcpHostname(_key) ) ) - log.info("InitDhcpHostname: preselected item = #{selected}") + log.info("InitDhcpHostname: preselected item = #{dhcp_hostname}") nil end @@ -418,25 +411,7 @@ def InitDhcpHostname(_key) # Store handler for DHCP_HOSTNAME def StoreDhcpHostname(_key, _event) return if !UI.QueryWidget(Id("DHCP_HOSTNAME"), :Enabled) - - device = UI.QueryWidget(Id("DHCP_HOSTNAME"), :Value) - - case device - when NONE_LABEL - LanItems.clear_set_hostname - - DNS.dhcp_hostname = false - when ANY_LABEL - LanItems.clear_set_hostname - - DNS.dhcp_hostname = true - when NO_CHANGE_LABEL - nil - else - LanItems.conf_set_hostname(device) - - DNS.dhcp_hostname = false - end + DNS.dhcp_hostname = UI.QueryWidget(Id("DHCP_HOSTNAME"), :Value) nil end diff --git a/src/include/network/widgets.rb b/src/include/network/widgets.rb index fe907ced6..5bf4b5f8a 100644 --- a/src/include/network/widgets.rb +++ b/src/include/network/widgets.rb @@ -116,7 +116,7 @@ def ManagedStore(_key, _event) end if NetworkService.Modified - LanItems.SetModified + Lan.SetModified if Stage.normal && NetworkService.is_network_manager Popup.AnyMessage( @@ -208,36 +208,5 @@ def ipv6_widget "store" => fun_ref(method(:storeIPv6), "void (string, map)") } end - - def GetDeviceDescription(device_id) - device_name = NetworkInterfaces.GetValue(device_id, "NAME") - if device_name.nil? || device_name == "" - # TRANSLATORS: Informs that device name is not known - device_name = _("Unknown device") - end - Builtins.y2milestone("device_name %1", device_name) - # avoid too long device names - # if (size(device_name) > 30) { - # device_name = substring (device_name, 0, 27) + "..."; - # } - ip_addr = if Builtins.issubstring(NetworkInterfaces.GetValue(device_id, "BOOTPROTO"), "dhcp") - # TRANSLATORS: Part of label, device with IP address assigned by DHCP - _("DHCP address") - else - # TRANSLATORS: Part of label, device with static IP address - NetworkInterfaces.GetValue(device_id, "IPADDR") - end - if ip_addr.nil? || ip_addr == "" - # TRANSLATORS: Informs that no IP has been assigned to the device - ip_addr = _("No IP address assigned") - end - output = Builtins.sformat( - _("%1 \n%2 - %3"), - device_name, - NetworkInterfaces.GetDeviceTypeName(device_id), - ip_addr - ) - output - end end end diff --git a/src/lib/cfa/generic_sysconfig.rb b/src/lib/cfa/generic_sysconfig.rb index 4f5c34762..1cf893c76 100644 --- a/src/lib/cfa/generic_sysconfig.rb +++ b/src/lib/cfa/generic_sysconfig.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cfa/base_model" require "cfa/augeas_parser" require "cfa/matcher" diff --git a/src/lib/cfa/hosts.rb b/src/lib/cfa/hosts.rb index acd65e483..ece5d39a5 100644 --- a/src/lib/cfa/hosts.rb +++ b/src/lib/cfa/hosts.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "yast2/target_file" diff --git a/src/lib/network/clients/inst_setup_dhcp.rb b/src/lib/network/clients/inst_setup_dhcp.rb index 86e84725b..ba5edb5ca 100644 --- a/src/lib/network/clients/inst_setup_dhcp.rb +++ b/src/lib/network/clients/inst_setup_dhcp.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "network/network_autoconfiguration" Yast.import "Linuxrc" diff --git a/src/lib/network/clients/network_proposal.rb b/src/lib/network/clients/network_proposal.rb index fb807a059..8f9e9238d 100644 --- a/src/lib/network/clients/network_proposal.rb +++ b/src/lib/network/clients/network_proposal.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cgi" require "installation/proposal_client" require "y2network/proposal_settings" diff --git a/src/lib/network/clients/save_network.rb b/src/lib/network/clients/save_network.rb index f8e392bbb..2f332b10e 100644 --- a/src/lib/network/clients/save_network.rb +++ b/src/lib/network/clients/save_network.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "y2storage" require "network/install_inf_convertor" require "network/network_autoconfiguration" @@ -181,7 +200,8 @@ def copy_from_instsys # 1) udev agent doesn't support SetRoot # 2) original ifcfg file is copied otherwise too. It doesn't break things itself # but definitely not looking well ;-) - NetworkAutoYast.instance.create_udevs if Mode.autoinst + # TODO: implement support for create udev rules if needed + # NetworkAutoYast.instance.create_udevs if Mode.autoinst copy_dhcp_info copy_udev_rules diff --git a/src/lib/network/confirm_virt_proposal.rb b/src/lib/network/confirm_virt_proposal.rb index 02ffae4e0..06f0df6f0 100644 --- a/src/lib/network/confirm_virt_proposal.rb +++ b/src/lib/network/confirm_virt_proposal.rb @@ -1,4 +1,21 @@ -# encoding: utf-8 +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. require "yast" diff --git a/src/lib/network/edit_nic_name.rb b/src/lib/network/edit_nic_name.rb deleted file mode 100644 index 1749e05b7..000000000 --- a/src/lib/network/edit_nic_name.rb +++ /dev/null @@ -1,180 +0,0 @@ -# encoding: utf-8 - -require "yast" - -module Yast - Yast.import "UI" - Yast.import "LanItems" - Yast.import "Popup" - - # The class represents a simple dialog which allows user to input new NIC - # name. It also allows to select a device attribute (MAC, Bus id, ...) which will - # be used for device selection. - class EditNicName - include UIShortcuts - include I18n - - # @return [String] current udev name before modifying it - attr_reader :old_name - # @return [String] current udev match criteria - attr_reader :old_key - - # udev rule attribute for MAC address - MAC_UDEV_ATTR = "ATTR{address}".freeze - - # udev rule attribute for BUS id - BUSID_UDEV_ATTR = "KERNELS".freeze - - def initialize - textdomain "network" - - Yast.include self, "network/routines.rb" - - current_item = LanItems.getCurrentItem - current_rule = LanItems.current_udev_rule - - @old_name = LanItems.current_udev_name - unless current_rule.empty? - @old_key = :mac unless LanItems.GetItemUdev(MAC_UDEV_ATTR).empty? - @old_key = :bus_id unless LanItems.GetItemUdev(BUSID_UDEV_ATTR).empty? - end - - if current_item["hwinfo"] - @mac = LanItems.item_mac(LanItems.current) - @bus_id = current_item["hwinfo"]["busid"] - else - @mac = "" - @bus_id = "" - end - end - - # Opens dialog for editing NIC name and runs event loop. - # - # @return [String] new NIC name - def run - open - - ret = nil - until [:cancel, :abort, :ok].include? ret - ret = UI.UserInput - - next if ret != :ok - - new_name = UI.QueryWidget(:dev_name, :Value) - - if CheckUdevNicName(new_name) - LanItems.rename(new_name) - else - UI.SetFocus(:dev_name) - ret = nil - - next - end - - udev_type = UI.QueryWidget(:udev_type, :CurrentButton) - - # FIXME: it changes udev key used for device identification - # and / or its value only, name is changed elsewhere - LanItems.update_item_udev_rule!(udev_type) if udev_type && (old_key != udev_type) - LanItems.rename_current_device_in_routing(old_name) if new_name != old_name - end - - close - - new_name || old_name - end - - private - - # Opens dialog for editing NIC name - def open - UI.OpenDialog( - VBox( - Left( - HBox( - Label(_("Device Name:")), - InputField(Id(:dev_name), Opt(:hstretch), "", old_name) - ) - ), - VSpacing(0.5), - Frame( - _("Base Udev Rule On"), - RadioButtonGroup( - Id(:udev_type), - VBox( - # make sure there is enough space (#367239) - HSpacing(30), - Left( - RadioButton( - Id(:mac), - _("MAC address: %s") % @mac - ) - ), - Left( - RadioButton( - Id(:bus_id), - _("BusID: %s") % @bus_id - ) - ) - ) - ) - ), - VSpacing(0.5), - HBox( - PushButton(Id(:ok), Opt(:default), Label.OKButton), - PushButton(Id(:cancel), Label.CancelButton) - ) - ) - ) - - if old_key - UI.ChangeWidget(Id(:udev_type), :CurrentButton, old_key) - else - Builtins.y2error("Unknown udev rule.") - end - end - - # Closes the dialog - def close - UI.CloseDialog - end - - # Checks if given name can be accepted as nic's new one. - # - # Pops up an explanation if the name is invalid - # - # @return [boolean] false if name is invalid - def CheckUdevNicName(name) - # check if the name is assigned to another device already - if LanItems.GetNetcardNames.include?(name) && name != LanItems.GetCurrentName - Popup.Error(_("Configuration name already exists.")) - return false - end - if !ValidNicName(name) - Popup.Error(_("Invalid configuration name.")) - return false - end - - true - end - - # When an interface name has changed, it returns whether the user wants to - # update the interface name in the related routes or not. - # - # return [Boolean] whether the routes have to be updated or not - def update_routes?(previous_name) - return false unless Routing.device_routes?(previous_name) - - Popup.YesNoHeadline( - Label.WarningMsg, - # TRANSLATORS: Ask for fixing a possible conflict after renaming - # an interface, %s are the previous and current interface names - format(_("The interface %s has been renamed to %s. There are \n" \ - "some routes that still use the previous name.\n\n" \ - "Would you like to update them now?\n"), - "'#{previous_name}'", - "'#{LanItems.current_name}'") - ) - end - end -end diff --git a/src/lib/network/install_inf_convertor.rb b/src/lib/network/install_inf_convertor.rb index ddd44502e..f251691fb 100644 --- a/src/lib/network/install_inf_convertor.rb +++ b/src/lib/network/install_inf_convertor.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "uri" diff --git a/src/lib/network/lan_items_summary.rb b/src/lib/network/lan_items_summary.rb index f68eca529..b6753a64d 100644 --- a/src/lib/network/lan_items_summary.rb +++ b/src/lib/network/lan_items_summary.rb @@ -33,29 +33,6 @@ def initialize Yast.import "Summary" end - # Generates a summary in RichText format for the configured interfaces - # - # @example - # LanItemsSummary.new.default - # => "
  • eth0
    DHCP

  • eth1
    NONE

" - # - # @see Summary - # @return [String] summary in RichText - def default - items = [] - - LanItems.Items.each do |item, conf| - next if !Yast::LanItems.IsItemConfigured(item) - - ifcfg = LanItems.GetDeviceMap(item) || {} - items << Summary.Device(conf["ifcfg"], ifcfg_protocol(ifcfg)) - end - - return Summary.NotConfigured if items.empty? - - Summary.DevicesList(items) - end - # Generates a summary in RichText format for the configured interfaces # # @example diff --git a/src/lib/network/network_autoconfiguration.rb b/src/lib/network/network_autoconfiguration.rb index 3e6026ce9..741ee30b1 100644 --- a/src/lib/network/network_autoconfiguration.rb +++ b/src/lib/network/network_autoconfiguration.rb @@ -1,4 +1,21 @@ -# encoding: utf-8 +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. require "yast" require "network/wicked" @@ -28,29 +45,31 @@ class NetworkAutoconfiguration # # returns [Boolean] true when at least one interface is active def any_iface_active? - network_cards.any? { |c| configured?(c) && active_config?(c) } + config.interfaces.any? { |c| config.connections.by_name(c.name) && active_config?(c.name) } end def configure_dhcp - Yast.include self, "network/routines.rb" + Yast::Lan.Read(:cache) + Yast.include self, "network/routines.rb" # TODO: needed only for phy_connected # find out network devices suitable for dhcp autoconfiguration. # Such device has to: # - be unconfigured # - physically connected to a network # (it speeds up the initialization phase of installer - bnc#872319) - dhcp_cards = network_cards.select do |c| - !configured?(c) && phy_connected?(c) + dhcp_cards = config.interfaces.select do |c| + next false if config.connections.by_name(c.name) + phy_connected?(c.name) end - log.info "Candidates for enabling DHCP: #{dhcp_cards}" + log.info "Candidates for enabling DHCP: #{dhcp_cards.inspect}" # TODO: time consuming, some progress would be nice dhcp_cards.each { |d| setup_dhcp(d) } - activate_changes(dhcp_cards) + activate_changes(dhcp_cards.map(&:name)) # drop devices without dhcp lease - inactive_devices = dhcp_cards.select { |c| !active_config?(c) } + inactive_devices = dhcp_cards.select { |c| !active_config?(c.name) } log.info "Inactive devices: #{inactive_devices}" inactive_devices.each { |c| delete_config(c) } @@ -65,10 +84,10 @@ def configure_dhcp # try to find just one dhcp aware device for allowing default route # if there is more than one dhcp devices enabled for setting default # route (DHCLIENT_SET_DEFAULT_ROUTE = "yes"). bnc#868187 - active_devices.find { |d| set_default_route_flag_if_wan_dev?(d) } + active_devices.find { |d| set_default_route_flag_if_wan_dev?(d.name) } end - activate_changes(dhcp_cards) + activate_changes(dhcp_cards.map(&:name)) end # Propose configuration for virtual devices @@ -85,7 +104,6 @@ def configure_virtuals # avoid restarting network (installation can run via ssh, vnc, ...) # Moreover virtual devices are not needed during first stage. So, it can # wait for rebooting into just installed target - LanItems.write return if Lan.yast_config == Lan.system_config Lan.yast_config.write end @@ -93,7 +111,6 @@ def configure_virtuals # Propose DNS and Hostname setup def configure_dns DNS.Read # handles NetworkConfig too - DNS.propose_hostname log.info("NetworkAutoconfiguration: proposing DNS / Hostname configuration") DNS.Write end @@ -110,49 +127,29 @@ def configure_hosts private - def network_cards - LanItems.Read - LanItems.GetNetcardNames - end - # Makes DHCP setup persistent # # instsys currently uses wicked as network services "manager" (including # dhcp client). wicked is currently able to configure a card for dhcp leases # only via loading config from file. All other ways are workarounds and # needn't to work when wickedd* services are already running + # @param card [Y2Network::Interface] def setup_dhcp(card) - index = LanItems.FindDeviceIndex(card) - - raise "Failed to save configuration for device #{card}" if index == -1 - - LanItems.current = index + builder = Y2Network::InterfaceConfigBuilder.for(card.type) + builder.name = card.name - builder = Y2Network::InterfaceConfigBuilder.for(LanItems.GetCurrentType()) - builder.name = LanItems.GetCurrentName() - - LanItems.SetItem(builder: builder) - - # tricky part if ifcfg is not set - # yes, this code smell and show bad API of LanItems - if !LanItems.IsCurrentConfigured - NetworkInterfaces.Add - current = LanItems.Items[LanItems.current] - current["ifcfg"] = card - end - - builder["BOOTPROTO"] = "dhcp" - builder["STARTMODE"] = "auto" + builder.boot_protocol = Y2Network::BootProtocol::DHCP + builder.startmode = Y2Network::Startmode.create("auto") LanItems.Commit(builder) end - def delete_config(devname) - LanItems.delete_dev(devname) + def delete_config(interface) + config.delete_interface(interface.name) end def write_configuration - NetworkInterfaces.Write("") + config.write end # Writes and activates changes in devices configurations @@ -160,23 +157,11 @@ def write_configuration # @param devnames [Array] list of device names # @return true when changes were successfully applied def activate_changes(devnames) - return false if !write_configuration - - # workaround for gh#yast/yast-core#74 (https://github.com/yast/yast-core/issues/74) - NetworkInterfaces.CleanCacheRead() + write_configuration reload_config(devnames) end - def configured?(devname) - # TODO: one day there should be LanItems.IsItemConfigured, but we currently - # miss index -> devname translation. As this LanItems internal structure - # will be subject of refactoring, we will use NetworkInterfaces directly. - # It currently doesn't hurt as it currently writes configuration for both - # wicked even sysconfig. - NetworkInterfaces.Check(devname) - end - # Checks if given device is active # # active device <=> a device which is reported as "up" by wicked @@ -224,8 +209,7 @@ def set_default_route_flag_if_wan_dev?(devname) # @param [String] devname name of device as seen by system (e.g. enp0s3) # @param [String] value "yes" or "no", as in sysconfig def set_default_route_flag(devname, value) - item_id = LanItems.FindDeviceIndex(devname) - LanItems.SetItemSysconfigOpt(item_id, "DHCLIENT_SET_DEFAULT_ROUTE", value) + # TODO: not implemented end # Decides if a proposal for virtualization host machine is required. @@ -239,5 +223,9 @@ def virtual_proposal_required? false end + + def config + Yast::Lan.yast_config + end end end diff --git a/src/lib/network/network_autoyast.rb b/src/lib/network/network_autoyast.rb index 62f877c89..63d97eba6 100644 --- a/src/lib/network/network_autoyast.rb +++ b/src/lib/network/network_autoyast.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" module Yast @@ -53,39 +72,6 @@ def merge_configs(conf) conf end - # Creates udev rules according definition from profile - def create_udevs - return if !Mode.autoinst - - log.info("Applying udev rules according to AY profile") - - # get implicitly defined udev rules (via old style names) - im_udev_rules = LanItems.createUdevFromIfaceName(ay_networking_section["interfaces"]) - log.info("- implicitly defined udev rules: #{im_udev_rules}") - - no_rules = im_udev_rules.empty? - - # get explicit udev definition from the profile - ex_udev_rules = ay_networking_section["net-udev"] || [] - log.info("- explicitly defined udev rules: #{ex_udev_rules}") - - no_rules &&= ex_udev_rules.empty? - return if no_rules - - # for the purpose of setting the persistent names, create the devices 1st - s390_devices = ay_networking_section.fetch("s390-devices", {}) - s390_devices.each { |rule| LanItems.createS390Device(rule) } if Arch.s390 - - LanItems.Read - - # implicitly defined udev rules are overwritten by explicit ones in - # case of collision. - assign_udevs_to_devs(im_udev_rules) - assign_udevs_to_devs(ex_udev_rules) - - LanItems.write - end - # Sets network service for target def set_network_service return if !Mode.autoinst @@ -275,88 +261,6 @@ def ay_host_section ay_current_profile["host"] end - # Checks if the udev rule is valid for renaming a NIC - def valid_rename_udev_rule?(rule) - return false if rule["name"].nil? || rule["name"].empty? - return false if rule["rule"].nil? || rule["rule"].empty? - return false if rule["value"].nil? || rule["value"].empty? - - true - end - - # Renames a network device represented by given item. - # - # @param item [Integer] is an item id. See LanItems for detail - # @param name_to [String] new device name - # @param attr [String] an udev attribute usable in NIC's rule. Currently just - # "KERNELS" or "ATTR{address}" makes sense. This parameter is optional - # @param key [String] 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 - LanItems.InitItemUdevRule(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. - # Note: initialization of LanItems has to be done outside of this method - def assign_udevs_to_devs(udev_rules) - return if udev_rules.nil? - - udev_rules.each do |rule| - name_to = rule["name"] - attr = rule["rule"] - key = rule["value"] - - next if !valid_rename_udev_rule?(rule) - key.downcase! - - # find item which matches to the given rule definition - item_id, matching_item = LanItems.Items.find do |_, i| - next unless i["hwinfo"] - busid = i["hwinfo"]["busid"] - # Match also parent busid if exist (bsc#1129012) - parent_busid = i["hwinfo"]["parent_busid"] || busid - mac = i["hwinfo"]["permanent_mac"] - # use mac if permanent_mac is missing (bsc#1149234) - mac = i["hwinfo"]["mac"] if mac.nil? || mac.empty? - - [busid, parent_busid, mac].any? { |v| v.casecmp(key).zero? } - end - next if !matching_item - - name_from = LanItems.current_name_for(item_id) - log.info("Matching device found - renaming <#{name_from}> -> <#{name_to}>") - - # rename item in collision - rename_lan_item(LanItems.colliding_item(name_to), name_from) - - # rename matching item - rename_lan_item(item_id, 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 diff --git a/src/lib/network/wicked.rb b/src/lib/network/wicked.rb index 9b3d8a4b2..1b8b1ff66 100644 --- a/src/lib/network/wicked.rb +++ b/src/lib/network/wicked.rb @@ -1,4 +1,21 @@ -# encoding: utf-8 +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. require "yast" require "shellwords" diff --git a/src/lib/y2network/autoinst/config_reader.rb b/src/lib/y2network/autoinst/config_reader.rb index 5fb242ad8..571940ba7 100644 --- a/src/lib/y2network/autoinst/config_reader.rb +++ b/src/lib/y2network/autoinst/config_reader.rb @@ -22,6 +22,8 @@ require "y2network/config" require "y2network/autoinst/routing_reader" require "y2network/autoinst/dns_reader" +require "y2network/autoinst/interfaces_reader" +require "y2network/autoinst/udev_rules_reader" require "y2network/autoinst_profile/networking_section" require "y2network/sysconfig/interfaces_reader" @@ -37,28 +39,28 @@ class ConfigReader # Constructor # # @param section [AutoinstProfile::NetworkingSection] - def initialize(section) + # @param original_config [Config] system configuration + def initialize(section, original_config) @section = section + @original_config = original_config end # @return [Y2Network::Config] Network configuration def config - attrs = { source: :sysconfig } - # FIXME: implement proper readers for AutoYaST - attrs[:interfaces] = interfaces_reader.interfaces - attrs[:connections] = interfaces_reader.connections - attrs[:routing] = RoutingReader.new(section.routing).config if section.routing - attrs[:dns] = DNSReader.new(section.dns).config if section.dns - Y2Network::Config.new(attrs) - end - - private + config = @original_config.copy + # apply at first udev rules, so interfaces names are correct + UdevRulesReader.new(section.udev_rules).apply(config) if section.udev_rules + config.routing = RoutingReader.new(section.routing).config if section.routing + config.dns = DNSReader.new(section.dns).config if section.dns + if section.interfaces + interfaces = InterfacesReader.new(section.interfaces).config + interfaces.each do |interface| + # add or update system configuration, this will also create all missing interfaces + config.add_or_update_connection_config(interface) + end + end - # Returns an interfaces reader instance - # - # @return [SysconfigInterfaces] Interfaces reader - def interfaces_reader - @interfaces_reader ||= Y2Network::Sysconfig::InterfacesReader.new + config end end end diff --git a/src/lib/y2network/autoinst/dns_reader.rb b/src/lib/y2network/autoinst/dns_reader.rb index bf102692c..967a1bf00 100644 --- a/src/lib/y2network/autoinst/dns_reader.rb +++ b/src/lib/y2network/autoinst/dns_reader.rb @@ -19,6 +19,7 @@ require "yast" require "y2network/dns" +require "y2network/hostname_reader" require "ipaddr" Yast.import "IP" @@ -29,6 +30,8 @@ class DNSReader # @return [AutoinstProfile::DNSSection] attr_reader :section + DEFAULT_RESOLV_CONF_POLICY = "auto".freeze + # @param section [AutoinstProfile::DNSSection] def initialize(section) @section = section @@ -40,9 +43,9 @@ def initialize(section) def config Y2Network::DNS.new( dhcp_hostname: section.dhcp_hostname, - hostname: section.hostname, + hostname: section.hostname || default_hostname, nameservers: valid_ips(section.nameservers), - resolv_conf_policy: section.resolv_conf_policy, + resolv_conf_policy: section.resolv_conf_policy || DEFAULT_RESOLV_CONF_POLICY, searchlist: section.searchlist ) end @@ -60,6 +63,13 @@ def valid_ips(ips) all << IPAddr.new(ip_str) if Yast::IP.Check(ip_str) end end + + # Returns a random hostname + # + # @return [String] + def default_hostname + HostnameReader.new.hostname + end end end end diff --git a/src/lib/y2network/autoinst/interfaces_reader.rb b/src/lib/y2network/autoinst/interfaces_reader.rb new file mode 100644 index 000000000..d5890cd95 --- /dev/null +++ b/src/lib/y2network/autoinst/interfaces_reader.rb @@ -0,0 +1,156 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/dns" +require "y2network/autoinst/type_detector" +require "ipaddr" +Yast.import "IP" + +module Y2Network + module Autoinst + # This class is responsible of importing the AutoYast interfaces section + class InterfacesReader + include Yast::Logger + + # @return [AutoinstProfile::InterfacesSection] + attr_reader :section + + # @param section [AutoinstProfile::InterfacesSection] + def initialize(section) + @section = section + end + + # Creates a new {ConnectionConfigsCollection} config from the imported profile interfaces + # section + # @note interfaces will be created automatic from connection configs + # + # @return [ConnectionConfigsCollection] the imported connections configs + def config + configs = @section.interfaces.map do |interface_section| + log.info "Creating config for interface section: #{interface_section.inspect}" + config = create_config(interface_section) + config.propose # propose reasonable defaults for not set attributes + load_generic(config, interface_section) + + case config + when ConnectionConfig::Vlan + load_vlan(config, interface_section) + when ConnectionConfig::Bridge + load_bridge(config, interface_section) + when ConnectionConfig::Bonding + load_bonding(config, interface_section) + when ConnectionConfig::Wireless + load_wireless(config, interface_section) + end + + log.info "Resulting config: #{config.inspect}" + config + end + + ConnectionConfigsCollection.new(configs) + end + + private + + def create_config(interface_section) + name = interface_section.name || interface_section.device + type = TypeDetector.type_of(name, interface_section) + # TODO: TUN/TAP interface missing for autoyast? + ConnectionConfig.const_get(type.class_name).new + end + + def load_generic(config, interface_section) + config.bootproto = BootProtocol.from_name(interface_section.bootproto) + config.name = interface_section.name || interface_section.device # device is just fallback + config.interface = config.name # in autoyast name and interface is same + if config.bootproto == BootProtocol::STATIC + # TODO: report if ipaddr missing for static config + ipaddr = IPAddress.from_string(interface_section.ipaddr) + # Assign first netmask, as prefixlen has precedence so it will overwrite it + ipaddr.netmask = interface_section.netmask if interface_section.netmask + ipaddr.prefix = interface_section.prefixlen.to_i if interface_section.prefixlen + broadcast = IPAddress.new(interface_section.broadcast) unless interface_section.broadcast.empty? + remote = IPAddress.new(interface_section.remote_ipaddr) unless interface_section.remote_ipaddr.empty? + config.ip = ConnectionConfig::IPConfig.new(ipaddr, broadcast: broadcast, remote_address: remote) + end + + # handle aliases + interface_section.aliases.each_value do |alias_h| + ipaddr = IPAddress.from_string(alias_h["IPADDR"]) + # Assign first netmask, as prefixlen has precedence so it will overwrite it + ipaddr.netmask = alias_h["NETMASK"] if alias_h["NETMASK"] + ipaddr.prefix = alias_h["PREFIXLEN"].delete("/").to_i if alias_h["PREFIXLEN"] + config.ip_aliases << ConnectionConfig::IPConfig.new(ipaddr, label: alias_h["LABEL"]) + end + + config.startmode = Startmode.create(interface_section.startmode) if interface_section.startmode + config.startmode.priority = interface_section.ifplugd_priority if config.startmode.name == "ifplugd" && interface_section.ifplugd_priority + config.mtu = interface_section.mtu.to_i if interface_section.mtu + config.ethtool_options = interface_section.ethtool_options if interface_section.ethtool_options + config.firewall_zone = interface_section.zone if interface_section.zone + end + + def load_wireless(config, interface_section) + config.mode = interface_section.wireless_mode if interface_section.wireless_mode + config.ap = interface_section.wireless_ap if interface_section.wireless_ap + config.bitrate = interface_section.wireless_bitrate.to_f if interface_section.wireless_bitrate + config.ca_cert = interface_section.wireless_ca_cert if interface_section.wireless_ca_cert + config.channel = interface_section.wireless_channel.to_i if interface_section.wireless_channel + config.client_cert = interface_section.wireless_client_cert if interface_section.wireless_client_cert + config.client_key = interface_section.wireless_client_key if interface_section.wireless_client_key + config.essid = interface_section.wireless_essid if interface_section.wireless_essid + config.auth_mode = interface_section.wireless_auth_mode.to_sym if interface_section.wireless_auth_mode + config.nick = interface_section.wireless_nick if interface_section.wireless_nick + config.nwid = interface_section.wireless_nwid if interface_section.wireless_nwid + config.wpa_anonymous_identity = interface_section.wireless_wpa_anonid if interface_section.wireless_wpa_anonid + config.wpa_identity = interface_section.wireless_wpa_identity if interface_section.wireless_wpa_identity + config.wpa_password = interface_section.wireless_wpa_password if interface_section.wireless_wpa_password + config.wpa_psk = interface_section.wireless_wpa_psk if interface_section.wireless_wpa_psk + config.keys = [] + (0..3).each do |i| + key = interface_section.public_send(:"wireless_key#{i}") + config.keys << key if key && !key.empty? + end + config.default_key = interface_section.wireless_key.to_i if interface_section.wireless_key + config.key_length = interface_section.wireless_key_length.to_i if interface_section.wireless_key_length + end + + def load_vlan(config, interface_section) + config.vlan_id = interface_section.vlan_id.to_i if interface_section.vlan_id + config.parent_device = interface_section.etherdevice + end + + def load_bridge(config, interface_section) + config.ports = interface_section.bridge_ports.split + config.stp = interface_section.bridge_stp == "on" if interface_section.bridge_stp + config.forward_delay = interface_section.bridge_forward_delay.to_i if interface_section.bridge_forward_delay + end + + def load_bonding(config, interface_section) + config.options = interface_section.bonding_module_opts if interface_section.bonding_module_opts + config.slaves = [] + (0..9).each do |i| + slave = interface_section.public_send(:"bonding_slave#{i}") + config.slaves << slave if slave && !slave.empty? + end + end + end + end +end diff --git a/src/lib/y2network/autoinst/type_detector.rb b/src/lib/y2network/autoinst/type_detector.rb new file mode 100644 index 000000000..426f74360 --- /dev/null +++ b/src/lib/y2network/autoinst/type_detector.rb @@ -0,0 +1,54 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_type" +require "y2network/type_detector" +require "y2network/sysconfig/interface_file" + +module Y2Network + module Autoinst + # Detects type of given interface based on autoyast profile + class TypeDetector < Y2Network::TypeDetector + class << self + def type_of(iface, section) + type_by_sys(iface) || type_by_config(section) || InterfaceType::ETHERNET + end + + private + + # Checks wheter iface type can be recognized by interface configuration + def type_by_config(section) + # TODO: autoyast backend for type detector? + # TODO: TUN/TAP interface missing for autoyast? + if !section.bonding_slaves.empty? + InterfaceType::BONDING + elsif !section.bridge_ports.empty? + InterfaceType::BRIDGE + elsif !section.etherdevice.empty? + InterfaceType::VLAN + elsif !section.wireless_essid.empty? + InterfaceType::WIRELESS + # TODO: when autoyast define tun/tap add it there + end + end + end + end + end +end diff --git a/src/lib/y2network/autoinst/udev_rules_reader.rb b/src/lib/y2network/autoinst/udev_rules_reader.rb new file mode 100644 index 000000000..deda9ca28 --- /dev/null +++ b/src/lib/y2network/autoinst/udev_rules_reader.rb @@ -0,0 +1,99 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/autoinst_profile/udev_rule_section" + +module Y2Network + module Autoinst + # This class is responsible of importing the AutoYast udev rules section + # It is a bit different than other readers as it does not produce its config, + # but instead it is applied on top of current config by applying proper names + # for interfaces or creating new ones. + class UdevRulesReader + include Yast::Logger + + # @return [AutoinstProfile::UdevRulesSection] + attr_reader :section + + # @param section [AutoinstProfile::UdevRulesSectionSection] + def initialize(section) + @section = section + end + + # Apply udev rules on passed config + # @param config [Config] + def apply(config) + @section.udev_rules.each do |udev_rule| + target_interface = interface_for(config, udev_rule) + if target_interface + rename_interface(config, target_interface, udev_rule) + else + create_interface(config, udev_rule) + end + end + end + + private + + # find according to udev rule interface that match given hardware specification or nil if not exist + # @param config [Config] + # @param udev_rule [AutoinstSection::UdevRuleSection] + def interface_for(config, udev_rule) + config.interfaces.find do |interface| + next unless interface.hardware + + hw_method = AutoinstProfile::UdevRuleSection::VALUE_MAPPING[udev_rule.mechanism] + interface.hardware.public_send(hw_method) == udev_rule.value + end + end + + # @param config [Config] + # @param target_interface [Interface] + # @param udev_rule [AutoinstSection::UdevRuleSection] + def rename_interface(config, target_interface, udev_rule) + solve_collision(config, target_interface, udev_rule) + + old_name = target_interface.name == udev_rule.name ? nil : target_interface.name + config.rename_interface(old_name, udev_rule.name, udev_rule.mechanism) + end + + # @param _config [Config] + # @param udev_rule [AutoinstSection::UdevRuleSection] + def create_interface(_config, udev_rule) + log.error "Cannot find interface to apply udev rule #{udev_rule.inspect}. Skipping ..." + end + + # @param config [Config] + # @param target_interface [Interface] + # @param udev_rule [AutoinstSection::UdevRuleSection] + def solve_collision(config, target_interface, udev_rule) + existing_interface = config.interfaces.by_name(udev_rule.name) + return unless existing_interface + return if existing_interface == target_interface + + prefix = existing_interface.name[/\A(.*[^\d])\d+\z/, 1] + new_name = config.interfaces.free_name(prefix) + + # TODO: what mechanism should be default? + config.rename_interface(existing_interface.name, new_name, :mac) + end + end + end +end diff --git a/src/lib/y2network/autoinst_profile/interface_section.rb b/src/lib/y2network/autoinst_profile/interface_section.rb new file mode 100644 index 000000000..423524d20 --- /dev/null +++ b/src/lib/y2network/autoinst_profile/interface_section.rb @@ -0,0 +1,379 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/autoinst_profile/section_with_attributes" + +module Y2Network + module AutoinstProfile + # This class represents an AutoYaST section under + # + # + # static + # 127.255.255.255 + # lo + # no + # 127.0.0.1 + # 255.0.0.0 + # 127.0.0.0 + # 8 + # nfsroot + # no + # + # + # @see InterfacesSection + class InterfaceSection < SectionWithAttributes + def self.attributes + [ + { name: :bootproto }, + { name: :broadcast }, + { name: :device }, + { name: :name }, # has precedence over device + { name: :ipaddr }, + { name: :remote_ipaddr }, + { name: :netmask }, + { name: :network }, # TODO: what it is? looks like ipaddr with applied prefix + { name: :prefixlen }, # has precedence over netmask + { name: :startmode }, + { name: :ifplugd_priority }, + { name: :usercontrol }, # no longer used, ignored + { name: :dhclient_set_hostname }, + { name: :bonding_master }, + { name: :bonding_slave0 }, + { name: :bonding_slave1 }, + { name: :bonding_slave2 }, + { name: :bonding_slave3 }, + { name: :bonding_slave4 }, + { name: :bonding_slave5 }, + { name: :bonding_slave6 }, + { name: :bonding_slave7 }, + { name: :bonding_slave8 }, + { name: :bonding_slave9 }, + { name: :bonding_module_opts }, + { name: :aliases }, + { name: :mtu }, + { name: :ethtool_options }, + { name: :wireless }, # TODO: what it is? + { name: :firewall }, # yes/no + { name: :zone }, # firewall zone + { name: :dhclient_set_down_link }, # TODO: what it do? + { name: :dhclient_set_default_route }, # TODO: what it do? + { name: :vlan_id }, + { name: :etherdevice }, + { name: :bridge }, # yes/no # why? bridge always have to be yes + { name: :bridge_ports }, + { name: :bridge_stp }, # on/off + { name: :bridge_forward_delay }, + { name: :wireless_ap }, + { name: :wireless_auth_mode }, + { name: :wireless_bitrate }, + { name: :wireless_ca_cert }, + { name: :wireless_channel }, + { name: :wireless_client_cert }, + { name: :wireless_client_key }, + { name: :wireless_client_key_password }, + { name: :wireless_default_key }, + { name: :wireless_eap_auth }, + { name: :wireless_eap_mode }, + { name: :wireless_essid }, + { name: :wireless_frequency }, + { name: :wireless_key }, # default wep key + { name: :wireless_key0 }, + { name: :wireless_key1 }, + { name: :wireless_key2 }, + { name: :wireless_key3 }, + { name: :wireless_key_length }, + { name: :wireless_mode }, + { name: :wireless_nick }, + { name: :wireless_nwid }, + { name: :wireless_peap_version }, + { name: :wireless_power }, + { name: :wireless_wpa_anonid }, + { name: :wireless_wpa_identity }, + { name: :wireless_wpa_password }, + { name: :wireless_wpa_psk } + ] + end + + define_attr_accessors + + # @!attribute bootproto + # @return [String] boot protocol + + # @!attribute broadcast + # @return [String] broadcast ip address. + + # @!attribute device + # @return [String] device name. Deprecated. `name` should be used instead. + + # @!attribute name + # @return [String] device name. + + # @!attribute ipaddr + # @return [String] ip address. + + # @!attribute remote_ipaddr + # @return [String] remote ip address for ptp connections. + + # @!attribute netmask + # @return [String] network mask. Deprecated `prefix` should be used instead. + + # @!attribute network + # @return [String] network ip after prefix applied. Deprecated as it can be computed from ipaddr and prefixlen. + + # @!attribute prefixlen + # @return [String] size of network prefix. + + # @!attribute startmode + # @return [String] when to start network. + + # @!attribute ifplugd_priority + # @return [String] priority for ifplugd startmode. + + # @!attribute usercontrol + # @return [String] no clue what it means, but it is ignored now. + + # @!attribute dhclient_set_hostname + # @return [String] if dhcp sets hostname. It is plain text, values? + + # @!attribute bonding_master + # @return [String] ??? + + # @!attribute bonding_slaveX + # @return [String] bonding slave on position X + + # @!attribute bonding_module_opts + # @return [String] bonding options + + # @!attribute aliases + # @example xml section for aliases from SLE15 + # + # + # 10.100.0.1 + # + # 255.255.255.0 + # 24 + # + # + # 10.100.0.2 + # + # 255.255.255.0 + # 24 + # + # + # + # @return [Object] aliases for interface + + # @!attribute mtu + # @return [String] MTU for interface + + # @!attribute ethtool_options + # @return [String] options for ethtool + + # @!attribute wireless + # @return [String] ??? + + # @!attribute firewall + # @return [String] ??? + + # @!attribute zone + # @return [String] firewall zone to which interface belongs + + # @!attribute dhclient_set_down_link + # @return [String] ??? + + # @!attribute dhclient_set_default_route + # @return [String] ??? + + # @!attribute vlan_id + # @return [String] id of vlan + + # @!attribute etherdevice + # @return [String] parent device of vlan + + # @!attribute bridge + # @return [String] "yes" if device is bridge + + # @!attribute bridge_ports + # @return [String] bridge ports separated by space + + # @!attribute bridge_stp + # @return [String] "on" if stp is enabled + + # @!attribute bridge_forward_delay + # @return [String] time of delay + + # @!attribute wireless_ap + # @!attribute wireless_auth_mode + # @!attribute wireless_bitrate + # @!attribute wireless_ca_cert + # @!attribute wireless_channel + # @!attribute wireless_client_cert + # @!attribute wireless_client_key + # @!attribute wireless_client_key_password + # @!attribute wireless_default_key + # @!attribute wireless_eap_auth + # @!attribute wireless_eap_mode + # @!attribute wireless_essid + # @!attribute wireless_frequency + # @!attribute wireless_key + # @!attribute wireless_keyX + # @return [String] key on position X + # @!attribute wireless_key_length + # @!attribute wireless_mode + # @!attribute wireless_nick + # @!attribute wireless_nwid + # @!attribute wireless_peap_version + # @!attribute wireless_power + # @!attribute wireless_wpa_anonid + # @!attribute wireless_wpa_identity + # @!attribute wireless_wpa_password + # @!attribute wireless_wpa_psk + + # Clones a network interface into an AutoYaST interface section + # + # @param connection_config [Y2Network::ConnectionConfig] Network connection config + # @return [InterfacesSection] + def self.new_from_network(connection_config) + result = new + result.init_from_config(connection_config) + result + end + + def initialize(*_args) + super + + self.class.attributes.each do |attr| + # init everything to empty string + public_send(:"#{attr[:name]}=", "") + end + + self.aliases = {} + end + + # Overwrite base method to load also nested aliases + def init_from_hashes(hash) + super + + self.aliases = hash["aliases"] if hash["aliases"] + end + + # Method used by {.new_from_network} to populate the attributes when cloning a network interface + # + # @param config [Y2Network::ConnectionConfig] + # @return [Boolean] + def init_from_config(config) + @bootproto = config.bootproto.name + @name = config.name + if config.bootproto == BootProtocol::STATIC && config.ip + @ipaddr = config.ip.address.to_s + @prefixlen = config.ip.address.address.to_s + @remote_ipaddr = config.ip.remote_address.address.to_s if config.ip.remote_address + @broadcast = config.ip.broadcast.address.to_s if config.ip.broadcast + end + + @startmode = config.startmode.name + @ifplugd_priority = config.startmode.priority.to_s if config.startmode.name == "ifplugd" + @mtu = config.mtu.to_s if config.mtu + @ethtool_options = config.ethtool_options if config.ethtool_options + @zone = config.firewall_zone.to_s + # see aliases for example output + @aliases = config.ip_aliases.each_with_index.each_with_object({}) do |(ip, index), res| + res["alias#{index}"] = { + "IPADDR" => ip.address.address.to_s, + "LABEL" => ip.label || "", + "PREFIXLEN" => ip.address.prefix.to_s + } + end + + case config + when ConnectionConfig::Vlan + @vlan_id = config.vlan_id.to_s + @etherdevice = config.parent_device + when ConnectionConfig::Bridge + @bridge = "yes" + @bridge_ports = config.ports.join(" ") + @bridge_stp = config.stp ? "on" : "off" + @bridge_forward_delay = config.forward_delay.to_s + when ConnectionConfig::Bonding + @bonding_module_opts = config.options + config.slaves.each_with_index do |slave, index| + public_send(:"bonding_slave#{index}=", slave) + end + when ConnectionConfig::Wireless + init_from_wireless(config) + end + + true + end + + # Helper to get wireless keys as array + # @return [Array] + def wireless_keys + keys = [] + (0..3).each do |i| + key = public_send(:"wireless_key#{i}") + keys << key unless key.empty? + end + + keys + end + + # Helper to get bonding slaves as array + # @return [Array] + def bonding_slaves + slaves = [] + + (0..9).each do |i| + slave = public_send(:"bonding_slave#{i}") + slaves << slave unless slave.empty? + end + + slaves + end + + private + + def init_from_wireless(config) + @wireless_mode = config.mode + @wireless_ap = config.ap + @wireless_bitrate = config.bitrate.to_s + @wireless_ca_cert = config.ca_cert + @wireless_channel = config.channel.to_s if config.channel + @wireless_client_cert = config.client_cert + @wireless_client_key = config.client_key + @wireless_essid = config.essid + @wireless_auth_mode = config.auth_mode.to_s + @wireless_nick = config.nick + @wireless_nwid = config.nwid + @wireless_wpa_anonid = config.wpa_anonymous_identity + @wireless_wpa_identity = config.wpa_identity + @wireless_wpa_password = config.wpa_password + @wireless_wpa_psk = config.wpa_psk + config.keys.each_with_index do |key, index| + public_send(:"wireless_key#{index}=", key) + end + @wireless_key = config.default_key.to_s + @wireless_key_length = config.key_length.to_s + # power dropped + # peap version not supported yet + # on other hand ap scan mode is not in autoyast + end + end + end +end diff --git a/src/lib/y2network/autoinst_profile/interfaces_section.rb b/src/lib/y2network/autoinst_profile/interfaces_section.rb new file mode 100644 index 000000000..b5b788802 --- /dev/null +++ b/src/lib/y2network/autoinst_profile/interfaces_section.rb @@ -0,0 +1,113 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/autoinst_profile/section_with_attributes" +require "y2network/autoinst_profile/interface_section" + +module Y2Network + module AutoinstProfile + # This class represents an AutoYaST section under + # + # + # + # dhcp + # eth0 + # auto + # + # + # static + # 127.255.255.255 + # lo + # no + # 127.0.0.1 + # 255.0.0.0 + # 127.0.0.0 + # 8 + # nfsroot + # no + # + # + # + # @see NetworkingSection + class InterfacesSection < SectionWithAttributes + include Yast::Logger + + def self.attributes + [ + { name: :interfaces } + ] + end + + define_attr_accessors + + # @!attribute interfaces + # @return [Array] + + # Clones network interfaces settings into an AutoYaST interfaces section + # + # @param config [Y2Network::Config] whole config as it need both interfaces and connection configs + # @return [InterfacesSection] + def self.new_from_network(config) + result = new + initialized = result.init_from_network(config) + initialized ? result : nil + end + + # Constructor + def initialize(*_args) + super + @interfaces = [] + end + + # Method used by {.new_from_hashes} to populate the attributes when importing a profile + # + # @param hash [Array] see {.new_from_hashes}. In this case it is array of interfaces + def init_from_hashes(hash) + @interfaces = interfaces_from_hash(hash) + end + + # Method used by {.new_from_network} to populate the attributes when cloning routing settings + # + # @param connection_configs [Y2Network::ConnectionConfigsCollection] Network settings + # @return [Boolean] Result true on success or false otherwise + def init_from_network(connection_configs) + @interfaces = interfaces_section(connection_configs) + true + end + + private + + # Returns an array of interfaces sections + # + # @param hash [Hash] Interfaces section hash + def interfaces_from_hash(hash) + hash.map do |h| + h = h["device"] if h["device"].is_a? ::Hash # hash can be enclosed in different hash + res = InterfaceSection.new_from_hashes(h) + log.info "interfaces section #{res.inspect} load from hash #{h.inspect}" + res + end + end + + def interfaces_section(connection_configs) + connection_configs.map { |c| Y2Network::AutoinstProfile::InterfaceSection.new_from_network(c) } + end + end + end +end diff --git a/src/lib/y2network/autoinst_profile/networking_section.rb b/src/lib/y2network/autoinst_profile/networking_section.rb index a05eaf892..dd19dc4b4 100644 --- a/src/lib/y2network/autoinst_profile/networking_section.rb +++ b/src/lib/y2network/autoinst_profile/networking_section.rb @@ -17,8 +17,10 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -require "y2network/autoinst_profile/routing_section" require "y2network/autoinst_profile/dns_section" +require "y2network/autoinst_profile/interfaces_section" +require "y2network/autoinst_profile/routing_section" +require "y2network/autoinst_profile/udev_rules_section" module Y2Network module AutoinstProfile @@ -36,6 +38,10 @@ class NetworkingSection attr_accessor :routing # @return [DNSSection] attr_accessor :dns + # @return [InterfacesSection] + attr_accessor :interfaces + # @return [UdevRulesSection] + attr_accessor :udev_rules # Creates an instance based on the profile representation used by the AutoYaST modules # (hash with nested hashes and arrays). @@ -46,6 +52,8 @@ def self.new_from_hashes(hash) result = new result.routing = RoutingSection.new_from_hashes(hash["routing"]) if hash["routing"] result.dns = DNSSection.new_from_hashes(hash["dns"]) if hash["dns"] + result.interfaces = InterfacesSection.new_from_hashes(hash["interfaces"]) if hash["interfaces"] + result.udev_rules = UdevRulesSection.new_from_hashes(hash["net-udev"]) if hash["net-udev"] result end @@ -58,6 +66,8 @@ def self.new_from_network(config) return result unless config result.routing = RoutingSection.new_from_network(config.routing) if config.routing result.dns = DNSSection.new_from_network(config.dns) if config.dns + result.interfaces = InterfacesSection.new_from_network(config.connections) + result.udev_rules = UdevRulesSection.new_from_network(config.interfaces) result end @@ -65,7 +75,12 @@ def self.new_from_network(config) # # @return [Hash] def to_hashes - { "routing" => routing.to_hashes } + { + "routing" => routing.to_hashes, + "dns" => dns.to_hashes, + "interfaces" => interfaces.to_hashes, + "net-udev" => udev_rules.to_hashes + } end end end diff --git a/src/lib/y2network/autoinst_profile/udev_rule_section.rb b/src/lib/y2network/autoinst_profile/udev_rule_section.rb new file mode 100644 index 000000000..cefbcfee2 --- /dev/null +++ b/src/lib/y2network/autoinst_profile/udev_rule_section.rb @@ -0,0 +1,106 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/autoinst_profile/section_with_attributes" + +module Y2Network + module AutoinstProfile + # This class represents an AutoYaST section under + # + # @example xml content + # + # eth0 + # ATTR\{address\} + # 00:30:6E:08:EC:80 + # + # + # @see InterfacesSection + class UdevRuleSection < SectionWithAttributes + include Yast::Logger + + def self.attributes + [ + { name: :rule }, + { name: :value }, + { name: :name } + ] + end + + define_attr_accessors + + # @!attribute rule + # @return [String] type of rule. Supported now is `ATTR\{address\}` and `KERNELS`. + # The first one is for MAC based rules and second for bus id based ones. + + # @!attribute value + # @return [String] mac or bus id value + + # @!attribute name + # @return [String] device name that should be used. + + # Clones a network interface into an AutoYaST udev rule section + # + # @param interface [Y2Network::Interface] + # @return [InterfacesSection, nil] Udev rule section or nil if udev naming is not implemented for interface + def self.new_from_network(interface) + return if interface.renaming_mechanism == :none + return unless interface.hardware + + new.tap { |r| r.init_from_config(interface) } + end + + def initialize(*_args) + super + + self.class.attributes.each do |attr| + # init everything to empty string + public_send(:"#{attr[:name]}=", "") + end + end + + # mapping of renaming_mechanism to rule string + RULE_MAPPING = { + mac: "ATTR{address}", + bus_id: "KERNELS" + }.freeze + + # mapping of renaming_mechanism to method to obtain value + VALUE_MAPPING = { + mac: :mac, + bus_id: :busid + }.freeze + + # Method used by {.new_from_network} to populate the attributes when cloning a udev rule + # + # @param interface [Y2Network::Interface] + def init_from_config(interface) + @name = interface.name + @rule = RULE_MAPPING[interface.renaming_mechanism] or + raise("invalid renaming mechanism #{interface.renaming_mechanism}") + @value = interface.hardware.public_send(VALUE_MAPPING[interface.renaming_mechanism]) + end + + # helper to get mechanism symbol from rule + # @return [Symbol] mechanism corresponding to {Interface#renaming_mechanism} + def mechanism + RULE_MAPPING.each_pair { |k, v| return k if v == rule } + end + end + end +end diff --git a/src/lib/y2network/autoinst_profile/udev_rules_section.rb b/src/lib/y2network/autoinst_profile/udev_rules_section.rb new file mode 100644 index 000000000..017c6a45a --- /dev/null +++ b/src/lib/y2network/autoinst_profile/udev_rules_section.rb @@ -0,0 +1,104 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/autoinst_profile/section_with_attributes" +require "y2network/autoinst_profile/udev_rule_section" + +module Y2Network + module AutoinstProfile + # This class represents an AutoYaST section under + # + # @example xml content + # + # + # eth0 + # ATTR\{address\} + # 00:30:6E:08:EC:80 + # + # + # + # @see NetworkingSection + class UdevRulesSection < SectionWithAttributes + include Yast::Logger + + def self.attributes + [ + { name: :udev_rules, xml_name: :"net-udev" } + ] + end + + define_attr_accessors + + # @!attribute udev_rules + # @return [Array] + + # Clones network interfaces settings into an AutoYaST interfaces section + # + # @param interfaces [Y2Network::InterfacesCollection] interfaces to detect udev rules + # @return [UdevRulesSection] + def self.new_from_network(interfaces) + new.tap { |r| r.init_from_network(interfaces) } + end + + # Constructor + def initialize(*_args) + super + @udev_rules = [] + end + + # Method used by {.new_from_hashes} to populate the attributes when importing a profile + # + # @param hash [Array] see {.new_from_hashes}. In this case it is array of udev_rules + def init_from_hashes(hash) + @udev_rules = udev_rules_from_hash(hash) + end + + # Method used by {.new_from_network} to populate the attributes when cloning udev rules settings + # + # @param interfaces [Y2Network::InterfacesCollection] Network settings + def init_from_network(interfaces) + @udev_rules = udev_rules_section(interfaces) + end + + private + + # Returns an array of udev rules sections + # + # @param hash [Hash] net-udev section hash + def udev_rules_from_hash(hash) + hash.map do |h| + res = UdevRuleSection.new_from_hashes(h) + log.info "udev rules section #{res.inspect} load from hash #{h.inspect}" + res + end + end + + # @param interfaces [Y2Network::InterfacesCollection] interfaces to detect udev rules + def udev_rules_section(interfaces) + result = interfaces + .map { |i| Y2Network::AutoinstProfile::UdevRuleSection.new_from_network(i) } + .compact + + log.info "udev rules for interfaces: #{interfaces.inspect} => #{result.inspect}" + + result + end + end + end +end diff --git a/src/lib/y2network/boot_protocol.rb b/src/lib/y2network/boot_protocol.rb new file mode 100644 index 000000000..d77ffd7d8 --- /dev/null +++ b/src/lib/y2network/boot_protocol.rb @@ -0,0 +1,97 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" + +module Y2Network + # This class represents the boot protocols which are supported (not all by all backends). + class BootProtocol + class << self + # Returns all the existing protocols + # + # @return [Array] + def all + @all ||= BootProtocol.constants + .map { |c| BootProtocol.const_get(c) } + .select { |c| c.is_a?(BootProtocol) } + end + + # Returns the boot protocol with a given name + # + # @param name [String] + # @return [BootProtocol,nil] Boot protocol or nil is not found + def from_name(name) + all.find { |t| t.name == name } + end + end + + # @return [String] Returns protocol name + attr_reader :name + + # Constructor + # + # @param name [String] protocol name + def initialize(name) + @name = name + end + + # checks if boot protocol is at least partially configured by dhcp + # + # @return [Boolean] + def dhcp? + [DHCP4, DHCP6, DHCP, DHCP_AUTOIP].include?(self) + end + + # Checks if boot protocol is static + # + # @return [Boolean] + def static? + STATIC == self + end + + # Determines whether two objects are equivalent + # + # They are equal when they refer to the same boot protocol (through the name). + # + # @param other [BootProtocol] Boot protocol to compare with + # @return [Boolean] + def ==(other) + name == other.name + end + + alias_method :eql?, :== + + # iBFT boot protocol + IBFT = new("ibft") + # statically assigned interface properties + STATIC = new("static") + # DHCP for both ipv4 and ipv6 + DHCP = new("dhcp") + # DHCP for ipv4 only + DHCP4 = new("dhcp4") + # DHCP for ipv6 only + DHCP6 = new("dhcp6") + # combination of zeroconf for ipv4 and DHCP for ipv6 + DHCP_AUTOIP = new("dhcp+autoip") + # zeroconf for ipv4 + AUTOIP = new("autoip") + # do not assign properties. Usefull for bond slave or bridge port + NONE = new("none") + end +end diff --git a/src/lib/y2network/can_be_copied.rb b/src/lib/y2network/can_be_copied.rb new file mode 100644 index 000000000..958932fd5 --- /dev/null +++ b/src/lib/y2network/can_be_copied.rb @@ -0,0 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +module Y2Network + # This module adds a #copy method. + module CanBeCopied + # Returns a deep-copy of the configuration + def copy + Marshal.load(Marshal.dump(self)) + end + end +end diff --git a/src/lib/y2network/config.rb b/src/lib/y2network/config.rb index 308f1ab4d..b628662e4 100644 --- a/src/lib/y2network/config.rb +++ b/src/lib/y2network/config.rb @@ -21,6 +21,9 @@ require "y2network/routing" require "y2network/dns" require "y2network/interfaces_collection" +require "y2network/connection_configs_collection" +require "y2network/physical_interface" +require "y2network/can_be_copied" module Y2Network # This class represents the current network configuration including interfaces, @@ -36,23 +39,28 @@ module Y2Network # config.routing.tables.first << route # config.write class Config + include CanBeCopied + include Yast::Logger + # @return [InterfacesCollection] attr_accessor :interfaces - # @return [Array] + # @return [ConnectionConfigsCollection] attr_accessor :connections # @return [Routing] Routing configuration attr_accessor :routing # @return [DNS] DNS configuration attr_accessor :dns + # @return [Array] Available drivers + attr_accessor :drivers # @return [Symbol] Information source (see {Y2Network::Reader} and {Y2Network::Writer}) attr_accessor :source class << self # @param source [Symbol] Source to read the configuration from - # @param opts [Hash] Reader options. Check readers documentation to find out - # supported options. - def from(source, opts = {}) - reader = ConfigReader.for(source, opts) + # @param opts [Array] Reader options. Check readers documentation to find out + # supported options. + def from(source, *opts) + reader = ConfigReader.for(source, *opts) reader.config end @@ -87,13 +95,17 @@ def configs # Constructor # - # @param interfaces [InterfacesCollection] List of interfaces - # @param routing [Routing] Object with routing configuration - # @param dns [DNS] Object with DNS configuration - # @param source [Symbol] Configuration source - def initialize(interfaces: InterfacesCollection.new, connections: [], routing: Routing.new, dns: DNS.new, source:) + # @param interfaces [InterfacesCollection] List of interfaces + # @param connections [ConnectionConfigsCollection] List of connection configurations + # @param routing [Routing] Object with routing configuration + # @param dns [DNS] Object with DNS configuration + # @param source [Symbol] Configuration source + # @param drivers [Array] List of available drivers + def initialize(interfaces: InterfacesCollection.new, connections: ConnectionConfigsCollection.new, + routing: Routing.new, dns: DNS.new, drivers: [], source:) @interfaces = interfaces @connections = connections + @drivers = drivers @routing = routing @dns = dns @source = source @@ -101,21 +113,16 @@ def initialize(interfaces: InterfacesCollection.new, connections: [], routing: R # Writes the configuration into the YaST modules # - # Writes only changes agains original configuration if the original configuration + # Writes only changes against original configuration if the original configuration # is provided # # @param original [Y2Network::Config] configuration used for detecting changes + # @param target [Symbol] Target to write the configuration to (:sysconfig) # # @see Y2Network::ConfigWriter - def write(original: nil) - Y2Network::ConfigWriter.for(source).write(self, original) - end - - # Returns a deep-copy of the configuration - # - # @return [Config] - def copy - Marshal.load(Marshal.dump(self)) + def write(original: nil, target: nil) + target ||= source + Y2Network::ConfigWriter.for(target).write(self, original) end # Determines whether two configurations are equal @@ -123,7 +130,79 @@ def copy # @return [Boolean] true if both configurations are equal; false otherwise def ==(other) source == other.source && interfaces == other.interfaces && - routing == other.routing && dns == other.dns + routing == other.routing && dns == other.dns && connections == other.connections + end + + # Renames a given interface and the associated connections + # + # @param old_name [String] Old interface's name + # @param new_name [String] New interface's name + # @param mechanism [Symbol] Property to base the rename on (:mac or :bus_id) + def rename_interface(old_name, new_name, mechanism) + log.info "Renaming #{old_name.inspect} to #{new_name.inspect} using #{mechanism.inspect}" + interface = interfaces.by_name(old_name || new_name) + interface.rename(new_name, mechanism) + return unless old_name # do not modify configurations if it is just renaming mechanism + connections.by_interface(old_name).each { |c| c.interface = new_name } + dns.dhcp_hostname = new_name if dns.dhcp_hostname == old_name + end + + # deletes interface and all its config. If interface is physical, + # it is not removed as we cannot remove physical interface. + # + # @param name [String] Interface's name + def delete_interface(name) + connections.reject! { |c| c.interface == name } + interface = interfaces.by_name(name) + return if interface.is_a?(PhysicalInterface) && interface.present? + + interfaces.reject! { |i| i.name == name } + end + + # Adds or update a connection config + # + # If the interface which is associated to does not exist (because it is a virtual one or it is + # not present), it gets added. + def add_or_update_connection_config(connection_config) + log.info "add_update connection config #{connection_config.inspect}" + connections.add_or_update(connection_config) + interface = interfaces.by_name(connection_config.interface) + return if interface + log.info "Creating new interface" + interfaces << Interface.from_connection(connection_config) + end + + # Returns the candidate drivers for a given interface + # + # @return [Array] + def drivers_for_interface(name) + interface = interfaces.by_name(name) + names = interface.drivers.map(&:name) + names << interface.custom_driver if interface.custom_driver && !names.include?(interface.custom_driver) + drivers.select { |d| names.include?(d.name) } + end + + # Adds or update a driver + # + # @param new_driver [Driver] Driver to add or update + def add_or_update_driver(new_driver) + idx = drivers.find_index { |d| d.name == new_driver.name } + if idx + drivers[idx] = new_driver + else + drivers << new_driver + end + end + + # Determines whether a given interface is configured or not + # + # An interface is considered as configured when it has an associated collection. + # + # @param iface_name [String] Interface's name + # @return [Boolean] + def configured_interface?(iface_name) + return false if iface_name.nil? || iface_name.empty? + !connections.by_interface(iface_name).empty? end alias_method :eql?, :== diff --git a/src/lib/y2network/config_reader.rb b/src/lib/y2network/config_reader.rb index 59328ca6b..81a2d9e53 100644 --- a/src/lib/y2network/config_reader.rb +++ b/src/lib/y2network/config_reader.rb @@ -21,13 +21,13 @@ module ConfigReader # Config reader for a given source # # @param source [Symbol] Source name (e.g., :sysconfig) - # @param opts [Hash] Reader options + # @param opts [Array] Reader options # @return [Y2Network::Autoinst::ConfigReader,Y2Network::Sysconfig::ConfigReader] - def self.for(source, opts = {}) + def self.for(source, *opts) require "y2network/#{source}/config_reader" modname = source.to_s.split("_").map(&:capitalize).join klass = Y2Network.const_get("#{modname}::ConfigReader") - klass.new(opts) + klass.new(*opts) end end end diff --git a/src/lib/y2network/connection_config.rb b/src/lib/y2network/connection_config.rb new file mode 100644 index 000000000..155611d2b --- /dev/null +++ b/src/lib/y2network/connection_config.rb @@ -0,0 +1,35 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" +require "y2network/connection_config/bonding" +require "y2network/connection_config/bridge" +require "y2network/connection_config/ctc" +require "y2network/connection_config/dummy" +require "y2network/connection_config/ethernet" +require "y2network/connection_config/hsi" +require "y2network/connection_config/infiniband" +require "y2network/connection_config/ip_config" +require "y2network/connection_config/lcs" +require "y2network/connection_config/qeth" +require "y2network/connection_config/tap" +require "y2network/connection_config/tun" +require "y2network/connection_config/usb" +require "y2network/connection_config/vlan" +require "y2network/connection_config/wireless" diff --git a/src/lib/y2network/connection_config/base.rb b/src/lib/y2network/connection_config/base.rb index 61efb4140..b24860b44 100644 --- a/src/lib/y2network/connection_config/base.rb +++ b/src/lib/y2network/connection_config/base.rb @@ -17,34 +17,128 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require "y2storage" require "y2network/interface_type" +require "y2network/boot_protocol" +require "y2network/startmode" module Y2Network module ConnectionConfig # This class is reponsible of a connection configuration # - # It holds a configuration (IP addresses, MTU, etc.) that can be applied to an interface. By - # comparison, it is the equivalent of the "Connection" concept in NetworkManager. When it comes - # to sysconfig, a "ConnectionConfig" is defined using a "ifcfg-*" file. + # It holds a configuration (IP addresses, MTU, WIFI settings, etc.) that can be applied to an + # interface. By comparison, it is the equivalent of the "Connection" concept in NetworkManager. + # When it comes to sysconfig, a "ConnectionConfig" is defined using a "ifcfg-*" file. + # + # Additionally, each connection config gets an internal ID which makes easier to track changes + # between two different {Y2Network::Config} objects. When they are copied, the same IDs are + # kept, so it is easy to find out which connections have been added, removed or simply changed. class Base # A connection could belongs to a specific interface or not. In case of # no specific interface then it could be activated by the first available # device. # - # #FIXME: Maybe it could be a matcher instead of an Interface, or just a - # the interface name by now. - # - # @return [Interface, nil] + # @return [String] Connection name + attr_accessor :name + + # @return [String, nil] Interface to apply the configuration to + # FIXME: Maybe in the future it could be a matcher. By now we will use + # the interface's name. attr_accessor :interface - # @return [String] Bootproto (static, dhcp, ,dhcp4, dhcp6, autoip, - # dhcp+autoip, auto6, 6to4, none) + + # @return [BootProtocol] Bootproto attr_accessor :bootproto - # @return [IPAddr,nil] - attr_accessor :ip_address - # @return [Array] - attr_accessor :secondary_ip_addresses + # @return [IPConfig] Primary IP configuration + attr_accessor :ip + # @return [Array] Additional IP configurations (also known as 'aliases') + attr_accessor :ip_aliases # @return [Integer, nil] attr_accessor :mtu + # @return [Startmode] + attr_accessor :startmode + # @return [String] Connection's description (e.g., "Ethernet Card 0") + attr_accessor :description + # @return [String] Link layer address + attr_accessor :lladdress + # @return [String] configuration for ethtools when initializing + attr_accessor :ethtool_options + # @return [String] assigned firewall zone to interface + attr_accessor :firewall_zone + # @return [String] interface's hostname + attr_accessor :hostname + + # @return [String] Connection identifier + attr_reader :id + + # @return [Integer] Connection identifier counter + @@last_id = 0 + + # Constructor + def initialize + @id = @@last_id += 1 + @ip_aliases = [] + @bootproto = BootProtocol::STATIC # TODO: maybe do test query if physical interface is attached? + @ip = IPConfig.new(IPAddress.from_string("0.0.0.0/32")) + @startmode = Startmode.create("manual") + @description = "" + @ethtool_options = "" + @firewall_zone = "" + end + + # Compares ConnectionConfigs + # + # @return [Boolean] true when both connections are same + # false otherwise + def ==(other) + return false if self.class != other.class + [:name, :interface, :bootproto, :ip, :ip_aliases, :mtu, :startmode, + :description, :lladdress, :ethtool_options, :firewall_zone, :hostname].all? do |method| + public_send(method) == other.public_send(method) + end + end + + alias_method :eql?, :== + + PROPOSED_PPPOE_MTU = 1492 # suggested value for PPPoE + + # Propose reasonable defaults for given config. Useful for newly created devices. + # @note difference between constructor and propose is that initialize should set simple defaults + # and propose have more tricky config that depends on env, product, etc. + def propose + propose_startmode + self.mtu = PROPOSED_PPPOE_MTU if Yast::Arch.s390 && (type.lcs? || type.ethernet?) + end + + def propose_startmode + Yast.import "ProductFeatures" + # see bsc#176804 + devicegraph = Y2Storage::StorageManager.instance.staging + if devicegraph.filesystem_in_network?("/") + @startmode = Startmode.create("nfsroot") + log.info "startmode nfsroot" + return + end + + product_startmode = Yast::ProductFeatures.GetStringFeature( + "network", + "startmode" + ) + + startmode = case product_startmode + when "ifplugd" + if replace_ifplugd? + hotplug_interface? ? "hotplug" : "auto" + else + product_startmode + end + when "auto" + "auto" + else + hotplug_interface? ? "hotplug" : "auto" + end + + @startmode = Startmode.create(startmode) + end # Returns the connection type # @@ -56,6 +150,57 @@ def type const_name = self.class.name.split("::").last.upcase InterfaceType.const_get(const_name) end + + # Whether a connection needs a virtual device associated or not. + # + # @return [Boolean] + def virtual? + false + end + + # Returns all IP configurations + # + # @return [Array] + def all_ips + ([ip] + ip_aliases).compact + end + + # find master from given collection of configs + # @param configs [ConnectionConfigsCollection] + # @return [ConnectionConfig::Bonding, ConnectionConfig::Bridge, nil] gets bridge, bonding or + # nil in which this device in enslaved + def find_master(configs) + configs.find do |config| + # TODO: what about VLAN? + if config.type.bonding? + config.slaves.include?(name) + elsif config.type.bridge? + config.ports.include?(name) + end + end + end + + private + + def replace_ifplugd? + Yast.import "Arch" + + return true if !Yast::Arch.is_laptop + # virtual devices cannot expect any event from ifplugd + return true if virtual? + + false + end + + def hotplug_interface? + # virtual interface is not hotplugable + return false if virtual? + # if interface is not there + return true unless interface + + false + # TODO: interface is just string so interface.hardware.hotplug does not work + end end end end diff --git a/src/lib/y2network/connection_config/bonding.rb b/src/lib/y2network/connection_config/bonding.rb index c744c8a68..095ae2aac 100644 --- a/src/lib/y2network/connection_config/bonding.rb +++ b/src/lib/y2network/connection_config/bonding.rb @@ -25,14 +25,25 @@ module ConnectionConfig # # @see https://www.kernel.org/doc/Documentation/networking/bonding.txt class Bonding < Base - # @return [Array] + # @return [Array] attr_accessor :slaves # @return [String] bond driver options attr_accessor :options def initialize + super() @slaves = [] - @options = "" + @options = "mode=active-backup miimon=100" + end + + def ==(other) + return false unless super + + options == other.options && ((slaves - other.slaves) + (other.slaves - slaves)).empty? + end + + def virtual? + true end end end diff --git a/src/lib/y2network/connection_config/bridge.rb b/src/lib/y2network/connection_config/bridge.rb new file mode 100644 index 000000000..9a484d904 --- /dev/null +++ b/src/lib/y2network/connection_config/bridge.rb @@ -0,0 +1,53 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for bridge connections + # + # @see https://www.kernel.org/doc/Documentation/networking/bridge.txt + class Bridge < Base + # @return [Array] + attr_accessor :ports + # @return [Boolean] whether spanning tree protocol is enabled or not + attr_accessor :stp + # @return [Integer] + attr_accessor :forward_delay + + def initialize + super() + @ports = [] + @stp = false + @forward_delay = 15 + end + + def virtual? + true + end + + def ==(other) + return false unless super + + stp == other.stp && forward_delay == other.forward_delay && ((ports - other.ports) + (other.ports - ports)).empty? + end + end + end +end diff --git a/src/lib/y2network/connection_config/ctc.rb b/src/lib/y2network/connection_config/ctc.rb new file mode 100644 index 000000000..a13e734b2 --- /dev/null +++ b/src/lib/y2network/connection_config/ctc.rb @@ -0,0 +1,71 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for ctc connections. + # + # @note The use of this connection is deprecated or not recommended as it + # will not be officially supported in future SLE versions. + class Ctc < Base + # Most I/O devices on a s390 system are typically driven through the + # channel I/O mechanism. + # + # The s390-tools provides a set of commands for working with CCW devices + # and CCW group devices, these commands use a device ID which is the + # device bus-ID + # + # The device bus-ID is of the format 0.., + # for example, 0.0.8000. + # + # @see https://www.ibm.com/developerworks/linux/linux390/documentation_suse.html + # + # The CTCM device driver requires two I/O subchannels for each interface, + # a read subchannel and a write subchannel + # + # @return [String] read device bus id + attr_accessor :read_channel + # @return [String] write device bus id + attr_accessor :write_channel + # @return [Integer] connection protocol (0, 1, 3, or 4) + # 0 Compatibility with peers other than OS/390®. (default) + # 1 Enhanced package checking for Linux peers. + # 3 For compatibility with OS/390 or z/OS peers. + # 4 For MPC connections to VTAM on traditional mainframe operating systems. + # @see https://www.ibm.com/support/knowledgecenter/en/linuxonibm/com.ibm.linux.z.ljdd/ljdd_t_ctcm_wrk_protocol.html + # @see https://github.com/SUSE/s390-tools/blob/master/ctc_configure#L16 + attr_accessor :protocol + + def initialize + super() + @protocol = 0 + end + + def ==(other) + return false unless super + + [:read_channel, :write_channel, :protocol].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/fake_interface.rb b/src/lib/y2network/connection_config/dummy.rb similarity index 63% rename from src/lib/y2network/fake_interface.rb rename to src/lib/y2network/connection_config/dummy.rb index db1a914a1..0f70ea990 100644 --- a/src/lib/y2network/fake_interface.rb +++ b/src/lib/y2network/connection_config/dummy.rb @@ -17,21 +17,14 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -require "y2network/interface" +require "y2network/connection_config/base" module Y2Network - # A physical interface that is not plugged in. - class FakeInterface < Interface - class << self - # Build connection - # - # @todo Would be possible to get the name from the connection? - # - # @param name [String] Interface name - # @param conn [ConnectionConfig] Connection configuration related to the - # network interface - def from_connection(name, conn) - new(name, type: conn.type) + module ConnectionConfig + # Configuration for dummy connections + class Dummy < Base + def virtual? + true end end end diff --git a/src/lib/y2network/connection_config/hsi.rb b/src/lib/y2network/connection_config/hsi.rb new file mode 100644 index 000000000..96d2afd9f --- /dev/null +++ b/src/lib/y2network/connection_config/hsi.rb @@ -0,0 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for hsi connections + class Hsi < Base + end + end +end diff --git a/src/lib/y2network/connection_config/infiniband.rb b/src/lib/y2network/connection_config/infiniband.rb new file mode 100644 index 000000000..d07791073 --- /dev/null +++ b/src/lib/y2network/connection_config/infiniband.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" +require "y2network/ipoib_mode" + +module Y2Network + module ConnectionConfig + # Configuration for infiniband connections + # + # @see https://www.kernel.org/doc/Documentation/infiniband/ipoib.txt + class Infiniband < Base + # @return [IpoibMode] transport mode + attr_accessor :ipoib_mode + + def initialize + super + + self.ipoib_mode = IpoibMode::DEFAULT + end + + def ==(other) + return false unless super + + [:ipoib_mode].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/connection_config/ip_config.rb b/src/lib/y2network/connection_config/ip_config.rb new file mode 100644 index 000000000..1b291a6a4 --- /dev/null +++ b/src/lib/y2network/connection_config/ip_config.rb @@ -0,0 +1,65 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/can_be_copied" + +module Y2Network + module ConnectionConfig + class IPConfig + include CanBeCopied + + # @return [IPAddress] IP address + attr_accessor :address + # @return [String,nil] Address label + attr_accessor :label + # @return [IPAddress,nil] Remote IP address of a point to point connection + attr_accessor :remote_address + # @return [IPAddress,nil] Broadcast address + attr_accessor :broadcast + # @return [String] ID (needed for sysconfig backend in order to write suffixes in + attr_accessor :id + + # Constructor + # + # @param address [IPAddress] + # @param id [String] ID (needed for sysconfig backend in order to write suffixes in + # ifcfg-* files) + # @param label [String,nil] + # @param remote_address [IPaddress,nil] + # @param broadcast [IPaddress,nil] + def initialize(address, id: "", label: nil, remote_address: nil, broadcast: nil) + @address = address + @id = id + @label = label + @remote_address = remote_address + @broadcast = broadcast + end + + # Determines whether IP configurations are equal + # + # @return [Boolean] true if both are equal; false otherwise + def ==(other) + return false if other.nil? + address == other.address && label == other.label && + remote_address == other.remote_address && broadcast == other.broadcast && + id == other.id + end + end + end +end diff --git a/src/lib/y2network/connection_config/lcs.rb b/src/lib/y2network/connection_config/lcs.rb new file mode 100644 index 000000000..691591296 --- /dev/null +++ b/src/lib/y2network/connection_config/lcs.rb @@ -0,0 +1,67 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for lcs connections + class Lcs < Base + # Most I/O devices on a s390 system are typically driven through the + # channel I/O mechanism. + # + # The s390-tools provides a set of commands for working with CCW devices + # and CCW group devices, these commands use a device ID which is the + # device bus-ID + # + # The device bus-ID is of the format 0.., + # for example, 0.0.8000. + # + # @see https://www.ibm.com/developerworks/linux/linux390/documentation_suse.html + # + # The LCS devices drivers requires two I/O subchannels for each interface, + # a read subchannel and a write subchannel and is very similar to the + # S390 CTC interface. + # + # @return [String] read device bus id + attr_accessor :read_channel + # @return [String] write device bus id + attr_accessor :write_channel + # The time the driver wait for a reply issuing a LAN command. + # + # @return [Integer] lcs lancmd timeout (default 5s) + # @see https://www.ibm.com/support/knowledgecenter/en/linuxonibm/com.ibm.linux.z.ljdd/ljdd_t_lcs_wrk_timeout.html + attr_accessor :timeout + + # Constructor + def initialize + super() + @timeout = 5 + end + + def ==(other) + return false unless super + + [:read_channel, :write_channel, :protocol, :timeout].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/connection_config/qeth.rb b/src/lib/y2network/connection_config/qeth.rb new file mode 100644 index 000000000..eae90618d --- /dev/null +++ b/src/lib/y2network/connection_config/qeth.rb @@ -0,0 +1,75 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for qeth connections + class Qeth < Base + # For CCW devices and CCW group devices, this device ID is the device + # bus-ID. + # + # The device bus-ID is of the format 0.., + # for example, 0.0.8000. + # + # The qeth device driver requires three I/O subchannels for each + # HiperSockets CHPID or OSA-ExpressCHPID in QDIO mode. + # + # One subchannel is for control reads, one for control writes, and the + # third is for data. + # + # @example enable a qeth interface (persistently) + # chzdev --enable qeth 0.0.a000:0.0.a001:0.0.a002 + # + # @see https://www.ibm.com/developerworks/linux/linux390/index.html + # @return [String] read bus id + attr_accessor :read_channel + # @return [String] write bus id + attr_accessor :write_channel + # @return [String] data bus id + attr_accessor :data_channel + # @return [Boolean] whether layer2 is enabled or not + attr_accessor :layer2 + # @return [Boolean] whether ipa takeover is enabled or not + attr_accessor :ipa_takeover + # @return [Integer] port number (0 or 1) + attr_accessor :port_number + # @return [String] configuration extra attributes + attr_accessor :attributes + + # Constructor + def initialize + super() + @layer2 = false + @port_number = 0 + @ipa_takeover = false + end + + def ==(other) + return false unless super + + [:read_channel, :write_channel, :data_channel, :layer2, + :port_number, :ipa_takeover, :attributes].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/connection_config/tap.rb b/src/lib/y2network/connection_config/tap.rb new file mode 100644 index 000000000..cc25fffa3 --- /dev/null +++ b/src/lib/y2network/connection_config/tap.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for TAP connections + class Tap < Base + # @return [String] tunnel owner (name or UID) + attr_accessor :owner + # @return [String] tunnel group (name or GID) + attr_accessor :group + + def initialize + super() + @owner = "" + @group = "" + end + + def virtual? + true + end + + def ==(other) + return false unless super + + [:owner, :group].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/connection_config/tun.rb b/src/lib/y2network/connection_config/tun.rb new file mode 100644 index 000000000..5b41fdc6b --- /dev/null +++ b/src/lib/y2network/connection_config/tun.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for TUN connections + class Tun < Base + # @return [String] tunnel owner (name or UID) + attr_accessor :owner + # @return [String] tunnel group (name or GID) + attr_accessor :group + + def initialize + super() + @owner = "" + @group = "" + end + + def virtual? + true + end + + def ==(other) + return false unless super + + [:owner, :group].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/connection_config/usb.rb b/src/lib/y2network/connection_config/usb.rb new file mode 100644 index 000000000..504099102 --- /dev/null +++ b/src/lib/y2network/connection_config/usb.rb @@ -0,0 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for Usb connections + class Usb < Base + end + end +end diff --git a/src/lib/y2network/connection_config/vlan.rb b/src/lib/y2network/connection_config/vlan.rb new file mode 100644 index 000000000..9a4bad41b --- /dev/null +++ b/src/lib/y2network/connection_config/vlan.rb @@ -0,0 +1,49 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/base" + +module Y2Network + module ConnectionConfig + # Configuration for vlan connections + class Vlan < Base + # FIXME: By now it will be just the interface name although in NM it + # could be a ifname, UUID or even a MAC address. + # TODO: consider using Interface instead of plain string? + # + # @return [String] the real interface associated with the vlan + attr_accessor :parent_device + # @return [Integer, nil] + attr_accessor :vlan_id + + # @see Y2Network::ConnectionConfig::Base#virtual? + def virtual? + true + end + + def ==(other) + return false unless super + + [:parent_device, :vlan_id].all? do |method| + public_send(method) == other.public_send(method) + end + end + end + end +end diff --git a/src/lib/y2network/connection_config/wireless.rb b/src/lib/y2network/connection_config/wireless.rb index be27f49e5..c899d809b 100644 --- a/src/lib/y2network/connection_config/wireless.rb +++ b/src/lib/y2network/connection_config/wireless.rb @@ -44,26 +44,63 @@ class Wireless < Base # @return [Integer] default WEP key attr_accessor :default_key # @return [String] - attr_accessor :nick + attr_accessor :nick # TODO: what it is? identity? # @return [String] attr_accessor :eap_mode # @return [String] attr_accessor :eap_auth - # @return [Integer] + # @return [Integer, nil] attr_accessor :channel # @return [Integer] attr_accessor :frequency - # @return [Integer] + # @return [Float, nil] bitrate limitation in Mb/s or nil for automatic attr_accessor :bitrate # @return [String] attr_accessor :ap - # @return [Boolean] - attr_accessor :power # FIXME: Consider an enum # @return [Integer] (0, 1, 2) attr_accessor :ap_scanmode # @return [String] - attr_accessor :wpa_password + attr_accessor :wpa_password # TODO unify psk and password and write correct one depending on mode + # @return [String] + attr_accessor :wpa_identity + # @return [String] initial identity used for creating tunnel + attr_accessor :wpa_anonymous_identity + # @return [String] ca certificate used to sign server certificate + attr_accessor :ca_cert + # @return [String] client certificate used to login for TLS + attr_accessor :client_cert + # @return [String] client private key used to encrypt for TLS + attr_accessor :client_key + + def initialize + super + + self.mode = "Managed" + self.essid = "" + self.nwid = "" + self.auth_mode = :open + self.wpa_psk = "" + self.key_length = 128 + self.keys = [] + self.default_key = 0 + self.eap_mode = "PEAP" + self.eap_auth = "MSCHAPV2" + self.ap_scanmode = 1 + # For WIFI DHCP makes more sense as majority of wifi routers act as dhcp servers + self.bootproto = BootProtocol::DHCP + end + + def ==(other) + return false unless super + + [:mode, :essid, :nwid, :auth_mode, :wpa_psk, :key_length, :keys, :default_key, :nick, + :eap_mode, :eap_auth, :channel, :frequency, :bitrate, :ap, :ap_scanmode, + :wpa_password, :wpa_identity, :wpa_anonymous_identity, :ca_cert, :client_cert, + :client_key].all? do |method| + public_send(method) == other.public_send(method) + end + end end end end diff --git a/src/lib/y2network/connection_configs_collection.rb b/src/lib/y2network/connection_configs_collection.rb new file mode 100644 index 000000000..e4506d7fe --- /dev/null +++ b/src/lib/y2network/connection_configs_collection.rb @@ -0,0 +1,115 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "forwardable" + +module Y2Network + # A container for connection configurations objects. + # + # @example Create a new collection + # eth0 = Y2Network::ConnectionConfig::Ethernet.new + # collection = Y2Network::ConnectionConfigsCollection.new([eth0]) + # + # @example Find a connection config using its name + # config = collection.by_name("eth0") #=> # + class ConnectionConfigsCollection + extend Forwardable + include Yast::Logger + + attr_reader :connection_configs + alias_method :to_a, :connection_configs + + def_delegators :@connection_configs, :each, :find, :push, :<<, :reject!, :map, :flat_map, + :any?, :size, :first + + # Constructor + # + # @param connection_configs [Array] List of connection configurations + def initialize(connection_configs = []) + @connection_configs = connection_configs + end + + # Returns a connection configuration with the given name if present + # + # @param name [String] Connection name + # @return [ConnectionConfig::Base,nil] Connection config with the given name or nil if not found + def by_name(name) + connection_configs.find { |c| c.name == name } + end + + # Returns connection configurations which are associated to the given interface + # + # @param interface_name [String] Interface name + # @return [Array] Associated connection configs + def by_interface(interface_name) + connection_configs.select { |c| c.interface == interface_name } + end + + # Returns connections with any of the given internal IDs + # + # @param ids [Array] Internal IDs + # @return [Array] Connection config with the given IDs + def by_ids(*ids) + select { |c| ids.include?(c.id) } + end + + # Adds or updates a connection configuration + # + # @note It uses the name to do the matching. + # + # @param connection_config [ConnectionConfig::Base] New connection configuration object + def add_or_update(connection_config) + idx = connection_configs.find_index { |c| c.name == connection_config.name } + if idx + connection_configs[idx] = connection_config + else + connection_configs << connection_config + end + end + + # Removes a connection configuration + # + # @note It uses the name to do the matching. + # + # @param connection_config [ConnectionConfig::Base,String] Connection configuration object or name + def remove(connection_config) + name = connection_config.respond_to?(:name) ? connection_config.name : connection_config + connection_configs.reject! { |c| c.name == name } + end + + # Selects connections which satisfy the given +block+ + # + # @param block [Proc] + # @return [ConnectionConfigsCollection] Collection including the selected connections + def select(&block) + self.class.new(to_a.select(&block)) + end + + # Compares ConnectionConfigsCollection + # + # @return [Boolean] true when both collections contain only equal connections, + # false otherwise + def ==(other) + ((connection_configs - other.connection_configs) + (other.connection_configs - connection_configs)).empty? + end + + alias_method :eql?, :== + end +end diff --git a/src/lib/y2network/dialogs/add_interface.rb b/src/lib/y2network/dialogs/add_interface.rb index 7de237c8f..c76b45cd0 100644 --- a/src/lib/y2network/dialogs/add_interface.rb +++ b/src/lib/y2network/dialogs/add_interface.rb @@ -1,8 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/dialog" require "y2network/widgets/interface_type" require "y2network/interface_config_builder" Yast.import "Label" +Yast.import "Lan" Yast.import "LanItems" Yast.import "NetworkInterfaces" @@ -11,7 +31,7 @@ module Dialogs # Dialog which starts new device creation class AddInterface < CWM::Dialog def initialize(default: nil) - @type_widget = Widgets::InterfaceType.new(default: default) + @type_widget = Widgets::InterfaceType.new(default: default ? default.short_name : nil) end def contents @@ -22,11 +42,6 @@ def contents # initialize legacy stuff, that should be removed soon def legacy_init - # FIXME: can be mostly deleted - Yast::LanItems.AddNew - # FIXME: can be partly deleted and partly moved - Yast::Lan.Add - # FIXME: This is for backward compatibility only # dhclient needs to set just one dhcp enabled interface to # DHCLIENT_SET_DEFAULT_ROUTE=yes. Otherwise interface is selected more @@ -46,27 +61,22 @@ def run log.info "AddInterface result #{ret}" ret = :back if ret == :abort - # TODO: replace with builder initialization - if ret == :back - Yast::LanItems.Rollback - return nil - end + return if ret == :back # TODO: use factory to get proper builder - builder = InterfaceConfigBuilder.for(@type_widget.result) - proposed_name = Yast::LanItems.new_type_devices(@type_widget.result, 1).first + builder = InterfaceConfigBuilder.for(InterfaceType.from_short_name(@type_widget.result)) + proposed_name = Yast::Lan.yast_config.interfaces.free_name(@type_widget.result) builder.name = proposed_name - Yast::NetworkInterfaces.Name = proposed_name - Yast::LanItems.Items[Yast::LanItems.current]["ifcfg"] = proposed_name - Yast::LanItems.Items[Yast::LanItems.current]["udev"] = {} builder end + # no back button for add dialog def back_button "" end + # as it is a sub dialog it can only cancel and cannot abort def abort_button Yast::Label.CancelButton end diff --git a/src/lib/y2network/dialogs/edit_interface.rb b/src/lib/y2network/dialogs/edit_interface.rb index 25626074a..ed208ac0f 100644 --- a/src/lib/y2network/dialogs/edit_interface.rb +++ b/src/lib/y2network/dialogs/edit_interface.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/dialog" require "y2network/widgets/address_tab.rb" @@ -5,16 +24,18 @@ require "y2network/widgets/bridge_slaves_tab.rb" require "y2network/widgets/general_tab.rb" require "y2network/widgets/hardware_tab.rb" +require "y2network/widgets/wireless_tab.rb" Yast.import "LanItems" Yast.import "Label" module Y2Network module Dialogs - # Dialog to Edit Interface. + # Dialog to Edit Interface. Content of the dialog heavily depends on the interface type and + # change of type is not allowed after dialog creation. class EditInterface < CWM::Dialog # @param settings [InterfaceBuilder] object holding interface configuration - # modified by dialog. + # modified by the dialog. def initialize(settings) @settings = settings @@ -26,20 +47,24 @@ def title end def contents - # if there is addr, make it initial + # if there is addr, make it initial unless for wifi, where first one should be wifi specific + # configs addr_tab = Widgets::AddressTab.new(@settings) - addr_tab.initial = true + addr_tab.initial = true unless @settings.type.wireless? - tabs = case @settings.type - when "vlan" + tabs = case @settings.type.short_name + when "vlan", "dummy" [Widgets::GeneralTab.new(@settings), addr_tab] when "tun", "tap" [addr_tab] when "br" [Widgets::GeneralTab.new(@settings), addr_tab, Widgets::BridgePorts.new(@settings)] when "bond" - [Widgets::GeneralTab.new(@settings), addr_tab, Widgets::HardwareTab.new(@settings), - Widgets::BondSlavesTab.new(@settings)] + [Widgets::GeneralTab.new(@settings), addr_tab, Widgets::BondSlavesTab.new(@settings)] + when "wlan" + wireless = Widgets::WirelessTab.new(@settings) + wireless.initial = true + [Widgets::GeneralTab.new(@settings), wireless, addr_tab, Widgets::HardwareTab.new(@settings)] else [Widgets::GeneralTab.new(@settings), addr_tab, Widgets::HardwareTab.new(@settings)] end @@ -47,12 +72,14 @@ def contents VBox(CWM::Tabs.new(*tabs)) end + # abort is just cancel as this is a sub dialog def abort_button Yast::Label.CancelButton end + # removes back button when editing device, but keep it when this dialog follows adding + # new interface def back_button - # TODO: decide operation based on builder @settings.newly_added? ? Yast::Label.BackButton : "" end end diff --git a/src/lib/y2network/dialogs/rename_interface.rb b/src/lib/y2network/dialogs/rename_interface.rb new file mode 100644 index 000000000..566985bab --- /dev/null +++ b/src/lib/y2network/dialogs/rename_interface.rb @@ -0,0 +1,68 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/popup" +require "y2network/widgets/interface_name" +require "y2network/widgets/renaming_mechanism" +require "y2network/virtual_interface" + +module Y2Network + module Dialogs + # This dialog allows the user to rename a network interface + # + # It allows the user to enter a new name and to select the attribute to + # base the rename on. Supported attributes are MAC address and Bus ID. + class RenameInterface < CWM::Popup + # Constructor + # + # @param builder [Y2Network::InterfaceConfigBuilder] Interface configuration builder object + def initialize(builder) + textdomain "network" + + @builder = builder + @old_name = builder.interface.name + end + + # @see CWM::CustomWidget + def contents + VBox( + Left(name_widget), + VSpacing(0.5), + rename_hwinfo_widget + ) + end + + private + + # Interface name widget + # + # @return [Y2Network::Widgets::InterfaceName] + def name_widget + @name_widget ||= Y2Network::Widgets::InterfaceName.new(@builder) + end + + # Widget to select the hardware information to base the rename on + # + # @return [Y2Network::Widgets::RenamingMechanism] + def rename_hwinfo_widget + @rename_hwinfo_widget ||= Y2Network::Widgets::RenamingMechanism.new(@builder) + end + end + end +end diff --git a/src/lib/y2network/dialogs/route.rb b/src/lib/y2network/dialogs/route.rb index 76edc2ceb..277475099 100644 --- a/src/lib/y2network/dialogs/route.rb +++ b/src/lib/y2network/dialogs/route.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "ipaddr" require "cwm/popup" diff --git a/src/lib/y2network/dialogs/s390_ctc_activation.rb b/src/lib/y2network/dialogs/s390_ctc_activation.rb new file mode 100644 index 000000000..36a2cc15d --- /dev/null +++ b/src/lib/y2network/dialogs/s390_ctc_activation.rb @@ -0,0 +1,71 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/dialogs/s390_device_activation" + +module Y2Network + module Dialogs + # Dialog for activating a CTC device + class S390CtcActivation < S390DeviceActivation + def contents + # Already defined in the base class but added here just because of the + # pot check + textdomain "network" + + HBox( + HSpacing(6), + # Frame label + Frame( + _("S/390 Device Settings"), + HBox( + HSpacing(2), + VBox( + VSpacing(1), + # TextEntry label + protocol_widget, + VSpacing(1), + HBox( + read_channel_widget, + HSpacing(1), + write_channel_widget + ) + ), + HSpacing(2) + ) + ), + HSpacing(6) + ) + end + + private + + def protocol_widget + Y2Network::Widgets::S390Protocol.new(builder) + end + + def read_channel_widget + Y2Network::Widgets::S390ReadChannel.new(builder) + end + + def write_channel_widget + Y2Network::Widgets::S390WriteChannel.new(builder) + end + end + end +end diff --git a/src/lib/y2network/dialogs/s390_device_activation.rb b/src/lib/y2network/dialogs/s390_device_activation.rb new file mode 100644 index 000000000..85dd6c0cd --- /dev/null +++ b/src/lib/y2network/dialogs/s390_device_activation.rb @@ -0,0 +1,104 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/dialog" +require "y2network/s390_device_activator" +require "y2network/widgets/s390_common" +require "y2network/widgets/s390_channels" + +module Y2Network + module Dialogs + # Base class dialog for activating S390 devices + class S390DeviceActivation < CWM::Dialog + # @param builder [Y2Network::InterfaceConfigBuilder] + # @return [S390DeviceActivation, nil] + def self.for(builder) + return nil unless builder.type + case builder.type.short_name + # Both interfaces uses the qeth driver and uses the same configuration + # for activating the group device. + when "qeth", "hsi" + require "y2network/dialogs/s390_qeth_activation" + require "y2network/s390_device_activators/qeth" + activator = S390DeviceActivators::Qeth.new(builder) + Y2Network::Dialogs::S390QethActivation.new(activator) + when "ctc" + require "y2network/dialogs/s390_ctc_activation" + require "y2network/s390_device_activators/ctc" + activator = S390DeviceActivators::Ctc.new(builder) + Y2Network::Dialogs::S390CtcActivation.new(activator) + when "lcs" + require "y2network/dialogs/s390_lcs_activation" + require "y2network/s390_device_activators/lcs" + activator = S390DeviceActivators::Lcs.new(builder) + Y2Network::Dialogs::S390LcsActivation.new(activator) + end + end + + attr_reader :builder + attr_reader :activator + + # Constructor + # + # @param activator [Y2Network::S390DeviceActivator] + def initialize(activator) + textdomain "network" + + @activator = activator + @activator.propose! + @builder = activator.builder + end + + def title + _("S/390 Network Card Configuration") + end + + def contents + Empty() + end + + def run + ret = super + if ret == :next + configured = activator.configure + builder.name = activator.configured_interface if configured + # TODO: Refresh the list of interfaces in yast_config. Take into + # account that the interface in yast_config does not have a name so + # the builder.interface is probably nil and should be obtained + # through the busid. + if !configured || builder.name.empty? + Yast::Popup.Error( + _( + "An error occurred while creating device.\nSee YaST log for details." + ) + ) + + ret = nil + end + end + + ret + end + + def abort_handler + Yast::Popup.ReallyAbort(true) + end + end + end +end diff --git a/src/lib/y2network/dialogs/s390_lcs_activation.rb b/src/lib/y2network/dialogs/s390_lcs_activation.rb new file mode 100644 index 000000000..7cf217862 --- /dev/null +++ b/src/lib/y2network/dialogs/s390_lcs_activation.rb @@ -0,0 +1,73 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/dialogs/s390_device_activation" + +module Y2Network + module Dialogs + # Dialog for activating a LCS s390 device + class S390LcsActivation < S390DeviceActivation + def contents + textdomain "network" + + HBox( + HSpacing(6), + # Frame label + Frame( + _("S/390 Device Settings"), + HBox( + HSpacing(2), + VBox( + VSpacing(1), + # TextEntry label + protocol_widget, + VSpacing(1), + HBox( + read_channel_widget, + HSpacing(1), + write_channel_widget + ) + ), + HSpacing(2) + ) + ), + HSpacing(6) + ) + end + + private + + def protocol_widget + Y2Network::Widgets::S390Protocol.new(builder) + end + + def read_channel_widget + Y2Network::Widgets::S390ReadChannel.new(builder) + end + + def write_channel_widget + Y2Network::Widgets::S390WriteChannel.new(builder) + end + + def timeout_widget + Y2network::Widgets::S390LanCmdTimeout.new(builder) + end + end + end +end diff --git a/src/lib/y2network/dialogs/s390_qeth_activation.rb b/src/lib/y2network/dialogs/s390_qeth_activation.rb new file mode 100644 index 000000000..35c042845 --- /dev/null +++ b/src/lib/y2network/dialogs/s390_qeth_activation.rb @@ -0,0 +1,79 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/dialogs/s390_device_activation" + +module Y2Network + module Dialogs + # Dialog for activating a QETH device + class S390QethActivation < S390DeviceActivation + def contents + textdomain "network" + + HBox( + HSpacing(6), + Frame( + _("S/390 Device Settings"), + HBox( + HSpacing(2), + VBox( + VSpacing(1), + HBox( + s390_port_number, + HSpacing(1), + s390_attributes + ), + VSpacing(1), + Left(s390_ip_takeover), + VSpacing(1), + Left(s390_layer2), + VSpacing(1), + s390_channels + ), + HSpacing(2) + ) + ), + HSpacing(6) + ) + end + + private + + def s390_port_number + Y2Network::Widgets::S390PortNumber.new(builder) + end + + def s390_attributes + Y2Network::Widgets::S390Attributes.new(builder) + end + + def s390_ip_takeover + Y2Network::Widgets::S390IPAddressTakeover.new(builder) + end + + def s390_channels + Y2Network::Widgets::S390Channels.new(builder) + end + + def s390_layer2 + Y2Network::Widgets::S390Layer2.new(builder) + end + end + end +end diff --git a/src/lib/y2network/dialogs/wireless_expert_settings.rb b/src/lib/y2network/dialogs/wireless_expert_settings.rb new file mode 100644 index 000000000..247dc2fe8 --- /dev/null +++ b/src/lib/y2network/dialogs/wireless_expert_settings.rb @@ -0,0 +1,126 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/dialog" +require "y2network/widgets/wireless" +require "y2network/widgets/wireless_expert" + +module Y2Network + module Dialogs + # Dialog that shows when expert button is clicked on wireless tab. + class WirelessExpertSettings < CWM::Dialog + # @param settings [InterfaceConfigBuilder] object holding interface configuration + # modified by the dialog. + def initialize(settings) + @settings = settings + + textdomain "network" + end + + def title + _("Wireless Expert Settings") + end + + def contents + HBox( + HSpacing(4), + VBox( + VSpacing(0.5), + Frame( + _("Wireless Expert Settings"), + HBox( + HSpacing(2), + VBox( + VSpacing(1), + channel_widget, # TODO: channel only when mode is master or adhoc + VSpacing(0.2), + bitrate_widget, + VSpacing(0.2), + access_point_widget, # TODO: Access point only in managed mode + VSpacing(0.2), + Left(ap_scan_mode_widget), # TODO: AP scan mode only in managed mode + VSpacing(1) + ), + HSpacing(2) + ) + ), + VSpacing(0.5) + ), + HSpacing(4) + ) + end + + def help + # Wireless expert dialog help 1/5 + _( + "

Here, set additional configuration parameters\n(rarely needed).

" + ) + + # Wireless expert dialog help 2/5 + _( + "

To use your wireless LAN card in master or ad-hoc mode,\n" \ + "set the Channel the card should use here. This is not needed\n" \ + "for managed mode--the card will hop through the channels searching for access\n" \ + "points in that case.

\n" + ) + + # Wireless expert dialog help 3/5 + _( + "

In some rare cases, you may want to set a transmission\nBit Rate explicitly. The default is to go as fast as possible.

" + ) + + # Wireless expert dialog help 4/5 + _( + "

In an environment with multiple Access Points, you may want to\ndefine the one to which to connect by entering its MAC address.

" + ) + + # Wireless expert dialog help 5/5 + _( + "

Use Power Management enables power saving mechanisms.\n" \ + "This is generally a good idea, especially if you are a laptop user and may\n" \ + "be disconnected from AC power.

\n" + ) + end + + # Always open new dialog to work properly in sequence + def should_open_dialog? + true + end + + private + + def channel_widget + @channel_widget ||= Y2Network::Widgets::WirelessChannel.new(@settings) + end + + def bitrate_widget + @bitrate_widget ||= Y2Network::Widgets::WirelessBitRate.new(@settings) + end + + def access_point_widget + @access_point_widget ||= Y2Network::Widgets::WirelessAccessPoint.new(@settings) + end + + def power_management_widget + @access_power_widget ||= Y2Network::Widgets::WirelessPowerManagement.new(@settings) + end + + def ap_scan_mode_widget + @ap_scan_mode_widget ||= Y2Network::Widgets::WirelessAPScanMode.new(@settings) + end + end + end +end diff --git a/src/lib/y2network/dialogs/wireless_wep_keys.rb b/src/lib/y2network/dialogs/wireless_wep_keys.rb new file mode 100644 index 000000000..4b16c56f7 --- /dev/null +++ b/src/lib/y2network/dialogs/wireless_wep_keys.rb @@ -0,0 +1,286 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" + +require "cwm/dialog" +require "cwm/custom_widget" +require "cwm/common_widgets" + +Yast.import "Label" + +module Y2Network + module Dialogs + # Dialog to manage WEP keys + class WirelessWepKeys < CWM::Dialog + # @param builder [InterfaceConfigBuilder] + def initialize(builder) + textdomain "network" + @builder = builder + end + + def title + _("Wireless Keys") + end + + def help + _( + "

In this dialog, define your WEP keys used\n" \ + "to encrypt your data before it is transmitted. You can have up to four keys,\n" \ + "although only one key is used to encrypt the data. This is the default key.\n" \ + "The other keys can be used to decrypt data. Usually you have only\n" \ + "one key.

" + ) + end + + def contents + HBox( + HSpacing(5), + VBox( + VSpacing(1), + # Frame label + Frame( + _("WEP Keys"), + HBox( + HSpacing(3), + VBox( + VSpacing(1), + # ComboBox label + Left(WEPKeyLength.new(@builder)), + VSpacing(1), + WEPKeys.new(@builder), + VSpacing(1) + ), + HSpacing(3) + ) + ), + VSpacing(1) + ), + HSpacing(5) + ) + end + + # Always open new dialog to work properly in sequence + def should_open_dialog? + true + end + + # Omits abort + def abort_button + "" + end + + # Omits back button, only let OK be. So it do directly modification + def back_button + "" + end + + def next_button + Yast::Label.OKButton + end + + class WEPKeyLength < CWM::ComboBox + def initialize(builder) + textdomain "network" + + @builder = builder + end + + ITEMS = [ + ["64", "64"], + ["128", "128"] + ].freeze + + def items + ITEMS + end + + def init + length_s = @builder.key_length.to_s + self.value = length_s.empty? ? "128" : length_s + end + + def store + @builder.key_length = value.to_i + end + + def label + _("&Key Length") + end + + def help + _( + "

Key Length defines the bit length of your WEP keys.\n" \ + "Possible are 64 and 128 bit, sometimes also referred to as 40 and 104 bit.\n" \ + "Some older hardware might not be able to handle 128 bit keys, so if your\n" \ + "wireless LAN connection does not establish, you may need to set this\n" \ + "value to 64.

" + ) + end + end + + class WEPKeys < CWM::CustomWidget + def initialize(settings) + textdomain "network" + @settings = settings + end + + def contents + VBox( + Table( + Id(:wep_keys_table), + Header( + # Table header label + # Abbreviation of Number + _("No."), + # Table header label + _("Key"), + # Table header label + Center(_("Default")) + ) + ), + HBox( + # PushButton label + PushButton(Id(:wep_keys_add), Yast::Label.AddButton), + # PushButton label + PushButton(Id(:wep_keys_edit), Yast::Label.EditButton), + # PushButton label + PushButton(Id(:wep_keys_delete), Yast::Label.DeleteButton), + # PushButton label + PushButton(Id(:wep_keys_default), _("&Set as Default")) + ) + ) + end + + # TODO: help text which explain format of WEP keys + + def init + refresh_table(0) + end + + def refresh_table(selected_index) + @settings.keys ||= [] # TODO: should be fixed by proper initialize of settings object + table_items = @settings.keys.each_with_index.map do |key, i| + next unless key + Item(Id(i), i.to_s, key, i == @settings.default_key ? "X" : "") + end + + compact_keys = @settings.keys.compact + + Yast::UI.ChangeWidget(Id(:wep_keys_table), :Items, table_items.compact) + [:wep_keys_delete, :wep_keys_edit, :wep_keys_default].each do |k| + Yast::UI.ChangeWidget( + Id(k), + :Enabled, + !compact_keys.empty? + ) + end + Yast::UI.ChangeWidget( + Id(:wep_keys_add), + :Enabled, + compact_keys.size < 4 # only 4 keys are possible + ) + Yast::UI.ChangeWidget(Id(:wep_keys_table), :CurrentItem, selected_index) + end + + # @return [Symbol, nil] dialog result + def handle(event) + return nil if event["EventReason"] != "Activated" + + cur = Yast::UI.QueryWidget(Id(:wep_keys_table), :CurrentItem).to_i + case event["ID"] + when :wep_keys_edit + key = dialog(@settings.keys[cur]) + if key + @settings.keys[cur] = key + refresh_table(cur) + end + when :wep_keys_add + key = dialog + if key + # replace first nil value + index = @settings.keys.index(nil) + if index + @settings.keys[index] = key + else + @settings.keys << key + end + log.info "new keys #{@settings.keys.inspect}" + refresh_table(@settings.keys.compact.size - 1) + end + when :wep_keys_delete + @settings.keys.delete_at(cur) + refresh_table(0) + when :wep_keys_default + @settings.default_key = cur + refresh_table(cur) + end + + nil + end + + # Open a dialog to add/edit a key. + # TODO: own class for it + # @param value [String, nil] existing key to edit or nil for new key. + # @return [String, nil] key or nil if dialog is canceled + def dialog(value = nil) + value ||= "" + Yast::UI.OpenDialog( + Opt(:decorated), + VBox( + HSpacing(1), + # TextEntry label + TextEntry(Id(:key), _("&WEP Key"), value), + HSpacing(1), + HBox( + PushButton(Id(:ok), Opt(:default), Yast::Label.OKButton), + PushButton(Id(:cancel), Yast::Label.CancelButton) + ) + ) + ) + + Yast::UI.SetFocus(Id(:key)) + val = nil + while (ret = Yast::UI.UserInput) == :ok + val = Yast::UI.QueryWidget(Id(:key), :Value) + break if valid_key?(val) + # Popup::Error text + Yast::Popup.Error( + _( + "The WEP key is not valid. WEP key can be specified either directly in hex " \ + "digits, with or without dashes, or in the key's ASCII representation " \ + "(prefix s: ), or as a passphrase which will be hashed (prefix h: )." + ) + ) + end + + Yast::UI.CloseDialog + return nil if ret != :ok + + val + end + + def valid_key?(_key) + # TODO: write validation + true + end + end + end + end +end diff --git a/src/lib/y2network/dns.rb b/src/lib/y2network/dns.rb index bc2f4cd6c..f48f3b96a 100644 --- a/src/lib/y2network/dns.rb +++ b/src/lib/y2network/dns.rb @@ -17,6 +17,8 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require "y2network/hostname_reader" + module Y2Network # DNS configuration (hostname, nameservers, etc.). class DNS @@ -32,7 +34,9 @@ class DNS # @return [String] resolv.conf update policy attr_accessor :resolv_conf_policy - # @return [Boolean] Whether to take the hostname from DHCP + # @return [String,Symbol] Whether to take the hostname from DHCP. + # It can be an interface name (String), :any for any interface or :none from no taking + # the hostname from DHCP. attr_accessor :dhcp_hostname # @todo receive an array instead all these arguments @@ -51,17 +55,6 @@ def initialize(opts = {}) @dhcp_hostname = opts[:dhcp_hostname] end - # @return [Array] Valid chars to be used in the random part of a hostname - HOSTNAME_CHARS = (("a".."z").to_a + ("0".."9").to_a).freeze - private_constant :HOSTNAME_CHARS - - # Sets a hostname is none is present - def ensure_hostname! - return unless @hostname.nil? || @hostname.empty? - suffix = HOSTNAME_CHARS.sample(4).join - @hostname = "linux-#{suffix}" - end - # @return [Array] Methods to check when comparing two instances ATTRS = [ :hostname, :nameservers, :searchlist, :resolv_conf_policy, :dhcp_hostname diff --git a/src/lib/y2network/driver.rb b/src/lib/y2network/driver.rb new file mode 100644 index 000000000..9321ddde8 --- /dev/null +++ b/src/lib/y2network/driver.rb @@ -0,0 +1,99 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/can_be_copied" + +module Y2Network + # This class represents a driver for an interface + # + # It is composed of a kernel module name and a string representing the module options + class Driver + include CanBeCopied + + class << self + # Returns a driver using the information from the system + # + # @param name [String] Driver's name + def from_system(name) + params = Yast::SCR.Read(Yast::Path.new(".modules.options.#{name}")) + params_string = params.map { |k, v| "#{k}=#{v}" }.join(" ") + new(name, params_string) + end + + # Writes driver options to the underlying system + # + # @param drivers [Y2Network::Driver] Drivers to write options + def write_options(drivers) + drivers.each(&:write_options) + commit + end + + # Commits drivers options to disk + def commit + Yast::SCR.Write(Yast::Path.new(".modules"), nil) + end + end + + # @return [String] Kernel module name + attr_accessor :name + # @return [String] Kernel module parameters + attr_accessor :params + + # Constructor + # + # @param name [String] Driver name + # @param params [String] Driver parameters (e.g., "csum=1 debug=16") + def initialize(name, params = "") + @name = name + @params = params + end + + # Determines whether two drivers are equal + # + # @param other [Object] Driver to compare with + # @return [Boolean] true if +other+ is a Driver instance with the same name and params; false otherwise. + def ==(other) + return false unless other.is_a?(Driver) + name == other.name && params == other.params + end + + # Adds driver parameters to be written to the underlying system + # + # Parameters are not written to disk until Y2Network::Driver.commit + # is called. The reason is that writing them is an expensive operation, so it is + # better to write parameters for all drivers at the same time. + # + # You might prefer to use {.write_options} instead. + # + # @see Y2Network::Driver.commit + def write_options + parts = params.split(/ +/) + params_hash = parts.each_with_object({}) do |param, hash| + key, value = param.split("=") + hash[key] = value.to_s + end + Yast::SCR.Write(Yast::Path.new(".modules.options.#{name}"), params_hash) + end + + # eql? (hash key equality) should alias ==, see also + # https://ruby-doc.org/core-2.3.3/Object.html#method-i-eql-3F + alias_method :eql?, :== + end +end diff --git a/src/lib/y2network/hostname_reader.rb b/src/lib/y2network/hostname_reader.rb new file mode 100644 index 000000000..cee79cdef --- /dev/null +++ b/src/lib/y2network/hostname_reader.rb @@ -0,0 +1,97 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" + +Yast.import "FileUtils" +Yast.import "Hostname" +Yast.import "IP" +Yast.import "Mode" +Yast.import "NetHwDetection" + +module Y2Network + # This class is responsible for reading the hostname + # + # Depending on different circunstamces, the hostname can be read from different places (from a + # simple call to `/usr/bin/hostname` to the `/etc/install.inf` during installation). + # + # @example Read hostname + # Y2Network::HostnameReader.new.hostname #=> "foo" + class HostnameReader + include Yast::Logger + + # Returns the hostname + # + # @note If the hostname cannot be determined, generate a random one. + # + # @return [String] + def hostname + if (Yast::Mode.installation || Yast::Mode.autoinst) && Yast::FileUtils.Exists("/etc/install.inf") + fqdn = hostname_from_install_inf + end + + fqdn || hostname_from_system || random_hostname + end + + # Reads the hostname from the /etc/install.inf file + # + # @return [String,nil] Hostname + def hostname_from_install_inf + install_inf_hostname = Yast::SCR.Read(Yast::Path.new(".etc.install_inf.Hostname")) || "" + log.info("Got #{install_inf_hostname} from install.inf") + + return nil if install_inf_hostname.empty? + + # if the name is actually IP, try to resolve it (bnc#556613, bnc#435649) + if Yast::IP.Check(install_inf_hostname) + fqdn = Yast::NetHwDetection.ResolveIP(install_inf_hostname) + log.info("Got #{fqdn} after resolving IP from install.inf") + else + fqdn = install_inf_hostname + end + + host, _domain = *Yast::Hostname.SplitFQ(fqdn) + host.empty? ? nil : host + end + + # Reads the hostname from the underlying system + # + # @return [String] Hostname + def hostname_from_system + Yast::Execute.on_target!("/usr/bin/hostname", "--fqdn", stdout: :capture).strip + rescue Cheetah::ExecutionFailed + name = Yast::SCR.Read(Yast::Path.new(".target.string"), "/etc/hostname").to_s.strip + name.empty? ? nil : name + end + + # @return [Array] Valid chars to be used in the random part of a hostname + HOSTNAME_CHARS = (("a".."z").to_a + ("0".."9").to_a).freeze + private_constant :HOSTNAME_CHARS + + # Returns a random hostname + # + # The idea is to use a name like this as fallback. + # + # @return [String] + def random_hostname + suffix = HOSTNAME_CHARS.sample(4).join + "linux-#{suffix}" + end + end +end diff --git a/src/lib/y2network/hwinfo.rb b/src/lib/y2network/hwinfo.rb index 0913821dc..24becf93a 100644 --- a/src/lib/y2network/hwinfo.rb +++ b/src/lib/y2network/hwinfo.rb @@ -18,17 +18,133 @@ # find current contact information at www.suse.com. require "yast" +require "y2network/driver" +require "y2network/udev_rule" + +Yast.import "LanItems" module Y2Network + class HardwareWrapper + def initialize + Yast.include self, "network/routines.rb" + end + + # Returns the network devices hardware information + # + # @return [Array] Hardware information for netword devices + def netcards + return @netcards if @netcards + read_hardware + @netcards = ReadHardware("netcard").map do |attrs| + name = attrs["dev_name"] + extra_attrs = name ? extra_attrs_for(name) : {} + Hwinfo.new(attrs.merge(extra_attrs)) + end + end + + private + + # Add aditional attributes + # + # @param name [String] Device name + # @return [Hash] Hash containing extra attributes + def extra_attrs_for(name) + extra = {} + raw_dev_port = Yast::SCR.Read( + Yast::Path.new(".target.string"), "/sys/class_net/#{name}/dev_port" + ).to_s.strip + extra["dev_port"] = raw_dev_port unless raw_dev_port.empty? + extra + end + + # Makes sure that the hardware information was read + def read_hardware + Yast::LanItems.ReadHw if Yast::LanItems.Hardware.empty? + end + end + # Stores useful (from networking POV) items of hwinfo for an interface # FIXME: decide whether it should read hwinfo (on demand or at once) for a network # device and store only necessary info or just parse provided hash class Hwinfo + # TODO: this method should be private attr_reader :hwinfo - def initialize(name:) + class << self + # Creates a new instance containing hardware information for a given interface + # + # It retrieves the information from two sources: + # + # * hardware (through {Yast::LanItems} for the time being), + # * from existing udev rules. + # + # @todo Probably, this logic should be moved to a separate class. + # + # @param name [String] Interface's name + # @return [Hwinfo] + def for(name) + hwinfo_from_hardware(name) || hwinfo_from_udev(name) || Hwinfo.new + end + + # Returns the network devices hardware information + # + # @return [Array] Hardware information for netword devices + def netcards + hardware_wrapper.netcards + end + + # Resets the hardware information + # + # It will be re-read the next time is needed. + def reset + @hardware_wrapper = nil + end + + private + + # Returns hardware information for the given device + # + # It relies on the {Yast::LanItems} module. + # + # @param name [String] Interface's name + # @return [Hwinfo,nil] Hardware info or nil if not found + def hwinfo_from_hardware(name) + hardware_wrapper.netcards.find { |h| h.dev_name == name } + end + + # Hardware wrapper instance + # + # It memoizes the hardware wrapper in order to speed up the access + # + # @return [HardWrapper] + def hardware_wrapper + @hardware_wrapper = HardwareWrapper.new + end + + # Returns hardware information for the given device + # + # It relies on udev rules. + # + # @param name [String] Interface's name + # @return [Hwinfo,nil] Hardware info or nil if not found + def hwinfo_from_udev(name) + udev_rule = UdevRule.find_for(name) + return nil if udev_rule.nil? + info = { + udev: udev_rule.bus_id, + mac: udev_rule.mac, + dev_port: udev_rule.dev_port + }.compact + new(info) + end + end + + # Constructor + # + # @param hwinfo [Hash] Hardware information + def initialize(hwinfo = {}) # FIXME: store only what's needed. - @hwinfo = load_hwinfo(name) + @hwinfo = Hash[hwinfo.map { |k, v| [k.to_s, v] }] end # Shortcuts for accessing hwinfo items. Each hwinfo item has own method for reading @@ -65,18 +181,21 @@ def initialize(name:) # @return [String,nil] [ { name: "dev_name", default: "" }, - { name: "mac", default: "" }, - { name: "busid", default: "" }, + { name: "permanent_mac", default: nil }, + { name: "busid", default: nil }, { name: "link", default: false }, { name: "driver", default: "" }, - { name: "drivers", default: [] }, { name: "module", default: nil }, { name: "requires", default: [] }, { name: "hotplug", default: false }, { name: "wl_auth_modes", default: "" }, { name: "wl_enc_modes", default: nil }, { name: "wl_channels", default: nil }, - { name: "wl_bitrates", default: nil } + { name: "wl_bitrates", default: nil }, + { name: "dev_port", default: nil }, + { name: "type", default: nil }, + { name: "name", default: "" }, + { name: "modalias", default: nil } ].each do |hwinfo_item| define_method hwinfo_item[:name].downcase do @hwinfo ? @hwinfo.fetch(hwinfo_item[:name], hwinfo_item[:default]) : hwinfo_item[:default] @@ -87,7 +206,7 @@ def initialize(name:) alias_method :name, :dev_name def exists? - !@hwinfo.nil? + !@hwinfo.empty? end # Device type description @@ -96,13 +215,70 @@ def description @hwinfo ? @hwinfo.fetch("name", "") : "" end - private + # Merges data from another Hwinfo object + # + # @param other [Hwinfo] Object to merge data from + def merge!(other) + @hwinfo.merge!(other.hwinfo) + self + end + + # Returns the list of kernel modules + # + # The list of modules is internally represented as: + # + # [[mod1name, mod1args], [mod2name, mod2args]] + # + # This method only returns the names, omitting the arguments. + # + # @return [Array] List of drivers + def drivers + driver = @hwinfo.fetch("drivers", []).first + return [] unless driver + modules = driver.fetch("modules", []) + modules.map { |m| Driver.new(*m) } + end - # for textdomain in network/hardware.rb - include Yast::I18n + # Determines whether the hardware is available (plugged) + # + # If the hardware layer was able to get its type, it consider the hardware to be connected. Bear + # in mind that it is not possible to just rely in #exists? because it could include some info + # from udev rules. + # + # @return [Boolean] + def present? + !!type + end - def load_hwinfo(name) - Yast::LanItems.Hardware.find { |h| h["dev_name"] == name } + # Returns the MAC adress + # + # It usually returns the permanent MAC address (defined in the firmware). However, when + # missing, it will use the current MAC. See bsc#1136929 and bsc#1149234 for the reasons + # behind preferring the permanent MAC address. + # + # @return [String,nil] MAC address + def mac + return permanent_mac unless permanent_mac.nil? || permanent_mac.empty? + used_mac end + + # MAC address which is being used by the device + # + # @return [String,nil] MAC address + def used_mac + @hwinfo["mac"] + end + + # Determines whether two objects are equivalent + # + # Ignores any element having a nil value. + # + # @param other [Hwinfo] Object to compare with + # @return [Boolean] + def ==(other) + hwinfo.compact == other.hwinfo.compact + end + + alias_method :eql?, :== end end diff --git a/src/lib/y2network/interface.rb b/src/lib/y2network/interface.rb index 33bac4add..90d59c9ac 100644 --- a/src/lib/y2network/interface.rb +++ b/src/lib/y2network/interface.rb @@ -17,51 +17,63 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require "yast" require "y2network/interface_type" -require "y2network/hwinfo" module Y2Network # Network interface. + # + # It represents network interfaces, no matter whether they are physical or virtual ones. This + # class (including its subclasses) are basically responsible for holding the hardware + # configuration (see {Hwinfo}), including naming and driver information. + # + # Logical configuration, like TCP/IP or WIFI settings, are handled through + # Y2Network::ConnectionConfig::Base classes. Actually, relationships with other interfaces (like + # bonding slaves) are kept in those configuration objects too. + # + # @see Y2Network::PhysicalInterface + # @see Y2Network::VirtualInterface + # @see Y2Network::FakeInterface class Interface + include Yast::Logger + # @return [String] Device name ('eth0', 'wlan0', etc.) attr_accessor :name # @return [String] Interface description attr_accessor :description - # @return [Symbol] Interface type + # @return [InterfaceType] Interface type attr_accessor :type - # @return [Boolean] - attr_reader :configured # @return [HwInfo] attr_reader :hardware + # @return [Symbol] Mechanism to rename the interface (:none -no rename-, :bus_id or :mac) + attr_accessor :renaming_mechanism + # @return [String,nil] + attr_reader :old_name - # Shortcuts for accessing interfaces' ifcfg options - # - # TODO: this makes Interface class tighly couplet to netconfig backend - # once we have generic layer for accessing backends these methods has to be replaced - ["STARTMODE", "BOOTPROTO"].each do |ifcfg_option| - method_name = ifcfg_option.downcase - - define_method method_name do - # when switching to new backend we need as much guards as possible - if !configured || config.nil? || config.empty? - raise "Trying to read configuration of an unconfigured interface #{@name}" - end + class << self + # Builds an interface based on a connection + # + # @param conn [ConnectionConfig] Connection configuration related to the network interface + def from_connection(conn) + # require here to avoid circular dependency + require "y2network/physical_interface" + require "y2network/virtual_interface" - config[ifcfg_option] + interface_class = conn.virtual? ? VirtualInterface : PhysicalInterface + interface_class.new(conn.interface || conn.name, type: conn.type) end end # Constructor # # @param name [String] Interface name (e.g., "eth0") + # @param type [InterfaceType] Interface type def initialize(name, type: InterfaceType::ETHERNET) @name = name @description = "" @type = type - # @hardware and @name should not change during life of the object - @hardware = Hwinfo.new(name: name) - - init(name) + # TODO: move renaming logic to physical interfaces only + @renaming_mechanism = :none end # Determines whether two interfaces are equal @@ -84,15 +96,23 @@ def config system_config(name) end - private - - def system_config(name) - Yast::NetworkInterfaces.devmap(name) + # Returns the list of kernel modules + # + # @return [Array] + # @see Hwinfo#drivers + def drivers + hardware.drivers end - def init(name) - @configured = false - @configured = !system_config(name).nil? if !(name.nil? || name.empty?) + # Renames the interface + # + # @param new_name [String] New interface's name + # @param mechanism [Symbol] Property to base the rename on (:mac or :bus_id) + def rename(new_name, mechanism) + log.info "Rename interface '#{name}' to '#{new_name}' using the '#{mechanism}'" + @old_name = name if name != new_name # same name, just set different mechanism + @name = new_name + @renaming_mechanism = mechanism end end end diff --git a/src/lib/y2network/interface_config_builder.rb b/src/lib/y2network/interface_config_builder.rb index 0edc04d70..a5abdf1d6 100644 --- a/src/lib/y2network/interface_config_builder.rb +++ b/src/lib/y2network/interface_config_builder.rb @@ -17,297 +17,452 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. require "yast" +require "forwardable" +require "y2network/connection_config" require "y2network/hwinfo" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/ip_address" require "y2firewall/firewalld" require "y2firewall/firewalld/interface" Yast.import "LanItems" Yast.import "NetworkInterfaces" +Yast.import "Host" module Y2Network - # Collects data from the UI until we have enough of it to create a {Y2Network::Interface}. - # {Yast::LanItemsClass#Commit Yast::LanItems.Commit(builder)} uses it. + # Collects data from the UI until we have enough of it to create a + # {Y2Network::ConnectionConfig::Base} object. + # + # {Yast::LanItemsClass#Commit Yast::LanItems.Commit(builder)} use it. class InterfaceConfigBuilder include Yast::Logger + extend Forwardable # Load fresh instance of interface config builder for given type. # It can be specialized type or generic, depending if specialized is needed. - # @param type [String] type of device - # TODO: it would be nice to have type of device as Enum and not pure string - def self.for(type) - require "y2network/interface_config_builders/#{type}" - InterfaceConfigBuilders.const_get(type.to_s.capitalize).new + # @param type [Y2Network::InterfaceType,String] type of device or its short name + # @param config [Y2Network::ConnectionConfig::Base, nil] existing configuration of device or nil + # for newly created + def self.for(type, config: nil) + if !type.is_a?(InterfaceType) + type = InterfaceType.from_short_name(type) or raise "Unknown type #{type.inspect}" + end + require "y2network/interface_config_builders/#{type.file_name}" + InterfaceConfigBuilders.const_get(type.class_name).new(config: config) rescue LoadError => e - log.info "Specialed builder for #{type} not found. Fallbacking to default. #{e.inspect}" - new(type: type) + log.info "Specialized builder for #{type} not found. Falling back to default. #{e.inspect}" + new(type: type, config: config) end # @return [String] Device name (eth0, wlan0, etc.) - attr_accessor :name - # @return [String] type of @see Y2Network::Interface which is intended to be build (e.g. "eth") - attr_accessor :type + attr_reader :name + # @return [Y2Network::InterfaceType] type of @see Y2Network::Interface which is intended to be build + attr_reader :type + # @return [Y2Network::ConnectionConfig] connection config on which builder operates + attr_reader :connection_config + # @return [Symbol] Mechanism to rename the interface (:none -no hardware based-, :mac or :bus_id) + attr_writer :renaming_mechanism + # @return [Y2Network::Interface,nil] Underlying interface if it exists + attr_reader :interface + + def_delegators :@connection_config, + :startmode, :ethtool_options, :ethtool_options= # Constructor # # Load with reasonable defaults - def initialize(type: nil) + # @param type [Y2Network::InterfaceType] type of device + # @param config [Y2Network::ConnectionConfig::Base, nil] existing configuration of device or nil + # for newly created + def initialize(type:, config: nil) @type = type - @config = init_device_config({}) - @s390_config = init_device_s390_config({}) - end - - def newly_added? - Yast::LanItems.operation == :add + # TODO: also config need to store it, as newly added can be later + # edited with option for not yet created interface + @newly_added = config.nil? + if config + self.name = config.name + else + config = connection_config_klass(type).new + config.propose + end + @connection_config = config + @original_ip_config = ip_config_default.copy end - def []=(key, value) - @config[key] = value + # Sets the interface name + # + # It initializes the interface using the given name if it exists + # + # @param value [String] Interface name + def name=(value) + @name = value + iface = find_interface + self.interface = iface if iface end - def [](key) - @config[key] + def newly_added? + @newly_added end + # saves builder content to backend + # @ TODO now still LanItems actively query config attribute and write it + # down, so here mainly workarounds, but ideally this save should change + # completely backend def save - Yast::LanItems.Items[Yast::LanItems.current]["ifcfg"] = name - if !driver.empty? - Yast::LanItems.setDriver(driver) - Yast::LanItems.driver_options[driver] = driver_options - end + @connection_config.name = name + @connection_config.interface = name + @connection_config.ip_aliases = aliases_to_ip_configs + @connection_config.firewall_zone = firewall_zone # create new instance as name can change firewall_interface = Y2Firewall::Firewalld::Interface.new(name) if Y2Firewall::Firewalld.instance.installed? - Yast::LanItems.firewall_zone = firewall_zone # TODO: should change only if different, but maybe firewall_interface responsibility? firewall_interface.zone = firewall_zone if !firewall_interface.zone || firewall_zone != firewall_interface.zone.name end - save_aliases + if interface.respond_to?(:custom_driver) + interface.custom_driver = driver_auto? ? nil : driver.name + yast_config.add_or_update_driver(driver) unless driver_auto? + end + yast_config.rename_interface(@old_name, name, renaming_mechanism) if renamed_interface? + yast_config.add_or_update_connection_config(@connection_config) nil end + # Determines whether the interface has been renamed + # + # @return [Boolean] true if it was renamed; false otherwise + def renamed_interface? + return false unless interface + name != interface.name || @renaming_mechanism != interface.renaming_mechanism + end + + # Renames the interface + # + # @param new_name [String] New interface's name + def rename_interface(new_name) + @old_name ||= name + @name = new_name + end + + # Returns the current renaming mechanism + # + # @return [Symbol,nil] Mechanism to rename the interface (nil -no rename-, :mac or :bus_id) + def renaming_mechanism + @renaming_mechanism || interface.renaming_mechanism + end + # how many device names is proposed NEW_DEVICES_COUNT = 10 - # Propose bunch of possible names for interface + # Proposes bunch of possible names for interface # do not modify anything # @return [Array] def proposed_names - Yast::LanItems.new_type_devices(type, NEW_DEVICES_COUNT) + interfaces.free_names(type.short_name, NEW_DEVICES_COUNT) end + # checks if passed name is valid as interface name + # TODO: looks sysconfig specific def valid_name?(name) !!(name =~ /^[[:alnum:]._:-]{1,15}\z/) end + # checks if interface name already exists def name_exists?(name) - Yast::NetworkInterfaces.List("").include?(name) + interfaces.known_names.include?(name) end + # gets valid characters that can be used in interface name + # TODO: looks sysconfig specific def name_valid_characters Yast::NetworkInterfaces.ValidCharsIfcfg end - def kernel_modules - Yast::LanItems.GetItemModules("") + # gets a list of available kernel modules for the interface + def drivers + return [] unless interface + yast_config.drivers_for_interface(interface.name) end + # gets currently assigned firewall zone def firewall_zone return @firewall_zone if @firewall_zone # TODO: handle renaming firewall_interface = Y2Firewall::Firewalld::Interface.new(name) - @firewall_zone = firewall_interface.zone && firewall_interface.zone.name + @firewall_zone = (firewall_interface.zone && firewall_interface.zone.name) || @connection_config.firewall_zone end - def firewall_zone=(value) - @firewall_zone = value + # sets assigned firewall zone + attr_writer :firewall_zone + + # @return [Y2Network::BootProtocol] + def boot_protocol + @connection_config.bootproto end - def driver - @driver ||= Yast::Ops.get_string(Yast::LanItems.getCurrentItem, ["udev", "driver"], "") + # @param[String, Y2Network::BootProtocol] + def boot_protocol=(value) + value = value.name if value.is_a?(Y2Network::BootProtocol) + @connection_config.bootproto = Y2Network::BootProtocol.from_name(value) end - def driver=(value) - @driver = value + # @param [String,Y2Network::Startmode] name startmode name used to create Startmode object + # or object itself + def startmode=(name) + mode = name.is_a?(Startmode) ? name : Startmode.create(name) + # assign only if it is not already this value. This helps with ordering of ifplugd_priority + return if @connection_config.startmode && @connection_config.startmode.name == mode.name + + @connection_config.startmode = mode end - def driver_options - target_driver = @driver - target_driver = hwinfo.module if target_driver.empty? - @driver_options ||= Yast::LanItems.driver_options[target_driver] || "" + # @param [Integer] value priority value + def ifplugd_priority=(value) + if !@connection_config.startmode || @connection_config.startmode.name != "ifplugd" + log.info "priority set and startmode is not ifplugd. Adapting..." + @connection_config.startmode = Startmode.create("ifplugd") + end + @connection_config.startmode.priority = value.to_i end - def driver_options=(value) - @driver_options = value + # @return [Integer] + def ifplugd_priority + startmode.name == "ifplugd" ? startmode.priority : 0 end + # gets currently assigned kernel module + def driver + return @driver if @driver + @driver = yast_config.drivers.find { |d| d.name == @interface.custom_driver } if @interface.custom_driver + @driver ||= :auto + end + + # sets kernel module for interface + # @param value [Driver] + def driver=(value) + @driver = value + end + + # gets aliases for interface + # @return [Array] hash values are `:label` for alias label, + # `:ip` for ip address, `:mask` for netmask and `:prefixlen` for prefix. + # Only one of `:mask` and `:prefixlen` is set. def aliases - @aliases ||= Yast::LanItems.aliases.each_value.map do |data| + return @aliases if @aliases + + aliases = @connection_config.ip_aliases.map do |data| { - label: data["LABEL"] || "", - ip: data["IPADDR"] || "", - mask: data["NETMASK"] || "", - prefixlen: data["PREFIXLEN"] || "" + label: data.label.to_s, + ip: data.address.address.to_s, + prefixlen: data.address.prefix.to_s, + id: data.id.to_s + # NOTE: new API does not have netmask at all, we need to adapt UI to clearly mention only prefix } end + @aliases = aliases end + # sets aliases for interface + # @param value [Array] see #aliases for hash values def aliases=(value) @aliases = value end - def udev_name - # cannot cache as EditNicName dialog can change it - Yast::LanItems.current_udev_name + # @return [String] + def ip_address + default = @connection_config.ip + if default + default.address.address.to_s + else + "" + end end - # Provides stored configuration in sysconfig format - # - # @return [Hash] where key is sysconfig option and value is the option's value - def device_sysconfig - # initalize options which has to be known and was not set by the user explicitly - init_mandatory_options + # @param [String] value + def ip_address=(value) + if value.nil? || value.empty? + @connection_config.ip = nil + else + ip_config_default.address.address = value + end + end - # with naive implementation of filtering options by type - config = @config.dup + # @return [String] returns prefix or netmask. prefix in format "/" + def subnet_prefix + if @connection_config.ip + "/" + @connection_config.ip.address.prefix.to_s + else + "" + end + end - # filter out options which are not needed - config.delete_if { |k, _| k =~ /WIRELESS.*/ } if type != "wlan" - config.delete_if { |k, _| k =~ /BONDING.*/ } if type != "bond" - config.delete_if { |k, _| k =~ /BRIDGE.*/ } if type != "br" - config.delete_if { |k, _| k =~ /TUNNEL.*/ } if !["tun", "tap"].include?(type) - config.delete_if { |k, _| k == "VLAN_ID" || k == "ETHERDEVICE" } if type != "vlan" - config.delete_if { |k, _| k == "IPOIB_MODE" } if type != "ib" - config.delete_if { |k, _| k == "INTERFACE" } if type != "dummy" - config.delete_if { |k, _| k == "IFPLUGD_PRIORITY" } if config["STARTMODE"] != "ifplugd" + # @param [String] value prefix or netmask is accepted. prefix in format "/" + def subnet_prefix=(value) + if value.empty? + ip_config_default.address.prefix = nil + elsif value.start_with?("/") + ip_config_default.address.prefix = value[1..-1].to_i + elsif value.size < 3 # one or two digits can be only prefixlen + ip_config_default.address.prefix = value.to_i + elsif value =~ /^\d{3}$/ + ip_config_default.address.prefix = value.to_i + else + ip_config_default.address.netmask = value + end + end - config.merge("_aliases" => lan_items_format_aliases) + # @return [String] + def hostname + @connection_config.hostname || "" end - # Updates itself according to the given sysconfig configuration - # - # @param devmap [Hash, nil] a key, value map where key is sysconfig option - # and corresponding value is the option value - def load_sysconfig(devmap) - @config.merge!(devmap || {}) + # @param [String] value + def hostname=(value) + @connection_config.hostname = value end - def load_s390_config(devmap) - @s390_config.merge!(devmap || {}) + # sets remote ip for ptp connections + # @return [String] + def remote_ip + default = @connection_config.ip + if default + default.remote_address.to_s + else + "" + end end - private + # @param [String] value + def remote_ip=(value) + ip_config_default.remote_address = IPAddress.from_string(value) + end - # Initializes device configuration map with default values when needed - # - # @param devmap [Hash] current device configuration - # - # @return device configuration map where unspecified values were set - # to reasonable defaults - def init_device_config(devmap) - # the defaults here are what sysconfig defaults to - # (as opposed to what a new interface gets, in {#Select)} - defaults = YAML.load_file(Yast::Directory.find_data_file("network/sysconfig_defaults.yml")) - defaults.merge(devmap) + # Gets Maximum Transition Unit + # @return [String] + def mtu + @connection_config.mtu.to_s end - def init_device_s390_config(devmap) - Yast.import "Arch" + # Sets Maximum Transition Unit + # @param [String] value + def mtu=(value) + @connection_config.mtu = value.to_i + end - return {} if !Yast::Arch.s390 + def configure_as_slave + self.boot_protocol = "none" + self.aliases = [] + end - # Default values used when creating an emulated NIC for physical s390 hardware. - s390_defaults = YAML.load_file(Yast::Directory.find_data_file("network/s390_defaults.yml")) - s390_defaults.merge(devmap) + # @param info [Hash] Hardware information + # @return [Hwinfo] + def hwinfo_from(info) + @hwinfo = Hwinfo.new(info) end - # returns a map with device options for newly created item - def init_mandatory_options - # FIXME: NetHwDetection is done in Lan.Read - Yast.import "NetHwDetection" + # @return [Hwinfo] + def hwinfo + @hwinfo ||= Hwinfo.for(name) + end - # #104494 - always write IPADDR+NETMASK, even empty - # #50955 omit computable fields - @config["BROADCAST"] = "" - @config["NETWORK"] = "" + private - if !@config["NETMASK"] || @config["NETMASK"].empty? - @config["NETMASK"] = Yast::NetHwDetection.result["NETMASK"] || "255.255.255.0" - end + def ip_config_default + return @connection_config.ip if @connection_config.ip + @connection_config.ip = ConnectionConfig::IPConfig.new(IPAddress.new("0.0.0.0")) + end - @config["STARTMODE"] = new_device_startmode if !@config["STARTMODE"] || @config["STARTMODE"].empty? + # Returns the connection config class for a given type + # + # @param type [Y2Network::InterfaceType] type of device + def connection_config_klass(type) + ConnectionConfig.const_get(type.class_name) + rescue NameError + log.error "Could not find a class to handle '#{type.class_name}' connections" + ConnectionConfig::Base end - # returns default startmode for a new device + # Sets the interface for the builder # - # startmode is returned according product, Arch and current device type - def new_device_startmode - Yast.import "ProductFeatures" - - product_startmode = Yast::ProductFeatures.GetStringFeature( - "network", - "startmode" - ) - - startmode = case product_startmode - when "ifplugd" - if replace_ifplugd? - hotplug_interface? ? "hotplug" : "auto" - else - product_startmode - end - when "auto" - "auto" - else - hotplug_interface? ? "hotplug" : "auto" - end + # @param iface [Interface] Interface to associate the builder with + def interface=(iface) + @interface = iface + @renaming_mechanism ||= @interface.renaming_mechanism + end - startmode + # Returns the underlying interface + # + # @return [Y2Network::Interface,nil] + def find_interface + return nil unless yast_config # in some tests, it could return nil + interfaces.by_name(name) end - def replace_ifplugd? - Yast.import "Arch" - Yast.import "NetworkService" + # Returns the interfaces collection + # + # @return [Y2Network::InterfacesCollection] + def interfaces + yast_config.interfaces + end - return true if !Yast::Arch.is_laptop - return true if Yast::NetworkService.is_network_manager - # virtual devices cannot expect any event from ifplugd - return true if ["bond", "vlan", "br"].include? type + # Helper method to access to the current configuration + # + # @return [Y2Network::Config] + def yast_config + Yast.import "Lan" # avoid circular dependency - false + Yast::Lan.yast_config end - def hotplug_interface? - hwinfo.hotplug + # Determines whether the IP configuration is required + # + # @return [Boolean] + def required_ip_config? + boot_protocol == BootProtocol::STATIC end - def hwinfo - @hwinfo ||= Hwinfo.new(name: name) + # Determines whether the driver should be set automatically + # + # @return [Boolean] + def driver_auto? + :auto == driver end - def lan_items_format_aliases - aliases.each_with_index.each_with_object({}) do |(a, i), res| - res[i] = { - "IPADDR" => a[:ip], - "LABEL" => a[:label], - "PREFIXLEN" => a[:prefixlen], - "NETMASK" => a[:mask] - - } + # Converts aliases in hash form to a list of IPConfig objects + # + # @return [Array] + def aliases_to_ip_configs + last_id = 0 + used_ids = aliases + .select { |a| a[:id] && a[:id] =~ /\A_\d+\z/ } + .map { |a| a[:id].sub("_", "").to_i } + aliases.each_with_object([]) do |map, result| + ipaddr = IPAddress.from_string(map[:ip]) + ipaddr.prefix = map[:prefixlen].delete("/").to_i if map[:prefixlen] + id = map[:id] + if id.nil? || id.empty? + last_id = id = find_free_alias_id(used_ids, last_id) if id.nil? || id.empty? + id = "_#{id}" + end + result << ConnectionConfig::IPConfig.new(ipaddr, label: map[:label], id: id) end end - def save_aliases - log.info "setting new aliases #{lan_items_format_aliases.inspect}" - aliases_to_delete = Yast::LanItems.aliases.dup # #48191 - Yast::NetworkInterfaces.Current["_aliases"] = lan_items_format_aliases - Yast::LanItems.aliases = lan_items_format_aliases - aliases_to_delete.each_pair do |a, v| - Yast::NetworkInterfaces.DeleteAlias(Yast::NetworkInterfaces.Name, a) if v + # Returns a free numeric ID for an IP aliases + # + # @param used_ids [Array] Already used IDs + # @param last_id [Integer] Last used ID + def find_free_alias_id(used_ids, last_id) + loop do + last_id += 1 + break unless used_ids.include?(last_id) end + last_id end end end diff --git a/src/lib/y2network/interface_config_builders/bond.rb b/src/lib/y2network/interface_config_builders/bond.rb deleted file mode 100644 index 4adf13fa2..000000000 --- a/src/lib/y2network/interface_config_builders/bond.rb +++ /dev/null @@ -1,80 +0,0 @@ -require "yast" -require "y2network/config" -require "y2network/interface_config_builder" - -module Y2Network - module InterfaceConfigBuilders - class Bond < InterfaceConfigBuilder - include Yast::Logger - - def initialize - super(type: "bond") - - # fill mandatory bond option - @config["BOND_SLAVES"] = [] - end - - # @return [Array] list of interfaces usable for the bond device - def bondable_interfaces - interfaces.all.select { |i| bondable?(i) } - end - - attr_writer :bond_options - def bond_options - return @bond_options if @bond_options - - @bond_options = Yast::LanItems.bond_option - if @bond_options.nil? || @bond_options.empty? - @bond_options = @config["BONDING_MODULE_OPTS"] - end - @bond_options - end - - def save - super - - Yast::LanItems.bond_slaves = @config["BOND_SLAVES"] - Yast::LanItems.bond_option = bond_options - end - - private - - def interfaces - Config.find(:yast).interfaces - end - - # Checks whether an interface can be enslaved in particular bond interface - # - # @param iface [Interface] an interface to be validated as bond_iface slave - # TODO: Check for valid configurations. E.g. bond device over vlan - # is nonsense and is not supported by netconfig. - # Also devices enslaved in a bridge should be excluded too. - def bondable?(iface) - Yast.import "Arch" - Yast.include self, "network/lan/s390.rb" - - # check if the device is L2 capable on s390 - if Yast::Arch.s390 - s390_config = s390_ReadQethConfig(iface.name) - - # only devices with L2 support can be enslaved in bond. See bnc#719881 - return false unless s390_config["QETH_LAYER2"] == "yes" - end - - if interfaces.bond_index[iface.name] && interfaces.bond_index[iface.name] != @name - log.debug("Excluding (#{iface.name}) - is already bonded") - return false - end - - # cannot enslave itself - # FIXME: this can happen only bcs we silently use LanItems::Items which - # already contains partially configured bond when adding - return false if iface.name == @name - - return true if !iface.configured - - iface.bootproto == "none" - end - end - end -end diff --git a/src/lib/y2network/interface_config_builders/bonding.rb b/src/lib/y2network/interface_config_builders/bonding.rb new file mode 100644 index 000000000..9ad608df0 --- /dev/null +++ b/src/lib/y2network/interface_config_builders/bonding.rb @@ -0,0 +1,118 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/config" +require "y2network/interface_config_builder" + +module Y2Network + module InterfaceConfigBuilders + class Bonding < InterfaceConfigBuilder + include Yast::Logger + extend Forwardable + + def initialize(config: nil) + super(type: InterfaceType::BONDING, config: config) + end + + # @return [Array] list of interfaces usable for the bond device + def bondable_interfaces + interfaces.select { |i| bondable?(i) } + end + + def_delegators :connection_config, + :slaves, :slaves= + + # @param value [String] options for bonding + def bond_options=(value) + connection_config.options = value + end + + # current options for bonding + # @return [String] + def bond_options + connection_config.options + end + + # Checks if any of given device is already configured and need adaptation for bridge + # @param devices [Array] devices to check + # @return [Boolean] true if there is device that needs adaptation + def already_configured?(devices) + devices.any? do |device| + next false unless yast_config.configured_interface?(device) + yast_config.connections.by_name(device).startmode.name != "none" + end + end + + # additionally it adapt slaves if needed + def save + slaves.each do |slave| + interface = yast_config.interfaces.by_name(slave) + connection = yast_config.connections.by_name(slave) + next if connection && connection.startmode.name == "none" + builder = InterfaceConfigBuilder.for(interface.type, config: connection) + builder.name = interface.name + builder.configure_as_slave + builder.save + end + + super + end + + private + + def interfaces + yast_config.interfaces + end + + # Checks whether an interface can be enslaved in particular bond interface + # + # @param iface [Interface] an interface to be validated as bond_iface slave + # TODO: Check for valid configurations. E.g. bond device over vlan + # is nonsense and is not supported by netconfig. + # Also devices enslaved in a bridge should be excluded too. + def bondable?(iface) + Yast.import "Arch" + Yast.include self, "network/lan/s390.rb" + + # check if the device is L2 capable on s390 + if Yast::Arch.s390 + s390_config = s390_ReadQethConfig(iface.name) + + # only devices with L2 support can be enslaved in bond. See bnc#719881 + return false unless s390_config["QETH_LAYER2"] == "yes" + end + + config = yast_config.connections.by_name(iface.name) + return true unless config # unconfigured device is always bondable + + master = config.find_master(yast_config.connections) + if master && master.name != name + log.debug("Excluding (#{iface.name}) - already has master #{master.inspect}") + return false + end + + # cannot enslave itself + return false if iface.name == @name + + true + end + end + end +end diff --git a/src/lib/y2network/interface_config_builders/br.rb b/src/lib/y2network/interface_config_builders/br.rb deleted file mode 100644 index 955297cd0..000000000 --- a/src/lib/y2network/interface_config_builders/br.rb +++ /dev/null @@ -1,73 +0,0 @@ -require "yast" -require "y2network/config" -require "y2network/interface_config_builder" - -Yast.import "NetworkInterfaces" - -module Y2Network - module InterfaceConfigBuilders - class Br < InterfaceConfigBuilder - include Yast::Logger - - def initialize - super(type: "br") - end - - def already_configured?(devices) - devices.any? do |device| - next false if Yast::NetworkInterfaces.devmap(device).nil? - ![nil, "none"].include?(Yast::NetworkInterfaces.devmap(device)["BOOTPROTO"]) - end - end - - # @return [Array] list of interfaces usable in the bridge - def bridgeable_interfaces - interfaces.all.select { |i| bridgeable?(i) } - end - - private - - def interfaces - Config.find(:yast).interfaces - end - - NONBRIDGEABLE_TYPES = ["br", "tun", "usb", "wlan"].freeze - NONBRIDGEABLE_STARTMODE = ["nfsroot", "ifplugd"].freeze - - # Checks whether an interface can be bridged in particular bridge - # - # @param iface [Interface] an interface to be validated as the bridge slave - def bridgeable?(iface) - # cannot enslave itself - # FIXME: this can happen only bcs we silently use LanItems::Items which - # already contains partially configured bridge when adding - return false if iface.name == @name - return true if !iface.configured - - if interfaces.bond_index[iface.name] - log.debug("Excluding (#{iface.name}) - is bonded") - return false - end - - # the iface is already in another bridge - if interfaces.bridge_index[iface.name] && interfaces.bridge_index[iface.name] != @name - log.debug("Excluding (#{iface.name}) - already bridged") - return false - end - - # exclude interfaces of type unusable for bridge - if NONBRIDGEABLE_TYPES.include?(iface.type.short_name) - log.debug("Excluding (#{iface.name}) - is #{iface.type.short_name}") - return false - end - - if NONBRIDGEABLE_STARTMODE.include?(iface.startmode) - log.debug("Excluding (#{iface.name}) - is #{iface.startmode}") - return false - end - - true - end - end - end -end diff --git a/src/lib/y2network/interface_config_builders/bridge.rb b/src/lib/y2network/interface_config_builders/bridge.rb new file mode 100644 index 000000000..f30e616ab --- /dev/null +++ b/src/lib/y2network/interface_config_builders/bridge.rb @@ -0,0 +1,115 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "forwardable" +require "y2network/config" +require "y2network/interface_config_builder" + +Yast.import "NetworkInterfaces" + +module Y2Network + module InterfaceConfigBuilders + class Bridge < InterfaceConfigBuilder + include Yast::Logger + extend Forwardable + + def initialize(config: nil) + super(type: InterfaceType::BRIDGE, config: config) + end + + # Checks if any of given device is already configured and need adaptation for bridge + # @param devices [Array] devices to check + # @return [Boolean] true if there is device that needs adaptation + def already_configured?(devices) + devices.any? do |device| + next false unless yast_config.configured_interface?(device) + yast_config.connections.by_name(device).bootproto.name != "none" + end + end + + # @return [Array] list of interfaces usable in the bridge + def bridgeable_interfaces + interfaces.select { |i| bridgeable?(i) } + end + + # additionally it adapt slaves if needed + def save + ports.each do |port| + interface = yast_config.interfaces.by_name(port) + connection = yast_config.connections.by_name(port) + next if connection && connection.startmode.name == "none" + builder = InterfaceConfigBuilder.for(interface.type, config: connection) + builder.name = interface.name + builder.configure_as_slave + builder.save + end + + super + end + + def_delegators :@connection_config, + :ports, :ports=, + :stp, :stp= + + private + + def interfaces + yast_config.interfaces + end + + NONBRIDGEABLE_TYPES = [ + InterfaceType::BRIDGE, + InterfaceType::TUN, + InterfaceType::USB, + InterfaceType::WIRELESS + ].freeze + NONBRIDGEABLE_STARTMODE = ["nfsroot", "ifplugd"].freeze + + # Checks whether an interface can be bridged in particular bridge + # + # @param iface [Interface] an interface to be validated as the bridge slave + def bridgeable?(iface) + # cannot enslave itself + return false if iface.name == @name + return true unless yast_config.configured_interface?(iface.name) + + config = yast_config.connections.by_name(iface.name) + master = config.find_master(yast_config.connections) + if master && master.name != name + log.debug("Excluding (#{iface.name}) - already has master #{master.inspect}") + return false + end + + # exclude interfaces of type unusable for bridge + if NONBRIDGEABLE_TYPES.include?(iface.type) + log.debug("Excluding (#{iface.name}) - is #{iface.type.name}") + return false + end + + if NONBRIDGEABLE_STARTMODE.include?(config.startmode.to_s) + log.debug("Excluding (#{iface.name}) - is #{config.startmode}") + return false + end + + true + end + end + end +end diff --git a/src/lib/y2network/interface_config_builders/ctc.rb b/src/lib/y2network/interface_config_builders/ctc.rb new file mode 100644 index 000000000..45b8b489b --- /dev/null +++ b/src/lib/y2network/interface_config_builders/ctc.rb @@ -0,0 +1,41 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_config_builder" + +Yast.import "LanItems" +Yast.import "NetworkInterfaces" + +module Y2Network + module InterfaceConfigBuilders + class Ctc < InterfaceConfigBuilder + extend Forwardable + + def initialize(config: nil) + super(type: InterfaceType::CTC, config: config) + end + + def_delegators :@connection_config, + :read_channel, :read_channel=, + :write_channel, :write_channel=, + :protocol, :protocol= + end + end +end diff --git a/src/lib/y2network/interface_config_builders/dummy.rb b/src/lib/y2network/interface_config_builders/dummy.rb index 964c228b9..0d960388b 100644 --- a/src/lib/y2network/interface_config_builders/dummy.rb +++ b/src/lib/y2network/interface_config_builders/dummy.rb @@ -1,23 +1,30 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "y2network/interface_config_builder" module Y2Network module InterfaceConfigBuilders class Dummy < InterfaceConfigBuilder - def initialize - super(type: "dummy") - end - - # It does all operations needed for sucessfull configuration export. - # - # In case of config builder for dummy interface type it gurantees that - # the interface will be recognized as dummy one by the backend properly. - def save - super - - @config["INTERFACETYPE"] = "dummy" - - nil + def initialize(config: nil) + super(type: InterfaceType::DUMMY, config: config) end end end diff --git a/src/lib/y2network/interface_config_builders/ib.rb b/src/lib/y2network/interface_config_builders/ib.rb deleted file mode 100644 index 34d3a3829..000000000 --- a/src/lib/y2network/interface_config_builders/ib.rb +++ /dev/null @@ -1,39 +0,0 @@ -require "yast" -require "y2network/interface_config_builder" - -Yast.import "LanItems" - -module Y2Network - module InterfaceConfigBuilders - class Ib < InterfaceConfigBuilder - def initialize - super(type: "ib") - end - - attr_writer :ipoib_mode - - # Returns current value of infiniband mode - # - # @return [String] particular mode or "default" when not set - def ipoib_mode - @ipoib_mode ||= if [nil, ""].include?(@config["IPOIB_MODE"]) - "default" - else - @config["IPOIB_MODE"] - end - end - - # It does all operations needed for sucessfull configuration export. - # - # In case of config builder for Ib interface type it sets infiniband's - # mode to reasonable default when not set explicitly. - def save - super - - @config["IPOIB_MODE"] = ipoib_mode == "default" ? nil : ipoib_mode - - nil - end - end - end -end diff --git a/src/lib/y2network/interface_config_builders/infiniband.rb b/src/lib/y2network/interface_config_builders/infiniband.rb new file mode 100644 index 000000000..4a3db02fa --- /dev/null +++ b/src/lib/y2network/interface_config_builders/infiniband.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_config_builder" +require "y2network/ipoib_mode" + +module Y2Network + module InterfaceConfigBuilders + class Infiniband < InterfaceConfigBuilder + def initialize(config: nil) + super(type: InterfaceType::INFINIBAND, config: config) + end + + # @param value [String] ipoib mode configuration + def ipoib_mode=(value) + value = "" if value == "default" + connection_config.ipoib_mode = IpoibMode.from_name(value) + end + + # Returns current value of infiniband mode + # + # @return [String] particular mode or "default" when not set + def ipoib_mode + return "default" if connection_config.ipoib_mode.name == "" + + connection_config.ipoib_mode.name + end + end + end +end diff --git a/src/lib/y2network/interface_config_builders/lcs.rb b/src/lib/y2network/interface_config_builders/lcs.rb new file mode 100644 index 000000000..25c48f632 --- /dev/null +++ b/src/lib/y2network/interface_config_builders/lcs.rb @@ -0,0 +1,42 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_config_builder" + +Yast.import "LanItems" +Yast.import "NetworkInterfaces" + +module Y2Network + module InterfaceConfigBuilders + class Lcs < InterfaceConfigBuilder + extend Forwardable + + def initialize(config: nil) + super(type: InterfaceType::LCS, config: config) + end + + def_delegators :@connection_config, + :read_channel, :read_channel=, + :write_channel, :write_channel=, + :protocol, :protocol=, + :timeout, :timeout= + end + end +end diff --git a/src/lib/y2network/interface_config_builders/qeth.rb b/src/lib/y2network/interface_config_builders/qeth.rb new file mode 100644 index 000000000..9286bf00a --- /dev/null +++ b/src/lib/y2network/interface_config_builders/qeth.rb @@ -0,0 +1,52 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_config_builder" + +Yast.import "LanItems" +Yast.import "NetworkInterfaces" + +module Y2Network + module InterfaceConfigBuilders + # Builder for S390 qeth interfaces. It also assumes the activation + # responsibilities. + class Qeth < InterfaceConfigBuilder + extend Forwardable + + # Constructor + # + # @param config [Y2Network::ConnectionConfig::Base, nil] existing configuration of device or nil + # for newly created + def initialize(config: nil) + super(type: InterfaceType::QETH, config: config) + end + + def_delegators :@connection_config, + :read_channel, :read_channel=, + :write_channel, :write_channel=, + :data_channel, :data_channel=, + :layer2, :layer2=, + :port_number, :port_number=, + :lladdress, :lladdress=, + :ipa_takeover, :ipa_takeover=, + :attributes, :attributes= + end + end +end diff --git a/src/lib/y2network/interface_config_builders/tap.rb b/src/lib/y2network/interface_config_builders/tap.rb new file mode 100644 index 000000000..a258a60e6 --- /dev/null +++ b/src/lib/y2network/interface_config_builders/tap.rb @@ -0,0 +1,43 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_config_builder" + +module Y2Network + module InterfaceConfigBuilders + class Tap < InterfaceConfigBuilder + def initialize(config: nil) + super(type: InterfaceType::TAP, config: config) + end + + # @return [Array(2)] user and group of tunnel + def tunnel_user_group + [connection_config.owner, connection_config.group] + end + + # @param [String] user owner of tunnel. Name or UID + # @param [String] group owner of tunnel. Name or GID + def assign_tunnel_user_group(user, group) + connection_config.owner = user + connection_config.group = group + end + end + end +end diff --git a/src/lib/y2network/interface_config_builders/tun.rb b/src/lib/y2network/interface_config_builders/tun.rb new file mode 100644 index 000000000..70d45e57c --- /dev/null +++ b/src/lib/y2network/interface_config_builders/tun.rb @@ -0,0 +1,43 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_config_builder" + +module Y2Network + module InterfaceConfigBuilders + class Tun < InterfaceConfigBuilder + def initialize(config: nil) + super(type: InterfaceType::TUN, config: config) + end + + # @return [Array(2)] user and group of tunnel + def tunnel_user_group + [connection_config.owner, connection_config.group] + end + + # @param [String] user owner of tunnel. Name or UID + # @param [String] group owner of tunnel. Name or GID + def assign_tunnel_user_group(user, group) + connection_config.owner = user + connection_config.group = group + end + end + end +end diff --git a/src/lib/y2network/interface_config_builders/vlan.rb b/src/lib/y2network/interface_config_builders/vlan.rb index 60f8c317a..1fec33292 100644 --- a/src/lib/y2network/interface_config_builders/vlan.rb +++ b/src/lib/y2network/interface_config_builders/vlan.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "y2network/interface_config_builder" @@ -7,40 +26,42 @@ module Y2Network module InterfaceConfigBuilders class Vlan < InterfaceConfigBuilder - def initialize - super(type: "vlan") + def initialize(config: nil) + super(type: InterfaceType::VLAN, config: config) + end + + # @return [Integer] + def vlan_id + connection_config.vlan_id || 0 + end + + # @param [Integer] value + def vlan_id=(value) + connection_config.vlan_id = value end + # @return [String] def etherdevice - @config["ETHERDEVICE"] + connection_config.parent_device end + # @param [String] value def etherdevice=(value) - @config["ETHERDEVICE"] = value + connection_config.parent_device = value end # @return [Hash] returns ordered list of devices that can be used for vlan # Keys are ids for #etherdevice and value are label def possible_vlans - res = {} - # unconfigured devices - Yast::LanItems.Items.each_value do |lan_item| - next unless (lan_item["ifcfg"] || "").empty? - dev_name = lan_item.fetch("hwinfo", {}).fetch("dev_name", "") - res[dev_name] = dev_name - end - # configured devices - configurations = Yast::NetworkInterfaces.FilterDevices("netcard") - # TODO: API looks horrible - Yast::NetworkInterfaces.CardRegex["netcard"].split("|").each do |devtype| - (configurations[devtype] || {}).each_key do |devname| - next if Yast::NetworkInterfaces.GetType(devname) == type - - res[devname] = "#{devname} - #{Yast::Ops.get_string(configurations, [devtype, devname, "NAME"], "")}" + yast_config.interfaces.to_a.each_with_object({}) do |interface, result| + next if interface.type.vlan? # does not make sense to have vlan of vlan + + result[interface.name] = if interface.hardware && interface.hardware.present? + "#{interface.name} - #{interface.hardware.description}" + else + interface.name end end - - res end end end diff --git a/src/lib/y2network/interface_config_builders/wireless.rb b/src/lib/y2network/interface_config_builders/wireless.rb new file mode 100644 index 000000000..d22e01d5f --- /dev/null +++ b/src/lib/y2network/interface_config_builders/wireless.rb @@ -0,0 +1,65 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "forwardable" +require "y2network/config" +require "y2network/interface_config_builder" + +module Y2Network + module InterfaceConfigBuilders + # Builder for wireless configuration. Many methods delegated to ConnectionConfig::Wireless + # @see ConnectionConfig::Wireless + class Wireless < InterfaceConfigBuilder + extend Forwardable + include Yast::Logger + + def initialize(config: nil) + super(type: InterfaceType::WIRELESS, config: config) + end + + def access_point + @connection_config.ap + end + + def access_point=(value) + @connection_config.ap = value + end + + def_delegators :@connection_config, + :auth_mode, :auth_mode=, + :eap_mode, :eap_mode=, + :mode, :mode=, + :essid, :essid=, + :wpa_psk, :wpa_psk=, + :wpa_password, :wpa_password=, + :wpa_identity, :wpa_identity=, + :wpa_anonymous_identity, :wpa_anonymous_identity=, + :ca_cert, :ca_cert=, + :client_key, :client_key=, + :client_cert, :client_cert=, + :channel, :channel=, + :bitrate, :bitrate=, + :ap_scanmode, :ap_scanmode=, + :keys, :keys=, + :key_length, :key_length=, + :default_key, :default_key= + end + end +end diff --git a/src/lib/y2network/interface_type.rb b/src/lib/y2network/interface_type.rb index e024b9a0f..aef1c99dd 100644 --- a/src/lib/y2network/interface_type.rb +++ b/src/lib/y2network/interface_type.rb @@ -21,26 +21,23 @@ module Y2Network # This class represents the interface types which are supported. - # - # Constants may be defined using the {define_type} method. + # Class have helpers to check if given type is what needed. It check name and also short name: + # @example check for ethernet cards + # type.ethernet? + # @example check for wifi + # type.wlan? class InterfaceType extend Yast::I18n include Yast::I18n class << self - # @param const_name [String] Constant name - # @param name [String] Type name ("Ethernet", "Wireless", etc.) - # @param short_name [String] Short name used in legacy code - def define_type(const_name, name, short_name) - const_set(const_name, new(name, short_name)) - all << const_get(const_name) - end - # Returns all the existing types # # @return [Array] Interface types def all - @types ||= [] + @types ||= InterfaceType.constants + .map { |c| InterfaceType.const_get(c) } + .select { |c| c.is_a?(InterfaceType) } end # Returns the interface type with a given short name @@ -60,6 +57,8 @@ def from_short_name(short_name) # Constructor # # @param name [String] Type name + # @param short_name [String] short name as is used for prefixing of + # interface name (e.g. bond, eth or wlan) def initialize(name, short_name) textdomain "network" @name = name @@ -73,21 +72,85 @@ def to_human_string _(name) end - # Define types constants - define_type "ETHERNET", N_("Ethernet"), "eth" - define_type "WIRELESS", N_("Wireless"), "wlan" - define_type "INFINIBAND", N_("Infiniband"), "ib" - define_type "BONDING", N_("Bonding"), "bond" - define_type "BRIDGE", N_("Bridge"), "br" - define_type "DUMMY", N_("Dummy"), "dummy" - define_type "VLAN", N_("VLAN"), "vlan" - define_type "TUN", N_("TUN"), "tun" - define_type "TAP", N_("TAP"), "tap" - define_type "USB", N_("USB"), "usb" - # s390 - define_type "QETH", N_("QETH"), "qeth" - define_type "LCS", N_("LCS"), "lcs" - define_type "HIPERSOCKETS", N_("HiperSockets"), "hsi" - define_type "FICON", N_("FICON"), "ficon" + # Returns name for specialized class for this type e.g. for reader, write or builder + # @return [String] + def class_name + name.capitalize + end + + # Returns name for file without suffix for this type e.g. for reader, write or builder + # @return [String] + def file_name + name.downcase + end + + def respond_to_missing?(method_name, _include_private = false) + return false unless method_name.to_s.end_with?("?") + + target_name = method_name.to_s[0..-2] + InterfaceType.all.any? do |type| + type.name.downcase == target_name || + type.short_name == target_name + end + end + + def method_missing(method_name, *arguments, &block) + return super unless respond_to_missing?(method_name) + + if !arguments.empty? + raise ArgumentError, "no params are accepted for method #{method_name}" + end + + target_name = method_name.to_s[0..-2] + [name.downcase, short_name].include?(target_name) + end + + # Ethernet card, integrated or attached + ETHERNET = new(N_("Ethernet"), "eth") + # Wireless card, integrated or attached + WIRELESS = new(N_("Wireless"), "wlan") + # Infiniband card + INFINIBAND = new(N_("Infiniband"), "ib") + # Infiniband card child device. Used in IPoIB (IP-over-InfiniBand) + INFINIBAND_CHILD = new(N_("Infiniband Child"), "ibchild") + # Bonding device + BONDING = new(N_("Bonding"), "bond") + # bridge device + BRIDGE = new(N_("Bridge"), "br") + # virtual dummy device provided by dummy kernel module + DUMMY = new(N_("Dummy"), "dummy") + # Virtual LAN + VLAN = new(N_("VLAN"), "vlan") + # TUN virtual device provided by kernel, operates on layer3 carrying IP packagets + TUN = new(N_("TUN"), "tun") + # TAP virtual device provided by kernel, operates on layer2 carrying Ethernet frames + TAP = new(N_("TAP"), "tap") + # ethernet over usb device provided by usbnet kernel module. Do not confuse + # with ethernet card attached to USB slot as it is common ETHERNET type. + USB = new(N_("USB"), "usb") + # device using qeth device driver for s390. Can operate on layer2 or layer3. + QETH = new(N_("QETH"), "qeth") + # LAN-Channel-Station (LCS) network devices. S390 specific. + LCS = new(N_("LCS"), "lcs") + # HiperSockets s390 network device + HSI = new(N_("HSI"), "hsi") + # Channel To Channel. S390 specific + CTC = new(N_("CTC"), "ctc") + # FICON-attached direct access storage devices. s390 specific + FICON = new(N_("FICON"), "ficon") + # Point-to-Point Protocol + PPP = new(N_("Point-to-Point Protocol"), "ppp") + # Ip in Ip protocol + IPIP = new(N_("Ip-in-ip"), "ipip") + # IPv6 Tunnel interface + IPV6TNL = new(N_("IPv6 Tunnel"), "ip6tnl") + # IPv6 over IPv4 + SIT = new(N_("IPv6 over IPv4 Tunnel"), "sit") + # IP over IPv4 + GRE = new(N_("IP over IPv4 Tunnel"), "gre") + # Infrared + IRDA = new(N_("Infrared"), "irda") + # Loopback + LO = new(N_("Loopback"), "lo") end end diff --git a/src/lib/y2network/interfaces_collection.rb b/src/lib/y2network/interfaces_collection.rb index 3cb8bc2c7..48e3cc7a9 100644 --- a/src/lib/y2network/interfaces_collection.rb +++ b/src/lib/y2network/interfaces_collection.rb @@ -17,39 +17,38 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require "yast" require "y2network/interface" +require "y2network/can_be_copied" require "forwardable" module Y2Network - # A container for network devices. In the end should implement methods for mass operations over - # network interfaces like old LanItems::find_dhcp_ifaces. + # A container for network devices. # - # @example Create a new collection - # eth0 = Y2Network::Interface.new("eth0") - # collection = Y2Network::InterfacesCollection.new(eth0) + # Objects of this class are able to keep a list of interfaces and perform simple queries + # on such a list. In the end should implement methods for mass operations over network + # interfaces like old LanItems::find_dhcp_ifaces. + # + # @example Finding an interface by its name + # interfaces = Y2Network::InterfacesCollection.new([eth0, wlan0]) + # interfaces.by_name("wlan0") # => wlan0 # # @example Find an interface using its name # iface = collection.by_name("eth0") #=> # + # + # @example FIXME (not implemented yet). For the future, we are aiming at this kind of API. + # interfaces = Y2Network::InterfacesCollection.new([eth0, wlan0]) + # interfaces.of_type(:eth).to_a # => [eth0] class InterfacesCollection - # Objects of this class are able to keep a list of interfaces and perform simple queries - # on such a list. - # - # @example Finding an interface by its name - # interfaces = Y2Network::InterfacesCollection.new([eth0, wlan0]) - # interfaces.by_name("wlan0") # => wlan0 - # - # @example FIXME (not implemented yet). For the future, we are aiming at this kind of API. - # interfaces = Y2Network::InterfacesCollection.new([eth0, wlan0]) - # interfaces.of_type(:eth).to_a # => [eth0] - extend Forwardable include Yast::Logger + include CanBeCopied # @return [Array] List of interfaces attr_reader :interfaces alias_method :to_a, :interfaces - def_delegators :@interfaces, :each, :push, :<<, :reject!, :map, :flat_map, :any? + def_delegators :@interfaces, :each, :push, :<<, :reject!, :map, :flat_map, :any?, :size, :select, :find # Constructor # @@ -60,17 +59,27 @@ def initialize(interfaces = []) # Returns an interface with the given name if present # - # @todo It uses the hardware's name as a fallback if interface's name is not set + # @note It uses the hardware's name as a fallback if interface's name is not set # # @param name [String] interface name ("eth0", "br1", ...) # @return [Interface,nil] Interface with the given name or nil if not found def by_name(name) interfaces.find do |iface| - iface_name = iface.name || iface.hwinfo.name + iface_name = iface.name || iface.hardware.name iface_name == name end end + # Returns an interface with the given hardware busid if present + # + # @param busid [String] interface busid ("0.0.0700", "0000:00:19.0", ...) + # @return [Interface,nil] Interface with the given busid or nil if not found + def by_busid(busid) + interfaces.find do |iface| + iface.hardware && iface.hardware.busid == busid + end + end + # Returns list of interfaces of given type # # @param type [InterfaceType] device type @@ -79,6 +88,13 @@ def by_type(type) InterfacesCollection.new(interfaces.select { |i| i.type == type }) end + # Returns the list of physical interfaces + # + # @return [InterfacesCollection] List of physical interfaces + def physical + interfaces.select { |i| i.is_a?(PhysicalInterface) } + end + # Deletes elements which meet a given condition # # @return [InterfacesCollection] @@ -87,6 +103,16 @@ def delete_if(&block) self end + # Returns all interfaces names + # + # For those interfaces that are renamed, the new and old names are included + # in the list. + # + # @return [Array] List of known interfaces + def known_names + @interfaces.map { |i| [i.old_name, i.name] }.flatten.compact + end + # Compares InterfacesCollections # # @return [Boolean] true when both collections contain only equal interfaces, @@ -151,14 +177,21 @@ def bond_index index end - def all - # FIXME: this is only helper when coexisting with old LanItems module - # can be used in new API of network-ng for read-only methods. It converts - # old LanItems::Items into new Interface objects - Yast::LanItems.Items.map do |_index, item| - name = item["ifcfg"] || item["hwinfo"]["dev_name"] - type = Yast::NetworkInterfaces.GetType(name) - Y2Network::Interface.new(name, type: InterfaceType.from_short_name(type)) + # @return [String] returns free interface name for given prefix + def free_name(prefix) + free_names(prefix, 1).first + end + + # @return [Array] returns free interface name for given prefix + def free_names(prefix, count) + result = [] + # TODO: when switch rubocop use endless range `(0..)` + (0..100000).each do |i| + candidate = prefix + i.to_s + next if by_name(candidate) + + result << candidate + return result if result.size == count end end end diff --git a/src/lib/y2network/ip_address.rb b/src/lib/y2network/ip_address.rb new file mode 100644 index 000000000..4b64ca2c6 --- /dev/null +++ b/src/lib/y2network/ip_address.rb @@ -0,0 +1,107 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "ipaddr" +require "forwardable" + +module Y2Network + # This class represents an IP address + # + # The IPAddr from the Ruby standard library drops the host part according to the netmask. The + # problem is that YaST uses a CIDR-like string, including the host part, to set IPADDR in ifcfg-* + # files (see man 5 ifcfg for further details). + # + # @example IPAddr from standard library behavior + # ip = IPAddr.new("192.168.122.1/24") + # ip.to_s #=> "192.168.122.0/24" + # + # However, what we need is to be able to keep the host part + # + # @example IPAddress behaviour + # ip = IPAddress.new("192.168.122.1/24") + # ip.to_s #=> "192.168.122.1/24" + # + # @example IPAddress with no prefix + # ip = IPAddress.new("192.168.122.1") + # ip.to_s #=> "192.168.122.1" + class IPAddress + extend Forwardable + + # @return [IPAddr] IP address + attr_reader :address + # @return [Integer] Prefix + attr_accessor :prefix + + def_delegators :@address, :ipv4?, :ipv6? + + class << self + def from_string(str) + address, prefix = str.split("/") + prefix = prefix.to_i if prefix + new(address, prefix) + end + end + + # Constructor + # + # @param address [String] IP address without the prefix + # @param prefix [Integer] IP prefix (number of bits). If not specified, 32 will be used for IPv4 + # and 128 for IPv6. + def initialize(address, prefix = nil) + @address = IPAddr.new(address) + @prefix = prefix + end + + # Returns a string representation of the address + def to_s + prefix? ? "#{@address}/#{@prefix}" : @address.to_s + end + + # Sets the prefix from a netmask + # + # @param netmask [String] String representation of the netmask + def netmask=(netmask) + self.prefix = IPAddr.new("#{netmask}/#{netmask}").prefix + end + + # Sets the address from the string + # + # @param value [String] String representation of the address + def address=(value) + @address = IPAddr.new(value) + end + + # Determines whether two addresses are equivalent + # + # @param other [IPAddress] The address to compare with + # @return [Boolean] + def ==(other) + address == other.address && prefix == other.prefix + end + + alias_method :eql?, :== + + # Determines whether a prefix is defined + # + # @return [Boolean] + def prefix? + !!@prefix + end + end +end diff --git a/src/lib/y2network/ipoib_mode.rb b/src/lib/y2network/ipoib_mode.rb new file mode 100644 index 000000000..258f9a397 --- /dev/null +++ b/src/lib/y2network/ipoib_mode.rb @@ -0,0 +1,72 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" + +module Y2Network + # This class represents the supported IPoIB transport modes. + class IpoibMode + class << self + # Returns all the existing modes + # + # @return [Array] + def all + @all ||= IpoibMode.constants + .map { |c| IpoibMode.const_get(c) } + .select { |c| c.is_a?(IpoibMode) } + end + + # Returns the transport mode with a given name + # + # @param name [String] + # @return [IpoibMode,nil] Ipoib mode or nil if not found + def from_name(name) + all.find { |t| t.name == name } + end + end + + # @return [String] Returns mode name + attr_reader :name + + # Constructor + # + # @param name [String] mode name + def initialize(name) + @name = name + end + + # Determines whether two objects are equivalent + # + # They are equal when they refer to the same IPoIB mode (through the name). + # + # @param other [IpoibMode] IPoIB mode to compare with + # @return [Boolean] + def ==(other) + name == other.name + end + + alias_method :eql?, :== + + DATAGRAM = new("datagram") + CONNECTED = new("connected") + # Not a mode at all but the default value that will be choose by the IB + # driver (bnc#1086454) + DEFAULT = new("") + end +end diff --git a/src/lib/y2network/physical_interface.rb b/src/lib/y2network/physical_interface.rb index 98e536ff8..4f8d75b44 100644 --- a/src/lib/y2network/physical_interface.rb +++ b/src/lib/y2network/physical_interface.rb @@ -23,13 +23,50 @@ module Y2Network # Physical interface class (ethernet, wireless, infiniband...) class PhysicalInterface < Interface - attr_writer :hwinfo # @return [String] attr_accessor :ethtool_options - # @return [Hwinfo] - def hwinfo - @hwinfo ||= Hwinfo.new({}) + # User selected driver + # + # This driver will be set using a udev rule. + # + # @return [String] + attr_accessor :custom_driver + + # Constructor + # + # @param name [String] Interface name (e.g., "eth0") + # @param type [InterfaceType] Interface type + # @param hardware [Hwinfo] Hardware information + def initialize(name, type: InterfaceType::ETHERNET, hardware: nil) + super(name, type: type) + # @hardware and @name should not change during life of the object + @hardware = hardware || Hwinfo.for(name) || Hwinfo.new + @description = @hardware.name + end + + # Returns interface modalias + # + # @return [String,nil] Modalias + def modalias + @hardware.modalias + end + + # Returns the name of the current driver + # + # @return [String] + def current_driver + @hardware.module + end + + # Determines whether the interface is present (attached) + # + # It relies in the hardware information + # + # @return [Boolean] + # @see Interface#present? + def present? + @hardware.present? end end end diff --git a/src/lib/y2network/presenters/interface_status.rb b/src/lib/y2network/presenters/interface_status.rb new file mode 100644 index 000000000..03ec6916f --- /dev/null +++ b/src/lib/y2network/presenters/interface_status.rb @@ -0,0 +1,62 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +Yast.import "HTML" +Yast.import "NetHwDetection" + +module Y2Network + module Presenters + # Mixin that provide status info about interface `status_info(config)` + module InterfaceStatus + include Yast::I18n + # @param config [ConnectionConfig::Base] + # @return [String] status information + def status_info(config) + textdomain "network" + + case config.bootproto + when BootProtocol::STATIC + return Yast::HTML.Colorize(_("Configured without an address"), "red") if !config.ip + + ip = config.ip.address.to_s + host = Yast::NetHwDetection.ResolveIP(config.ip.address.address.to_s) + addr = ip + addr << "(#{host})" if host && !host.empty? + if config.ip.remote_address + # TRANSLATORS %{local} is local address and %{remote} is remote address + format( + _("Configured with address %{local} (remote %{remote})"), + local: addr, + remote: config.remote_address.to_s + ) + else + # TRANSLATORS %s is address + format(_("Configured with address %s"), addr) + end + when BootProtocol::NONE + _("Do not assign (e.g. bond or bridge slaves)") + else + # TODO: maybe human name for boot protocols? + format(_("Configured with %s"), config.bootproto.name) + end + end + end + end +end diff --git a/src/lib/y2network/presenters/interface_summary.rb b/src/lib/y2network/presenters/interface_summary.rb new file mode 100644 index 000000000..071fed1a8 --- /dev/null +++ b/src/lib/y2network/presenters/interface_summary.rb @@ -0,0 +1,123 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/presenters/interface_status" + +Yast.import "Summary" +Yast.import "HTML" + +module Y2Network + module Presenters + # This class converts a connection config configuration object into a string to be used + # in an AutoYaST summary or in a table. + class InterfaceSummary + include Yast::I18n + include InterfaceStatus + + # @return [String] + attr_reader :name + + # Constructor + # + # @param name [String] name of device to describe + # @param config [Y2Network::Config] + def initialize(name, config) + textdomain "network" + @name = name + @config = config + end + + def text + interface = @config.interfaces.by_name(@name) + hardware = interface ? interface.hardware : nil + descr = hardware ? hardware.description : "" + + connection = @config.connections.by_name(@name) + bullets = [] + rich = "" + + if connection + descr = connection.name if descr.empty? + + status = status_info(connection) + + bullets << _("Device Name: %s") % connection.name + bullets << status + bullets << connection.startmode.long_description + bullets += aliases_info(connection) + + if connection.type.bonding? + # TRANSLATORS: text label before list of slaves + label = _("Bonding Slaves") + bullets << "#{label}: #{connection.slaves.join(" ")}" + elsif connection.type.bridge? + # TRANSLATORS: text label before list of ports + label = _("Bridge Ports") + bullets << "#{label}: #{connection.ports.join(" ")}" + end + + master = connection.find_master(@config.connections) + if master + master_desc = if master.type.bonding? + # TRANSLATORS: text label before device which is master for this device + _("Bonding master") + else + # TRANSLATORS: text label before device which is bridge for this device + _("Bridge") + end + bullets << format("%s: %s", master_desc, master.name) + end + end + + if hardware.nil? || !hardware.exists? + rich << "(" << _("No hardware information") << ")
" + else + rich << "(" << _("Not connected") << ")
" if !hardware.link + rich << "MAC : " << hardware.mac << "
" if hardware.mac + rich << "BusID : " << hardware.busid << "
" if hardware.busid + # TODO: physical port id. Probably in hardware? + end + + rich = Yast::HTML.Bold(descr) + "
" + rich + if connection + rich << Yast::HTML.List(bullets) + else + if hardware && hardware.name && !hardware.name.empty? + dev_name = _("Device Name: %s") % hardware.name + rich << Yast::HTML.Bold(dev_name) << "
" + end + + rich << "

" + rich << _("The device is not configured. Press Edit\nto configure.\n") + rich << "

" + end + rich + end + + private + + def aliases_info(config) + config.ip_aliases.map do |alias_| + "#{alias_.address} (#{alias_.label})" + end + end + end + end +end diff --git a/src/lib/y2network/presenters/interfaces_summary.rb b/src/lib/y2network/presenters/interfaces_summary.rb new file mode 100644 index 000000000..1d021ba4a --- /dev/null +++ b/src/lib/y2network/presenters/interfaces_summary.rb @@ -0,0 +1,55 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/presenters/interface_status" + +Yast.import "Summary" +Yast.import "HTML" + +module Y2Network + module Presenters + # This class converts a connection config configurations into a string to be used + # in an AutoYaST summary. + class InterfacesSummary + include Yast::I18n + include Yast::Logger + include InterfaceStatus + + # @return [Config] + attr_reader :config + + def initialize(config) + @config = config + end + + def text + overview = config.interfaces.map do |interface| + connection = config.connections.by_name(interface.name) + descr = interface.hardware ? interface.hardware.description : "" + descr = interface.name if descr.empty? + status = connection ? status_info(connection) : Yast::Summary.NotConfigured + Yast::Summary.Device(descr, status) + end + + Yast::Summary.DevicesList(overview) + end + end + end +end diff --git a/src/lib/y2network/s390_device_activator.rb b/src/lib/y2network/s390_device_activator.rb new file mode 100644 index 000000000..03996db3c --- /dev/null +++ b/src/lib/y2network/s390_device_activator.rb @@ -0,0 +1,115 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "yast2/execute" + +module Y2Network + # This class is responsable of activating the supported S390 devices. + class S390DeviceActivator + extend Forwardable + include Yast::Logger + + # Command for configuring z Systems specific devices + CONFIGURE_CMD = "/sbin/chzdev".freeze + # Command for displaying configuration of z Systems specific devices + LIST_CMD = "/sbin/lszdev".freeze + + def_delegators :@builder, :type + + attr_accessor :builder + + # Load fresh instace of device activator for given interface config builder + def self.for(builder) + type = builder.type + require "y2network/s390_device_activators/#{type.file_name}" + S390DeviceActivators.const_get(type.class_name).new(builder) + rescue LoadError => e + log.info "Specialized device activator for #{type.short_name} not found. #{e.inspect}" + nil + end + + # Constructor + # + # @param builder [Y2Network::InterfaceConfigBuilder] + def initialize(builder) + @builder = builder + end + + # Each s390 device type permits a set of attributes to be passed as extra + # options to the configuration command. This method return a list of each + # option in the form "attribute=value" + # + # @example qeth options + # @activator.configure_attributes + # #=> ["bridge_role=primary", "layer2=1", "portno=0", "ipa_takeover/enable=1"] + # + # @example ctc options + # @activator.configure_attributes + # #=> ["protocol=1"] + # + # @return [Array] + def configure_attributes + [] + end + + # The device id to be used by lszdev or chzdev commands + # + # @return [String, nil] + def device_id + nil + end + + # Returns the complete device id which contains the given channel + # + # @param channel [String] + # @return [String] + def device_id_from(channel) + cmd = [LIST_CMD, type.short_name, "-c", "id", "-n"] + + Yast::Execute.stdout.on_target!(cmd).split("\n").find do |d| + d.include? channel + end + end + + # It tries to enable the interface with the configured device id + # + # @return [Boolean] true when enabled + def configure + return false unless device_id + cmd = [CONFIGURE_CMD, type.short_name, device_id, "-e"].concat(configure_attributes) + + Yast::Execute.on_target!(*cmd, allowed_exitstatus: 0..255).zero? + end + + # Obtains the enabled interface name associated with the device id + # + # @return [String] device name + def configured_interface + return "" unless device_id + cmd = [LIST_CMD, device_id, "-c", "names", "-n"] + + Yast::Execute.stdout.on_target!(cmd).chomp + end + + # Makes a new configuration proposal + def propose! + end + end +end diff --git a/src/lib/y2network/s390_device_activators/ctc.rb b/src/lib/y2network/s390_device_activators/ctc.rb new file mode 100644 index 000000000..6c3c771ec --- /dev/null +++ b/src/lib/y2network/s390_device_activators/ctc.rb @@ -0,0 +1,55 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/s390_device_activator" + +module Y2Network + module S390DeviceActivators + # This class is responsible of activating CTC group devices. + class Ctc < S390DeviceActivator + def_delegators :@builder, + :read_channel, :read_channel=, + :write_channel, :write_channel=, + :hwinfo, :protocol + + def device_id + return if read_channel.to_s.empty? + + [read_channel, write_channel].join(":") + end + + def configure_attributes + return [] unless builder.protocol + + ["protocol=#{builder.protocol}"] + end + + # Modifies the read and write channel from the the device id + def propose_channels + id = device_id_from(hwinfo.busid) + return unless id + self.read_channel, self.write_channel = id.split(":") + end + + def propose! + propose_channels unless device_id + end + end + end +end diff --git a/src/lib/y2network/s390_device_activators/lcs.rb b/src/lib/y2network/s390_device_activators/lcs.rb new file mode 100644 index 000000000..14097cdaa --- /dev/null +++ b/src/lib/y2network/s390_device_activators/lcs.rb @@ -0,0 +1,38 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/s390_device_activators/ctc" + +module Y2Network + module S390DeviceActivators + # The Lcs device activator is based in Ctc as both have two group device + # channels (read and write). + # + # In the past they shared also the configure command 'ctc_configure' and + # the 'protocol' attribute was needed, but as the configuration has + # been moved to 'chzdev' command it is not the case anymore. + class Lcs < Ctc + def configure_attributes + return [] unless builder.timeout + + ["lancmd_timeout=#{builder.timeout}"] + end + end + end +end diff --git a/src/lib/y2network/s390_device_activators/qeth.rb b/src/lib/y2network/s390_device_activators/qeth.rb new file mode 100644 index 000000000..fc6198a43 --- /dev/null +++ b/src/lib/y2network/s390_device_activators/qeth.rb @@ -0,0 +1,99 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/s390_device_activator" + +module Y2Network + module S390DeviceActivators + # This class is responsible of activating OSA-Express (QDIO) and + # HiperSockets group devices (qeth driver). + class Qeth < S390DeviceActivator + def_delegators :@builder, + :read_channel, :read_channel=, + :write_channel, :write_channel=, + :data_channel, :data_channel=, + :layer2, :port_number, :ipa_takeover, + :hwinfo, :attributes + + def device_id + return if read_channel.to_s.empty? + + [read_channel, write_channel, data_channel].join(":") + end + + # Return a list of the options to be set when activating the device. The + # list is composed by the attributes configured and the attributes that + # have their own variable (layer2, port_number and ipa_takeover). + # + # @example Qeth configuration + # activator.layer2 #=> true + # activator.port_number #=> 1 + # activator.ipa_takeover #=> true + # activator.attributes #=> "bridge_role=secondary" + # activator.configure_attributes #=> ["bridge_role=secondary", + # "ipa_takeover/enable=1", "layer2=1", "portno=1"] + # + # @see [S390DeviceActivator#configure_attributes] + def configure_attributes + extra_attributes = [] + extra_attributes.concat(attributes.split(" ")) if attributes + extra_attributes << ipa_takeover_attribute unless ipa_takeover.nil? + extra_attributes << layer2_attribute unless layer2.nil? + extra_attributes << port_attribute + end + + # Modifies the read, write and data channel from the the device id + def propose_channels + id = device_id_from(hwinfo.busid) + return unless id + self.read_channel, self.write_channel, self.data_channel = id.split(":") + end + + def propose! + propose_channels unless device_id + end + + private + + # Convenience method to obtain the layer2 attribute for the configuration + # command + # + # @return [String] + def layer2_attribute + "layer2=#{layer2 ? 1 : 0}" + end + + # Convenience method to obtain the port number attribute for the + # configuration command + # + # @return [String] + def port_attribute + "portno=#{port_number}" + end + + # Convenience method to obtain the port number attribute for the + # configuration command + # + # @return [String] + def ipa_takeover_attribute + "ipa_takeover/enable=#{ipa_takeover ? 1 : 0}" + end + end + end +end diff --git a/src/lib/y2network/sequences/interface.rb b/src/lib/y2network/sequences/interface.rb index dda30f679..885695aa6 100644 --- a/src/lib/y2network/sequences/interface.rb +++ b/src/lib/y2network/sequences/interface.rb @@ -1,8 +1,32 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" module Y2Network module Sequences - # Sequence for interface + # The responsibility of this class is to drive workflow for sequence of dialogs. + # + # In this case, allowed operations for interface are, 'add' for adding a new interface, + # 'edit' for editing an existing device / interface or 'init_s390' for s390 + # initialization which needs specific activation before using it. + # # TODO: use UI::Sequence, but it needs also another object dialogs e.g for wifi class Interface include Yast::I18n @@ -12,6 +36,7 @@ def initialize Yast.include self, "network/lan/wizards.rb" end + # Starts sequence for adding new interface and configuring it def add(default: nil) res = Y2Network::Dialogs::AddInterface.run(default: default) return unless res @@ -23,10 +48,12 @@ def add(default: nil) sym end + # Starts sequence for editing configuration of interface def edit(builder) NetworkCardSequence("edit", builder: builder) end + # Starts sequence for activating s390 device and configuring it def init_s390(builder) NetworkCardSequence("init_s390", builder: builder) end diff --git a/src/lib/y2network/startmode.rb b/src/lib/y2network/startmode.rb new file mode 100644 index 000000000..223e9cd21 --- /dev/null +++ b/src/lib/y2network/startmode.rb @@ -0,0 +1,58 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" + +module Y2Network + # Base class for startmode. It allows to create new one according to name or anlist all. + # Its child have to define `to_human_string` method and possibly its own specialized attributes. + # TODO: as backends differs, we probably also need to have flag there to which backends mode exists + class Startmode + include Yast::Logger + + attr_reader :name + alias_method :to_s, :name + + def initialize(name) + @name = name + end + + # gets new instance of startmode for given type and its params + def self.create(name) + name = "auto" if name == "onboot" # onboot is alias for auto + # avoid circular dependencies + require "y2network/startmodes" + Startmodes.const_get(name.capitalize).new + rescue NameError => e + log.error "Invalid startmode #{e.inspect}" + nil + end + + def self.all + require "y2network/startmodes" + Startmodes.constants.map { |c| Startmodes.const_get(c).new } + end + + def ==(other) + name == other.name + end + + alias_method :eql?, :== + end +end diff --git a/src/lib/y2network/startmodes.rb b/src/lib/y2network/startmodes.rb new file mode 100644 index 000000000..4529e9a7e --- /dev/null +++ b/src/lib/y2network/startmodes.rb @@ -0,0 +1,6 @@ +require "y2network/startmodes/auto" +require "y2network/startmodes/hotplug" +require "y2network/startmodes/ifplugd" +require "y2network/startmodes/manual" +require "y2network/startmodes/nfsroot" +require "y2network/startmodes/off" diff --git a/src/lib/y2network/startmodes/auto.rb b/src/lib/y2network/startmodes/auto.rb new file mode 100644 index 000000000..4e3419ff6 --- /dev/null +++ b/src/lib/y2network/startmodes/auto.rb @@ -0,0 +1,51 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/startmode" + +module Y2Network + module Startmodes + # Auto start mode + # + # Interface will be set up as soon as it is available (and service network was started). + # This either happens at boot time when network is starting or via hotplug when a interface + # is added to the system (by adding a device or loading a driver). + # To be backward compliant onboot, on and boot are aliases for auto. + # TODO: when reading use that aliases + class Auto < Startmode + include Yast::I18n + + def initialize + textdomain "network" + + super("auto") + end + + def to_human_string + _("At Boot Time") + end + + def long_description + _( + "Started automatically at boot" + ) + end + end + end +end diff --git a/src/lib/y2network/startmodes/hotplug.rb b/src/lib/y2network/startmodes/hotplug.rb new file mode 100644 index 000000000..e8a03f67b --- /dev/null +++ b/src/lib/y2network/startmodes/hotplug.rb @@ -0,0 +1,48 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/startmode" + +module Y2Network + module Startmodes + # hotplug startmode + # + # Interface will be activated when it is available. Use instead of auto for devices which + # may be missed, such as bonding slaves, usb or other plugable hardware. + class Hotplug < Startmode + include Yast::I18n + + def initialize + textdomain "network" + + super("hotplug") + end + + def to_human_string + _("On Hotplug") + end + + def long_description + _( + "Started automatically when attached" + ) + end + end + end +end diff --git a/src/lib/y2network/startmodes/ifplugd.rb b/src/lib/y2network/startmodes/ifplugd.rb new file mode 100644 index 000000000..f3f6b7a99 --- /dev/null +++ b/src/lib/y2network/startmodes/ifplugd.rb @@ -0,0 +1,59 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/startmode" + +module Y2Network + module Startmodes + # ifplugd startmode with additional attribute for priority. + # + # Boot of interface is managed by ifplugd daemon and not wicked. So it cannot be found in + # ifcfg man page. + # Ifplugd is a daemon which will automatically configure your ethernet device when a cable + # is plugged in and automatically unconfigure it if the cable is pulled. This is useful on + # laptops with on-board network adapters, since it will only configure the interface when a + # cable is really connected. + class Ifplugd < Startmode + include Yast::I18n + attr_accessor :priority + + def initialize + textdomain "network" + + @priority = 0 + + super("ifplugd") + end + + def to_human_string + _("On Cable Connection") + end + + def ==(other) + name == other.name && priority == other.priority + end + + def long_description + _( + "Started automatically on cable connection" + ) + end + end + end +end diff --git a/src/lib/y2network/startmodes/manual.rb b/src/lib/y2network/startmodes/manual.rb new file mode 100644 index 000000000..12713bb6b --- /dev/null +++ b/src/lib/y2network/startmodes/manual.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/startmode" + +module Y2Network + module Startmodes + # Manual startmode + # + # Interface will be set up if ip is called manually + class Manual < Startmode + include Yast::I18n + + def initialize + textdomain "network" + + super("manual") + end + + def to_human_string + _("Manually") + end + + def long_description + _( + "Started manually" + ) + end + end + end +end diff --git a/src/lib/y2network/startmodes/nfsroot.rb b/src/lib/y2network/startmodes/nfsroot.rb new file mode 100644 index 000000000..c8fd51133 --- /dev/null +++ b/src/lib/y2network/startmodes/nfsroot.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/startmode" + +module Y2Network + module Startmodes + # NFS root startmode + # + # Nearly like auto, but interfaces with this startmode will be not shut down by default. + # Use this mode when you use a root filesystem via network or want to avoid interface shutdown. + # To force a nfsroot interface down, use either + # `wicked ifdown --force device-down ` or `ifdown -o force`. + class Nfsroot < Startmode + include Yast::I18n + + def initialize + textdomain "network" + + super("nfsroot") + end + + def to_human_string + _("On NFSroot") + end + + def long_description + _( + "Started automatically at boot (mandatory)" + ) + end + end + end +end diff --git a/src/lib/y2network/startmodes/off.rb b/src/lib/y2network/startmodes/off.rb new file mode 100644 index 000000000..78596e573 --- /dev/null +++ b/src/lib/y2network/startmodes/off.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/startmode" + +module Y2Network + module Startmodes + # disabled startmode + # + # Interface will never be activated. + class Off < Startmode + include Yast::I18n + + def initialize + textdomain "network" + + super("off") + end + + def to_human_string + _("Never") + end + + def long_description + _( + "Will not be started at all" + ) + end + end + end +end diff --git a/src/lib/y2network/sysconfig/config_reader.rb b/src/lib/y2network/sysconfig/config_reader.rb index d58784a0f..218d83b71 100644 --- a/src/lib/y2network/sysconfig/config_reader.rb +++ b/src/lib/y2network/sysconfig/config_reader.rb @@ -28,6 +28,7 @@ require "y2network/interfaces_collection" Yast.import "NetworkInterfaces" +Yast.import "Host" module Y2Network module Sysconfig @@ -40,18 +41,25 @@ def initialize(_opts = {}) # @return [Y2Network::Config] Network configuration def config + # NOTE: This code might be moved outside of the Sysconfig namespace, as it is generic. + Yast::Host.Read + routing_tables = find_routing_tables(interfaces_reader.interfaces) routing = Routing.new( tables: routing_tables, forward_ipv4: forward_ipv4?, forward_ipv6: forward_ipv6? ) - Config.new( + result = Config.new( interfaces: interfaces_reader.interfaces, connections: interfaces_reader.connections, + drivers: interfaces_reader.drivers, routing: routing, dns: dns, source: :sysconfig ) + + log.info "Sysconfig reader result: #{result.inspect}" + result end private diff --git a/src/lib/y2network/sysconfig/config_writer.rb b/src/lib/y2network/sysconfig/config_writer.rb index e8c5a5496..c777f80e8 100644 --- a/src/lib/y2network/sysconfig/config_writer.rb +++ b/src/lib/y2network/sysconfig/config_writer.rb @@ -21,22 +21,28 @@ require "y2network/sysconfig_paths" require "y2network/sysconfig/routes_file" require "y2network/sysconfig/dns_writer" +require "y2network/sysconfig/connection_config_writer" +require "y2network/sysconfig/interfaces_writer" + +Yast.import "Host" module Y2Network module Sysconfig # This class imports a configuration into YaST modules # # Ideally, it should be responsible of writing the changes to the underlying - # system. But, for the time being, it just relies in {Yast::Routing}. + # system. class ConfigWriter + include Yast::Logger + # Writes the configuration into YaST network related modules # # @param config [Y2Network::Config] Configuration to write # @param old_config [Y2Network::Config] Old configuration def write(config, old_config = nil) - return unless config.routing - - write_ip_forwarding(config.routing) + log.info "Writing configuration: #{config.inspect}" + log.info "Old configuration: #{old_config.inspect}" + write_ip_forwarding(config.routing) if config.routing write_interface_changes(config, old_config) # update /etc/sysconfig/network/routes file @@ -44,7 +50,13 @@ def write(config, old_config = nil) file.routes = find_routes_for(nil, config.routing.routes) file.save + write_drivers(config.drivers) + write_interfaces(config.interfaces) + write_connections(config.connections, old_config) write_dns_settings(config, old_config) + + # NOTE: This code might be moved outside of the Sysconfig namespace, as it is generic. + Yast::Host.Write(gui: false) end private @@ -60,11 +72,14 @@ def write(config, old_config = nil) def write_interface_changes(config, old_config) # Write ifroute files config.interfaces.each do |dev| + # S390 devices that have not been activated yet will be part of the + # collection but with an empty name. + next if dev.name.empty? routes = find_routes_for(dev, config.routing.routes) file = routes_file_for(dev) # Remove ifroutes-* if empty or interface is not configured - file.remove if routes.empty? || !dev.configured + file.remove if routes.empty? || !config.configured_interface?(dev.name) file.routes = routes file.save @@ -165,6 +180,50 @@ def write_dns_settings(config, old_config) writer = Y2Network::Sysconfig::DNSWriter.new writer.write(config.dns, old_dns) end + + # Updates the interfaces configuration + # + # @param interfaces [Y2Network::InterfacesCollection] + # @see Y2Network::Sysconfig::InterfacesWriter + def write_interfaces(interfaces) + writer = Y2Network::Sysconfig::InterfacesWriter.new + writer.write(interfaces) + end + + # Writes connections configuration + # + # @todo Handle old connections (removing those that are not needed, etc.) + # + # @param conns [Array] Connections to write + # @param old_config [Y2Network::Config,nil] Old configuration; nil if it is unknown + def write_connections(conns, old_config) + # FIXME: this code might live in its own class + writer = Y2Network::Sysconfig::ConnectionConfigWriter.new + remove_old_connections(conns, old_config.connections, writer) if old_config + conns.each do |conn| + old_conn = old_config ? old_config.connections.by_ids(conn.id).first : nil + writer.write(conn, old_conn) + end + end + + # Writes drivers options + # + # @param drivers [Array] Drivers to write options + def write_drivers(drivers) + Y2Network::Driver.write_options(drivers) + end + + # Removes old connections files + # + # @param conns [ConnectionConfigsCollection] New connections + # @param old_conns [ConnectionConfigsCollection] Old connections + # @param writer [Sysconfig::ConnectionConfigWriter] Writer instance to save changes + def remove_old_connections(conns, old_conns, writer) + ids_to_remove = old_conns.map(&:id) - conns.map(&:id) + to_remove = old_conns.by_ids(*ids_to_remove) + log.info "removing connections #{to_remove.map(&:name).inspect}" + to_remove.each { |c| writer.remove(c) } + end end end end diff --git a/src/lib/y2network/sysconfig/connection_config_reader.rb b/src/lib/y2network/sysconfig/connection_config_reader.rb index 08b57f899..6506366d1 100644 --- a/src/lib/y2network/sysconfig/connection_config_reader.rb +++ b/src/lib/y2network/sysconfig/connection_config_reader.rb @@ -28,12 +28,13 @@ class ConnectionConfigReader # Constructor # # @param name [String] Interface name - # @param type [Symbol,nil] Interface type (:eth, :wlan, etc.); if the type is unknown, + # @param type [InterfaceType, string, nil] Interface type; if the type is unknown, # `nil` can be used and it will be guessed from the configuration file is possible. # # @return [Y2Network::ConnectionConfig::Base] def read(name, type) file = Y2Network::Sysconfig::InterfaceFile.new(name) + file.load handler_class = find_handler_class(type || file.type) return nil if handler_class.nil? handler_class.new(file).connection_config @@ -43,11 +44,17 @@ def read(name, type) # Returns the class to handle a given interface type # - # @param type [Symbol] + # @param type [String, InterfaceType, nil] interface type or its short name # @return [Class] A class which belongs to the ConnectionConfigReaders module def find_handler_class(type) - require "y2network/sysconfig/connection_config_readers/#{type}" - ConnectionConfigReaders.const_get(type.to_s.capitalize) + return unless type + + if !type.is_a?(InterfaceType) + t = InterfaceType.from_short_name(type) or raise "Unknown type #{type.inspect} #{type.class.inspect}" + type = t + end + require "y2network/sysconfig/connection_config_readers/#{type.file_name}" + ConnectionConfigReaders.const_get(type.class_name) rescue LoadError, NameError => e log.info "Unknown connection type: '#{type}'. " \ "Connection handler could not be loaded: #{e.message}" diff --git a/src/lib/y2network/sysconfig/connection_config_readers/base.rb b/src/lib/y2network/sysconfig/connection_config_readers/base.rb new file mode 100644 index 000000000..7cdf888ee --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/base.rb @@ -0,0 +1,130 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/ip_config" +require "y2network/ip_address" +require "y2network/boot_protocol" +require "y2network/startmode" + +Yast.import "Host" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This is the base class for connection config readers. + # + # The derived classes should implement {#update_connection_config} method. + # methods. + class Base + # @return [Y2Network::Sysconfig::InterfaceFile] Interface's configuration file + attr_reader :file + + # Constructor + # + # @param file [Y2Network::Sysconfig::InterfaceFile] Interface's configuration file + def initialize(file) + @file = file + end + + # Builds a connection configuration object + # + # @return [Y2Network::ConnectionConfig::Base] + def connection_config + connection_class.new.tap do |conn| + conn.bootproto = BootProtocol.from_name(file.bootproto || "static") + conn.description = file.name + conn.interface = file.interface + conn.ip = all_ips.find { |i| i.id.empty? } + conn.ip_aliases = all_ips.reject { |i| i.id.empty? } + conn.name = file.interface + conn.lladdress = file.lladdr + conn.startmode = Startmode.create(file.startmode || "manual") + conn.startmode.priority = file.ifplugd_priority if conn.startmode.name == "ifplugd" + conn.ethtool_options = file.ethtool_options + conn.firewall_zone = file.zone + conn.hostname = hostname(conn) + update_connection_config(conn) + end + end + + private + + # Returns the class of the connection configuration + # + # @return [Class] + def connection_class + class_name = self.class.to_s.split("::").last + file_name = class_name.gsub(/(\w)([A-Z])/, "\\1_\\2").downcase + require "y2network/connection_config/#{file_name}" + Y2Network::ConnectionConfig.const_get(class_name) + end + + # Sets connection config settings from the given file + # + # @note This method should be redefined by derived classes. + # + # @param _conn [Y2Network::ConnectionConfig::Base] + def update_connection_config(_conn) + end + + # Returns the IPs configuration from the file + # + # @return [Array] IP addresses configuration + # @see Y2Network::ConnectionConfig::IPConfig + def all_ips + @all_ips ||= file.ipaddrs.each_with_object([]) do |(id, ip), all| + next unless ip.is_a?(Y2Network::IPAddress) + ip_address = build_ip(ip, file.prefixlens[id], file.netmasks[id]) + all << Y2Network::ConnectionConfig::IPConfig.new( + ip_address, + id: id, + label: file.labels[id], + remote_address: file.remote_ipaddrs[id], + broadcast: file.broadcasts[id] + ) + end + end + + # Builds an IP address + # + # It takes an IP address and, optionally, a prefix or a netmask. + # + # @param ip [Y2Network::IPAddress] IP address + # @param prefix [Integer,nil] Address prefix + # @param netmask [String,nil] Netmask + def build_ip(ip, prefix, netmask) + ipaddr = ip.clone + return ipaddr if ip.prefix? + ipaddr.netmask = netmask if netmask + ipaddr.prefix = prefix if prefix + ipaddr + end + + # Returns the hostname for the given connection + # + # @return [String,nil] + def hostname(conn) + return nil unless conn.ip + Yast::Host.Read + Yast::Host.names(conn.ip.address.address.to_s).first + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/bonding.rb b/src/lib/y2network/sysconfig/connection_config_readers/bonding.rb new file mode 100644 index 000000000..96a86a317 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/bonding.rb @@ -0,0 +1,45 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Bonding object given a + # SysconfigInterfaceFile object. + class Bonding < Base + private + + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.slaves = slaves + conn.options = file.bonding_module_opts + end + + # Convenience method to obtain the bonding slaves defined in the file + # + # @return [Array] bonding slaves defined in the file + def slaves + (file.bonding_slaves || {}).values + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/bridge.rb b/src/lib/y2network/sysconfig/connection_config_readers/bridge.rb new file mode 100644 index 000000000..f62add362 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/bridge.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Bridge object given a + # SysconfigInterfaceFile object. + class Bridge < Base + private + + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.ports = file.bridge_ports ? file.bridge_ports.split(" ") : [] + conn.stp = file.bridge_stp + conn.forward_delay = file.bridge_forwarddelay + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/ctc.rb b/src/lib/y2network/sysconfig/connection_config_readers/ctc.rb new file mode 100644 index 000000000..44afadb99 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/ctc.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Ctc object given a + # Sysconfig::InterfaceFile object. + class Ctc < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/dummy.rb b/src/lib/y2network/sysconfig/connection_config_readers/dummy.rb new file mode 100644 index 000000000..8cf54a7ed --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/dummy.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Dummy object given a + # Sysconfig::InterfaceFile object. + class Dummy < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/eth.rb b/src/lib/y2network/sysconfig/connection_config_readers/ethernet.rb similarity index 60% rename from src/lib/y2network/sysconfig/connection_config_readers/eth.rb rename to src/lib/y2network/sysconfig/connection_config_readers/ethernet.rb index ca0280947..c7e4d3640 100644 --- a/src/lib/y2network/sysconfig/connection_config_readers/eth.rb +++ b/src/lib/y2network/sysconfig/connection_config_readers/ethernet.rb @@ -17,31 +17,18 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -require "y2network/connection_config/ethernet" +require "y2network/sysconfig/connection_config_readers/base" module Y2Network module Sysconfig module ConnectionConfigReaders # This class is able to build a ConnectionConfig::Ethernet object given a # Sysconfig::InterfaceFile object. - class Eth - # @return [Y2Network::Sysconfig::InterfaceFile] - attr_reader :file + class Ethernet < Base + private - # Constructor - # - # @param file [Y2Network::Sysconfig::InterfaceFile] File to get interface configuration from - def initialize(file) - @file = file - end - - # @return [Y2Network::ConnectionConfig::Ethernet] - def connection_config - Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| - conn.interface = file.name - conn.bootproto = file.bootproto - conn.ip_address = file.ip_address - end + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(_conn) end end end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/hsi.rb b/src/lib/y2network/sysconfig/connection_config_readers/hsi.rb new file mode 100644 index 000000000..ff39ce9cf --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/hsi.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Hsi object given a + # Sysconfig::InterfaceFile object. + class Hsi < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/infiniband.rb b/src/lib/y2network/sysconfig/connection_config_readers/infiniband.rb new file mode 100644 index 000000000..7b4b68929 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/infiniband.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" +require "y2network/ipoib_mode" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Infiniband object given a + # SysconfigInterfaceFile object. + class Infiniband < Base + private + + # @param conn [Y2Network::ConnectionConfig::Infiniband] + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.ipoib_mode = IpoibMode.from_name(file.ipoib_mode.to_s) + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/lcs.rb b/src/lib/y2network/sysconfig/connection_config_readers/lcs.rb new file mode 100644 index 000000000..9b7a50a6b --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/lcs.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Lcs object given a + # Sysconfig::InterfaceFile object. + class Lcs < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/qeth.rb b/src/lib/y2network/sysconfig/connection_config_readers/qeth.rb new file mode 100644 index 000000000..3b08c4b26 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/qeth.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Qeth object given a + # Sysconfig::InterfaceFile object. + class Qeth < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/tap.rb b/src/lib/y2network/sysconfig/connection_config_readers/tap.rb new file mode 100644 index 000000000..907a89efc --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/tap.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Tap object given a + # Sysconfig::InterfaceFile object. + class Tap < Base + private + + # @param conn [Y2Network::ConnectionConfig::Tap] + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.owner = file.tunnel_set_owner + conn.group = file.tunnel_set_group + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/tun.rb b/src/lib/y2network/sysconfig/connection_config_readers/tun.rb new file mode 100644 index 000000000..c3df84f38 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/tun.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Tun object given a + # Sysconfig::InterfaceFile object. + class Tun < Base + private + + # @param conn [Y2Network::ConnectionConfig::Tun] + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.owner = file.tunnel_set_owner + conn.group = file.tunnel_set_group + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/usb.rb b/src/lib/y2network/sysconfig/connection_config_readers/usb.rb new file mode 100644 index 000000000..19d03a9b5 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/usb.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Usb object given a + # Sysconfig::InterfaceFile object. + class Usb < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/vlan.rb b/src/lib/y2network/sysconfig/connection_config_readers/vlan.rb new file mode 100644 index 000000000..978e812cb --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/vlan.rb @@ -0,0 +1,40 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" +require "y2network/connection_config/vlan" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Vlan object given a + # SysconfigInterfaceFile object. + class Vlan < Base + private + + # @param conn [Y2Network::ConnectionConfig::Vlan] + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.parent_device = file.etherdevice + conn.vlan_id = file.vlan_id + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/wireless.rb b/src/lib/y2network/sysconfig/connection_config_readers/wireless.rb new file mode 100644 index 000000000..c4017592c --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_readers/wireless.rb @@ -0,0 +1,64 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_readers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigReaders + # This class is able to build a ConnectionConfig::Wireless object given a + # Sysconfig::InterfaceFile object. + class Wireless < Base + private + + # @see Y2Network::Sysconfig::ConnectionConfigReaders::Base#update_connection_config + def update_connection_config(conn) + conn.ap = file.wireless_ap + conn.ap_scanmode = file.wireless_ap_scanmode + conn.auth_mode = file.wireless_auth_mode + conn.default_key = file.wireless_default_key + conn.eap_auth = file.wireless_eap_auth + conn.eap_mode = file.wireless_eap_mode + conn.essid = file.wireless_essid + conn.key_length = file.wireless_key_length + conn.keys = wireless_keys + conn.mode = file.wireless_mode + conn.nwid = file.wireless_nwid + conn.ca_cert = file.wireless_ca_cert + conn.client_cert = file.wireless_client_cert + conn.client_key = file.wireless_client_key + conn.wpa_password = file.wireless_wpa_password + conn.wpa_psk = file.wireless_wpa_psk + conn.wpa_identity = file.wireless_wpa_identity + conn.wpa_anonymous_identity = file.wireless_wpa_anonid + conn.channel = file.wireless_channel + conn.bitrate = file.wireless_rate + end + + # Max number of wireless keys + MAX_WIRELESS_KEYS = 4 + + # Reads the array of wireless keys from the file + def wireless_keys + (0..MAX_WIRELESS_KEYS - 1).map { |i| file.wireless_keys["_#{i}"] } + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_readers/wlan.rb b/src/lib/y2network/sysconfig/connection_config_readers/wlan.rb deleted file mode 100644 index 372e6a41e..000000000 --- a/src/lib/y2network/sysconfig/connection_config_readers/wlan.rb +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) [2019] SUSE LLC -# -# All Rights Reserved. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of version 2 of the GNU General Public License as published -# by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, contact SUSE LLC. -# -# To contact SUSE LLC about this file by physical or electronic mail, you may -# find current contact information at www.suse.com. - -require "y2network/connection_config/wireless" - -module Y2Network - module Sysconfig - module ConnectionConfigReaders - # This class is able to build a ConnectionConfig::Wireless object given a - # Sysconfig::InterfaceFile object. - class Wlan - # @return [Y2Network::Sysconfig::InterfaceFile] - attr_reader :file - - def initialize(file) - @file = file - end - - # Returns a wireless connection configuration - # - # @return [ConnectionConfig::Wireless] - def connection_config - Y2Network::ConnectionConfig::Wireless.new.tap do |conn| - conn.interface = file.name - conn.bootproto = file.bootproto - conn.ip_address = file.ip_address - conn.ap = file.wireless_ap - conn.ap_scanmode = file.wireless_ap_scanmode - conn.auth_mode = file.wireless_auth_mode - conn.default_key = file.wireless_default_key - conn.eap_auth = file.wireless_eap_auth - conn.eap_mode = file.wireless_eap_mode - conn.essid = file.wireless_essid - conn.key_length = file.wireless_key_length - conn.keys = file.wireless_keys - conn.mode = file.wireless_mode - conn.nwid = file.wireless_nwid - conn.wpa_password = file.wireless_wpa_password - conn.wpa_psk = file.wireless_wpa_psk - end - end - end - end - end -end diff --git a/src/lib/y2network/sysconfig/connection_config_writer.rb b/src/lib/y2network/sysconfig/connection_config_writer.rb new file mode 100644 index 000000000..dc6aa3053 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writer.rb @@ -0,0 +1,73 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/interface_file" +require "y2network/sysconfig/routes_file" + +Yast.import "Host" + +module Y2Network + module Sysconfig + # This class is responsible for writing interfaces changes + class ConnectionConfigWriter + include Yast::Logger + + # Writes connection config to the underlying system + # + # The method can receive the old configuration in order to perform clean-up tasks. + # + # @param conn [Y2Network::ConnectionConfig::Base] Connection configuration to write + # @param old_conn [Y2Network::ConnectionConfig::Base,nil] Connection configuration to write + def write(conn, old_conn = nil) + return if conn == old_conn + file = Y2Network::Sysconfig::InterfaceFile.new(conn.interface) + handler_class = find_handler_class(conn.type) + return nil if handler_class.nil? + remove(old_conn) if old_conn + file.clean + handler_class.new(file).write(conn) + file.save + end + + # Removes connection config from the underlying system + # + # @param conn [Y2Network::Conn] Connection name to remove + def remove(conn) + ifcfg = Y2Network::Sysconfig::InterfaceFile.find(conn.interface) + ifcfg && ifcfg.remove + Yast::Host.remove_ip(conn.ip.address.address.to_s) if conn.ip + end + + private + + # Returns the class to handle a given interface type + # + # @param type [Y2Network::InterfaceType] Interface type + # @return [Class] A class which belongs to the ConnectionConfigWriters module + def find_handler_class(type) + require "y2network/sysconfig/connection_config_writers/#{type.file_name}" + ConnectionConfigWriters.const_get(type.class_name) + rescue LoadError, NameError => e + log.info "Unknown connection type: '#{type}'. " \ + "Connection handler could not be loaded: #{e.message}" + nil + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/base.rb b/src/lib/y2network/sysconfig/connection_config_writers/base.rb new file mode 100644 index 000000000..b9e35768e --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/base.rb @@ -0,0 +1,97 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/connection_config/ip_config" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This is the base class for connection config writers. + # + # The derived classes should implement {#update_file} method. + class Base + # @return [Y2Network::Sysconfig::InterfaceFile] Interface's configuration file + attr_reader :file + + # Constructor + # + # @param file [Y2Network::Sysconfig::InterfaceFile] Interface's configuration file + def initialize(file) + @file = file + end + + # Writes connection information to the interface configuration file + # + # @param conn [Y2Network::ConnectionConfig::Base] Connection to take settings from + def write(conn) + file.bootproto = conn.bootproto.name + file.name = conn.description + file.lladdr = conn.lladdress + file.startmode = conn.startmode.to_s + file.ifplugd_priority = conn.startmode.priority if conn.startmode.name == "ifplugd" + if conn.ethtool_options && !conn.ethtool_options.empty? + file.ethtool_options = conn.ethtool_options + end + file.zone = conn.firewall_zone + add_ips(conn) + update_file(conn) + add_hostname(conn) if conn.bootproto.static? + end + + private + + # Sets file values from the given connection configuration + # + # @note This method should be redefined by derived classes. + # + # @param _conn [Y2Network::ConnectionConfig::Base] + def update_file(_conn) + end + + # Adds IP addresses + # + # @param conn [Y2Network::ConnectionConfig::Base] Connection to take settings from + def add_ips(conn) + file.ipaddrs.clear + ips_to_add = conn.ip_aliases.clone + ips_to_add << conn.ip if conn.ip && !conn.bootproto.dhcp? + ips_to_add.each { |i| add_ip(i) } + end + + # Adds a single IP to the file + # + # @param ip [Y2Network::IPAddress] IP address to add + def add_ip(ip) + file.ipaddrs[ip.id] = ip.address + file.labels[ip.id] = ip.label + file.remote_ipaddrs[ip.id] = ip.remote_address + file.broadcasts[ip.id] = ip.broadcast + end + + # Adds the hostname to /etc/hosts + # + # @param conn [Y2Network::ConnectionConfig::Base] Connection to take settings from + def add_hostname(conn) + return unless conn.hostname && conn.ip + Yast::Host.Update("", conn.hostname, conn.ip.address.address.to_s) + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/bonding.rb b/src/lib/y2network/sysconfig/connection_config_writers/bonding.rb new file mode 100644 index 000000000..43e0cb939 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/bonding.rb @@ -0,0 +1,48 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Bonding + # object to the underlying system. + class Bonding < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + # @param conn [Y2Network::ConnectionConfig::Bonding] Configuration to write + def update_file(conn) + file.bonding_slaves = file_slaves(conn) + file.bonding_module_opts = conn.options + file.bonding_master = "yes" + end + + # Convenience method to obtain the map of bonding slaves in the file + # format + # + # @return [Hash] indexed bonding slaves + def file_slaves(conn) + conn.slaves.each_with_index.with_object({}) { |(name, i), h| h[i] = name } + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/bridge.rb b/src/lib/y2network/sysconfig/connection_config_writers/bridge.rb new file mode 100644 index 000000000..37b41dd8d --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/bridge.rb @@ -0,0 +1,42 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a + # ConnectionConfig::Bridge object to the underlying system. + class Bridge < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + # @param conn [Y2Network::ConnectionConfig::Bridge] Configuration to + # write + def update_file(conn) + file.bridge = "yes" + file.bridge_ports = conn.ports.join(" ") + file.bridge_forwarddelay = conn.forward_delay + file.bridge_stp = conn.stp ? "on" : "off" + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/ctc.rb b/src/lib/y2network/sysconfig/connection_config_writers/ctc.rb new file mode 100644 index 000000000..59e7b670b --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/ctc.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Ctc + # object to the underlying system. + class Ctc < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/dummy.rb b/src/lib/y2network/sysconfig/connection_config_writers/dummy.rb new file mode 100644 index 000000000..ff4b738ad --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/dummy.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Dummy + # object to the underlying system. + class Dummy < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + def update_file(_conn) + # Force the interfacetype otherwise there is no way to infer the type + # from the file values (bsc#1129552) + file.interfacetype = "dummy" + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/ethernet.rb b/src/lib/y2network/sysconfig/connection_config_writers/ethernet.rb new file mode 100644 index 000000000..733e3934d --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/ethernet.rb @@ -0,0 +1,36 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Ethernet + # object to the underlying system. + class Ethernet < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + def update_file(_conn) + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/hsi.rb b/src/lib/y2network/sysconfig/connection_config_writers/hsi.rb new file mode 100644 index 000000000..46eaf2033 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/hsi.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a + # ConnectionConfig::Hsi object to the underlying system. + class Hsi < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/infiniband.rb b/src/lib/y2network/sysconfig/connection_config_writers/infiniband.rb new file mode 100644 index 000000000..9dc18b74e --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/infiniband.rb @@ -0,0 +1,37 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Infiniband + # object to the underlying system. + class Infiniband < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + def update_file(conn) + file.ipoib_mode = conn.ipoib_mode.name + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/lcs.rb b/src/lib/y2network/sysconfig/connection_config_writers/lcs.rb new file mode 100644 index 000000000..98325bee7 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/lcs.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Lcs + # object to the underlying system. + class Lcs < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/qeth.rb b/src/lib/y2network/sysconfig/connection_config_writers/qeth.rb new file mode 100644 index 000000000..62ca35e47 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/qeth.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Qeth + # object to the underlying system. + class Qeth < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/tap.rb b/src/lib/y2network/sysconfig/connection_config_writers/tap.rb new file mode 100644 index 000000000..ce2a50d6a --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/tap.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Tap + # object to the underlying system. + class Tap < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + def update_file(conn) + file.tunnel = "tap" + file.tunnel_set_owner = conn.owner + file.tunnel_set_group = conn.group + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/tun.rb b/src/lib/y2network/sysconfig/connection_config_writers/tun.rb new file mode 100644 index 000000000..b1b7e3407 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/tun.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Tun + # object to the underlying system. + class Tun < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + def update_file(conn) + file.tunnel = "tun" + file.tunnel_set_owner = conn.owner + file.tunnel_set_group = conn.group + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/usb.rb b/src/lib/y2network/sysconfig/connection_config_writers/usb.rb new file mode 100644 index 000000000..d16f4abb2 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/usb.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Usb + # object to the underlying system. + class Usb < Base + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/vlan.rb b/src/lib/y2network/sysconfig/connection_config_writers/vlan.rb new file mode 100644 index 000000000..fc1502625 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/vlan.rb @@ -0,0 +1,39 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Vlan + # object to the underlying system. + class Vlan < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + # @param conn [Y2Network::ConnectionConfig::Vlan] Configuration to write + def update_file(conn) + file.vlan_id = conn.vlan_id + file.etherdevice = conn.parent_device + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/connection_config_writers/wireless.rb b/src/lib/y2network/sysconfig/connection_config_writers/wireless.rb new file mode 100644 index 000000000..bd7a77938 --- /dev/null +++ b/src/lib/y2network/sysconfig/connection_config_writers/wireless.rb @@ -0,0 +1,89 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/sysconfig/connection_config_writers/base" + +module Y2Network + module Sysconfig + module ConnectionConfigWriters + # This class is responsible for writing the information from a ConnectionConfig::Wireless + # object to the underlying system. + class Wireless < Base + private + + # @see Y2Network::ConnectionConfigWriters::Base#update_file + def update_file(conn) + file.wireless_ap = conn.ap + file.wireless_ap_scanmode = conn.ap_scanmode + file.wireless_essid = conn.essid + file.wireless_mode = conn.mode + file.wireless_nwid = conn.nwid + file.wireless_channel = conn.channel + file.wireless_rate = conn.bitrate + write_auth_settings(conn) if conn.auth_mode + end + + # Writes authentication settings + # + # This method relies in `write_*_auth_settings` methods. + # + # + # @param conn [Y2Network::ConnectionConfig::Base] Configuration to write + # + # @see #write_eap_auth_settings + # @see #write_psk_auth_settings + # @see #write_shared_auth_settings + def write_auth_settings(conn) + file.wireless_auth_mode = conn.auth_mode || :open + meth = "write_#{conn.auth_mode}_auth_settings".to_sym + send(meth, conn) if respond_to?(meth, true) + end + + # Writes autentication settings for WPA-EAP networks + # + # @param conn [Y2Network::ConnectionConfig::Base] Configuration to write + def write_eap_auth_settings(conn) + file.wireless_eap_mode = conn.eap_mode + file.wireless_wpa_password = conn.wpa_password + file.wireless_wpa_identity = conn.wpa_identity + file.wireless_ca_cert = conn.ca_cert + file.wireless_wpa_anonid = conn.wpa_anonymous_identity if conn.eap_mode == "TTLS" + file.wireless_client_cert = conn.client_cert if conn.eap_mode == "TLS" + file.wireless_client_key = conn.client_key if conn.eap_mode == "TLS" + end + + # Writes autentication settings for WPA-PSK networks + # + # @param conn [Y2Network::ConnectionConfig::Base] Configuration to write + def write_psk_auth_settings(conn) + file.wireless_wpa_psk = conn.wpa_psk + end + + # Writes autentication settings for WEP networks + # + # @param conn [Y2Network::ConnectionConfig::Base] Configuration to write + def write_shared_auth_settings(conn) + file.wireless_keys = conn.keys + file.wireless_key_length = conn.key_length + file.wireless_default_key = conn.default_key + end + end + end + end +end diff --git a/src/lib/y2network/sysconfig/dns_reader.rb b/src/lib/y2network/sysconfig/dns_reader.rb index 15b85c17c..9cb161e11 100644 --- a/src/lib/y2network/sysconfig/dns_reader.rb +++ b/src/lib/y2network/sysconfig/dns_reader.rb @@ -18,12 +18,8 @@ # find current contact information at www.suse.com. require "yast" require "y2network/dns" - -Yast.import "Hostname" -Yast.import "Mode" -Yast.import "IP" -Yast.import "FileUtils" -Yast.import "NetHwDetection" +require "y2network/sysconfig/interface_file" +require "y2network/hostname_reader" module Y2Network module Sysconfig @@ -79,40 +75,7 @@ def resolv_conf_policy # # @return [String] def hostname - if (Yast::Mode.installation || Yast::Mode.autoinst) && Yast::FileUtils.Exists("/etc/install.inf") - fqdn = hostname_from_install_inf - end - - return hostname_from_system if fqdn.nil? || fqdn.empty? - host, _domain = *Yast::Hostname.SplitFQ(fqdn) - host - end - - # Reads the hostname from the /etc/install.inf file - # - # @return [String] Hostname - def hostname_from_install_inf - install_inf_hostname = Yast::SCR.Read(Yast::Path.new(".etc.install_inf.Hostname")) || "" - log.info("Got #{install_inf_hostname} from install.inf") - - return "" if install_inf_hostname.empty? - - # if the name is actually IP, try to resolve it (bnc#556613, bnc#435649) - if Yast::IP.Check(install_inf_hostname) - fqdn = Yast::NetHwDetection.ResolveIP(install_inf_hostname) - log.info("Got #{fqdn} after resolving IP from install.inf") - else - fqdn = install_inf_hostname - end - - fqdn - end - - # Reads the hostname from the underlying system - # - # @return [String] Hostname - def hostname_from_system - Yast::Execute.stdout.on_target!("/bin/hostname").strip + HostnameReader.new.hostname end # Return the list of search domains @@ -126,10 +89,17 @@ def searchlist # Returns whether the hostname should be taken from DHCP # - # @return [Boolean] + # @return [String,:any,:none] Interface to set the hostname based on DHCP settings; + # :any for any interface; :none for ignoring the hostname assigned via DHCP def dhcp_hostname value = Yast::SCR.Read(Yast::Path.new(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME")) - value == "yes" + return :any if value == "yes" + files = InterfaceFile.all + file = files.find do |f| + f.load + f.dhclient_set_hostname == "yes" + end + file ? file.interface : :none end end end diff --git a/src/lib/y2network/sysconfig/dns_writer.rb b/src/lib/y2network/sysconfig/dns_writer.rb index 8a28ef14e..cb3d3eb9d 100644 --- a/src/lib/y2network/sysconfig/dns_writer.rb +++ b/src/lib/y2network/sysconfig/dns_writer.rb @@ -18,6 +18,7 @@ # find current contact information at www.suse.com. require "yast" require "yast2/execute" +require "y2network/sysconfig/interface_file" module Y2Network module Sysconfig @@ -44,27 +45,35 @@ def write(dns, old_dns) # @return [String] Sendmail update script (included in "sendmail" package) SENDMAIL_UPDATE_PATH = "/usr/lib/sendmail.d/update".freeze - def update_sysconfig_dhcp(config, old_config) - if old_config && (old_config.dhcp_hostname == config.dhcp_hostname || config.dhcp_hostname.nil?) + # @param dns [Y2Network::DNS] DNS configuration + # @param old_dns [Y2Network::DNS] Old DNS configuration + def update_sysconfig_dhcp(dns, old_dns) + if old_dns.nil? || old_dns.dhcp_hostname == dns.dhcp_hostname log.info("No update for /etc/sysconfig/network/dhcp") return end - # dhcp_hostname can currently be nil only when not present in original file. So, do not - # add it in such a case. Yast::SCR.Write( Yast::Path.new(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"), - config.dhcp_hostname ? "yes" : "no" + dns.dhcp_hostname == :any ? "yes" : "no" ) Yast::SCR.Write(Yast::Path.new(".sysconfig.network.dhcp"), nil) + + # Clean-up values from ifcfg-* values + Y2Network::Sysconfig::InterfaceFile.all.each do |file| + value = file.interface == dns.dhcp_hostname ? "yes" : nil + file.load + next if file.dhclient_set_hostname == value + file.dhclient_set_hostname = value + file.save + end end # Sets the hostname # # @param dns [Y2Network::DNS] DNS configuration def update_hostname(dns) - dns.ensure_hostname! - Yast::Execute.on_target!("/bin/hostname", dns.hostname.split(".")[0]) + Yast::Execute.on_target!("/usr/bin/hostname", dns.hostname.split(".")[0]) Yast::SCR.Write(Yast::Path.new(".target.string"), HOSTNAME_PATH, "#{dns.hostname}\n") end diff --git a/src/lib/y2network/sysconfig/interface_file.rb b/src/lib/y2network/sysconfig/interface_file.rb index 7427c7e29..fc04c79dc 100644 --- a/src/lib/y2network/sysconfig/interface_file.rb +++ b/src/lib/y2network/sysconfig/interface_file.rb @@ -19,151 +19,517 @@ require "yast" require "pathname" -require "ipaddr" +require "y2network/ip_address" +require "y2network/interface_type" + +Yast.import "FileUtils" module Y2Network module Sysconfig # This class represents a sysconfig file containing an interface configuration # + # The configuration is defined by a set of variables that are included in the file. + # Check ifcfg(5) for further information. + # # @example Finding the file for a given interface # file = Y2Network::Sysconfig::InterfaceFile.find("wlan0") # file.wireless_essid #=> "dummy" + # + # ## Multivalued variables + # + # When dealing with multivalued variables, values are returned in a hash which + # indexes are the suffixes. For instance: + # + # IPADDR='192.168.122.1/24' + # IPADDR_EXTRA='192.168.123.1/24' + # IPADDR_ALT='10.0.0.1/8' + # + # @example Reading multivalued variables + # file = Y2Network::Sysconfig::InterfaceFile.find("wlan0") + # file.ipaddrs #=> { default: #, "_EXTRA" => #, "_ALT" => # } class InterfaceFile + # Auxiliar class to hold variables definition information + Variable = Struct.new(:name, :type, :collection?) + # @return [String] Interface name class << self SYSCONFIG_NETWORK_DIR = Pathname.new("/etc/sysconfig/network").freeze + # @return [Regex] expression to filter out invalid ifcfg-* files + IGNORE_IFCFG_REGEX = /(\.bak|\.orig|\.rpmnew|\.rpmorig|-range|~|\.old|\.scpmbackup)$/ + + # Returns all configuration files + # + # @return [Array] + def all + Yast::SCR.Dir(Yast::Path.new(".network.section")) + .reject { |f| IGNORE_IFCFG_REGEX =~ f || f == "lo" } + .map { |f| find(f) } + .compact + end + # Finds the ifcfg-* file for a given interface # - # @param name [String] Interface name + # @param interface [String] Interface name # @return [Sysconfig::InterfaceFile,nil] Sysconfig - def find(name) - return nil unless Yast::FileUtils.Exists(SYSCONFIG_NETWORK_DIR.join("ifcfg-#{name}").to_s) - new(name) + def find(interface) + return nil unless Yast::FileUtils.Exists(SYSCONFIG_NETWORK_DIR.join("ifcfg-#{interface}").to_s) + new(interface) + end + + # Defines a parameter + # + # This method registers the parameter and adds a pair of methods to get and set its + # value. + # + # @param param_name [Symbol] Parameter name + # @param type [Symbol] Parameter type (:string, :integer, :symbol, :ipaddr) + def define_variable(param_name, type = :string) + name = variable_name(param_name) + variables[name] = Variable.new(name, type, false) + + define_method param_name do + @values[name] + end + + define_method "#{param_name}=" do |value| + # The `value` should be an object which responds to #to_s so its value can be written to + # the ifcfg file. + @values[name] = value + end end - def define_parameter(name, type = :string) - define_method name do - value = fetch(name.to_s.upcase) - send("value_as_#{type}", value) + # Defines an array parameter + # + # This method registers the parameter and adds a pair of methods to get and set its + # value. In this case, the parameter is an array. + # + # @param param_name [Symbol] Parameter name + # @param type [Symbol] Array elements type (:string, :integer, :symbol, :ipaddr) + def define_collection_variable(param_name, type = :string) + name = variable_name(param_name) + variables[name] = Variable.new(name, type, true) + + define_method "#{param_name}s" do + @values[name] end + + define_method "#{param_name}s=" do |value| + @values[name] = value + end + end + + # Known configuration variables + # + # A variable is defined by using {define_variable} or {define_collection_variable} methods. + # + # @return [Array] + def variables + @variables ||= {} + end + + private + + # Parameter name to internal variable name + # + # @param param_name [Symbol] + # @return [String] Convert a parameter name to the expected internal name + def variable_name(param_name) + param_name.to_s.upcase end end - attr_reader :name + # @return [String] Interface's name + attr_reader :interface + + # !@attribute [r] ipaddr + # @return [Y2Network::IPAddress] IP address + define_collection_variable(:ipaddr, :ipaddr) + + # !@attribute [r] name + # @return [String] Interface's description (e.g., "Ethernet Card 0") + define_variable(:name, :string) + + # !@attribute [r] interfacetype + # @return [String] Forced Interface's type (e.g., "dummy") + define_variable(:interfacetype, :string) # !@attribute [r] bootproto - # return [Symbol] Set up protocol (:static, :dhcp, :dhcp4, :dhcp6, :autoip, :dhcp+autoip, - # :auto6, :6to4, :none) - define_parameter(:bootproto, :symbol) + # return [String] Set up protocol (static, dhcp, dhcp4, dhcp6, autoip, dhcp+autoip, + # auto6, 6to4, none) + define_variable(:bootproto) # !@attribute [r] bootproto - # return [Symbol] When the interface should be set up (:manual, :auto, :hotplug, :nfsroot, :off) - define_parameter(:startmode, :symbol) + # return [String] When the interface should be set up (manual, auto, hotplug, nfsroot, off, + # and ifplugd which is not handled by wicked, but by ifplugd daemon and is not mentioned + # in man page) + define_variable(:startmode) + + # !@attribute [r] labels + # @return [Hash] Label to assign to the address + define_collection_variable(:label, :string) + + # !@attribute [r] remote_ipaddrs + # @return [Hash] Remote IP address of a point to point connection + define_collection_variable(:remote_ipaddr, :ipaddr) + + # !@attribute [r] ifplugd_priority + # return [Integer] when startmode is set to ifplugd this defines its priority. Not handled + # by wicked, but own daemon. Not documented in man page. + define_variable(:ifplugd_priority, :symbol) + + # !@attribute [r] broadcasts + # @return [Hash] Broadcasts addresses + define_collection_variable(:broadcast, :ipaddr) + + # !@attribute [r] prefixlens + # @return [Hash] Prefixes lengths + define_collection_variable(:prefixlen, :integer) + + # !@attribute [r] netmasks + # @return [Hash] Netmasks + define_collection_variable(:netmask) + + # !@attribute [r] lladdr + # @return [String] Link layer address + define_variable(:lladdr) + + # !@attribute [r] ethtool_options + # @return [String] setting variables on device activation. See man ethtool + define_variable(:ethtool_options) + + # !@attribute [r] zone + # @return [String] assign zone to interface. Extensions then can handle it + define_variable(:zone) # !@attribute [r] wireless_key_length # @return [Integer] Length in bits for all keys used - define_parameter(:wireless_key_length, :integer) + define_variable(:wireless_key_length, :integer) + + # !@attribute [r] wireless_keys + # @return [Array] List of wireless keys + define_collection_variable(:wireless_key, :string) # !@attribute [r] wireless_default_key # @return [Integer] Index of the default key # @see #wireless_keys - define_parameter(:wireless_default_key, :integer) + define_variable(:wireless_default_key, :integer) # !@attribute [r] wireless_essid # @return [String] Wireless SSID/ESSID - define_parameter(:wireless_essid) + define_variable(:wireless_essid) # !@attribute [r] wireless_auth_mode # @return [Symbol] Wireless authorization mode (:open, :shared, :psk, :eap) - define_parameter(:wireless_auth_mode, :symbol) + define_variable(:wireless_auth_mode, :symbol) # @!attribute [r] wireless_mode # @return [Symbol] Operating mode for the device (:managed, :ad_hoc or :master) - define_parameter(:wireless_mode, :symbol) + define_variable(:wireless_mode, :symbol) # @!attribute [r] wireless_wpa_password # @return [String] Password as configured on the RADIUS server (for WPA-EAP) - define_parameter(:wireless_wpa_password) + define_variable(:wireless_wpa_password) + + # @!attribute [r] wireless_wpa_anonid + # @return [String] anonymous identity used for initial tunnel (TTLS) + define_variable(:wireless_wpa_anonid) # @!attribute [r] wireless_wpa_driver # @return [String] Driver to be used by the wpa_supplicant program - define_parameter(:wireless_wpa_driver) + define_variable(:wireless_wpa_driver) # @!attribute [r] wireless_wpa_psk # @return [String] WPA preshared key (for WPA-PSK) - define_parameter(:wireless_wpa_psk) + define_variable(:wireless_wpa_psk) + + # @!attribute [r] wireless_wpa_identity + # @return [String] WPA identify + define_variable(:wireless_wpa_identity) + + # @!attribute [r] wireless_ca_cert + # @return [String] CA certificate used to sign server certificate + define_variable(:wireless_ca_cert) + + # @!attribute [r] wireless_client_cert + # @return [String] CA certificate used to sign server certificate + define_variable(:wireless_client_cert) + + # @!attribute [r] wireless_client_key + # @return [String] client private key used for encryption in TLS + define_variable(:wireless_client_key) # @!attribute [r] wireless_eap_mode # @return [String] WPA-EAP outer authentication method - define_parameter(:wireless_eap_mode) + define_variable(:wireless_eap_mode) # @!attribute [r] wireless_eap_auth # @return [String] WPA-EAP inner authentication with TLS tunnel method - define_parameter(:wireless_eap_auth) + define_variable(:wireless_eap_auth) # @!attribute [r] wireless_ap_scanmode # @return [String] SSID scan mode ("0", "1" and "2") - define_parameter(:wireless_ap_scanmode) + define_variable(:wireless_ap_scanmode) # @!attribute [r] wireless_ap # @return [String] AP MAC address - define_parameter(:wireless_ap) + define_variable(:wireless_ap) # @!attribute [r] wireless_channel - # @return [Integer] Wireless channel - define_parameter(:wireless_channel) + # @return [Integer, nil] Wireless channel or nil for auto selection + define_variable(:wireless_channel, :integer) # @!attribute [r] wireless_nwid # @return [String] Network ID - define_parameter(:wireless_nwid) + define_variable(:wireless_nwid) + + # @!attribute [r] wireless_rate + # @return [String] Wireless bit rate specification ( Mb/s) + define_variable(:wireless_rate, :float) + + ## INFINIBAND + + # @!attribute [r] ipoib_mode + # @return [String] IPOIB mode ("connected" or "datagram") + define_variable(:ipoib_mode) + + ## VLAN + + # !@attribute [r] etherdevice + # @return [String] Real device for the virtual LAN + define_variable(:etherdevice) + + # !@attribute [r] vlan_id + # @return [String] VLAN ID + define_variable(:vlan_id, :integer) + + ## BONDING + + # @!attribute [r] bonding_master + # @return [String] whether the interface is a bond device or not + define_variable(:bonding_master) + + # @!attribute [r] bonding_slaves + # @return [Hash] Bonding slaves + define_collection_variable(:bonding_slave) + + # @!attribute [r] bonding_module_opts + # @return [String] options for the bonding module ('mode=active-backup + # miimon=100') + define_variable(:bonding_module_opts) + + ## BRIDGE + + # @!attribute [r] bridge + # @return [String] whether the interface is a bridge or not + define_variable(:bridge) + + # @!attribute [r] bridge_ports + # @return [String] interfaces members of the bridge + define_variable(:bridge_ports) + + # @!attribute [r] bridge_stp + # @return [String] Spanning Tree Protocol ("off" or "on") + define_variable(:bridge_stp) + + # @!attribute [r] bridge_forwarddelay + # @return [Integer] + define_variable(:bridge_forwarddelay, :integer) + + ## TUN / TAP + + # @!attribute [r] tunnel + # @return [String] tunnel protocol ("sit", "gre", "ipip", "tun", "tap") + define_variable(:tunnel) + + # @!attribute [r] tunnel_set_owner + # @return [String] tunnel owner + define_variable(:tunnel_set_owner) + + # @!attribute [r] tunnel_set_group + # @return [String] tunnel group + define_variable(:tunnel_set_group) + + # @!attribute [r] dhclient_set_hostname + # @return [String] use hostname from dhcp + define_variable(:dhclient_set_hostname) # Constructor # - # @param name [String] Interface name - def initialize(name) - @name = name + # @param interface [String] Interface interface + def initialize(interface) + @interface = interface + @values = collection_variables.each_with_object({}) do |variable, hash| + hash[variable.name] = {} + end + end + + SYSCONFIG_NETWORK_PATH = Pathname.new("/etc").join("sysconfig", "network").freeze + + # Returns the file path + # + # @return [Pathname] + def path + SYSCONFIG_NETWORK_PATH.join("ifcfg-#{interface}") + end + + # Loads values from the configuration file + # + # @return [Hash] All values from the file + def load + @values = self.class.variables.values.each_with_object({}) do |variable, hash| + meth = variable.collection? ? :fetch_collection : :fetch_scalar + hash[variable.name] = send(meth, variable.name, variable.type) + end + end + + # Writes the changes to the file + # + # @note Writes only changed values, keeping the rest as they are. + def save + self.class.variables.keys.each do |name| + value = @values[name] + meth = value.is_a?(Hash) ? :write_collection : :write_scalar + send(meth, name, value) + end + Yast::SCR.Write(Yast::Path.new(".network"), nil) + end + + # Determines the interface's type + # + # @return [Y2Network::InterfaceType] Interface's type depending on the configuration + # If particular type cannot be recognized, then + # ETHERNET is returned (same default as in wicked) + def type + type_by_key_value || + type_by_key_existence || + type_from_interfacetype || + type_by_name || + InterfaceType::ETHERNET + end + + # Empties all known values + # + # This method clears all values from the file. The idea is to use this method + # to do some clean-up before writing the final values. + def clean + @values = self.class.variables.values.each_with_object({}) do |variable, hash| + hash[variable.name] = variable.collection? ? {} : nil + end + @defined_variables = nil + end + + # Removes the file + def remove + return unless Yast::FileUtils.Exists(path.to_s) + Yast::SCR.Execute(Yast::Path.new(".target.remove"), path.to_s) + end + + private + + # Detects interface type according to type specific option and its value + # + # @return [Y2Network::InterfaceType, nil] particular type if recognized, nil otherwise + def type_by_key_value + return InterfaceType::BONDING if bonding_master == "yes" + return InterfaceType::BRIDGE if bridge == "yes" + return InterfaceType.from_short_name(tunnel) if tunnel + + # in relation to original implementation ommited ENCAP option which leads to isdn + # and PPPMODE which leads to ppp. Neither of this type has been handled as + # "netcard" - see Yast::NetworkInterfaces for details + + nil end - # Returns the IP address if defined + KEY_TO_TYPE = { + "ETHERDEVICE" => InterfaceType::VLAN, + "WIRELESS_MODE" => InterfaceType::WIRELESS, + "MODEM_DEVICE" => InterfaceType::PPP + }.freeze + + # Detects interface type according to type specific option + # + # @return [Y2Network::InterfaceType, nil] particular type if recognized, nil otherwise + def type_by_key_existence + key = KEY_TO_TYPE.keys.find { |k| defined_variables.include?(k) } + return KEY_TO_TYPE[key] if key + + nil + end + + # Detects interface type according to sysconfig's INTERFACETYPE option + # + # This option is kind of special that it deserve own method mostly for documenting + # that it should almost never be used. Only meaningful cases for its usage is + # dummy device definition and loopback device (when an additional to standard ifcfg-lo + # is defined) # - # @return [IPAddr,nil] IP address or nil if it is not defined - def ip_address - str = fetch("IPADDR") - str.nil? || str.empty? ? nil : IPAddr.new(str) + # @return [Y2Network::InterfaceType, nil] type according to INTERFACETYPE option + # value if recognized, nil otherwise + def type_from_interfacetype + return InterfaceType.from_short_name(interfacetype) if interfacetype + nil end - # @return [Integer] Number of supported keys - SUPPORTED_KEYS = 4 + # Distinguishes interface type by its name + # + # The only case should be loopback device with special name (in sysconfig) "lo" + # + # @return [Y2Network::InterfaceType, nil] InterfaceType::LO or nil if not loopback + def type_by_name + InterfaceType::LO if interface == "lo" + nil + end - # List of wireless keys + # Returns a list of those keys that have a value # - # @return [Array] Wireless keys - def wireless_keys - keys = [fetch("WIRELESS_KEY")] - keys += Array.new(SUPPORTED_KEYS) { |i| fetch("WIRELESS_KEY_#{i}") } - keys.compact + # @return [Array] name of keys that are included in the file + def defined_variables + @defined_variables ||= Yast::SCR.Dir(Yast::Path.new(".network.value.\"#{interface}\"")) || [] end - # Fetches a key + # Fetches the value for a given key # - # @param key [String] Interface key + # @param key [String] Value key + # @param type [Symbol] Type to convert the value to # @return [Object] Value for the given key - def fetch(key) - path = Yast::Path.new(".network.value.\"#{name}\".#{key}") - Yast::SCR.Read(path) + def fetch_scalar(key, type) + path = Yast::Path.new(".network.value.\"#{interface}\".#{key}") + value = Yast::SCR.Read(path) + send("value_as_#{type}", value) end - # Determines the interface's type + # Returns a hash containing all the values for a given key # - # @todo Borrow logic from https://github.com/yast/yast-yast2/blob/6f7a789d00cd03adf62e00da34720f326f0e0633/library/network/src/modules/NetworkInterfaces.rb#L291 + # When working with collections, all values are represented in a hash, indexed by the suffix. # - # @return [Symbol] Interface's type depending on the file values - def type - :eth + # For instance, this set of IPADDR_* keys: + # + # IPADDR='192.168.122.1' + # IPADDR0='192.168.122.2' + # + # will be represented like: + # + # { :default => "192.168.122.1", "0" => "192.168.122.2" } + # + # @param key [Symbol] Key base name (without the suffix) + # @param type [Symbol] Type to convert the values to + # @return [Hash] + def fetch_collection(key, type) + collection_keys(key).each_with_object({}) do |k, h| + index = k.sub(key, "") + h[index] = fetch_scalar(k, type) + end end - private + def collection_keys(key) + collection_keys = defined_variables.select do |k| + k == key || k.start_with?(key) + end + other_keys = self.class.variables.keys - [key] + collection_keys - other_keys + end # Converts the value into a string (or nil if empty) # @@ -181,12 +547,70 @@ def value_as_integer(value) value.nil? || value.empty? ? nil : value.to_i end + # Converts the value into an float (or nil if empty) + # + # @param [String] value + # @return [Float,nil] + def value_as_float(value) + value.nil? || value.empty? ? nil : value.to_f + end + # Converts the value into a symbol (or nil if empty) # # @param [String] value # @return [Symbol,nil] def value_as_symbol(value) - value.nil? || value.empty? ? nil : value.to_sym + value.nil? || value.empty? ? nil : value.downcase.to_sym + end + + # Converts the value into a IPAddress (or nil if empty) + # + # @param [String] value + # @return [Y2Network::IPAddress,nil] + def value_as_ipaddr(value) + value.nil? || value.empty? ? nil : Y2Network::IPAddress.from_string(value) + end + + # Writes an array as a value for a given key + # + # @param key [String] Key + # @param values [Array<#to_s>] Values to write + # @see #clean_collection + def write_collection(key, values) + clean_collection(key) + values.each do |suffix, value| + write_key = suffix == :default ? key : "#{key}#{suffix}" + write_scalar(write_key, value) + end + end + + # Cleans all values from a collection + # + # @todo There is no way to remove elements from the configuration file so we are setting them + # to blank. However, using CFA might be an alternative. + # + # @param key [String] Key + def clean_collection(key) + collection_keys(key).each { |k| write_scalar(k, nil) } + end + + # Writes the value for a given key + # + # If the value is set to nil, the key will be removed. + # + # @param key [Symbol] Key + # @param value [#to_s,nil] Value to write + def write_scalar(key, value) + raw_value = value ? value.to_s : nil + path = Yast::Path.new(".network.value.\"#{interface}\".#{key}") + Yast::SCR.Write(path, raw_value) + end + + # Returns the variables which are collections + # + # @return [Array] List of collection variables + def collection_variables + self.class.variables.values.select(&:collection?) end end end diff --git a/src/lib/y2network/sysconfig/interfaces_reader.rb b/src/lib/y2network/sysconfig/interfaces_reader.rb index 4c99773d5..bece0eb80 100644 --- a/src/lib/y2network/sysconfig/interfaces_reader.rb +++ b/src/lib/y2network/sysconfig/interfaces_reader.rb @@ -19,13 +19,15 @@ require "yast" require "y2network/interface" +require "y2network/interface_type" require "y2network/virtual_interface" require "y2network/physical_interface" -require "y2network/fake_interface" require "y2network/sysconfig/connection_config_reader" - -Yast.import "LanItems" -Yast.import "NetworkInterfaces" +require "y2network/sysconfig/interface_file" +require "y2network/interfaces_collection" +require "y2network/connection_configs_collection" +require "y2network/sysconfig/type_detector" +require "y2network/udev_rule" module Y2Network module Sysconfig @@ -46,7 +48,8 @@ def config return @config if @config find_physical_interfaces find_connections - @config = { interfaces: @interfaces, connections: @connections } + find_drivers + @config = { interfaces: @interfaces, connections: @connections, drivers: @drivers } end # Convenience method to get connections configuration @@ -64,14 +67,19 @@ def interfaces config[:interfaces] end + # Convenience method to get the drivers list + # + # @return [Array] + def drivers + config[:drivers] + end + private # Finds the physical interfaces - # - # Physical interfaces are read from the old LanItems module def find_physical_interfaces return if @interfaces - physical_interfaces = Yast::LanItems.Hardware.map do |h| + physical_interfaces = Hwinfo.netcards.map do |h| build_physical_interface(h) end @interfaces = Y2Network::InterfacesCollection.new(physical_interfaces) @@ -80,60 +88,85 @@ def find_physical_interfaces # Finds the connections configurations def find_connections @connections ||= - configured_devices.each_with_object([]) do |name, conns| - interface = @interfaces.by_name(name) + InterfaceFile.all.each_with_object(ConnectionConfigsCollection.new([])) do |file, conns| + interface = @interfaces.by_name(file.interface) connection = ConnectionConfigReader.new.read( - name, + file.interface, interface ? interface.type : nil ) next unless connection - add_fake_interface(name, connection) if interface.nil? + add_interface(connection) if interface.nil? conns << connection end end - # Instantiates an interface given a hash containing hardware details - # - # If there is not information about the type, it will rely on NetworkInterfaces#GetTypeFromSysfs. - # This responsability could be moved to the PhysicalInterface class. - # - # @todo Improve detection logic according to NetworkInterfaces#GetTypeFromIfcfgOrName. - # - # @param data [Hash] hardware information - # @option data [String] "dev_name" Device name ("eth0") - # @option data [String] "name" Device description - # @option data [String] "type" Device type ("eth", "wlan", etc.) - def build_physical_interface(data) - Y2Network::PhysicalInterface.new(data["dev_name"]).tap do |iface| - iface.description = data["name"] - type = data["type"] || Yast::NetworkInterfaces.GetTypeFromSysfs(iface.name) - iface.type = type.nil? ? :eth : type.to_sym + # Finds the available drivers + def find_drivers + physical_interfaces = @interfaces.physical + drivers_names = physical_interfaces.flat_map(&:drivers).map(&:name) + drivers_names += @interfaces.physical.map(&:custom_driver).compact + drivers_names.uniq! + + @drivers = drivers_names.map do |name| + Y2Network::Driver.from_system(name) end end - # @return [Regex] expression to filter out invalid ifcfg-* files - IGNORE_IFCFG_REGEX = /(\.bak|\.orig|\.rpmnew|\.rpmorig|-range|~|\.old|\.scpmbackup)$/ - - # List of devices which has a configuration file (ifcfg-*) + # Instantiates an interface given a hash containing hardware details # - # @return [Array] List of configured devices - def configured_devices - files = Yast::SCR.Dir(Yast::Path.new(".network.section")) - files.reject { |f| IGNORE_IFCFG_REGEX =~ f || f == "lo" } + # @param hwinfo [Hash] hardware information + def build_physical_interface(hwinfo) + Y2Network::PhysicalInterface.new(hwinfo.dev_name, hardware: hwinfo).tap do |iface| + iface.renaming_mechanism = renaming_mechanism_for(iface) + iface.custom_driver = custom_driver_for(iface) + iface.type = InterfaceType.from_short_name(hwinfo.type) || TypeDetector.type_of(iface.name) + end end - # Adds a fake interface for a given connection + # Adds a fake or virtual interface for a given connection # # It may happen that a configured interface is not plugged # while reading the configuration. In such situations, a fake one # should be added. # - # @param name [String] Interface name # @param conn [ConnectionConfig] Connection configuration related to the # network interface - def add_fake_interface(name, conn) - new_interface = Y2Network::FakeInterface.from_connection(name, conn) - @interfaces << new_interface + def add_interface(conn) + interface = + if conn.virtual? + VirtualInterface.from_connection(conn) + else + PhysicalInterface.new(conn.name, hardware: Hwinfo.for(conn.name)) + end + @interfaces << interface + end + + # Detects the renaming mechanism used by the interface + # + # @param iface [PhysicalInterface] Interface + # @return [Symbol] :mac (MAC address), :bus_id (BUS ID) or :none (no renaming) + def renaming_mechanism_for(iface) + rule = UdevRule.find_for(iface.name) + return :none unless rule + if rule.parts.any? { |p| p.key == "ATTR{address}" } + :mac + elsif rule.parts.any? { |p| p.key == "KERNELS" } + :bus_id + else + :none + end + end + + # Detects the custom driver used by the interface + # + # A driver is considered "custom" is it was set by the user through a udev rule. + # + # @param iface [PhysicalInterface] Interface to fetch the custom driver + # @return [String,nil] Custom driver (or nil if not set) + def custom_driver_for(iface) + return nil unless iface.modalias + rule = UdevRule.drivers_rules.find { |r| r.original_modalias == iface.modalias } + rule ? rule.driver : nil end end end diff --git a/src/lib/y2network/sysconfig/interfaces_writer.rb b/src/lib/y2network/sysconfig/interfaces_writer.rb new file mode 100644 index 000000000..0458290e6 --- /dev/null +++ b/src/lib/y2network/sysconfig/interfaces_writer.rb @@ -0,0 +1,115 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/udev_rule" +require "yast2/execute" +require "y2network/sysconfig/interface_file" +require "y2network/sysconfig/routes_file" + +Yast.import "Mode" + +module Y2Network + module Sysconfig + # This class writes interfaces specific configuration + # + # Although it might be confusing, this class is only responsible for writing + # hardware specific configuration through udev rules. + # + # @see Y2Network::InterfacesCollection + class InterfacesWriter + # Writes interfaces hardware configuration and refreshes udev + # + # @param interfaces [Y2Network::InterfacesCollection] Interfaces collection + def write(interfaces) + set_old_interfaces_down(interfaces) + update_udevd(interfaces) + end + + private + + # Creates an udev rule to rename the given interface + # + # @param iface [Interface] Interface to generate the udev rule for + # @return [UdevRule,nil] udev rule or nil if it is not needed + def renaming_udev_rule_for(iface) + case iface.renaming_mechanism + when :mac + Y2Network::UdevRule.new_mac_based_rename(iface.name, iface.hardware.mac) + when :bus_id + Y2Network::UdevRule.new_bus_id_based_rename(iface.name, iface.hardware.busid, iface.hardware.dev_port) + end + end + + # Creates an udev rule to set the driver for the given interface + # + # @param iface [Interface] Interface to generate the udev rule for + # @return [UdevRule,nil] udev rule or nil if it is not needed + def driver_udev_rule_for(iface) + return nil unless iface.respond_to?(:custom_driver) && iface.custom_driver + Y2Network::UdevRule.new_driver_assignment(iface.modalias, iface.custom_driver) + end + + # Renames interfaces and refreshes the udev service + # + # @param interfaces [InterfaceCollection] Interfaces + def update_udevd(interfaces) + update_renaming_udev_rules(interfaces) + update_drivers_udev_rules(interfaces) + reload_udev_rules + end + + def update_renaming_udev_rules(interfaces) + udev_rules = interfaces.map { |i| renaming_udev_rule_for(i) }.compact + known_names = interfaces.known_names + custom_rules = Y2Network::UdevRule.naming_rules.reject { |u| known_names.include?(u.device) } + Y2Network::UdevRule.write_net_rules(custom_rules + udev_rules) + end + + def update_drivers_udev_rules(interfaces) + udev_rules = interfaces.map { |i| driver_udev_rule_for(i) }.compact + Y2Network::UdevRule.write_drivers_rules(udev_rules) + end + + def reload_udev_rules + Yast::Execute.on_target("/usr/bin/udevadm", "control", "--reload") + Yast::Execute.on_target("/usr/bin/udevadm", "trigger", "--subsystem-match=net", "--action=add") + # wait so that ifcfgs written in NetworkInterfaces are newer + # (1-second-wise) than netcontrol status files, + # and rcnetwork reload actually works (bnc#749365) + Yast::Execute.on_target("/usr/bin/udevadm", "settle") + sleep(1) + end + + # Cleans and shutdowns renamed interfaces + # + # @param interfaces [InterfacesCollection] Interfaces + def set_old_interfaces_down(interfaces) + interfaces.to_a.select(&:old_name).each { |i| set_interface_down(i.old_name) } + end + + # Sets the interface down + # + # @param iface_name [String] Interface's name + def set_interface_down(iface_name) + Yast::Execute.on_target("/sbin/ifdown", iface_name) unless Yast::Mode.autoinst + end + end + end +end diff --git a/src/lib/y2network/sysconfig/routes_file.rb b/src/lib/y2network/sysconfig/routes_file.rb index 9009018c1..cdaf7325c 100644 --- a/src/lib/y2network/sysconfig/routes_file.rb +++ b/src/lib/y2network/sysconfig/routes_file.rb @@ -39,6 +39,7 @@ module Sysconfig # file.routes.size #=> 1 class RoutesFile DEFAULT_ROUTES_FILE = "/etc/sysconfig/network/routes".freeze + SYSCONFIG_NETWORK_DIR = "/etc/sysconfig/network".freeze # @return [Array] Routes attr_accessor :routes @@ -46,6 +47,14 @@ class RoutesFile # @return [String] File path attr_reader :file_path + class << self + # @param interface [String] Interface's name + def find(interface) + file_path = File.join(SYSCONFIG_NETWORK_DIR, "ifroute-#{interface}") + new(file_path) + end + end + # @param file_path [String] File path def initialize(file_path = DEFAULT_ROUTES_FILE) @register_agent = file_path != DEFAULT_ROUTES_FILE diff --git a/src/lib/y2network/sysconfig/type_detector.rb b/src/lib/y2network/sysconfig/type_detector.rb new file mode 100644 index 000000000..f142841ee --- /dev/null +++ b/src/lib/y2network/sysconfig/type_detector.rb @@ -0,0 +1,42 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_type" +require "y2network/type_detector" +require "y2network/sysconfig/interface_file" + +module Y2Network + module Sysconfig + # Detects type of given interface. New implementation of what was in + # @see Yast::NetworkInterfaces.GetType + class TypeDetector < Y2Network::TypeDetector + class << self + private + + # Checks wheter iface type can be recognized by interface configuration + def type_by_config(iface) + iface_file = Y2Network::Sysconfig::InterfaceFile.find(iface) + iface_file.load + iface_file.type + end + end + end + end +end diff --git a/src/lib/y2network/type_detector.rb b/src/lib/y2network/type_detector.rb new file mode 100644 index 000000000..4d408ccec --- /dev/null +++ b/src/lib/y2network/type_detector.rb @@ -0,0 +1,141 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "y2network/interface_type" + +module Y2Network + # Detects type of given interface. New implementation of what was in + # @see Yast::NetworkInterfaces.GetType + class TypeDetector + class << self + # Finds type of given interface + # + # @param iface [String] interface name + # + # @return [Y2Network::InterfaceType, nil] type of given interface or nil if cannot be recognized + def type_of(iface) + type_by_sys(iface) || type_by_config(iface) || nil + end + + private + + include Yast::Logger + + SYS_TYPE_NUMBERS = { + "1" => InterfaceType::ETHERNET, + "24" => InterfaceType::ETHERNET, + "32" => InterfaceType::INFINIBAND, + "512" => InterfaceType::PPP, + "768" => InterfaceType::IPIP, + "769" => InterfaceType::IPV6TNL, + "772" => InterfaceType::LO, + "776" => InterfaceType::SIT, + "778" => InterfaceType::GRE, + "783" => InterfaceType::IRDA, + "801" => InterfaceType::WIRELESS, # wlan_aux if needed later + "65534" => InterfaceType::TUN + }.freeze + + # Checks wheter iface type can be recognized by /sys filesystem + # + # @param iface [String] interface name + # @return [Y2Network::InterfaceType, nil] nil when type can be recognized in /sys, + # interface type otherwise + def type_by_sys(iface) + raise ArgumentError, "An interface has to be given." if iface.nil? + + type_dir_path = "/sys/class/net/#{iface}/type" + return nil if !::File.exist?(type_dir_path) + + sys_type = Yast::SCR.Read(Yast::Path.new(".target.string"), type_dir_path).to_s.strip + type = SYS_TYPE_NUMBERS[sys_type] + + # finer detection for some types + type = case type + when InterfaceType::ETHERNET + eth_type_by_sys(iface) + when InterfaceType::INFINIBAND + ib_type_by_sys(iface) + end + + log.info("TypeDetector: #{iface} is of #{type} type according to /sys") + + type + end + + # Detects a subtype of Ethernet device type according /sys or /proc content + # + # @example + # eth_type_by_sys("eth0") -> Ethernet + # eth_type_by_sys("bond0") -> Bonding + # + # @param iface [String] interface name + # @return [Y2Network::InterfaceType] interface type + def eth_type_by_sys(iface) + sys_dir_path = "/sys/class/net/#{iface}" + + if ::File.exist?("#{sys_dir_path}/wireless") + InterfaceType::WIRELESS + elsif ::File.exist?("#{sys_dir_path}/phy80211") + InterfaceType::WIRELESS + elsif ::File.exist?("#{sys_dir_path}/bridge") + InterfaceType::BRIDGE + elsif ::File.exist?("#{sys_dir_path}/bonding") + InterfaceType::BONDING + elsif ::File.exist?("#{sys_dir_path}/tun_flags") + InterfaceType::TUN + elsif ::File.exist?("/proc/net/vlan/#{iface}") + InterfaceType::VLAN + elsif ::File.exist?("/sys/devices/virtual/net/#{iface}") && iface =~ /dummy/ + InterfaceType::DUMMY + else + InterfaceType::ETHERNET + end + end + + # Detects a subtype of InfiniBand device type according /sys + # + # @example + # ib_type_by_sys("ib0") -> Infiniband + # ib_type_by_sys("bond0") -> Bonding + # ib_type_by_sys("ib0.8001") -> Infiniband child + # + # @param iface [String] interface name + # @return [Y2Network::InterfaceType] interface type + def ib_type_by_sys(iface) + sys_dir_path = "/sys/class/net/#{iface}" + + if ::File.exist?("#{sys_dir_path}/bonding") + InterfaceType::BONDING + elsif ::File.exist?("#{sys_dir_path}/create_child") + InterfaceType::INFINIBAND + else + InterfaceType::INFINIBAND_CHILD + end + end + + # Checks wheter iface type can be recognized by interface configuration + def type_by_config(_iface) + # this part is backend specific + raise NotImplementedError + end + end + end +end diff --git a/src/lib/y2network/udev_rule.rb b/src/lib/y2network/udev_rule.rb new file mode 100644 index 000000000..7552e561d --- /dev/null +++ b/src/lib/y2network/udev_rule.rb @@ -0,0 +1,285 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/udev_rule_part" + +module Y2Network + # Simple udev rule class + # + # This class represents a network udev rule. The current implementation is quite simplistic, + # featuring a an API which is tailored to our needs. + # + # Basically, udev rules are kept in two different files under `/etc/udev/rules.d`: + # + # * 70-persistent-net.rules ('net' group): rules to assign names to interfaces. + # * 79-yast2-drivers.rules ('drivers' group): rules to assign drivers to interfaces. + # + # This class offers a set of constructors to build different kinds of rules. + # See {.new_mac_based_rename}, {.new_bus_id_based_rename} and {.new_driver_assignment}. + # + # When it comes to write rules to the filesystem, we decided to offer different methods to + # write to each file. See {.write_net_rules} and {.write_drivers_rules}. + # + # @example Create a rule containing some key/value pairs (rule part) + # rule = Y2Network::UdevRule.new( + # Y2Network::UdevRulePart.new("ATTR{address}", "==", "?*31:78:f2"), + # Y2Network::UdevRulePart.new("NAME", "=", "mlx4_ib3") + # ) + # rule.to_s #=> "ACTION==\"add\", SUBSYSTEM==\"net\", ATTR{address}==\"?*31:78:f2\", NAME=\"eth0\"" + # + # @example Create a rule from a string + # rule = UdevRule.find_for("eth0") + # rule.to_s #=> "ACTION==\"add\", SUBSYSTEM==\"net\", ATTR{address}==\"?*31:78:f2\", NAME=\"eth0\"" + # + # @example Writing renaming rules + # rule = UdevRule.new_mac_based_rename("00:12:34:56:78:ab", "eth0") + # UdevRule.write_net_rules([rule]) + # + # @example Writing driver assignment rules + # rule = UdevRule.new_driver_assignment("virtio:d00000001v00001AF4", "virtio_net") + # UdevRule.write_drivers_rules([rule]) + # + class UdevRule + class << self + # Returns all persistent network rules + # + # @return [Array] Persistent network rules + def all + naming_rules + drivers_rules + end + + # Returns naming rules + # + # @return [Array] Naming network rules + def naming_rules + find_rules(:net) + end + + # Returns driver rules + # + # @return [Array] Drivers rules + def drivers_rules + find_rules(:drivers) + end + + # Returns the udev rule for a given device + # + # Only the naming rules are considered. + # + # @param device [String] Network device name + # @return [UdevRule] udev rule + def find_for(device) + naming_rules.find { |r| r.device == device } + end + + # Helper method to create a rename rule based on a MAC address + # + # @param name [String] Interface's name + # @param mac [String] MAC address + def new_mac_based_rename(name, mac) + new_network_rule( + [ + # Guard to not try to rename everything with the same MAC address (e.g. vlan devices + # inherit the MAC address from the underlying device). + # FIXME: it won't work when using predictable network names (openSUSE) + # UdevRulePart.new("KERNEL", "==", "eth*"), + # The port number of a NIC where the ports share the same hardware device. + UdevRulePart.new("ATTR{dev_id}", "==", "0x0"), + UdevRulePart.new("ATTR{address}", "==", mac), + UdevRulePart.new("NAME", "=", name) + ] + ) + end + + # Helper method to create a rename rule based on the BUS ID + # + # @param name [String] Interface's name + # @param bus_id [String] BUS ID (e.g., "0000:08:00.0") + # @param dev_port [String] Device port + def new_bus_id_based_rename(name, bus_id, dev_port = nil) + parts = [UdevRulePart.new("KERNELS", "==", bus_id)] + parts << UdevRulePart.new("ATTR{dev_port}", "==", dev_port) if dev_port + parts << UdevRulePart.new("NAME", "=", name) + new_network_rule(parts) + end + + # Returns a network rule + # + # The network rule includes some parts by default. + # + # @param parts [Array] List of udev rules + def write_drivers_rules(udev_rules) + rules_hash = udev_rules.each_with_object({}) do |rule, hash| + driver = rule.part_value_for("ENV{MODALIAS}", "=") + next unless driver + hash[driver] = rule.parts.map(&:to_s) + end + Yast::SCR.Write(Yast::Path.new(".udev_persistent.drivers"), rules_hash) + Yast::SCR.Write(Yast::Path.new(".udev_persistent.nil"), []) # Writes changes to the rules file + end + + # Clears rules cache map + def reset_cache + @all = nil + end + + private + + def find_rules(group) + @all ||= {} + return @all[group] if @all[group] + rules_map = Yast::SCR.Read(Yast::Path.new(".udev_persistent.#{group}")) || {} + @all[group] = rules_map.values.map do |parts| + udev_parts = parts.map { |p| UdevRulePart.from_string(p) } + new(udev_parts) + end + end + end + + # @return [Array] Parts of the udev rule + attr_reader :parts + + # Constructor + # + # @param parts [Array] udev rule parts + def initialize(parts = []) + @parts = parts + end + + # Adds a part to the rule + # + # @param key [String] Key name + # @param operator [String] Operator + # @param value [String] Value to match or assign + def add_part(key, operator, value) + @parts << UdevRulePart.new(key, operator, value) + end + + # Returns an string representation that can be used in a rules file + # + # @return [String] + def to_s + parts.map(&:to_s).join(", ") + end + + # Returns the part with the given key + # + # @param key [String] Key name to match + # @param operator [String,nil] Operator to match; nil omits matching the operator + def part_by_key(key, operator = nil) + parts.find { |p| p.key == key && (operator.nil? || p.operator == operator) } + end + + # Returns the value for a given part + # + # @param key [String] Key name + # @param operator [String,nil] Operator to match; nil omits matching the operator + # @return [String,nil] Value or nil if not found a part which such a key + def part_value_for(key, operator = nil) + part = part_by_key(key, operator) + return nil unless part + part.value + end + + # Returns the MAC in the udev rule + # + # @return [String,nil] MAC address or nil if not found + # @see #part_value_for + def mac + part_value_for("ATTR{address}") + end + + # Returns the BUS ID in the udev rule + # + # @return [String,nil] BUS ID or nil if not found + # @see #part_value_for + def bus_id + part_value_for("KERNELS") + end + + # Returns the device port in the udev rule + # + # @return [String,nil] Device port or nil if not found + # @see #part_value_for + def dev_port + part_value_for("ATTR{dev_port}") + end + + # Returns the device mentioned in the rule (if any) + # + # @return [String,nil] Device name or nil if not found + def device + part_value_for("NAME", "=") + end + + # Returns the original modalias + # + # @return [String,nil] Original modalias or nil if not found + def original_modalias + part_value_for("ENV{MODALIAS}", "==") + end + + # Returns the modalias + # + # @return [String,nil] Original modalias or nil if not found + def driver + part_value_for("ENV{MODALIAS}", "=") + end + end +end diff --git a/src/lib/y2network/udev_rule_part.rb b/src/lib/y2network/udev_rule_part.rb new file mode 100644 index 000000000..cce777152 --- /dev/null +++ b/src/lib/y2network/udev_rule_part.rb @@ -0,0 +1,88 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +module Y2Network + # Simple class to represent a key-value pair in a udev rule + # + # This class does not check whether operators or keys/values are valid or not. We can implement + # that logic later if required. + class UdevRulePart + # Regular expression to match a udev rule part + PART_REGEXP = Regexp.new("\\A(?[A-Za-z\{\}]+)(?[^\"]+)\"(?.+)\"\\Z") + + class << self + # Returns a rule part from a string + # + # @example Simple case + # part = UdevRulePart.from_string('ACTION=="add"') + # part.key #=> "ACTION" + # part.operator #=> "==" + # part.value #=> "add" + # + # @example Using globs + # part = UdevRulePart.from_string('ATTR{address}=='"?*31:78:f2"') + # part.key #=> "ATTR{æddress}" + # part.operator #=> "==" + # part.value #=> "\"?*31:78:f2\"" + + # @param str [String] string form of an udev rule + # @return [UdevRule] udev rule object + def from_string(str) + match = PART_REGEXP.match(str) + return if match.nil? + new(match[:key], match[:operator], match[:value]) + end + end + # @return [String] Key name + attr_accessor :key + # @return [String] Operator. There are two comparison operators ("==", "!=") and four assignment + # operators ("=", "+=", "-=", ":="). See udev(7) for further information. + attr_accessor :operator + # @return [String] Value to match or assign + attr_accessor :value + + # Constructor + # + # @param key [String] Key name + # @param operator [String] Operator ("==", "!=", "=", "+=", "-=", ":=") + # @param value [String] Value to match or assign + def initialize(key, operator, value) + @key = key + @operator = operator + @value = value + end + + # Determines whether two udev rule parts are equivalent + # + # @param other [UdevRulePart] The rule part to compare with + # @return [Boolean] + def ==(other) + key == other.key && operator == other.operator && value == other.value + end + + alias_method :eql?, :== + + # Returns an string representation of the udev rule part + # + # @return [String] + def to_s + "#{key}#{operator}\"#{value}\"" + end + end +end diff --git a/src/lib/y2network/widgets/add_interface.rb b/src/lib/y2network/widgets/add_interface.rb new file mode 100644 index 000000000..7df6882cb --- /dev/null +++ b/src/lib/y2network/widgets/add_interface.rb @@ -0,0 +1,43 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/common_widgets" +require "y2network/sequences/interface" + +Yast.import "Label" + +module Y2Network + module Widgets + class AddInterface < CWM::PushButton + def initialize + textdomain "network" + end + + def label + Yast::Label.AddButton + end + + def handle + Y2Network::Sequences::Interface.new.add + :redraw + end + end + end +end diff --git a/src/lib/y2network/widgets/additional_addresses.rb b/src/lib/y2network/widgets/additional_addresses.rb index da3890dba..269378eaf 100644 --- a/src/lib/y2network/widgets/additional_addresses.rb +++ b/src/lib/y2network/widgets/additional_addresses.rb @@ -1,5 +1,25 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/custom_widget" +require "ipaddr" Yast.import "IP" Yast.import "Popup" @@ -81,8 +101,7 @@ def init def refresh_table table_items = @settings.aliases.each_with_index.map do |data, i| - mask = data[:prefixlen].empty? ? data[:mask] : "/#{data[:prefixlen]}" - Item(Id(i), data[:label], data[:ip], mask) + Item(Id(i), data[:label], data[:ip], "/#{data[:prefixlen]}") end Yast::UI.ChangeWidget(Id(:address_table), :Items, table_items) @@ -138,11 +157,9 @@ def handle(event) def dialog(name, entry) label = entry ? entry[:label] : "" ip = entry ? entry[:ip] : "" - mask = if entry - entry[:prefixlen].empty? ? entry[:mask] : "/#{entry[:prefixlen]}" - else - "" - end + id = entry ? entry[:id] : "" + mask = entry ? "/#{entry[:prefixlen]}" : "" + Yast::UI.OpenDialog( Opt(:decorated), VBox( @@ -210,6 +227,7 @@ def dialog(name, entry) Yast::UI.SetFocus(Id(:netmask)) next end + netmask = "" prefixlen = "" if val.start_with?("/") @@ -219,8 +237,14 @@ def dialog(name, entry) else netmask = val end - res[:mask] = netmask + + res[:prefixlen] = if prefixlen.empty? + IPAddr.new("#{netmask}/#{netmask}") + else + prefixlen + end res[:prefixlen] = prefixlen + res[:id] = id break end diff --git a/src/lib/y2network/widgets/address_tab.rb b/src/lib/y2network/widgets/address_tab.rb index be64a3577..b71923c82 100644 --- a/src/lib/y2network/widgets/address_tab.rb +++ b/src/lib/y2network/widgets/address_tab.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/tabs" @@ -27,13 +46,13 @@ def label def contents type = @settings.type - drvtype = driver_type(type) + drvtype = driver_type(type.short_name) # TODO: check if this kind of device is still valid and used is_ptp = drvtype == "ctc" || drvtype == "iucv" # TODO: dynamic for dummy. or add dummy from outside? no_dhcp = is_ptp || - type == "dummy" + type.dummy? address_p2p_contents = Frame( "", # labelless frame @@ -61,10 +80,10 @@ def contents end label = HBox( - type == "vlan" ? VBox(HBox(VlanInterface.new(@settings), VlanID.new(@settings))) : Empty() + type.vlan? ? VBox(HBox(VlanInterface.new(@settings), VlanID.new(@settings))) : Empty() ) - address_contents = if ["tun", "tap"].include?(type) + address_contents = if type.tun? || type.tap? # TODO: move it to own tab or general as it does not fit here VBox(Left(label), Tunnel.new(@settings)) else diff --git a/src/lib/y2network/widgets/blink_button.rb b/src/lib/y2network/widgets/blink_button.rb index 86af77002..afd4d0350 100644 --- a/src/lib/y2network/widgets/blink_button.rb +++ b/src/lib/y2network/widgets/blink_button.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/custom_widget" require "y2network/widgets/slave_items" @@ -31,7 +50,7 @@ def contents end def handle - device = @settings["IFCFG"] + device = @settings.name timeout = Yast::UI.QueryWidget(:blink_time, :Value) log.info "blink, blink ... #{timeout} seconds on #{device} device" cmd = "/usr/sbin/ethtool -p #{device.shellescape} #{timeout.to_i}" diff --git a/src/lib/y2network/widgets/bond_options.rb b/src/lib/y2network/widgets/bond_options.rb index a452cbd81..4f74282a4 100644 --- a/src/lib/y2network/widgets/bond_options.rb +++ b/src/lib/y2network/widgets/bond_options.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" module Y2Network diff --git a/src/lib/y2network/widgets/bond_slave.rb b/src/lib/y2network/widgets/bond_slave.rb index 0f720cf78..4a4691ea0 100644 --- a/src/lib/y2network/widgets/bond_slave.rb +++ b/src/lib/y2network/widgets/bond_slave.rb @@ -1,9 +1,29 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "ui/text_helpers" require "yast" require "cwm/custom_widget" require "y2network/widgets/slave_items" Yast.import "Label" +Yast.import "Lan" Yast.import "Popup" Yast.import "UI" @@ -68,11 +88,12 @@ def help # Default function to init the value of slave devices box for bonding. def init - slaves = @settings["BOND_SLAVES"] || [] + slaves = @settings.slaves # TODO: use def items, but problem now is that slave_items returns term and not array items = slave_items_from( @settings.bondable_interfaces.map(&:name), - slaves + slaves, + Yast::Lan.yast_config # ideally get it from builder? ) # reorder the items @@ -95,7 +116,7 @@ def init # Default function to store the value of slave devices box. def store - @settings["BOND_SLAVES"] = selected_items + @settings.slaves = selected_items end # Validates created bonding. Currently just prevent the user to create a @@ -105,7 +126,19 @@ def store def validate physical_ports = repeated_physical_port_ids(selected_items) - physical_ports.empty? ? true : continue_with_duplicates?(physical_ports) + if !physical_ports.empty? + return false unless continue_with_duplicates?(physical_ports) + end + + if @settings.already_configured?(selected_items || []) + return Yast::Popup.ContinueCancel( + _( + "At least one selected device is already configured.\nAdapt the configuration for bonding?\n" + ) + ) + else + true + end end def value diff --git a/src/lib/y2network/widgets/bond_slaves_tab.rb b/src/lib/y2network/widgets/bond_slaves_tab.rb index e591084cb..3d4be73a0 100644 --- a/src/lib/y2network/widgets/bond_slaves_tab.rb +++ b/src/lib/y2network/widgets/bond_slaves_tab.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/tabs" diff --git a/src/lib/y2network/widgets/boot_protocol.rb b/src/lib/y2network/widgets/boot_protocol.rb index 486879609..83060689a 100644 --- a/src/lib/y2network/widgets/boot_protocol.rb +++ b/src/lib/y2network/widgets/boot_protocol.rb @@ -1,6 +1,27 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/custom_widget" +require "y2network/boot_protocol" + Yast.import "DNS" Yast.import "Hostname" Yast.import "IP" @@ -87,7 +108,7 @@ def contents def ibft_available? # IBFT only for eth, is it correct? - @settings.type == "eth" + @settings.type.ethernet? end def init @@ -98,53 +119,45 @@ def init ) end - case @settings["BOOTPROTO"] - when "static" + case @settings.boot_protocol + when Y2Network::BootProtocol::STATIC Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_static) Yast::UI.ChangeWidget( Id(:bootproto_ipaddr), :Value, - @settings["IPADDR"] || "" + @settings.ip_address + ) + Yast::UI.ChangeWidget( + Id(:bootproto_netmask), + :Value, + @settings.subnet_prefix ) - if @settings["PREFIXLEN"] && !@settings["PREFIXLEN"].empty? - Yast::UI.ChangeWidget( - Id(:bootproto_netmask), - :Value, - "/#{@settings["PREFIXLEN"]}" - ) - else - Yast::UI.ChangeWidget( - Id(:bootproto_netmask), - :Value, - @settings["NETMASK"] || "" - ) - end Yast::UI.ChangeWidget( Id(:bootproto_hostname), :Value, - @settings["HOSTNAME"] + @settings.hostname ) - when "dhcp" + when Y2Network::BootProtocol::DHCP Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_dynamic) Yast::UI.ChangeWidget(Id(:bootproto_dhcp_mode), :Value, :bootproto_dhcp_both) Yast::UI.ChangeWidget(Id(:bootproto_dyn), :Value, :bootproto_dhcp) - when "dhcp4" + when Y2Network::BootProtocol::DHCP4 Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_dynamic) Yast::UI.ChangeWidget(Id(:bootproto_dhcp_mode), :Value, :bootproto_dhcp_v4) Yast::UI.ChangeWidget(Id(:bootproto_dyn), :Value, :bootproto_dhcp) - when "dhcp6" + when Y2Network::BootProtocol::DHCP6 Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_dynamic) Yast::UI.ChangeWidget(Id(:bootproto_dhcp_mode), :Value, :bootproto_dhcp_v6) Yast::UI.ChangeWidget(Id(:bootproto_dyn), :Value, :bootproto_dhcp) - when "dhcp+autoip" + when Y2Network::BootProtocol::DHCP_AUTOIP Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_dynamic) Yast::UI.ChangeWidget(Id(:bootproto_dyn), :Value, :bootproto_dhcp_auto) - when "autoip" + when Y2Network::BootProtocol::AUTOIP Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_dynamic) Yast::UI.ChangeWidget(Id(:bootproto_dyn), :Value, :bootproto_auto) - when "none" + when Y2Network::BootProtocol::NONE Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_none) - when "ibft" + when Y2Network::BootProtocol::IBFT Yast::UI.ChangeWidget(Id(:bootproto), :CurrentButton, :bootproto_none) Yast::UI.ChangeWidget(Id(:bootproto_ibft), :Value, true) end @@ -184,42 +197,37 @@ def handle def store # FIXME: this value reset should be in backend in general not Yast::UI responsibility - @settings["IPADDR"] = @settings["NETMASK"] = @settings["PREFIXLEN"] = "" + @settings.ip_address = "" + @settings.subnet_prefix = "" case value when :bootproto_none bootproto = "none" if ibft_available? bootproto = Yast::UI.QueryWidget(Id(:bootproto_ibft), :Value) ? "ibft" : "none" end - @settings["BOOTPROTO"] = bootproto + @settings.boot_protocol = bootproto when :bootproto_static - @settings["BOOTPROTO"] = "static" - @settings["IPADDR"] = Yast::UI.QueryWidget(:bootproto_ipaddr, :Value) - mask = Yast::UI.QueryWidget(:bootproto_netmask, :Value) - if mask.start_with?("/") - @settings["PREFIXLEN"] = mask[1..-1] - else - param = Yast::Netmask.Check6(mask) ? "PREFIXLEN" : "NETMASK" - @settings[param] = mask - end - @settings["HOSTNAME"] = Yast::UI.QueryWidget(:bootproto_hostname, :Value) + @settings.boot_protocol = "static" + @settings.ip_address = Yast::UI.QueryWidget(:bootproto_ipaddr, :Value) + @settings.subnet_prefix = Yast::UI.QueryWidget(:bootproto_netmask, :Value) + @settings.hostname = Yast::UI.QueryWidget(:bootproto_hostname, :Value) when :bootproto_dynamic case Yast::UI.QueryWidget(:bootproto_dyn, :Value) when :bootproto_dhcp case Yast::UI.QueryWidget(:bootproto_dhcp_mode, :Value) when :bootproto_dhcp_both - @settings["BOOTPROTO"] = "dhcp" + @settings.boot_protocol = "dhcp" when :bootproto_dhcp_v4 - @settings["BOOTPROTO"] = "dhcp4" + @settings.boot_protocol = "dhcp4" when :bootproto_dhcp_v6 - @settings["BOOTPROTO"] = "dhcp6" + @settings.boot_protocol = "dhcp6" else raise "Unexpected dhcp mode value #{Yast::UI.QueryWidget(:bootproto_dhcp_mode, :Value).inspect}" end when :bootproto_dhcp_auto - @settings["BOOTPROTO"] = "dhcp+autoip" + @settings.boot_protocol = "dhcp+autoip" when :bootproto_auto - @settings["BOOTPROTO"] = "autoip" + @settings.boot_protocol = "autoip" else raise "Unexpected dynamic mode value #{Yast::UI.QueryWidget(:bootproto_dyn, :Value).inspect}" end @@ -248,7 +256,7 @@ def validate hname = Yast::UI.QueryWidget(:bootproto_hostname, :Value) if !hname.empty? if !Yast::Hostname.CheckFQ(hname) - Popup.Error(_("Invalid hostname.")) + Yast::Popup.Error(_("Invalid hostname.")) Yast::UI.SetFocus(:bootproto_hostname) return false end diff --git a/src/lib/y2network/widgets/bridge_ports.rb b/src/lib/y2network/widgets/bridge_ports.rb index 20cca779b..752f041aa 100644 --- a/src/lib/y2network/widgets/bridge_ports.rb +++ b/src/lib/y2network/widgets/bridge_ports.rb @@ -1,8 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" require "y2network/widgets/slave_items" Yast.import "Label" +Yast.import "Lan" Yast.import "Popup" Yast.import "UI" @@ -11,6 +31,7 @@ module Widgets class BridgePorts < CWM::MultiSelectionBox include SlaveItems + # @param [Y2Network::InterfaceConfigBuilders::Bridge] settings def initialize(settings) textdomain "network" @settings = settings @@ -27,10 +48,11 @@ def help # Default function to init the value of slave devices box for bridging. def init - br_ports = @settings["BRIDGE_PORTS"].split + br_ports = @settings.ports items = slave_items_from( @settings.bridgeable_interfaces.map(&:name), - br_ports + br_ports, + Yast::Lan.yast_config # ideally get it from builder? ) # it is list of Items, so cannot use `change_items` helper @@ -39,8 +61,7 @@ def init # Default function to store the value of slave devices box. def store - # TODO: fix it in builder to use array and not space separated string - @settings["BRIDGE_PORTS"] = value.join(" ") + @settings.ports = value end # Validates created bridge. Currently just prevent the user to create a diff --git a/src/lib/y2network/widgets/bridge_slaves_tab.rb b/src/lib/y2network/widgets/bridge_slaves_tab.rb index 884132552..74d45f80b 100644 --- a/src/lib/y2network/widgets/bridge_slaves_tab.rb +++ b/src/lib/y2network/widgets/bridge_slaves_tab.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/tabs" diff --git a/src/lib/y2network/widgets/client_cert_path.rb b/src/lib/y2network/widgets/client_cert_path.rb new file mode 100644 index 000000000..6006a5197 --- /dev/null +++ b/src/lib/y2network/widgets/client_cert_path.rb @@ -0,0 +1,58 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/widgets/path_widget" + +module Y2Network + module Widgets + class ClientCertPath < PathWidget + def initialize(builder) + textdomain "network" + @builder = builder + end + + # FIXME: label and help text is wrong, here it is certificate of CA that is used to sign server certificate + def label + _("&Client Certificate") + end + + def help + _( + "

TLS uses a Client Certificate instead of a username and\n" \ + "password combination for authentication. It uses a public and private key pair\n" \ + "to encrypt negotiation communication, therefore you will additionally need\n" \ + "a Client Key file that contains your private key and\n" \ + "the appropriate Client Key Password for that file.

\n" + ) + end + + def browse_label + _("Choose a Certificate") + end + + def init + self.value = @builder.client_cert + end + + def store + @builder.client_cert = value + end + end + end +end diff --git a/src/lib/y2network/widgets/client_key_path.rb b/src/lib/y2network/widgets/client_key_path.rb new file mode 100644 index 000000000..ccc16a25d --- /dev/null +++ b/src/lib/y2network/widgets/client_key_path.rb @@ -0,0 +1,51 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/widgets/path_widget" + +module Y2Network + module Widgets + class ClientKeyPath < PathWidget + def initialize(builder) + textdomain "network" + @builder = builder + end + + def label + _("Client &Key") + end + + def help + "" # TODO: was missing, write something + end + + def browse_label + _("Choose a File with Private Key") + end + + def init + self.value = @builder.client_key + end + + def store + @builder.client_key = value + end + end + end +end diff --git a/src/lib/y2network/widgets/delete_interface.rb b/src/lib/y2network/widgets/delete_interface.rb new file mode 100644 index 000000000..176eb0c95 --- /dev/null +++ b/src/lib/y2network/widgets/delete_interface.rb @@ -0,0 +1,60 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/common_widgets" + +Yast.import "Label" +Yast.import "Lan" +Yast.import "Popup" + +module Y2Network + module Widgets + class DeleteInterface < CWM::PushButton + # @param table [InterfacesTable] + def initialize(table) + textdomain "network" + @table = table + end + + def label + Yast::Label.DeleteButton + end + + def handle + config = Yast::Lan.yast_config + connection_config = config.connections.by_name(@table.value) + return nil unless connection_config # unconfigured physical device. Delete do nothing + + if connection_config.startmode.name == "nfsroot" + if !Yast::Popup.YesNoHeadline( + Label.WarningMsg, + _("Device you select has STARTMODE=nfsroot. Really delete?") + ) + return nil + end + end + + config.delete_interface(@table.value) + + :redraw + end + end + end +end diff --git a/src/lib/y2network/widgets/destination.rb b/src/lib/y2network/widgets/destination.rb index 06cf31691..6f4ae7779 100644 --- a/src/lib/y2network/widgets/destination.rb +++ b/src/lib/y2network/widgets/destination.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + Yast.import "IP" Yast.import "Popup" diff --git a/src/lib/y2network/widgets/devices.rb b/src/lib/y2network/widgets/devices.rb index 16094fe63..b1aef5508 100644 --- a/src/lib/y2network/widgets/devices.rb +++ b/src/lib/y2network/widgets/devices.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "y2network/interface" require "cwm/common_widgets" diff --git a/src/lib/y2network/widgets/driver.rb b/src/lib/y2network/widgets/driver.rb new file mode 100644 index 000000000..fa5a02c72 --- /dev/null +++ b/src/lib/y2network/widgets/driver.rb @@ -0,0 +1,111 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm" +require "y2network/widgets/kernel_module" +require "y2network/widgets/kernel_options" + +module Y2Network + module Widgets + # Widget to select the driver and to specify its options + class Driver < CWM::CustomWidget + include Yast::Logger + + def initialize(builder) + textdomain "network" + @builder = builder + self.handle_all_events = true + end + + def contents + Frame( + _("&Kernel Module"), + HBox( + HSpacing(0.5), + VBox( + VSpacing(0.4), + HBox( + kernel_module_widget, + HSpacing(0.5), + kernel_options_widget + ), + VSpacing(0.4) + ), + HSpacing(0.5) + ) + ) + end + + def init + disable_kernel_options if @builder.driver == :auto + end + + def handle(event) + return unless event["ID"] == "kernel_module" && event["EventReason"] == "ValueChanged" + return nil if @old_kernel_module == kernel_module_widget.value + + if kernel_module_widget.value == :auto + disable_kernel_options + else + enable_kernel_options + end + + @old_kernel_module = kernel_module_widget.value + + nil + end + + def store + @builder.driver = + if kernel_module_widget.value == :auto + :auto + else + Y2Network::Driver.new(kernel_module_widget.value, kernel_options_widget.value) + end + end + + private + + def kernel_module_widget + return @kernel_module_widget if @kernel_module_widget + drivers_names = @builder.drivers.map(&:name) + selected_driver = @builder.driver.name if @builder.driver != :auto + @kernel_module_widget = KernelModule.new(drivers_names, selected_driver) + end + + def kernel_options_widget + return @kernel_options_widget if @kernel_options_widget + options = @builder.driver != :auto ? @builder.driver.params : "" + @kernel_options_widget ||= KernelOptions.new(options) + end + + def disable_kernel_options + kernel_options_widget.value = "" + kernel_options_widget.disable + end + + def enable_kernel_options + new_driver = @builder.drivers.find { |d| d.name == kernel_module_widget.value } + kernel_options_widget.value = new_driver.params if new_driver + kernel_options_widget.enable + end + end + end +end diff --git a/src/lib/y2network/widgets/edit_interface.rb b/src/lib/y2network/widgets/edit_interface.rb new file mode 100644 index 000000000..d8838b45e --- /dev/null +++ b/src/lib/y2network/widgets/edit_interface.rb @@ -0,0 +1,60 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/common_widgets" +require "y2network/sequences/interface" + +Yast.import "Label" +Yast.import "Lan" + +module Y2Network + module Widgets + class EditInterface < CWM::PushButton + # @param table [InterfacesTable] + def initialize(table) + textdomain "network" + + @table = table + end + + def label + Yast::Label.EditButton + end + + def handle + config = Yast::Lan.yast_config.copy + connection_config = config.connections.by_name(@table.value) + + name, type = + if connection_config + [connection_config.name, connection_config.type] + else + interface = config.interfaces.by_name(@table.value) + [interface.name, interface.type] + end + + builder = Y2Network::InterfaceConfigBuilder.for(type, config: connection_config) + builder.name = name + Y2Network::Sequences::Interface.new.edit(builder) + :redraw + end + end + end +end diff --git a/src/lib/y2network/widgets/ethtools_options.rb b/src/lib/y2network/widgets/ethtools_options.rb index df62983e8..d18123749 100644 --- a/src/lib/y2network/widgets/ethtools_options.rb +++ b/src/lib/y2network/widgets/ethtools_options.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -25,11 +44,11 @@ def opt end def init - self.value = @settings["ETHTOOL_OPTIONS"] + self.value = @settings.ethtool_options end def store - @settings["ETHTOOL_OPTIONS"] = value + @settings.ethtool_options = value end end end diff --git a/src/lib/y2network/widgets/firewall_zone.rb b/src/lib/y2network/widgets/firewall_zone.rb index 40cb54f3a..848e0739d 100644 --- a/src/lib/y2network/widgets/firewall_zone.rb +++ b/src/lib/y2network/widgets/firewall_zone.rb @@ -102,12 +102,6 @@ def default_label _("Assign to the default ZONE") end - # @return [String] - def no_zone_label - # TRANSLATORS: List item to no interface ZONE assignment - _("Do not assign ZONE") - end - # @return [Yast::Term] zones select list def zones_widget ComboBox(Id(:zones), Opt(:notify, :hstretch), label) @@ -140,7 +134,7 @@ def populate_select(zones) # @return [Array >] list of names an description of # available zones def firewall_zones - zones = [[nil, no_zone_label], ["", default_label]] + zones = [["", default_label]] firewalld.zones.each { |z| zones << [z.name, z.name] } zones end diff --git a/src/lib/y2network/widgets/gateway.rb b/src/lib/y2network/widgets/gateway.rb index 0a477b4fc..8e9f87778 100644 --- a/src/lib/y2network/widgets/gateway.rb +++ b/src/lib/y2network/widgets/gateway.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + Yast.import "IP" Yast.import "Popup" Yast.import "UI" diff --git a/src/lib/y2network/widgets/general_tab.rb b/src/lib/y2network/widgets/general_tab.rb index c9c4670cc..9fd36a59d 100644 --- a/src/lib/y2network/widgets/general_tab.rb +++ b/src/lib/y2network/widgets/general_tab.rb @@ -1,9 +1,27 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/tabs" # used widgets -require "y2network/widgets/interface_name" -require "y2network/widgets/udev_rules" +require "y2network/widgets/interface_naming" require "y2network/widgets/startmode" require "y2network/widgets/ifplugd_priority" require "y2network/widgets/firewall_zone" @@ -33,8 +51,7 @@ def contents 1, 0, VBox( - # FIXME: udev rules for anything without hwinfo is wrong - @settings.newly_added? ? InterfaceName.new(@settings) : UdevRules.new(@settings), + InterfaceNaming.new(@settings), Frame( _("Device Activation"), HBox(Startmode.new(@settings, ifplugd_widget), ifplugd_widget, HStretch()) @@ -42,8 +59,8 @@ def contents VSpacing(0.4), Frame(_("Firewall Zone"), HBox(FirewallZone.new(@settings), HStretch())), VSpacing(0.4), - type == "ib" ? HBox(IPoIBMode.new(@settings)) : Empty(), - type == "ib" ? VSpacing(0.4) : Empty(), + type.infiniband? ? HBox(IPoIBMode.new(@settings)) : Empty(), + type.infiniband? ? VSpacing(0.4) : Empty(), Frame( _("Maximum Transfer Unit (MTU)"), HBox(MTU.new(@settings), HStretch()) diff --git a/src/lib/y2network/widgets/hardware_tab.rb b/src/lib/y2network/widgets/hardware_tab.rb index 773854d5f..b92017f5e 100644 --- a/src/lib/y2network/widgets/hardware_tab.rb +++ b/src/lib/y2network/widgets/hardware_tab.rb @@ -1,10 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/tabs" # used widgets require "y2network/widgets/blink_button" -require "y2network/widgets/kernel_module" -require "y2network/widgets/kernel_options" +require "y2network/widgets/driver" require "y2network/widgets/ethtools_options" module Y2Network @@ -24,22 +42,7 @@ def contents VBox( # FIXME: ensure that only eth, maybe also ib? eth? ? BlinkButton.new(@settings) : Empty(), - Frame( - _("&Kernel Module"), - HBox( - HSpacing(0.5), - VBox( - VSpacing(0.4), - HBox( - KernelModule.new(@settings), - HSpacing(0.5), - KernelOptions.new(@settings) - ), - VSpacing(0.4) - ), - HSpacing(0.5) - ) - ), + Driver.new(@settings), # FIXME: probably makes sense only for eth EthtoolsOptions.new(@settings), VStretch() @@ -47,7 +50,7 @@ def contents end def eth? - @settings.type == "eth" + @settings.type.ethernet? end end end diff --git a/src/lib/y2network/widgets/ifplugd_priority.rb b/src/lib/y2network/widgets/ifplugd_priority.rb index c356e7990..b262965df 100644 --- a/src/lib/y2network/widgets/ifplugd_priority.rb +++ b/src/lib/y2network/widgets/ifplugd_priority.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" module Y2Network @@ -35,11 +54,11 @@ def maximum end def init - self.value = @config["IFPLUGD_PRIORITY"].to_i + self.value = @config.ifplugd_priority end def store - @config["IFPLUGD_PRIORITY"] = value.to_s + @config.ifplugd_priority = value.to_i if enabled? end end end diff --git a/src/lib/y2network/widgets/interface_description.rb b/src/lib/y2network/widgets/interface_description.rb new file mode 100644 index 000000000..bb107ad4c --- /dev/null +++ b/src/lib/y2network/widgets/interface_description.rb @@ -0,0 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/common_widgets" + +module Y2Network + module Widgets + class InterfaceDescription < CWM::RichText + end + end +end diff --git a/src/lib/y2network/widgets/interface_name.rb b/src/lib/y2network/widgets/interface_name.rb index 2d7122d44..007cace84 100644 --- a/src/lib/y2network/widgets/interface_name.rb +++ b/src/lib/y2network/widgets/interface_name.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -11,6 +30,7 @@ def initialize(settings) textdomain "network" @settings = settings + @old_name = @settings.name end def label @@ -38,7 +58,7 @@ def items end def validate - if @settings.name_exists?(value) + if @old_name != value && @settings.name_exists?(value) Yast::Popup.Error( format(_("Configuration name %s already exists.\nChoose a different one."), value) ) @@ -60,7 +80,8 @@ def validate end def store - @settings.name = value + return if @settings.name == value + @settings.rename_interface(value) end end end diff --git a/src/lib/y2network/widgets/interface_naming.rb b/src/lib/y2network/widgets/interface_naming.rb new file mode 100644 index 000000000..c733fb173 --- /dev/null +++ b/src/lib/y2network/widgets/interface_naming.rb @@ -0,0 +1,58 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/custom_widget" +require "y2network/widgets/udev_rules" +require "y2network/widgets/interface_name" + +module Y2Network + module Widgets + # Widget to handle interface naming, including udev based renames. + class InterfaceNaming < ::CWM::CustomWidget + # Constructor + # + # @param builder [Y2Network::InterfaceConfigBuilder] Interface configuration builder object + def initialize(builder) + @builder = builder + end + + # @see CWM::CustomWidget#contents + def contents + VBox(widget) + end + + private + + def udev_based_rename? + return false unless @builder.interface + hardware = @builder.interface.hardware + return false unless hardware + !!(hardware.mac || hardware.busid) + end + + # Internal widget + # + # It uses an internal widget to handle the rename depending on whether + # udev based renames are needed or not. + def widget + @widget ||= udev_based_rename? ? UdevRules.new(@builder) : InterfaceName.new(@builder) + end + end + end +end diff --git a/src/lib/y2network/widgets/interface_type.rb b/src/lib/y2network/widgets/interface_type.rb index 1c13b702d..83972f391 100644 --- a/src/lib/y2network/widgets/interface_type.rb +++ b/src/lib/y2network/widgets/interface_type.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" diff --git a/src/lib/y2network/widgets/interfaces_table.rb b/src/lib/y2network/widgets/interfaces_table.rb new file mode 100644 index 000000000..0d7a61f39 --- /dev/null +++ b/src/lib/y2network/widgets/interfaces_table.rb @@ -0,0 +1,134 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/table" +require "y2network/presenters/interface_summary" + +Yast.import "NetworkService" +Yast.import "Lan" +Yast.import "Popup" +Yast.import "UI" + +module Y2Network + module Widgets + class InterfacesTable < CWM::Table + def initialize(description) + textdomain "network" + + @description = description + end + + def header + [ + _("Name"), + _("IP Address"), + _("Device"), + _("Note") + ] + end + + def opt + [:notify, :immediate] + end + + def handle + @description.value = create_description + + nil + end + + def items + config = Yast::Lan.yast_config + config.interfaces.map do |interface| + conn = config.connections.by_name(interface.name) + [ + interface.name, # first is ID in table + friendly_name(interface), + interface_protocol(conn), + interface.name, + note(interface, conn, config) + ] + end + end + + # Workaround for usage in old CWM which also cache content of cwm items + def init + if Yast::NetworkService.is_network_manager + Yast::Popup.Warning( + _( + "Network is currently handled by NetworkManager\n" \ + "or completely disabled. YaST is unable to configure some options." + ) + ) + # switch to global tab + Yast::UI.FakeUserInput("ID" => "global") + return + end + + change_items(items) + handle + end + + private + + def note(interface, conn, config) + if interface.name != interface.old_name && interface.old_name + return format("%s -> %s", interface.old_name, interface.name) + end + + return "" unless conn + + master = conn.find_master(config.connections) + return format(_("enslaved in %s"), master.name) if master + + return format(_("parent: %s"), conn.parent_device) if conn.type.vlan? + + "" + end + + def interface_protocol(connection) + return _("Not configured") if connection.nil? + + bootproto = connection.bootproto.name + + if bootproto == "static" + ip_config = connection.ip + ip_config ? ip_config.address.to_s : "" + else + bootproto.upcase + end + end + + def create_description + config = Yast::Lan.yast_config + Presenters::InterfaceSummary.new(value, config).text + end + + # Returns a friendly name for a given interface + # + # @param interface [Interface] Network interface + # @return [String] Friendly name for the interface (description or name) + def friendly_name(interface) + hwinfo = interface.hardware + hwinfo && hwinfo.present? ? hwinfo.description : interface.name + end + end + end +end diff --git a/src/lib/y2network/widgets/ip4_forwarding.rb b/src/lib/y2network/widgets/ip4_forwarding.rb index 2bc08ca06..28f40015b 100644 --- a/src/lib/y2network/widgets/ip4_forwarding.rb +++ b/src/lib/y2network/widgets/ip4_forwarding.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" Yast.import "NetworkService" diff --git a/src/lib/y2network/widgets/ip6_forwarding.rb b/src/lib/y2network/widgets/ip6_forwarding.rb index b76934346..c8bee1714 100644 --- a/src/lib/y2network/widgets/ip6_forwarding.rb +++ b/src/lib/y2network/widgets/ip6_forwarding.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" Yast.import "NetworkService" diff --git a/src/lib/y2network/widgets/ip_address.rb b/src/lib/y2network/widgets/ip_address.rb index aec71feb9..7e16ae1ad 100644 --- a/src/lib/y2network/widgets/ip_address.rb +++ b/src/lib/y2network/widgets/ip_address.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -27,11 +46,11 @@ def opt end def init - self.value = @settings["IPADDR"] + self.value = @settings.ip_address end def store - @settings["IPADDR"] = value + @settings.ip_address = value end def validate diff --git a/src/lib/y2network/widgets/ipoib_mode.rb b/src/lib/y2network/widgets/ipoib_mode.rb index 38d1468b8..fc1979b10 100644 --- a/src/lib/y2network/widgets/ipoib_mode.rb +++ b/src/lib/y2network/widgets/ipoib_mode.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" diff --git a/src/lib/y2network/widgets/kernel_module.rb b/src/lib/y2network/widgets/kernel_module.rb index c9a507d79..88a5340cd 100644 --- a/src/lib/y2network/widgets/kernel_module.rb +++ b/src/lib/y2network/widgets/kernel_module.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -5,9 +24,15 @@ module Y2Network module Widgets class KernelModule < CWM::ComboBox - def initialize(settings) + # Constructor + # + # @param names [Array] Drivers names + # @param selected [String,nil] Initially selected driver (nil if no driver is selected) + def initialize(names, selected) textdomain "network" - @settings = settings + @names = names + @selected = selected + self.widget_id = "kernel_module" end def label @@ -21,21 +46,20 @@ def help end def opt - [:editable] + [:editable, :notify] end def items - @settings.kernel_modules.map do |i| - [i, i] - end + @items ||= [["", _("Auto")]] + @names.map { |n| [n, n] } end def init - self.value = @settings.driver unless @settings.driver.empty? + self.value = @selected if @selected end - def store - @settings.driver = value + def value + ret = super + ret == "" ? :auto : ret end end end diff --git a/src/lib/y2network/widgets/kernel_options.rb b/src/lib/y2network/widgets/kernel_options.rb index 2424798a3..5a7550321 100644 --- a/src/lib/y2network/widgets/kernel_options.rb +++ b/src/lib/y2network/widgets/kernel_options.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -6,10 +25,12 @@ module Y2Network module Widgets class KernelOptions < CWM::InputField - def initialize(settings) + # Constructor + # + # @param options [String] Driver options + def initialize(options) textdomain "network" - - @settings = settings + @options = options end def label @@ -29,11 +50,7 @@ def opt end def init - self.value = @settings.driver_options - end - - def store - @settings.driver_options = value + self.value = @options end end end diff --git a/src/lib/y2network/widgets/mtu.rb b/src/lib/y2network/widgets/mtu.rb index 6d30feab9..08e7407af 100644 --- a/src/lib/y2network/widgets/mtu.rb +++ b/src/lib/y2network/widgets/mtu.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" module Y2Network @@ -34,16 +53,16 @@ def ipoib_items end def items - @settings["IFCFGTYPE"] == "ib" ? ipoib_items : default_items + @settings.type.infiniband? ? ipoib_items : default_items end def init change_items(items) - self.value = @settings["MTU"] + self.value = @settings.mtu end def store - @settings["MTU"] = value + @settings.mtu = value end end end diff --git a/src/lib/y2network/widgets/netmask.rb b/src/lib/y2network/widgets/netmask.rb index fb6d3eec3..c3560efd4 100644 --- a/src/lib/y2network/widgets/netmask.rb +++ b/src/lib/y2network/widgets/netmask.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -28,21 +47,11 @@ def opt end def init - self.value = if @settings["PREFIXLEN"] && !@settings["PREFIXLEN"].empty? - "/#{@settings["PREFIXLEN"]}" - else - @settings["NETMASK"] || "" - end + self.value = @settings.subnet_prefix end def store - mask = value - if mask.start_with?("/") - @settings["PREFIXLEN"] = mask[1..-1] - else - param = Yast::Netmask.Check6(mask) ? "PREFIXLEN" : "NETMASK" - @settings[param] = mask - end + @settings.subnet_prefix = value end def validate diff --git a/src/lib/y2network/widgets/path_widget.rb b/src/lib/y2network/widgets/path_widget.rb new file mode 100644 index 000000000..1bf76b239 --- /dev/null +++ b/src/lib/y2network/widgets/path_widget.rb @@ -0,0 +1,77 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "abstract_method" + +module Y2Network + module Widgets + # Generic widget for path with browse button + # TODO: add to CWM as generic widget + class PathWidget < CWM::CustomWidget + def initialize + textdomain "network" + end + + abstract_method :label + abstract_method :browse_label + + def contents + HBox( + InputField(Id(text_id), label), + PushButton(Id(button_id), button_label) + ) + end + + def handle + directory = File.dirname(value) + file = ask_method(directory) + self.value = file if file + + nil + end + + def value + Yast::UI.QueryWidget(Id(text_id), :Value) + end + + def value=(path) + Yast::UI.ChangeWidget(Id(text_id), :Value, path) + end + + def text_id + widget_id + "_path" + end + + def button_id + widget_id + "_browse" + end + + def button_label + "..." + end + + # UI method responsible for asking for file/directory. By default uses + # Yast::UI.AskForExistingFile with "*" filter. Redefine if different popup is needed or + # specific filter required. + def ask_method(directory) + Yast::UI.AskForExistingFile(directory, "*", browse_label) + end + end + end +end diff --git a/src/lib/y2network/widgets/remote_ip.rb b/src/lib/y2network/widgets/remote_ip.rb index 085e56ede..3fa971a44 100644 --- a/src/lib/y2network/widgets/remote_ip.rb +++ b/src/lib/y2network/widgets/remote_ip.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/common_widgets" @@ -25,11 +44,11 @@ def help end def init - self.value = @settings["REMOTEIP"] + self.value = @settings.remote_ip end def store - @settings["REMOTEIP"] = value + @settings.remote_ip = value end def validate diff --git a/src/lib/y2network/widgets/renaming_mechanism.rb b/src/lib/y2network/widgets/renaming_mechanism.rb new file mode 100644 index 000000000..1446f4a7d --- /dev/null +++ b/src/lib/y2network/widgets/renaming_mechanism.rb @@ -0,0 +1,86 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm" +require "cwm/custom_widget" + +module Y2Network + module Widgets + # This class allows the user to select which hardware information + # should be taken into account when renaming a device + class RenamingMechanism < CWM::CustomWidget + # @return [Symbol] Renaming mechanism + attr_reader :result + + # Constructor + # + # @param builder [InterfaceConfigBuilder] Interface configuration builder object + def initialize(builder) + textdomain "network" + @builder = builder + + interface = builder.interface + @hwinfo = interface.hardware + @mac = @hwinfo.mac + @bus_id = @hwinfo.busid + @renaming_mechanism = builder.renaming_mechanism || :mac + end + + # @see CWM::AbstractWidget#init + def init + Yast::UI.ChangeWidget(Id(:udev_type), :Value, @renaming_mechanism) + end + + # @see CWM::AbstractWidget#store + def store + @builder.renaming_mechanism = value + end + + # @see CWM::CustomWidget + def contents + Frame( + _("Base Udev Rule On"), + RadioButtonGroup( + Id(:udev_type), + VBox( + # make sure there is enough space (#367239) + HSpacing(30), + *radio_buttons + ) + ) + ) + end + + # @see CWM::ValueBasedWidget#value + def value + Yast::UI.QueryWidget(Id(:udev_type), :Value) + end + + private + + def radio_buttons + buttons = [] + buttons << Left(RadioButton(Id(:mac), _("MAC address: %s") % @mac)) if @mac + buttons << Left(RadioButton(Id(:bus_id), _("BusID: %s") % @bus_id)) if @bus_id + buttons + end + end + end +end diff --git a/src/lib/y2network/widgets/route_options.rb b/src/lib/y2network/widgets/route_options.rb index 9a26067be..fb2100cdb 100644 --- a/src/lib/y2network/widgets/route_options.rb +++ b/src/lib/y2network/widgets/route_options.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + Yast.import "Netmask" Yast.import "Label" diff --git a/src/lib/y2network/widgets/routing_buttons.rb b/src/lib/y2network/widgets/routing_buttons.rb index d05f67d61..b67b64e1e 100644 --- a/src/lib/y2network/widgets/routing_buttons.rb +++ b/src/lib/y2network/widgets/routing_buttons.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" require "y2network/dialogs/route" require "y2network/route" diff --git a/src/lib/y2network/widgets/routing_table.rb b/src/lib/y2network/widgets/routing_table.rb index 7cdf147ba..324d0775b 100644 --- a/src/lib/y2network/widgets/routing_table.rb +++ b/src/lib/y2network/widgets/routing_table.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "ipaddr" require "cwm/table" diff --git a/src/lib/y2network/widgets/s390_button.rb b/src/lib/y2network/widgets/s390_button.rb index 34f221256..6f55a8c86 100644 --- a/src/lib/y2network/widgets/s390_button.rb +++ b/src/lib/y2network/widgets/s390_button.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" module Y2Network diff --git a/src/lib/y2network/widgets/s390_channels.rb b/src/lib/y2network/widgets/s390_channels.rb new file mode 100644 index 000000000..9993769e5 --- /dev/null +++ b/src/lib/y2network/widgets/s390_channels.rb @@ -0,0 +1,146 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/custom_widget" +require "cwm/common_widgets" + +module Y2Network + module Widgets + # A container widget for setting the QETH and HSI device channels (read, + # write and control) + class S390Channels < CWM::CustomWidget + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def label + "" + end + + # @see CWM::AbstractWidget + def contents + HBox( + S390ReadChannel.new(@settings), + HSpacing(1), + S390WriteChannel.new(@settings), + HSpacing(1), + S390DataChannel.new(@settings) + ) + end + end + + # Widget for setting the s390 device read channel + class S390ReadChannel < CWM::InputField + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.read_channel + end + + # @see CWM::AbstractWidget + def opt + [:hstretch] + end + + # @see CWM::AbstractWidget + def label + _("&Read Channel") + end + + # @see CWM::AbstractWidget + def store + @settings.read_channel = value + end + end + + # Widget for setting the s390 device write channel + class S390WriteChannel < CWM::InputField + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.write_channel + end + + # @see CWM::AbstractWidget + def opt + [:hstretch] + end + + # @see CWM::AbstractWidget + def label + _("&Write Channel") + end + + # @see CWM::AbstractWidget + def store + @settings.write_channel = value + end + end + + # Widget for setting the s390 device data channel + class S390DataChannel < CWM::InputField + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.data_channel + end + + # @see CWM::AbstractWidget + def opt + [:hstretch] + end + + # @see CWM::AbstractWidget + def label + _("Control Channel") + end + + # @see CWM::AbstractWidget + def store + @settings.data_channel = value + end + end + end +end diff --git a/src/lib/y2network/widgets/s390_common.rb b/src/lib/y2network/widgets/s390_common.rb new file mode 100644 index 000000000..8944c801f --- /dev/null +++ b/src/lib/y2network/widgets/s390_common.rb @@ -0,0 +1,367 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" +require "cwm/custom_widget" + +module Y2Network + module Widgets + # Widget for setting the Lcs group device lancmd timeout + class S390LanCmdTimeout < CWM::InputField + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + + @settings = settings + end + + # @see CWM::AbstractWidget + def label + _("&LANCMD Time-Out") + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.timeout + end + + # @see CWM::AbstractWidget + def store + @settings.timeout = value + end + + # @see CWM::AbstractWidget + def help + _("

Specify the LANCMD Time-Out for this interface.

") + end + end + + # Widget for setting the Ctc device protocol to be used + class S390Protocol < CWM::ComboBox + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.protocol.to_s + end + + # @see CWM::AbstractWidget + def label + _("&Protocol") + end + + # @see CWM::AbstractWidget + def items + [ + # ComboBox item: CTC device protocol + ["0", _("Compatibility Mode")], + # ComboBox item: CTC device protocol + ["1", _("Extended Mode")], + # ComboBox item: CTC device protocol + ["2", _("CTC-Based tty (Linux to Linux Connections)")], + # ComboBox item: CTC device protocol + ["3", _("Compatibility Mode with OS/390 and z/OS")] + ] + end + + # @see CWM::AbstractWidget + def store + @settings.protocol = value.to_i + end + + # @see CWM::AbstractWidget + def help + _("

Choose the Protocol for this interface.

") + end + end + + # Widget for specifying whether use the port number 0 or 1 + class S390PortNumber < CWM::ComboBox + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.port_number.to_s + end + + # @see CWM::AbstractWidget + def label + _("Port Number") + end + + # @see CWM::AbstractWidget + def items + [["0", "0"], ["1", "1"]] + end + + # @see CWM::AbstractWidget + def store + @settings.port_number = value.to_i + end + + # @see CWM::AbstractWidget + def help + _("

Choose which physical Port Number on the OSA Adapter " \ + "will be used by this interface. (0 by default)

") + end + end + + # This widget permits to pass defined any extra attribute to set during + # Qeth device activation + class S390Attributes < CWM::InputField + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def label + _("Options") + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.attributes + end + + # @see CWM::AbstractWidget + def opt + [:hstretch] + end + + # @see CWM::AbstractWidget + def help + # TRANSLATORS: S/390 dialog help for QETH Options + _("

Enter any additional Options for this interface (separated by spaces).

") + end + + # @see CWM::AbstractWidget + def store + @settings.attributes = value + end + end + + # Checkbox for enabling IPA Takeover in the configured interface + class S390IPAddressTakeover < CWM::CheckBox + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = !!@settings.ipa_takeover + end + + # @see CWM::AbstractWidget + def label + _("Enable IPA takeover") + end + + # @see CWM::AbstractWidget + def help + _("

Select Enable IPA Takeover if IP address takeover should be enabled " \ + "for this interface.

") + end + + # @see CWM::AbstractWidget + def store + @settings.ipa_takeover = value + end + end + + # This custom widget contents a checkbox for enabling the layer2 support + # and an input field for setting the mac address to be used in case of + # enablement. + class S390Layer2 < CWM::CustomWidget + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + self.handle_all_events = true + end + + # @see CWM::AbstractWidget + def contents + VBox( + Left(support_widget), + Left(mac_address_widget) + ) + end + + # @see CWM::AbstractWidget + def init + refresh + end + + # @see CWM::AbstractWidget + def handle(event) + case event["ID"] + when support_widget.widget_id + refresh + end + + nil + end + + def validate + return true if !layer2? || valid_mac?(mac_address_widget.value) + + report_mac_error + false + end + + private + + def report_mac_error + # TRANSLATORS: Popup error about not valid MAC address provided + msg = _("The MAC address provided is not valid, please provide a valid one.") + Yast::Popup.Error(msg) + end + + # Convenience method to check whether layer2 support is enabled or not + # + # @return [Boolean] true if enabled; false otherwise + def layer2? + !!support_widget.value + end + + # Convenience method to check whether the MAC address provided is valid + # or not + # + # @return [Boolean] true when valid; false otherwise + # @param mac_address [String] + def valid_mac?(mac_address) + return false if mac_address.to_s.empty? + return false if mac_address == "00:00:00:00:00:00" + !!(mac_address =~ /^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/i) + end + + # Convenience method to enable or disable the mac address widget when the + # layer2 support is modified + def refresh + support_widget.checked? ? mac_address_widget.enable : mac_address_widget.disable + end + + # @return [S390Layer2Support] + def support_widget + @support_widget ||= S390Layer2Support.new(@settings) + end + + # @return [S390Layer2Address] + def mac_address_widget + @mac_address_widget ||= S390Layer2Address.new(@settings) + end + end + + # Widget for enabling layer2 support in the configured device + class S390Layer2Support < CWM::CheckBox + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = !!@settings.layer2 + end + + # @see CWM::AbstractWidget + def opt + # Needed for handling the event in other widgets that contents it. + [:notify] + end + + # @see CWM::AbstractWidget + def label + _("Enable Layer2 Support") + end + + # @see CWM::AbstractWidget + def help + "

Select Enable Layer 2 Support if this card has been " \ + "configured with layer 2 support.

" + end + + # @see CWM::AbstractWidget + def store + @settings.layer2 = value + end + end + + # Widget for setting the mac address to be used in case of layer2 supported + class S390Layer2Address < CWM::InputField + # Constructor + # + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + # @see CWM::AbstractWidget + def init + self.value = @settings.lladdress + end + + # @see CWM::AbstractWidget + def label + _("Layer2 MAC Address") + end + + # @see CWM::AbstractWidget + def help + _("

Enter the Layer 2 MAC Address if this card has been " \ + "configured with layer 2 support.

") + end + + # @see CWM::AbstractWidget + def store + @settings.lladdress = value + end + end + end +end diff --git a/src/lib/y2network/widgets/server_ca_path.rb b/src/lib/y2network/widgets/server_ca_path.rb new file mode 100644 index 000000000..c3722f383 --- /dev/null +++ b/src/lib/y2network/widgets/server_ca_path.rb @@ -0,0 +1,54 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/widgets/path_widget" + +module Y2Network + module Widgets + class ServerCAPath < PathWidget + def initialize(builder) + textdomain "network" + @builder = builder + end + + # FIXME: label and help text is wrong, here it is certificate of CA that is used to sign server certificate + def label + _("&Server Certificate") + end + + def help + "

To increase security, it is recommended to configure\n" \ + "a Server Certificate. It is used\n" \ + "to validate the server's authenticity.

\n" + end + + def browse_label + _("Choose a Certificate") + end + + def init + self.value = @builder.ca_cert + end + + def store + @builder.ca_cert = value + end + end + end +end diff --git a/src/lib/y2network/widgets/slave_items.rb b/src/lib/y2network/widgets/slave_items.rb index c251ddb23..c5101d011 100644 --- a/src/lib/y2network/widgets/slave_items.rb +++ b/src/lib/y2network/widgets/slave_items.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" Yast.import "NetworkInterfaces" @@ -15,51 +34,41 @@ module SlaveItems # # @param [Array] slaves list of device names # @param [Array] enslaved_ifaces list of device names of already enslaved devices - def slave_items_from(slaves, enslaved_ifaces) + # @param [ConnectionConfig] config where slaves lives + def slave_items_from(slaves, enslaved_ifaces, config) raise ArgumentError, "slaves cannot be nil" if slaves.nil? raise ArgumentError, "enslaved interfaces cannot be nil" if enslaved_ifaces.nil? raise ArgumentError, "slaves cannot be empty" if slaves.empty? && !enslaved_ifaces.empty? textdomain "network" - item_ids = slaves.map { |s| Yast::LanItems.find_configured(s) || Yast::LanItems.FindDeviceIndex(s) } - - item_ids.each_with_object([]) do |item_id, items| - # TODO: do not touch directly LanItems here, but current it is quite hard to list all items without it - dev_name = Yast::LanItems.GetDeviceName(item_id) + log.debug "creating list of slaves from #{slaves.inspect}" - next if dev_name.nil? || dev_name.empty? + slaves.each_with_object([]) do |slave, result| + interface = config.interfaces.by_name(slave) - dev_type = Yast::LanItems.GetDeviceType(item_id) + next unless interface - if ["tun", "tap"].include?(dev_type) - description = Yast::NetworkInterfaces.GetDevTypeDescription(dev_type, true) + if interface.type.tun? || interface.type.tap? + description = Yast::NetworkInterfaces.GetDevTypeDescription(interface.type.short_name, true) else - ifcfg = Yast::LanItems.GetDeviceMap(item_id) || {} - - description = TmpInclude.new.BuildDescription( - dev_type, - dev_name, - ifcfg, - [Yast::LanItems.GetLanItem(item_id)["hwinfo"] || {}] - ) + description = interface.name # this conditions origin from bridge configuration # if enslaving a configured device then its configuration is rewritten # to "0.0.0.0/32" # # translators: a note that listed device is already configured - description += " " + _("configured") if ifcfg["IPADDR"] != "0.0.0.0" + description += " " + _("configured") if config.connections.by_name(interface.name) end - selected = false - selected = enslaved_ifaces.include?(dev_name) if enslaved_ifaces + selected = enslaved_ifaces.include?(interface.name) - description << " (Port ID: #{physical_port_id(dev_name)})" if physical_port_id?(dev_name) + description << " (Port ID: #{physical_port_id(interface.name)})" if physical_port_id?(interface.name) - items << Yast::Term.new(:item, - Yast::Term.new(:id, dev_name), - "#{dev_name} - #{description}", + result << Yast::Term.new(:item, + Yast::Term.new(:id, interface.name), + "#{interface.name} - #{description}", selected) end end @@ -84,16 +93,6 @@ def physical_port_id(dev_name) def physical_port_id?(dev_name) !physical_port_id(dev_name).empty? end - - # TODO: just cleaner way to get a bit more complex method from include for now - class TmpInclude - include Yast - include Yast::I18n - - def initialize - Yast.include self, "network/complex.rb" - end - end end end end diff --git a/src/lib/y2network/widgets/startmode.rb b/src/lib/y2network/widgets/startmode.rb index 5a94b0f67..c8b42eb3c 100644 --- a/src/lib/y2network/widgets/startmode.rb +++ b/src/lib/y2network/widgets/startmode.rb @@ -1,4 +1,24 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" +require "y2network/startmode" module Y2Network module Widgets @@ -19,12 +39,12 @@ def label end def init - self.value = @config["STARTMODE"] + self.value = @config.startmode.name handle end def store - @config["STARTMODE"] = value + @config.startmode = value end def handle @@ -78,21 +98,9 @@ def help end def items - [ - # onboot, on and boot are aliases for auto - # See NetworkInterfaces::CanonicalizeStartmode - # TRANSLATORS: Combo box option for Device Activation - ["auto", _("At Boot Time")], - ["off", _("Never")], - # TRANSLATORS: Combo box option for Device Activation - ["manual", _("Manually")], - # TRANSLATORS: Combo box option for Device Activation - ["ifplugd", _("On Cable Connection")], - # TRANSLATORS: Combo box option for Device Activation - ["hotplug", _("On Hotplug")], - # TRANSLATORS: Combo box option for Device Activation - ["nfsroot", _("On NFSroot")] - ] + Y2Network::Startmode.all.map do |mode| + [mode.to_s, mode.to_human_string] + end end end end diff --git a/src/lib/y2network/widgets/tunnel.rb b/src/lib/y2network/widgets/tunnel.rb index bc295c356..d4384fbf0 100644 --- a/src/lib/y2network/widgets/tunnel.rb +++ b/src/lib/y2network/widgets/tunnel.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/custom_widget" @@ -26,14 +45,17 @@ def help def init log.info "init tunnel with #{@settings.inspect}" + owner, group = @settings.tunnel_user_group - Yast::UI.ChangeWidget(:tunnel_owner, :Value, @settings["TUNNEL_SET_OWNER"] || "") - Yast::UI.ChangeWidget(:tunnel_group, :Value, @settings["TUNNEL_SET_GROUP"] || "") + Yast::UI.ChangeWidget(:tunnel_owner, :Value, owner || "") + Yast::UI.ChangeWidget(:tunnel_group, :Value, group || "") end def store - @settings["TUNNEL_SET_OWNER"] = Yast::UI.QueryWidget(:tunnel_owner, :Value) - @settings["TUNNEL_SET_GROUP"] = Yast::UI.QueryWidget(:tunnel_group, :Value) + @settings.assign_tunnel_user_group( + Yast::UI.QueryWidget(:tunnel_owner, :Value), + Yast::UI.QueryWidget(:tunnel_group, :Value) + ) end end end diff --git a/src/lib/y2network/widgets/udev_rules.rb b/src/lib/y2network/widgets/udev_rules.rb index c8930a6aa..766cdf906 100644 --- a/src/lib/y2network/widgets/udev_rules.rb +++ b/src/lib/y2network/widgets/udev_rules.rb @@ -1,6 +1,25 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "cwm/custom_widget" -require "network/edit_nic_name" +require "y2network/dialogs/rename_interface" Yast.import "UI" @@ -17,26 +36,27 @@ def contents _("Udev Rules"), HBox( InputField(Id(:udev_rules_name), Opt(:hstretch, :disabled), _("Device Name"), ""), - PushButton(Id(:udev_rules_change), _("Change")) + VBox( + VSpacing(), + PushButton(Id(:udev_rules_change), _("Change")) + ) ) ) end def init - # TODO: get it from settings - self.value = @settings.udev_name + self.value = @settings.name end def handle - self.value = Yast::EditNicName.new.run + dialog = Y2Network::Dialogs::RenameInterface.new(@settings) + ret = dialog.run + return unless ret == :ok + self.value = @settings.name nil end - def store - # TODO: nothing to do as done in EditNicName which looks wrong - end - def value=(name) Yast::UI.ChangeWidget(Id(:udev_rules_name), :Value, name) end diff --git a/src/lib/y2network/widgets/vlan_id.rb b/src/lib/y2network/widgets/vlan_id.rb index e714d3405..90fbd568a 100644 --- a/src/lib/y2network/widgets/vlan_id.rb +++ b/src/lib/y2network/widgets/vlan_id.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" module Y2Network @@ -19,11 +38,11 @@ def help end def init - self.value = (@config["VLAN_ID"] || "0").to_i + self.value = @config.vlan_id end def store - @config["VLAN_ID"] = value.to_s + @config.vlan_id = value end def minimum diff --git a/src/lib/y2network/widgets/vlan_interface.rb b/src/lib/y2network/widgets/vlan_interface.rb index ddca8b3e0..b6131c2f0 100644 --- a/src/lib/y2network/widgets/vlan_interface.rb +++ b/src/lib/y2network/widgets/vlan_interface.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "cwm/common_widgets" module Y2Network diff --git a/src/lib/y2network/widgets/wireless.rb b/src/lib/y2network/widgets/wireless.rb new file mode 100644 index 000000000..4d9c9b122 --- /dev/null +++ b/src/lib/y2network/widgets/wireless.rb @@ -0,0 +1,112 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "y2network/widgets/wireless_essid" +require "y2network/widgets/wireless_mode" +require "y2network/dialogs/wireless_expert_settings" + +module Y2Network + module Widgets + # Top level widget for frame with general wireless settings + class Wireless < CWM::CustomWidget + attr_reader :settings + + # @param settings [Y2Network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + def contents + VBox( + VSpacing(0.5), + # Frame label + Frame( + _("Wireless Device Settings"), + HBox( + HSpacing(2), + VBox( + VSpacing(0.5), + # ComboBox label + mode_widget, + VSpacing(0.2), + # Text entry label + essid_widget, + VSpacing(0.2), + expert_settings_widget, + VSpacing(0.5) + ), + HSpacing(2) + ) + ), + VSpacing(0.5) + ) + end + + private + + def refresh + wep_keys_widget.disable + encryption_widget.enable + case auth_mode_widget.value + when "wpa-eap" + mode_widget.value = "Managed" + encryption_widget.disable + when "wpa-psk" + mode_widget.value = "Managed" + when "wep" + encryption_widget.disable + wep_keys_widget.enable + when "no-encryption" + encryption_widget.disable + end + end + + def mode_widget + @mode_widget ||= Y2Network::Widgets::WirelessMode.new(settings) + end + + def essid_widget + @essid_widget ||= Y2Network::Widgets::WirelessEssid.new(settings) + end + + def expert_settings_widget + @expert_settings_widget ||= Y2Network::Widgets::WirelessExpertSettings.new(settings) + end + end + + class WirelessExpertSettings < CWM::PushButton + def initialize(settings) + @settings = settings + + textdomain "network" + end + + def label + _("E&xpert Settings") + end + + def handle + Y2Network::Dialogs::WirelessExpertSettings.new(@settings).run + + nil + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_auth.rb b/src/lib/y2network/widgets/wireless_auth.rb new file mode 100644 index 000000000..23558695f --- /dev/null +++ b/src/lib/y2network/widgets/wireless_auth.rb @@ -0,0 +1,124 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/custom_widget" +require "cwm/replace_point" + +require "y2network/dialogs/wireless_wep_keys" +require "y2network/widgets/wireless_auth_mode" +require "y2network/widgets/wireless_eap" +require "y2network/widgets/wireless_password" + +module Y2Network + module Widgets + # Top level widget for wireless authentication. It changes content dynamically depending + # on selected authentication method. + class WirelessAuth < CWM::CustomWidget + attr_reader :settings + + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + self.handle_all_events = true + textdomain "network" + end + + def init + auth_mode_widget.init # force init of auth to ensure that refresh has correct value + replace_widget.init + refresh + end + + def handle(event) + return if event["ID"] != auth_mode_widget.widget_id + + refresh + nil + end + + def contents + Frame( + _("Wireless Authentication"), + VBox( + VSpacing(0.5), + auth_mode_widget, + VSpacing(0.2), + replace_widget, + VSpacing(0.5) + ) + ) + end + + private + + def refresh + case auth_mode_widget.value + when "no-encryption" then replace_widget.replace(empty_auth_widget) + when "sharedkey", "open" then replace_widget.replace(wep_keys_widget) + when "wpa-psk" then replace_widget.replace(encryption_widget) + when "wpa-eap" then replace_widget.replace(eap_widget) + else + raise "invalid value #{auth_mode_widget.value.inspect}" + end + end + + def replace_widget + @replace_widget ||= CWM::ReplacePoint.new(id: "wireless_replace_point", widget: empty_auth_widget) + end + + def empty_auth_widget + @empty_auth ||= CWM::Empty.new("wireless_empty") + end + + def auth_mode_widget + @auth_mode_widget ||= WirelessAuthMode.new(settings) + end + + def encryption_widget + @encryption_widget ||= WirelessPassword.new(settings) + end + + def wep_keys_widget + @wep_keys_widget ||= WirelessWepKeys.new(settings) + end + + def eap_widget + @eap_widget ||= WirelessEap.new(settings) + end + + # Button for showing WEP Keys dialog + class WirelessWepKeys < CWM::PushButton + def initialize(settings) + @settings = settings + textdomain "network" + end + + def label + _("&WEP Keys") + end + + def handle + Y2Network::Dialogs::WirelessWepKeys.run(@settings) + + nil + end + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_auth_mode.rb b/src/lib/y2network/widgets/wireless_auth_mode.rb new file mode 100644 index 000000000..e9463ebee --- /dev/null +++ b/src/lib/y2network/widgets/wireless_auth_mode.rb @@ -0,0 +1,65 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" + +module Y2Network + module Widgets + class WirelessAuthMode < CWM::ComboBox + def initialize(settings) + textdomain "network" + + @settings = settings + end + + def init + self.value = @settings.auth_mode + end + + def label + _("&Authentication Mode") + end + + def opt + [:hstretch, :notify] + end + + def items + [ + ["no-encryption", _("No Encryption")], + ["open", _("WEP - Open")], + ["sharedkey", _("WEP - Shared Key")], + ["wpa-psk", _("WPA-PSK (\"home\")")], + ["wpa-eap", _("WPA-EAP (\"Enterprise\")")] + ] + end + + def help + # TODO: improve help text, mention all options and security problems with WEP + "

WPA-EAP uses a RADIUS server to authenticate users. There\n" \ + "are different methods in EAP to connect to the server and\n" \ + "perform the authentication, namely TLS, TTLS, and PEAP.

\n" + end + + def store + @settings.auth_mode = value + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_eap.rb b/src/lib/y2network/widgets/wireless_eap.rb new file mode 100644 index 000000000..646c937d6 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_eap.rb @@ -0,0 +1,215 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/custom_widget" +require "cwm/replace_point" + +require "y2network/widgets/wireless_eap_mode" +require "y2network/widgets/server_ca_path" +require "y2network/widgets/client_cert_path" +require "y2network/widgets/client_key_path" + +module Y2Network + module Widgets + # High Level widget that allow to select kind of EAP authentication and also dynamically change + # its content according to the selection + class WirelessEap < CWM::CustomWidget + attr_reader :settings + + def initialize(settings) + @settings = settings + self.handle_all_events = true + end + + def init + refresh + end + + def handle(event) + return if event["ID"] != eap_mode.widget_id + + refresh + nil + end + + def contents + VBox( + eap_mode, + VSpacing(0.2), + replace_widget + ) + end + + private + + def eap_mode + @eap_mode ||= WirelessEapMode.new(settings) + end + + def replace_widget + @replace_widget ||= CWM::ReplacePoint.new(id: "wireless_eap_point", widget: CWM::Empty.new("wireless_eap_empty")) + end + + def refresh + case eap_mode.value + when "TTLS" then replace_widget.replace(ttls_widget) + when "PEAP" then replace_widget.replace(peap_widget) + when "TLS" then replace_widget.replace(tls_widget) + else raise "unknown value #{eap_mode.value.inspect}" + end + end + + def ttls_widget + @ttls_widget ||= EapTtls.new(@settings) + end + + def peap_widget + @peap_widget ||= EapPeap.new(@settings) + end + + def tls_widget + @tls_widget ||= EapTls.new(@settings) + end + end + + # High level widget that represent PEAP authentication + class EapPeap < CWM::CustomWidget + attr_reader :settings + + def initialize(settings) + @settings = settings + end + + def contents + VBox( + HBox(EapUser.new(@settings), EapPassword.new(@settings)), + ServerCAPath.new(@settings) + ) + end + end + + # High level widget that represent TTLS authentication + class EapTtls < CWM::CustomWidget + attr_reader :settings + + def initialize(settings) + @settings = settings + end + + def contents + VBox( + HBox(EapUser.new(@settings), EapPassword.new(@settings)), + EapAnonymousUser.new(@settings), + ServerCAPath.new(@settings) + ) + end + end + + # High level widget that represent TLS authentication + class EapTls < CWM::CustomWidget + attr_reader :settings + + def initialize(settings) + @settings = settings + end + + def contents + VBox( + HBox( + ClientCertPath.new(@settings), + ClientKeyPath.new(@settings) + ), + ServerCAPath.new(@settings) + ) + end + end + + # Widget that represent EAP password + class EapPassword < CWM::Password + def initialize(builder) + @builder = builder + textdomain "network" + end + + def label + _("Password") + end + + def init + self.value = @builder.wpa_password + end + + def store + @builder.wpa_password = value + end + + def help + "" # TODO: write it + end + end + + # Widget that represent EAP user + class EapUser < CWM::InputField + def initialize(builder) + @builder = builder + textdomain "network" + end + + def label + _("Identity") + end + + def init + self.value = @builder.wpa_identity + end + + def store + @builder.wpa_identity = value + end + + def help + "" # TODO: write it + end + end + + # Widget that represent EAP anonymous user that is used for initial connection + class EapAnonymousUser < CWM::InputField + def initialize(builder) + @builder = builder + textdomain "network" + end + + def label + _("&Anonymous Identity") + end + + def init + self.value = @builder.wpa_anonymous_identity + end + + def store + @builder.wpa_anonymous_identity = value + end + + def help + "" # TODO: write it + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_eap_mode.rb b/src/lib/y2network/widgets/wireless_eap_mode.rb new file mode 100644 index 000000000..d262df1d8 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_eap_mode.rb @@ -0,0 +1,67 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" + +module Y2Network + module Widgets + # Widget to select EAP mode. + class WirelessEapMode < CWM::ComboBox + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + textdomain "network" + @settings = settings + end + + def init + self.value = @settings.eap_mode + end + + def store + @settings.eap_mode = value + end + + def label + _("EAP &Mode") + end + + # generate event when changed so higher level widget can change content + # @see Y2Network::Widgets::WirelessEap + def opt + [:notify] + end + + def items + [ + ["PEAP", _("PEAP")], + ["TLS", _("TLS")], + ["TTLS", _("TTLS")] + ] + end + + def help + _( + "

WPA-EAP uses a RADIUS server to authenticate users. There\n" \ + "are different methods in EAP to connect to the server and\n" \ + "perform the authentication, namely TLS, TTLS, and PEAP.

\n" + ) + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_essid.rb b/src/lib/y2network/widgets/wireless_essid.rb new file mode 100644 index 000000000..a20353042 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_essid.rb @@ -0,0 +1,156 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" +require "cwm/custom_widget" +require "yast2/feedback" + +Yast.import "String" + +module Y2Network + module Widgets + # Widget to setup wifi network essid + class WirelessEssid < CWM::CustomWidget + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + textdomain "network" + end + + def contents + HBox( + essid, + VBox( + VSpacing(1), + scan + ) + ) + end + + private + + def essid + @essid ||= WirelessEssidName.new(@settings) + end + + def scan + @scan ||= WirelessScan.new(@settings, update: essid) + end + end + + # Widget for network name combobox + class WirelessEssidName < CWM::ComboBox + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + textdomain "network" + end + + def label + _("Ne&twork Name (ESSID)") + end + + def init + Yast::UI.ChangeWidget(Id(widget_id), :ValidChars, valid_chars) + self.value = @settings.essid.to_s + end + + # allow to use not found name e.g. when scan failed or when network is hidden + def opt + [:editable] + end + + # updates essid list with given array and ensure that previously selected value is preserved + # @param networks [Array] + def update_essid_list(networks) + old_value = value + change_items(networks.map { |n| [n, n] }) + self.value = old_value + end + + def store + @settings.essid = value + end + + private + + def valid_chars + Yast::String.CPrint + end + end + + # Button for scan network sites + class WirelessScan < CWM::PushButton + # @param settings [Y2network::InterfaceConfigBuilder] + # @param update [WirelessEssidName] + def initialize(settings, update:) + @settings = settings + @update_widget = update + textdomain "network" + end + + def label + _("Scan Network") + end + + def handle + networks = essid_list + + Yast2::Feedback.show("Obtaining essid list", headline: "Scanning network") do |_f| + networks = essid_list + log.info("Found networks: #{networks}") + end + + return unless @update_widget + @update_widget.update_essid_list(networks) + nil + end + + private + + # TODO: own class and do not call directly in widget. + def essid_list + command = "#{link_up} && #{scan} | #{grep_and_cut_essid} | #{sort}" + + output = Yast::SCR.Execute(Yast::Path.new(".target.bash_output"), command) + output["stdout"].split("\n") + end + + def sort + "/usr/bin/sort -u" + end + + def grep_and_cut_essid + "/usr/bin/grep ESSID | /usr/bin/cut -d':' -f2 | /usr/bin/cut -d'\"' -f2" + end + + def link_up + "/usr/sbin/ip link set #{interface} up" + end + + def scan + "/usr/sbin/iwlist #{interface} scan" + end + + def interface + @settings.name + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_expert.rb b/src/lib/y2network/widgets/wireless_expert.rb new file mode 100644 index 000000000..09a1e26a1 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_expert.rb @@ -0,0 +1,157 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" + +module Y2Network + module Widgets + # Channel selector widget + class WirelessChannel < CWM::ComboBox + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + + textdomain "network" + end + + def init + self.value = @settings.channel.to_s + end + + def store + @settings.channel = value.empty? ? nil : value.to_i + end + + def label + _("&Channel") + end + + def opt + [:hstretch] + end + + def items + # FIXME: different protocol has different number of channels, we need to reflect it somehow + # 1..14 is number of channels available in legal range for wireless + 1.upto(14).map { |c| [c.to_s, c.to_s] }.prepend(["", _("Automatic")]) + end + end + + # bit rate selection widget + class WirelessBitRate < CWM::ComboBox + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + + textdomain "network" + end + + def opt + [:hstretch, :editable] + end + + def label + _("B&it Rate") + end + + def init + self.value = @settings.bitrate.to_s + end + + def store + @settings.bitrate = value.empty? ? nil : value.to_f + end + + def items + bitrates.map { |b| [b.to_s, b.to_s] }.prepend(["", _("Automatic")]) + end + + # TODO: help text with units (Mb/s) + + private + + def bitrates + [54, 48, 36, 24, 18, 12, 11, 9, 6, 5.5, 2, 1] + end + end + + # Widget to select access point if site consist of multiple ones + class WirelessAccessPoint < CWM::InputField + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + textdomain "network" + end + + def opt + [:hstretch] + end + + def label + _("&Access Point") + end + + def init + self.value = @settings.access_point + end + + def store + @settings.access_point = value + end + + # TODO: help text with explanation what exactly is needed + # TODO: validation of MAC address + end + + # widget to set Scan mode + class WirelessAPScanMode < CWM::IntField + # @param settings [Y2network::InterfaceConfigBuilder] + def initialize(settings) + @settings = settings + textdomain "network" + end + + def opt + [:hstretch] + end + + def label + _("AP ScanMode") + end + + def minimum + 0 + end + + def maximum + 2 + end + + def init + self.value = @settings.ap_scanmode + end + + def store + @settings.ap_scanmode = value + end + + # TODO: help text + end + end +end diff --git a/src/lib/y2network/widgets/wireless_mode.rb b/src/lib/y2network/widgets/wireless_mode.rb new file mode 100644 index 000000000..a0d78a524 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_mode.rb @@ -0,0 +1,58 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" + +module Y2Network + module Widgets + # Widget to select mode in which wifi card operate + class WirelessMode < CWM::ComboBox + # @param config [Y2network::InterfaceConfigBuilder] + def initialize(config) + @config = config + textdomain "network" + end + + def label + _("O&perating Mode") + end + + def init + self.value = @config.mode + end + + # notify when mode change as it affect other elements + def opt + [:notify, :hstretch] + end + + def store + @config.mode = value + end + + def items + [ + ["ad-hoc", _("Ad-hoc")], + ["Managed", _("Managed")], + ["master", _("Master")] + ] + end + end + end +end diff --git a/src/lib/y2network/widgets/wireless_password.rb b/src/lib/y2network/widgets/wireless_password.rb new file mode 100644 index 000000000..ee93e1396 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_password.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "cwm/common_widgets" + +module Y2Network + module Widgets + # Widget for WPA "home" password. It is not used for EAP password. + class WirelessPassword < CWM::Password + # @param builder [Y2network::InterfaceConfigBuilder] + def initialize(builder) + textdomain "network" + @builder = builder + end + + def label + _("Password") + end + + def init + self.value = @builder.wpa_psk + end + + def store + @builder.wpa_psk = value + end + + # TODO: write help text + + # TODO: write validation. From man page: You can enter it in hex digits (needs to be exactly + # 64 digits long) or as passphrase getting hashed (8 to 63 ASCII characters long). + end + end +end diff --git a/src/lib/y2network/widgets/wireless_tab.rb b/src/lib/y2network/widgets/wireless_tab.rb new file mode 100644 index 000000000..0b75bdee1 --- /dev/null +++ b/src/lib/y2network/widgets/wireless_tab.rb @@ -0,0 +1,55 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "cwm/tabs" + +# used widgets +require "y2network/widgets/wireless" +require "y2network/widgets/wireless_auth" + +module Y2Network + module Widgets + # Tab for wireless specific stuff. Useful only for wireless cards + class WirelessTab < CWM::Tab + # @param builder [Y2network::InterfaceConfigBuilder] + def initialize(builder) + textdomain "network" + + @builder = builder + end + + def label + _("&Wireless Specific") + end + + def contents + VBox( + VSpacing(0.5), + Y2Network::Widgets::Wireless.new(@builder), + VSpacing(0.5), + Y2Network::Widgets::WirelessAuth.new(@builder), + VSpacing(0.5), + # TODO: wireless auth widget + VStretch() + ) + end + end + end +end diff --git a/src/lib/y2remote/modes/socket_based.rb b/src/lib/y2remote/modes/socket_based.rb index 1a3d0f36a..ad4df20ec 100644 --- a/src/lib/y2remote/modes/socket_based.rb +++ b/src/lib/y2remote/modes/socket_based.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yast" require "yast2/systemd/socket" diff --git a/src/lib/y2remote/modes/vnc.rb b/src/lib/y2remote/modes/vnc.rb index 209a937af..cd8cd30ec 100644 --- a/src/lib/y2remote/modes/vnc.rb +++ b/src/lib/y2remote/modes/vnc.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "y2remote/modes/base" require "y2remote/modes/socket_based" diff --git a/src/lib/y2remote/modes/web.rb b/src/lib/y2remote/modes/web.rb index 1af164cf4..5c3138ab8 100644 --- a/src/lib/y2remote/modes/web.rb +++ b/src/lib/y2remote/modes/web.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "y2remote/modes/base" require "y2remote/modes/socket_based" diff --git a/src/modules/DNS.rb b/src/modules/DNS.rb index f86ee193a..4f1772bbb 100644 --- a/src/modules/DNS.rb +++ b/src/modules/DNS.rb @@ -218,13 +218,6 @@ def Write(_gui: true) true end - # Proposes hostname if none was given - # - # @see Y2Network::DNS#ensure_hostname! - def propose_hostname - yast_dns_config.ensure_hostname! - end - # Check if hostname or IP address is local computer # Used to determine if LDAP server is local (and it should be checked if # required schemes are included @@ -298,9 +291,9 @@ def modified # @return [Hash] a map containing ip, hostname_short, and hostname_fq keys def dhcp_data { - "ip" => Yast::Execute.stdout.on_target!("/bin/hostname -i").strip, - "hostname_short" => Yast::Execute.stdout.on_target!("/bin/hostname").strip, - "hostname_fq" => Yast::Execute.stdout.on_target!("/bin/hostname -f").strip + "ip" => Yast::Execute.stdout.on_target!("/usr/bin/hostname -i").strip, + "hostname_short" => Yast::Execute.stdout.on_target!("/usr/bin/hostname").strip, + "hostname_fq" => Yast::Execute.stdout.on_target!("/usr/bin/hostname -f").strip } end diff --git a/src/modules/Lan.rb b/src/modules/Lan.rb index 4ec677d41..ea892ed27 100644 --- a/src/modules/Lan.rb +++ b/src/modules/Lan.rb @@ -35,8 +35,10 @@ require "y2firewall/firewalld" require "y2network/autoinst_profile/networking_section" require "y2network/config" +require "y2network/interface_config_builder" require "y2network/presenters/routing_summary" require "y2network/presenters/dns_summary" +require "y2network/presenters/interfaces_summary" require "shellwords" @@ -69,7 +71,6 @@ def main Yast.include self, "network/complex.rb" Yast.include self, "network/runtime.rb" - Yast.include self, "network/lan/bridge.rb" #------------- # GLOBAL DATA @@ -99,6 +100,8 @@ def main @backend = nil + @modified = false + # Y2Network::Config objects @configs = {} end @@ -110,7 +113,7 @@ def main # Return a modification status # @return true if data was modified def Modified - return true if LanItems.GetModified + return true if @modified return true unless system_config == yast_config return true if NetworkConfig.Modified return true if NetworkService.Modified @@ -119,6 +122,11 @@ def Modified false end + def SetModified + @modified = true + nil + end + # function for use from autoinstallation (Fate #301032) def isAnyInterfaceDown down = false @@ -264,10 +272,6 @@ def Read(cache) return true end - system_config = Y2Network::Config.from(:sysconfig) - add_config(:system, system_config) - add_config(:yast, system_config.copy) - # Read dialog caption caption = _("Initializing Network Configuration") @@ -333,7 +337,7 @@ def Read(cache) return false if Abort() ProgressNextStage(_("Reading device configuration...")) if @gui - LanItems.Read + read_config Builtins.sleep(sl) @@ -399,7 +403,7 @@ def SetIPv6(status) if @ipv6 != status @ipv6 = status Popup.Warning(_("To apply this change, a reboot is needed.")) - LanItems.SetModified + Lan.SetModified end nil @@ -511,7 +515,6 @@ def Write(gui: true) return false if Abort() # Progress step 3 - multiple devices may be present, really plural ProgressNextStage(_("Writing device configuration...")) - LanItems.write Builtins.sleep(sl) return false if Abort() @@ -525,7 +528,8 @@ def Write(gui: true) ProgressNextStage(_("Writing routing configuration...")) orig = Progress.set(false) - yast_config.write(original: system_config) + target = :sysconfig if Mode.auto + yast_config.write(original: system_config, target: target) Progress.set(orig) Builtins.sleep(sl) @@ -627,36 +631,9 @@ 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 >" - ) - ) do |k, v| - # replace "alias0" to "0" (bnc#372687) - t = Convert.convert( - value, - from: "any", - to: "map " - ) - 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["interfaces"] ||= [] + # TODO: remove when s390 no longer need it + interfaces = Builtins.listmap(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 = {} @@ -731,7 +708,7 @@ def FromAY(input) end Builtins.y2milestone("input=%1", input) - deep_copy(input) + input end # Import data. @@ -744,8 +721,9 @@ def FromAY(input) def Import(settings) settings = {} if settings.nil? + Lan.Read(:cache) profile = Y2Network::AutoinstProfile::NetworkingSection.new_from_hashes(settings) - config = Y2Network::Config.from(:autoinst, profile) + config = Y2Network::Config.from(:autoinst, profile, system_config) add_config(:yast, config) LanItems.Import(settings) @@ -767,18 +745,11 @@ def Import(settings) # @return dumped settings def Export profile = Y2Network::AutoinstProfile::NetworkingSection.new_from_network(yast_config) - devices = NetworkInterfaces.Export("") - udev_rules = LanItems.export(devices) ay = { "dns" => profile.dns ? profile.dns.to_hashes : {}, - "s390-devices" => Ops.get_map( - udev_rules, - "s390-devices", - {} - ), - "net-udev" => Ops.get_map(udev_rules, "net-udev", {}), + "net-udev" => profile.udev_rules ? profile.udev_rules.udev_rules.map(&:to_hashes) : [], "config" => NetworkConfig.Export, - "devices" => devices, + "interfaces" => profile.interfaces ? profile.interfaces.interfaces.map(&:to_hashes) : [], "ipv6" => @ipv6, "routing" => profile.routing ? profile.routing.to_hashes : {}, "managed" => NetworkService.is_network_manager, @@ -804,29 +775,14 @@ def Export def Summary(mode) case mode when "summary" - "#{LanItems.BuildLanOverview.first}#{dns_summary}#{routing_summary}" + "#{interfaces_summary}#{dns_summary}#{routing_summary}" when "proposal" "#{LanItems.summary(:proposal)}#{dns_summary}#{routing_summary}" else - LanItems.BuildLanOverview.first + interfaces_summary end end - # Add a new device - # @return true if success - def Add - return false if LanItems.Select("") != true - NetworkInterfaces.Add - true - end - - # Delete current device (see LanItems::current) - # @return true if success - def Delete - LanItems.DeleteItem - true - end - # Uses product info and is subject to installed packages. # @return Should NM be enabled? def UseNetworkManager @@ -857,60 +813,28 @@ def UseNetworkManager nm_default && nm_installed end - def IfcfgsToSkipVirtualizedProposal - skipped = [] - yast_config = Y2Network::Config.find(:yast) - - LanItems.Items.each do |_current, config| - ifcfg = config["ifcfg"] - ifcfg_type = NetworkInterfaces.GetType(ifcfg) - - case ifcfg_type - when "br" - skipped << ifcfg - - yast_config.interfaces.bridge_slaves(ifcfg).each { |port| skipped << port } - when "bond" - yast_config.interfaces.bond_slaves(ifcfg).each do |slave| - log.info("For interface #{ifcfg} found slave #{slave}") - skipped << slave - end - - # Skip also usb and wlan devices as they are not good for bridge proposal (bnc#710098) - when "usb", "wlan" - log.info("#{ifcfg_type} device #{ifcfg} skipped from bridge proposal") - skipped << ifcfg - end - - next unless NetworkInterfaces.GetValue(ifcfg, "STARTMODE") == "nfsroot" - - log.info("Skipped #{ifcfg} interface from bridge slaves because of nfsroot.") - - skipped << ifcfg - end - log.info("Skipped interfaces : #{skipped}") - - skipped - end - def ProposeVirtualized # then each configuration (except bridges) move to the bridge # and add old device name into bridge_ports - LanItems.Items.each do |current, config| - bridge_name = LanItems.new_type_device("br") + yast_config.interfaces.each do |interface| + bridge_builder = Y2Network::InterfaceConfigBuilder.for("br") + bridge_builder.name = yast_config.interfaces.free_name("br") - next unless connected_and_bridgeable?(bridge_name, current, config) + next unless connected_and_bridgeable?(bridge_builder, interface) - # first configure all connected unconfigured devices with dhcp (with default parameters) - # FIXME: ProposeItem is always true - next if !LanItems.IsItemConfigured(current) && !LanItems.ProposeItem(current) + next if yast_config.connections.by_name(interface.name) - ifcfg = LanItems.GetDeviceName(current) - next unless configure_as_bridge!(ifcfg, bridge_name) - # reconfigure existing device as newly created bridge's port - configure_as_bridge_port(ifcfg) - LanItems.move_routes(ifcfg, bridge_name) - refresh_lan_items + bridge_builder.stp = false + bridge_builder.ports = [interface.name] + bridge_builder.startmode = "auto" + bridge_builder.save + + builder = Y2Network::InterfaceConfigBuilder.for(interface.type) + builder.name = interface.name + builder.configure_as_slave + builder.save + LanItems.move_routes(builder.name, bridge_builder.name) + refresh_interfaces end nil @@ -1061,70 +985,48 @@ def activate_network_service end end - def configure_as_bridge!(ifcfg, bridge_name) - return false if !NetworkInterfaces.Edit(ifcfg) - - log.info("device #{ifcfg} is gointg to be in bridge #{bridge_name}") - - NetworkInterfaces.Name = bridge_name - - # from bridge interface remove all bonding-related stuff - NetworkInterfaces.Current.each do |key, _value| - NetworkInterfaces.Current[key] = nil if key.include? "BONDING" - end - - NetworkInterfaces.Current["BRIDGE"] = "yes" - NetworkInterfaces.Current["BRIDGE_PORTS"] = ifcfg - NetworkInterfaces.Current["BRIDGE_STP"] = "off" - NetworkInterfaces.Current["BRIDGE_FORWARDDELAY"] = "0" - - # hardcode startmode (bnc#450670), it can't be ifplugd! - NetworkInterfaces.Current["STARTMODE"] = "auto" - # remove description - will be replaced by new (real) one - NetworkInterfaces.Current.delete("NAME") - # remove ETHTOOLS_OPTIONS as it is useful only for real hardware - NetworkInterfaces.Current.delete("ETHTOOLS_OPTIONS") - - NetworkInterfaces.Commit - end - # Convenience method that returns true if the current item has link and can # be enslaved in a bridge. # # @return [Boolean] true if it is bridgeable - def connected_and_bridgeable?(bridge_name, item, config) - # FIXME: a workaround until we fully use builders in proposals - bridge_builder = Y2Network::InterfaceConfigBuilder.for("br") - bridge_builder.name = bridge_name - - if !bridge_builder.bridgeable_interfaces.map(&:name).include?(LanItems.GetDeviceName(item)) - log.info "The interface #{config["ifcfg"]} cannot be proposed as bridge." + def connected_and_bridgeable?(bridge_builder, interface) + if !bridge_builder.bridgeable_interfaces.map(&:name).include?(interface.name) + log.info "The interface #{interface.name} cannot be proposed as bridge." return false end - hwinfo = config.fetch("hwinfo", {}) - unless hwinfo.fetch("link", false) - log.warn("Lan item #{item} has link:false detected") + hwinfo = interface.hardware + if !hwinfo.present? || !hwinfo.link + log.warn("Lan item #{interface.inspect} has link:false detected") return false end - if hwinfo.fetch("type", "") == "wlan" - log.warn("Not proposing WLAN interface for lan item: #{item}") + + if interface.type.wireless? + log.warn("Not proposing WLAN interface for lan item: #{interface.inspect}") return false end true end - def refresh_lan_items - LanItems.force_restart = true - log.info("List #{NetworkInterfaces.List("")}") - # re-read configuration to see new items in UI - LanItems.Read - - # note: LanItems.Read resets modification flag - # the Read is used as a trick how to update LanItems' internal - # cache according NetworkInterfaces' one. As NetworkInterfaces' - # cache was edited directly, LanItems is not aware of changes. - LanItems.SetModified + # Refreshes YaST network interfaces + # + # It refreshes system configuration and update the list of interfaces + # for the current YaST configuration. The rest of the configuration + # is not modified. + # + # TODO: consider adding an API to Y2Network::Config to do partial refreshes. + def refresh_interfaces + system_config = Y2Network::Config.from(:sysconfig) + yast_config.interfaces = system_config.interfaces.copy + end + + # Reads system configuration + # + # It clears already read configuration. + def read_config + system_config = Y2Network::Config.from(:sysconfig) + Yast::Lan.add_config(:system, system_config) + Yast::Lan.add_config(:yast, system_config.copy) end # Returns the routing summary @@ -1147,6 +1049,16 @@ def dns_summary presenter.text end + # Returns the interfaces configuration summary + # + # @return [String] + def interfaces_summary + config = find_config(:yast) + return "" unless config + presenter = Y2Network::Presenters::InterfacesSummary.new(config) + presenter.text + end + def firewalld Y2Firewall::Firewalld.instance end diff --git a/src/modules/LanItems.rb b/src/modules/LanItems.rb index 25a65bebd..4abb3acfd 100644 --- a/src/modules/LanItems.rb +++ b/src/modules/LanItems.rb @@ -28,46 +28,25 @@ require "network/wicked" require "network/lan_items_summary" require "y2network/config" +require "y2network/boot_protocol" require "shellwords" module Yast - # Does way too many things. - # - # 1. Aggregates data about network interfaces, both configured - # and unconfigured, in {#Items}, which see. - # - # 2. Provides direct access to individual items of ifcfg files. - # For example BOOTPROTO and STARTMODE are accessible in - # {#bootproto} and {#startmode} (set via {#SetDeviceVars} - # via {#Select} or {#SetItem}). The reverse direction (putting - # the individual values back to an item) is {#Commit}. - # - # 3. ... - # - # FIXME: well this class really is not nice class LanItemsClass < Module - attr_accessor :firewall_zone - include Logger include Wicked def main - Yast.import "UI" textdomain "network" Yast.import "NetworkInterfaces" - Yast.import "ProductFeatures" Yast.import "NetworkConfig" - Yast.import "Host" Yast.import "Directory" - Yast.import "Stage" Yast.include self, "network/complex.rb" Yast.include self, "network/routines.rb" Yast.include self, "network/lan/s390.rb" - Yast.include self, "network/lan/udev.rb" - Yast.include self, "network/lan/bridge.rb" reset_cache @@ -100,72 +79,7 @@ def main @Requires = [] - # address options - # boot protocol: BOOTPROTO - @bootproto = "static" - @ipaddr = "" - @netmask = "" - @prefix = "" - - @startmode = "auto" - - # wireless options - @wl_mode = "" - @wl_essid = "" - @wl_nwid = "" - @wl_auth_mode = "" - # when adding another key, don't forget the chmod 600 in NetworkInterfaces - @wl_wpa_psk = "" - @wl_key_length = "" - @wl_key = [] - @wl_default_key = 0 - @wl_nick = "" - @firewall_zone = nil - - # FIXME: We should unify bridge_ports and bond_slaves variables - - # interfaces attached to bridge (list delimited by ' ') - @bridge_ports = "" - - # bond options - @bond_slaves = [] - @bond_option = "" - - # VLAN option - @vlan_etherdevice = "" - - # wl_wpa_eap aggregates the settings in a map for easier CWM access. - # - # **Structure:** - # - # wpa_eap - # WPA_EAP_MODE: string ("TTLS" "PEAP" or "TLS") - # WPA_EAP_IDENTITY: string - # WPA_EAP_PASSWORD: string (for TTLS and PEAP) - # WPA_EAP_ANONID: string (for TTLS and PEAP) - # WPA_EAP_CLIENT_CERT: string (for TLS, file name) - # WPA_EAP_CLIENT_KEY: string (for TLS, file name) - # WPA_EAP_CLIENT_KEY_PASSWORD: string (for TLS) - # WPA_EAP_CA_CERT: string (file name) - # WPA_EAP_AUTH: string ("", "MD5", "GTC", "CHAP"*, "PAP"*, "MSCHAP"*, "MSCHAPV2") (*: TTLS only) - # WPA_EAP_PEAP_VERSION: string ("", "0", "1") - @wl_wpa_eap = {} - @wl_channel = "" - @wl_frequency = "" - @wl_bitrate = "" - @wl_accesspoint = "" - @wl_power = true - @wl_ap_scanmode = "" - - # Card Features from hwinfo - # if not provided, we use the default full list - @wl_auth_modes = nil - @wl_enc_modes = nil - @wl_channels = nil - @wl_bitrates = nil - # s390 options - @qeth_portname = "" @qeth_portnumber = "" # * ctc as PROTOCOL (or ctc mode, number in { 0, 1, .., 4 }, default: 0) @chan_mode = "0" @@ -192,13 +106,6 @@ def main Yast.include self, "network/hardware.rb" - # Default values used when creating an emulated NIC for physical s390 hardware. - @s390_defaults = YAML.load_file(Directory.find_data_file("network/s390_defaults.yml")) if Arch.s390 - - # the defaults here are what sysconfig defaults to - # (as opposed to what a new interface gets, in {#Select)} - @SysconfigDefaults = YAML.load_file(Directory.find_data_file("network/sysconfig_defaults.yml")) - # this is the map of kernel modules vs. requested firmware # non-empty keys are firmware packages shipped by SUSE @request_firmware = YAML.load_file(Directory.find_data_file("network/firmwares.yml")) @@ -249,22 +156,6 @@ def GetDeviceName(item_id) "" # this should never happen end - # Convenience method to obtain the current Item udev rule - # - # @return [Array] Item udev rule - def current_udev_rule - LanItems.GetItemUdevRule(LanItems.current) - end - - # Returns name which is going to be used in the udev rule - def current_udev_name - if LanItems.current_renamed? - LanItems.current_renamed_to - else - LanItems.GetItemUdev("NAME") - end - end - # transforms given list of item ids onto device names # # item id is index into internal @Items structure @@ -344,421 +235,6 @@ def SetItemSysconfigOpt(item_id, opt, value) SetDeviceMap(item_id, devmap) end - # Returns udev rule known for particular item - # - # @param itemId [Integer] a key for {#Items} - def GetItemUdevRule(itemId) - Ops.get_list(GetLanItem(itemId), ["udev", "net"], []) - end - - # Sets udev rule for given item - # - # @param itemId [Integer] a key for {#Items} - # @param rule [String] an udev rule - def SetItemUdevRule(itemId, rule) - GetLanItem(itemId)["udev"]["net"] = rule - end - - # Returns item's mac - # - # hwinfo[permanent_mac] is prefered if exists. hwinfo[mac] is used otherwise - # - # @param item_id [Integer] a key for {#Items} - # @return [, nil] mac address or nil - def item_mac(item_id) - hwinfo = GetLanItem(item_id).fetch("hwinfo", {}) - hwinfo.fetch("permanent_mac", hwinfo.fetch("mac", nil)) - end - - # Inits item's udev rule to a default one if none is present - # - # @param item_id [Integer] a key for {#Items} - # @return [String] item's udev rule - def InitItemUdevRule(item_id) - udev = GetItemUdevRule(item_id) - return udev if !udev.empty? - - default_mac = item_mac(item_id) - raise ArgumentError, "Cannot propose udev rule - NIC not present" if !default_mac - - default_udev = GetDefaultUdevRule( - GetDeviceName(item_id), - default_mac - ) - SetItemUdevRule(item_id, default_udev) - - default_udev - end - - def ReadUdevDriverRules - Builtins.y2milestone("Reading udev rules ...") - @udev_net_rules = Convert.convert( - SCR.Read(path(".udev_persistent.net")), - from: "any", - to: "map " - ) - - Builtins.y2milestone("Reading driver options ...") - Builtins.foreach(SCR.Dir(path(".modules.options"))) do |driver| - pth = Builtins.sformat(".modules.options.%1", driver) - Builtins.foreach( - Convert.convert( - SCR.Read(Builtins.topath(pth)), - from: "any", - to: "map " - ) - ) do |key, value| - Ops.set( - @driver_options, - driver, - Builtins.sformat( - "%1%2%3=%4", - Ops.get_string(@driver_options, driver, ""), - if Ops.greater_than( - Builtins.size(Ops.get_string(@driver_options, driver, "")), - 0 - ) - " " - else - "" - end, - key, - value - ) - ) - end - end - - true - end - - def getUdevFallback - udev_rules = Ops.get_list(getCurrentItem, ["udev", "net"], []) - - if IsEmpty(udev_rules) - udev_rules = GetDefaultUdevRule( - GetCurrentName(), - item_mac(@current) || "" - ) - Builtins.y2milestone( - "No Udev rules found, creating default: %1", - udev_rules - ) - end - - deep_copy(udev_rules) - end - - # It returns a value for the particular key of udev rule belonging to the current item. - def GetItemUdev(key) - udev_key_value(getUdevFallback, key) - end - - # It deletes the given key from the udev rule of the current item. - # - # @param key [string] udev key which identifies the tuple to be removed - # @return [Object, nil] the current item's udev rule without the given key; nil if - # there is not udev rules for the current item - def RemoveItemUdev(key) - return nil if current_udev_rule.empty? - - log.info("Removing #{key} from #{current_udev_rule}") - Items()[@current]["udev"]["net"] = - LanItems.RemoveKeyFromUdevRule(current_udev_rule, key) - end - - # Updates the udev rule of the current Lan Item based on the key given - # which currently could be mac or bus_id. - # - # In case of bus_id the dev_port will be always added to avoid cases where - # the interfaces shared the same bus_id (i.e. Multiport cards using the - # same function to all the ports) (bsc#1007172) - # - # @param based_on [Symbol] principal key to be matched, `:mac` or `:bus_id` - # @return [void] - def update_item_udev_rule!(based_on = :mac) - new_rule = current_udev_rule.empty? - LanItems.InitItemUdevRule(LanItems.current) if new_rule - - case based_on - when :mac - return if new_rule - - LanItems.RemoveItemUdev("ATTR{dev_port}") - - # FIXME: While the user is able to modify the udev rule using the - # mac address instead of bus_id when bonding, could be that the - # mac in use was not the permanent one. We could read it with - # ethtool -P dev_name} - LanItems.ReplaceItemUdev( - "KERNELS", - "ATTR{address}", - item_mac(@current) || "" - ) - when :bus_id - # Update or insert the dev_port if the sysfs dev_port attribute is present - LanItems.ReplaceItemUdev( - "ATTR{dev_port}", - "ATTR{dev_port}", - LanItems.dev_port(LanItems.GetCurrentName) - ) if LanItems.dev_port?(LanItems.GetCurrentName) - - # If the current rule is mac based, overwrite to bus id. Don't touch otherwise. - LanItems.ReplaceItemUdev( - "ATTR{address}", - "KERNELS", - LanItems.getCurrentItem.fetch("hwinfo", {}).fetch("busid", "") - ) - else - raise ArgumentError, "The key given for udev rule #{based_on} is not supported" - end - end - - # It replaces a tuple identified by replace_key in current item's udev rule - # - # Note that the tuple is identified by key only. However modification flag is - # set only if value was changed (in case when replace_key == new_key) - # - # It also contain a logic on tuple operators. When the new_key is "NAME" - # then assignment operator (=) is used. Otherwise equality operator (==) is used. - # Thats bcs this function is currently used for touching "NAME", "KERNELS" and - # `ATTR{address}` keys only - # - # @param replace_key [string] udev key which identifies tuple to be replaced - # @param new_key [string] new key to by used - # @param new_val [string] value for new key - # @return updated rule when replace_key is found, current rule otherwise - def ReplaceItemUdev(replace_key, new_key, new_val) - # = for assignment - # == for equality checks - operator = new_key == "NAME" ? "=" : "==" - rule = RemoveKeyFromUdevRule(getUdevFallback, replace_key) - - # NAME="devname" has to be last in the rule. - # otherwise SCR agent .udev_persistent.net returns crap - # isn't that fun - name_tuple = rule.pop - new_rule = AddToUdevRule(rule, "#{new_key}#{operator}\"#{new_val}\"") - new_rule.push(name_tuple) - - if current_udev_rule.sort != new_rule.sort - SetModified() - - log.info("ReplaceItemUdev: new udev rule = #{new_rule}") - - Items()[@current]["udev"] = { "net" => [] } if !Items()[@current]["udev"] - Items()[@current]["udev"]["net"] = new_rule - end - - deep_copy(new_rule) - end - - # Updates device name. - # - # It updates device's udev rules and config name. - # Updating config name means that old configuration is deleted from - # the system. - # - # @param itemId [Integer] a key for {#Items} - # - # Returns new name - def SetItemName(itemId, name) - lan_items = LanItems.Items - - if name && !name.empty? - if lan_items[itemId]["udev"] - updated_rule = update_udev_rule_key(GetItemUdevRule(itemId), "NAME", name) - lan_items[itemId]["udev"]["net"] = updated_rule - end - else - # rewrite rule for empty name is meaningless - lan_items[itemId].delete("udev") - end - - if lan_items[itemId].key?("ifcfg") - NetworkInterfaces.Delete2(lan_items[itemId]["ifcfg"]) - lan_items[itemId]["ifcfg"] = name.to_s - end - - name - end - - # Updates current device name. - # - # It updates device's udev rules and config name. - # Updating config name means that old configuration is deleted from - # the system. - # - # Returns new name - def SetCurrentName(name) - SetItemName(@current, name) - end - - # Sets new device name for current item - def rename(name) - if GetCurrentName() != name - @Items[@current]["renamed_to"] = name - SetModified() - else - @Items[@current].delete("renamed_to") - end - end - - # Returns new name for current item - # - # @param item_id [Integer] a key for {#Items} - def renamed_to(item_id) - Items()[item_id]["renamed_to"] - end - - def current_renamed_to - renamed_to(@current) - end - - # Tells if current item was renamed - # - # @param item_id [Integer] a key for {#Items} - def renamed?(item_id) - return false if !renamed_to(item_id) - renamed_to(item_id) != GetDeviceName(item_id) - end - - def current_renamed? - renamed?(@current) - end - - # Writes udev rules for all items. - # - # Currently only interesting change is renaming interface. - def WriteUdevItemsRules - # loop over all items and checks if device name has changed - net_rules = [] - - Builtins.foreach( - Convert.convert( - Map.Keys(@Items), - from: "list", - to: "list " - ) - ) do |key| - item_udev_net = GetItemUdevRule(key) - next if IsEmpty(item_udev_net) - dev_name = Ops.get_string(@Items, [key, "hwinfo", "dev_name"], "") - @current = key - if dev_name != GetItemUdev("NAME") - # when changing device name you have a choice - # - change kernel "match rule", or - # - remove it completely - # removing is less error prone when tracking name changes, so it was chosen. - item_udev_net = RemoveKeyFromUdevRule(item_udev_net, "KERNEL") - # setting links down during AY is forbidden bcs it can freeze ssh installation - SetIfaceDown(dev_name) if !Mode.autoinst - - @force_restart = true - end - net_rules = Builtins.add( - net_rules, - Builtins.mergestring(item_udev_net, ", ") - ) - end - - Builtins.y2milestone("write net udev rules: %1", net_rules) - - write_update_udevd(net_rules) - - true - end - - def WriteUdevDriverRules - udev_drivers_rules = {} - - Builtins.foreach( - Convert.convert( - Map.Keys(@Items), - from: "list", - to: "list " - ) - ) do |key| - driver = Ops.get_string(@Items, [key, "udev", "driver"], "") - if IsNotEmpty(driver) - modalias = Ops.get_string(@Items, [key, "hwinfo", "modalias"], "") - driver_rule = [] - - driver_rule = AddToUdevRule( - driver_rule, - Builtins.sformat("ENV{MODALIAS}==\"%1\"", modalias) - ) - driver_rule = AddToUdevRule( - driver_rule, - Builtins.sformat("ENV{MODALIAS}=\"%1\"", driver) - ) - - Ops.set(udev_drivers_rules, driver, driver_rule) - end - end - - Builtins.y2milestone("write drivers udev rules: %1", udev_drivers_rules) - - SCR.Write(path(".udev_persistent.drivers"), udev_drivers_rules) - - # write rules from driver - Builtins.foreach( - Convert.convert( - @driver_options, - from: "map ", - to: "map " - ) - ) do |key, value| - val = {} - Builtins.foreach(Builtins.splitstring(value, " ")) do |k| - l = Builtins.splitstring(k, "=") - Ops.set(val, Ops.get(l, 0, ""), Ops.get(l, 1, "")) - end - val = nil if IsEmpty(value) - SCR.Write(Builtins.add(path(".modules.options"), key), val) - end - - SCR.Write(path(".modules"), nil) - - nil - end - - def WriteUdevRules - WriteUdevItemsRules() - WriteUdevDriverRules() - - # wait so that ifcfgs written in NetworkInterfaces are newer - # (1-second-wise) than netcontrol status files, - # and rcnetwork reload actually works (bnc#749365) - SCR.Execute(path(".target.bash"), "/usr/bin/udevadm settle") - sleep(1) - - nil - end - - def write - renamed_items = @Items.keys.select { |item_id| renamed?(item_id) } - renamed_items.each do |item_id| - devmap = GetDeviceMap(item_id) - # change configuration name if device is configured - NetworkInterfaces.Change2(renamed_to(item_id), devmap, false) if devmap - SetItemName(item_id, renamed_to(item_id)) - end - - LanItems.WriteUdevRules if !Stage.cont && InstallInfConvertor.instance.AllowUdevModify - - # FIXME: hack: no "netcard" filter as biosdevname names it diferently (bnc#712232) - NetworkInterfaces.Write("") - end - - # Exports configuration for use in AY profile - # - # TODO: it currently exports only udevs (as a consequence of dropping LanUdevAuto) - # so once it is extended, all references has to be checked - def export(devices) - export_udevs(devices) - end - # Function which returns if the settings were modified # @return [Boolean] settings were modified def GetModified @@ -773,30 +249,6 @@ def SetModified nil end - def AddNew - @current = @Items.to_h.size - # Items[@current] is expected to always exist - @Items[@current] = {} - @operation = :add - - nil - end - - # return list of available modules for current device - # with default default_module (on first possition) - - def GetItemModules(default_module) - mods = [] - mods = Builtins.add(mods, default_module) if IsNotEmpty(default_module) - Builtins.foreach( - Ops.get_list(@Items, [@current, "hwinfo", "drivers"], []) - ) do |row| - tmp_mod = Ops.get_string(row, ["modules", 0, 0], "") - mods = Builtins.add(mods, tmp_mod) if !Builtins.contains(mods, tmp_mod) - end - deep_copy(mods) - end - # Creates list of all known netcard items # # It means list of item ids of all netcards which are detected and/or @@ -900,29 +352,6 @@ def FindAndSelect(device) !item_id.nil? end - # search all known devices to find it's index in Items array - # - # @param [String] device matched with item[ "hwinfo", "dev_name"] - # @return index in Items or -1 if not found - def FindDeviceIndex(device) - ret = -1 - - Builtins.foreach( - Convert.convert( - @Items, - from: "map ", - to: "map >" - ) - ) do |i, a| - if Ops.get_string(a, ["hwinfo", "dev_name"], "") == device - ret = i - raise Break - end - end - - ret - end - # It finds a new style device name for device name in old fashioned format # # It goes through currently present devices and tries to mach it to given @@ -959,87 +388,6 @@ def ReadHw @Items = {} @Hardware = ReadHardware("netcard") # Hardware = [$["active":true, "bus":"pci", "busid":"0000:02:00.0", "dev_name":"wlan0", "drivers":[$["active":true, "modprobe":true, "modules":[["ath5k" , ""]]]], "link":true, "mac":"00:22:43:37:55:c3", "modalias":"pci:v0000168Cd0000001Csv00001A3Bsd00001026bc02s c00i00", "module":"ath5k", "name":"AR242x 802.11abg Wireless PCI Express Adapter", "num":0, "options":"", "re quires":[], "sysfs_id":"/devices/pci0000:00/0000:00:1c.1/0000:02:00.0", "type":"wlan", "udi":"/org/freedeskto p/Hal/devices/pci_168c_1c", "wl_auth_modes":["open", "sharedkey", "wpa-psk", "wpa-eap"], "wl_bitrates":nil, " wl_channels":["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], "wl_enc_modes":["WEP40", "WEP104", "T KIP", "CCMP"]], $["active":true, "bus":"pci", "busid":"0000:01:00.0", "dev_name":"eth0", "drivers":[$["active ":true, "modprobe":true, "modules":[["atl1e", ""]]]], "link":false, "mac":"00:23:54:3f:7c:c3", "modalias":"pc i:v00001969d00001026sv00001043sd00008324bc02sc00i00", "module":"atl1e", "name":"L1 Gigabit Ethernet Adapter", "num":1, "options":"", "requires":[], "sysfs_id":"/devices/pci0000:00/0000:00:1c.3/0000:01:00.0", "type":"et h", "udi":"/org/freedesktop/Hal/devices/pci_1969_1026", "wl_auth_modes":nil, "wl_bitrates":nil, "wl_channels" :nil, "wl_enc_modes":nil]]; - ReadUdevDriverRules() - - udev_drivers_rules = Convert.convert( - SCR.Read(path(".udev_persistent.drivers")), - from: "any", - to: "map " - ) - Builtins.foreach(@Hardware) do |hwitem| - udev_net = if Ops.get_string(hwitem, "dev_name", "") != "" - Ops.get_list( - @udev_net_rules, - Ops.get_string(hwitem, "dev_name", ""), - [] - ) - else - [] - end - mod = Builtins.deletechars( - Ops.get( - Builtins.splitstring( - Ops.get( - Ops.get_list( - udev_drivers_rules, - Ops.get_string(hwitem, "modalias", ""), - [] - ), - 1, - "" - ), - "=" - ), - 1, - "" - ), - "\"" - ) - Ops.set( - @Items, - Builtins.size(@Items), - "hwinfo" => hwitem, - "udev" => { "net" => udev_net, "driver" => mod } - ) - end - - nil - end - - # initializates @Items - # - # It does: - # (1) read hardware present on the system - # (2) read known configurations (e.g. ifcfg-eth0) - # (3) joins together. Join is done via device name (e.g. eth0) as key. - # It is full outer join in -> you can have hwinfo part with no coresponding - # netconfig part (or vice versa) in @Items when the method is done. - def Read - reset_cache - - ReadHw() - NetworkInterfaces.Read - NetworkInterfaces.adapt_old_config! - NetworkInterfaces.CleanHotplugSymlink - - interfaces = getNetworkInterfaces - items = LanItems.Items - # match configurations to Items list with hwinfo - interfaces.each do |confname| - items.each do |key, value| - match = value.fetch("hwinfo", {}).fetch("dev_name", "") == confname - items[key]["ifcfg"] = confname if match - end - end - - interfaces.each do |confname| - next if items.values.any? { |item| item && item["ifcfg"] == confname } - - items[items.size] = { "ifcfg" => confname } - end - - log.info "Read Configuration LanItems::Items #{@Items}" - nil end @@ -1067,12 +415,6 @@ def reset_cache def Import(settings) reset_cache - items = LanItems.Items - NetworkInterfaces.Import("netcard", settings["devices"] || {}) - NetworkInterfaces.List("netcard").each do |device| - items[items.size] = { "ifcfg" => device } - end - @autoinstall_settings["start_immediately"] = settings.fetch("start_immediately", false) @autoinstall_settings["strict_IP_check_timeout"] = settings.fetch("strict_IP_check_timeout", -1) @autoinstall_settings["keep_install_network"] = settings.fetch("keep_install_network", true) @@ -1090,39 +432,6 @@ def Import(settings) true end - def GetDescr - descr = [] - Builtins.foreach( - Convert.convert( - @Items, - from: "map ", - to: "map >" - ) - ) do |key, value| - if Builtins.haskey(value, "table_descr") && - Ops.greater_than( - Builtins.size(Ops.get_map(@Items, [key, "table_descr"], {})), - 1 - ) - descr = Builtins.add( - descr, - "id" => key, - "rich_descr" => Ops.get_string( - @Items, - [key, "table_descr", "rich_descr"], - "" - ), - "table_descr" => Ops.get_list( - @Items, - [key, "table_descr", "table_descr"], - [] - ) - ) - end - end - deep_copy(descr) - end - def needFirmwareCurrentItem need = false if IsNotEmpty(Ops.get_string(@Items, [@current, "hwinfo", "driver"], "")) @@ -1234,9 +543,9 @@ def startmode_overview(item_id) # It supports differents types of summaries depending on the options[:type] # # @see LanItemsSummary - # @param type [Hash] summary options + # @param type [String,Symbol] summary options, supported "one-line" and "proposal" # @return [String] summary of the configured items - def summary(type = "default") + def summary(type) LanItemsSummary.new.send(type) end @@ -1277,126 +586,6 @@ def ip_overview(dev_map) bullets end - # FIXME: side effect: sets @type. No reason for that. It should only build item - # overview. Check and remove. - def BuildLanOverview - overview = [] - links = [] - - LanItems.Items.each_key do |key| - rich = "" - - item_hwinfo = LanItems.Items[key]["hwinfo"] || {} - descr = item_hwinfo["name"] || "" - - note = "" - bullets = [] - ifcfg_name = LanItems.Items[key]["ifcfg"] || "" - ifcfg_type = NetworkInterfaces.GetType(ifcfg_name) - - if !ifcfg_name.empty? - ifcfg_conf = GetDeviceMap(key) - log.error("BuildLanOverview: devmap for #{key}/#{ifcfg_name} is nil") if ifcfg_conf.nil? - - ifcfg_desc = ifcfg_conf["NAME"] - descr = ifcfg_desc if !ifcfg_desc.nil? && !ifcfg_desc.empty? - descr = CheckEmptyName(ifcfg_type, descr) - status = DeviceStatus(ifcfg_type, ifcfg_name, ifcfg_conf) - - bullets << _("Device Name: %s") % ifcfg_name - bullets += startmode_overview(key) - bullets += ip_overview(ifcfg_conf) if ifcfg_conf["STARTMODE"] != "managed" - - if ifcfg_type == "wlan" && - ifcfg_conf["WIRELESS_AUTH_MODE"] == "open" && - IsEmpty(ifcfg_conf["WIRELESS_KEY_0"]) - - # avoid colons - ifcfg_name = ifcfg_name.tr(":", "/") - href = "lan--wifi-encryption-" + ifcfg_name - # interface summary: WiFi without encryption - warning = HTML.Colorize(_("Warning: no encryption is used."), "red") - # Hyperlink: Change the configuration of an interface - status << " " << warning << " " << Hyperlink(href, _("Change.")) - links << href - end - - if ifcfg_type == "bond" || ifcfg_type == "br" - bullets << slaves_desc(ifcfg_type, ifcfg_name) - end - - if enslaved?(ifcfg_name) - if yast_config.interfaces.bond_index[ifcfg_name] - master = yast_config.interfaces.bond_index[ifcfg_name] - master_desc = _("Bonding master") - else - master = yast_config.interfaces.bridge_index[ifcfg_name] - master_desc = _("Bridge") - end - note = format(_("enslaved in %s"), master) - bullets << format("%s: %s", master_desc, master) - end - - if renamed?(key) - note = format("%s -> %s", GetDeviceName(key), renamed_to(key)) - end - - overview << Summary.Device(descr, status) - else - descr = CheckEmptyName(ifcfg_type, descr) - overview << Summary.Device(descr, Summary.NotConfigured) - end - conn = "" - conn = HTML.Bold(format("(%s)", _("Not connected"))) if !item_hwinfo["link"] - conn = HTML.Bold(format("(%s)", _("No hwinfo"))) if item_hwinfo.empty? - - mac_dev = HTML.Bold("MAC : ") + item_mac(key).to_s + "
" - bus_id = HTML.Bold("BusID : ") + item_hwinfo["busid"].to_s + "
" - physical_port_id = HTML.Bold("PhysicalPortID : ") + physical_port_id(ifcfg_name) + "
" - - rich << " " << conn << "
" << mac_dev if IsNotEmpty(item_mac(key)) - rich << bus_id if IsNotEmpty(item_hwinfo["busid"]) - rich << physical_port_id if physical_port_id?(ifcfg_name) - # display it only if we need it, don't duplicate "ifcfg_name" above - if IsNotEmpty(item_hwinfo["dev_name"]) && ifcfg_name.empty? - dev_name = _("Device Name: %s") % item_hwinfo["dev_name"] - rich << HTML.Bold(dev_name) << "
" - end - rich = HTML.Bold(descr) + rich - if IsEmpty(item_hwinfo["dev_name"]) && !item_hwinfo.empty? && !Arch.s390 - rich << "

" - rich << _("Unable to configure the network card because the kernel device (eth0, wlan0) is not present. This is mostly caused by missing firmware (for wlan devices). See dmesg output for details.") - rich << "

" - elsif !ifcfg_name.empty? - rich << HTML.List(bullets) - else - rich << "

" - rich << _("The device is not configured. Press Edit\nto configure.\n") - rich << "

" - - curr = @current - @current = key - if needFirmwareCurrentItem - fw = GetFirmwareForCurrentItem() - rich << format("%s : %s", _("Needed firmware"), !fw.empty? ? fw : _("unknown")) - end - @current = curr - end - LanItems.Items[key]["table_descr"] = { - "rich_descr" => rich, - "table_descr" => [descr, DeviceProtocol(ifcfg_conf), ifcfg_name, note] - } - end - [Summary.DevicesList(overview), links] - end - - # Create an overview table with all configured devices - # @return table items - def Overview - BuildLanOverview() - GetDescr() - end - # Is current device hotplug or not? I.e. is connected via usb/pcmci? def isCurrentHotplug hotplugtype = Ops.get_string(getCurrentItem, ["hwinfo", "hotplug"], "") @@ -1407,313 +596,23 @@ def isCurrentHotplug # from DHCP (v4, v6 or both) # @return true if it is def isCurrentDHCP - Builtins.regexpmatch(@bootproto, "dhcp[46]?") + return false unless @bootproto + + Y2Network::BootProtocol.from_name(@bootproto).dhcp? end # Checks whether given device configuration is set to use a dhcp bootproto # # ideally should replace @see isCurrentDHCP def dhcp?(devmap) - ["dhcp4", "dhcp6", "dhcp", "dhcp+autoip"].include?(devmap["BOOTPROTO"]) - end + return false unless devmap["BOOTPROTO"] - def GetItemDescription - Ops.get_string(@Items, [@current, "table_descr", "rich_descr"], "") - end - - # Select the hardware component - # @param hardware the component - def SelectHWMap(hardware) - hardware = deep_copy(hardware) - sel = SelectHardwareMap(hardware) - - # common stuff - @description = Ops.get_string(sel, "name", "") - @type = Ops.get_string(sel, "type", "eth") - @hotplug = Ops.get_string(sel, "hotplug", "") - - @Requires = Ops.get_list(sel, "requires", []) - # #44977: Requires now contain the appropriate kernel packages - # but they are handled differently due to multiple kernel flavors - # (see Package::InstallKernel) - # Leave only those not starting with "kernel". - @Requires = Builtins.filter(@Requires) do |r| - Builtins.search(r, "kernel") != 0 - end - Builtins.y2milestone("requires=%1", @Requires) - - # FIXME: devname - @hotplug = "" - - # Wireless Card Features - @wl_auth_modes = Builtins.prepend( - hardware["wl_auth_modes"], - "no-encryption" - ) - @wl_enc_modes = hardware["wl_enc_modes"] - @wl_channels = hardware["wl_channels"] - @wl_bitrates = hardware["wl_bitrates"] - - Builtins.y2milestone("hw=%1", hardware) - - @hw = deep_copy(hardware) - if Arch.s390 && @operation == :add - Builtins.y2internal("Propose chan_ids values for %1", @hw) - devid = 0 - devstr = "" - s390chanid = "[0-9]+\\.[0-9]+\\." - if Builtins.regexpmatch(Ops.get_string(@hw, "busid", ""), s390chanid) - devid = Builtins.tointeger( - Ops.add( - "0x", - Builtins.regexpsub( - Ops.get_string(@hw, "busid", ""), - Ops.add(s390chanid, "(.*)"), - "\\1" - ) - ) - ) - devstr = Builtins.regexpsub( - Ops.get_string(@hw, "busid", ""), - Ops.add(Ops.add("(", s390chanid), ").*"), - "\\1" - ) - end - - Builtins.y2milestone("devid=%1(%2)", devid, devstr) - devid = 0 if devid.nil? - devid0 = String.PadZeros( - Builtins.regexpsub(Builtins.tohexstring(devid), "0x(.*)", "\\1"), - 4 - ) - devid1 = String.PadZeros( - Builtins.regexpsub( - Builtins.tohexstring(Ops.add(devid, 1)), - "0x(.*)", - "\\1" - ), - 4 - ) - devid2 = String.PadZeros( - Builtins.regexpsub( - Builtins.tohexstring(Ops.add(devid, 2)), - "0x(.*)", - "\\1" - ), - 4 - ) - @qeth_chanids = if DriverType(@type) == "ctc" || DriverType(@type) == "lcs" - Builtins.sformat("%1%2 %1%3", devstr, devid0, devid1) - else - Builtins.sformat( - "%1%2 %1%3 %1%4", - devstr, - devid0, - devid1, - devid2 - ) - end - end - - nil + Y2Network::BootProtocol.from_name(devmap["BOOTPROTO"]).dhcp? end #------------------- # PRIVATE FUNCTIONS - # Distributes an ifcfg hash to individual attributes. - # @param devmap [Hash] an ifcfg, values are strings - # @param defaults [Hash] should provide defaults for devmap - # @return [Hash] devmap with used values - def SetDeviceVars(devmap, defaults) - d = defaults.merge(devmap) - # address options - @bootproto = d["BOOTPROTO"] - @ipaddr = d["IPADDR"] - @prefix = d["PREFIXLEN"] - @netmask = d["NETMASK"] - @firewall_zone = d["ZONE"] - @set_default_route = case d["DHCLIENT_SET_DEFAULT_ROUTE"] - when "yes" then true - when "no" then false - # all other values! count as unspecified which is default value - end - - @startmode = d["STARTMODE"] - @description = d["NAME"] - @bond_option = d["BONDING_MODULE_OPTS"] - @vlan_etherdevice = d["ETHERDEVICE"] - - @bridge_ports = d["BRIDGE_PORTS"] - - @bond_slaves = [] - Builtins.foreach(devmap) do |key, value| - if Builtins.regexpmatch(Convert.to_string(key), "BONDING_SLAVE[0-9]+") - if !Convert.to_string(value).nil? - @bond_slaves = Builtins.add(@bond_slaves, Convert.to_string(value)) - end - end - end - d["BOND_SLAVES"] = @bond_slaves - - # tun/tap settings - @tunnel_set_owner = d["TUNNEL_SET_OWNER"] - @tunnel_set_group = d["TUNNEL_SET_GROUP"] - - # wireless options - @wl_mode = d["WIRELESS_MODE"] - @wl_essid = d["WIRELESS_ESSID"] - @wl_nwid = d["WIRELESS_NWID"] - @wl_auth_mode = d["WIRELESS_AUTH_MODE"] - @wl_wpa_psk = d["WIRELESS_WPA_PSK"] - @wl_key_length = d["WIRELESS_KEY_LENGTH"] - @wl_key = [ - d["WIRELESS_KEY_0"], - d["WIRELESS_KEY_1"], - d["WIRELESS_KEY_2"], - d["WIRELESS_KEY_3"] - ] - @wl_key[0] = d["WIRELESS_KEY"] if (@wl_key[0] || "").empty? - - @wl_default_key = d["WIRELESS_DEFAULT_KEY"].to_i - @wl_nick = d["WIRELESS_NICK"] - @wl_wpa_eap = { - "WPA_EAP_MODE" => d["WIRELESS_EAP_MODE"], - "WPA_EAP_IDENTITY" => d["WIRELESS_WPA_IDENTITY"], - "WPA_EAP_PASSWORD" => d["WIRELESS_WPA_PASSWORD"], - "WPA_EAP_ANONID" => d["WIRELESS_WPA_ANONID"], - "WPA_EAP_CLIENT_CERT" => d["WIRELESS_CLIENT_CERT"], - "WPA_EAP_CLIENT_KEY" => d["WIRELESS_CLIENT_KEY"], - "WPA_EAP_CLIENT_KEY_PASSWORD" => d["WIRELESS_CLIENT_KEY_PASSWORD"], - "WPA_EAP_CA_CERT" => d["WIRELESS_CA_CERT"], - "WPA_EAP_AUTH" => d["WIRELESS_EAP_AUTH"], - "WPA_EAP_PEAP_VERSION" => d["WIRELESS_PEAP_VERSION"] - } - @wl_channel = d["WIRELESS_CHANNEL"] - @wl_frequency = d["WIRELESS_FREQUENCY"] - @wl_bitrate = d["WIRELESS_BITRATE"] - @wl_accesspoint = d["WIRELESS_AP"] - @wl_power = d["WIRELESS_POWER"] == "yes" - @wl_ap_scanmode = d["WIRELESS_AP_SCANMODE"] - - @aliases = Ops.get_map(devmap, "_aliases", {}) - - d - end - - # Initializes s390 specific device variables. - # - # @param [Hash] devmap map with s390 specific attributes and its values - # @param [Hash] defaults map with default values for attributes not found in devmap - # @return [Hash] devmap with used values - def SetS390Vars(devmap, defaults) - return if !Arch.s390 - d = defaults.merge(devmap) - - @qeth_portname = d["QETH_PORTNAME"] - @qeth_portnumber = d["QETH_PORTNUMBER"] - @qeth_layer2 = d["QETH_LAYER2"] == "yes" - @qeth_chanids = d["QETH_CHANIDS"] - - # s/390 options - # We always have to set the MAC Address for qeth Layer2 support. - # It is used mainly as a hint for user that MAC is expected in case - # of Layer2 devices. Other devices do not need it. Simply - # because such devices do not emulate Layer2 - @qeth_macaddress = d["LLADDR"] if @qeth_layer2 - - # qeth attribute. FIXME: currently not read from system. - @ipa_takeover = defaults["IPA_TAKEOVER"] == "yes" - - # not device attribute - @qeth_options = defaults["QETH_OPTIONS"] || "" - - # handle non qeth devices - @iucv_user = defaults["IUCV_USER"] || "" - @chan_mode = defaults["CHAN_MODE"] || "" - - d - end - - def InitS390VarsByDefaults - SetS390Vars({}, @s390_defaults) - end - - # Select the given device - # FIXME: currently *dev* is always "" - # @param [String] dev device to select ("" for new device, default values) - # @return true if success - def Select(dev) - Builtins.y2debug("dev=%1", dev) - - # FIXME: should be removed, it is genereated by builder - devmap = {} - - # FIXME: encapsulate into LanItems.GetItemType ? - @type = Ops.get_string(@Items, [@current, "hwinfo", "type"], "eth") - @device = new_type_device(@type) - - # TODO: instead of udev use hwinfo dev_name - NetworkInterfaces.Name = GetItemUdev("NAME") - if Ops.less_than(Builtins.size(@Items), @current) - Ops.set(@Items, @current, "ifcfg" => NetworkInterfaces.Name) - else - Ops.set(@Items, [@current, "ifcfg"], NetworkInterfaces.Name) - end - - # general stuff - @description = BuildDescription(@type, @device, devmap, @Hardware) - - SetDeviceVars(devmap, @SysconfigDefaults) - InitS390VarsByDefaults() - - @hotplug = "" - Builtins.y2debug("type=%1", @type) - if Builtins.issubstring(@type, "-") - @type = Builtins.regexpsub(@type, "([^-]+)-.*$", "\\1") - end - Builtins.y2debug("type=%1", @type) - - true - end - - TRISTATE_TO_S = { nil => nil, false => "no", true => "yes" }.freeze - - # Sets device map items related to dhclient - def setup_dhclient_options(devmap) - if dhcp?(devmap) - val = TRISTATE_TO_S.fetch(@set_default_route) - devmap["DHCLIENT_SET_DEFAULT_ROUTE"] = val if val - end - devmap - end - - # Sets bonding specific sysconfig options in given device map - # - # If any bonding specific option is present already it gets overwritten - # by new ones in case of collision. If any BONDING_SLAVEx from devmap - # is not set, then its value is set to 'nil' - # - # @param devmap [Hash] hash of a device's sysconfig variables - # @param slaves [array] list of strings, each string is a bond slave name - # - # @return [Hash] updated copy of the device map - def setup_bonding(devmap, slaves, options) - raise ArgumentError, "Device map has to be provided." if devmap.nil? - - devmap = deep_copy(devmap) - slaves ||= [] - - slave_opts = devmap.select { |k, _| k.start_with?("BONDING_SLAVE") }.keys - slave_opts.each { |s| devmap[s] = nil } - slaves.each_with_index { |s, i| devmap["BONDING_SLAVE#{i}"] = s } - - devmap["BONDING_MODULE_OPTS"] = options || "" - devmap["BONDING_MASTER"] = "yes" - - devmap - end - # Commit pending operation # # It commits *only* content of the corresponding ifcfg into NetworkInterfaces. @@ -1722,126 +621,11 @@ def setup_bonding(devmap, slaves, options) # # @return true if success def Commit(builder) - if @operation != :add && @operation != :edit - log.error("Unknown operation: #{@operation}") - raise ArgumentError, "Unknown operation: #{@operation}" - end - log.info "committing builder #{builder.inspect}" builder.save # does all modification, later only things that is not yet converted - # FIXME: most of the following stuff should be moved into InterfaceConfigBuilder - # when generating sysconfig configuration - newdev = builder.device_sysconfig - - # #50955 omit computable fields - newdev["BROADCAST"] = "" - newdev["NETWORK"] = "" - - # set LLADDR to sysconfig only for device on layer2 and only these which needs it - # do not write incorrect LLADDR. - # FIXME: s390 - broken in network-ng - if @qeth_layer2 && s390_correct_lladdr(@qeth_macaddress) - busid = Ops.get_string(@Items, [@current, "hwinfo", "busid"], "") - # sysfs id has changed from css0... - sysfs_id = "/devices/qeth/#{busid}" - log.info("busid #{busid}") - if s390_device_needs_persistent_mac(sysfs_id, @Hardware) - newdev["LLADDR"] = @qeth_macaddress - end - end - newdev = setup_dhclient_options(newdev) - - # FIXME: network-ng currently works for eth only - case builder.type - when "bond" - # we need current slaves - when some of them is not used anymore we need to - # configure it for deletion from ifcfg (SCR expects special value nil) - current_slaves = (GetCurrentMap() || {}).select { |k, _| k.start_with?("BONDING_SLAVE") } - newdev = setup_bonding(newdev.merge(current_slaves), @bond_slaves, builder.bond_options) - - when "wlan" - newdev["WIRELESS_MODE"] = @wl_mode - newdev["WIRELESS_ESSID"] = @wl_essid - newdev["WIRELESS_NWID"] = @wl_nwid - newdev["WIRELESS_AUTH_MODE"] = @wl_auth_mode - newdev["WIRELESS_WPA_PSK"] = @wl_wpa_psk - newdev["WIRELESS_KEY_LENGTH"] = @wl_key_length - # obsoleted by WIRELESS_KEY_0 - newdev["WIRELESS_KEY"] = "" # TODO: delete the varlable - newdev["WIRELESS_KEY_0"] = Ops.get(@wl_key, 0, "") - newdev["WIRELESS_KEY_1"] = Ops.get(@wl_key, 1, "") - newdev["WIRELESS_KEY_2"] = Ops.get(@wl_key, 2, "") - newdev["WIRELESS_KEY_3"] = Ops.get(@wl_key, 3, "") - Ops.set( - newdev, - "WIRELESS_DEFAULT_KEY", - Builtins.tostring(@wl_default_key) - ) - Ops.set(newdev, "WIRELESS_NICK", @wl_nick) - Ops.set(newdev, "WIRELESS_AP_SCANMODE", @wl_ap_scanmode) - - if @wl_wpa_eap != {} - Ops.set( - newdev, - "WIRELESS_EAP_MODE", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_MODE", "") - ) - Ops.set( - newdev, - "WIRELESS_WPA_IDENTITY", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_IDENTITY", "") - ) - Ops.set( - newdev, - "WIRELESS_WPA_PASSWORD", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_PASSWORD", "") - ) - Ops.set( - newdev, - "WIRELESS_WPA_ANONID", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_ANONID", "") - ) - Ops.set( - newdev, - "WIRELESS_CLIENT_CERT", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_CLIENT_CERT", "") - ) - Ops.set( - newdev, - "WIRELESS_CLIENT_KEY", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_CLIENT_KEY", "") - ) - Ops.set( - newdev, - "WIRELESS_CLIENT_KEY_PASSWORD", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_CLIENT_KEY_PASSWORD", "") - ) - Ops.set( - newdev, - "WIRELESS_CA_CERT", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_CA_CERT", "") - ) - Ops.set( - newdev, - "WIRELESS_EAP_AUTH", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_AUTH", "") - ) - Ops.set( - newdev, - "WIRELESS_PEAP_VERSION", - Ops.get_string(@wl_wpa_eap, "WPA_EAP_PEAP_VERSION", "") - ) - end - - newdev["WIRELESS_CHANNEL"] = @wl_channel - newdev["WIRELESS_FREQUENCY"] = @wl_frequency - newdev["WIRELESS_BITRATE"] = @wl_bitrate - newdev["WIRELESS_AP"] = @wl_accesspoint - newdev["WIRELESS_POWER"] = @wl_power ? "yes" : "no" - end - - if DriverType(builder.type) == "ctc" + # TODO: still needed? + if DriverType(builder.type.short_name) == "ctc" if Ops.get(NetworkConfig.Config, "WAIT_FOR_INTERFACES").nil? || Ops.less_than( Ops.get_integer(NetworkConfig.Config, "WAIT_FOR_INTERFACES", 0), @@ -1851,37 +635,6 @@ def Commit(builder) end end - # ZONE uses an empty string as the default ZONE which means that is not - # the same than not defining the attribute - current_map = (GetCurrentMap() || {}).select { |k, v| !v.nil? && (k == "ZONE" || !v.empty?) } - # FIXME: move to config builder (might depend on some proposals above) - new_map = newdev.select { |k, v| !v.nil? && (k == "ZONE" || !v.empty?) } - - log.info "current map #{current_map.inspect}" - log.info "new map #{newdev.inspect}" - - # CanonicalizeIP is called to get new device map into the same shape as - # NetworkInterfaces provides the current one. - if current_map != NetworkInterfaces.CanonicalizeIP(new_map) - keep_existing = false - ifcfg_name = builder.name - ifcfg_name.replace("") if !NetworkInterfaces.Change2(ifcfg_name, newdev, keep_existing) - - # bnc#752464 - can leak wireless passwords - # useful only for debugging. Writes huge struct mostly filled by defaults. - Builtins.y2debug("%1", NetworkInterfaces.ConcealSecrets1(newdev)) - - # configure bridge ports - bridge_ports = builder["BRIDGE_PORTS"] - if bridge_ports - log.info "Configuring bridge ports #{bridge_ports} for: #{ifcfg_name}" - bridge_ports.split.each { |bp| configure_as_bridge_port(bp) } - end - - SetModified() - end - - @operation = nil true end @@ -1903,69 +656,7 @@ def Rollback true end - # Deletes item and its configuration - # - # Item for deletion is searched using device name - def delete_dev(name) - FindAndSelect(name) - DeleteItem() - end - - # Deletes the {#current} item and its configuration - def DeleteItem - return if @current < 0 - return if @Items.nil? || @Items.empty? - - log.info("DeleteItem: #{@Items[@current]}") - - devmap = GetCurrentMap() - drop_hosts(devmap["IPADDR"]) if devmap - # We have to remove it from routing before deleting the item - remove_current_device_from_routing - - SetCurrentName("") - - current_item = @Items[@current] - - if current_item["hwinfo"].nil? || current_item["hwinfo"].empty? - # size is always > 0 here and items are numbered 0, 1, ..., size -1 - delete_index = @Items.size - 1 - - @Items[@current] = @Items[delete_index] if delete_index != @current - @Items.delete(delete_index) - - # item was deleted, so original @current is invalid - @current = -1 - end - - SetModified() - - nil - end - - def SetItem(builder:) - @operation = :edit - @device = Ops.get_string(getCurrentItem, "ifcfg", "") - - NetworkInterfaces.Edit(@device) - @type = Ops.get_string(getCurrentItem, ["hwinfo", "type"], "") - - @type = NetworkInterfaces.GetType(@device) if @type.empty? - - # general stuff - devmap = deep_copy(NetworkInterfaces.Current) - s390_devmap = s390_ReadQethConfig( - Ops.get_string(getCurrentItem, ["hwinfo", "dev_name"], "") - ) - - @description = BuildDescription(@type, @device, devmap, @Hardware) - - devmap = SetDeviceVars(devmap, @SysconfigDefaults) - s390_devmap = SetS390Vars(s390_devmap, @s390_defaults) - - builder.load_sysconfig(devmap) - builder.load_s390_config(s390_devmap) - + def SetItem(*) @hotplug = "" Builtins.y2debug("type=%1", @type) if Builtins.issubstring(@type, "-") @@ -1976,74 +667,6 @@ def SetItem(builder:) nil end - # A default configuration for device when installer needs to configure it - def ProposeItem(item_id) - Builtins.y2milestone("Propose configuration for %1", GetDeviceName(item_id)) - return false if Select("") != true - - type = Items().fetch(item_id, {}).fetch("hwinfo", {})[type] - builder = Y2Network::InterfaceConfigBuilder.for(type) - - builder["MTU"] = "1492" if Arch.s390 && Builtins.contains(["lcs", "eth"], type) - builder["IPADDR"] = "" - builder["NETMASK"] = "" - builder["BOOTPROTO"] = "dhcp" - - # see bsc#176804 - devicegraph = Y2Storage::StorageManager.instance.staging - if devicegraph.filesystem_in_network?("/") - builder["STARTMODE"] = "nfsroot" - Builtins.y2milestone("startmode nfsroot") - end - - # FIXME: seems like a hack - NetworkInterfaces.Add - @operation = :edit - Ops.set( - @Items, - [item_id, "ifcfg"], - Ops.get_string(GetLanItem(item_id), ["hwinfo", "dev_name"], "") - ) - # FIXME: is it needed? - @description = HardwareName( - [Ops.get_map(GetLanItem(item_id), "hwinfo", {})], - Ops.get_string(GetLanItem(item_id), ["hwinfo", "dev_name"], "") - ) - - Commit(builder) - Builtins.y2milestone("After configuration propose %1", GetLanItem(item_id)) - true - end - - def setDriver(driver) - Builtins.y2milestone( - "driver %1, %2", - driver, - Ops.get_string(getCurrentItem, ["hwinfo", "module"], "") - ) - if Ops.get_string(getCurrentItem, ["hwinfo", "module"], "") == driver && - IsEmpty(Ops.get_string(getCurrentItem, ["udev", "driver"], "")) - return - end - Ops.set(@Items, [@current, "udev", "driver"], driver) - - nil - end - - def enableCurrentEditButton - return true if needFirmwareCurrentItem - return true if Arch.s390 - if IsEmpty(Ops.get_string(getCurrentItem, ["hwinfo", "dev_name"], "")) && - Ops.greater_than( - Builtins.size(Ops.get_map(getCurrentItem, "hwinfo", {})), - 0 - ) - return false - else - return true - end - end - # Creates eth emulation for s390 devices # # @param dev_attrs [Hash] an s390 device description (e.g. as obtained from AY profile). @@ -2062,7 +685,6 @@ def createS390Device(dev_attrs = {}) @type = dev_attrs["type"] || "" @qeth_chanids = dev_attrs["chanids"] || "" @qeth_layer2 = dev_attrs.fetch("layer2", false) - @qeth_portname = dev_attrs["portname"] || "" @chan_mode = dev_attrs["protocol"] || "" @iucv_user = dev_attrs["router"] || "" end @@ -2079,11 +701,6 @@ def createS390Device(dev_attrs = {}) else "" end - @portname_param = if Ops.greater_than(Builtins.size(@qeth_portname), 0) - Builtins.sformat("-p %1", @qeth_portname.shellescape) - else - "" - end @options_param = if Ops.greater_than(Builtins.size(@qeth_options), 0) Builtins.sformat("-o %1", @qeth_options.shellescape) else @@ -2093,7 +710,6 @@ def createS390Device(dev_attrs = {}) "/sbin/qeth_configure %1 %2 %3 %4 %5 1", @options_param, @qeth_layer2 ? "-l" : "", - @portname_param, @portnumber_param, @qeth_chanids ) @@ -2213,55 +829,6 @@ def createUdevFromIfaceName(interfaces) udev_rules end - # Configures available devices for obtaining hostname via specified device - # - # This is related to setting system's hostname via DHCP. Apart of global - # DHCLIENT_SET_HOSTNAME which is set in /etc/sysconfig/network/dhcp and is - # used as default, one can specify the option even per interface. To avoid - # collisions / undeterministic behavior the system should be configured so, - # that just one DHCP interface can update the hostname. E.g. global option - # can be set to "no" and just only one ifcfg can have the option set to "yes". - # - # @param [String] device name where should be hostname configuration active - # @return [Boolean] false when the configuration cannot be set for a device - def conf_set_hostname(device) - return false if !find_dhcp_ifaces.include?(device) - - clear_set_hostname - - ret = SetItemSysconfigOpt(find_configured(device), "DHCLIENT_SET_HOSTNAME", "yes") - - SetModified() - - ret - end - - # Removes DHCLIENT_SET_HOSTNAME from all ifcfgs - # - # @return [Array] list of names of cleared devices - def clear_set_hostname - log.info("Clearing DHCLIENT_SET_HOSTNAME flag from device configs") - - ret = [] - - GetNetcardInterfaces().each do |item_id| - dev_map = GetDeviceMap(item_id) - next if dev_map.nil? || dev_map.empty? - next if !dev_map["DHCLIENT_SET_HOSTNAME"] - - dev_map["DHCLIENT_SET_HOSTNAME"] = nil - - SetDeviceMap(item_id, dev_map) - SetModified() - - ret << GetDeviceName(item_id) - end - - log.info("#{ret.inspect} use default DHCLIENT_SET_HOSTNAME") - - ret - end - # Returns unused name for device of given type # # When already having eth0, eth1, enp0s3 devices (eth type) and asks for new @@ -2318,61 +885,6 @@ def self.publish_variable(name, type) publish variable: name, type: type end - # Returns a formated string with the interfaces that are part of a bridge - # or of a bond interface. - # - # @param [String] ifcfg_type - # @param [String] ifcfg_name - # @return [String] formated string with the interface type and the interfaces enslaved - def slaves_desc(ifcfg_type, ifcfg_name) - if ifcfg_type == "bond" - slaves = Y2Network::Config.find(:yast).interfaces.bond_slaves(ifcfg_name) - desc = _("Bonding slaves") - else - slaves = Y2Network::Config.find(:yast).interfaces.bridge_slaves(ifcfg_name) - desc = _("Bridge Ports") - end - - format("%s: %s", desc, slaves.join(" ")) - end - - # Check if the given interface is enslaved in a bond or in a bridge - # - # @return [Boolean] true if enslaved - def enslaved?(ifcfg_name) - bond_index = Y2Network::Config.find(:yast).interfaces.bond_index - bridge_index = Y2Network::Config.find(:yast).interfaces.bridge_index - - return true if bond_index[ifcfg_name] || bridge_index[ifcfg_name] - - false - end - - # Return the current name of the {LanItems} given - # - # @param item_id [Integer] a key for {#Items} - def current_name_for(item_id) - renamed?(item_id) ? renamed_to(item_id) : GetDeviceName(item_id) - end - - # Finds a LanItem which name is in collision to the provided name - # - # @param name [String] a device name (eth0, ...) - # @return [Integer, nil] item id (see LanItems::Items) - def colliding_item(name) - item_id, _item_map = Items().find { |i, _| name == current_name_for(i) } - item_id - end - - # Return wether the routing devices list needs to be updated or not to include - # the current interface name - # - # @return [Boolean] false if the current interface name is already present - def update_routing_devices? - device_names = yast_config.interfaces.map(&:name) - !device_names.include?(current_name) - end - # Adds a new interface with the given name # # @todo This method exists just to keep some compatibility during @@ -2393,167 +905,14 @@ def move_routes(from, to) return unless config && config.routing routing = config.routing add_device_to_routing(to) - target_interface = config.interfaces.find { |i| i.name == to } + target_interface = config.interfaces.by_name(to) return unless target_interface routing.routes.select { |r| r.interface && r.interface.name == from } .each { |r| r.interface = target_interface } end - # Renames an interface - # - # @todo This method exists just to keep some compatibility during - # the migration to network-ng. - - # @param old_name [String] Old device name - def rename_current_device_in_routing(old_name) - config = yast_config - return if config.nil? - interface = config.interfaces.by_name(old_name) - return unless interface - interface.name = current_name - end - - # Removes the interface with the given name - # - # @todo This method exists just to keep some compatibility during - # the migration to network-ng. - # @todo It does not check orphan routes. - def remove_current_device_from_routing - config = yast_config - return if config.nil? - name = current_name - return if name.empty? - config.interfaces.delete_if { |i| i.name == name } - end - private - # Checks if given lladdr can be written into ifcfg - # - # @param lladdr [String] logical link address, usually MAC address in case - # of qeth device - # @return [true, false] check result - def s390_correct_lladdr(lladdr) - return false if !Arch.s390 - return false if lladdr.nil? - return false if lladdr.empty? - return false if lladdr.strip == "00:00:00:00:00:00" - - true - end - - # 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.remove_ip(ip) - end - - # Exports udev rules for AY profile - def export_udevs(devices) - devices = deep_copy(devices) - ay = { "s390-devices" => {}, "net-udev" => {} } - if Arch.s390 - devs = [] - Builtins.foreach( - Convert.convert(devices, from: "map", to: "map ") - ) do |_type, value| - devs = Convert.convert( - Builtins.union(devs, Map.Keys(Convert.to_map(value))), - from: "list", - to: "list " - ) - end - Builtins.foreach(devs) do |device| - begin - driver = File.readlink("/sys/class/net/#{device}/device/driver") - rescue SystemCallError => e - Builtins.y2error("Failed to read driver #{e.inspect}") - next - end - driver = File.basename(driver) - device_type = "" - chanids = "" - case driver - when "qeth" - device_type = driver - when "ctcm" - device_type = "ctc" - when "netiucv" - device_type = "iucv" - else - Builtins.y2error("unknown driver type :#{driver}") - end - chan_ids = SCR.Execute( - path(".target.bash_output"), - Builtins.sformat( - "for i in $(seq 0 2); do chanid=$(/usr/bin/ls -l /sys/class/net/%1/device/cdev$i); /usr/bin/echo ${chanid##*/}; done | /usr/bin/tr '\n' ' '", - device.shellescape - ) - ) - if !chan_ids["stdout"].empty? - chanids = String.CutBlanks(Ops.get_string(chan_ids, "stdout", "")) - end - - # we already know that kernel device exist, otherwise next above would apply - # FIXME: It seems that it is not always the case (bsc#1124002) - portname_file = "/sys/class/net/#{device}/device/portname" - portname = ::File.exist?(portname_file) ? ::File.read(portname_file).strip : "" - - protocol_file = "/sys/class/net/#{device}/device/protocol" - protocol = ::File.exist?(protocol_file) ? ::File.read(protocol_file).strip : "" - - layer2_ret = SCR.Execute( - path(".target.bash"), - Builtins.sformat( - "/usr/bin/grep -q 1 /sys/class/net/%1/device/layer2", - device.shellescape - ) - ) - layer2 = layer2_ret == 0 - Ops.set(ay, ["s390-devices", device], "type" => device_type) - Ops.set(ay, ["s390-devices", device, "chanids"], chanids) if !chanids.empty? - Ops.set(ay, ["s390-devices", device, "portname"], portname) if !portname.empty? - Ops.set(ay, ["s390-devices", device, "protocol"], protocol) if !protocol.empty? - Ops.set(ay, ["s390-devices", device, "layer2"], true) if layer2 - port0 = SCR.Execute( - path(".target.bash_output"), - Builtins.sformat( - "port0=$(/usr/bin/ls -l /sys/class/net/%1/device/cdev0); /usr/bin/echo ${port0##*/} | /usr/bin/tr -d '\n'", - device - ) - ) - Builtins.y2milestone("port0 %1", port0) - if !port0["stdout"].empty? - value = Ops.get_string(port0, "stdout", "") - Ops.set( - ay, - ["net-udev", device], - "rule" => "KERNELS", "name" => device, "value" => value - ) - end - end - else - configured = Items().select { |i, _| IsItemConfigured(i) } - ay["net-udev"] = configured.keys.each_with_object({}) do |id, udev| - @current = id # for GetItemUdev - - name = GetItemUdev("NAME").to_s - rule = ["ATTR{address}", "KERNELS"].find { |r| !GetItemUdev(r).to_s.empty? } - - next if !rule || name.empty? - - udev[name] = { - "rule" => rule, - "name" => name, - "value" => GetItemUdev(rule) - } - end - end - - Builtins.y2milestone("AY profile %1", ay) - deep_copy(ay) - end - # Searches available items according sysconfig option # # Expects a block. The block is provided @@ -2608,37 +967,7 @@ def yast_config publish_variable :hotplug, "string" # @attribute Requires publish_variable :Requires, "list " - publish_variable :bootproto, "string" - publish_variable :ipaddr, "string" - publish_variable :netmask, "string" publish_variable :set_default_route, "boolean" - publish_variable :prefix, "string" - publish_variable :startmode, "string" - publish_variable :wl_mode, "string" - publish_variable :wl_essid, "string" - publish_variable :wl_nwid, "string" - publish_variable :wl_auth_mode, "string" - publish_variable :wl_wpa_psk, "string" - publish_variable :wl_key_length, "string" - publish_variable :wl_key, "list " - publish_variable :wl_default_key, "integer" - publish_variable :wl_nick, "string" - publish_variable :bond_slaves, "list " - publish_variable :bond_option, "string" - publish_variable :vlan_etherdevice, "string" - publish_variable :bridge_ports, "string" - publish_variable :wl_wpa_eap, "map " - publish_variable :wl_channel, "string" - publish_variable :wl_frequency, "string" - publish_variable :wl_bitrate, "string" - publish_variable :wl_accesspoint, "string" - publish_variable :wl_power, "boolean" - publish_variable :wl_ap_scanmode, "string" - publish_variable :wl_auth_modes, "list " - publish_variable :wl_enc_modes, "list " - publish_variable :wl_channels, "list " - publish_variable :wl_bitrates, "list " - publish_variable :qeth_portname, "string" publish_variable :qeth_portnumber, "string" publish_variable :chan_mode, "string" publish_variable :qeth_options, "string" @@ -2660,36 +989,16 @@ def yast_config publish function: :GetCurrentName, type: "string ()" publish function: :GetDeviceType, type: "string (integer)" publish function: :GetDeviceMap, type: "map (integer)" - publish function: :GetItemUdevRule, type: "list (integer)" - publish function: :GetItemUdev, type: "string (string)" - publish function: :ReplaceItemUdev, type: "list (string, string, string)" - publish function: :WriteUdevRules, type: "void ()" publish function: :GetModified, type: "boolean ()" publish function: :SetModified, type: "void ()" - publish function: :AddNew, type: "void ()" - publish function: :GetItemModules, type: "list (string)" - publish function: :GetNetcardNames, type: "list ( list )" publish function: :FindAndSelect, type: "boolean (string)" - publish function: :FindDeviceIndex, type: "integer (string)" publish function: :ReadHw, type: "void ()" publish function: :Read, type: "void ()" publish function: :needFirmwareCurrentItem, type: "boolean ()" - publish function: :GetFirmwareForCurrentItem, type: "string ()" - publish function: :GetBondSlaves, type: "list (string)" - publish function: :BuildLanOverview, type: "list ()" - publish function: :Overview, type: "list ()" publish function: :isCurrentHotplug, type: "boolean ()" publish function: :isCurrentDHCP, type: "boolean ()" - publish function: :GetItemDescription, type: "string ()" - publish function: :SelectHWMap, type: "void (map)" - publish function: :SetDeviceVars, type: "void (map, map)" - publish function: :Select, type: "boolean (string)" publish function: :Commit, type: "boolean ()" publish function: :Rollback, type: "boolean ()" - publish function: :DeleteItem, type: "void ()" - publish function: :ProposeItem, type: "boolean ()" - publish function: :setDriver, type: "void (string)" - publish function: :enableCurrentEditButton, type: "boolean ()" publish function: :createS390Device, type: "boolean ()" publish function: :find_dhcp_ifaces, type: "list ()" end diff --git a/src/scrconf/cfg_dhcp.scr b/src/scrconf/cfg_dhcp.scr index 2118eb06b..69cd0c9f2 100644 --- a/src/scrconf/cfg_dhcp.scr +++ b/src/scrconf/cfg_dhcp.scr @@ -1,3 +1,23 @@ +/** Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + /** * File: cfg_dhcp.scr * Summary: Agent for reading/writing /etc/sysconfig/network/dhcp diff --git a/src/scrconf/cfg_network.scr b/src/scrconf/cfg_network.scr index 90a941ae8..11e81a73c 100644 --- a/src/scrconf/cfg_network.scr +++ b/src/scrconf/cfg_network.scr @@ -1,3 +1,23 @@ +/** Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + /** * File: cfg_network.scr * Summary: Agent for reading/writing /etc/sysconfig/network/config diff --git a/src/scrconf/cfg_udev_persistent.scr b/src/scrconf/cfg_udev_persistent.scr index 822378322..885873f69 100644 --- a/src/scrconf/cfg_udev_persistent.scr +++ b/src/scrconf/cfg_udev_persistent.scr @@ -1,3 +1,23 @@ +/** Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + /** * File: * tty.scr diff --git a/src/scrconf/etc_hosts.scr b/src/scrconf/etc_hosts.scr index b29533f65..4f0165272 100644 --- a/src/scrconf/etc_hosts.scr +++ b/src/scrconf/etc_hosts.scr @@ -1,3 +1,23 @@ +/** Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + /** * File: etc_hosts.scr * Summary: Agent for reading/writing /etc/hosts diff --git a/src/scrconf/routes.scr b/src/scrconf/routes.scr index 39837935b..2d59e806e 100644 --- a/src/scrconf/routes.scr +++ b/src/scrconf/routes.scr @@ -1,3 +1,23 @@ +/** Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + /** * File: routes.scr * Summary: Agent for /etc/sysconfig/network/routes diff --git a/src/servers_non_y2/ag_udev_persistent b/src/servers_non_y2/ag_udev_persistent index 726932118..ba5739a07 100755 --- a/src/servers_non_y2/ag_udev_persistent +++ b/src/servers_non_y2/ag_udev_persistent @@ -1,4 +1,24 @@ #!/usr/bin/perl -w + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + package ag_udev_persistent; BEGIN { push( @INC, '/usr/share/YaST2/modules/' ); } use ycp; diff --git a/t/add-del.t b/t/add-del.t index b896b4b72..b0453dcd5 100755 --- a/t/add-del.t +++ b/t/add-del.t @@ -1,4 +1,24 @@ #!/bin/sh + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + # a TAP compatible test # quick and dirty: fail early diff --git a/test/SCRStub.rb b/test/SCRStub.rb index 5705327e3..f99f5c78f 100644 --- a/test/SCRStub.rb +++ b/test/SCRStub.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require "yaml" # Helpers for stubbing several agent operations. diff --git a/test/address_test.rb b/test/address_test.rb deleted file mode 100755 index b4117e7b4..000000000 --- a/test/address_test.rb +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -Yast.import "UI" - -class DummyClassForAddressTest < Yast::Module - def initialize - Yast.include self, "network/lan/address.rb" - end -end - -describe "NetworkLanAddressInclude" do - subject { DummyClassForAddressTest.new } - - describe "#update_hostname" do - let(:ip) { "1.1.1.1" } - let(:initial_hostname) { "initial.hostname.com" } - let(:new_hostname) { "new.hostname.com" } - - before(:each) do - allow(Yast::LanItems) - .to receive(:ipaddr) - .and_return(ip) - allow(subject) - .to receive(:initial_hostname) - .and_return(initial_hostname) - allow(Yast::Host) - .to receive(:names) - .and_call_original - allow(Yast::Host) - .to receive(:names) - .with(ip) - .and_return(["#{initial_hostname} custom-name"]) - end - - context "when ip has not changed" do - it "drops old /etc/hosts record if hostname was changed" do - expect(Yast::Host) - .to receive(:remove_ip) - .with(ip) - expect(Yast::Host) - .to receive(:Update) - .with(initial_hostname, new_hostname, ip) - - subject.send(:update_hostname, ip, new_hostname) - end - end - - context "when ip has changed" do - it "keeps names if there is no change in hostname" do - new_ip = "2.2.2.2" - - original_names = Yast::Host.names(ip) - subject.send(:update_hostname, new_ip, initial_hostname) - - expect(Yast::Host.names(new_ip)).to eql original_names - end - - it "does not crash when no hostnames exist for old ip and new hostname is not set" do - new_ip = "2.2.2.2" - - # targeted especially against newly created devices ;-) - allow(Yast::LanItems) - .to receive(:ipaddr) - .and_return("") - - expect { subject.send(:update_hostname, new_ip, "") }.not_to raise_error - end - end - end -end diff --git a/test/bond_test.rb b/test/bond_test.rb deleted file mode 100755 index b7672fbf5..000000000 --- a/test/bond_test.rb +++ /dev/null @@ -1,153 +0,0 @@ -#! /usr/bin/env rspec - -require_relative "test_helper" - -require "yast" - -Yast.import "LanItems" - -describe Yast::LanItems do - let(:netconfig_items) do - { - "eth" => { - "eth1" => { "BOOTPROTO" => "none" }, - "eth2" => { "BOOTPROTO" => "none" }, - "eth4" => { "BOOTPROTO" => "none" }, - "eth5" => { "BOOTPROTO" => "none" }, - "eth6" => { "BOOTPROTO" => "dhcp" } - }, - "bond" => { - "bond0" => { - "BOOTPROTO" => "static", - "BONDING_MASTER" => "yes", - "BONDING_SLAVE0" => "eth1", - "BONDING_SLAVE1" => "eth2" - }, - "bond1" => { - "BOOTPROTO" => "static", - "BONDING_MASTER" => "yes" - } - } - } - end - let(:hwinfo_items) do - [ - { "dev_name" => "eth11" }, - { "dev_name" => "eth12" } - ] - end - - before(:each) do - allow(Yast::NetworkInterfaces).to receive(:Read).and_return(true) - allow(Yast::NetworkInterfaces).to receive(:FilterDevices).with("netcard") { netconfig_items } - allow(Yast::NetworkInterfaces).to receive(:adapt_old_config!) - allow(Yast::NetworkInterfaces).to receive(:CleanHotplugSymlink).and_return(true) - - allow(Yast::LanItems).to receive(:ReadHardware) { hwinfo_items } - - allow(Yast::NetworkInterfaces).to receive(:devmap).and_return(nil) - - netconfig_items.each_pair do |_type, device_maps| - device_maps.each_pair do |dev, devmap| - allow(Yast::NetworkInterfaces) - .to receive(:devmap) - .with(dev) - .and_return(devmap) - end - end - - Yast::LanItems.Read - end - - describe "#GetBondableInterfaces" do - let(:expected_bondable) { ["eth4", "eth5", "eth11", "eth12"] } - # when converting to new API new API is used - # for selecting bridgable devices but imports interfaces - # from LanItems internally - let(:config) { Y2Network::Config.new(source: :test) } - let(:builder) { Y2Network::InterfaceConfigBuilder.for("bond") } - - before do - allow(Y2Network::Config) - .to receive(:find) - .with(:yast) - .and_return(config) - end - - context "on common architectures" do - before(:each) do - expect(Yast::Arch).to receive(:s390).at_least(:once).and_return false - end - - it "returns list of slave candidates" do - builder.name = "bond1" - expect(builder.bondable_interfaces.map(&:name)) - .to match_array expected_bondable - end - end - - context "on s390" do - before(:each) do - expect(Yast::Arch).to receive(:s390).at_least(:once).and_return true - end - - it "returns list of slave candidates" do - expect(builder).to receive(:s390_ReadQethConfig).with("eth4") - .and_return("QETH_LAYER2" => "yes") - expect(builder).to receive(:s390_ReadQethConfig).with(::String) - .at_least(:once).and_return("QETH_LAYER2" => "no") - - builder.name = "bond1" - expect(builder.bondable_interfaces.map(&:name)) - .to match_array ["eth4"] - end - end - end - - describe "#setup_bonding" do - let(:bonding_map) { { "BONDING_SLAVE0" => "eth0", "BONDING_SLAVE1" => "enp0s3" } } - let(:mandatory_opts) { { "BONDING_MASTER" => "yes", "BONDING_MODULE_OPTS" => option } } - let(:option) { "bonding_option" } - - it "sets BONDING_MASTER and BONDING_MODULE_OPTS" do - expected_map = mandatory_opts - - ret = Yast::LanItems.setup_bonding({}, [], option) - - expect(ret.select { |k, _| k !~ /BONDING_SLAVE/ }).to match(expected_map) - end - - it "sets BONDING_SLAVEx options according to given list" do - expected_map = bonding_map - - ret = Yast::LanItems.setup_bonding({}, ["eth0", "enp0s3"], nil) - - expect(ret.select { |k, v| k =~ /BONDING_SLAVE/ && !v.nil? }).to match expected_map - end - - it "clears BONDING_SLAVEx which are not needed anymore" do - expected_map = { "BONDING_SLAVE0" => "enp0s3" } - - ret = Yast::LanItems.setup_bonding(bonding_map, ["enp0s3"], nil) - - expect(ret.select { |k, v| k =~ /BONDING_SLAVE/ && !v.nil? }).to match expected_map - # Following is required to get unneeded BONDING_SLAVEx deleted - # during write - expect(ret).to have_key("BONDING_SLAVE1") - expect(ret["BONDING_SLAVE1"]).to be nil - end - - it "clears all BONDING_SLAVESx and sets BONDING_MASTER, BONDING_OPTIONS when no slaves provided" do - ret = Yast::LanItems.setup_bonding(bonding_map, nil, option) - expected_slaves = { "BONDING_SLAVE0" => nil, "BONDING_SLAVE1" => nil } - expected_map = mandatory_opts.merge(expected_slaves) - - expect(ret).to match(expected_map) - end - - it "raises an exception in case of nil devmap" do - expect { Yast::LanItems.setup_bonding(nil, nil, nil) } - .to raise_error(ArgumentError, "Device map has to be provided.") - end - end -end diff --git a/test/bridge_test.rb b/test/bridge_test.rb index ac6579c1e..8eb500bd1 100755 --- a/test/bridge_test.rb +++ b/test/bridge_test.rb @@ -1,12 +1,33 @@ #! /usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" require "y2network/config" require "y2network/interface" +require "y2network/type_detector" +Yast.import "Lan" Yast.import "LanItems" describe Yast::LanItems do @@ -95,7 +116,10 @@ end end - Yast::LanItems.Read + allow(Y2Network::TypeDetector) + .to receive(:type_of) + .with(/eth[0-9]/) + .and_return(Y2Network::InterfaceType::ETHERNET) end describe "#GetBridgeableInterfaces" do @@ -103,9 +127,10 @@ # for selecting bridgable devices but imports interfaces # from LanItems internally let(:config) { Y2Network::Config.new(source: :test) } - let(:builder) { Y2Network::InterfaceConfigBuilder.for("br") } + let(:builder) { Y2Network::InterfaceConfigBuilder.for(Y2Network::InterfaceType::BRIDGE) } it "returns list of slave candidates" do + pending "old API is dropped, so adapt it" allow(Y2Network::Config) .to receive(:find) .with(:yast) diff --git a/test/build_lan_overview_test.rb b/test/build_lan_overview_test.rb index 310e36ce1..f09686e9c 100755 --- a/test/build_lan_overview_test.rb +++ b/test/build_lan_overview_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" @@ -8,130 +27,6 @@ include Yast::I18n -describe "LanItemsClass#BuildLanOverview" do - let(:unknown_device_overview) do - ["
  • Unknown Network Device
    Not configured yet.

", []] - end - let(:german_translation_overview) do - [ - "
  • WiFi Link 6000 Series
    Ohne Adresse konfiguriert (KEINE) Warnung: Es wird keine Verschl\u00FCsselung verwendet. \u00C4ndern Sie dies.

", - ["lan--wifi-encryption-wlan0"] - ] - end - let(:wlan_items) do - { - 0 => { - "ifcfg" => "wlan0" - } - } - end - let(:wlan_ifcfg) do - { - "BOOTPROTO" => "none", - "NAME" => "WiFi Link 6000 Series", - "WIRELESS_AUTH_MODE" => "open", - "WIRELESS_KEY_0" => "" - } - end - let(:lan_items) do - { - 0 => { "hwinfo" => { - "name" => "Ethernet Card 0", - "type" => "eth", - "udi" => "", - "sysfs_id" => "/devices/pci0000:00/0000:00:03.0/virtio0", - "dev_name" => "eth0", - "requires" => [], - "modalias" => "virtio:d00000001v00001AF4", - "unique" => "vWuh.VIRhsc57kTD", - "driver" => "virtio_net", - "num" => 0, - "active" => true, - "module" => "virtio_net", - "bus" => "Virtio", - "busid" => "virtio0", - "mac" => "02:00:00:12:34:56", - "link" => true - }, - "udev" => { - "net" => ["SUBSYSTEM==\"net\"", "ACTION==\"add\"", "DRIVERS==\"virtio-pci\"", - "ATTR{dev_id}==\"0x0\"", "KERNELS==\"0000:00:03.0\"", - "ATTR{type}==\"1\"", "KERNEL==\"eth*\"", "NAME=\"eth0\""], - "driver" => "" - }, - "ifcfg" => "eth0" } - } - end - let(:lan_ifcfg) do - { "STARTMODE" => "nfsroot", - "BOOTPROTO" => "dhcp", - "DHCLIENT_SET_DEFAULT_ROUTE" => "yes" } - end - let(:interfaces) { Y2Network::InterfacesCollection.new([]) } - - before do - allow(Y2Network::Config) - .to receive(:find) - .and_return(instance_double(Y2Network::Config, interfaces: interfaces)) - end - - # targeted mainly against bnc#906694 - context "with an wlan interface" do - before do - allow(Yast::LanItems) - .to receive(:Items) - .and_return(wlan_items) - allow(Yast::LanItems) - .to receive(:GetDeviceMap) - .and_return(wlan_ifcfg) - allow(Yast::NetworkInterfaces) - .to receive(:Current) - .and_return(wlan_ifcfg) - allow(Yast::NetworkInterfaces) - .to receive(:GetType) - .and_call_original - allow(Yast::NetworkInterfaces) - .to receive(:GetType) - .with("wlan0") - .and_return("wlan") - allow(FastGettext) - .to receive(:locale) - .and_return("de") - end - it "returns translated network device textual description for wlan device" do - # locale search path - stub_const("Yast::I18n::LOCALE_DIR", File.expand_path("../locale", __FILE__)) - - textdomain("network") - - # other checks depends on this - # - output of BuildLanOverview changes according number of devices - # even for "failing" (unknown devices) path - expect(Yast::LanItems.Items.size).to eql 1 - - overview = Yast::LanItems.BuildLanOverview - expect(overview).not_to eql unknown_device_overview - expect(overview).to eql german_translation_overview - end - end - - context "with an lan interface" do - before do - allow(Yast::LanItems).to receive(:Items) - .and_return(lan_items) - allow(Yast::LanItems).to receive(:GetDeviceMap) - .and_return(lan_ifcfg) - allow(Yast::NetworkInterfaces).to receive(:Current) - .and_return(lan_ifcfg) - end - it "returns description for lan device with the correct start option" do - Yast::LanItems.BuildLanOverview - expect(Yast::LanItems.Items.size).to eql 1 - expect(Yast::LanItems.Items[0]["table_descr"]["rich_descr"].include?("Started automatically at boot")).to eql(true) - end - end -end - describe "LanItemsClass#ip_overview" do # smoke test for bnc#1013684 it "do not crash when devmap for staticaly configured device do not contain PREFIXLEN" do diff --git a/test/cfa/generic_sysconfig_test.rb b/test/cfa/generic_sysconfig_test.rb index ede684fae..15ff12dec 100644 --- a/test/cfa/generic_sysconfig_test.rb +++ b/test/cfa/generic_sysconfig_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "../test_helper" require "cfa/generic_sysconfig" require "cfa/memory_file" diff --git a/test/cfa/hosts_test.rb b/test/cfa/hosts_test.rb index 2428bc19e..508e86ac0 100755 --- a/test/cfa/hosts_test.rb +++ b/test/cfa/hosts_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "../test_helper" require "cfa/hosts" require "cfa/memory_file" diff --git a/test/cmdline_test.rb b/test/cmdline_test.rb index f53f4b3b4..abafcba7a 100644 --- a/test/cmdline_test.rb +++ b/test/cmdline_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" class DummyClass < Yast::Module @@ -11,6 +30,10 @@ def initialize describe "NetworkLanCmdlineInclude" do subject { DummyClass.new } + before do + allow(Yast::Lan).to receive(:yast_config).and_return(Y2Network::Config.new(source: :fake)) + end + describe "#ShowHandler" do it "creates plain text from formatted html" do richtext = "test
  • item1
  • item2
" @@ -48,11 +71,13 @@ def initialize context "when startmode is given" do context "but with an invalid option" do it "reports an error" do + pending "invalid option is not yet handled" expect(Yast::Report).to receive(:Error) subject.AddHandler(options.merge("startmode" => "wrong")) end it "returns false" do + pending "invalid option is not yet handled" expect(subject.AddHandler(options.merge("startmode" => "wrong"))).to eq false end end @@ -86,6 +111,7 @@ def initialize before do allow(Yast::LanItems).to receive(:Items).and_return(items) + allow(Yast::LanItems).to receive(:GetCurrentType).and_return("eth") richtext = "test
  • item1
" allow(subject).to receive(:getConfigList).and_return(["0" => { "rich_descr" => richtext }]) end diff --git a/test/complex_test.rb b/test/complex_test.rb index ffd020782..df0d1abdc 100755 --- a/test/complex_test.rb +++ b/test/complex_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/data/hardware.yml b/test/data/hardware.yml new file mode 100644 index 000000000..4eb748f81 --- /dev/null +++ b/test/data/hardware.yml @@ -0,0 +1,30 @@ +--- +- name: Ethernet Card 0 + type: eth + udi: '' + sysfs_id: "/devices/pci0000:00/0000:00:02.0/0000:01:00.0/virtio0" + dev_name: enp1s0 + requires: [] + modalias: virtio:d00000001v00001AF4 + unique: Ds1o.VIRhsc57kTD + driver: virtio_net + num: 0 + drivers: + - active: true + modprobe: true + modules: + - - virtio_net + - '' + active: true + module: virtio_net + options: '' + bus: Virtio + busid: virtio0 + parent_busid: '0000:01:00.0' + mac: 52:54:00:68:54:fb + permanent_mac: 52:54:00:68:54:fb + link: true + wl_channels: + wl_bitrates: + wl_auth_modes: + wl_enc_modes: diff --git a/test/data/scr_read/etc/hosts b/test/data/scr_read/etc/hosts new file mode 100644 index 000000000..e6b73b292 --- /dev/null +++ b/test/data/scr_read/etc/hosts @@ -0,0 +1,2 @@ +127.0.0.1 localhost +192.168.123.1 foo \ No newline at end of file diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-bond0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-bond0 new file mode 100644 index 000000000..11e58e988 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-bond0 @@ -0,0 +1,6 @@ +STARTMODE=auto +BOOTPROTO=static +IPADDR=192.168.20.100/24 +BONDING_SLAVE1=eth0 +BONDING_SLAVE2=eth1 +BONDING_MODULE_OPTS='mode=active-backup miimon=100' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-br0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-br0 new file mode 100644 index 000000000..385fc11bd --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-br0 @@ -0,0 +1,6 @@ +STARTMODE='auto' +BOOTPROTO='dhcp' +BRIDGE='yes' +BRIDGE_PORTS='eth0 eth1' +BRIDGE_STP='on' +BRIDGE_FORWARDDELAY='5' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-ctc0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-ctc0 new file mode 100644 index 000000000..132179fce --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-ctc0 @@ -0,0 +1,3 @@ +STARTMODE='auto' +BOOTPROTO='static' +IPADDR='192.168.20.50/24' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-dummy0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-dummy0 new file mode 100644 index 000000000..25659f827 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-dummy0 @@ -0,0 +1,3 @@ +INTERFACETYPE='dummy' +BOOTPROTO='static' +IPADDR='192.168.100.150/24' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth0.100 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth0.100 new file mode 100644 index 000000000..ea9e7eea4 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth0.100 @@ -0,0 +1,5 @@ +BOOTPROTO='static' +IPADDR='192.168.100.20/24' +STARTMODE='auto' +VLAN_ID=100 +ETHERDEVICE=eth0 diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth1 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth1 new file mode 100644 index 000000000..66d6c4638 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth1 @@ -0,0 +1,11 @@ +BOOTPROTO='static' +BROADCAST='' +ETHTOOL_OPTIONS='' +IPADDR_0='192.168.123.1/24' +IPADDR_1='10.0.0.1' +NETMASK_1='255.0.0.0' +MTU='' +NAME='Ethernet Card 0' +NETWORK='' +REMOTE_IPADDR='' +STARTMODE='auto' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth2 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth2 new file mode 100644 index 000000000..7caf76410 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth2 @@ -0,0 +1,7 @@ +BOOTPROTO='static' +BROADCAST='' +MTU='' +IPADDR='172.16.0.1' +PREFIXLEN='12' +NAME='Ethernet Card 2' +STARTMODE='auto' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth3 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth3 new file mode 100644 index 000000000..b595cee76 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth3 @@ -0,0 +1,7 @@ +BOOTPROTO='static' +BROADCAST='' +MTU='' +IPADDR='10.0.0.1' +NETMASK='255.0.0.0' +NAME='Ethernet Card 3' +STARTMODE='auto' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth4 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth4 new file mode 100644 index 000000000..ab0b8cffb --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth4 @@ -0,0 +1,5 @@ +BOOTPROTO='dhcp' +BROADCAST='' +MTU='' +NAME='Ethernet Card 4' +STARTMODE='auto' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth5 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth5 new file mode 100644 index 000000000..2f9e6e88b --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth5 @@ -0,0 +1,4 @@ +STARTMODE='auto' +BOOTPROTO='static' +IPADDR='192.168.50.1/24' +LLADDR='00:06:29:55:2A:01' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-eth6 b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth6 new file mode 100644 index 000000000..bdc3361fb --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-eth6 @@ -0,0 +1,4 @@ +# LCS interface +STARTMODE='auto' +BOOTPROTO='static' +IPADDR='192.168.70.100/24' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-hsi0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-hsi0 new file mode 100644 index 000000000..09fd4f449 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-hsi0 @@ -0,0 +1,3 @@ +STARTMODE='auto' +BOOTPROTO='static' +IPADDR='192.168.100.10/24' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-ib0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-ib0 new file mode 100644 index 000000000..dba4531c7 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-ib0 @@ -0,0 +1,6 @@ +BOOTPROTO=static +STARTMODE=auto +IPOIB_MODE=datagram +IPADDR=192.168.20.1 +NETMASK=255.255.255.0 +BROADCAST=192.168.20.255 diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-tap0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-tap0 new file mode 100644 index 000000000..27eaf2d1a --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-tap0 @@ -0,0 +1,5 @@ +STARTMODE='auto' +BOOTPROTO='static' +TUNNEL='tap' +TUNNEL_SET_OWNER='nobody' +TUNNEL_SET_GROUP='nobody' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-tun0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-tun0 new file mode 100644 index 000000000..655002435 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-tun0 @@ -0,0 +1,5 @@ +STARTMODE='manual' +BOOTPROTO='static' +TUNNEL='tun' +TUNNEL_SET_OWNER='nobody' +TUNNEL_SET_GROUP='nobody' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-usb0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-usb0 new file mode 100644 index 000000000..38caa4cb3 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-usb0 @@ -0,0 +1,3 @@ +BOOTPROTO='static' +IPADDR='192.168.231.33/24' +STARTMODE='auto' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan0 b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan0 index 1a7203eea..6ecc0b49d 100644 --- a/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan0 +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan0 @@ -1,11 +1,12 @@ +# WPA-EAP network configuration BOOTPROTO='static' BROADCAST='' NAME='Wireless Adapter 0' STARTMODE='auto' -WIRELESS_AUTH_MODE='shared' -WIRELESS_DEFAULT_KEY='2' +WIRELESS_AUTH_MODE='eap' +WIRELESS_EAP_MODE='PEAP' +WIRELESS_EAP_AUTH='mschapv2' +WIRELESS_WPA_PASSWORD='example_passwd' WIRELESS_ESSID='example_ssid' -WIRELESS_KEY="0-1-2-3-4-5" -WIRELESS_KEY_1="s:password" -WIRELESS_KEY_LENGTH='128' -WIRELESS_MODE='Managed' \ No newline at end of file +WIRELESS_MODE='Managed' +WIRELESS_AP_SCANMODE='1' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan1 b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan1 new file mode 100644 index 000000000..86828d60f --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan1 @@ -0,0 +1,11 @@ +# WPA-PSK network configuration +BOOTPROTO='static' +BROADCAST='' +NAME='Wireless Adapter 1' +STARTMODE='auto' +WIRELESS_AUTH_MODE='psk' +WIRELESS_ESSID='example_ssid' +WIRELESS_WPA_PSK='example_psk' +WIRELESS_MODE='Managed' +WIRELESS_AP_SCANMODE='1' +WIRELESS_AP='00:11:22:33:44:55' diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan2 b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan2 new file mode 100644 index 000000000..c4bd98c79 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan2 @@ -0,0 +1,12 @@ +# WEP network configuration +BOOTPROTO='static' +BROADCAST='' +NAME='Wireless Adapter 2' +STARTMODE='auto' +WIRELESS_AUTH_MODE='shared' +WIRELESS_DEFAULT_KEY='1' +WIRELESS_ESSID='example_ssid' +WIRELESS_KEY_0="0-1-2-3-4-5" +WIRELESS_KEY_1="s:password" +WIRELESS_KEY_LENGTH='128' +WIRELESS_MODE='Managed' \ No newline at end of file diff --git a/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan3 b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan3 new file mode 100644 index 000000000..cec0e83e5 --- /dev/null +++ b/test/data/scr_read/etc/sysconfig/network/ifcfg-wlan3 @@ -0,0 +1,6 @@ +BOOTPROTO='static' +BROADCAST='' +NAME='Wireless Adapter 3' +STARTMODE='auto' +WIRELESS_AUTH_MODE='open' +WIRELESS_MODE='Managed' \ No newline at end of file diff --git a/test/default_route_test.rb b/test/default_route_test.rb deleted file mode 100755 index 284ff224d..000000000 --- a/test/default_route_test.rb +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -require "network/install_inf_convertor" - -describe "Yast::LanItemsClass" do - subject { Yast::LanItems } - - before do - Yast.import "LanItems" - - @ifcfg_files = SectionKeyValue.new - - # network configs - allow(Yast::SCR).to receive(:Dir) do |path| - case path.to_s - when ".network.section" - @ifcfg_files.sections - when /^\.network\.value\."(eth\d+)"$/ - @ifcfg_files.keys(Regexp.last_match(1)) - when ".modules.options", ".etc.install_inf" - [] - else - raise "Unexpected Dir #{path}" - end - end - - allow(Yast::SCR).to receive(:Read) do |path| - if path.to_s =~ /^\.network\.value\."(eth\d+)".(.*)/ - next @ifcfg_files.get(Regexp.last_match(1), Regexp.last_match(2)) - end - - raise "Unexpected Read #{path}" - end - - allow(Yast::SCR).to receive(:Write) do |path, value| - if path.to_s =~ /^\.network\.value\."(eth\d+)".(.*)/ - @ifcfg_files.set(Regexp.last_match(1), Regexp.last_match(2), value) - elsif path.to_s == ".network" && value.nil? - true - else - raise "Unexpected Write #{path}, #{value}" - end - end - - # stub NetworkInterfaces, apart from the ifcfgs - allow(Yast::NetworkInterfaces) - .to receive(:CleanHotplugSymlink) - allow(Yast::NetworkInterfaces) - .to receive(:adapt_old_config!) - allow(Yast::NetworkInterfaces) - .to receive(:GetTypeFromSysfs) - .with(/eth\d+/) - .and_return "eth" - allow(Yast::NetworkInterfaces) - .to receive(:GetType) - .with(/eth\d+/) - .and_return "eth" - allow(Yast::NetworkInterfaces) - .to receive(:GetType) - .with("") - .and_return nil - Yast::NetworkInterfaces.instance_variable_set(:@initialized, false) - - allow(Yast::InstallInfConvertor.instance) - .to receive(:AllowUdevModify).and_return false - - # These "expect" should be "allow", but then it does not work out, - # because SCR multiplexes too much and the matchers get confused. - - # Hardware detection - expect(Yast::SCR) - .to receive(:Read) - .with(path(".probe.netcard")) - .and_return([]) - - # miscellaneous uninteresting but hard to avoid stuff - - allow(Yast::Arch).to receive(:architecture).and_return "x86_64" - allow(Yast::Confirm).to receive(:Detection).and_return true - - expect(Yast::SCR) - .to receive(:Read) - .with(path(".etc.install_inf.BrokenModules")) - .and_return "" - expect(Yast::SCR) - .to receive(:Read) - .with(path(".udev_persistent.net")) - .and_return({}) - expect(Yast::SCR) - .to receive(:Read) - .with(path(".udev_persistent.drivers")) - .and_return({}) - end - - it "does not modify DHCLIENT_SET_DEFAULT_ROUTE if not explicitly set, when editing an ifcfg" do - @ifcfg_files.set("eth0", "STARTMODE", "auto") - @ifcfg_files.set("eth0", "BOOTPROTO", "dhcp4") - - subject.Read - subject.current = 0 - - builder = Y2Network::InterfaceConfigBuilder.new - builder.name = subject.GetCurrentName() - builder.type = subject.GetCurrentType() - subject.SetItem(builder: builder) - - subject.Commit(builder) - subject.write - - ifcfg = Yast::NetworkInterfaces.FilterDevices("")["eth"]["eth0"] - expect(ifcfg["DHCLIENT_SET_DEFAULT_ROUTE"]).to be_nil - end -end diff --git a/test/dns_test.rb b/test/dns_test.rb index 139ed246c..1be60fa77 100755 --- a/test/dns_test.rb +++ b/test/dns_test.rb @@ -1,194 +1,214 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" -module Yast - import "Arch" - import "DNS" - import "ProductControl" - import "Lan" +require "y2network/type_detector" +require "y2network/interface_type" - describe DNS do - let(:lan_config) do - Y2Network::Config.new(dns: dns_config, source: :sysconfig) - end - let(:dns_config) do - Y2Network::DNS.new(dhcp_hostname: true) - end +Yast.import "Arch" +Yast.import "DNS" +Yast.import "ProductControl" +Yast.import "Lan" + +describe Yast::DNS do + let(:lan_config) do + Y2Network::Config.new(dns: dns_config, source: :sysconfig) + end + let(:dns_config) do + Y2Network::DNS.new(dhcp_hostname: true) + end + + subject { Yast::DNS } + before do + allow(Yast::Lan).to receive(:Read) + allow(Yast::Lan).to receive(:yast_config).and_return(lan_config) + end + + describe ".default_dhcp_hostname" do before do - allow(Lan).to receive(:yast_config).and_return(lan_config) + allow(Yast::Arch).to receive(:is_laptop).and_return laptop + Yast::ProductControl.ReadControlFile(File.join(SCRStub::DATA_PATH, control_file)) end - describe ".default_dhcp_hostname" do - before do - allow(Arch).to receive(:is_laptop).and_return laptop - ProductControl.ReadControlFile(File.join(SCRStub::DATA_PATH, control_file)) - end - - context "with dhcp_hostname=true in control file" do - let(:control_file) { "dhcp_hostname_true.xml" } + context "with dhcp_hostname=true in control file" do + let(:control_file) { "dhcp_hostname_true.xml" } - context "in a laptop" do - let(:laptop) { true } + context "in a laptop" do + let(:laptop) { true } - it "returns the value from product features" do - expect(DNS.default_dhcp_hostname).to eql(true) - end + it "returns the value from product features" do + expect(subject.default_dhcp_hostname).to eql(true) end + end - context "in a workstation" do - let(:laptop) { false } + context "in a workstation" do + let(:laptop) { false } - it "returns the value from product features" do - expect(DNS.default_dhcp_hostname).to eql(true) - end + it "returns the value from product features" do + expect(subject.default_dhcp_hostname).to eql(true) end end + end - context "with dhcp_hostname=false in control file" do - let(:control_file) { "dhcp_hostname_false.xml" } + context "with dhcp_hostname=false in control file" do + let(:control_file) { "dhcp_hostname_false.xml" } - context "in a laptop" do - let(:laptop) { true } + context "in a laptop" do + let(:laptop) { true } - it "returns the value from product features" do - expect(DNS.default_dhcp_hostname).to eql(false) - end + it "returns the value from product features" do + expect(subject.default_dhcp_hostname).to eql(false) end + end - context "in a workstation" do - let(:laptop) { false } + context "in a workstation" do + let(:laptop) { false } - it "returns the value from product features" do - expect(DNS.default_dhcp_hostname).to eql(false) - end + it "returns the value from product features" do + expect(subject.default_dhcp_hostname).to eql(false) end end + end - context "without dhcp_hostname in control file" do - let(:control_file) { "dhcp_hostname_nil.xml" } + context "without dhcp_hostname in control file" do + let(:control_file) { "dhcp_hostname_nil.xml" } - context "in a laptop" do - let(:laptop) { true } + context "in a laptop" do + let(:laptop) { true } - it "returns false" do - expect(DNS.default_dhcp_hostname).to eql(false) - end + it "returns false" do + expect(subject.default_dhcp_hostname).to eql(false) end + end - context "in a workstation" do - let(:laptop) { false } + context "in a workstation" do + let(:laptop) { false } - it "returns true" do - expect(DNS.default_dhcp_hostname).to eql(true) - end + it "returns true" do + expect(subject.default_dhcp_hostname).to eql(true) end end end + end - describe ".IsHostLocal" do - let(:ip) { "10.111.66.75" } - let(:hostname_short) { "test" } - let(:hostname_fq) { "test.test.de" } - let(:output) { { "ip" => ip, "hostname_short" => hostname_short, "hostname_fq" => hostname_fq } } - let(:ipv4) { false } - let(:ipv6) { false } - let(:stdout) { double } - - before do - DNS.dhcp_hostname = true - - allow(DNS).to receive(:Read) - allow(IP).to receive(:Check4).and_return(ipv4) - allow(IP).to receive(:Check6).and_return(ipv6) - allow(Yast::Execute).to receive(:stdout).and_return(stdout) - allow(stdout).to receive(:on_target!).with("/bin/hostname -i").and_return(ip) - allow(stdout).to receive(:on_target!).with("/bin/hostname").and_return(hostname_short) - allow(stdout).to receive(:on_target!).with("/bin/hostname -f").and_return(hostname_fq) - end + describe ".IsHostLocal" do + let(:ip) { "10.111.66.75" } + let(:hostname_short) { "test" } + let(:hostname_fq) { "test.test.de" } + let(:output) { { "ip" => ip, "hostname_short" => hostname_short, "hostname_fq" => hostname_fq } } + let(:ipv4) { false } + let(:ipv6) { false } + let(:stdout) { double } - ["localhost", "localhost.localdomain", "::1", "127.0.0.1"].each do |host| - it "returns true when host is \"#{host}\"" do - expect(DNS.IsHostLocal(host)).to eq(true) - end - end + before do + allow(Y2Network::TypeDetector) + .to receive(:type_of) + .with(/eth[0-9]/) + .and_return(Y2Network::InterfaceType::ETHERNET) + allow(subject).to receive(:Read) + allow(Yast::IP).to receive(:Check4).and_return(ipv4) + allow(Yast::IP).to receive(:Check6).and_return(ipv6) + allow(Yast::Execute).to receive(:stdout).and_return(stdout) + allow(stdout).to receive(:on_target!).with("/usr/bin/hostname -i").and_return(ip) + allow(stdout).to receive(:on_target!).with("/usr/bin/hostname").and_return(hostname_short) + allow(stdout).to receive(:on_target!).with("/usr/bin/hostname -f").and_return(hostname_fq) + + subject.dhcp_hostname = true + end - it "returns true when the short hostname is given" do - expect(DNS.IsHostLocal(hostname_short)).to eq(true) + ["localhost", "localhost.localdomain", "::1", "127.0.0.1"].each do |host| + it "returns true when host is \"#{host}\"" do + expect(subject.IsHostLocal(host)).to eq(true) end + end - it "returns true when the fq hostname is given" do - expect(DNS.IsHostLocal(hostname_fq)).to eq(true) - end + it "returns true when the short hostname is given" do + expect(subject.IsHostLocal(hostname_short)).to eq(true) + end - context "for IPv4" do - let(:ipv4) { true } + it "returns true when the fq hostname is given" do + expect(subject.IsHostLocal(hostname_fq)).to eq(true) + end - it "returns true when the ip of local machine is given" do - expect(DNS.IsHostLocal(ip)).to eq(true) - end + context "for IPv4" do + let(:ipv4) { true } - it "returns false when the ip of local machine is not given" do - expect(DNS.IsHostLocal("1.2.3.4")).to eq(false) - end + it "returns true when the ip of local machine is given" do + expect(subject.IsHostLocal(ip)).to eq(true) end - end - describe ".Read" do - it "delegates DNS settings reading to Yast::Lan module" do - expect(Yast::Lan).to receive(:Read).with(:cache) - Yast::DNS.Read + it "returns false when the ip of local machine is not given" do + expect(subject.IsHostLocal("1.2.3.4")).to eq(false) end end + end - describe ".Write" do - let(:dns_writer) { instance_double(Y2Network::Sysconfig::DNSWriter) } - let(:yast_config) { double("Y2Network::Config", dns: instance_double("Y2Network::DNS")) } - let(:system_config) { double("Y2Network::Config", dns: instance_double("Y2Network::DNS")) } + describe ".Read" do + it "delegates DNS settings reading to Yast::Lan module" do + expect(Yast::Lan).to receive(:Read).with(:cache) + subject.Read + end + end - before do - allow(Y2Network::Sysconfig::DNSWriter).to receive(:new).and_return(dns_writer) - allow(Yast::Lan).to receive(:yast_config).and_return(yast_config) - allow(Yast::Lan).to receive(:system_config).and_return(system_config) - end + describe ".Write" do + let(:dns_writer) { instance_double(Y2Network::Sysconfig::DNSWriter) } + let(:yast_config) { double("Y2Network::Config", dns: instance_double("Y2Network::DNS")) } + let(:system_config) { double("Y2Network::Config", dns: instance_double("Y2Network::DNS")) } - it "writes DNS settings" do - expect(dns_writer).to receive(:write).with(yast_config.dns, system_config.dns) - DNS.Write - end + before do + allow(Y2Network::Sysconfig::DNSWriter).to receive(:new).and_return(dns_writer) + allow(Yast::Lan).to receive(:yast_config).and_return(yast_config) + allow(Yast::Lan).to receive(:system_config).and_return(system_config) end - describe ".modified" do - let(:yast_config) { double("Y2Network::Config", dns: double("dns")) } - let(:system_config) { double("Y2Network::Config", dns: double("dns")) } - - before do - allow(Yast::Lan).to receive(:yast_config).and_return(yast_config) - allow(Yast::Lan).to receive(:system_config).and_return(system_config) - end + it "writes DNS settings" do + expect(dns_writer).to receive(:write).with(yast_config.dns, system_config.dns) + subject.Write + end + end - context "when DNS configuration has changed" do - it "returns true" do - expect(DNS.modified).to eq(true) - end - end + describe ".modified" do + let(:yast_config) { double("Y2Network::Config", dns: double("dns")) } + let(:system_config) { double("Y2Network::Config", dns: double("dns")) } - context "when DNS configuration has not changed" do - let(:system_config) { double("Y2Network::Config", dns: yast_config.dns) } + before do + allow(Yast::Lan).to receive(:yast_config).and_return(yast_config) + allow(Yast::Lan).to receive(:system_config).and_return(system_config) + end - it "returns false" do - expect(DNS.modified).to eq(false) - end + context "when DNS configuration has changed" do + it "returns true" do + expect(subject.modified).to eq(true) end end - describe "#propose_hostname" do - it "proposes a hostname" do - expect(dns_config).to receive(:ensure_hostname!) - DNS.propose_hostname + context "when DNS configuration has not changed" do + let(:system_config) { double("Y2Network::Config", dns: yast_config.dns) } + + it "returns false" do + expect(subject.modified).to eq(false) end end end diff --git a/test/host_auto_test.rb b/test/host_auto_test.rb index 70510aeec..6d5ba9d6f 100755 --- a/test/host_auto_test.rb +++ b/test/host_auto_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/host_service_test.rb b/test/host_service_test.rb index 555bfbc89..7778acbce 100755 --- a/test/host_service_test.rb +++ b/test/host_service_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" Yast.import "UI" diff --git a/test/host_test.rb b/test/host_test.rb index 3a6cd5a40..465874316 100755 --- a/test/host_test.rb +++ b/test/host_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" @@ -7,6 +26,7 @@ require "cfa/memory_file" require "cfa/base_model" require "cfa/hosts" +require "y2network/sysconfig/type_detector" Yast.import "Host" Yast.import "DNS" @@ -31,8 +51,13 @@ end before do + allow(Yast::Lan).to receive(:Read) allow(Yast::Lan).to receive(:yast_config).and_return(lan_config) allow(Yast::SCR).to receive(:Read).with(path(".target.size"), "/etc/hosts").and_return(50) + allow(Y2Network::Sysconfig::TypeDetector) + .to receive(:type_of) + .with(/eth[0-9]/) + .and_return(Y2Network::InterfaceType::ETHERNET) # reset internal caches Yast::Host.instance_variable_set(:"@modified", false) diff --git a/test/inst_setup_dhcp_test.rb b/test/inst_setup_dhcp_test.rb index a93262e66..edbe7fd68 100755 --- a/test/inst_setup_dhcp_test.rb +++ b/test/inst_setup_dhcp_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "network/clients/inst_setup_dhcp" @@ -12,6 +31,7 @@ end before do + allow(Yast::Lan).to receive(:Read).and_return(lan_config) allow(Yast::Lan).to receive(:yast_config).and_return(lan_config) end diff --git a/test/install_inf_convertor_test.rb b/test/install_inf_convertor_test.rb index 9c1701705..14fe180b7 100755 --- a/test/install_inf_convertor_test.rb +++ b/test/install_inf_convertor_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/lan_auto_test.rb b/test/lan_auto_test.rb index 11955d8f7..a715d9188 100755 --- a/test/lan_auto_test.rb +++ b/test/lan_auto_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" @@ -15,13 +34,13 @@ context "when func is GetModified" do let(:func) { "GetModified" } - it "returns true if LanItems.GetModified is true" do - expect(Yast::LanItems).to receive(:GetModified).and_return(true) + it "returns true if Lan.GetModified is true" do + expect(Yast::Lan).to receive(:Modified).and_return(true) expect(subject.main).to eq(true) end - it "returns false if LanItems.GetModified is false" do - expect(Yast::LanItems).to receive(:GetModified).and_return(false) + it "returns false if Lan.GetModified is false" do + expect(Yast::Lan).to receive(:Modified).and_return(false) expect(subject.main).to eq(false) end end diff --git a/test/lan_cmdline_test.rb b/test/lan_cmdline_test.rb index 041369173..33d2e54fa 100644 --- a/test/lan_cmdline_test.rb +++ b/test/lan_cmdline_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" Yast.import "Report" diff --git a/test/lan_items_export_test.rb b/test/lan_items_export_test.rb deleted file mode 100755 index d537cc9a6..000000000 --- a/test/lan_items_export_test.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -require "yast" - -Yast.import "LanItems" -Yast.import "Arch" - -describe "LanItemsClass#export_udevs" do - subject { Yast::LanItems } - - let(:devices) do - { - "eth" => { - "eth0" => {} - } - } - end - - 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 - - before(:each) do - allow(Yast::Arch).to receive(:s390).and_return(is_s390) - end - - 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(::File) - .to receive(:readlink) - .and_return("../../../qeth") - - allow(::File) - .to receive(:read) - .and_return("") - - ay = subject.send(:export_udevs, devices) - - expect(ay["s390-devices"]).not_to be_empty - # check if the export builds correct map - expect(ay["s390-devices"]["eth0"]["type"]).to eql "qeth" - end - end -end diff --git a/test/lan_items_helpers_test.rb b/test/lan_items_helpers_test.rb index 02df20fca..aff1f2138 100755 --- a/test/lan_items_helpers_test.rb +++ b/test/lan_items_helpers_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" @@ -31,21 +50,6 @@ end end -describe "LanItemsClass#delete_dev" do - before(:each) do - Yast::LanItems.Items = { - 0 => { - "ifcfg" => "enp0s3" - } - } - end - - it "removes device config when found" do - Yast::LanItems.delete_dev("enp0s3") - expect(Yast::LanItems.Items).to be_empty - end -end - describe "LanItemsClass#getNetworkInterfaces" do NETCONFIG_ITEMS = { "eth" => { @@ -111,234 +115,6 @@ end end -describe "LanItemsClass#s390_correct_lladdr" do - Yast.import "Arch" - - before(:each) do - allow(Yast::Arch) - .to receive(:s390) - .and_return(true) - end - - it "fails if given lladdr is nil" do - expect(Yast::LanItems.send(:s390_correct_lladdr, nil)).to be false - end - - it "fails if given lladdr is empty" do - expect(Yast::LanItems.send(:s390_correct_lladdr, "")).to be false - end - - it "fails if given lladdr contains zeroes only" do - expect(Yast::LanItems.send(:s390_correct_lladdr, "00:00:00:00:00:00")).to be false - end - - it "succeeds if given lladdr contains valid MAC" do - expect(Yast::LanItems.send(:s390_correct_lladdr, "0a:00:27:00:00:00")).to be true - end -end - -describe "LanItems#InitItemUdev" do - def udev_rule(mac, name) - [ - "SUBSYSTEM==\"net\"", - "ACTION==\"add\"", - "DRIVERS==\"?*\"", - "ATTR{address}==\"#{mac}\"", - "ATTR{type}==\"1\"", - "NAME=\"#{name}\"" - ] - end - - before(:each) do - allow(Yast::LanItems) - .to receive(:Items) - .and_return( - 0 => { - "ifcfg" => "eth0", - "udev" => { - "net" => udev_rule("24:be:05:ce:1e:91", "eth0") - } - }, - 1 => { - "hwinfo" => { - "permanent_mac" => "00:00:00:00:00:01", - "dev_name" => "eth1" - }, - # always exists - "udev" => { - "net" => [] - } - } - ) - end - - it "returns existing udev rule if there is any already" do - expect(Yast::LanItems.InitItemUdevRule(0)).to eql udev_rule("24:be:05:ce:1e:91", "eth0") - end - - it "creates new udev rule if none is present" do - expect(Yast::LanItems.InitItemUdevRule(1)).to eql udev_rule("00:00:00:00:00:01", "eth1") - end -end - -describe "LanItems#GetItemUdev" do - def check_GetItemUdev(key, expected_value) - expect(Yast::LanItems.GetItemUdev(key)).to eql expected_value - end - - context "when current item has an udev rule associated" do - BUSID = "0000:00:00.0".freeze - - before(:each) do - allow(Yast::LanItems) - .to receive(:getCurrentItem) - .and_return("udev" => { "net" => ["KERNELS==\"#{BUSID}\""] }) - end - - it "returns proper value when key exists" do - check_GetItemUdev("KERNELS", BUSID) - end - - it "returns an empty string when key doesn't exist" do - check_GetItemUdev("NAME", "") - end - end - - context "when current item doesn't have an udev rule associated" do - MAC = "00:11:22:33:44:55".freeze - - before(:each) do - allow(Yast::LanItems) - .to receive(:GetLanItem) - .and_return("hwinfo" => { "permanent_mac" => MAC }, "ifcfg" => "eth0") - end - - it "returns proper value when key exists" do - check_GetItemUdev("ATTR{address}", MAC) - end - - it "returns an empty string when key doesn't exist" do - check_GetItemUdev("KERNELS", "") - end - end -end - -describe "LanItems#RemoveItemUdev" do - let(:rule) { { 0 => { "udev" => { "net" => ["KEY_TO_DELETE==\"VALUE\"", "OTHER_KEY"] } } } } - before(:each) do - Yast::LanItems.Items = rule - Yast::LanItems.current = 0 - end - - context "when the current item has an udev rule associated" do - it "removes the given key from the current rule if exists" do - Yast::LanItems.RemoveItemUdev("KEY_TO_DELETE") - - expect(Yast::LanItems.GetItemUdevRule(0)).to eql(["OTHER_KEY"]) - end - - it "the current rule keeps untouched if the given key does not exist" do - Yast::LanItems.RemoveItemUdev("NOT_PRESENT_KEY") - - expect(Yast::LanItems.GetItemUdevRule(0)) - .to eql(["KEY_TO_DELETE==\"VALUE\"", "OTHER_KEY"]) - end - end - - context "when the current item doesn't have an udev rule associated" do - let(:rule) { { 0 => { "ifcfg" => "eth0" } } } - - it "returns nil" do - expect(Yast::LanItems.RemoveItemUdev("KEY_TO_DELETE")).to eql(nil) - end - end -end - -describe "#current_udev_rule" do - let(:busid) { "0000:08:00.0" } - let(:hwinfo) { { "dev_name" => "test0", "busid" => busid, "permanent_mac" => "24:be:05:ce:1e:91" } } - let(:udev_net) { ["KERNELS==\"#{busid}\"", "NAME=\"test0\""] } - let(:rule) { { 0 => { "hwinfo" => hwinfo, "udev" => { "net" => udev_net } } } } - - before do - Yast::LanItems.Items = rule - Yast::LanItems.current = 0 - end - - it "returns the current item udev rule" do - expect(Yast::LanItems.current_udev_rule).to contain_exactly("KERNELS==\"#{busid}\"", "NAME=\"test0\"") - end -end - -describe "#update_item_udev_rule!" do - let(:hwinfo) { { "dev_name" => "test0", "busid" => "0000:08:00.0", "permanent_mac" => "24:be:05:ce:1e:91" } } - let(:udev_net) { ["ATTR{address}==\"24:be:05:ce:1e:91\"", "KERNEL==\"eth*\"", "NAME=\"test0\""] } - let(:rule) { { 0 => { "hwinfo" => hwinfo, "udev" => { "net" => udev_net } } } } - - before do - Yast::LanItems.Items = rule - Yast::LanItems.current = 0 - # The current item hasn't got a dev_port - allow(Yast::LanItems).to receive(:dev_port).and_return("") - end - - context "when the given rule key is :bus_id" do - it "uses KERNELS attribute with busid match instead of mac address" do - expect(Yast::LanItems.Items[0]["udev"]["net"]).to eql(udev_net) - Yast::LanItems.update_item_udev_rule!(:bus_id) - expect(Yast::LanItems.Items[0]["udev"]["net"]) - .to eql(["KERNEL==\"eth*\"", "KERNELS==\"0000:08:00.0\"", "NAME=\"test0\""]) - end - - context "and the dev_port is available via sysfs" do - it "also adds the dev_port to the current rule" do - allow(Yast::LanItems).to receive(:dev_port).and_return("0") - expect(Yast::LanItems.Items[0]["udev"]["net"]).to eql(udev_net) - Yast::LanItems.update_item_udev_rule!(:bus_id) - expect(Yast::LanItems.Items[0]["udev"]["net"]) - .to eql(["KERNEL==\"eth*\"", "ATTR{dev_port}==\"0\"", "KERNELS==\"0000:08:00.0\"", "NAME=\"test0\""]) - end - end - end - - context "when the given rule key is :mac" do - let(:udev_net) { ["KERNEL==\"eth*\"", "KERNELS==\"0000:08:00.0\"", "NAME=\"test0\""] } - - it "uses mac attribute" do - expect(Yast::LanItems.Items[0]["udev"]["net"]).to eql(udev_net) - Yast::LanItems.update_item_udev_rule!(:mac) - expect(Yast::LanItems.Items[0]["udev"]["net"]) - .to eql(["KERNEL==\"eth*\"", "ATTR{address}==\"24:be:05:ce:1e:91\"", "NAME=\"test0\""]) - end - - context "and the current item has got a dev port" do - let(:udev_net) { ["KERNEL==\"eth*\"", "ATTR{dev_port}==\"0\"", "KERNELS==\"0000:08:00.0\"", "NAME=\"test0\""] } - - it "removes the dev_port from current rule if present" do - expect(Yast::LanItems.Items[0]["udev"]["net"]).to eql(udev_net) - Yast::LanItems.update_item_udev_rule!(:mac) - expect(Yast::LanItems.Items[0]["udev"]["net"]) - .to eql(["KERNEL==\"eth*\"", "ATTR{address}==\"24:be:05:ce:1e:91\"", "NAME=\"test0\""]) - end - end - end - - context "when not supported key is given" do - it "raises an ArgumentError exception" do - expect { Yast::LanItems.update_item_udev_rule!(:other) }.to raise_error(ArgumentError) - end - end - - context "when no parameters is given" do - it "uses :mac as default" do - expect(Yast::LanItems).to receive(:RemoveItemUdev).with("ATTR{dev_port}") - expect(Yast::LanItems).to receive(:ReplaceItemUdev) - - Yast::LanItems.update_item_udev_rule! - end - end -end - describe "LanItems#find_type_ifaces" do let(:mocked_items) do { @@ -511,39 +287,6 @@ def mock_items(dev_maps) end end - describe "LanItems#clear_set_hostname" do - let(:dhcp_yes_maps) do - { - "eth0" => { "DHCLIENT_SET_HOSTNAME" => "yes" } - }.freeze - end - let(:dhcp_no_maps) do - { - "eth1" => { "DHCLIENT_SET_HOSTNAME" => "no" } - }.freeze - end - let(:no_dhclient_maps) do - { "eth6" => { "BOOT" => "dhcp" } }.freeze - end - - it "clears all DHCLIENT_SET_HOSTNAME options" do - dhclient_maps = dhcp_yes_maps.merge(dhcp_no_maps) - mock_items(dhclient_maps.merge(no_dhclient_maps)) - - expect(Yast::LanItems) - .to receive(:SetDeviceMap) - .with(kind_of(Integer), "DHCLIENT_SET_HOSTNAME" => nil) - .twice - expect(Yast::LanItems) - .to receive(:SetModified) - .at_least(:once) - - ret = Yast::LanItems.clear_set_hostname - - expect(ret).to eql dhclient_maps.keys - end - end - describe "LanItems#valid_dhcp_cfg?" do def mock_dhcp_setup(ifaces, global) allow(Yast::LanItems) @@ -601,40 +344,6 @@ def mock_dhcp_setup(ifaces, global) allow(Yast::LanItems).to receive(:Items).and_return(0 => item_0) end - describe "LanItems#current_name_for" do - context "when the LanItem has not been renamed" do - it "returns the item name" do - expect(Yast::LanItems.current_name_for(0)).to eql "eth0" - end - end - - context "when the LanItem has been renamed" do - let(:renamed_to) { "new1" } - - it "returns the new name" do - expect(Yast::LanItems.current_name_for(0)).to eql "new1" - end - end - end - - describe "LanItems#colliding_item" do - it "returns nothing if no collision was found" do - expect(Yast::LanItems.colliding_item("enp0s3")).to be nil - end - - it "returns the Item index which is in collision" do - expect(Yast::LanItems.colliding_item("eth0")).to be 0 - end - - context "if some of the devices were renamed" do - let(:renamed_to) { "enp0s3" } - - it "uses the new name to detect the collision" do - expect(Yast::LanItems.colliding_item("enp0s3")).to be 0 - end - end - end - describe "LanItems.add_device_to_routing" do let(:eth0) { Y2Network::Interface.new("eth0") } let(:wlan0) { Y2Network::Interface.new("wlan0") } @@ -678,78 +387,6 @@ def mock_dhcp_setup(ifaces, global) end end - describe "LanItems.rename_current_device_in_routing" do - let(:eth0) { Y2Network::Interface.new("eth0") } - let(:wlan0) { Y2Network::Interface.new("wlan0") } - let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, wlan0]) } - let(:yast_config) do - instance_double(Y2Network::Config, interfaces: interfaces, routing: double("routing")) - end - - before do - allow(Y2Network::Config).to receive(:find).with(:yast).and_return(yast_config) - allow(Yast::LanItems).to receive(:current_name).and_return("wlan1") - end - - it "updates the list of Routing devices with current device names" do - Yast::LanItems.rename_current_device_in_routing("wlan0") - new_names = yast_config.interfaces.map(&:name) - expect(new_names).to eq(["eth0", "wlan1"]) - end - end - - describe "LanItems.remove_current_device_from_routing" do - let(:eth0) { Y2Network::Interface.new("eth0") } - let(:wlan0) { Y2Network::Interface.new("wlan0") } - let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, wlan0]) } - - let(:yast_config) do - instance_double(Y2Network::Config, interfaces: interfaces, routing: double("routing")) - end - - before do - allow(Y2Network::Config).to receive(:find).with(:yast).and_return(yast_config) - allow(Yast::LanItems).to receive(:current_name).and_return("wlan0") - end - - it "removes the device" do - Yast::LanItems.remove_current_device_from_routing - names = yast_config.interfaces.map(&:name) - expect(names).to eq(["eth0"]) - end - end - - describe "LanItems.update_routing_devices?" do - let(:eth0) { Y2Network::Interface.new("eth0") } - let(:wlan0) { Y2Network::Interface.new("wlan0") } - let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, wlan0]) } - - let(:yast_config) do - instance_double(Y2Network::Config, interfaces: interfaces, routing: double("routing")) - end - - before do - allow(Y2Network::Config).to receive(:find).with(:yast).and_return(yast_config) - allow(Yast::LanItems).to receive(:current_name).and_return(current_name) - end - - context "when there are no changes in the device names" do - let(:current_name) { "eth0" } - - it "returns false" do - expect(Yast::LanItems.update_routing_devices?).to eql(false) - end - end - - context "when some interface have been renaming and Routing device names differs" do - let(:current_name) { "eth1" } - - it "returns true" do - expect(Yast::LanItems.update_routing_devices?).to eql(true) - end - end - end - describe "LanItems.move_routes" do let(:routing) { Y2Network::Routing.new(tables: [table1]) } let(:table1) { Y2Network::RoutingTable.new(routes) } @@ -761,8 +398,9 @@ def mock_dhcp_setup(ifaces, global) end let(:eth0) { Y2Network::Interface.new("eth0") } let(:br0) { Y2Network::Interface.new("br0") } + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, br0]) } let(:yast_config) do - instance_double(Y2Network::Config, interfaces: [eth0, br0], routing: routing) + instance_double(Y2Network::Config, interfaces: interfaces, routing: routing) end before do diff --git a/test/lan_items_read_test.rb b/test/lan_items_read_test.rb deleted file mode 100755 index f6684c0d9..000000000 --- a/test/lan_items_read_test.rb +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -require "yast" -Yast.import "LanItems" - -describe "LanItemsClass" do - subject { Yast::LanItems } - - describe "#SetDeviceVars" do - let(:defaults) do - { - "WIRELESS_KEY" => "", - "WIRELESS_KEY_0" => "", - "WIRELESS_KEY_1" => "", - "WIRELESS_KEY_2" => "", - "WIRELESS_KEY_3" => "" - } - end - - it "reads value from sysconfig data" do - subject.SetDeviceVars({ "BOOTPROTO" => "dhcp8" }, "BOOTPROTO" => "dhcp7") - expect(subject.bootproto).to eq "dhcp8" - end - - it "reads value from default data" do - subject.SetDeviceVars({}, "BOOTPROTO" => "dhcp7") - expect(subject.bootproto).to eq "dhcp7" - end - - it "reads nil if neither hash specifies the data" do - subject.SetDeviceVars({}, {}) - expect(subject.bootproto).to eq nil - end - - it "converts set_default_route" do - subject.SetDeviceVars({ "DHCLIENT_SET_DEFAULT_ROUTE" => "yes" }, defaults) - expect(subject.set_default_route).to eq true - - subject.SetDeviceVars({ "DHCLIENT_SET_DEFAULT_ROUTE" => "no" }, defaults) - expect(subject.set_default_route).to eq false - - subject.SetDeviceVars({}, defaults) - expect(subject.set_default_route).to eq nil - - subject.SetDeviceVars({ "DHCLIENT_SET_DEFAULT_ROUTE" => "unrecognized" }, defaults) - expect(subject.set_default_route).to eq nil - end - - it "converts wl_power" do - subject.SetDeviceVars({ "WIRELESS_POWER" => "yes" }, defaults) - expect(subject.wl_power).to eq true - end - - it "makes wl_key a 4-tuple when 1 key is specified" do - subject.SetDeviceVars({ "WIRELESS_KEY" => "k0" }, defaults) - expect(subject.wl_key).to eq ["k0", "", "", ""] - end - - it "makes wl_key a 4-tuple when 2 keys are specified" do - subject.SetDeviceVars({ "WIRELESS_KEY_0" => "k00", "WIRELESS_KEY_1" => "k01" }, defaults) - expect(subject.wl_key).to eq ["k00", "k01", "", ""] - end - - it "makes wl_wpa_eap a hash, with renamed kes" do - subject.SetDeviceVars({ - "WIRELESS_EAP_MODE" => "foo", - "WIRELESS_PEAP_VERSION" => "bar" - }, {}) - expect(subject.wl_wpa_eap["WPA_EAP_MODE"]).to eq "foo" - expect(subject.wl_wpa_eap["WPA_EAP_PEAP_VERSION"]).to eq "bar" - end - end - - describe "#SetS390Vars" do - let(:defaults) { {} } - - it "converts qeth_layer2" do - expect(Yast::Arch).to receive(:s390).and_return true - - subject.SetS390Vars({ "QETH_LAYER2" => "yes" }, defaults) - expect(subject.qeth_layer2).to eq true - end - end -end diff --git a/test/lan_items_rollback_test.rb b/test/lan_items_rollback_test.rb index 1444a2f8e..24296e8d6 100755 --- a/test/lan_items_rollback_test.rb +++ b/test/lan_items_rollback_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/lan_items_summary_test.rb b/test/lan_items_summary_test.rb index a4d85d637..dd2e5d70e 100755 --- a/test/lan_items_summary_test.rb +++ b/test/lan_items_summary_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "network/lan_items_summary" @@ -42,23 +61,6 @@ .and_return(instance_double(Y2Network::Config, interfaces: interfaces)) end - describe "#default" do - it "returns a Richtext summary of the configured interfaces" do - expect(subject.default) - .to eql "
    " \ - "
  • eth0
    DHCP

  • " \ - "
  • eth1
    NONE

  • " \ - "
  • br0
    STATIC

  • " \ - "
" - end - - it "returns Summary.NotConfigured in case of not configured interfaces" do - allow(Yast::LanItems).to receive(:IsItemConfigured).and_return(false) - - expect(subject.default).to eql Yast::Summary.NotConfigured - end - end - describe "#proposal" do let(:interfaces) { instance_double(Y2Network::InterfacesCollection) } let(:br0) { instance_double(Y2Network::Interface, name: "br0", type: Y2Network::InterfaceType::BRIDGE) } diff --git a/test/lan_test.rb b/test/lan_test.rb index 980ea0ab0..e1a6ac62c 100755 --- a/test/lan_test.rb +++ b/test/lan_test.rb @@ -1,20 +1,37 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" require "y2network/config" require "y2network/routing" +require "y2network/interface_config_builder" Yast.import "Lan" describe "LanClass" do subject { Yast::Lan } - let(:system_config) { instance_double(Y2Network::Config, "System") } - before do - Yast::Lan.clear_configs - end + let(:system_config) { Y2Network::Config.new(interfaces: [], source: :sysconfig) } describe "#Packages" do before(:each) do @@ -143,11 +160,11 @@ it "flushes internal state of LanItems correctly when asked for reset" do expect(Yast::Lan.Import(ay_profile)).to be true - expect(Yast::LanItems.GetModified).to be true + expect(Yast::Lan.GetModified).to be true expect(Yast::LanItems.Items).not_to be_empty expect(Yast::Lan.Import({})).to be true - expect(Yast::LanItems.GetModified).to be false + expect(Yast::Lan.GetModified).to be false expect(Yast::LanItems.Items).to be_empty end @@ -159,56 +176,68 @@ end describe "#Modified" do - def reset_modification_statuses - allow(Yast::LanItems).to receive(:GetModified).and_return false - allow(Yast::DNS).to receive(:modified).and_return false + let(:yast_config) { system_config.copy } + + before do allow(Yast::NetworkConfig).to receive(:Modified).and_return false allow(Yast::NetworkService).to receive(:Modified).and_return false allow(Yast::Host).to receive(:GetModified).and_return false + + Yast::Lan.add_config(:system, system_config) + Yast::Lan.add_config(:yast, yast_config) end - def expect_modification_succeedes(modname, method) - reset_modification_statuses + context "when the configuration was not changed" do + it "returns false" do + expect(Yast::Lan.Modified).to eq(false) + end + end - allow(modname) - .to receive(method) - .and_return true + context "when the configuration was changed" do + before do + yast_config.routing.forward_ipv4 = !system_config.routing.forward_ipv4 + end - expect(modname.send(method)).to be true - expect(Yast::Lan.Modified).to be true + it "returns true" do + expect(Yast::Lan.Modified).to eq(true) + end end - it "returns true when LanItems module was modified" do - expect_modification_succeedes(Yast::LanItems, :GetModified) - end + context "when the NetworkConfig module was modified" do + before { allow(Yast::NetworkConfig).to receive(:Modified).and_return(true) } - let(:config) do - Y2Network::Config.new(interfaces: [], routing: routing, source: :sysconfig) + it "returns true" do + expect(Yast::Lan.Modified).to eq(true) + end end - let(:routing) { Y2Network::Routing.new(tables: []) } - it "returns true when Routing module was modified" do - Yast::Lan.add_config(:system, config) - yast_config = config.copy - yast_config.routing.forward_ipv4 = !config.routing.forward_ipv4 - Yast::Lan.add_config(:yast, yast_config) + context "when the NetworkService module was modified" do + before { allow(Yast::NetworkService).to receive(:Modified).and_return(true) } - reset_modification_statuses - expect(Yast::Lan.Modified).to eq(true) - Yast::Lan.clear_configs + it "returns true" do + expect(Yast::Lan.Modified).to eq(true) + end end - it "returns true when NetworkConfig module was modified" do - expect_modification_succeedes(Yast::NetworkConfig, :Modified) + context "when the Host module was modified" do + before { allow(Yast::Host).to receive(:GetModified).and_return(true) } + + it "returns true" do + expect(Yast::Lan.Modified).to eq(true) + end end + end - it "returns true when NetworkService module was modified" do - expect_modification_succeedes(Yast::NetworkService, :Modified) + describe "#SetModified" do + before do + allow(Yast::NetworkConfig).to receive(:Modified).and_return false + allow(Yast::NetworkService).to receive(:Modified).and_return false + allow(Yast::Host).to receive(:GetModified).and_return false end - it "returns false when no module was modified" do - reset_modification_statuses - expect(Yast::Lan.Modified).to be false + it "changes Modified to true" do + subject.main + expect { subject.SetModified }.to change { subject.Modified }.from(false).to(true) end end @@ -227,121 +256,10 @@ def expect_modification_succeedes(modname, method) end end - describe "#IfcfgsToSkipVirtualizedProposal" do - let(:items) do - { - "0" => { "ifcfg" => "bond0" }, - "1" => { "ifcfg" => "br0" }, - "2" => { "ifcfg" => "eth0" }, - "3" => { "ifcfg" => "eth1" }, - "4" => { "ifcfg" => "wlan0" }, - "5" => { "ifcfg" => "wlan1" } - } - end - - let(:current_interface) do - { - "ONBOOT" => "yes", - "BOOTPROTO" => "dhcp", - "DEVICE" => "br1", - "BRIDGE" => "yes", - "BRIDGE_PORTS" => "eth1", - "BRIDGE_STP" => "off", - "IPADDR" => "10.0.0.1", - "NETMASK" => "255.255.255.0" - } - end - - context "when various interfaces are present in the system" do - let(:interfaces) { Y2Network::InterfacesCollection.new([]) } - - before do - allow(Yast::NetworkInterfaces).to receive(:GetType).with("br0").and_return("br") - allow(Yast::NetworkInterfaces).to receive(:GetType).with("bond0").and_return("bond") - allow(Yast::NetworkInterfaces).to receive(:GetType).with("eth0").and_return("eth") - allow(Yast::NetworkInterfaces).to receive(:GetType).with("eth1").and_return("eth") - allow(Yast::NetworkInterfaces).to receive(:GetType).with("wlan0").and_return("usb") - allow(Yast::NetworkInterfaces).to receive(:GetType).with("wlan1").and_return("wlan") - allow(Yast::NetworkInterfaces).to receive(:Current).and_return(current_interface) - allow(Yast::NetworkInterfaces).to receive(:GetValue).and_return(nil) - allow(Yast::LanItems).to receive(:Items).and_return(items) - allow(Y2Network::Config) - .to receive(:find) - .and_return(instance_double(Y2Network::Config, interfaces: interfaces)) - end - - context "and one of them is a bridge" do - it "returns an array containining the bridge interface" do - (expect Yast::Lan.IfcfgsToSkipVirtualizedProposal).to include("br0") - end - - it "returns an array containing the bridged interfaces" do - allow(interfaces).to receive(:bridge_slaves).and_return(["eth1"]) - - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).to include("eth1") - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).to_not include("eth0") - end - end - - context "and one of them is a bond" do - let(:current_interface) do - { - "BOOTPROTO" => "static", - "BONDING_MASTER" => "yes", - "DEVICE" => "bond0", - "BONDING_SLAVE" => "eth0" - } - end - - it "returns an array containing the bonded interfaces" do - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).not_to include("bond0") - end - end - - context "and one of them is an usb or a wlan interface" do - it "returns an array containing the interface" do - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).to include("wlan0", "wlan1") - end - end - - context "and the interface startmode is 'nfsroot'" do - it "returns an array containing the interface" do - allow(Yast::NetworkInterfaces).to receive(:GetValue) - .with("eth0", "STARTMODE").and_return("nfsroot") - - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).to include("eth0") - end - end - - context "and all the interfaces are bridgeable" do - let(:current_item) do - { - "BOOTPROTO" => "dhcp", - "STARTMODE" => "auto" - } - end - it "returns an empty array" do - allow(Yast::NetworkInterfaces).to receive(:GetType).and_return("eth") - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).to eql([]) - end - end - end - - context "there is no interfaces in the system" do - it "returns an empty array" do - allow(Yast::LanItems).to receive(:Items).and_return({}) - expect(Yast::Lan.IfcfgsToSkipVirtualizedProposal).to eql([]) - end - end - end - - describe "#ProposeVirtualized" do + xdescribe "#ProposeVirtualized" do before do allow(Yast::LanItems).to receive(:IsCurrentConfigured).and_return(true) - allow(Yast::LanItems).to receive(:ProposeItem) - allow(Yast::Lan).to receive(:configure_as_bridge!) - allow(Yast::Lan).to receive(:configure_as_bridge_port) allow(Yast::Lan).to receive(:refresh_lan_items) allow(Yast::LanItems).to receive(:Items) @@ -355,7 +273,6 @@ def expect_modification_succeedes(modname, method) it "does not propose the interface" do allow(Yast::LanItems).to receive(:IsCurrentConfigured).and_return(false) - expect(Yast::LanItems).not_to receive(:ProposeItem) allow(Y2Network::Config) .to receive(:find) .and_return(instance_double(Y2Network::Config, interfaces: interfaces)) @@ -367,7 +284,7 @@ def expect_modification_succeedes(modname, method) context "when an interface is bridgeable" do before do allow(Yast::Lan).to receive(:connected_and_bridgeable?) - .with(anything, 0, anything).and_return(true) + .with(anything, 0).and_return(true) allow(Yast::Lan).to receive(:connected_and_bridgeable?) .with(anything, 1, anything).and_return(false) allow(Yast::Lan).to receive(:connected_and_bridgeable?) @@ -376,14 +293,12 @@ def expect_modification_succeedes(modname, method) it "does not configure the interface if it is not connected" do allow(Yast::Lan).to receive(:connected_and_bridgeable?).and_return(false) - expect(Yast::LanItems).not_to receive(:ProposeItem) - Yast::Lan.ProposeVirtualized + expect { Yast::Lan.ProposeVirtualized }.to_not change { yast_config.connections.size } end it "configures the interface with defaults before anything if not configured" do allow(Yast::LanItems).to receive(:IsItemConfigured).and_return(false) - expect(Yast::LanItems).to receive(:ProposeItem) Yast::Lan.ProposeVirtualized end @@ -513,6 +428,10 @@ def expect_modification_succeedes(modname, method) context "when wicked is in use" do let(:nm_enabled) { false } + before do + allow(Yast::Lan).to receive(:ReadWithCacheNoGUI) + end + it "reads the current network configuration" do expect(Yast::Lan).to receive(:ReadWithCacheNoGUI) subject.dhcp_ntp_servers @@ -552,9 +471,8 @@ def expect_modification_succeedes(modname, method) end it "cleans the configurations list" do - expect { subject.clear_configs } - .to change { subject.find_config(:system) } - .from(system_config).to(nil) + subject.clear_configs + expect(subject.find_config(:system)).to be_nil end end diff --git a/test/lan_udev_auto_test.rb b/test/lan_udev_auto_test.rb index c3e7f2ff7..be461ae45 100755 --- a/test/lan_udev_auto_test.rb +++ b/test/lan_udev_auto_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/link_handlers_test.rb b/test/link_handlers_test.rb index 932904405..30f8ea41a 100755 --- a/test/link_handlers_test.rb +++ b/test/link_handlers_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/netcard_probe_helper.rb b/test/netcard_probe_helper.rb index 2b463a7a2..7df6eb515 100644 --- a/test/netcard_probe_helper.rb +++ b/test/netcard_probe_helper.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + def probe_netcard [ { diff --git a/test/netcard_test.rb b/test/netcard_test.rb index 2f5f7083c..5cb57f3f9 100755 --- a/test/netcard_test.rb +++ b/test/netcard_test.rb @@ -1,5 +1,24 @@ #! /usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" HWINFO_DEVICE_DESC = "Intel Ethernet controller".freeze @@ -72,27 +91,6 @@ Yast.import "LanItems" -describe "When querying netcard device name" do - before(:each) do - @lan_items = Yast::LanItems - @lan_items.main - - # mocking only neccessary parts of Yast::LanItems so we need not to call - # and mock inputs for Yast::LanItems.Read here - @lan_items.Items = Yast.deep_copy(MOCKED_ITEMS) - end - - it "returns empty list when querying device name with nil or empty input" do - [nil, []].each { |i| expect(@lan_items.GetDeviceNames(i)).to be_empty } - end - - it "can return list of device names available in the system" do - expected_names = ["bond0", "br0", "eth1", "eth11", "enp0s3", "tap0", "tun0"].sort - - expect(@lan_items.GetNetcardNames.sort).to eq expected_names - end -end - class NetworkComplexIncludeClass < Yast::Module def initialize Yast.include self, "network/complex.rb" @@ -138,91 +136,6 @@ def initialize end end -describe "LanItemsClass#BuildLanOverview" do - let(:interfaces) { Y2Network::InterfacesCollection.new([]) } - - before(:each) do - @lan_items = Yast::LanItems - @lan_items.main - @lan_items.Items = Yast.deep_copy(MOCKED_ITEMS) - - allow(Y2Network::Config) - .to receive(:find) - .and_return(instance_double(Y2Network::Config, interfaces: interfaces)) - end - - it "returns description and uses custom name if present" do - allow(@lan_items).to receive(:GetDeviceMap) { { "NAME" => "Custom name" } } - - @lan_items.BuildLanOverview - @lan_items.Items.each_pair do |_key, value| - # it is not issue, really same index two times - desc = value["table_descr"]["table_descr"].first - - if value["ifcfg"] - expect(desc).to eql "Custom name" - else - expect(desc).not_to be_empty - end - end - end - - it "returns description and uses type based name if hwinfo is not present" do - allow(@lan_items).to receive(:GetDeviceMap) { { "NAME" => "" } } - - @lan_items.BuildLanOverview - @lan_items.Items.each_pair do |_key, value| - desc = value["table_descr"]["table_descr"].first - - if !value["hwinfo"] - dev_name = value["ifcfg"].to_s - dev_type = Yast::NetworkInterfaces.GetType(dev_name) - expected_dev_desc = Yast::NetworkInterfaces.GetDevTypeDescription(dev_type, true) - else - expected_dev_desc = value["hwinfo"]["name"] - end - - expect(desc).not_to be_empty - expect(desc).to eql expected_dev_desc - end - end -end - -describe "LanItemsClass#DeleteItem" do - before(:each) do - @lan_items = Yast::LanItems - @lan_items.main - @lan_items.Items = Yast.deep_copy(MOCKED_ITEMS) - end - - it "removes an existing item" do - before_items = nil - - while before_items != @lan_items.Items && !@lan_items.Items.empty? - @lan_items.current = 0 - - item_name = @lan_items.GetCurrentName - before_items = @lan_items.Items - - @lan_items.DeleteItem - - expect(@lan_items.FindAndSelect(item_name)).to be false - end - end - - it "removes only the configuration if the item has hwinfo" do - before_size = @lan_items.Items.size - item_name = "enp0s3" - - expect(@lan_items.FindAndSelect(item_name)).to be true - - @lan_items.DeleteItem - - expect(@lan_items.FindAndSelect(item_name)).to be false - expect(@lan_items.Items.size).to eql before_size - end -end - describe "LanItemsClass#GetItemName" do before(:each) do @lan_items = Yast::LanItems @@ -243,21 +156,6 @@ def initialize end end -describe "LanItemsClass#SetItemName" do - subject { Yast::LanItems } - let(:new_name) { "new_name" } - - # this test covers bnc#914833 - it "doesn't try to update udev rules when none exists for the item" do - allow(subject) - .to receive(:Items) - .and_return(MOCKED_ITEMS) - - item_id = subject.Items.find { |_k, v| !v.key?("udev") }.first - expect(subject.SetItemName(item_id, new_name)).to eql new_name - end -end - describe "LanItemsClass#FindAndSelect" do before(:each) do @lan_items = Yast::LanItems diff --git a/test/network/edit_nic_name_test.rb b/test/network/edit_nic_name_test.rb deleted file mode 100755 index 9082f3ecc..000000000 --- a/test/network/edit_nic_name_test.rb +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "../test_helper" - -require "yast" -require "network/edit_nic_name" - -require "y2network/route" -require "y2network/routing" -require "y2network/routing_table" -require "y2network/interfaces_collection" -require "y2network/interface" -require "y2network/config" - -Yast.import "LanItems" - -describe Yast::EditNicName do - let(:subject) { described_class.new } - let(:current_name) { "spec0" } - let(:new_name) { "new1" } - let(:existing_new_name) { "existing_new_name" } - let(:interface_hwinfo) { { "dev_name" => current_name, "permanent_mac" => "00:01:02:03:04:05" } } - - let(:route1) { Y2Network::Route.new } - let(:table1) { Y2Network::RoutingTable.new(routes: [route1]) } - let(:routing) { Y2Network::Routing.new(tables: table1) } - let(:iface) { Y2Network::Interface.new(current_name) } - let(:ifaces) { Y2Network::InterfacesCollection.new([iface]) } - let(:yast_config) do - Y2Network::Config.new(interfaces: ifaces, routing: routing, source: :sysconfig) - end - - before do - allow(Y2Network::Config).to receive(:find).and_return(yast_config) - end - - describe "#run" do - # general mocking stuff is placed here - before(:each) do - # NetworkInterfaces are too low level. Everything needed should be mocked - stub_const("NetworkInterfaces", double(adapt_old_config!: nil)) - - # mock devices configuration - allow(Yast::LanItems).to receive(:ReadHardware).and_return([interface_hwinfo]) - allow(Yast::LanItems).to receive(:getNetworkInterfaces).and_return([current_name]) - allow(Yast::LanItems).to receive(:GetItemUdev) { "" } - allow(Yast::LanItems).to receive(:current_udev_name).and_return(current_name) - allow(Yast::LanItems).to receive(:GetItemUdev).with("ATTR{address}") { "00:01:02:03:04:05" } - allow(Yast::LanItems).to receive(:GetNetcardNames).and_return([current_name]) - - # LanItems initialization - - Yast::LanItems.Read - Yast::LanItems.FindAndSelect(current_name) - end - - context "when closed without any change" do - before(:each) do - # emulate Yast::UI work - allow(Yast::UI).to receive(:QueryWidget).with(:dev_name, :Value) { current_name } - allow(Yast::UI).to receive(:QueryWidget).with(:udev_type, :CurrentButton) { :mac } - allow(Yast::UI).to receive(:UserInput) { :ok } - allow(Yast::LanItems).to receive(:update_item_udev_rule!) - end - - it "returns current name when used Ok button" do - expect(subject.run).to be_equal current_name - end - - it "returns current name when used Cancel button" do - allow(Yast::UI).to receive(:UserInput) { :cancel } - - expect(subject.run).to be_equal current_name - end - end - - context "when name changed" do - before(:each) do - # emulate Yast::UI work - allow(Yast::UI).to receive(:QueryWidget).with(:dev_name, :Value) { new_name } - allow(Yast::UI).to receive(:QueryWidget).with(:udev_type, :CurrentButton) { :mac } - allow(Yast::UI).to receive(:UserInput) { :ok } - allow(subject).to receive(:update_routes?).and_return(false) - end - - context "and closed confirming the changes" do - it "returns the new name" do - expect(subject.run).to be_equal new_name - end - - it "asks for new user input when name already exists" do - allow(Yast::UI).to receive(:QueryWidget) - .with(:dev_name, :Value).and_return(existing_new_name, new_name) - expect(subject).to receive(:CheckUdevNicName).with(existing_new_name).and_return(false) - expect(subject).to receive(:CheckUdevNicName).with(new_name).and_return(true) - expect(Yast::UI).to receive(:SetFocus) - expect(Yast::LanItems).to receive(:rename).with(new_name) - subject.run - end - - it "updates the Routing devices list with the new name" do - expect(Yast::LanItems).to receive(:rename_current_device_in_routing) - .with("spec0") - subject.run - end - - context "but used the same matching udev key" do - it "does not touch the current udev rule" do - expect(Yast::LanItems).to_not receive(:update_item_udev_rule!) - end - end - - xcontext "and there are some routes referencing the previous name" do - before do - allow(Yast::Routing).to receive(:device_routes?).with(current_name).and_return(true) - expect(subject).to receive(:update_routes?).with(current_name).and_call_original - allow(Yast::LanItems).to receive(:update_routes!).with(current_name) - end - - it "asks the user about updating the routes device name" do - expect(Yast::Popup).to receive(:YesNoHeadline) - - subject.run - end - - it "updates the routes if the user accepts to do it" do - expect(Yast::Popup).to receive(:YesNoHeadline).and_return(true) - expect(Yast::LanItems).to receive(:update_routes!).with(current_name) - - subject.run - end - - it "does not touch the routes if the user does not want to touch them" do - expect(Yast::Popup).to receive(:YesNoHeadline).and_return(false) - expect(Yast::LanItems).to_not receive(:update_routes!) - subject.run - end - end - - context "having modified the matching udev key" do - before(:each) do - # emulate UI work - allow(Yast::UI).to receive(:QueryWidget).with(:dev_name, :Value) { current_name } - allow(Yast::UI).to receive(:QueryWidget).with(:udev_type, :CurrentButton) { :bus_id } - end - - it "updates the current udev rule with the key used" do - expect(Yast::LanItems).to_not receive(:update_item_udev_rule!).with(:bus_id) - end - end - end - - context "and closed canceling the changes" do - it "returns current name when used Cancel button" do - allow(Yast::UI).to receive(:UserInput) { :cancel } - - expect(subject.run).to be_equal current_name - end - end - end - end -end diff --git a/test/network_autoconfiguration_test.rb b/test/network_autoconfiguration_test.rb index 96b55d09f..3cd569cfe 100755 --- a/test/network_autoconfiguration_test.rb +++ b/test/network_autoconfiguration_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" @@ -47,45 +66,21 @@ def probe_netcard_factory(num) end describe Yast::NetworkAutoconfiguration do + let(:yast_config) { Y2Network::Config.new(source: :sysconfig) } + let(:system_config) { yast_config.copy } + + before do + Y2Network::Config.add(:yast, yast_config) + Y2Network::Config.add(:system, system_config) + allow(yast_config).to receive(:write) + allow(Yast::Lan).to receive(:Read) + end + describe "it sets DHCLIENT_SET_DEFAULT_ROUTE properly" do let(:instance) { Yast::NetworkAutoconfiguration.instance } let(:network_interfaces) { double("NetworkInterfaces") } before(:each) do - ifcfg_files = SectionKeyValue.new - - # network configs - allow(Yast::SCR).to receive(:Dir) do |path| - case path.to_s - when ".network.section" - ifcfg_files.sections - when /^\.network\.value\."(eth\d+)"$/ - ifcfg_files.keys(Regexp.last_match(1)) - when ".modules.options", ".etc.install_inf" - [] - else - raise "Unexpected Dir #{path}" - end - end - - allow(Yast::SCR).to receive(:Read) do |path| - if path.to_s =~ /^\.network\.value\."(eth\d+)".(.*)/ - next ifcfg_files.get(Regexp.last_match(1), Regexp.last_match(2)) - end - - raise "Unexpected Read #{path}" - end - - allow(Yast::SCR).to receive(:Write) do |path, value| - if path.to_s =~ /^\.network\.value\."(eth\d+)".(.*)/ - ifcfg_files.set(Regexp.last_match(1), Regexp.last_match(2), value) - elsif path.to_s == ".network" && value.nil? - true - else - raise "Unexpected Write #{path}, #{value}" - end - end - # stub NetworkInterfaces, apart from the ifcfgs allow(Yast::NetworkInterfaces) .to receive(:CleanHotplugSymlink) @@ -120,13 +115,13 @@ def probe_netcard_factory(num) # because SCR multiplexes too much and the matchers get confused. # Hardware detection - expect(Yast::SCR) + allow(Yast::SCR) .to receive(:Read) .with(path(".probe.netcard")) .and_return([probe_netcard_factory(0), probe_netcard_factory(1)]) # link status - expect(Yast::SCR) + allow(Yast::SCR) .to receive(:Read) .with(path(".target.string"), %r{/sys/class/net/.*/carrier}) .twice @@ -136,28 +131,12 @@ def probe_netcard_factory(num) allow(Yast::Arch).to receive(:architecture).and_return "x86_64" allow(Yast::Confirm).to receive(:Detection).and_return true - expect(Yast::SCR) - .to receive(:Read) - .with(path(".etc.install_inf.BrokenModules")) - .and_return "" - expect(Yast::SCR) - .to receive(:Read) - .with(path(".udev_persistent.net")) - .and_return({}) - expect(Yast::SCR) - .to receive(:Read) - .with(path(".udev_persistent.drivers")) - .and_return({}) allow(Yast::NetworkInterfaces).to receive(:Write).and_call_original - # reinit network interfaces to avoid trash from other tests - Yast::NetworkInterfaces.main end it "configures just one NIC to have a default route" do expect { instance.configure_dhcp }.to_not raise_error - result = Yast::NetworkInterfaces.FilterDevices("") - expect(result["eth"]["eth0"]["DHCLIENT_SET_DEFAULT_ROUTE"]).to eq "yes" - expect(result["eth"]["eth1"]["DHCLIENT_SET_DEFAULT_ROUTE"]).to eq nil + # TODO: write it when we can set up dhcp default route end end @@ -167,18 +146,12 @@ def probe_netcard_factory(num) let(:instance) { Yast::NetworkAutoconfiguration.instance } it "returns true if any of available interfaces has configuration and is up" do - allow(Yast::LanItems) - .to receive(:Read) - .and_return(true) - allow(Yast::LanItems) - .to receive(:GetNetcardNames) - .and_return([IFACE, "enp0s3", "br7"]) - allow(Yast::NetworkInterfaces) - .to receive(:adapt_old_config!) - allow(Yast::NetworkInterfaces) - .to receive(:Check) - .with(IFACE) - .and_return(true) + allow(Yast::Lan).to receive(:yast_config) + .and_return(Y2Network::Config.new( + interfaces: Y2Network::InterfacesCollection.new([double(name: IFACE)]), + connections: Y2Network::ConnectionConfigsCollection.new([double(name: IFACE)]), + source: :testing + )) allow(Yast::SCR) .to receive(:Execute) .and_return(0) @@ -198,18 +171,19 @@ def probe_netcard_factory(num) end let(:eth0) { Y2Network::Interface.new("eth0") } let(:br0) { Y2Network::Interface.new("br0") } + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, br0]) } let(:yast_config) do - Y2Network::Config.new(interfaces: [eth0, br0], routing: routing, source: :testing) + Y2Network::Config.new(interfaces: interfaces, routing: routing, source: :testing) end - let(:system_config) { yast_config.copy } let(:instance) { Yast::NetworkAutoconfiguration.instance } let(:proposal) { false } let(:eth0_profile) do { - "BOOTPROTO" => "static", - "IPADDR" => "192.168.122.213", - "NETMASK" => "255.255.255.0", - "STARTMODE" => "auto" + "bootproto" => "static", + "ipaddr" => "192.168.122.213", + "netmask" => "255.255.255.0", + "startmode" => "auto", + "device" => "eth0" } end let(:routes_profile) do @@ -227,12 +201,12 @@ def probe_netcard_factory(num) allow(Y2Network::Config).to receive(:find).with(:yast).and_return(yast_config) allow(Y2Network::Config).to receive(:find).with(:system).and_return(system_config) allow(instance).to receive(:virtual_proposal_required?).and_return(proposal) - allow(Yast::LanItems).to receive(:write) - allow(Yast::LanItems).to receive(:Read) allow(yast_config).to receive(:write) allow(Yast::Lan).to receive(:connected_and_bridgeable?).and_return(true) - Yast::Lan.Import("devices" => { "eth" => { "eth0" => eth0_profile } }, - "routing" => { "routes" => routes_profile }) + allow(Yast::PackageSystem).to receive(:Installed).and_return(true) + Yast::Lan.Import( + "routing" => { "routes" => routes_profile } + ) end context "when the proposal is not required" do @@ -243,21 +217,16 @@ def probe_netcard_factory(num) end context "when the proposal is required" do + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0]) } let(:proposal) { true } - after(:each) do - # some methods might have sideeffects - clen them :-/ - Yast::NetworkInterfaces.Devices.reject! { |k, _| k == "br" } - end - it "creates the virtulization proposal config" do expect(Yast::Lan).to receive(:ProposeVirtualized).and_call_original - expect { instance.configure_virtuals }.to change { Yast::NetworkInterfaces.Devices.keys.size }.from(1).to(2) - expect(Yast::NetworkInterfaces.Devices["br"]["br0"]).to include(eth0_profile.merge("BRIDGE_PORTS" => "eth0")) + expect { instance.configure_virtuals }.to change { yast_config.connections.size }.from(0).to(2) end it "writes the configuration of the interfaces" do - expect(Yast::LanItems).to receive(:write) + expect(Yast::Lan.yast_config).to receive(:write) instance.configure_virtuals end diff --git a/test/network_autoyast_test.rb b/test/network_autoyast_test.rb index f2a9676a5..122c6acc6 100755 --- a/test/network_autoyast_test.rb +++ b/test/network_autoyast_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "network/network_autoyast" @@ -250,318 +269,6 @@ def keep_install_network_value(value) end end - context "When AY profile contains old style name" do - let(:ay_old_id) do - { - "interfaces" => [{ "device" => "eth-bus-0.0.1111" }] - } - end - let(:ay_old_mac) do - { - "interfaces" => [{ "device" => "eth-id-00:11:22:33:44:55" }] - } - end - let(:ay_both_vers) do - { "interfaces" => ay_old_id["interfaces"] + [{ "device" => "eth0" }] } - end - - describe "#createUdevFromIfaceName" do - Yast.import "LanItems" - - subject(:lan_udev_auto) { Yast::LanItems } - - let(:ay_interfaces) { ay_both_vers["interfaces"] + ay_old_mac["interfaces"] } - - before(:each) do - allow(Yast::LanItems).to receive(:getDeviceName).and_return("eth0") - end - - it "returns empty list when no interfaces are provided" do - expect(lan_udev_auto.createUdevFromIfaceName(nil)).to be_empty - expect(lan_udev_auto.createUdevFromIfaceName([])).to be_empty - end - - it "do not modify list of interfaces" do - ifaces = ay_interfaces - - lan_udev_auto.send(:createUdevFromIfaceName, ay_interfaces) - - # note that this function originally filtered non old style interfaces out. - expect(ifaces).to eql ay_interfaces - end - - it "updates udev rules list according old style name" do - udev_list = lan_udev_auto.send(:createUdevFromIfaceName, ay_both_vers["interfaces"]) - - expect(udev_list.first["rule"]).to eql "KERNELS" - expect(udev_list.first["value"]).to eql "0.0.1111" - expect(udev_list.first["name"]).to eql "eth0" - end - end - end - - context "When AY profile doesn't contain old style name" do - let(:ay_only_new) do - { - "interfaces" => [{ "device" => "eth0" }] - } - end - - describe "#createUdevFromIfaceName" do - subject(:lan_udev_auto) { Yast::LanItems } - - it "returns no udev rules" do - ifaces = ay_only_new["interfaces"] - - expect(lan_udev_auto.send(:createUdevFromIfaceName, ifaces)).to be_empty - end - end - end - - describe "#valid_rename_udev_rule?" do - it "fails when the rule do not contain new name" do - rule = { "rule" => "ATTR{address}", "value" => "00:02:29:69:d3:63" } - expect(network_autoyast.send(:valid_rename_udev_rule?, rule)).to be false - expect(network_autoyast.send(:valid_rename_udev_rule?, rule["name"] = "")).to be false - end - - it "fails when the rule do not contain dev attribute" do - rule = { "name" => "eth0", "value" => "00:02:29:69:d3:63" } - expect(network_autoyast.send(:valid_rename_udev_rule?, rule)).to be false - expect(network_autoyast.send(:valid_rename_udev_rule?, rule["rule"] = "")).to be false - end - - it "fails when the rule do not contain dev attribute's value" do - rule = { "name" => "eth0", "rule" => "ATTR{address}" } - expect(network_autoyast.send(:valid_rename_udev_rule?, rule)).to be false - expect(network_autoyast.send(:valid_rename_udev_rule?, rule["value"] = "")).to be false - end - - it "succeedes for complete rule" do - complete_rule = { - "name" => "eth0", - "rule" => "ATTR{address}", - "value" => "00:02:29:69:d3:63" - } - 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", "udev" => { "net" => ["ATTR{address}==\"24:be:05:ce:1e:91\"", "KERNEL==\"eth*\"", "NAME=\"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 - - context "When creating udev rules based on the AY profile" do - def mock_lan_item(renamed_to: nil) - allow(Yast::LanItems) - .to receive(:Items) - .and_return( - 0 => { - "ifcfg" => "eth0", - "renamed_to" => renamed_to, - "udev" => { - "net" => [ - "ATTR{address}==\"24:be:05:ce:1e:91\"", - "NAME=\"#{renamed_to}\"" - ] - } - } - ) - end - - describe "#assign_udevs_to_devs" do - Yast.import "LanItems" - - let(:udev_rules) do - [ - { - "name" => "eth1", - "rule" => "KERNELS", - "value" => "0000:01:00.0" - }, - { - "name" => "eth3", - "rule" => "KERNELS", - "value" => "0000:01:00.4" - }, - { - "name" => "eth0", - "rule" => "KERNELS", - "value" => "0000:01:00.2" - } - ] - end - - let(:udev_mac_rules) do - [ - { - "name" => "eth1", - "rule" => "ATTR{address}", - "value" => "00:00:00:00:00:00" - }, - { - "name" => "eth3", - "rule" => "ATTR{address}", - "value" => "00:00:00:00:00:01" - }, - { - "name" => "eth0", - "rule" => "ATTR{address}", - "value" => "00:00:00:00:00:02" - } - ] - end - - let(:persistent_udevs) do - { - "eth0" => [ - "KERNELS==\"0000:01:00.0\"", - "NAME=eth0" - ], - "eth1" => [ - "KERNELS==\"0000:01:00.1\"", - "NAME=eth1" - ], - "eth2" => [ - "KERNELS==\"0000:01:00.2\"", - "NAME=eth2" - ] - } - end - - let(:hw_netcard) do - [ - { - "dev_name" => "eth0", - "busid" => "0000:01:00.0", - "mac" => "00:00:00:00:00:00", - "permanent_mac" => "00:00:00:00:00:00" - }, - { - "dev_name" => "eth1", - "busid" => "0000:01:00.1", - "mac" => "00:00:00:00:00:01", - "permanent_mac" => "00:00:00:00:00:01" - }, - { - "dev_name" => "eth2", - "busid" => "0000:01:00.2", - "mac" => "00:00:00:00:00:02", - "permanent_mac" => "" - } - ] - end - - before(:each) do - allow(Yast::LanItems) - .to receive(:ReadHardware) - .with("netcard") - .and_return(hw_netcard) - allow(Yast::NetworkInterfaces) - .to receive(:Read) - .and_return(true) - # respective agent is not able to change scr root - allow(Yast::SCR) - .to receive(:Read) - .with(path(".udev_persistent.net")) - .and_return(persistent_udevs) - - Yast::LanItems.Read - end - - # see bnc#1056109 - # - basically dev_name is renamed_to || ifcfg || hwinfo.devname for purposes - # of this test (ifcfg is name distinguished from sysconfig configuration, - # hwinfo.devname is name assigned by kernel during device initialization and - # renamed_to is new device name assigned by user when asking for device renaming - # - updating udev rules) - # - # - when we have devices and ruleset defined in AY profile - # which renames these devices it could, before the fix, happen that after - # applying of the ruleset we could end with new nameset e.g. - # which obviously leads to misconfiguration of the system - it "applies rules so, that names remain unique" do - Yast::LanItems.Items[3] = { "ifcfg" => "eth3" } - - network_autoyast.send(:assign_udevs_to_devs, udev_rules) - - lan_items = Yast::LanItems - names = lan_items.Items.keys.map do |i| - lan_items.renamed?(i) ? lan_items.renamed_to(i) : lan_items.GetDeviceName(i) - end - - # check if device names are unique - expect(names.sort).to eql ["eth0", "eth1", "eth2", "eth3"] - end - - it "matches devices according to permanent mac or mac field when first one is missing" do - network_autoyast.send(:assign_udevs_to_devs, udev_mac_rules) - - lan_items = Yast::LanItems - names = lan_items.Items.keys.map do |i| - lan_items.renamed?(i) ? lan_items.renamed_to(i) : lan_items.GetDeviceName(i) - end - - # check if device names are unique - expect(names.sort).to eql ["eth0", "eth1", "eth3"] - end - end - end - describe "#configure_lan" do before do allow(Yast::Profile).to receive(:current) diff --git a/test/network_proposal_test.rb b/test/network_proposal_test.rb index aee2b416c..603eb8dd3 100755 --- a/test/network_proposal_test.rb +++ b/test/network_proposal_test.rb @@ -1,4 +1,24 @@ #!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "network/clients/network_proposal" diff --git a/test/read_hardware_test.rb b/test/read_hardware_test.rb index 4f69ed2f4..a0cebaa4d 100755 --- a/test/read_hardware_test.rb +++ b/test/read_hardware_test.rb @@ -1,5 +1,24 @@ #! /usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/routines_test.rb b/test/routines_test.rb index c4e27a3b1..77e50d9c6 100755 --- a/test/routines_test.rb +++ b/test/routines_test.rb @@ -1,5 +1,24 @@ #! /usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/s390_helpers_test.rb b/test/s390_helpers_test.rb index f6c1e6105..56e2f3703 100755 --- a/test/s390_helpers_test.rb +++ b/test/s390_helpers_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/save_network_test.rb b/test/save_network_test.rb index 5bd0cee26..d1dc2c105 100644 --- a/test/save_network_test.rb +++ b/test/save_network_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "y2storage" diff --git a/test/test_helper.rb b/test/test_helper.rb index 248346124..87bf6e85b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,3 +1,22 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + srcdir = File.expand_path("../../src", __FILE__) y2dirs = ENV.fetch("Y2DIR", "").split(":") ENV["Y2DIR"] = y2dirs.unshift(srcdir).join(":") @@ -31,6 +50,8 @@ Yast.import "NetworkInterfaces" Yast.import "Lan" +require "y2storage" + require_relative "SCRStub" RSpec.configure do |c| @@ -44,6 +65,8 @@ c.before do Yast::Lan.clear_configs allow(Yast::NetworkInterfaces).to receive(:Write) + allow(Y2Network::Hwinfo).to receive(:hwinfo_from_hardware) + Y2Storage::StorageManager.create_test_instance end end diff --git a/test/udev_test.rb b/test/udev_test.rb deleted file mode 100755 index f2368c411..000000000 --- a/test/udev_test.rb +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -require "yast" - -Yast.import "LanItems" -Yast.import "Stage" - -# mock class only for testing include -class NetworkLanComplexUdev < Yast::Module - def initialize - Yast.include self, "network/lan/udev" - end -end - -describe "NetworkLanUdevInclude#update_udev_rule_key" do - subject { NetworkLanComplexUdev.new } - - let(:default_rule) do - subject.GetDefaultUdevRule("default1", "00:11:22:33:44:55") - end - - it "updates existing assignment key to new value" do - new_name = "renamed2" - - updated_rule = subject.update_udev_rule_key( - default_rule, - "NAME", - new_name - ) - expect(updated_rule).to include "NAME=\"#{new_name}\"" - end - - it "updates existing comparison key to new value" do - new_subsystem = "hdd" - - updated_rule = subject.update_udev_rule_key( - default_rule, - "SUBSYSTEM", - new_subsystem - ) - expect(updated_rule).to include "SUBSYSTEM==\"#{new_subsystem}\"" - end - - it "returns unchanged rule when key is not found" do - updated_rule = subject.update_udev_rule_key( - default_rule, - "NONEXISTENT_UDEV_KEY", - "value" - ) - expect(updated_rule).to eq default_rule - end -end - -describe "#udev_rule_key" do - let(:rule) { ["KERNELS==\"invalid\"", "KERNEL==\"eth*\"", "NAME=\"eth1\""] } - - it "raises ArgumentError if given rule is empty" do - expect { Yast::LanItems.udev_key_value(nil, "KERNEL") } - .to raise_error(ArgumentError, "Rule must not be nil when querying a key value") - end - - it "returns value of the first attribute which matches given key" do - expect(Yast::LanItems.udev_key_value(rule, "KERNEL")).to eql("eth*") - end - - it "returns an empty string if no rule matches" do - expect(Yast::LanItems.udev_key_value(rule, "ATTR{address}")).to eql("") - end -end - -describe "NetworkLanUdevInclude#AddToUdevRule" do - subject(:udev) { NetworkLanComplexUdev.new } - - let(:rule) { ["KERNELS==\"invalid\"", "KERNEL==\"eth*\"", "NAME=\"eth1\""] } - - it "adds new tripled into existing rule" do - updated_rule = udev.AddToUdevRule(rule, "ENV{MODALIAS}==\"e1000\"") - expect(updated_rule).to include "ENV{MODALIAS}==\"e1000\"" - end -end - -describe "NetworkLanUdevInclude#RemoveKeyFromUdevRule" do - subject(:udev) { NetworkLanComplexUdev.new } - - let(:rule) { ["KERNELS==\"invalid\"", "KERNEL==\"eth*\"", "NAME=\"eth1\""] } - - it "removes tripled from existing rule" do - updated_rule = udev.RemoveKeyFromUdevRule(rule, "KERNEL") - expect(updated_rule).not_to include "KERNEL=\"eth*\"" - end -end - -describe "LanItems#ReplaceItemUdev" do - let(:rule) { [] } - let(:items) { { 0 => { "udev" => { "net" => rule } } } } - let(:mac_address) { "xx:01:02:03:04:05" } - - before(:each) do - Yast::LanItems.current = 0 - - # LanItems should create "udev" and "net" subkeys for each item - # during Read - allow(Yast::LanItems).to receive(:Items).and_return(items) - end - - context "when the given Item hasn't got an udev rule" do - let(:items) { { 0 => {} } } - let(:default_rule) { Yast::LanItems.GetDefaultUdevRule("eth1", mac_address) } - - it "creates and assings a new rule for the given Item" do - allow(Yast::LanItems).to receive(:getUdevFallback).and_return(default_rule) - - expect(Yast::LanItems).to receive(:SetModified) - - updated_rule = Yast::LanItems.ReplaceItemUdev("KERNELS", "ATTR{address}", mac_address) - item_rule = Yast::LanItems.getCurrentItem["udev"]["net"] - - expect(updated_rule).to include "ATTR{address}==\"#{mac_address}\"" - expect(item_rule).to include "ATTR{address}==\"#{mac_address}\"" - end - end - - context "when the given Item has got an udev rule" do - let(:rule) { ["ATTR==\"#{mac_address}\"", "KERNEL==\"eth*\"", "NAME=\"eth1\""] } - let(:bus_id) { "0000:08:00.0" } - - it "replaces triplet in the rule as requested" do - expect(Yast::LanItems).to receive(:SetModified) - - updated_rule = Yast::LanItems.ReplaceItemUdev("ATTR{address}", "KERNELS", bus_id) - item_rule = Yast::LanItems.getCurrentItem["udev"]["net"] - - expect(updated_rule).to include "KERNELS==\"#{bus_id}\"" - expect(updated_rule).not_to include "ATTR{address}==\"#{mac_address}\"" - expect(item_rule).to include "KERNELS==\"#{bus_id}\"" - expect(item_rule).not_to include "ATTR{address}==\"#{mac_address}\"" - end - - it "does not set modification flag in case of no change" do - Yast::LanItems.ReplaceItemUdev("ATTR{address}", "ATTR{address}", mac_address) - - expect(Yast::LanItems).not_to receive(:SetModified) - end - - # this is an SCR limitation - it "contains NAME tuplet at last position" do - updated_rule = Yast::LanItems.ReplaceItemUdev("ATTR{address}", "KERNELS", bus_id) - - expect(updated_rule.last).to match(/NAME.*/) - end - end -end diff --git a/test/wicked_test.rb b/test/wicked_test.rb index 5d359eb62..b505f4548 100644 --- a/test/wicked_test.rb +++ b/test/wicked_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "test_helper" require "yast" diff --git a/test/wireless_test.rb b/test/wireless_test.rb deleted file mode 100755 index ea6cecd1d..000000000 --- a/test/wireless_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "test_helper" - -require "yast" - -Yast.import "LanItems" - -class WirelessTestClass < Yast::Module - def initialize - Yast.include self, "network/lan/wireless.rb" - end -end - -describe "WirelessInclude" do - subject { WirelessTestClass.new } - - describe "#InitPeapVersion" do - before do - allow(Yast::UI).to receive(:ChangeWidget) - end - - it "Enables widget if WPA_EAP_MODE is PEAP" do - Yast::LanItems.wl_wpa_eap["WPA_EAP_MODE"] = "PEAP" - expect(Yast::UI).to receive(:ChangeWidget).with(Id("test"), :Enabled, true) - - subject.InitPeapVersion("test") - end - end -end diff --git a/test/y2network/autoinst/config_reader_test.rb b/test/y2network/autoinst/config_reader_test.rb index 080b68267..5ef272d85 100644 --- a/test/y2network/autoinst/config_reader_test.rb +++ b/test/y2network/autoinst/config_reader_test.rb @@ -20,14 +20,17 @@ # find current contact information at www.suse.com. require_relative "../../test_helper" +require "y2network/config" require "y2network/autoinst_profile/networking_section" require "y2network/autoinst/config_reader" +require "y2network/sysconfig/interfaces_reader" describe Y2Network::Autoinst::ConfigReader do - let(:subject) { described_class.new(networking_section) } + let(:subject) { described_class.new(networking_section, system_config) } let(:networking_section) do Y2Network::AutoinstProfile::NetworkingSection.new_from_hashes(profile) end + let(:system_config) { Y2Network::Config.new(source: :testing) } let(:eth0) { { "device" => "eth0", "bootproto" => "dhcp", "startmode" => "auto" } } let(:interfaces) { [eth0] } diff --git a/test/y2network/autoinst/dns_reader_test.rb b/test/y2network/autoinst/dns_reader_test.rb index 0db431666..8f2b32157 100644 --- a/test/y2network/autoinst/dns_reader_test.rb +++ b/test/y2network/autoinst/dns_reader_test.rb @@ -25,28 +25,54 @@ describe Y2Network::Autoinst::DNSReader do subject { described_class.new(dns_section) } + let(:dns_section) do Y2Network::AutoinstProfile::DNSSection.new_from_hashes(dns_profile) end - let(:forwarding_profile) { { "ip_forward" => true } } - let(:dhcp_hostname) { true } let(:dns_profile) do { "hostname" => "linux.example.org", "nameservers" => ["192.168.122.1", "10.0.0.1"], "searchlist" => ["suse.com"], - "resolv_conf_policy" => "auto" + "resolv_conf_policy" => "some-policy" } end + let(:hostname_reader) { instance_double(Y2Network::HostnameReader, hostname: "foo") } + + before do + allow(Y2Network::HostnameReader).to receive(:new).and_return(hostname_reader) + end describe "#config" do + EMPTY_DNS_SECTION = Y2Network::AutoinstProfile::DNSSection.new_from_hashes({}) + it "builds a new Y2Network::DNS config from the profile" do expect(subject.config).to be_a Y2Network::DNS expect(subject.config.hostname).to eq("linux.example.org") - expect(subject.config.resolv_conf_policy).to eq("auto") + expect(subject.config.resolv_conf_policy).to eq("some-policy") expect(subject.config.nameservers.size).to eq(2) end + + it "falls back to the current hostname" do + config = described_class.new(EMPTY_DNS_SECTION).config + expect(config.hostname).to eq("foo") + end + + it "falls back to 'auto' for resolv_conf_policy" do + config = described_class.new(EMPTY_DNS_SECTION).config + expect(config.resolv_conf_policy).to eq("auto") + end + + it "falls back to an empty array for the name servers" do + config = described_class.new(EMPTY_DNS_SECTION).config + expect(config.nameservers).to eq([]) + end + + it "falls back to an empty array for the search list" do + config = described_class.new(EMPTY_DNS_SECTION).config + expect(config.searchlist).to eq([]) + end end end diff --git a/test/y2network/autoinst/interfaces_reader_test.rb b/test/y2network/autoinst/interfaces_reader_test.rb new file mode 100644 index 000000000..1e3aca9a4 --- /dev/null +++ b/test/y2network/autoinst/interfaces_reader_test.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/autoinst_profile/interfaces_section" +require "y2network/autoinst/interfaces_reader" +require "y2network/interface" + +describe Y2Network::Autoinst::InterfacesReader do + let(:subject) { described_class.new(interfaces_section) } + let(:interfaces_section) do + Y2Network::AutoinstProfile::InterfacesSection.new_from_hashes(interfaces_profile) + end + + let(:interfaces_profile) do + [ + { + "bootproto" => "dhcp", + "name" => "eth0", + "startmode" => "auto", + "aliases" => { + "alias0" => { + "IPADDR" => "10.100.0.1", + "PREFIXLEN" => "24", + "LABEL" => "test" + }, + "alias1" => { + "IPADDR" => "10.100.0.2", + "PREFIXLEN" => "24", + "LABEL" => "test2" + } + } + } + ] + end + + describe "#config" do + it "builds a new Y2Network::ConnectionConfigsCollection" do + expect(subject.config).to be_a Y2Network::ConnectionConfigsCollection + expect(subject.config.size).to eq(1) + end + + it "assign properly all values in profile" do + config = subject.config.by_name("eth0") + expect(config.startmode).to eq Y2Network::Startmode.create("auto") + expect(config.bootproto).to eq Y2Network::BootProtocol.from_name("dhcp") + expect(config.ip_aliases.size).to eq 2 + end + end +end diff --git a/test/y2network/autoinst/udev_rules_reader_test.rb b/test/y2network/autoinst/udev_rules_reader_test.rb new file mode 100644 index 000000000..94cce4ea6 --- /dev/null +++ b/test/y2network/autoinst/udev_rules_reader_test.rb @@ -0,0 +1,82 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/autoinst_profile/udev_rules_section" +require "y2network/autoinst/udev_rules_reader" +require "y2network/interface" + +describe Y2Network::Autoinst::UdevRulesReader do + let(:subject) { described_class.new(udev_rules_section) } + let(:udev_rules_section) do + Y2Network::AutoinstProfile::UdevRulesSection.new_from_hashes(udev_rules_profile) + end + + let(:udev_rules_profile) do + [ + { + "name" => "eth1", + "rule" => "KERNELS", + "value" => "bus1" + } + ] + end + + describe "#apply" do + let(:config) do + double(interfaces: Y2Network::InterfacesCollection.new([double(name: "eth0", hardware: double(busid: "bus1"))]), rename_interface: nil) + end + + it "renames interface with matching hardware properties" do + expect(config).to receive(:rename_interface).with("eth0", "eth1", :bus_id) + subject.apply(config) + end + + context "when there is already interface with matching name and non-matching hardware" do + let(:config) do + double( + interfaces: Y2Network::InterfacesCollection.new( + [ + double(name: "eth0", hardware: double(busid: "bus1")), + double(name: "eth1", hardware: double(busid: "bus2")) + ] + ), + rename_interface: nil + ) + end + + it "renames colliding interface to new free name" do + expect(config).to receive(:rename_interface).with("eth1", "eth2", :mac) + subject.apply(config) + end + end + + context "when there is no interface with matching hardware" do + let(:config) do + double(interfaces: Y2Network::InterfacesCollection.new([double(name: "eth0", hardware: double(busid: "bus2"))]), rename_interface: nil) + end + + it "do nothing" do + expect(config).to_not receive(:rename_interface) + end + end + end +end diff --git a/test/y2network/autoinst_profile/interface_section_test.rb b/test/y2network/autoinst_profile/interface_section_test.rb new file mode 100644 index 000000000..fd8852a71 --- /dev/null +++ b/test/y2network/autoinst_profile/interface_section_test.rb @@ -0,0 +1,102 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/autoinst_profile/interface_section" +require "y2network/connection_config/ip_config" + +describe Y2Network::AutoinstProfile::InterfaceSection do + subject(:section) { described_class.new } + + describe ".new_from_network" do + let(:config) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |c| + c.bootproto = Y2Network::BootProtocol::STATIC + c.startmode = Y2Network::Startmode.create("ifplugd") + c.startmode.priority = 50 + c.firewall_zone = "DMZ" + c.ethtool_options = "test=1" + c.interface = "eth0" + c.ip = Y2Network::ConnectionConfig::IPConfig.new(Y2Network::IPAddress.new("10.100.0.1/24")) + c.ip_aliases = [ + Y2Network::ConnectionConfig::IPConfig.new(Y2Network::IPAddress.from_string("10.100.0.1/24"), label: "test"), + Y2Network::ConnectionConfig::IPConfig.new(Y2Network::IPAddress.from_string("10.100.0.2/24"), label: "test1") + ] + end + end + + it "initializes values properly" do + section = described_class.new_from_network(config) + expect(section.bootproto).to eq("static") + expect(section.aliases).to eq( + "alias0" => { "IPADDR" => "10.100.0.1", "PREFIXLEN" => "24", "LABEL" => "test" }, + "alias1" => { "IPADDR" => "10.100.0.2", "PREFIXLEN" => "24", "LABEL" => "test1" } + ) + end + end + + describe ".new_from_hashes" do + let(:hash) do + { + "bootproto" => "dhcp4", + "device" => "eth0", + "startmode" => "auto" + } + end + + it "loads properly boot protocol" do + section = described_class.new_from_hashes(hash) + expect(section.bootproto).to eq "dhcp4" + end + end + + describe "#wireless_keys" do + it "returns array" do + section = described_class.new_from_hashes({}) + expect(section.wireless_keys).to be_a Array + end + + it "return each defined wireless key" do + hash = { + "wireless_key1" => "test1", + "wireless_key3" => "test3" + } + + section = described_class.new_from_hashes(hash) + expect(section.wireless_keys).to eq ["test1", "test3"] + end + end + + describe "#bonding_slaves" do + it "returns array" do + section = described_class.new_from_hashes({}) + expect(section.bonding_slaves).to be_a Array + end + + it "return each defined wireless key" do + hash = { + "bonding_slave1" => "eth0", + "bonding_slave3" => "eth1" + } + + section = described_class.new_from_hashes(hash) + expect(section.bonding_slaves).to eq ["eth0", "eth1"] + end + end +end diff --git a/test/y2network/autoinst_profile/networking_section_test.rb b/test/y2network/autoinst_profile/networking_section_test.rb index 2b90fb0a9..9ca94bab7 100644 --- a/test/y2network/autoinst_profile/networking_section_test.rb +++ b/test/y2network/autoinst_profile/networking_section_test.rb @@ -47,14 +47,16 @@ describe ".new_from_hashes" do let(:hash) do { - "routing" => routing, - "dns" => dns + "routing" => routing, + "dns" => dns, + "net-udev" => net_udev } end let(:routing) { {} } let(:routing_section) { double("RoutingSection") } let(:dns) { {} } let(:dns_section) { double("DNSSection") } + let(:net_udev) { {} } before do allow(Y2Network::AutoinstProfile::RoutingSection).to receive(:new_from_hashes) @@ -91,5 +93,9 @@ end end + it "initializes the udev rules section" do + section = described_class.new_from_hashes(hash) + expect(section.udev_rules.udev_rules).to eq([]) + end end end diff --git a/test/y2network/autoinst_profile/udev_rule_test.rb b/test/y2network/autoinst_profile/udev_rule_test.rb new file mode 100644 index 000000000..be8b965cf --- /dev/null +++ b/test/y2network/autoinst_profile/udev_rule_test.rb @@ -0,0 +1,42 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/autoinst_profile/udev_rule_section" +require "y2network/connection_config/ip_config" + +describe Y2Network::AutoinstProfile::UdevRuleSection do + subject(:section) { described_class.new } + + describe ".new_from_network" do + let(:hardware) do + double(Y2Network::Hwinfo, mac: "mac1", busid: "bus1") + end + let(:interface) do + double(Y2Network::Interface, renaming_mechanism: :mac, hardware: hardware, name: "eth0") + end + + it "initializes values properly" do + section = described_class.new_from_network(interface) + expect(section.name).to eq "eth0" + expect(section.value).to eq "mac1" + expect(section.rule).to eq "ATTR{address}" + end + end +end diff --git a/test/y2network/boot_protocol_test.rb b/test/y2network/boot_protocol_test.rb new file mode 100644 index 000000000..1badb6305 --- /dev/null +++ b/test/y2network/boot_protocol_test.rb @@ -0,0 +1,76 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" + +require "y2network/boot_protocol" + +describe Y2Network::BootProtocol do + subject(:protocol) { described_class.new("dhcp") } + + describe ".all" do + it "returns all known boot protocols" do + expect(described_class.all).to_not be_empty + expect(described_class.all.first).to be_a(described_class) + end + end + + describe ".from_name" do + it "returns boot protocol with given name" do + expect(described_class.from_name("dhcp4")).to eq Y2Network::BootProtocol::DHCP4 + end + + it "returns nil if given name not found" do + expect(described_class.from_name("dhcp8")).to eq nil + end + end + + describe "#dhcp?" do + it "returns true if protocol at least partially is read from dhcp" do + expect(Y2Network::BootProtocol::DHCP4.dhcp?).to eq true + expect(Y2Network::BootProtocol::DHCP_AUTOIP.dhcp?).to eq true + expect(Y2Network::BootProtocol::STATIC.dhcp?).to eq false + end + end + + describe "#static?" do + it "returns true for STATIC boot protocol" do + expect(Y2Network::BootProtocol::STATIC.static?).to eq(true) + end + + it "returns false for non static boot protocols" do + non_static = Y2Network::BootProtocol.all - [Y2Network::BootProtocol::STATIC] + non_static.each { |b| expect(b.static?).to eq(false) } + end + end + + describe "#==" do + context "when the other object refers to the same boot protocol" do + it "returns true" do + expect(protocol).to eq(described_class.new("dhcp")) + end + end + + context "when the other object refers to a different boot protocol" do + it "returns false" do + expect(protocol).to_not eq(described_class.new("static")) + end + end + end +end diff --git a/test/y2network/fake_interface_test.rb b/test/y2network/can_be_copied_test.rb similarity index 57% rename from test/y2network/fake_interface_test.rb rename to test/y2network/can_be_copied_test.rb index 52fe98fdc..615e6df5b 100644 --- a/test/y2network/fake_interface_test.rb +++ b/test/y2network/can_be_copied_test.rb @@ -16,25 +16,23 @@ # # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. + require_relative "../test_helper" -require "y2network/fake_interface" -require "y2network/connection_config/wireless" -describe Y2Network::FakeInterface do - subject(:interface) { described_class.new("eth0", type: iface_type) } +require "y2network/can_be_copied" - let(:iface_type) { Y2Network::InterfaceType::ETHERNET } +describe Y2Network::CanBeCopied do + Dummy = Struct.new(:value) do + include Y2Network::CanBeCopied + end - describe ".from_connection" do - let(:conn) do - Y2Network::ConnectionConfig::Wireless.new.tap do |conn| - conn.interface = "wlan0" - end - end + subject { Dummy.new("test") } - it "returns a fake interface using the connection's type" do - iface = described_class.from_connection("wlan0", conn) - expect(iface.type).to eq(Y2Network::InterfaceType::WIRELESS) + describe "#copy" do + it "returns an equivalent but different object" do + other = subject.copy + expect(subject.value).to eq(other.value) + expect(subject).to_not be(other) end end end diff --git a/test/y2network/config_test.rb b/test/y2network/config_test.rb index 864ae4300..56ff4d87a 100644 --- a/test/y2network/config_test.rb +++ b/test/y2network/config_test.rb @@ -18,8 +18,13 @@ # find current contact information at www.suse.com. require_relative "../test_helper" require "y2network/config" +require "y2network/driver" require "y2network/routing_table" require "y2network/interface" +require "y2network/interfaces_collection" +require "y2network/connection_config/bridge" +require "y2network/connection_config/ethernet" +require "y2network/connection_configs_collection" require "y2network/sysconfig/config_reader" require "y2network/sysconfig/config_writer" @@ -29,7 +34,10 @@ end subject(:config) do - described_class.new(interfaces: [eth0], routing: routing, source: :sysconfig) + described_class.new( + interfaces: interfaces, connections: connections, routing: routing, + drivers: drivers, source: :sysconfig + ) end let(:route1) { Y2Network::Route.new } @@ -38,7 +46,18 @@ let(:table1) { Y2Network::RoutingTable.new([route1]) } let(:table2) { Y2Network::RoutingTable.new([route2]) } - let(:eth0) { Y2Network::Interface.new("eth0") } + let(:eth0) { Y2Network::PhysicalInterface.new("eth0") } + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0]) } + + let(:eth0_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.interface = "eth0" + end + end + let(:connections) { Y2Network::ConnectionConfigsCollection.new([eth0_conn]) } + + let(:virtio_net) { Y2Network::Driver.new("virtio_net", "csum=1") } + let(:drivers) { [virtio_net] } let(:routing) { Y2Network::Routing.new(tables: [table1, table2]) } @@ -48,7 +67,7 @@ end before do - allow(Y2Network::ConfigReader).to receive(:for).with(:sysconfig, {}) + allow(Y2Network::ConfigReader).to receive(:for).with(:sysconfig) .and_return(reader) end @@ -127,7 +146,14 @@ context "when interfaces list is different" do it "returns false" do - copy.interfaces = [Y2Network::Interface.new("eth1")] + copy.interfaces = Y2Network::InterfacesCollection.new([Y2Network::Interface.new("eth1")]) + expect(copy).to_not eq(config) + end + end + + context "when connection list is different" do + it "returns false" do + copy.connections = Y2Network::ConnectionConfigsCollection.new([]) expect(copy).to_not eq(config) end end @@ -146,4 +172,237 @@ end end end + + describe "#rename_interface" do + it "adjusts the interface name" do + config.rename_interface("eth0", "eth1", :mac) + eth1 = config.interfaces.by_name("eth1") + expect(eth1.renaming_mechanism).to eq(:mac) + end + + it "adjusts the connection configurations for that interface" do + config.rename_interface("eth0", "eth1", :mac) + eth1_conns = config.connections.by_interface("eth1") + expect(eth1_conns).to_not be_empty + end + + context "when the interface is renamed twice" do + it "adjusts the interface name to the last name" do + config.rename_interface("eth0", "eth1", :mac) + config.rename_interface("eth1", "eth2", :bios_id) + eth2 = config.interfaces.by_name("eth2") + expect(eth2.renaming_mechanism).to eq(:bios_id) + end + + it "adjusts the connection configurations for that interface using the last name" do + config.rename_interface("eth0", "eth1", :mac) + config.rename_interface("eth1", "eth2", :mac) + eth2_conns = config.connections.by_interface("eth2") + expect(eth2_conns).to_not be_empty + end + end + + context "when the old name is nil" do + it "adjust renaming mechanism only" do + config.rename_interface(nil, "eth0", :mac) + eth1 = config.interfaces.by_name("eth0") + expect(eth1.renaming_mechanism).to eq(:mac) + end + end + + context "when dhcp_hostname points to the renamed interface" do + before do + allow(config.dns).to receive(:dhcp_hostname).and_return("eth0") + end + + it "adjusts the dhcp_hostname" do + expect(config.dns).to receive(:dhcp_hostname=).with("eth1") + config.rename_interface("eth0", "eth1", :mac) + end + end + + context "when dhcp_hostname does not point to the renamed interface" do + before do + allow(config.dns).to receive(:dhcp_hostname).and_return(:any) + end + + it "does not adjust the dhcp_hostname" do + expect(config.dns).to_not receive(:dhcp_hostname=) + config.rename_interface("eth0", "eth1", :mac) + end + end + end + + describe "#add_or_update_connection_config" do + let(:new_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.interface = "eth2" + end + end + + it "adds the connection config" do + config.add_or_update_connection_config(new_conn) + expect(config.connections.by_name(new_conn.name)).to eq(new_conn) + end + + context "when a connection config with the same name exists" do + let(:other_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.interface = "eth2" + end + end + + before do + config.add_or_update_connection_config(new_conn) + end + + it "updates the connection config" do + config.add_or_update_connection_config(other_conn) + expect(config.connections.by_name(new_conn.name)).to eq(other_conn) + end + end + + context "when the interface is missing" do + let(:new_conn) do + Y2Network::ConnectionConfig::Bridge.new.tap do |conn| + conn.interface = "br0" + end + end + + it "adds the corresponding interface" do + config.add_or_update_connection_config(new_conn) + expect(config.interfaces.by_name("br0")).to be_a(Y2Network::VirtualInterface) + end + + context "and the interface already exists" do + before do + config.interfaces << Y2Network::VirtualInterface.new("br0") + end + + it "does not add any interface" do + expect { config.add_or_update_connection_config(new_conn) } + .to_not change { config.interfaces.size } + end + end + end + end + + describe "#delete_interface" do + let(:br0) { Y2Network::VirtualInterface.new("br0") } + let(:br0_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.interface = "br0" + end + end + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, br0]) } + let(:connections) { Y2Network::ConnectionConfigsCollection.new([eth0_conn, br0_conn]) } + + context "when it is not a physical interface" do + it "removes the connection config" do + expect { config.delete_interface(br0.name) }.to change { config.connections.to_a } + .from([eth0_conn, br0_conn]).to([eth0_conn]) + end + + it "removes the interface" do + expect { config.delete_interface(br0.name) }.to change { config.interfaces.to_a } + .from([eth0, br0]).to([eth0]) + end + end + + context "when it is a physical interface" do + let(:present?) { true } + + before do + allow(eth0).to receive(:present?).and_return(present?) + end + + it "removes the connection config" do + expect { config.delete_interface(eth0.name) }.to change { config.connections.to_a } + .from([eth0_conn, br0_conn]).to([br0_conn]) + end + + it "does not remove the interface" do + expect { config.delete_interface(eth0.name) }.to_not change { config.interfaces.to_a } + end + + context "when the interface is not present" do + let(:present?) { false } + + it "removes the interface" do + expect { config.delete_interface(eth0.name) }.to change { config.interfaces.to_a } + .from([eth0, br0]).to([br0]) + end + end + end + end + + describe "#drivers_for_interface" do + let(:e1000) { Y2Network::Driver.new("e1000", "") } + let(:custom) { Y2Network::Driver.new("custom", "") } + let(:drivers) { [virtio_net, e1000, custom] } + + before do + allow(eth0).to receive(:drivers).and_return([Y2Network::Driver.new("virtio_net")]) + end + + it "returns the driver for a given interface" do + drivers = config.drivers_for_interface("eth0") + expect(drivers).to eq([virtio_net]) + end + + context "when a custom driver is set" do + before do + eth0.custom_driver = custom.name + end + + it "includes the custom driver" do + expect(config.drivers_for_interface("eth0")) + .to include(custom) + end + end + end + + describe "#add_or_update_driver" do + let(:new_driver) { Y2Network::Driver.new("e1000", "") } + + it "adds the driver" do + config.add_or_update_driver(new_driver) + expect(config.drivers).to eq([virtio_net, new_driver]) + end + + context "when a driver with the same name already exists" do + let(:new_driver) { Y2Network::Driver.new("virtio_net", "csum=0") } + + it "replaces the driver with the given one" do + config.add_or_update_driver(new_driver) + expect(config.drivers).to eq([new_driver]) + end + end + end + + describe "#configured_interface?" do + context "when a connection for the given interface exists" do + it "reeturns true" do + expect(config.configured_interface?("eth0")).to eq(true) + end + end + + context "when no connection for the given interface exists" do + it "reeturns false" do + expect(config.configured_interface?("eth9")).to eq(false) + end + end + + context "when interface name is nil" do + it "returns false" do + expect(config.configured_interface?(nil)).to eq(false) + end + end + + context "when interface name is empty" do + it "returns false" do + expect(config.configured_interface?("")).to eq(false) + end + end + end end diff --git a/test/y2network/connection_config/ctc_test.rb b/test/y2network/connection_config/ctc_test.rb new file mode 100644 index 000000000..67bac2757 --- /dev/null +++ b/test/y2network/connection_config/ctc_test.rb @@ -0,0 +1,30 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/ctc" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Ctc do + describe "#type" do + it "returns 'ctc'" do + expect(subject.type).to eq(Y2Network::InterfaceType::CTC) + end + end +end diff --git a/test/y2network/connection_config/dummy_test.rb b/test/y2network/connection_config/dummy_test.rb new file mode 100644 index 000000000..211c1689f --- /dev/null +++ b/test/y2network/connection_config/dummy_test.rb @@ -0,0 +1,32 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/dummy" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Dummy do + subject(:config) { described_class.new } + + describe "#type" do + it "returns 'dummy'" do + expect(config.type).to eq(Y2Network::InterfaceType::DUMMY) + end + end +end diff --git a/test/y2network/connection_config/hsi_test.rb b/test/y2network/connection_config/hsi_test.rb new file mode 100644 index 000000000..bee5b6a7d --- /dev/null +++ b/test/y2network/connection_config/hsi_test.rb @@ -0,0 +1,30 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/hsi" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Hsi do + describe "#type" do + it "returns 'hsi'" do + expect(subject.type).to eq(Y2Network::InterfaceType::HSI) + end + end +end diff --git a/test/y2network/connection_config/lcs_test.rb b/test/y2network/connection_config/lcs_test.rb new file mode 100644 index 000000000..b72d94210 --- /dev/null +++ b/test/y2network/connection_config/lcs_test.rb @@ -0,0 +1,30 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/lcs" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Lcs do + describe "#type" do + it "returns 'lcs'" do + expect(subject.type).to eq(Y2Network::InterfaceType::LCS) + end + end +end diff --git a/test/y2network/connection_config/qeth_test.rb b/test/y2network/connection_config/qeth_test.rb new file mode 100644 index 000000000..ff60b2572 --- /dev/null +++ b/test/y2network/connection_config/qeth_test.rb @@ -0,0 +1,30 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/qeth" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Qeth do + describe "#type" do + it "returns 'qeth'" do + expect(subject.type).to eq(Y2Network::InterfaceType::QETH) + end + end +end diff --git a/test/y2network/connection_config/tap_test.rb b/test/y2network/connection_config/tap_test.rb new file mode 100644 index 000000000..a74906d1a --- /dev/null +++ b/test/y2network/connection_config/tap_test.rb @@ -0,0 +1,32 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/tap" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Tap do + subject(:config) { described_class.new } + + describe "#type" do + it "returns 'tap'" do + expect(config.type).to eq(Y2Network::InterfaceType::TAP) + end + end +end diff --git a/test/y2network/connection_config/tun_test.rb b/test/y2network/connection_config/tun_test.rb new file mode 100644 index 000000000..cae6a49e1 --- /dev/null +++ b/test/y2network/connection_config/tun_test.rb @@ -0,0 +1,32 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/tun" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Tun do + subject(:config) { described_class.new } + + describe "#type" do + it "returns 'tun'" do + expect(config.type).to eq(Y2Network::InterfaceType::TUN) + end + end +end diff --git a/test/y2network/connection_config/usb_test.rb b/test/y2network/connection_config/usb_test.rb new file mode 100644 index 000000000..b095e6755 --- /dev/null +++ b/test/y2network/connection_config/usb_test.rb @@ -0,0 +1,32 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/usb" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Usb do + subject(:config) { described_class.new } + + describe "#type" do + it "returns 'usb'" do + expect(config.type).to eq(Y2Network::InterfaceType::USB) + end + end +end diff --git a/test/y2network/connection_config/vlan_test.rb b/test/y2network/connection_config/vlan_test.rb new file mode 100644 index 000000000..41b9db857 --- /dev/null +++ b/test/y2network/connection_config/vlan_test.rb @@ -0,0 +1,32 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/connection_config/vlan" +require "y2network/interface_type" + +describe Y2Network::ConnectionConfig::Vlan do + subject(:config) { described_class.new } + + describe "#type" do + it "returns 'vlan'" do + expect(config.type).to eq(Y2Network::InterfaceType::VLAN) + end + end +end diff --git a/test/y2network/connection_configs_collection_test.rb b/test/y2network/connection_configs_collection_test.rb new file mode 100644 index 000000000..e0fd6addf --- /dev/null +++ b/test/y2network/connection_configs_collection_test.rb @@ -0,0 +1,115 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/connection_configs_collection" +require "y2network/connection_config/ethernet" +require "y2network/connection_config/wireless" + +describe Y2Network::ConnectionConfigsCollection do + subject(:collection) { described_class.new(connections) } + + let(:connections) { [eth0, wlan0] } + let(:eth0) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.name = "eth0" + conn.interface = "eth0" + end + end + let(:wlan0) { Y2Network::ConnectionConfig::Wireless.new.tap { |c| c.name = "wlan0" } } + + describe "#by_name" do + it "returns the connection configuration with the given name" do + expect(collection.by_name("eth0")).to eq(eth0) + end + + context "when name is not defined" do + it "returns nil" do + expect(collection.by_name("eth1")).to be_nil + end + end + end + + describe "#by_interface" do + it "returns the connection configurations associated to the given interface name" do + expect(collection.by_interface("eth0")).to eq([eth0]) + end + end + + describe "#by_ids" do + it "retuns the connection configurations with the given IDs" do + expect(collection.by_ids(eth0.id)).to eq(described_class.new([eth0])) + end + end + + describe "#add_or_update" do + let(:eth0_1) { Y2Network::ConnectionConfig::Ethernet.new.tap { |c| c.name = "eth0" } } + + context "when a connection configuration having the same name exists" do + it "replaces the existing configuration with the new one" do + collection.add_or_update(eth0_1) + expect(collection.by_name("eth0")).to be(eth0_1) + expect(collection.size).to eq(2) + end + end + + context "if a connection configuration having the same name does not exist" do + let(:wlan1) { Y2Network::ConnectionConfig::Wireless.new.tap { |c| c.name = "wlan1" } } + + it "adds the configuration to the collection" do + collection.add_or_update(wlan1) + expect(collection.by_name("wlan1")).to be(wlan1) + expect(collection.size).to eq(3) + end + end + end + + describe "#select" do + it "returns a collection containing those configs which satisfy the block" do + selected = collection.select { |c| c.name == "wlan0" } + expect(selected).to eq(described_class.new([wlan0])) + end + end + + describe "#remove" do + context "when a connection configuration having the same name exists" do + it "removes the configuration from the collection" do + collection.remove(eth0) + expect(collection.by_name("eth0")).to be_nil + expect(collection.size).to eq(1) + end + end + + context "when a name is given, instead of a connection configuration" do + it "removes the configuration with the given name" do + collection.remove("eth0") + expect(collection.by_name("eth0")).to be_nil + expect(collection.size).to eq(1) + end + end + + context "when a connection configuration having the same name does not exists" do + let(:wlan1) { Y2Network::ConnectionConfig::Wireless.new.tap { |c| c.name = "wlan1" } } + + it "does not modify the collection" do + expect { collection.remove("wlan1") }.to_not change { collection.size } + end + end + end +end diff --git a/test/y2network/dialogs/add_interface_test.rb b/test/y2network/dialogs/add_interface_test.rb index 1fcef3071..294b33af1 100644 --- a/test/y2network/dialogs/add_interface_test.rb +++ b/test/y2network/dialogs/add_interface_test.rb @@ -30,18 +30,9 @@ before do allow(Y2Network::Widgets::InterfaceType).to receive(:new).and_return(double(result: "eth")) allow(subject).to receive(:cwm_show).and_return(:abort) - end - - it "adds new interface to lan items" do - expect(Yast::LanItems).to receive(:AddNew) - - subject.run - end - - it "adds new interface to lan" do - expect(Yast::Lan).to receive(:Add) - - subject.run + allow(Yast::Lan).to receive(:yast_config).and_return( + double(interfaces: double(free_name: "eth0", by_name: nil)) + ) end it "returns nil if canceled" do diff --git a/test/y2network/dialogs/edit_interface_test.rb b/test/y2network/dialogs/edit_interface_test.rb index 71d43e2bf..3e65116af 100644 --- a/test/y2network/dialogs/edit_interface_test.rb +++ b/test/y2network/dialogs/edit_interface_test.rb @@ -23,7 +23,7 @@ require "y2network/dialogs/edit_interface" describe Y2Network::Dialogs::EditInterface do - subject { described_class.new(Y2Network::InterfaceConfigBuilder.new) } + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("eth")) } include_examples "CWM::Dialog" end diff --git a/test/y2network/dialogs/rename_interface_test.rb b/test/y2network/dialogs/rename_interface_test.rb new file mode 100644 index 000000000..0791cc98b --- /dev/null +++ b/test/y2network/dialogs/rename_interface_test.rb @@ -0,0 +1,42 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/dialogs/rename_interface" +require "y2network/interface_config_builder" +require "y2network/physical_interface" + +describe Y2Network::Dialogs::RenameInterface do + subject { described_class.new(builder) } + + let(:builder) do + Y2Network::InterfaceConfigBuilder.for("eth").tap do |builder| + builder.name = "eth0" + end + end + let(:interface) { Y2Network::PhysicalInterface.new("eth0") } + + before do + allow(builder).to receive(:interface).and_return(interface) + end + + include_examples "CWM::CustomWidget" +end diff --git a/test/y2network/dialogs/s390_ctc_activation_test.rb b/test/y2network/dialogs/s390_ctc_activation_test.rb new file mode 100644 index 000000000..9a4edd97c --- /dev/null +++ b/test/y2network/dialogs/s390_ctc_activation_test.rb @@ -0,0 +1,33 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/dialogs/s390_ctc_activation" +require "y2network/interface_config_builder" + +describe Y2Network::Dialogs::S390CtcActivation do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("ctc") } + let(:activator) { Y2Network::S390DeviceActivator.for(builder) } + + subject { described_class.new(activator) } + + include_examples "CWM::Dialog" +end diff --git a/test/y2network/dialogs/s390_device_activation_test.rb b/test/y2network/dialogs/s390_device_activation_test.rb new file mode 100644 index 000000000..4db495ba9 --- /dev/null +++ b/test/y2network/dialogs/s390_device_activation_test.rb @@ -0,0 +1,92 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/dialogs/s390_device_activation" +require "y2network/interface_config_builder" + +describe Y2Network::Dialogs::S390DeviceActivation do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + let(:activator) { Y2Network::S390DeviceActivator.for(builder) } + + subject { described_class.new(activator) } + + include_examples "CWM::Dialog" + + describe ".new" do + it "creates a proposal for the configured device" do + expect(activator).to receive(:propose!) + described_class.new(activator) + end + end + + describe "#run" do + let(:configured) { true } + let(:dialog_action) { :next } + + before do + allow(activator).to receive(:configure).and_return(configured) + allow(activator).to receive(:configured_interface).and_return("eth4") + allow(subject).to receive(:cwm_show).and_return(dialog_action) + end + + context "when going :next" do + it "tries to activate the s390 device" do + expect(activator).to receive(:configure) + subject.run + end + + context "when activated the device" do + it "sets the builder name with the associated interface" do + subject.run + expect(builder.name).to eql("eth4") + end + + it "returns :next" do + expect(subject.run).to eql(:next) + end + end + + context "when failed the activation" do + let(:configured) { false } + it "popups an error" do + expect(Yast::Popup).to receive(:Error) + subject.run + end + + it "returns nil" do + expect(subject.run).to be_nil + end + end + end + + context "when pressed :abort" do + end + end + + describe "#abort_handler" do + it "asks for abort confirmation" do + expect(Yast::Popup).to receive(:ReallyAbort).with(true) + + subject.abort_handler + end + end +end diff --git a/test/y2network/dialogs/s390_lcs_activation_test.rb b/test/y2network/dialogs/s390_lcs_activation_test.rb new file mode 100644 index 000000000..62bc87f84 --- /dev/null +++ b/test/y2network/dialogs/s390_lcs_activation_test.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/dialogs/s390_lcs_activation" +require "y2network/interface_config_builder" + +describe Y2Network::Dialogs::S390LcsActivation do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("lcs") } + subject { described_class.for(builder) } + + include_examples "CWM::Dialog" +end diff --git a/test/y2network/dialogs/s390_qeth_activation_test.rb b/test/y2network/dialogs/s390_qeth_activation_test.rb new file mode 100644 index 000000000..6439c11fe --- /dev/null +++ b/test/y2network/dialogs/s390_qeth_activation_test.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/dialogs/s390_qeth_activation" +require "y2network/interface_config_builder" + +describe Y2Network::Dialogs::S390QethActivation do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + subject { described_class.for(builder) } + + include_examples "CWM::Dialog" +end diff --git a/test/y2network/dialogs/wireless_wep_keys_test.rb b/test/y2network/dialogs/wireless_wep_keys_test.rb new file mode 100644 index 000000000..659786751 --- /dev/null +++ b/test/y2network/dialogs/wireless_wep_keys_test.rb @@ -0,0 +1,58 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/dialogs/wireless_wep_keys" +require "y2network/interface_config_builder" + +describe Y2Network::Dialogs::WirelessWepKeys do + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("wlan")) } + + include_examples "CWM::Dialog" +end + +describe Y2Network::Dialogs::WirelessWepKeys::WEPKeyLength do + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("wlan")) } + + include_examples "CWM::ComboBox" +end + +describe Y2Network::Dialogs::WirelessWepKeys::WEPKeys do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" + + describe "#handle" do + it "opens wep key dialog for edit button" do + builder.keys = ["test"] + expect(Yast::UI).to receive(:UserInput).and_return(:cancel) + + subject.handle("EventReason" => "Activated", "ID" => :wep_keys_edit) + end + + it "opens wep key dialog for add button" do + expect(Yast::UI).to receive(:UserInput).and_return(:cancel) + + subject.handle("EventReason" => "Activated", "ID" => :wep_keys_add) + end + end +end diff --git a/test/y2network/dns_test.rb b/test/y2network/dns_test.rb index 06b11d73e..ec05554b0 100644 --- a/test/y2network/dns_test.rb +++ b/test/y2network/dns_test.rb @@ -78,33 +78,4 @@ end end end - - describe "#ensure_hostname!" do - context "when no hostname was given" do - subject(:dns) { described_class.new } - - it "returns a random name" do - dns.ensure_hostname! - expect(dns.hostname).to match(/linux-\w{4}/) - end - end - - context "when an empty hostname was given" do - subject(:dns) { described_class.new(hostname: "") } - - it "returns a random name" do - dns.ensure_hostname! - expect(dns.hostname).to match(/linux-\w{4}/) - end - end - - context "when a hostname was given" do - subject(:dns) { described_class.new(hostname: "foo") } - - it "returns the given name" do - dns.ensure_hostname! - expect(dns.hostname).to eq("foo") - end - end - end end diff --git a/test/y2network/driver_test.rb b/test/y2network/driver_test.rb new file mode 100644 index 000000000..587afbf1f --- /dev/null +++ b/test/y2network/driver_test.rb @@ -0,0 +1,55 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" + +require "y2network/driver" + +describe Y2Network::Driver do + subject(:driver) { described_class.new("virtio_net", "debug=16") } + + describe ".from_system" do + before do + allow(Yast::SCR).to receive(:Read).with(Yast::Path.new(".modules.options.virtio_net")) + .and_return("csum" => "1") + end + + it "reads the parameters from the underlying system" do + driver = described_class.from_system("virtio_net") + expect(driver.params).to eq("csum=1") + end + end + + describe ".write_options" do + it "writes the parameters to the underlying system" do + expect(driver).to receive(:write_options) + expect(Yast::SCR).to receive(:Write) + .with(Yast::Path.new(".modules"), nil) + described_class.write_options([driver]) + end + end + + describe "#write_options" do + it "writes options to the underlying system" do + expect(Yast::SCR).to receive(:Write) + .with(Yast::Path.new(".modules.options.virtio_net"), "debug" => "16") + driver.write_options + end + end +end diff --git a/test/y2network/hostname_reader_test.rb b/test/y2network/hostname_reader_test.rb new file mode 100644 index 000000000..4403a63fc --- /dev/null +++ b/test/y2network/hostname_reader_test.rb @@ -0,0 +1,156 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/hostname_reader" + +describe Y2Network::HostnameReader do + subject(:reader) { described_class.new } + + describe "#hostname" do + let(:install_inf_hostname) { "linuxrc" } + let(:system_hostname) { "system" } + + before do + allow(reader).to receive(:hostname_from_install_inf).and_return(install_inf_hostname) + allow(reader).to receive(:hostname_from_system).and_return(system_hostname) + allow(reader).to receive(:random_hostname).and_return("linux-abcd") + end + + it "returns the system's hostname" do + expect(reader.hostname).to eq("system") + end + + context "when the hostname cannot be determined" do + let(:system_hostname) { nil } + + it "returns a random one" do + expect(reader.hostname).to eq("linux-abcd") + end + end + + context "during installation" do + let(:install_inf_exists?) { true } + + before do + allow(Yast::Mode).to receive(:installation).and_return(true) + allow(Yast::FileUtils).to receive(:Exists).with("/etc/install.inf") + .and_return(install_inf_exists?) + end + + it "reads the hostname from /etc/install.conf" do + expect(reader.hostname).to eq("linuxrc") + end + + context "when the /etc/install.inf file does not exists" do + let(:install_inf_exists?) { false } + + it "reads the hostname from the system" do + expect(reader.hostname).to eq("system") + end + end + + context "when the hostname cannot be determined" do + let(:install_inf_hostname) { nil } + let(:system_hostname) { nil } + + it "returns a random one" do + expect(reader.hostname).to eq("linux-abcd") + end + end + end + end + + describe "#hostname_from_install_inf" do + let(:hostname) { "foo.bar.com" } + + before do + allow(Yast::SCR).to receive(:Read).and_return(hostname) + end + + it "returns the hostname (without the domain)" do + expect(reader.hostname_from_install_inf).to eq("foo") + end + + context "when the Hostname is not defined in the install.inf file" do + let(:hostname) { nil } + + it "returns nil" do + expect(reader.hostname_from_install_inf).to be_nil + end + end + + context "when an IP address is used instead of a hostname" do + let(:hostname) { "foo1.bar.cz" } + + before do + allow(Yast::NetHwDetection).to receive(:ResolveIP).and_return(hostname) + end + + it "returns the associated hostname" do + expect(reader.hostname_from_install_inf).to eq("foo1") + end + + context "and it is not resolvable to an IP" do + let(:hostname) { nil } + + it "returns nil" do + expect(reader.hostname_from_install_inf).to be_nil + end + end + end + end + + describe "#hostname_from_system" do + it "returns the systems' hostname" do + expect(Yast::Execute).to receive(:on_target!).with("/usr/bin/hostname", "--fqdn", stdout: :capture) + .and_return("foo\n") + expect(reader.hostname_from_system).to eq("foo") + end + + context "when the fqdn cannot be determined" do + let(:hostname_content) { "bar\n" } + + before do + allow(Yast::Execute).to receive(:on_target!).with("/usr/bin/hostname", "--fqdn", stdout: :capture) + .and_raise(Cheetah::ExecutionFailed.new([], "", nil, nil)) + allow(Yast::SCR).to receive(:Read).with(Yast::Path.new(".target.string"), "/etc/hostname") + .and_return(hostname_content) + end + + it "returns the name in /etc/hostname" do + expect(reader.hostname_from_system).to eq("bar") + end + + context "when the /etc/hostname file is empty" do + let(:hostname_content) { "\n" } + + it "returns nil" do + expect(reader.hostname_from_system).to be_nil + end + end + end + end + + describe "#random_hostname" do + it "returns a random name" do + expect(reader.random_hostname).to match(/linux-\w{4}/) + end + end +end diff --git a/test/y2network/hwinfo_test.rb b/test/y2network/hwinfo_test.rb new file mode 100644 index 000000000..6fb08604e --- /dev/null +++ b/test/y2network/hwinfo_test.rb @@ -0,0 +1,200 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/hwinfo" + +describe Y2Network::Hwinfo do + subject(:hwinfo) { described_class.for(interface_name) } + + let(:hardware) do + YAML.load_file(File.join(DATA_PATH, "hardware.yml")) + end + + let(:interface_name) { "enp1s0" } + let(:hw_wrapper) { Y2Network::HardwareWrapper.new } + + before do + allow(Y2Network::Hwinfo).to receive(:hwinfo_from_hardware).and_call_original + allow(Y2Network::HardwareWrapper).to receive(:new).and_return(hw_wrapper) + allow(Y2Network::UdevRule).to receive(:find_for).with(interface_name).and_return(udev_rule) + allow(hw_wrapper).to receive(:ReadHardware).and_return(hardware) + end + + let(:udev_rule) { nil } + + describe ".for" do + context "when there is info from hardware" do + it "returns a hwinfo object containing the info from hardware" do + hwinfo = described_class.for(interface_name) + expect(hwinfo.mac).to eq("52:54:00:68:54:fb") + end + end + + context "when there is no info from hardware" do + let(:hardware) { [] } + let(:udev_rule) { Y2Network::UdevRule.new_mac_based_rename(interface_name, "01:23:45:67:89:ab") } + + it "returns info from udev rules" do + hwinfo = described_class.for(interface_name) + expect(hwinfo.mac).to eq("01:23:45:67:89:ab") + end + + context "when there is no info from udev rules" do + let(:udev_rule) { nil } + + it "returns nil" do + hwinfo = described_class.for(interface_name) + expect(hwinfo.exists?).to eq(false) + end + end + end + end + + describe "#exists?" do + context "when the device exists" do + it "returns true" do + expect(hwinfo.exists?).to eq(true) + end + end + + context "when the device does not exist" do + let(:interface_name) { "missing" } + + it "returns false" do + expect(hwinfo.exists?).to eq(false) + end + end + end + + describe "#merge!" do + subject(:hwinfo) { described_class.new(mac: "00:11:22:33:44:55:66", busid: "0000:08:00.0") } + let(:other) { described_class.new(mac: "01:23:45:78:90:ab", dev_port: "1") } + + it "merges data from another Hwinfo object" do + hwinfo.merge!(other) + expect(hwinfo.mac).to eq(other.mac) + expect(hwinfo.busid).to eq("0000:08:00.0") + expect(hwinfo.dev_port).to eq("1") + end + end + + describe "#drivers" do + it "returns the list of kernel modules names" do + expect(hwinfo.drivers).to eq( + [Y2Network::Driver.new("virtio_net", "")] + ) + end + end + + describe "#dev_port" do + let(:interface_name) { "enp1s0" } + + before do + allow(Yast::SCR).to receive(:Read) + .with(Yast::Path.new(".target.string"), "/sys/class_net/#{interface_name}/dev_port") + .and_return(raw_dev_port) + end + + context "when the dev_port is defined" do + let(:raw_dev_port) { "0000:08:00.0" } + + it "returns the dev_port" do + expect(hwinfo.dev_port).to eq("0000:08:00.0") + end + end + + context "when the dev_port is not defined" do + let(:raw_dev_port) { "\n" } + + it "returns the dev_port" do + expect(hwinfo.dev_port).to be_nil + end + end + end + + describe "#==" do + context "when both objects contain the same information" do + it "returns true" do + expect(described_class.new("dev_name" => "eth0")) + .to eq(described_class.new("dev_name" => "eth0")) + end + end + + context "when both objects contain different information" do + it "returns false" do + expect(described_class.new("dev_name" => "eth0")) + .to_not eq(described_class.new("dev_name" => "eth1")) + end + end + + it "ignores nil values" do + expect(described_class.new("dev_name" => "eth0", "other" => nil)) + .to eq(described_class.new("dev_name" => "eth0")) + end + end + + describe "#present?" do + context "when the hardware was detected" do + subject(:hwinfo) { described_class.new("type" => "eth") } + + it "returns true" do + expect(hwinfo).to be_present + end + end + + context "when the hardware was not detected" do + subject(:hwinfo) { described_class.new({}) } + + it "returns false" do + expect(hwinfo).to_not be_present + end + end + end + + describe "#mac" do + before do + allow(hwinfo).to receive(:permanent_mac).and_return(permanent_mac) + end + + context "when the permanent MAC is defined" do + let(:permanent_mac) { "00:11:22:33:44:55" } + + it "returns the permanent MAC" do + expect(hwinfo.mac).to eq(hwinfo.permanent_mac) + end + end + + context "when the permanent MAC is empty" do + let(:permanent_mac) { "" } + + it "returns the current MAC" do + expect(hwinfo.mac).to eq(hwinfo.used_mac) + end + end + + context "when the permanent MAC is not defined" do + let(:permanent_mac) { nil } + + it "returns the current MAC" do + expect(hwinfo.mac).to eq(hwinfo.used_mac) + end + end + end +end diff --git a/test/y2network/interface_config_builder_test.rb b/test/y2network/interface_config_builder_test.rb index 5b1f263dd..21e1ded01 100644 --- a/test/y2network/interface_config_builder_test.rb +++ b/test/y2network/interface_config_builder_test.rb @@ -1,151 +1,236 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "../test_helper" require "yast" require "y2network/interface_config_builder" +require "y2network/interfaces_collection" +require "y2network/physical_interface" + +Yast.import "Lan" describe Y2Network::InterfaceConfigBuilder do subject(:config_builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" + res = Y2Network::InterfaceConfigBuilder.for("eth") res.name = "eth0" res end + let(:config) { Y2Network::Config.new(interfaces: interfaces, source: :sysconfig) } + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0]) } + let(:eth0) { Y2Network::PhysicalInterface.new("eth0") } + + before do + allow(Yast::Lan).to receive(:yast_config).and_return(config) + end + describe ".for" do context "specialized class for given type exists" do it "returns new instance of that class" do - expect(described_class.for("ib").class.to_s).to eq "Y2Network::InterfaceConfigBuilders::Ib" + expect(described_class.for("ib").class.to_s).to eq "Y2Network::InterfaceConfigBuilders::Infiniband" end end context "specialized class for given type does NOT exist" do it "returns instance of InterfaceConfigBuilder" do - expect(described_class.for("generic-device").class).to eq described_class + expect(described_class.for("eth").class).to eq described_class end - it "sets type to passed type" do - expect(described_class.for("dummy").type).to eq "dummy" + it "sets type to passed type as InterfaceType" do + expect(described_class.for("dummy").type).to eq Y2Network::InterfaceType::DUMMY end end end - describe ".save" do - around do |block| - Yast::LanItems.AddNew - # FIXME: workaround for device without reading hwinfo, so udev is not initialized - Yast::LanItems.Items[Yast::LanItems.current]["udev"] = {} - block.call - Yast::LanItems.Rollback - end + describe "#save" do + let(:driver) { Y2Network::Driver.new("virtio_net", "csum=1") } it "stores driver configuration" do - subject.driver = "e1000e" - subject.driver_options = "test" + expect(config).to receive(:add_or_update_driver).with(driver) + subject.driver = driver subject.save - expect(Yast::LanItems.Items[Yast::LanItems.current]["udev"]["driver"]).to eq "e1000e" - expect(Yast::LanItems.driver_options["e1000e"]).to eq "test" end - it "stores aliases" do - # Avoid deleting old aliases as it can break other tests, due to singleton NetworkInterfaces - allow(Yast::NetworkInterfaces).to receive(:DeleteAlias) - subject.aliases = [{ ip: "10.0.0.0", prefixlen: "24", label: "test", mask: "" }] + context "when a driver is selected" do + before do + config_builder.driver = driver + end + + it "sets the interface driver" do + expect(eth0).to receive(:custom_driver=).with(driver.name) + subject.save + end + + it "updates the driver" do + expect(config).to receive(:add_or_update_driver).with(driver) + subject.save + end + end + + context "when no driver is selected" do + before do + config_builder.driver = :auto + end + + it "sets the interface driver to nil" do + expect(eth0).to receive(:custom_driver=).with(nil) + subject.save + end + + it "does not update any driver" do + expect(config).to_not receive(:add_or_update_driver) + subject.save + end + end + + it "saves connection config" do + expect(config).to receive(:add_or_update_connection_config).with(Y2Network::ConnectionConfig::Base) subject.save - expect(Yast::LanItems.aliases).to eq( - 0 => { "IPADDR" => "10.0.0.0", "LABEL" => "test", "PREFIXLEN" => "24", "NETMASK" => "" } - ) end - end - describe "#new_device_startmode" do - DEVMAP_STARTMODE_INVALID = { - "STARTMODE" => "invalid" - }.freeze - - AVAILABLE_PRODUCT_STARTMODES = [ - "hotplug", - "manual", - "off", - "nfsroot" - ].freeze - - ["hotplug", ""].each do |hwinfo_hotplug| - expected_startmode = hwinfo_hotplug == "hotplug" ? "hotplug" : "auto" - hotplug_desc = hwinfo_hotplug == "hotplug" ? "can hotplug" : "cannot hotplug" - - context "When product_startmode is auto and device " + hotplug_desc do - it "results to auto" do - expect(Yast::ProductFeatures) - .to receive(:GetStringFeature) - .with("network", "startmode") { "auto" } - - result = config_builder.device_sysconfig["STARTMODE"] - expect(result).to be_eql "auto" - end + context "when interface was renamed" do + before do + subject.rename_interface("eth2") + subject.renaming_mechanism = :mac end - context "When product_startmode is ifplugd and device " + hotplug_desc do - before(:each) do - expect(Yast::ProductFeatures) - .to receive(:GetStringFeature) - .with("network", "startmode") { "ifplugd" } - allow(config_builder).to receive(:hotplug_interface?) { hwinfo_hotplug == "hotplug" } - # setup stubs by default at results which doesn't need special handling - allow(Yast::Arch).to receive(:is_laptop) { true } - allow(Yast::NetworkService).to receive(:is_network_manager) { false } - end + it "updates the name in the configuration" do + expect(config).to receive(:rename_interface) + .with("eth0", "eth2", :mac) + subject.save + end + end - it "results to #{expected_startmode} when not running on laptop" do - expect(Yast::Arch) - .to receive(:is_laptop) { false } + context "when interface was not renamed" do + it "does not alter the name" do + expect(config).to_not receive(:rename_interface) + subject.save + end + end - result = config_builder.device_sysconfig["STARTMODE"] - expect(result).to be_eql expected_startmode - end + context "when aliases are defined" do + before do + subject.aliases = [ + { id: "_1", ip: "192.168.122.100", prefixlen: "/24", label: "alias1" }, + { id: "suffix1", ip: "192.168.123.100", prefixlen: "/24", label: "alias2" }, + { ip: "10.0.0.2", label: "alias3" }, + { id: "", ip: "10.0.0.3", label: "alias4" } + ] + end - it "results to ifplugd when running on laptop" do - expect(Yast::Arch) - .to receive(:is_laptop) { true } + it "sets aliases for the connection config" do + subject.save + expect(subject.connection_config.ip_aliases).to eq( + [ + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("192.168.122.100/24"), id: "_1", label: "alias1" + ), + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("192.168.123.100/24"), id: "suffix1", label: "alias2" + ), + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("10.0.0.2"), id: "_2", label: "alias3" + ), + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("10.0.0.3"), id: "_3", label: "alias4" + ) + ] + ) + end + end + end - result = config_builder.device_sysconfig["STARTMODE"] - expect(result).to be_eql "ifplugd" - end + describe "#rename_interface" do + it "updates the interface name" do + expect { config_builder.rename_interface("eth2") } + .to change { config_builder.name }.from("eth0").to("eth2") + end + end - it "results to #{expected_startmode} when running NetworkManager" do - expect(Yast::NetworkService) - .to receive(:is_network_manager) { true } + describe "#renamed_interface?" do + context "when the interface name has not been changed" do + it "returns false" do + expect(config_builder.renamed_interface?).to eq(false) + end - result = config_builder.device_sysconfig["STARTMODE"] - expect(result).to be_eql expected_startmode + context "but it was initially renamed by udev" do + before do + allow(eth0).to receive(:renaming_mechanism).and_return(:mac) end - it "results to #{expected_startmode} when current device is virtual one" do - # check for virtual device type is done via Builtins.contains. I don't - # want to stub it because it requires default stub value definition for - # other calls of the function. It might have unexpected inpacts. - allow(config_builder).to receive(:type) { "bond" } - - result = config_builder.device_sysconfig["STARTMODE"] - expect(result).to be_eql expected_startmode + it "returns false" do + expect(config_builder.renamed_interface?).to eq(false) end end + end - context "When product_startmode is not auto neither ifplugd" do - AVAILABLE_PRODUCT_STARTMODES.each do |product_startmode| - it "for #{product_startmode} it results to #{expected_startmode} if device " + hotplug_desc do - expect(Yast::ProductFeatures) - .to receive(:GetStringFeature) - .with("network", "startmode") { product_startmode } - expect(config_builder) - .to receive(:hotplug_interface?) { hwinfo_hotplug == "hotplug" } + context "when the interface has been renamed" do + before do + config_builder.rename_interface("eth2") + end - result = config_builder.device_sysconfig["STARTMODE"] - expect(result).to be_eql expected_startmode - end - end + it "returns true" do + expect(config_builder.renamed_interface?).to eq(true) + end + end + + context "when the renaming mechanism has been changed" do + before do + config_builder.renaming_mechanism = :busid end + + it "returns true" do + expect(config_builder.renamed_interface?).to eq(true) + end + end + + context "when the old name and mechanism have been restored " do + before do + config_builder.rename_interface("eth2") + config_builder.renaming_mechanism = :mac + end + + it "returns false" do + config_builder.rename_interface("eth0") + config_builder.renaming_mechanism = :none + expect(config_builder.renamed_interface?).to eq(false) + end + end + end + + describe "#hwinfo_from" do + let(:hwinfo) { { "dev_name" => "", "busid" => "0.0.0700" } } + + it "overrides the builder hwinfo with the given hardware info" do + config_builder.hwinfo_from(hwinfo) + expect(config_builder.hwinfo.busid).to eq("0.0.0700") + end + end + + describe "#hostname=" do + it "sets the hostname for the connection config" do + expect(subject.connection_config).to receive(:hostname=).with("foo").and_call_original + expect { subject.hostname = "foo" }.to change { subject.hostname } + .from("").to("foo") end end end diff --git a/test/y2network/interface_config_builders/bond_test.rb b/test/y2network/interface_config_builders/bond_test.rb deleted file mode 100644 index 6021ed892..000000000 --- a/test/y2network/interface_config_builders/bond_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "../../test_helper" - -require "yast" -require "y2network/interface_config_builders/bond" - -describe Y2Network::InterfaceConfigBuilders::Bond do - let(:config) { Y2Network::Config.new(source: :test) } - - before do - allow(Y2Network::Config) - .to receive(:find) - .with(:yast) - .and_return(config) - end - - subject(:config_builder) do - res = Y2Network::InterfaceConfigBuilders::Bond.new - res.name = "bond0" - res - end - - describe "#type" do - it "returns 'bond'" do - expect(subject.type).to eq "bond" - end - end - - describe "#bondable_interfaces" do - it "returns array" do - expect(subject.bondable_interfaces).to be_a(::Array) - end - end -end diff --git a/test/y2network/interface_config_builders/bonding_test.rb b/test/y2network/interface_config_builders/bonding_test.rb new file mode 100644 index 000000000..af2459c77 --- /dev/null +++ b/test/y2network/interface_config_builders/bonding_test.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "yast" +require "y2network/interface_config_builders/bonding" + +describe Y2Network::InterfaceConfigBuilders::Bonding do + let(:config) { Y2Network::Config.new(source: :test) } + + before do + allow(Y2Network::Config) + .to receive(:find) + .with(:yast) + .and_return(config) + end + + subject(:config_builder) do + res = Y2Network::InterfaceConfigBuilders::Bonding.new + res.name = "bond0" + res + end + + describe "#type" do + it "returns bonding interface type" do + expect(subject.type).to eq Y2Network::InterfaceType::BONDING + end + end + + describe "#bondable_interfaces" do + it "returns array" do + expect(subject.bondable_interfaces).to be_a(::Array) + end + end +end diff --git a/test/y2network/interface_config_builders/br_test.rb b/test/y2network/interface_config_builders/br_test.rb deleted file mode 100644 index 71208261e..000000000 --- a/test/y2network/interface_config_builders/br_test.rb +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "../../test_helper" - -require "yast" -require "y2network/interface_config_builders/br" - -describe Y2Network::InterfaceConfigBuilders::Br do - let(:config) { Y2Network::Config.new(source: :test) } - - before do - allow(Y2Network::Config) - .to receive(:find) - .with(:yast) - .and_return(config) - end - - subject(:config_builder) do - res = Y2Network::InterfaceConfigBuilders::Br.new - res.name = "br0" - res - end - - describe "#type" do - it "returns 'br'" do - expect(subject.type).to eq "br" - end - end - - describe "#bridgable_interfaces" do - # TODO: better and more reasonable test when we have easy way how to describe configuration - it "returns array" do - expect(subject.bridgeable_interfaces).to be_a(::Array) - end - end - - describe "#already_configured?" do - it "returns boolean" do - expect(subject.already_configured?([])).to eq false - end - end -end diff --git a/test/y2network/interface_config_builders/bridge_test.rb b/test/y2network/interface_config_builders/bridge_test.rb new file mode 100644 index 000000000..2affe77b0 --- /dev/null +++ b/test/y2network/interface_config_builders/bridge_test.rb @@ -0,0 +1,61 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "yast" +require "y2network/interface_config_builders/bridge" + +describe Y2Network::InterfaceConfigBuilders::Bridge do + let(:config) { Y2Network::Config.new(source: :test) } + + before do + allow(Y2Network::Config) + .to receive(:find) + .with(:yast) + .and_return(config) + end + + subject(:config_builder) do + res = Y2Network::InterfaceConfigBuilders::Bridge.new + res.name = "br0" + res + end + + describe "#type" do + it "returns bridge type" do + expect(subject.type).to eq Y2Network::InterfaceType::BRIDGE + end + end + + describe "#bridgable_interfaces" do + # TODO: better and more reasonable test when we have easy way how to describe configuration + it "returns array" do + expect(subject.bridgeable_interfaces).to be_a(::Array) + end + end + + describe "#already_configured?" do + it "returns boolean" do + expect(subject.already_configured?([])).to eq false + end + end +end diff --git a/test/y2network/interface_config_builders/ctc_test.rb b/test/y2network/interface_config_builders/ctc_test.rb new file mode 100644 index 000000000..7c55571c8 --- /dev/null +++ b/test/y2network/interface_config_builders/ctc_test.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "yast" +require "y2network/interface_config_builders/ctc" +require "y2network/interface_type" + +describe Y2Network::InterfaceConfigBuilders::Ctc do + subject(:builder) do + res = Y2Network::InterfaceConfigBuilders::Ctc.new + res.name = "ctc0" + res + end + + describe "#type" do + it "returns ctc type" do + expect(subject.type).to eq Y2Network::InterfaceType::CTC + end + end +end diff --git a/test/y2network/interface_config_builders/ib_test.rb b/test/y2network/interface_config_builders/ib_test.rb deleted file mode 100644 index 062f5493a..000000000 --- a/test/y2network/interface_config_builders/ib_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env rspec - -require_relative "../../test_helper" - -require "yast" -require "y2network/interface_config_builders/ib" - -describe Y2Network::InterfaceConfigBuilders::Ib do - subject(:config_builder) do - res = Y2Network::InterfaceConfigBuilders::Ib.new - res.name = "ib0" - res - end - - describe "#type" do - it "returns 'ib'" do - expect(subject.type).to eq "ib" - end - end - - describe "#save" do - around do |test| - Yast::LanItems.AddNew - test.call - Yast::LanItems.Rollback - end - - it "stores ipoib configuration" do - subject.ipoib_mode = "datagram" - - subject.save - devmap = subject.device_sysconfig - - expect(devmap).to include("IPOIB_MODE" => "datagram") - end - - it "stores nil to ipoib configuration if mode is 'default'" do - subject.ipoib_mode = "default" - - subject.save - devmap = subject.device_sysconfig - - expect(devmap).to include("IPOIB_MODE" => nil) - end - end - - describe "#ipoib_mode" do - context "modified by ipoib=" do - it "returns modified value" do - subject.ipoib_mode = "default" - expect(subject.ipoib_mode).to eq "default" - end - end - end -end diff --git a/test/y2network/interface_config_builders/infiniband_test.rb b/test/y2network/interface_config_builders/infiniband_test.rb new file mode 100644 index 000000000..254314360 --- /dev/null +++ b/test/y2network/interface_config_builders/infiniband_test.rb @@ -0,0 +1,58 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "yast" +require "y2network/interface_config_builders/infiniband" +require "y2network/interfaces_collection" +require "y2network/physical_interface" + +describe Y2Network::InterfaceConfigBuilders::Infiniband do + subject(:config_builder) do + res = Y2Network::InterfaceConfigBuilders::Infiniband.new + res.name = "ib0" + res + end + + let(:config) { Y2Network::Config.new(interfaces: interfaces, source: :sysconfig) } + let(:interfaces) { Y2Network::InterfacesCollection.new([ib0]) } + let(:ib0) { Y2Network::PhysicalInterface.new("ib0") } + + before do + allow(Yast::Lan).to receive(:yast_config).and_return(config) + end + + describe "#type" do + it "returns infiniband interface type" do + expect(subject.type).to eq Y2Network::InterfaceType::INFINIBAND + end + end + + describe "#ipoib_mode" do + context "modified by ipoib=" do + it "returns modified value" do + subject.ipoib_mode = "default" + expect(subject.ipoib_mode).to eq "default" + end + end + end +end diff --git a/test/y2network/interface_config_builders/lcs_test.rb b/test/y2network/interface_config_builders/lcs_test.rb new file mode 100644 index 000000000..5060b7e49 --- /dev/null +++ b/test/y2network/interface_config_builders/lcs_test.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "yast" +require "y2network/interface_config_builders/lcs" +require "y2network/interface_type" + +describe Y2Network::InterfaceConfigBuilders::Lcs do + subject(:builder) do + res = Y2Network::InterfaceConfigBuilders::Lcs.new + res.name = "eth1" + res + end + + describe "#type" do + it "returns lcs type" do + expect(subject.type).to eq Y2Network::InterfaceType::LCS + end + end +end diff --git a/test/y2network/interface_config_builders/qeth_test.rb b/test/y2network/interface_config_builders/qeth_test.rb new file mode 100644 index 000000000..ebb1460af --- /dev/null +++ b/test/y2network/interface_config_builders/qeth_test.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "yast" +require "y2network/interface_config_builders/qeth" +require "y2network/interface_type" + +describe Y2Network::InterfaceConfigBuilders::Qeth do + subject(:builder) do + res = Y2Network::InterfaceConfigBuilders::Qeth.new + res.name = "eth0" + res + end + + describe "#type" do + it "returns qeth type" do + expect(subject.type).to eq Y2Network::InterfaceType::QETH + end + end +end diff --git a/test/y2network/interface_config_builders/vlan_test.rb b/test/y2network/interface_config_builders/vlan_test.rb index 0f304c538..bcb8631ff 100644 --- a/test/y2network/interface_config_builders/vlan_test.rb +++ b/test/y2network/interface_config_builders/vlan_test.rb @@ -1,9 +1,29 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "../../test_helper" require "yast" require "y2network/interface_config_builders/vlan" +require "y2network/interface_type" describe Y2Network::InterfaceConfigBuilders::Vlan do subject(:config_builder) do @@ -12,9 +32,13 @@ res end + before do + allow(config_builder).to receive(:yast_config).and_return(Y2Network::Config.new(source: :testing)) + end + describe "#type" do - it "returns 'vlan'" do - expect(subject.type).to eq "vlan" + it "returns vlan type" do + expect(subject.type).to eq Y2Network::InterfaceType::VLAN end end diff --git a/test/y2network/interface_test.rb b/test/y2network/interface_test.rb index f8d3c92dd..cd598331d 100644 --- a/test/y2network/interface_test.rb +++ b/test/y2network/interface_test.rb @@ -24,6 +24,56 @@ described_class.new("eth0") end + describe ".from_connection" do + context "when the connection is virtual" do + let(:conn) do + Y2Network::ConnectionConfig::Bridge.new.tap { |c| c.name = "br0" } + end + + it "returns a virtual interface" do + interface = described_class.from_connection(conn) + expect(interface).to be_a(Y2Network::VirtualInterface) + end + end + + context "when the connection is not virtual" do + let(:conn) do + Y2Network::ConnectionConfig::Wireless.new.tap { |c| c.name = "wlan0" } + end + + it "returns a physical interface" do + interface = described_class.from_connection(conn) + expect(interface).to be_a(Y2Network::PhysicalInterface) + end + end + end + + describe "#rename" do + it "assign name to new_name parameter" do + interface.rename("eth1", :mac) + expect(interface.name).to eq "eth1" + end + + it "assign renaming_mechanism to mechanism parameter" do + interface.rename("eth1", :mac) + expect(interface.renaming_mechanism).to eq :mac + end + + context "if new_name differs to old one" do + it "assign old name to old_name attribute" do + interface.rename("eth1", :mac) + expect(interface.old_name).to eq "eth0" + end + end + + context "if new_name is same as old one" do + it "does not assign old_name attribute" do + interface.rename("eth0", :mac) + expect(interface.old_name).to eq nil + end + end + end + describe "#==" do context "given two interfaces with the same name" do let(:other) { Y2Network::Interface.new(interface.name) } @@ -41,4 +91,17 @@ end end end + + describe "#modules_names" do + let(:driver) { instance_double(Y2Network::Driver) } + let(:hwinfo) { instance_double(Y2Network::Hwinfo, drivers: [driver]) } + + before do + allow(interface).to receive(:hardware).and_return(hwinfo) + end + + it "returns modules names from hardware information" do + expect(interface.drivers).to eq([driver]) + end + end end diff --git a/test/y2network/interface_type_test.rb b/test/y2network/interface_type_test.rb index e148f1976..af449a48a 100644 --- a/test/y2network/interface_type_test.rb +++ b/test/y2network/interface_type_test.rb @@ -28,4 +28,25 @@ expect(type).to be(Y2Network::InterfaceType::WIRELESS) end end + + describe ".all" do + it "returns all known interface types" do + expect(described_class.all).to_not be_empty + expect(described_class.all.first).to be_a described_class + end + end + + describe "#?" do + it "returns true if name is same as in method name" do + expect(ethernet.ethernet?).to eq true + expect(ethernet.wireless?).to eq false + end + end + + describe "#?" do + it "returns true if short_name is same as in method name" do + expect(ethernet.eth?).to eq true + expect(ethernet.wlan?).to eq false + end + end end diff --git a/test/y2network/interfaces_collection_test.rb b/test/y2network/interfaces_collection_test.rb index ce38219bd..07b1d18c3 100644 --- a/test/y2network/interfaces_collection_test.rb +++ b/test/y2network/interfaces_collection_test.rb @@ -42,8 +42,8 @@ let(:eth0_hwinfo) { double("hwinfo", name: "eth0") } before do - allow(wlan0).to receive(:hwinfo).and_return(wlan0_hwinfo) - allow(eth0).to receive(:hwinfo).and_return(eth0_hwinfo) + allow(wlan0).to receive(:hardware).and_return(wlan0_hwinfo) + allow(eth0).to receive(:hardware).and_return(eth0_hwinfo) end it "returns the interface with the given name" do @@ -91,4 +91,31 @@ end end end + + describe "#physical" do + it "returns only physical interfaces" do + expect(collection.physical.map(&:name)).to eq(["eth0", "wlan0"]) + end + end + + describe "#known_names" do + it "returns the list of known interfaces" do + expect(collection.known_names).to eq(["eth0", "br0", "wlan0"]) + end + + context "when an interface was renamed" do + before do + eth0.rename("eth1", :mac) + end + it "returns the old and the new names" do + expect(collection.known_names).to eq(["eth0", "eth1", "br0", "wlan0"]) + end + end + end + + describe "#free_names" do + it "returns count of names with prefix that is not yet used" do + expect(collection.free_names("eth", 3)).to eq(["eth1", "eth2", "eth3"]) + end + end end diff --git a/test/y2network/ip_address_test.rb b/test/y2network/ip_address_test.rb new file mode 100644 index 000000000..95195fb3c --- /dev/null +++ b/test/y2network/ip_address_test.rb @@ -0,0 +1,135 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/ip_address" + +describe Y2Network::IPAddress do + subject(:ip) { described_class.new("192.168.122.1", 24) } + + describe ".from_string" do + context "when a string representing an IPv4 is given" do + let(:ip_address) { "192.168.122.1/24" } + + it "creates an IPAddress object" do + ip = described_class.from_string(ip_address) + expect(ip.address).to eq(IPAddr.new("192.168.122.1")) + expect(ip.prefix).to eq(24) + end + + context "when no prefix is given" do + let(:ip_address) { "192.168.122.1" } + + it "does not set a prefix" do + ip = described_class.from_string(ip_address) + expect(ip.address).to eq(IPAddr.new("192.168.122.1")) + expect(ip.prefix).to be_nil + end + end + end + + context "when a string representing an IPv6 is given" do + let(:ip_address) { "2001:db8:1234:ffff:ffff:ffff:ffff:fff1/48" } + + it "creates an IPAddress object" do + ip = described_class.from_string(ip_address) + expect(ip.address).to eq(IPAddr.new("2001:db8:1234:ffff:ffff:ffff:ffff:fff1")) + expect(ip.prefix).to eq(48) + end + + context "when no prefix is given" do + let(:ip_address) { "2001:db8:1234:ffff:ffff:ffff:ffff:fff1" } + + it "does not set a prefix" do + ip = described_class.from_string(ip_address) + expect(ip.address).to eq(IPAddr.new(ip_address)) + expect(ip.prefix).to be_nil + end + end + end + end + + describe "#to_s" do + subject(:ip) { described_class.new("192.168.122.1", 24) } + + it "returns a string CIDR based representation" do + expect(ip.to_s).to eq("192.168.122.1/24") + end + + context "when it is a host address" do + subject(:ip) { described_class.new("192.168.122.1") } + + it "omits the prefix" do + expect(ip.to_s).to eq("192.168.122.1") + end + end + end + + describe "#prefix=" do + it "sets the address prefix" do + expect { ip.prefix = 32 }.to change { ip.prefix }.from(24).to(32) + end + end + + describe "#netmask=" do + it "sets the address prefix" do + expect { ip.netmask = "255.255.255.255" }.to change { ip.prefix }.from(24).to(32) + end + end + + describe "#==" do + context "when address and prefix are the same" do + it "returns true" do + expect(described_class.new("192.168.122.1", 24)) + .to eq(described_class.new("192.168.122.1", 24)) + end + end + + context "when addresses are different" do + it "returns false" do + expect(described_class.new("192.168.122.1", 24)) + .to_not eq(described_class.new("192.168.122.2", 24)) + end + end + + context "when prefixes are different" do + it "returns false" do + expect(described_class.new("192.168.122.1", 24)) + .to_not eq(described_class.new("192.168.122.1", 32)) + + end + end + end + + describe "#prefix?" do + context "when a prefix was set" do + it "returns true" do + expect(ip.prefix?).to eq(true) + end + end + + context "when a prefix was not set" do + subject(:ip) { described_class.new("192.168.122.1") } + + it "returns false" do + expect(ip.prefix?).to eq(false) + end + end + end +end diff --git a/test/y2network/ipoib_mode_test.rb b/test/y2network/ipoib_mode_test.rb new file mode 100644 index 000000000..6f961bad3 --- /dev/null +++ b/test/y2network/ipoib_mode_test.rb @@ -0,0 +1,56 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" + +require "y2network/ipoib_mode" + +describe Y2Network::IpoibMode do + subject(:mode) { described_class.new("datagram") } + + describe ".all" do + it "returns all know IPoIB modes" do + expect(described_class.all).to contain_exactly( + Y2Network::IpoibMode::CONNECTED, + Y2Network::IpoibMode::DATAGRAM, + Y2Network::IpoibMode::DEFAULT + ) + end + end + + describe ".from_name" do + it "returns the IPoIB mode with the given mode" do + expect(described_class.from_name("datagram")).to eq(Y2Network::IpoibMode::DATAGRAM) + end + end + + describe "#==" do + context "when the other object refers to the same IPoIB mode" do + it "returns true" do + expect(mode).to eq(described_class.new("datagram")) + end + end + + context "when the other object refers to a different IPoIB mode" do + it "returns false" do + expect(mode).to_not eq(described_class.new("connected")) + end + end + end +end diff --git a/test/y2network/presenters/interface_summary_test.rb b/test/y2network/presenters/interface_summary_test.rb new file mode 100644 index 000000000..5590984b2 --- /dev/null +++ b/test/y2network/presenters/interface_summary_test.rb @@ -0,0 +1,71 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "y2network/config" +require "y2network/connection_config" +require "y2network/connection_configs_collection" +require "y2network/interface" +require "y2network/interfaces_collection" +require "y2network/presenters/interface_summary" + +describe Y2Network::Presenters::InterfaceSummary do + subject(:presenter) { described_class.new(name, config) } + + let(:name) { "vlan1" } + + let(:config) do + Y2Network::Config.new( + interfaces: interfaces, connections: connections, source: :testing + ) + end + let(:interfaces) do + Y2Network::InterfacesCollection.new( + [ + double(Y2Network::Interface, hardware: nil, name: "vlan1"), + double(Y2Network::Interface, hardware: double.as_null_object, name: "eth1"), + double(Y2Network::Interface, hardware: double.as_null_object, name: "eth0") + ] + ) + end + let(:connections) do + Y2Network::ConnectionConfigsCollection.new([vlan1, eth0]) + end + + let(:vlan1) do + config = Y2Network::ConnectionConfig::Vlan.new.tap(&:propose) + config.name = "vlan1" + config.parent_device = "eth0" + config + end + + let(:eth0) do + config = Y2Network::ConnectionConfig::Ethernet.new.tap(&:propose) + config.name = "eth0" + config + end + + describe "#text" do + it "returns a summary in text form" do + text = presenter.text + expect(text).to be_a(::String) + end + end +end diff --git a/test/y2network/presenters/interfaces_summary_test.rb b/test/y2network/presenters/interfaces_summary_test.rb new file mode 100644 index 000000000..156ceb62a --- /dev/null +++ b/test/y2network/presenters/interfaces_summary_test.rb @@ -0,0 +1,73 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "y2network/config" +require "y2network/connection_config" +require "y2network/connection_configs_collection" +require "y2network/interface" +require "y2network/interfaces_collection" +require "y2network/presenters/interfaces_summary" + +describe Y2Network::Presenters::InterfacesSummary do + subject(:presenter) { described_class.new(config) } + + # testing scenario: TODO: have easy yaml mockup format + # - eth0 configured + # - eth1 unconfingured + # - vlan1 on top of eth0 + let(:config) do + Y2Network::Config.new( + interfaces: interfaces, connections: connections, source: :testing + ) + end + let(:interfaces) do + Y2Network::InterfacesCollection.new( + [ + double(Y2Network::Interface, hardware: nil, name: "vlan1"), + double(Y2Network::Interface, hardware: double.as_null_object, name: "eth1"), + double(Y2Network::Interface, hardware: double.as_null_object, name: "eth0") + ] + ) + end + let(:connections) do + Y2Network::ConnectionConfigsCollection.new([vlan1, eth0]) + end + + let(:vlan1) do + config = Y2Network::ConnectionConfig::Vlan.new.tap(&:propose) + config.name = "vlan1" + config.parent_device = "eth0" + config + end + + let(:eth0) do + config = Y2Network::ConnectionConfig::Ethernet.new.tap(&:propose) + config.name = "eth0" + config + end + + describe "#text" do + it "returns a summary in text form" do + text = presenter.text + expect(text).to be_a(::String) + end + end +end diff --git a/test/y2network/proposal_settings_test.rb b/test/y2network/proposal_settings_test.rb index 61a0d6a91..376a71b47 100755 --- a/test/y2network/proposal_settings_test.rb +++ b/test/y2network/proposal_settings_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "../test_helper" require "y2network/proposal_settings" diff --git a/test/y2network/s390_device_activator_test.rb b/test/y2network/s390_device_activator_test.rb new file mode 100644 index 000000000..ff40e72ac --- /dev/null +++ b/test/y2network/s390_device_activator_test.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" + +require "y2network/s390_device_activator" +require "y2network/interface_config_builder" + +describe Y2Network::S390DeviceActivator do + let(:logger) { double(info: true) } + let(:known_builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + let(:unknown_builder) { Y2Network::InterfaceConfigBuilder.for("dummy") } + + describe ".for" do + context "specialized class for given known builder" do + it "returns new instance of that class" do + expect(described_class.for(known_builder).class.to_s).to eq "Y2Network::S390DeviceActivators::Qeth" + end + end + + context "specialized class for given builder does NOT exist" do + before do + allow(described_class).to receive(:log).and_return(logger) + end + + it "returns nil" do + expect(described_class.for(unknown_builder)).to be_nil + end + + it "logs the error" do + expect(logger).to receive(:info).with(/Specialized device activator for dummy not found/) + described_class.for(unknown_builder) + end + end + end +end diff --git a/test/y2network/s390_device_activators/ctc_test.rb b/test/y2network/s390_device_activators/ctc_test.rb new file mode 100644 index 000000000..0dda0f382 --- /dev/null +++ b/test/y2network/s390_device_activators/ctc_test.rb @@ -0,0 +1,140 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "y2network/s390_device_activators/ctc" +require "y2network/interface_config_builders/ctc" + +describe Y2Network::S390DeviceActivators::Ctc do + let(:builder) do + res = Y2Network::InterfaceConfigBuilders::Ctc.new + res.name = "ctc0" + res + end + + subject(:activator) { Y2Network::S390DeviceActivators::Ctc.new(builder) } + + let(:executor) { double("Yast::Execute", on_target!: "") } + let(:initialize_channels) { true } + before do + allow(Yast::Execute).to receive(:stdout).and_return(executor) + builder.read_channel = "0.0.0900" if initialize_channels + builder.write_channel = "0.0.0901" if initialize_channels + builder.protocol = 0 + end + + describe "#configure" do + it "tries to activate the group device associated with the defined device id" do + expect(Yast::Execute).to receive(:on_target!) + .with("/sbin/chzdev", "ctc", subject.device_id, "-e", + "protocol=#{builder.protocol}", allowed_exitstatus: 0..255) + .and_return(0) + subject.configure + end + + context "when activated succesfully" do + it "returns true" do + expect(Yast::Execute).to receive(:on_target!).and_return(0) + expect(subject.configure).to eq(true) + end + end + + context "when failed the activation and returned a non zero return code" do + it "returns false" do + expect(Yast::Execute).to receive(:on_target!).and_return(34) + expect(subject.configure).to eq(false) + end + end + end + + describe "#configured_interface" do + before do + allow(executor).to receive(:on_target!) + .with(["/sbin/lszdev", activator.device_id, "-c", "names", "-n"]) + .and_return("ctc1") + end + + it "obtains the network interface associated with builder device id" do + expect(subject.configured_interface).to eq("ctc1") + end + end + + describe "#device_id_from" do + context "given the read or write device id" do + let(:device_id) { "0.0.0800:0.0.0801" } + let(:write_channel) { "0.0.0801" } + let(:hwinfo) { Y2Network::Hwinfo.new("busid" => write_channel) } + before do + allow(builder).to receive(:hwinfo).and_return(hwinfo) + allow(executor).to receive(:on_target!) + .with(["/sbin/lszdev", "ctc", "-c", "id", "-n"]) + .and_return(device_id) + end + + it "obtains the triplet device ids listed by lszdev" do + expect(subject.device_id_from(hwinfo.busid)).to eq(device_id) + end + end + end + + describe "#device_id" do + it "returns the read and write channel device ids joined by ':'" do + expect(subject.device_id).to eql("0.0.0900:0.0.0901") + end + end + + describe "#propose_channels" do + context "when the read and write channel have not been initialized" do + let(:initialize_channels) { false } + let(:device_id) { "0.0.0800:0.0.0801" } + let(:write_channel) { "0.0.0801" } + let(:hwinfo) { Y2Network::Hwinfo.new("busid" => write_channel) } + + before do + allow(subject).to receive(:device_id_from).with(write_channel).and_return(device_id) + allow(builder).to receive(:hwinfo).and_return(hwinfo) + end + + it "initializes them from the given busid" do + expect { subject.propose_channels }.to change { subject.device_id }.from(nil).to(device_id) + end + end + end + + describe "#propose!" do + context "when no device id has been initialized" do + let(:initialize_channels) { false } + it "proposes the channel device ids to be used" do + expect(subject).to receive(:propose_channels) + subject.propose! + end + end + + context "when the channel device ids have been set already" do + it "does not propose anything" do + expect(subject).to_not receive(:propose_channels) + subject.propose! + end + end + end + +end diff --git a/test/y2network/s390_device_activators/lcs_test.rb b/test/y2network/s390_device_activators/lcs_test.rb new file mode 100644 index 000000000..52e7ab639 --- /dev/null +++ b/test/y2network/s390_device_activators/lcs_test.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "y2network/s390_device_activators/lcs" +require "y2network/interface_config_builders/lcs" + +describe Y2Network::S390DeviceActivators::Lcs do + let(:builder) do + res = Y2Network::InterfaceConfigBuilders::Lcs.new + res.name = "lcs0" + res + end + + subject(:activator) { Y2Network::S390DeviceActivator.for(builder) } + + let(:executor) { double("Yast::Execute", on_target!: "") } + let(:initialize_channels) { true } + before do + allow(Yast::Execute).to receive(:stdout).and_return(executor) + builder.read_channel = "0.0.0900" if initialize_channels + builder.write_channel = "0.0.0901" if initialize_channels + builder.timeout = 15 + end + + describe "#configure" do + it "tries to activate the group device associated with the defined device id" do + expect(Yast::Execute).to receive(:on_target!) + .with("/sbin/chzdev", "lcs", subject.device_id, "-e", + "lancmd_timeout=15", allowed_exitstatus: 0..255) + .and_return(0) + subject.configure + end + + context "when activated succesfully" do + it "returns true" do + expect(Yast::Execute).to receive(:on_target!).and_return(0) + expect(subject.configure).to eq(true) + end + end + + context "when failed the activation and returned a non zero return code" do + it "returns false" do + expect(Yast::Execute).to receive(:on_target!).and_return(34) + expect(subject.configure).to eq(false) + end + end + end +end diff --git a/test/y2network/s390_device_activators/qeth_test.rb b/test/y2network/s390_device_activators/qeth_test.rb new file mode 100644 index 000000000..5720f3446 --- /dev/null +++ b/test/y2network/s390_device_activators/qeth_test.rb @@ -0,0 +1,143 @@ +#!/usr/bin/env rspec + +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "y2network/s390_device_activators/qeth" +require "y2network/interface_config_builders/qeth" + +describe Y2Network::S390DeviceActivators::Qeth do + let(:builder) do + res = Y2Network::InterfaceConfigBuilders::Qeth.new + res.name = "eth0" + res + end + + subject(:activator) { Y2Network::S390DeviceActivator.for(builder) } + + let(:executor) { double("Yast::Execute", on_target!: "") } + let(:initialize_channels) { true } + before do + allow(Yast::Execute).to receive(:stdout).and_return(executor) + if initialize_channels + builder.read_channel = "0.0.0700" + builder.write_channel = "0.0.0701" + builder.data_channel = "0.0.0702" + end + end + + describe "#configure" do + it "tries to activate the group device associated with the defined device id" do + expect(Yast::Execute).to receive(:on_target!) + .with("/sbin/chzdev", "qeth", subject.device_id, "-e", + "ipa_takeover/enable=0", "layer2=0", "portno=0", + allowed_exitstatus: 0..255) + .and_return(0) + subject.configure + end + + context "when activated succesfully" do + it "returns true" do + expect(Yast::Execute).to receive(:on_target!).and_return(0) + expect(subject.configure).to eq(true) + end + end + + context "when failed the activation and returned a non zero return code" do + it "returns false" do + expect(Yast::Execute).to receive(:on_target!).and_return(34) + expect(subject.configure).to eq(false) + end + end + end + + describe "#configured_interface" do + before do + allow(executor).to receive(:on_target!) + .with(["/sbin/lszdev", activator.device_id, "-c", "names", "-n"]) + .and_return("eth1") + end + + it "obtains the network interface associated with builder device id" do + expect(subject.configured_interface).to eq("eth1") + end + end + + describe "#device_id_from" do + context "given the read or write device id" do + let(:device_id) { "0.0.0800:0.0.0801:0.0.0802" } + let(:write_channel) { "0.0.0801" } + let(:hwinfo) { Y2Network::Hwinfo.new("busid" => write_channel) } + before do + allow(builder).to receive(:hwinfo).and_return(hwinfo) + allow(executor).to receive(:on_target!) + .with(["/sbin/lszdev", "qeth", "-c", "id", "-n"]) + .and_return(device_id) + end + + it "obtains the triplet device ids listed by lszdev" do + expect(subject.device_id_from(hwinfo.busid)).to eq(device_id) + end + end + end + + describe "#device_id" do + it "returns the read and write channel device ids joined by ':'" do + expect(subject.device_id).to eql("0.0.0700:0.0.0701:0.0.0702") + end + end + + describe "#propose_channels" do + context "when the read and write channel have not been initialized" do + let(:initialize_channels) { false } + let(:device_id) { "0.0.0800:0.0.0801:0.0.0802" } + let(:write_channel) { "0.0.0801" } + let(:hwinfo) { Y2Network::Hwinfo.new("busid" => write_channel) } + + before do + allow(subject).to receive(:device_id_from).with(write_channel).and_return(device_id) + allow(builder).to receive(:hwinfo).and_return(hwinfo) + end + + it "initializes them from the given busid" do + expect { subject.propose_channels }.to change { subject.device_id }.from(nil).to(device_id) + end + end + end + + describe "#propose!" do + context "when no device id has been initialized" do + let(:initialize_channels) { false } + it "proposes the channel device ids to be used" do + expect(subject).to receive(:propose_channels) + subject.propose! + end + end + + context "when the channel device ids have been set already" do + it "does not propose anything" do + expect(subject).to_not receive(:propose_channels) + subject.propose! + end + end + end + +end diff --git a/test/y2network/sequences/interface_test.rb b/test/y2network/sequences/interface_test.rb index e3a89138a..0067b9436 100644 --- a/test/y2network/sequences/interface_test.rb +++ b/test/y2network/sequences/interface_test.rb @@ -26,9 +26,7 @@ describe Y2Network::Sequences::Interface do let(:builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" - res + Y2Network::InterfaceConfigBuilder.for("eth") end describe "#edit" do diff --git a/test/y2network/sysconfig/config_reader_test.rb b/test/y2network/sysconfig/config_reader_test.rb index 042e1486e..85537209f 100644 --- a/test/y2network/sysconfig/config_reader_test.rb +++ b/test/y2network/sysconfig/config_reader_test.rb @@ -28,13 +28,15 @@ let(:interfaces) { [eth0, wlan0] } let(:eth0_config) { instance_double(Y2Network::ConnectionConfig::Ethernet) } let(:connections) { [eth0_config] } + let(:drivers) { Y2Network::Driver.new("virtio_net", "") } let(:routes_file) { instance_double(Y2Network::Sysconfig::RoutesFile, load: nil, routes: []) } let(:dns_reader) { instance_double(Y2Network::Sysconfig::DNSReader, config: dns) } let(:interfaces_reader) do instance_double( Y2Network::Sysconfig::InterfacesReader, interfaces: Y2Network::InterfacesCollection.new(interfaces), - connections: connections + connections: connections, + drivers: drivers ) end @@ -70,6 +72,11 @@ expect(config.dns).to eq(dns) end + it "returns a configuration which includes available drivers" do + config = reader.config + expect(config.drivers).to eq(drivers) + end + it "sets the config source to :sysconfig" do config = reader.config expect(config.source).to eq(:sysconfig) diff --git a/test/y2network/sysconfig/config_writer_test.rb b/test/y2network/sysconfig/config_writer_test.rb index 3cd5f3395..7bdef9876 100644 --- a/test/y2network/sysconfig/config_writer_test.rb +++ b/test/y2network/sysconfig/config_writer_test.rb @@ -19,25 +19,50 @@ require_relative "../../test_helper" require "y2network/sysconfig/config_writer" require "y2network/config" +require "y2network/connection_configs_collection" require "y2network/interface" +require "y2network/interfaces_collection" require "y2network/routing" require "y2network/route" require "y2network/routing_table" require "y2network/sysconfig_paths" +require "y2network/connection_config/ethernet" +require "y2network/connection_config/ip_config" describe Y2Network::Sysconfig::ConfigWriter do subject(:writer) { described_class.new } describe "#write" do - let(:config) do + before do + allow(Yast::Host).to receive(:Write) + end + + let(:old_config) do Y2Network::Config.new( - interfaces: [eth0], - routing: routing, - source: :sysconfig + interfaces: Y2Network::InterfacesCollection.new([eth0]), + connections: Y2Network::ConnectionConfigsCollection.new([old_eth0_conn]), + source: :testing ) end - let(:old_config) { instance_double(Y2Network::Config, dns: double("dns"), interfaces: nil) } + + let(:config) do + old_config.copy.tap do |cfg| + cfg.add_or_update_connection_config(eth0_conn) + cfg.routing = routing + end + end + let(:ip) { Y2Network::ConnectionConfig::IPConfig.new(address: IPAddr.new("192.168.122.2")) } let(:eth0) { Y2Network::Interface.new("eth0") } + let(:old_eth0_conn) { eth0_conn.clone } + + let(:eth0_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.interface = "eth0" + conn.name = "eth0" + conn.bootproto = :static + conn.ip = ip + end + end let(:route) do Y2Network::Route.new( to: IPAddr.new("10.0.0.2/8"), @@ -71,6 +96,7 @@ let(:routes) { [route, default_route] } let(:dns_writer) { instance_double(Y2Network::Sysconfig::DNSWriter, write: nil) } + let(:interfaces_writer) { instance_double(Y2Network::Sysconfig::InterfacesWriter, write: nil) } before do allow(Y2Network::Sysconfig::RoutesFile).to receive(:new) @@ -81,7 +107,9 @@ .and_return(routes_file) allow(Y2Network::Sysconfig::DNSWriter).to receive(:new) .and_return(dns_writer) - allow(eth0).to receive(:configured).and_return(true) + allow_any_instance_of(Y2Network::Sysconfig::ConnectionConfigWriter).to receive(:write) + allow(Y2Network::Sysconfig::InterfacesWriter).to receive(:new) + .and_return(interfaces_writer) end it "saves general routes to main routes file" do @@ -117,10 +145,10 @@ context "when an interface is deleted" do let(:old_config) do - instance_double( - Y2Network::Config, + Y2Network::Config.new( dns: double("dns"), - interfaces: [eth0, eth1] + interfaces: Y2Network::InterfacesCollection.new([eth0, eth1]), + source: :testing ) end let(:eth1) { Y2Network::Interface.new("eth1") } @@ -132,7 +160,6 @@ allow(Y2Network::Sysconfig::RoutesFile).to receive(:new) .with("/etc/sysconfig/network/ifroute-eth1") .and_return(ifroute_eth1) - allow(eth1).to receive(:configured).and_return(true) end it "removes the ifroute file" do @@ -141,6 +168,40 @@ end end + context "when a connection is deleted" do + let(:config) do + old_config.copy.tap do |cfg| + cfg.interfaces = Y2Network::InterfacesCollection.new([eth0]) + cfg.connections = Y2Network::ConnectionConfigsCollection.new([]) + cfg.source = :testing + end + end + + let(:old_config) do + Y2Network::Config.new( + interfaces: Y2Network::InterfacesCollection.new([eth0]), + connections: Y2Network::ConnectionConfigsCollection.new([eth0_conn]), + source: :sysconfig + ) + end + + let(:conn_writer) { instance_double(Y2Network::Sysconfig::ConnectionConfigWriter) } + let(:ifroute_eth1) do + instance_double(Y2Network::Sysconfig::RoutesFile, save: nil, :routes= => nil, remove: nil) + end + + before do + allow(Y2Network::Sysconfig::ConnectionConfigWriter).to receive(:new) + .and_return(conn_writer) + allow(Y2Network::Sysconfig::RoutesFile).to receive(:new).and_return(ifroute_eth1) + end + + it "removes the connection" do + expect(conn_writer).to receive(:remove).with(eth0_conn) + writer.write(config, old_config) + end + end + context "When IPv4 forwarding is set" do let(:forward_ipv4) { true } @@ -179,5 +240,21 @@ expect(dns_writer).to receive(:write).with(config.dns, old_config.dns) writer.write(config, old_config) end + + it "writes connections configurations" do + expect_any_instance_of(Y2Network::Sysconfig::ConnectionConfigWriter) + .to receive(:write).with(eth0_conn, old_eth0_conn) + writer.write(config, old_config) + end + + it "writes interfaces configurations" do + expect(interfaces_writer).to receive(:write).with(config.interfaces) + writer.write(config) + end + + it "writes /etc/hosts changes" do + expect(Yast::Host).to receive(:Write).with(gui: false) + writer.write(config) + end end end diff --git a/test/y2network/sysconfig/connection_config_reader_test.rb b/test/y2network/sysconfig/connection_config_reader_test.rb index c218092c4..92fadd182 100644 --- a/test/y2network/sysconfig/connection_config_reader_test.rb +++ b/test/y2network/sysconfig/connection_config_reader_test.rb @@ -20,28 +20,28 @@ require_relative "../../test_helper" require "y2network/sysconfig/connection_config_reader" -require "y2network/sysconfig/connection_config_readers/wlan" +require "y2network/sysconfig/connection_config_readers/wireless" require "y2network/physical_interface" describe Y2Network::Sysconfig::ConnectionConfigReader do subject(:reader) { described_class.new } describe "#read" do - let(:interface) { instance_double(Y2Network::PhysicalInterface, name: "wlan0", type: :wlan) } + let(:interface) { instance_double(Y2Network::PhysicalInterface, name: "wlan0", type: "wlan") } let(:interface_file) do - instance_double(Y2Network::Sysconfig::InterfaceFile, type: :wlan).as_null_object + instance_double(Y2Network::Sysconfig::InterfaceFile, type: "wlan").as_null_object end let(:connection_config) { double("connection_config") } let(:handler) do instance_double( - Y2Network::Sysconfig::ConnectionConfigReaders::Wlan, + Y2Network::Sysconfig::ConnectionConfigReaders::Wireless, connection_config: connection_config ) end before do allow(reader).to receive(:require).and_call_original - allow(Y2Network::Sysconfig::ConnectionConfigReaders::Wlan).to receive(:new) + allow(Y2Network::Sysconfig::ConnectionConfigReaders::Wireless).to receive(:new) .and_return(handler) allow(Y2Network::Sysconfig::InterfaceFile).to receive(:new) .and_return(interface_file) @@ -49,8 +49,8 @@ it "uses the appropiate handler" do expect(reader).to receive(:require) - .with("y2network/sysconfig/connection_config_readers/wlan") - conn = reader.read(interface, :wlan) + .with("y2network/sysconfig/connection_config_readers/wireless") + conn = reader.read(interface, "wlan") expect(conn).to be(connection_config) end @@ -62,11 +62,11 @@ context "when the interface type is unknown" do let(:interface_file) do - instance_double(Y2Network::Sysconfig::InterfaceFile, type: :foo) + instance_double(Y2Network::Sysconfig::InterfaceFile, type: :foo).as_null_object end - it "returns nil" do - expect(reader.read(interface, :foo)).to be_nil + it "raise exception" do + expect { reader.read(interface, :null) }.to raise_error(RuntimeError) end end end diff --git a/test/y2network/sysconfig/connection_config_readers/eth_test.rb b/test/y2network/sysconfig/connection_config_readers/bonding_test.rb similarity index 59% rename from test/y2network/sysconfig/connection_config_readers/eth_test.rb rename to test/y2network/sysconfig/connection_config_readers/bonding_test.rb index b9eb124bc..ae0f6bdae 100644 --- a/test/y2network/sysconfig/connection_config_readers/eth_test.rb +++ b/test/y2network/sysconfig/connection_config_readers/bonding_test.rb @@ -18,29 +18,29 @@ # find current contact information at www.suse.com. require_relative "../../../test_helper" -require "y2network/sysconfig/connection_config_readers/eth" +require "y2network/sysconfig/connection_config_readers/bonding" require "y2network/sysconfig/interface_file" -describe Y2Network::Sysconfig::ConnectionConfigReaders::Eth do +describe Y2Network::Sysconfig::ConnectionConfigReaders::Bonding do subject(:handler) { described_class.new(file) } - let(:address) { IPAddr.new("192.168.122.1") } + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "bond0" } let(:file) do - instance_double( - Y2Network::Sysconfig::InterfaceFile, - name: "eth0", - bootproto: :static, - ip_address: address - ) + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) end describe "#connection_config" do - it "returns an ethernet connection config object" do - eth = handler.connection_config - expect(eth.interface).to eq("eth0") - expect(eth.bootproto).to eq(:static) - expect(eth.ip_address).to eq(address) + it "returns a bonding connection config object" do + bonding_conn = handler.connection_config + expect(bonding_conn.interface).to eq("bond0") + expect(bonding_conn.slaves).to eq(["eth0", "eth1"]) + expect(bonding_conn.options).to eq("mode=active-backup miimon=100") end end end diff --git a/test/y2network/sysconfig/connection_config_readers/bridge_test.rb b/test/y2network/sysconfig/connection_config_readers/bridge_test.rb new file mode 100644 index 000000000..fe57a1536 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/bridge_test.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/bridge" +require "y2network/sysconfig/interface_file" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Bridge do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "br0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a bridge connection config object" do + bridge_conn = handler.connection_config + expect(bridge_conn.interface).to eq("br0") + expect(bridge_conn.ports).to eq(["eth0", "eth1"]) + expect(bridge_conn.stp).to eq("on") + expect(bridge_conn.forward_delay).to eq(5) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/ctc_test.rb b/test/y2network/sysconfig/connection_config_readers/ctc_test.rb new file mode 100644 index 000000000..a5ad1e0a9 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/ctc_test.rb @@ -0,0 +1,49 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/ctc" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Ctc do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "ctc0" } + + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a ctc connection config object" do + ctc = handler.connection_config + expect(ctc.type).to eql(Y2Network::InterfaceType::CTC) + expect(ctc.interface).to eq("ctc0") + expect(ctc.ip.address).to eq(Y2Network::IPAddress.from_string("192.168.20.50/24")) + expect(ctc.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/dummy_test.rb b/test/y2network/sysconfig/connection_config_readers/dummy_test.rb new file mode 100644 index 000000000..a99158fe7 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/dummy_test.rb @@ -0,0 +1,48 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/dummy" +require "y2network/sysconfig/interface_file" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Dummy do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "dummy0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + let(:ip_address) { Y2Network::IPAddress.from_string("192.168.100.150/24") } + + it "returns a dummy connection config object" do + dummy_conn = handler.connection_config + expect(dummy_conn.interface).to eq("dummy0") + expect(dummy_conn.ip.address).to eq(ip_address) + expect(dummy_conn.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/ethernet_test.rb b/test/y2network/sysconfig/connection_config_readers/ethernet_test.rb new file mode 100644 index 000000000..cdcea4a29 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/ethernet_test.rb @@ -0,0 +1,99 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/ethernet" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Ethernet do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "eth0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns an ethernet connection config object" do + eth = handler.connection_config + expect(eth.interface).to eq("eth0") + expect(eth.ip.address).to eq(Y2Network::IPAddress.from_string("192.168.123.1/24")) + expect(eth.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + + context "when prefixlen is specified" do + let(:interface_name) { "eth2" } + + it "uses the prefixlen as the address prefix" do + eth = handler.connection_config + expect(eth.ip.address).to eq(Y2Network::IPAddress.from_string("172.16.0.1/12")) + end + end + + context "when netmask is specified" do + let(:interface_name) { "eth3" } + + it "uses the netmask to set the address prefix" do + eth = handler.connection_config + expect(eth.ip.address).to eq(Y2Network::IPAddress.from_string("10.0.0.1/8")) + end + end + + context "when the configuration is static" do + before do + Yast::Host.main + Yast::Host.Read + end + + context "and a hostname is specified" do + it "sets the hostname" do + eth = handler.connection_config + expect(eth.hostname).to eq("foo") + end + end + + context "and a hostname is not specified" do + let(:interface_name) { "eth1" } + + it "does not set the hostname" do + eth = handler.connection_config + expect(eth.hostname).to be_nil + end + end + end + + context "when the configuration is not static" do + let(:interface_name) { "eth4" } + + before { Yast::Host.Read } + + it "does not set the hostname" do + eth = handler.connection_config + expect(eth.hostname).to be_nil + end + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/hsi_test.rb b/test/y2network/sysconfig/connection_config_readers/hsi_test.rb new file mode 100644 index 000000000..533ed1ebf --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/hsi_test.rb @@ -0,0 +1,49 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/hsi" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Hsi do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "hsi0" } + + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a hsi connection config object" do + hsi = handler.connection_config + expect(hsi.type).to eql(Y2Network::InterfaceType::HSI) + expect(hsi.interface).to eq("hsi0") + expect(hsi.ip.address).to eq(Y2Network::IPAddress.from_string("192.168.100.10/24")) + expect(hsi.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/infiniband_test.rb b/test/y2network/sysconfig/connection_config_readers/infiniband_test.rb new file mode 100644 index 000000000..357c297d8 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/infiniband_test.rb @@ -0,0 +1,49 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/infiniband" +require "y2network/sysconfig/interface_file" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Infiniband do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "ib0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + let(:ip_address) { Y2Network::IPAddress.from_string("192.168.20.1/24") } + + it "returns a infiniband connection config object" do + infiniband_conn = handler.connection_config + expect(infiniband_conn.interface).to eq("ib0") + expect(infiniband_conn.ipoib_mode).to eq(Y2Network::IpoibMode::DATAGRAM) + expect(infiniband_conn.all_ips.map(&:address)).to eq([ip_address]) + expect(infiniband_conn.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/lcs_test.rb b/test/y2network/sysconfig/connection_config_readers/lcs_test.rb new file mode 100644 index 000000000..94a510d5d --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/lcs_test.rb @@ -0,0 +1,49 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/lcs" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Lcs do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "eth6" } + + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a lcs connection config object" do + lcs = handler.connection_config + expect(lcs.type).to eql(Y2Network::InterfaceType::LCS) + expect(lcs.interface).to eq("eth6") + expect(lcs.ip.address).to eq(Y2Network::IPAddress.from_string("192.168.70.100/24")) + expect(lcs.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/qeth_test.rb b/test/y2network/sysconfig/connection_config_readers/qeth_test.rb new file mode 100644 index 000000000..85d7f20c6 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/qeth_test.rb @@ -0,0 +1,49 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/qeth" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Qeth do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "eth5" } + + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a qeth connection config object" do + qeth = handler.connection_config + expect(qeth.type).to eql(Y2Network::InterfaceType::QETH) + expect(qeth.interface).to eq("eth5") + expect(qeth.ip.address).to eq(Y2Network::IPAddress.from_string("192.168.50.1/24")) + expect(qeth.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/tap_test.rb b/test/y2network/sysconfig/connection_config_readers/tap_test.rb new file mode 100644 index 000000000..374a86e44 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/tap_test.rb @@ -0,0 +1,48 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/tap" +require "y2network/sysconfig/interface_file" +require "y2network/interface_type" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Tap do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "tap0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a tap connection config object" do + tap_conn = handler.connection_config + expect(tap_conn.interface).to eq("tap0") + expect(tap_conn.type).to eq(Y2Network::InterfaceType::TAP) + expect(tap_conn.owner).to eq("nobody") + expect(tap_conn.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/tun_test.rb b/test/y2network/sysconfig/connection_config_readers/tun_test.rb new file mode 100644 index 000000000..45fc8cac2 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/tun_test.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/tun" +require "y2network/sysconfig/interface_file" +require "y2network/interface_type" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Tun do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "tun0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a tun connection config object" do + tun_conn = handler.connection_config + expect(tun_conn.interface).to eq("tun0") + expect(tun_conn.type).to eq(Y2Network::InterfaceType::TUN) + expect(tun_conn.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/usb_test.rb b/test/y2network/sysconfig/connection_config_readers/usb_test.rb new file mode 100644 index 000000000..f1b289b8e --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/usb_test.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/usb" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Usb do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "usb0" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns an usb connection config object" do + usb = handler.connection_config + expect(usb.interface).to eq("usb0") + expect(usb.ip.address).to eq(Y2Network::IPAddress.from_string("192.168.231.33/24")) + expect(usb.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/vlan_test.rb b/test/y2network/sysconfig/connection_config_readers/vlan_test.rb new file mode 100644 index 000000000..c535f4ea4 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_readers/vlan_test.rb @@ -0,0 +1,47 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_readers/vlan" +require "y2network/sysconfig/interface_file" + +describe Y2Network::Sysconfig::ConnectionConfigReaders::Vlan do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end + + let(:interface_name) { "eth0.100" } + let(:file) do + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) + end + + describe "#connection_config" do + it "returns a vlan connection config object" do + vlan_conn = handler.connection_config + expect(vlan_conn.interface).to eq("eth0.100") + expect(vlan_conn.vlan_id).to eq(100) + expect(vlan_conn.parent_device).to eq("eth0") + expect(vlan_conn.bootproto).to eq(Y2Network::BootProtocol::STATIC) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_readers/wlan_test.rb b/test/y2network/sysconfig/connection_config_readers/wireless_test.rb similarity index 52% rename from test/y2network/sysconfig/connection_config_readers/wlan_test.rb rename to test/y2network/sysconfig/connection_config_readers/wireless_test.rb index 655c49c19..c8b458173 100644 --- a/test/y2network/sysconfig/connection_config_readers/wlan_test.rb +++ b/test/y2network/sysconfig/connection_config_readers/wireless_test.rb @@ -18,43 +18,26 @@ # find current contact information at www.suse.com. require_relative "../../../test_helper" -require "y2network/sysconfig/connection_config_readers/wlan" +require "y2network/sysconfig/connection_config_readers/wireless" require "y2network/sysconfig/interface_file" -describe Y2Network::Sysconfig::ConnectionConfigReaders::Wlan do +describe Y2Network::Sysconfig::ConnectionConfigReaders::Wireless do subject(:handler) { described_class.new(file) } - let(:address) { IPAddr.new("192.168.122.1") } + let(:interface_name) { "wlan0" } + + let(:scr_root) { File.join(DATA_PATH, "scr_read") } + + around do |example| + change_scr_root(scr_root, &example) + end let(:file) do - instance_double( - Y2Network::Sysconfig::InterfaceFile, - name: "wlan0", - startmode: :auto, - bootproto: :static, - ip_address: address, - wireless_keys: ["0-1-2-3-4-5", "s:password"], - wireless_default_key: 1, - wireless_key_length: 128 - ).as_null_object + Y2Network::Sysconfig::InterfaceFile.find(interface_name).tap(&:load) end context "WPA-EAP network configuration" do - let(:file) do - instance_double( - Y2Network::Sysconfig::InterfaceFile, - name: "wlan0", - bootproto: :static, - ip_address: address, - wireless_auth_mode: "eap", - wireless_eap_mode: "PEAP", - wireless_eap_auth: "mschapv2", - wireless_essid: "example_ssid", - wireless_mode: "Managed", - wireless_wpa_password: "example_passwd", - wireless_ap_scanmode: "1" - ).as_null_object - end + let(:interface_name) { "wlan0" } it "returns a wireless connection config object" do wlan = handler.connection_config @@ -65,10 +48,10 @@ wlan = handler.connection_config expect(wlan).to have_attributes( interface: "wlan0", - mode: "Managed", + mode: :managed, essid: "example_ssid", ap_scanmode: "1", - auth_mode: "eap", + auth_mode: :eap, eap_mode: "PEAP", eap_auth: "mschapv2", wpa_password: "example_passwd" @@ -77,24 +60,7 @@ end context "WPA-PSK network configuration" do - let(:wireless_attributes) do - COMMON_PARAMETERS.merge( - "WIRELESS_AP" => "00:11:22:33:44:55", - "WIRELESS_AUTH_MODE" => "psk", - "WIRELESS_ESSID" => "example_ssid", - "WIRELESS_WPA_PSK" => "example_psk" - ) - end - let(:file) do - instance_double( - Y2Network::Sysconfig::InterfaceFile, - name: "wlan0", - bootproto: :static, - wireless_ap: "00:11:22:33:44:55", - wireless_auth_mode: "psk", - wireless_wpa_psk: "example_psk" - ).as_null_object - end + let(:interface_name) { "wlan1" } it "returns a wireless connection config object" do wlan = handler.connection_config @@ -104,8 +70,8 @@ it "sets relevant attributes" do wlan = handler.connection_config expect(wlan).to have_attributes( - interface: "wlan0", - auth_mode: "psk", + interface: "wlan1", + auth_mode: :psk, wpa_psk: "example_psk", ap: "00:11:22:33:44:55" ) @@ -113,19 +79,7 @@ end context "WEP network configuration" do - let(:file) do - instance_double( - Y2Network::Sysconfig::InterfaceFile, - name: "wlan0", - bootproto: :static, - ip_address: address, - wireless_auth_mode: "shared", - wireless_essid: "example_ssid", - wireless_keys: ["0-1-2-3-4-5", "s:password"], - wireless_key_length: 128, - wireless_default_key: 1 - ).as_null_object - end + let(:interface_name) { "wlan2" } it "returns a wireless connection config object" do wlan = handler.connection_config @@ -135,9 +89,9 @@ it "sets relevant attributes" do wlan = handler.connection_config expect(wlan).to have_attributes( - interface: "wlan0", + interface: "wlan2", essid: "example_ssid", - keys: ["0-1-2-3-4-5", "s:password"], + keys: ["0-1-2-3-4-5", "s:password", nil, nil], key_length: 128, default_key: 1 ) @@ -145,14 +99,7 @@ end context "open network configuration" do - let(:file) do - instance_double( - Y2Network::Sysconfig::InterfaceFile, - name: "wlan0", - wireless_auth_mode: :open, - wireless_mode: :managed - ).as_null_object - end + let(:interface_name) { "wlan3" } it "returns a wireless connection object" do wlan = handler.connection_config diff --git a/test/y2network/sysconfig/connection_config_writer_test.rb b/test/y2network/sysconfig/connection_config_writer_test.rb new file mode 100644 index 000000000..99abf8909 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writer_test.rb @@ -0,0 +1,119 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "y2network/sysconfig/connection_config_writer" +require "y2network/sysconfig/connection_config_writers/ethernet" +require "y2network/connection_config/ethernet" +require "y2network/interface_type" + +describe Y2Network::Sysconfig::ConnectionConfigWriter do + subject(:writer) { described_class.new } + + let(:conn) do + instance_double( + Y2Network::ConnectionConfig::Ethernet, + interface: "eth0", + type: Y2Network::InterfaceType::ETHERNET, + ip: ip_config + ) + end + + let(:old_conn) do + instance_double( + Y2Network::ConnectionConfig::Ethernet, + interface: "eth0", + type: Y2Network::InterfaceType::ETHERNET + ) + end + + let(:ip_config) do + Y2Network::ConnectionConfig::IPConfig.new(Y2Network::IPAddress.from_string("10.100.0.1/24")) + end + + let(:file) do + instance_double( + Y2Network::Sysconfig::InterfaceFile, save: nil, clean: nil, remove: nil + ) + end + + describe "#write" do + let(:handler) do + instance_double( + Y2Network::Sysconfig::ConnectionConfigWriters::Ethernet, + write: nil + ) + end + + before do + allow(writer).to receive(:require).and_call_original + allow(Y2Network::Sysconfig::ConnectionConfigWriters::Ethernet).to receive(:new) + .and_return(handler) + allow(Y2Network::Sysconfig::InterfaceFile).to receive(:new).and_return(file) + end + + it "uses the appropiate handler" do + expect(writer).to receive(:require).and_return(handler) + expect(handler).to receive(:write).with(conn) + writer.write(conn) + end + + it "cleans old values and writes new ones" do + expect(file).to receive(:clean) + expect(file).to receive(:save) + writer.write(conn) + end + + it "removes the old configuration if given" do + expect(writer).to receive(:remove).with(old_conn) + writer.write(conn, old_conn) + end + + it "does nothing if the connection has not changed" do + expect(file).to_not receive(:save) + writer.write(conn, conn) + end + end + + describe "#remove" do + before do + allow(Y2Network::Sysconfig::InterfaceFile).to receive(:find).and_return(file) + end + + it "removes the configuration file" do + expect(file).to receive(:remove) + writer.remove(conn) + end + + it "removes the hostname" do + expect(Yast::Host).to receive(:remove_ip).with(conn.ip.address.address.to_s) + writer.remove(conn) + end + + context "if no IP address is defined" do + let(:ip_config) { nil } + + it "does not try to remove the hostname" do + expect(Yast::Host).to_not receive(:remove_ip) + writer.remove(conn) + end + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/bonding_test.rb b/test/y2network/sysconfig/connection_config_writers/bonding_test.rb new file mode 100644 index 000000000..0eb76f04e --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/bonding_test.rb @@ -0,0 +1,61 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/bonding" +require "y2network/sysconfig/interface_file" +require "y2network/startmode" +require "y2network/connection_config/bonding" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Bonding do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Bonding.new.tap do |c| + c.name = "bond0" + c.interface = "bond0" + c.description = "" + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::DHCP + c.slaves = ["eth0", "eth1"] + c.options = "mode=active-backup miimon=100" + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + startmode: "auto", + bootproto: "dhcp" + ) + end + + it "writes bonding properties" do + handler.write(conn) + expect(file).to have_attributes( + bonding_slaves: { 0 => "eth0", 1 => "eth1" }, + bonding_module_opts: "mode=active-backup miimon=100" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/bridge_test.rb b/test/y2network/sysconfig/connection_config_writers/bridge_test.rb new file mode 100644 index 000000000..8dd861f74 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/bridge_test.rb @@ -0,0 +1,66 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/bridge" +require "y2network/sysconfig/interface_file" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/bridge" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Bridge do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Bridge.new.tap do |c| + c.name = "br1" + c.interface = "br1" + c.description = "" + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::DHCP + c.ports = ["eth0", "eth1"] + c.stp = false + c.forward_delay = 5 + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + name: conn.description, + startmode: "auto", + bootproto: "dhcp" + ) + end + + it "writes bridge properties" do + handler.write(conn) + expect(file).to have_attributes( + bridge: "yes", + bridge_ports: "eth0 eth1", + bridge_forwarddelay: 5, + bridge_stp: "off" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/ctc_test.rb b/test/y2network/sysconfig/connection_config_writers/ctc_test.rb new file mode 100644 index 000000000..f7acb157f --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/ctc_test.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/ctc" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/ctc" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Ctc do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Ctc.new.tap do |c| + c.name = "ctc1" + c.interface = "ctc1" + c.bootproto = Y2Network::BootProtocol::DHCP + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "dhcp", + startmode: "auto" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/dummy_test.rb b/test/y2network/sysconfig/connection_config_writers/dummy_test.rb new file mode 100644 index 000000000..aa4a9665e --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/dummy_test.rb @@ -0,0 +1,75 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/dummy" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/dummy" +require "y2network/connection_config/ip_config" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Dummy do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { Dir.mktmpdir } + + around do |example| + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end + end + + let(:ip) do + Y2Network::ConnectionConfig::IPConfig.new(Y2Network::IPAddress.from_string("10.0.0.100/24")) + end + + let(:conn) do + Y2Network::ConnectionConfig::Dummy.new.tap do |c| + c.name = "dummy1" + c.interface = "dummy1" + c.description = "" + c.ip = ip + c.bootproto = Y2Network::BootProtocol::STATIC + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "static", + startmode: "auto" + ) + end + + it "writes the interfacetype as 'dummy'" do + handler.write(conn) + expect(file).to have_attributes( + interfacetype: Y2Network::InterfaceType::DUMMY.short_name + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/ethernet_test.rb b/test/y2network/sysconfig/connection_config_writers/ethernet_test.rb new file mode 100644 index 000000000..a59ad9e33 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/ethernet_test.rb @@ -0,0 +1,111 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/ethernet" +require "y2network/boot_protocol" +require "y2network/startmode" +require "y2network/sysconfig/interface_file" +require "y2network/connection_config/ethernet" +require "y2network/connection_config/ip_config" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Ethernet do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { Dir.mktmpdir } + + around do |example| + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end + end + + let(:ip) do + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("192.168.122.1/24"), + id: "", broadcast: Y2Network::IPAddress.from_string("192.168.122.255") + ) + end + + let(:ip_alias) do + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("10.0.0.1/8"), + id: "_0", label: "my-label", remote_address: Y2Network::IPAddress.from_string("10.0.0.2") + ) + end + + let(:all_ips) { [ip, ip_alias] } + + let(:conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |c| + c.name = "eth0" + c.interface = "eth0" + c.description = "Ethernet Card 0" + c.bootproto = Y2Network::BootProtocol::STATIC + c.ip = ip + c.ip_aliases = [ip_alias] + c.startmode = Y2Network::Startmode.create("auto") + c.hostname = "foo" + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.find(conn.interface) } + + describe "#write" do + it "updates common properties" do + handler.write(conn) + expect(file).to have_attributes( + name: conn.description, + bootproto: "static", + startmode: "auto" + ) + end + + it "sets IP configuration attributes" do + handler.write(conn) + expect(file).to have_attributes( + ipaddrs: { "" => ip.address, "_0" => ip_alias.address }, + broadcasts: { "" => ip.broadcast, "_0" => nil }, + remote_ipaddrs: { "" => nil, "_0" => ip_alias.remote_address }, + labels: { "" => nil, "_0" => "my-label" } + ) + end + + context "when using dhcp" do + before do + conn.bootproto = Y2Network::BootProtocol::DHCP + end + + it "only writes ip aliases" do + handler.write(conn) + expect(file.ipaddrs[""]).to be_nil + expect(file.ipaddrs["_0"]).to eq(ip_alias.address) + end + end + + it "sets the hostname" do + expect(Yast::Host).to receive(:Update).with("", "foo", ip.address.address.to_s) + handler.write(conn) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/hsi_test.rb b/test/y2network/sysconfig/connection_config_writers/hsi_test.rb new file mode 100644 index 000000000..63d016b43 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/hsi_test.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/hsi" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/hsi" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Hsi do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Hsi.new.tap do |c| + c.name = "hsi1" + c.interface = "hsi1" + c.bootproto = Y2Network::BootProtocol::DHCP + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "dhcp", + startmode: "auto" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/infiniband_test.rb b/test/y2network/sysconfig/connection_config_writers/infiniband_test.rb new file mode 100644 index 000000000..06c614552 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/infiniband_test.rb @@ -0,0 +1,69 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/infiniband" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/ipoib_mode" +require "y2network/connection_config/infiniband" +require "y2network/connection_config/ip_config" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Infiniband do + subject(:handler) { described_class.new(file) } + + let(:scr_root) { Dir.mktmpdir } + + around do |example| + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end + end + + let(:ip) do + Y2Network::ConnectionConfig::IPConfig.new(Y2Network::IPAddress.from_string("192.168.20.1/24")) + end + + let(:conn) do + Y2Network::ConnectionConfig::Infiniband.new.tap do |c| + c.name = "ib0" + c.interface = "ib0" + c.description = "" + c.ipoib_mode = Y2Network::IpoibMode::CONNECTED + c.ip = ip + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::STATIC + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes the 'ipoib_mode' attribute" do + handler.write(conn) + expect(file).to have_attributes( + ipoib_mode: "connected" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/lcs_test.rb b/test/y2network/sysconfig/connection_config_writers/lcs_test.rb new file mode 100644 index 000000000..44d56ba45 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/lcs_test.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/lcs" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/lcs" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Lcs do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Lcs.new.tap do |c| + c.name = "eth0" + c.interface = "eth0" + c.bootproto = Y2Network::BootProtocol::DHCP + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "dhcp", + startmode: "auto" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/qeth_test.rb b/test/y2network/sysconfig/connection_config_writers/qeth_test.rb new file mode 100644 index 000000000..4aabcd339 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/qeth_test.rb @@ -0,0 +1,52 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/qeth" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/qeth" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Qeth do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Qeth.new.tap do |c| + c.name = "eth6" + c.interface = "eth6" + c.bootproto = Y2Network::BootProtocol::STATIC + c.lladdress = "00:06:29:55:2A:04" + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "static", + startmode: "auto", + lladdr: "00:06:29:55:2A:04" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/tap_test.rb b/test/y2network/sysconfig/connection_config_writers/tap_test.rb new file mode 100644 index 000000000..00b985ade --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/tap_test.rb @@ -0,0 +1,85 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/tap" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/tap" +require "y2network/connection_config/ip_config" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Tap do + subject(:handler) { described_class.new(file) } + + def file_content(scr_root, file) + path = File.join(scr_root, file.path.to_s) + File.read(path) + end + + let(:scr_root) { Dir.mktmpdir } + + around do |example| + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end + end + + let(:conn) do + Y2Network::ConnectionConfig::Tap.new.tap do |c| + c.name = "tap1" + c.interface = "tap1" + c.owner = "nobody" + c.group = "nobody" + c.description = "" + c.bootproto = Y2Network::BootProtocol::STATIC + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "static", + startmode: "auto" + ) + end + + it "writes 'tunnel' as 'tap'" do + handler.write(conn) + expect(file).to have_attributes( + tunnel: "tap" + ) + end + + it "writes the 'tunnel_set_owner' and 'tunnel_set_group'" do + handler.write(conn) + expect(file).to have_attributes( + tunnel_set_owner: "nobody", + tunnel_set_group: "nobody" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/tun_test.rb b/test/y2network/sysconfig/connection_config_writers/tun_test.rb new file mode 100644 index 000000000..86cefa30d --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/tun_test.rb @@ -0,0 +1,85 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/tun" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/tun" +require "y2network/connection_config/ip_config" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Tun do + subject(:handler) { described_class.new(file) } + + def file_content(scr_root, file) + path = File.join(scr_root, file.path.to_s) + File.read(path) + end + + let(:scr_root) { Dir.mktmpdir } + + around do |example| + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end + end + + let(:conn) do + Y2Network::ConnectionConfig::Tun.new.tap do |c| + c.name = "tun1" + c.interface = "tun1" + c.owner = "nobody" + c.group = "nobody" + c.description = "" + c.bootproto = Y2Network::BootProtocol::STATIC + c.startmode = Y2Network::Startmode.create("auto") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "static", + startmode: "auto" + ) + end + + it "writes 'tunnel' as 'tun'" do + handler.write(conn) + expect(file).to have_attributes( + tunnel: "tun" + ) + end + + it "writes the 'tunnel_set_owner' and 'tunnel_set_group'" do + handler.write(conn) + expect(file).to have_attributes( + tunnel_set_owner: "nobody", + tunnel_set_group: "nobody" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/usb_test.rb b/test/y2network/sysconfig/connection_config_writers/usb_test.rb new file mode 100644 index 000000000..175b497e4 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/usb_test.rb @@ -0,0 +1,50 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/usb" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/usb" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Usb do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Usb.new.tap do |c| + c.name = "usb1" + c.interface = "usb1" + c.bootproto = Y2Network::BootProtocol::DHCP + c.startmode = Y2Network::Startmode.create("manual") + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes common properties" do + handler.write(conn) + expect(file).to have_attributes( + bootproto: "dhcp", + startmode: "manual" + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/vlan_test.rb b/test/y2network/sysconfig/connection_config_writers/vlan_test.rb new file mode 100644 index 000000000..9010cf163 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/vlan_test.rb @@ -0,0 +1,54 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" + +require "y2network/sysconfig/connection_config_writers/vlan" +require "y2network/startmode" +require "y2network/boot_protocol" +require "y2network/connection_config/vlan" +require "y2network/connection_config/ip_config" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Vlan do + subject(:handler) { described_class.new(file) } + + let(:conn) do + Y2Network::ConnectionConfig::Vlan.new.tap do |c| + c.name = "eth0.100" + c.interface = "eth0.100" + c.description = "" + c.parent_device = "eth0" + c.vlan_id = 100 + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::DHCP + end + end + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new(conn.name) } + + describe "#write" do + it "writes the 'etherdevice' and the 'vlan_id' attributes" do + handler.write(conn) + expect(file).to have_attributes( + etherdevice: "eth0", + vlan_id: 100 + ) + end + end +end diff --git a/test/y2network/sysconfig/connection_config_writers/wireless_test.rb b/test/y2network/sysconfig/connection_config_writers/wireless_test.rb new file mode 100644 index 000000000..8b76955e7 --- /dev/null +++ b/test/y2network/sysconfig/connection_config_writers/wireless_test.rb @@ -0,0 +1,153 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "y2network/sysconfig/connection_config_writers/wireless" +require "y2network/sysconfig/interface_file" +require "y2network/boot_protocol" +require "y2network/startmode" +require "y2network/connection_config/wireless" + +describe Y2Network::Sysconfig::ConnectionConfigWriters::Wireless do + subject(:handler) { described_class.new(file) } + + let(:file) { Y2Network::Sysconfig::InterfaceFile.new("wlan0") } + + let(:conn) do + Y2Network::ConnectionConfig::Wireless.new.tap do |c| + c.interface = "wlan0" + c.description = "Wireless Card 0" + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::STATIC + c.ip = ip + c.ip_aliases = [ip_alias] + c.mode = "managed" + c.essid = "example_essid" + c.auth_mode = :open + c.ap = "00:11:22:33:44:55" + c.ap_scanmode = "1" + end + end + + let(:ip) do + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("192.168.122.1/24"), + id: "", broadcast: Y2Network::IPAddress.from_string("192.168.122.255") + ) + end + + let(:ip_alias) do + Y2Network::ConnectionConfig::IPConfig.new( + Y2Network::IPAddress.from_string("10.0.0.1/8"), + id: "_0", label: "my-label", remote_address: Y2Network::IPAddress.from_string("10.0.0.2") + ) + end + + it "sets relevant attributes" do + handler.write(conn) + expect(file).to have_attributes( + startmode: "auto", + bootproto: "static", + wireless_mode: conn.mode, + wireless_essid: conn.essid, + wireless_auth_mode: :open, + wireless_ap: conn.ap, + wireless_ap_scanmode: conn.ap_scanmode + ) + end + + it "sets IP configuration attributes" do + handler.write(conn) + expect(file).to have_attributes( + ipaddrs: { "" => ip.address, "_0" => ip_alias.address }, + broadcasts: { "" => ip.broadcast, "_0" => nil }, + remote_ipaddrs: { "" => nil, "_0" => ip_alias.remote_address }, + labels: { "" => nil, "_0" => "my-label" } + ) + end + + context "WPA-EAP network configuration" do + let(:conn) do + Y2Network::ConnectionConfig::Wireless.new.tap do |c| + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::STATIC + c.mode = "managed" + c.essid = "example_essid" + c.auth_mode = "eap" + c.eap_mode = "PEAP" + c.essid = "example_essid" + c.wpa_password = "example_passwd" + end + end + + it "sets relevant attributes" do + handler.write(conn) + expect(file).to have_attributes( + wireless_auth_mode: "eap", + wireless_eap_mode: "PEAP", + wireless_essid: "example_essid", + wireless_wpa_password: "example_passwd" + ) + end + end + + context "WPA-PSK network configuration" do + let(:conn) do + Y2Network::ConnectionConfig::Wireless.new.tap do |c| + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::STATIC + c.mode = "managed" + c.auth_mode = "psk" + c.wpa_psk = "example_psk" + end + end + + it "sets relevant attributes" do + handler.write(conn) + expect(file).to have_attributes( + wireless_auth_mode: "psk", + wireless_wpa_psk: "example_psk" + ) + end + end + + context "WEP network configuration" do + let(:conn) do + Y2Network::ConnectionConfig::Wireless.new.tap do |c| + c.startmode = Y2Network::Startmode.create("auto") + c.bootproto = Y2Network::BootProtocol::STATIC + c.mode = "managed" + c.auth_mode = "shared" + c.keys = ["123456", "abcdef"] + c.key_length = 128 + c.default_key = 1 + end + end + + it "sets relevant attributes" do + handler.write(conn) + expect(file).to have_attributes( + wireless_auth_mode: "shared", + wireless_keys: ["123456", "abcdef"], + wireless_key_length: 128, + wireless_default_key: 1 + ) + end + end +end diff --git a/test/y2network/sysconfig/dns_reader_test.rb b/test/y2network/sysconfig/dns_reader_test.rb index efbb1836b..1a83c5b91 100644 --- a/test/y2network/sysconfig/dns_reader_test.rb +++ b/test/y2network/sysconfig/dns_reader_test.rb @@ -39,8 +39,13 @@ { "config" => netconfig_dns, "dhcp" => netconfig_dhcp } end - let(:executor) do - double("Yast::Execute", on_target!: "") + let(:hostname_reader) { instance_double(Y2Network::HostnameReader, hostname: "foo") } + + let(:ifcfg_eth0) do + instance_double( + Y2Network::Sysconfig::InterfaceFile, interface: "eth0", dhclient_set_hostname: nil, + load: nil + ) end before do @@ -48,7 +53,8 @@ key_parts = arg.to_s.split(".") netconfig.dig(*key_parts[3..-1]) end - allow(Yast::Execute).to receive(:stdout).and_return(executor) + allow(Y2Network::HostnameReader).to receive(:new).and_return(hostname_reader) + allow(Y2Network::Sysconfig::InterfaceFile).to receive(:all).and_return([ifcfg_eth0]) end it "returns the DNS configuration" do @@ -57,61 +63,10 @@ end it "includes the hostname" do - expect(executor).to receive(:on_target!).with(/hostname/).and_return("foo") config = reader.config expect(config.hostname).to eq("foo") end - context "during installation" do - before do - allow(Yast::Mode).to receive(:installation).and_return(true) - allow(Yast::FileUtils).to receive(:Exists).with("/etc/install.inf") - .and_return(install_inf_exists) - allow(Yast::SCR).to receive(:Read).and_return(hostname) - allow(executor).to receive(:on_target!).with(/hostname/).and_return("foo") - end - - let(:hostname) { "linuxrc.example.net" } - let(:install_inf_exists) { true } - - it "reads the hostname from /etc/install.conf" do - config = reader.config - expect(config.hostname).to eq("linuxrc") - end - - context "and the hostname from /etc/install.conf is an IP address" do - let(:hostname) { "192.168.122.1" } - - before do - allow(Yast::NetHwDetection).to receive(:ResolveIP).with(hostname) - .and_return("router") - end - - it "returns the name for the address" do - config = reader.config - expect(config.hostname).to eq("router") - end - end - - context "and the hostname is not defined in /etc/install.conf" do - let(:hostname) { nil } - - it "reads the hostname from the system" do - config = reader.config - expect(config.hostname).to eq("foo") - end - end - - context "and the /etc/install.inf file does not exists" do - let(:install_inf_exists) { false } - - it "reads the hostname from the system" do - config = reader.config - expect(config.hostname).to eq("foo") - end - end - end - it "includes the list of name servers" do config = reader.config expect(config.nameservers).to eq([IPAddr.new("1.1.1.1"), IPAddr.new("2.2.2.2")]) @@ -160,19 +115,64 @@ end end - it "sets the dhcp_hostname parameter" do - config = reader.config - expect(config.dhcp_hostname).to eq(true) + context "when DHCLIENT_SET_HOSTNAME is set to 'yes'" do + let(:netconfig_dhcp) do + { "DHCLIENT_SET_HOSTNAME" => "yes" } + end + + it "sets dhcp_hostname to :any" do + config = reader.config + expect(config.dhcp_hostname).to eq(:any) + end end - context "when DHCLIENT_SET_HOSTNAME is not set to 'yes'" do + context "when DHCLIENT_SET_HOSTNAME is set to 'no'" do let(:netconfig_dhcp) do { "DHCLIENT_SET_HOSTNAME" => "no" } end - it "sets dhcp_hostname to false" do + it "sets dhcp_hostname to :none" do config = reader.config - expect(config.dhcp_hostname).to eq(false) + expect(config.dhcp_hostname).to eq(:none) + end + + context "when a interface configuration file has DHCLIENT_SET_HOSTNAME set to 'yes'" do + let(:ifcfg_eth0) do + instance_double( + Y2Network::Sysconfig::InterfaceFile, interface: "eth0", dhclient_set_hostname: "yes", + load: nil + ) + end + + it "returns the interface name" do + config = reader.config + expect(config.dhcp_hostname).to eq("eth0") + end + end + end + + context "when DHCLIENT_SET_HOSTNAME is missing" do + let(:netconfig_dhcp) do + { "DHCLIENT_SET_HOSTNAME" => nil } + end + + it "sets dhcp_hostname to :none" do + config = reader.config + expect(config.dhcp_hostname).to eq(:none) + end + + context "when a interface configuration file has DHCLIENT_SET_HOSTNAME set to 'yes'" do + let(:ifcfg_eth0) do + instance_double( + Y2Network::Sysconfig::InterfaceFile, interface: "eth0", dhclient_set_hostname: "yes", + load: nil + ) + end + + it "returns the interface name" do + config = reader.config + expect(config.dhcp_hostname).to eq("eth0") + end end end end diff --git a/test/y2network/sysconfig/dns_writer_test.rb b/test/y2network/sysconfig/dns_writer_test.rb index a8bce9f35..e2aa8a82a 100644 --- a/test/y2network/sysconfig/dns_writer_test.rb +++ b/test/y2network/sysconfig/dns_writer_test.rb @@ -25,7 +25,7 @@ describe "#write" do - let(:dhcp_hostname) { true } + let(:dhcp_hostname) { :any } let(:dns) do Y2Network::DNS.new( nameservers: [IPAddr.new("10.0.0.1"), IPAddr.new("10.0.0.2")], @@ -36,7 +36,7 @@ ) end - let(:old_dhcp_hostname) { !dhcp_hostname } + let(:old_dhcp_hostname) { :none } let(:old_dns) do Y2Network::DNS.new( nameservers: [IPAddr.new("10.0.0.1")], @@ -47,14 +47,29 @@ ) end let(:hostname) { "myhost.example.net" } + let(:ifcfg_eth0) do + instance_double( + Y2Network::Sysconfig::InterfaceFile, interface: "eth0", + dhclient_set_hostname: "yes", :dhclient_set_hostname= => nil, load: nil, save: nil + ) + end + let(:ifcfg_eth1) do + instance_double( + Y2Network::Sysconfig::InterfaceFile, interface: "eth1", + dhclient_set_hostname: "no", :dhclient_set_hostname= => nil, load: nil, save: true + ) + end before do allow(Yast::SCR).to receive(:Write) allow(Yast::Execute).to receive(:on_target!) + allow(Y2Network::Sysconfig::InterfaceFile).to receive(:all) + .and_return([ifcfg_eth0, ifcfg_eth1]) end - context "when dhcp_hostname is changed to true" do - let(:dhcp_hostname) { true } + context "when dhcp_hostname is changed to :any" do + let(:old_dhcp_hostname) { :none } + let(:dhcp_hostname) { :any } it "sets DHCLIENT_SET_HOSTNAME to 'yes'" do expect(Yast::SCR).to receive(:Write) @@ -63,8 +78,9 @@ end end - context "when dhcp_hostname is changed to false" do - let(:dhcp_hostname) { false } + context "when dhcp_hostname is changed to :none" do + let(:old_dhcp_hostname) { :any } + let(:dhcp_hostname) { :none } it "writes DHCLIENT_SET_HOSTNAME to 'no'" do expect(Yast::SCR).to receive(:Write) @@ -83,32 +99,23 @@ end end - context "when dhcp_hostname is nil" do - let(:dhcp_hostname) { nil } + context "when dhcp_hostname is set to an interface name" do + let(:dhcp_hostname) { "eth1" } - it "does not write DHCLIENT_SET_HOSTNAME" do - expect(Yast::SCR).to_not receive(:Write) - .with(Yast::Path.new(".sysconfig.network.dhcp.DHCLIENT_SET_HOSTNAME"), anything) + it "sets the DHCLIENT_SET_HOSTNAME in the corresponding ifcfg-* file" do + expect(ifcfg_eth0).to receive(:dhclient_set_hostname=).with(nil) + expect(ifcfg_eth1).to receive(:dhclient_set_hostname=).with("yes") writer.write(dns, old_dns) end end it "updates the hostname" do - expect(Yast::Execute).to receive(:on_target!).with("/bin/hostname", "myhost") + expect(Yast::Execute).to receive(:on_target!).with("/usr/bin/hostname", "myhost") expect(Yast::SCR).to receive(:Write) .with(Yast::Path.new(".target.string"), "/etc/hostname", "myhost.example.net\n") writer.write(dns, old_dns) end - context "when no hostname is given" do - let(:hostname) { nil } - - it "proposes a hostname" do - expect(Yast::Execute).to receive(:on_target!).with("/bin/hostname", /linux-/) - writer.write(dns, old_dns) - end - end - context "when sendmail update script is installed" do before do allow(Yast::FileUtils).to receive(:Exists).with(/sendmail/).and_return(true) diff --git a/test/y2network/sysconfig/interface_file_test.rb b/test/y2network/sysconfig/interface_file_test.rb index a7c7b0e67..713a39640 100644 --- a/test/y2network/sysconfig/interface_file_test.rb +++ b/test/y2network/sysconfig/interface_file_test.rb @@ -19,21 +19,34 @@ require_relative "../../test_helper" require "y2network/sysconfig/interface_file" +require "tmpdir" describe Y2Network::Sysconfig::InterfaceFile do - subject(:file) { described_class.new("eth0") } + subject(:file) { described_class.new(interface_name).tap(&:load) } - let(:scr_root) { File.join(DATA_PATH, "scr_read") } + let(:interface_name) { "eth0" } + + def file_content(scr_root, file) + path = File.join(scr_root, file.path.to_s) + File.read(path) + end + + let(:scr_root) { Dir.mktmpdir } around do |example| - change_scr_root(scr_root, &example) + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end end describe ".find" do it "returns an object representing the file" do file = described_class.find("eth0") expect(file).to be_a(described_class) - expect(file.name).to eq("eth0") + expect(file.interface).to eq("eth0") end context "when a file for the given interface does not exist" do @@ -43,62 +56,160 @@ end end - describe "#fetch" do - it "returns the raw value from the sysconfig file" do - expect(file.fetch("IPADDR")).to eq("192.168.123.1/24") + describe ".all" do + it "returns all the present interfaces files" do + files = described_class.all + expect(files).to be_all(Y2Network::Sysconfig::InterfaceFile) + interfaces = files.map(&:interface) + expect(interfaces).to include("wlan0") end - end - describe "#ip_address" do - let(:ipaddr) { "192.168.122.122/24" } + describe "#remove" do + subject(:file) { described_class.find("eth0") } - before do - allow(file).to receive(:fetch).with("IPADDR").and_return(ipaddr) + it "removes the file" do + expect(file).to_not be_nil + file.remove + expect(File).to_not exist(File.join(scr_root, file.path)) end + end - it "returns the IP address" do - expect(file.ip_address).to eq(IPAddr.new(ipaddr)) + describe "#ipaddrs" do + it "returns the IP addresses" do + expect(file.ipaddrs).to eq( + "" => Y2Network::IPAddress.from_string("192.168.123.1/24") + ) end - context "when the IP address is empty" do - let(:ipaddr) { "" } + context "when multiple addresses are defined" do + let(:interface_name) { "eth1" } - it "returns nil" do - expect(file.ip_address).to be_nil + it "returns a hash with IP addresses indexed by their suffixes" do + expect(file.ipaddrs).to eq( + "_0" => Y2Network::IPAddress.from_string("192.168.123.1/24"), + "_1" => Y2Network::IPAddress.from_string("10.0.0.1") + ) end end - context "when the IP address is undefined" do - let(:ipaddr) { nil } + context "when the IP address is missing" do + let(:interface_name) { "eth4" } - it "returns nil" do - expect(file.ip_address).to be_nil + it "returns an empty hash" do + expect(file.ipaddrs).to be_empty end end end + describe "#ipaddrs=" do + let(:interface_name) { "eth4" } + + it "sets the bootproto" do + addresses = { default: Y2Network::IPAddress.from_string("10.0.0.1") } + expect { file.ipaddrs = addresses } + .to change { file.ipaddrs }.from({}).to(addresses) + end + end + describe "#bootproto" do let(:bootproto) { "static" } - it "returns the bootproto as a symbol" do - expect(file.bootproto).to eq(:static) + it "returns the bootproto as a string" do + expect(file.bootproto).to eq("static") + end + end + + describe "#bootproto=" do + it "sets the bootproto" do + expect { file.bootproto = "dhcp" }.to change { file.bootproto }.from("static").to("dhcp") end end describe "#startmode" do let(:startmode) { "auto" } - it "returns the startmode as a symbol" do - expect(file.startmode).to eq(:auto) + it "returns the startmode as a string" do + expect(file.startmode).to eq("auto") + end + end + + describe "#startmode=" do + it "sets the startmode" do + expect { file.startmode = "manual" }.to change { file.startmode }.from("auto").to("manual") end end describe "#wireless_keys" do - subject(:file) { described_class.new("wlan0") } + let(:interface_name) { "wlan2" } it "returns the list of keys" do - expect(file.wireless_keys).to eq(["0-1-2-3-4-5", "s:password"]) + expect(file.wireless_keys).to eq("_0" => "0-1-2-3-4-5", "_1" => "s:password") + end + end + + describe "#wireless_keys=" do + let(:keys) { { default: "123456", "_1" => "abcdef" } } + + it "sets the wireless keys" do + expect { file.wireless_keys = keys }.to change { file.wireless_keys }.to(keys) + end + end + + describe "#save" do + subject(:file) { described_class.new("eth0") } + + it "writes the changes to the file" do + file.ipaddrs = { default: Y2Network::IPAddress.from_string("192.168.122.1/24") } + file.bootproto = :static + file.save + + content = file_content(scr_root, file) + expect(content).to include("BOOTPROTO='static'") + expect(content).to include("IPADDR='192.168.122.1/24'") + end + + describe "when multiple wireless keys are specified" do + it "writes indexes keys" do + file.wireless_keys = { "" => "123456", "_1" => "abcdef" } + file.save + + content = file_content(scr_root, file) + expect(content).to include("WIRELESS_KEY='123456") + expect(content).to include("WIRELESS_KEY_1='abcdef") + end + end + + describe "when a nil value is specified" do + it "removes the key from the value" do + file.bootproto = nil + file.save + + content = file_content(scr_root, file) + expect(content).to_not include("BOOTPROTO") + end + end + end + + describe "#clean" do + let(:interface_name) { "eth1" } + + it "removes all known values from the file" do + file.clean + file.save + + content = file_content(scr_root, file) + expect(content).to_not include("IPADDR_0") + end + end + + describe "#type" do + it "determines the interface type from the attributes" do + file.interfacetype = "dummy" + expect(file.type.short_name).to eql("dummy") + file.interfacetype = nil + file.tunnel = "tap" + expect(file.type.short_name).to eql("tap") end end end diff --git a/test/y2network/sysconfig/interfaces_reader_test.rb b/test/y2network/sysconfig/interfaces_reader_test.rb index 9274c9646..27da24b8a 100644 --- a/test/y2network/sysconfig/interfaces_reader_test.rb +++ b/test/y2network/sysconfig/interfaces_reader_test.rb @@ -19,56 +19,60 @@ require_relative "../../test_helper" require "y2network/sysconfig/interfaces_reader" +require "y2network/udev_rule_part" describe Y2Network::Sysconfig::InterfacesReader do subject(:reader) { described_class.new } - let(:netcards) do - [eth0] - end + + let(:netcards) { [eth0] } let(:eth0) do { - "active" => true, "dev_name" => "eth0", "mac" => "00:12:34:56:78:90", "name" => "Ethernet Connection", - "type" => "eth" + "active" => true, "dev_name" => "eth0", "mac" => "00:12:34:56:78:90", + "name" => "Ethernet Connection", "type" => "eth", + "drivers" => [{ "modules" => [["virtio_net", ""]] }] } end + let(:udev_rule) do + Y2Network::UdevRule.new( + [ + Y2Network::UdevRulePart.new("ATTR{address}", "==", "00:12:34:56:78"), + Y2Network::UdevRulePart.new("NAME", "=", "eth0") + ] + ) + end + let(:configured_interfaces) { ["lo", "eth0"] } + let(:hardware_wrapper) { Y2Network::HardwareWrapper.new } + let(:driver) { Y2Network::Driver.new("virtio_net") } + TYPES = { "eth0" => "eth" }.freeze - IFCFGS = { - "eth0" => { - "BOOTPROTO" => "static", - "IPADDR" => "192.168.1.2" - }, - "eth1" => { - "BOOTPROTO" => "static", - "IPADDR" => "10.0.0.2" - } - }.freeze - SCR_PATH_REGEXP = /.network.value.\"(\w+)\".(\w+)\Z/ before do - allow(Yast::LanItems).to receive(:Hardware).and_return(netcards) + allow(hardware_wrapper).to receive(:ReadHardware).and_return(netcards) + allow(Y2Network::HardwareWrapper).to receive(:new).and_return(hardware_wrapper) allow(Yast::SCR).to receive(:Dir).with(Yast::Path.new(".network.section")) .and_return(configured_interfaces) + allow(Yast::SCR).to receive(:Dir).and_call_original allow(Yast::NetworkInterfaces).to receive(:GetTypeFromSysfs) { |n| TYPES[n] } - allow(Yast::SCR).to receive(:Read) do |path, &block| - m = SCR_PATH_REGEXP.match(path.to_s) - if m - iface, key = m[1..2] - IFCFGS[iface][key] - else - block.call - end - end + allow(Y2Network::UdevRule).to receive(:find_for).and_return(udev_rule) + allow(Y2Network::Driver).to receive(:from_system).and_return(driver) end + around { |e| change_scr_root(File.join(DATA_PATH, "scr_read"), &e) } + describe "#interfaces" do it "reads physical interfaces" do interfaces = reader.interfaces expect(interfaces.by_name("eth0")).to_not be_nil end + it "sets the renaming mechanism" do + eth0 = reader.interfaces.by_name("eth0") + expect(eth0.renaming_mechanism).to eq(:mac) + end + it "reads wifi interfaces" it "reads bridge interfaces" it "reads bonding interfaces" @@ -77,9 +81,20 @@ context "when a connection for a not existing device is found" do let(:configured_interfaces) { ["lo", "eth0", "eth1"] } - it "creates a fake interface" do - eth1 = reader.interfaces.by_name("eth1") - expect(eth1).to_not be_nil + context "and it is a virtual connection" do + it "creates a virtual interface" do + vlan = reader.interfaces.by_name("eth0.100") + expect(vlan).to_not be_nil + expect(vlan).to be_a Y2Network::VirtualInterface + end + end + + context "and it is not a virtual connection" do + it "creates a not present physical interface" do + eth1 = reader.interfaces.by_name("eth1") + expect(eth1).to be_a Y2Network::PhysicalInterface + expect(eth1).to_not be_present + end end end end @@ -87,8 +102,14 @@ describe "#connections" do it "reads ethernet connections" do connections = reader.connections - conn = connections.find { |i| i.interface == "eth0" } + conn = connections.by_name("eth0") expect(conn.interface).to eq("eth0") end end + + describe "#drivers" do + it "returns a list of drivers" do + expect(reader.drivers).to eq([driver]) + end + end end diff --git a/test/y2network/sysconfig/interfaces_writer_test.rb b/test/y2network/sysconfig/interfaces_writer_test.rb new file mode 100644 index 000000000..4f71f8db7 --- /dev/null +++ b/test/y2network/sysconfig/interfaces_writer_test.rb @@ -0,0 +1,162 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/sysconfig/interfaces_writer" +require "y2network/udev_rule" +require "y2network/physical_interface" +require "y2network/interfaces_collection" +require "tmpdir" + +describe Y2Network::Sysconfig::InterfacesWriter do + subject(:writer) { described_class.new } + + describe "#write" do + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0]) } + let(:eth0) do + Y2Network::PhysicalInterface.new("eth0", hardware: hardware).tap do |i| + i.renaming_mechanism = renaming_mechanism + i.custom_driver = driver + end + end + + let(:hardware) do + instance_double( + Y2Network::Hwinfo, name: "Ethernet Card 0", busid: "00:1c.0", mac: "01:23:45:67:89:ab", + dev_port: "1", modalias: "virtio:d00000001v00001AF4" + ) + end + let(:renaming_mechanism) { :none } + let(:driver) { nil } + let(:scr_root) { Dir.mktmpdir } + + before do + allow(Yast::Execute).to receive(:on_target) + allow(writer).to receive(:sleep) + + # prevent collision with real hardware + allow(Y2Network::UdevRule).to receive(:naming_rules).and_return([]) + allow(Y2Network::UdevRule).to receive(:drivers_rules).and_return([]) + end + + around do |example| + begin + FileUtils.cp_r(File.join(DATA_PATH, "scr_read", "etc"), scr_root) + change_scr_root(scr_root, &example) + ensure + FileUtils.remove_entry(scr_root) + end + end + + context "when the interface is renamed" do + before do + eth0.rename("eth1", renaming_mechanism) + end + + it "sets the interface down" do + expect(Yast::Execute).to receive(:on_target).with("/sbin/ifdown", "eth0") + subject.write(interfaces) + end + + context "during autoinstallation" do + before do + allow(Yast::Mode).to receive(:autoinst).and_return(true) + end + + it "does not set the interface down" do + expect(Yast::Execute).to_not receive(:on_target).with("/sbin/ifdown", any_args) + subject.write(interfaces) + end + end + + context "when the interface is renamed using the MAC" do + let(:renaming_mechanism) { :mac } + + it "writes a MAC based udev renaming rule" do + expect(Y2Network::UdevRule).to receive(:write_net_rules) do |rules| + expect(rules.first.to_s).to eq( + "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", " \ + "ATTR{type}==\"1\", ATTR{dev_id}==\"0x0\", " \ + "ATTR{address}==\"01:23:45:67:89:ab\", NAME=\"eth1\"" + ) + end + subject.write(interfaces) + end + end + + context "when the interface is renamed using the BUS ID" do + let(:renaming_mechanism) { :bus_id } + + it "writes a BUS ID based udev renaming rule" do + expect(Y2Network::UdevRule).to receive(:write_net_rules) do |rules| + expect(rules.first.to_s).to eq( + "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", " \ + "ATTR{type}==\"1\", KERNELS==\"00:1c.0\", ATTR{dev_port}==\"1\", NAME=\"eth1\"" + ) + end + subject.write(interfaces) + end + end + + context "when there is some rule for an unknown interface" do + let(:unknown_rule) { Y2Network::UdevRule.new_mac_based_rename("unknown", "00:11:22:33:44:55:66") } + + before do + allow(Y2Network::UdevRule).to receive(:naming_rules).and_return([unknown_rule]) + end + + it "keeps the rule" do + expect(Y2Network::UdevRule).to receive(:write_net_rules) do |rules| + expect(rules.first.to_s).to eq(unknown_rule.to_s) + end + subject.write(interfaces) + end + end + end + + context "when the interface is not renamed" do + let(:renaming_mechanism) { nil } + + it "does not write a udev rule" do + expect(Y2Network::UdevRule).to receive(:write_net_rules) do |rules| + expect(rules).to be_empty + end + subject.write(interfaces) + end + end + + context "when a driver is set for an interface" do + let(:driver) { "virtio_net" } + + it "writes an udev driver rule" do + expect(Y2Network::UdevRule).to receive(:write_drivers_rules) do |rules| + expect(rules.first.to_s).to eq("ENV{MODALIAS}==\"#{hardware.modalias}\", ENV{MODALIAS}=\"#{driver}\"") + end + subject.write(interfaces) + end + end + + it "refreshes udev" do + expect(Yast::Execute).to receive(:on_target).with("/usr/bin/udevadm", "control", any_args) + expect(Yast::Execute).to receive(:on_target).with("/usr/bin/udevadm", "trigger", any_args) + expect(Yast::Execute).to receive(:on_target).with("/usr/bin/udevadm", "settle") + subject.write(interfaces) + end + end +end diff --git a/test/y2network/udev_rule_part_test.rb b/test/y2network/udev_rule_part_test.rb new file mode 100644 index 000000000..5a581aa99 --- /dev/null +++ b/test/y2network/udev_rule_part_test.rb @@ -0,0 +1,78 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/udev_rule_part" + +describe Y2Network::UdevRulePart do + subject(:part) { described_class.new(key, operator, value) } + + let(:key) { "ACTION" } + let(:operator) { "==" } + let(:value) { "add" } + + describe ".from_string" do + it "returns an udev rule part extracting the elements from the string" do + part = described_class.from_string("ACTION==\"add\"") + expect(part.key).to eq("ACTION") + expect(part.operator).to eq("==") + expect(part.value).to eq("add") + end + end + + describe "#to_s" do + it "returns an string representation compatible with rules files format" do + expect(part.to_s).to eq('ACTION=="add"') + end + end + + describe "#==" do + let(:other) { described_class.new("ACTION", "==", "add") } + + context "when key, operator and value are the same" do + it "returns true" do + expect(part).to eq(other) + end + end + + context "when the key differs" do + let(:key) { "ENV{var1}" } + + it "returns false" do + expect(part).to_not eq(other) + end + end + + context "when the operator differs" do + let(:operator) { "!=" } + + it "returns false" do + expect(part).to_not eq(other) + end + end + + context "when the value differs" do + let(:value) { "remove" } + + it "returns false" do + expect(part).to_not eq(other) + end + end + end +end diff --git a/test/y2network/udev_rule_test.rb b/test/y2network/udev_rule_test.rb new file mode 100644 index 000000000..ec9f0d248 --- /dev/null +++ b/test/y2network/udev_rule_test.rb @@ -0,0 +1,233 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/udev_rule" + +describe Y2Network::UdevRule do + subject(:udev_rule) { described_class.new(parts) } + + before { described_class.reset_cache } + + let(:parts) { [] } + + let(:udev_persistent_net) do + { + "eth0" => ["SUBSYSTEM==\"net\"", "ACTION==\"add\"", "ATTR{address}==\"?*31:78:f2\"", "NAME=\"eth0\""] + } + end + + let(:udev_persistent_drivers) do + { + "virtio:d00000001v00001AF4" => ["ENV{MODALIAS}==\"virtio:d00000001v00001AF4\"", "ENV{MODALIAS}=\"e1000\""] + } + end + + before do + allow(Yast::SCR).to receive(:Read).with(Yast::Path.new(".udev_persistent.net")) + .and_return(udev_persistent_net) + allow(Yast::SCR).to receive(:Read).with(Yast::Path.new(".udev_persistent.drivers")) + .and_return(udev_persistent_drivers) + end + + describe ".naming_rules" do + it "returns naming rules" do + rules = described_class.naming_rules + expect(rules.first.to_s).to match(/NAME=/) + end + end + + describe ".drivers_rules" do + it "returns drivers rules" do + rules = described_class.drivers_rules + expect(rules.first.to_s).to match(/MODALIAS/) + end + end + + describe ".find_for" do + it "returns the udev rule for the given device" do + rule = described_class.find_for("eth0") + expect(rule.to_s).to eq( + "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"?*31:78:f2\", NAME=\"eth0\"" + ) + end + + context "when there is no udev rule for the given device" do + it "returns nil" do + expect(described_class.find_for("eth1")).to be_nil + end + end + end + + describe ".new_mac_based_rename" do + it "returns a MAC based renaming rule" do + rule = described_class.new_mac_based_rename("eth0", "01:23:45:67:89:ab") + expect(rule.to_s).to eq( + "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{type}==\"1\", " \ + "ATTR{dev_id}==\"0x0\", ATTR{address}==\"01:23:45:67:89:ab\", " \ + "NAME=\"eth0\"" + ) + end + end + + describe ".new_bus_id_based_rename" do + it "returns a BUS ID based renaming rule" do + rule = described_class.new_bus_id_based_rename("eth0", "0000:08:00.0", "1") + expect(rule.to_s).to eq( + "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{type}==\"1\", " \ + "KERNELS==\"0000:08:00.0\", ATTR{dev_port}==\"1\", NAME=\"eth0\"" + ) + end + + context "when the dev_port is not defined" do + it "does not include the dev_port part" do + rule = described_class.new_bus_id_based_rename("eth0", "0000:08:00.0") + expect(rule.to_s).to eq( + "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{type}==\"1\", " \ + "KERNELS==\"0000:08:00.0\", NAME=\"eth0\"" + ) + end + end + end + + describe ".new_driver_assignment" do + it "returns a module assignment rule" do + rule = described_class.new_driver_assignment("virtio:0000", "virtio_net") + expect(rule.to_s).to eq("ENV{MODALIAS}==\"virtio:0000\", ENV{MODALIAS}=\"virtio_net\"") + end + end + + describe ".write_net_rules" do + it "writes changes using the udev_persistent agent" do + expect(Yast::SCR).to receive(:Write).with( + Yast::Path.new(".udev_persistent.rules"), [udev_rule.to_s] + ) + expect(Yast::SCR).to receive(:Write).with(Yast::Path.new(".udev_persistent.nil"), []) + described_class.write_net_rules([udev_rule]) + end + end + + describe ".write_drivers_rules" do + let(:udev_rule) { described_class.new_driver_assignment("virtio:0000", "virtio_net") } + + it "writes changes using the udev_persistent agent" do + expect(Yast::SCR).to receive(:Write).with( + Yast::Path.new(".udev_persistent.drivers"), "virtio_net" => udev_rule.parts.map(&:to_s) + ) + expect(Yast::SCR).to receive(:Write).with(Yast::Path.new(".udev_persistent.nil"), []) + described_class.write_drivers_rules([udev_rule]) + end + end + + describe "#add_part" do + it "adds a new key/value to the rule" do + udev_rule.add_part("ACTION", "==", "add") + expect(udev_rule.parts).to eq( + [Y2Network::UdevRulePart.new("ACTION", "==", "add")] + ) + end + end + + describe "#to_s" do + let(:parts) do + [ + Y2Network::UdevRulePart.new("ACTION", "==", "add"), + Y2Network::UdevRulePart.new("NAME", "=", "dummy") + ] + end + + it "returns the string representation for the rules file" do + expect(udev_rule.to_s).to eq( + 'ACTION=="add", NAME="dummy"' + ) + end + end + + describe "#mac" do + subject(:udev_rule) { described_class.new_mac_based_rename("eth0", "01:23:45:67:89:ab") } + + it "returns the MAC from the udev rule" do + expect(udev_rule.mac).to eq("01:23:45:67:89:ab") + end + + context "if no MAC address is present" do + subject(:udev_rule) { described_class.new } + + it "returns nil" do + expect(udev_rule.mac).to be_nil + end + end + end + + describe "#bus_id" do + subject(:udev_rule) { described_class.new_bus_id_based_rename("eth0", "0000:08:00.0") } + + it "returns the BUS ID from the udev rule" do + expect(udev_rule.bus_id).to eq("0000:08:00.0") + end + + context "if no BUS ID is present" do + subject(:udev_rule) { described_class.new } + + it "returns nil" do + expect(udev_rule.bus_id).to be_nil + end + end + end + + describe "#dev_port" do + subject(:udev_rule) { described_class.new_bus_id_based_rename("eth0", "0000:08:00.0", "1") } + + it "returns the device port from the udev rule" do + expect(udev_rule.dev_port).to eq("1") + end + + context "if no device port is present" do + subject(:udev_rule) { described_class.new } + + it "returns nil" do + expect(udev_rule.dev_port).to be_nil + end + end + end + + describe "#device" do + subject(:udev_rule) { described_class.new_mac_based_rename("eth0", "01:23:45:67:89:ab") } + + it "returns device" do + expect(udev_rule.device).to eq("eth0") + end + end + + describe "#original_modalias" do + subject(:udev_rule) { described_class.new_driver_assignment("virtio:0000", "virtio_net") } + + it "returns the original modalias" do + expect(udev_rule.original_modalias).to eq("virtio:0000") + end + end + + describe "#driver" do + subject(:udev_rule) { described_class.new_driver_assignment("virtio:0000", "virtio_net") } + + it "return the assigned driver" do + expect(udev_rule.driver).to eq("virtio_net") + end + end +end diff --git a/test/y2network/virtual_interface_test.rb b/test/y2network/virtual_interface_test.rb new file mode 100644 index 000000000..5f3e0015b --- /dev/null +++ b/test/y2network/virtual_interface_test.rb @@ -0,0 +1,28 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../test_helper" +require "y2network/virtual_interface" +require "y2network/connection_config/bridge" + +describe Y2Network::VirtualInterface do + subject(:interface) { described_class.new("br0", type: type) } + + let(:type) { Y2Network::InterfaceType::BRIDGE } +end diff --git a/test/y2network/widgets/additional_addresses_test.rb b/test/y2network/widgets/additional_addresses_test.rb index ab3808e56..c34232880 100644 --- a/test/y2network/widgets/additional_addresses_test.rb +++ b/test/y2network/widgets/additional_addresses_test.rb @@ -24,7 +24,7 @@ require "y2network/interface_config_builder" describe Y2Network::Widgets::AdditionalAddresses do - subject { described_class.new(Y2Network::InterfaceConfigBuilder.new) } + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("eth")) } include_examples "CWM::CustomWidget" diff --git a/test/y2network/widgets/address_tab_test.rb b/test/y2network/widgets/address_tab_test.rb index dc168a108..8749161e0 100644 --- a/test/y2network/widgets/address_tab_test.rb +++ b/test/y2network/widgets/address_tab_test.rb @@ -24,7 +24,7 @@ require "y2network/interface_config_builder" describe Y2Network::Widgets::AddressTab do - subject { described_class.new(Y2Network::InterfaceConfigBuilder.new) } + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("eth")) } include_examples "CWM::Tab" end diff --git a/test/y2network/widgets/blink_button_test.rb b/test/y2network/widgets/blink_button_test.rb index e0f2010d6..4d88be9b5 100644 --- a/test/y2network/widgets/blink_button_test.rb +++ b/test/y2network/widgets/blink_button_test.rb @@ -21,10 +21,14 @@ require "cwm/rspec" require "y2network/widgets/blink_button" +require "y2network/interface_config_builder" describe Y2Network::Widgets::BlinkButton do - subject { described_class.new("IFCFG" => "eth0") } + let(:builder) { Y2Network::InterfaceConfigBuilder.for("eth") } + subject { described_class.new(builder) } + before do + builder.name = "eth0" # no real blinking allow(Yast::SCR).to receive(:Execute) end diff --git a/test/y2network/widgets/bond_options_test.rb b/test/y2network/widgets/bond_options_test.rb index d3370f76a..bb1269841 100644 --- a/test/y2network/widgets/bond_options_test.rb +++ b/test/y2network/widgets/bond_options_test.rb @@ -21,9 +21,11 @@ require "cwm/rspec" require "y2network/widgets/bond_options" +require "y2network/interface_config_builder" describe Y2Network::Widgets::BondOptions do - subject { described_class.new({}) } + let(:builder) { Y2Network::InterfaceConfigBuilder.for("bond") } + subject { described_class.new(builder) } include_examples "CWM::ComboBox" end diff --git a/test/y2network/widgets/bond_slave_test.rb b/test/y2network/widgets/bond_slave_test.rb index 74853da10..9392522cc 100644 --- a/test/y2network/widgets/bond_slave_test.rb +++ b/test/y2network/widgets/bond_slave_test.rb @@ -21,9 +21,15 @@ require "cwm/rspec" require "y2network/widgets/bond_slave" +require "y2network/interface_config_builders/bonding" describe Y2Network::Widgets::BondSlave do - subject { described_class.new({}) } + let(:builder) { Y2Network::InterfaceConfigBuilders::Bonding.new } + subject { described_class.new(builder) } + + before do + allow(builder).to receive(:yast_config).and_return(Y2Network::Config.new(source: :testing)) + end include_examples "CWM::CustomWidget" @@ -60,9 +66,9 @@ allow(subject).to receive(:physical_port_id).with(i).and_return("00010486fd348") end - expect(Yast::Popup).to receive(:YesNoHeadline).and_return(:request_answer) + expect(Yast::Popup).to receive(:YesNoHeadline).and_return(false) - expect(subject.validate).to eql(:request_answer) + expect(subject.validate).to eql(false) end end end diff --git a/test/y2network/widgets/bond_slaves_tab_test.rb b/test/y2network/widgets/bond_slaves_tab_test.rb index a27e7a633..891372443 100644 --- a/test/y2network/widgets/bond_slaves_tab_test.rb +++ b/test/y2network/widgets/bond_slaves_tab_test.rb @@ -24,7 +24,7 @@ require "y2network/interface_config_builder" describe Y2Network::Widgets::BondSlavesTab do - subject { described_class.new(Y2Network::InterfaceConfigBuilder.new) } + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("bond")) } include_examples "CWM::Tab" end diff --git a/test/y2network/widgets/boot_protocol_test.rb b/test/y2network/widgets/boot_protocol_test.rb index be83b5b41..1708522a3 100644 --- a/test/y2network/widgets/boot_protocol_test.rb +++ b/test/y2network/widgets/boot_protocol_test.rb @@ -22,6 +22,7 @@ require "y2network/widgets/boot_protocol" require "y2network/interface_config_builder" +require "y2network/connection_config/ethernet" describe Y2Network::Widgets::BootProtocol do let(:builder) { Y2Network::InterfaceConfigBuilder.for("eth") } @@ -51,10 +52,10 @@ def expect_set_widget(id, value, value_type: :Value) context "static configuration" do before do - builder["BOOTPROTO"] = "static" - builder["IPADDR"] = "10.5.0.6" - builder["PREFIXLEN"] = "24" - builder["HOSTNAME"] = "pepa" + builder.boot_protocol = "static" + builder.ip_address = "10.5.0.6" + builder.subnet_prefix = "24" + builder.hostname = "pepa" allow(subject).to receive(:value).and_return(:bootproto_static) allow(Yast::UI).to receive(:QueryWidget).and_return("pepa") end @@ -86,7 +87,7 @@ def expect_set_widget(id, value, value_type: :Value) context "dhcp configuration" do before do - builder["BOOTPROTO"] = "dhcp" + builder.boot_protocol = "dhcp" allow(subject).to receive(:value).and_return(:bootproto_dynamic) end @@ -97,7 +98,7 @@ def expect_set_widget(id, value, value_type: :Value) context "dhcp4 configuration" do before do - builder["BOOTPROTO"] = "dhcp4" + builder.boot_protocol = "dhcp4" allow(subject).to receive(:value).and_return(:bootproto_dynamic) end @@ -108,7 +109,7 @@ def expect_set_widget(id, value, value_type: :Value) context "dhcp6 configuration" do before do - builder["BOOTPROTO"] = "dhcp6" + builder.boot_protocol = "dhcp6" allow(subject).to receive(:value).and_return(:bootproto_dynamic) end @@ -119,7 +120,7 @@ def expect_set_widget(id, value, value_type: :Value) context "dhcp+autoip configuration" do before do - builder["BOOTPROTO"] = "dhcp+autoip" + builder.boot_protocol = "dhcp+autoip" allow(subject).to receive(:value).and_return(:bootproto_dynamic) end @@ -130,7 +131,7 @@ def expect_set_widget(id, value, value_type: :Value) context "autoip configuration" do before do - builder["BOOTPROTO"] = "autoip" + builder.boot_protocol = "autoip" allow(subject).to receive(:value).and_return(:bootproto_dynamic) end @@ -141,7 +142,7 @@ def expect_set_widget(id, value, value_type: :Value) context "none configuration" do before do - builder["BOOTPROTO"] = "none" + builder.boot_protocol = "none" end it "does not crash" do @@ -151,7 +152,7 @@ def expect_set_widget(id, value, value_type: :Value) context "ibft configuration" do before do - builder["BOOTPROTO"] = "ibft" + builder.boot_protocol = "ibft" end it "does not crash" do @@ -173,7 +174,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "ibft" + expect(builder.boot_protocol.name).to eq "ibft" end it "sets bootproto to none if ibft is not selected" do @@ -181,7 +182,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "none" + expect(builder.boot_protocol.name).to eq "none" end end @@ -195,7 +196,7 @@ def expect_set_widget(id, value, value_type: :Value) it "sets bootproto to static" do subject.store - expect(builder["BOOTPROTO"]).to eq "static" + expect(builder.boot_protocol.name).to eq "static" end it "sets ipaddr to value of ip address widget" do @@ -203,7 +204,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["IPADDR"]).to eq "10.100.0.1" + expect(builder.ip_address).to eq "10.100.0.1" end it "sets hostname to value of hostname widget" do @@ -211,7 +212,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["HOSTNAME"]).to eq "test.suse.cz" + expect(builder.hostname).to eq "test.suse.cz" end it "sets prefixlen when value of netmast start with '/'" do @@ -219,7 +220,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["PREFIXLEN"]).to eq "24" + expect(builder.subnet_prefix).to eq "/24" end it "sets prefixlen for ipv6" do @@ -227,15 +228,16 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["PREFIXLEN"]).to eq "124" + expect(builder.subnet_prefix).to eq "/124" end it "sets netmask for ipv4 netmask value" do + pending "drop netmask" allow(Yast::UI).to receive(:QueryWidget).with(:bootproto_netmask, :Value).and_return("255.255.0.0") subject.store - expect(builder["NETMASK"]).to eq "255.255.0.0" + expect(builder.subnet_prefix).to eq "255.255.0.0" end end @@ -255,7 +257,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "dhcp" + expect(builder.boot_protocol.name).to eq "dhcp" end it "sets bootproto to dhcp4 when dhcp for ipv4 only is selected" do @@ -266,7 +268,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "dhcp4" + expect(builder.boot_protocol.name).to eq "dhcp4" end it "sets bootproto to dhcp6 when dhcp for ipv6 only is selected" do @@ -277,7 +279,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "dhcp6" + expect(builder.boot_protocol.name).to eq "dhcp6" end it "sets bootproto to dhcp+autoip when dhcp and zeroconf is selected" do @@ -286,7 +288,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "dhcp+autoip" + expect(builder.boot_protocol.name).to eq "dhcp+autoip" end it "sets bootproto to autoip when zeroconf is selected" do @@ -295,7 +297,7 @@ def expect_set_widget(id, value, value_type: :Value) subject.store - expect(builder["BOOTPROTO"]).to eq "autoip" + expect(builder.boot_protocol.name).to eq "autoip" end end end diff --git a/test/y2network/widgets/bridge_ports_test.rb b/test/y2network/widgets/bridge_ports_test.rb index 4dd32a648..6006064e9 100644 --- a/test/y2network/widgets/bridge_ports_test.rb +++ b/test/y2network/widgets/bridge_ports_test.rb @@ -21,10 +21,10 @@ require "cwm/rspec" require "y2network/widgets/bridge_ports" -require "y2network/interface_config_builders/br" +require "y2network/interface_config_builders/bridge" describe Y2Network::Widgets::BridgePorts do - let(:builder) { Y2Network::InterfaceConfigBuilders::Br.new } + let(:builder) { Y2Network::InterfaceConfigBuilders::Bridge.new } subject { described_class.new(builder) } before do diff --git a/test/y2network/widgets/bridge_slaves_tab_test.rb b/test/y2network/widgets/bridge_slaves_tab_test.rb index ea5a3c6a9..27cc819f5 100644 --- a/test/y2network/widgets/bridge_slaves_tab_test.rb +++ b/test/y2network/widgets/bridge_slaves_tab_test.rb @@ -24,7 +24,7 @@ require "y2network/interface_config_builder" describe Y2Network::Widgets::BridgeSlavesTab do - subject { described_class.new(Y2Network::InterfaceConfigBuilder.new) } + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("br")) } include_examples "CWM::Tab" end diff --git a/test/y2network/widgets/driver_test.rb b/test/y2network/widgets/driver_test.rb new file mode 100644 index 000000000..f2edc0a87 --- /dev/null +++ b/test/y2network/widgets/driver_test.rb @@ -0,0 +1,57 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/driver" + +describe Y2Network::Widgets::Driver do + subject(:widget) { described_class.new(builder) } + + let(:builder) do + Y2Network::InterfaceConfigBuilder.for("eth") + end + let(:virtio_net) { Y2Network::Driver.new("virtio_net", "csum=1") } + let(:eth0) { Y2Network::PhysicalInterface.new("eth0") } + + before do + allow(builder).to receive(:drivers).and_return([virtio_net]) + allow(builder).to receive(:driver).and_return(virtio_net) + allow(builder).to receive(:interface).and_return(eth0) + end + + include_examples "CWM::CustomWidget" + + describe "#contents" do + it "contains a kernel module widget" do + expect(Y2Network::Widgets::KernelModule).to receive(:new) + .with(["virtio_net"], "virtio_net") + widget.contents + end + + it "contains a kernel options widget" do + expect(Y2Network::Widgets::KernelOptions).to receive(:new) + .with("csum=1") + widget.contents + end + end + + describe "#handle" +end diff --git a/test/y2network/widgets/edit_interface_test.rb b/test/y2network/widgets/edit_interface_test.rb new file mode 100644 index 000000000..f9634e7bf --- /dev/null +++ b/test/y2network/widgets/edit_interface_test.rb @@ -0,0 +1,71 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/edit_interface" +require "y2network/config" + +describe Y2Network::Widgets::EditInterface do + subject { described_class.new(table) } + + let(:selected) { "eth0" } + let(:table) { double("table", value: selected) } + let(:config) { Y2Network::Config.new(interfaces: interfaces, connections: connections, source: :sysconfig) } + let(:eth0) { Y2Network::PhysicalInterface.new("eth0") } + let(:eth1) { Y2Network::PhysicalInterface.new("eth1") } + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, eth1]) } + let(:eth0_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap do |conn| + conn.name = "eth0" + end + end + let(:connections) { Y2Network::ConnectionConfigsCollection.new([eth0_conn]) } + let(:sequence) { instance_double(Y2Network::Sequences::Interface, edit: nil) } + + before do + allow(Yast::Lan).to receive(:yast_config).and_return(config) + allow(Y2Network::Sequences::Interface).to receive(:new).and_return(sequence) + end + + include_examples "CWM::PushButton" + + describe "#handle" do + it "runs the interface edition sequence" do + expect(sequence).to receive(:edit) do |builder| + expect(builder.type.short_name).to eq("eth") + expect(builder.name).to eq("eth0") + end + subject.handle + end + + context "when the interface is unconfigured" do + let(:selected) { "eth1" } + + it "runs the interface edition sequence" do + expect(sequence).to receive(:edit) do |builder| + expect(builder.type.short_name).to eq("eth") + expect(builder.name).to eq("eth1") + end + subject.handle + end + end + end +end diff --git a/test/y2network/widgets/firewall_zone_test.rb b/test/y2network/widgets/firewall_zone_test.rb index 793bee7df..0693793b0 100755 --- a/test/y2network/widgets/firewall_zone_test.rb +++ b/test/y2network/widgets/firewall_zone_test.rb @@ -1,5 +1,24 @@ #!/usr/bin/env rspec +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + require_relative "../../test_helper.rb" require "y2network/widgets/firewall_zone" require "y2network/interface_config_builder" @@ -8,8 +27,7 @@ describe Y2Network::Widgets::FirewallZone do let(:builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" + res = Y2Network::InterfaceConfigBuilder.for("eth") res.name = "eth0" res end diff --git a/test/y2network/widgets/general_tab_test.rb b/test/y2network/widgets/general_tab_test.rb index 5e24a43eb..070b62759 100644 --- a/test/y2network/widgets/general_tab_test.rb +++ b/test/y2network/widgets/general_tab_test.rb @@ -25,8 +25,7 @@ describe Y2Network::Widgets::GeneralTab do let(:builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" + res = Y2Network::InterfaceConfigBuilder.for("eth") res.name = "eth0" res end diff --git a/test/y2network/widgets/hardware_tab_test.rb b/test/y2network/widgets/hardware_tab_test.rb index 38d4325e1..906acf523 100644 --- a/test/y2network/widgets/hardware_tab_test.rb +++ b/test/y2network/widgets/hardware_tab_test.rb @@ -24,7 +24,7 @@ require "y2network/interface_config_builder" describe Y2Network::Widgets::HardwareTab do - subject { described_class.new(Y2Network::InterfaceConfigBuilder.new) } + subject { described_class.new(Y2Network::InterfaceConfigBuilder.for("eth")) } include_examples "CWM::Tab" end diff --git a/test/y2network/widgets/ifplugd_priority_test.rb b/test/y2network/widgets/ifplugd_priority_test.rb index 39adb172c..6be1af5b7 100644 --- a/test/y2network/widgets/ifplugd_priority_test.rb +++ b/test/y2network/widgets/ifplugd_priority_test.rb @@ -25,9 +25,8 @@ describe Y2Network::Widgets::IfplugdPriority do let(:builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" - res["IFPLUGD_PRIORITY"] = "50" + res = Y2Network::InterfaceConfigBuilder.for("eth") + res.ifplugd_priority = 50 res end subject { described_class.new(builder) } @@ -57,10 +56,11 @@ describe "#store" do it "sets IFPLUGD_PRIORITY according to widget value as string" do expect(subject).to receive(:value).and_return(20) + expect(subject).to receive(:enabled?).and_return(true) subject.store - expect(builder["IFPLUGD_PRIORITY"]).to eq "20" + expect(builder.ifplugd_priority).to eq 20 end end end diff --git a/test/y2network/widgets/interface_name_test.rb b/test/y2network/widgets/interface_name_test.rb index 1b0f8bed5..9f697fa92 100644 --- a/test/y2network/widgets/interface_name_test.rb +++ b/test/y2network/widgets/interface_name_test.rb @@ -25,12 +25,20 @@ describe Y2Network::Widgets::InterfaceName do let(:builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" - res + Y2Network::InterfaceConfigBuilder.for("eth") end subject { described_class.new(builder) } + let(:known_names) { [] } + + before do + allow(Yast::Lan).to receive(:yast_config).and_return( + double(interfaces: double(known_names: known_names, free_names: ["eth1"])) + ) + allow(builder).to receive(:find_interface) + builder.name = "eth0" + end + include_examples "CWM::ComboBox" describe "#validate" do @@ -39,7 +47,6 @@ it "passes for valid names only" do allow(subject).to receive(:value).and_return valid_name - allow(Yast::NetworkInterfaces).to receive(:List).and_return [] expect(Yast::Popup).to_not receive(:Error) expect(subject.validate).to be true @@ -48,18 +55,55 @@ # bnc#991486 it "fails for long names" do allow(subject).to receive(:value).and_return long_name - allow(Yast::NetworkInterfaces).to receive(:List).and_return [] expect(Yast::UI).to receive(:SetFocus) expect(subject.validate).to be false end - it "fails for already used names" do - allow(subject).to receive(:value).and_return valid_name - allow(Yast::NetworkInterfaces).to receive(:List).and_return [valid_name] + context "when the name is already used" do + let(:known_names) { [valid_name] } + before do + allow(subject).to receive(:value).and_return valid_name + end - expect(Yast::UI).to receive(:SetFocus) - expect(subject.validate).to be false + context "if the name was changed" do + let(:valid_name) { "eth1" } + + it "fails" do + expect(Yast::UI).to receive(:SetFocus) + expect(subject.validate).to be false + end + end + + context "if the name was not changed" do + it "passes" do + expect(subject.validate).to eq(true) + end + end + end + end + + describe "#store" do + before do + allow(subject).to receive(:value).and_return(value) + end + + context "when the name has changed" do + let(:value) { "eth1" } + + it "renames the interface" do + expect(builder).to receive(:rename_interface).with(value) + subject.store + end + end + + context "when the name has changed" do + let(:value) { builder.name } + + it "does not rename the interface" do + expect(builder).to_not receive(:rename_interface) + subject.store + end end end end diff --git a/test/y2network/widgets/interface_naming_test.rb b/test/y2network/widgets/interface_naming_test.rb new file mode 100644 index 000000000..26d65f586 --- /dev/null +++ b/test/y2network/widgets/interface_naming_test.rb @@ -0,0 +1,43 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/interface_naming" +require "y2network/interface_config_builder" +require "y2network/physical_interface" + +describe Y2Network::Widgets::InterfaceNaming do + subject(:widget) { described_class.new(builder) } + + let(:builder) do + instance_double( + Y2Network::InterfaceConfigBuilder, + interface: interface, + name: "eth0" + ) + end + + let(:interface) { instance_double(Y2Network::PhysicalInterface, hardware: nil) } + + include_examples "CWM::CustomWidget" + describe "#contents" + describe "#store" +end diff --git a/test/y2network/widgets/interfaces_table_test.rb b/test/y2network/widgets/interfaces_table_test.rb new file mode 100644 index 000000000..14bfdf31c --- /dev/null +++ b/test/y2network/widgets/interfaces_table_test.rb @@ -0,0 +1,132 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/interfaces_table" + +Yast.import "Lan" + +describe Y2Network::Widgets::InterfacesTable do + subject { described_class.new(description) } + + let(:description) { double(:value= => nil) } + + let(:eth0) { instance_double(Y2Network::Interface, name: "eth0", hardware: hwinfo, old_name: "eth1") } + let(:br0) { instance_double(Y2Network::VirtualInterface, name: "br0", hardware: nil, old_name: nil) } + let(:interfaces) { Y2Network::InterfacesCollection.new([eth0, br0]) } + let(:hwinfo) do + instance_double(Y2Network::Hwinfo, link: link, mac: mac, busid: busid, + exists?: exists?, present?: true, description: "", name: "Coold device") + end + let(:mac) { "01:23:45:67:89:ab" } + let(:busid) { "0000:04:00.0" } + let(:link) { false } + let(:exists?) { true } + let(:connections) { Y2Network::ConnectionConfigsCollection.new([eth0_conn, br0_conn]) } + let(:eth0_conn) do + Y2Network::ConnectionConfig::Ethernet.new.tap { |c| c.name = "eth0" } + end + let(:br0_conn) do + Y2Network::ConnectionConfig::Bridge.new.tap { |c| c.name = "br0" } + end + + before do + allow(Yast::Lan).to receive(:yast_config).and_return(double(interfaces: interfaces, connections: connections)) + allow(Yast::UI).to receive(:QueryWidget).and_return([]) + allow(subject).to receive(:value).and_return("eth0") + end + + include_examples "CWM::Table" + + describe "#handle" do + + it "updates the description" do + expect(description).to receive(:value=) + subject.handle + end + + it "includes the MAC address in the description" do + expect(description).to receive(:value=).with(/MAC/) + subject.handle + end + + context "when there is no MAC address" do + let(:mac) { nil } + + it "does not include the MAC in the description" do + expect(description).to receive(:value=) do |text| + expect(text).to_not include("MAC") + end + subject.handle + end + end + + it "includes the Bus ID address in the description" do + expect(description).to receive(:value=).with(/BusID/) + subject.handle + end + + context "when there is no Bus ID" do + let(:busid) { nil } + + it "does not include the Bus ID in the description" do + expect(description).to receive(:value=) do |text| + expect(text).to_not include("BusID") + end + subject.handle + end + end + + context "when there is no hardware information" do + let(:exists?) { false } + + it "sets the description with 'no hardware information' warning" do + expect(description).to receive(:value=).with(/No hardware information/) + subject.handle + end + end + + context "when there is no link" do + let(:link) { false } + + it "sets includes a 'Not connected' text" do + expect(description).to receive(:value=).with(/Not connected/) + subject.handle + end + end + + context "when the device is configured" do + it "includes its device name in the description" do + expect(description).to receive(:value=).with(/Device Name: eth0/) + subject.handle + end + end + + context "when the device is not configured" do + let(:connections) { Y2Network::ConnectionConfigsCollection.new([]) } + + it "includes a warning in the description" do + expect(description).to receive(:value=).with(/The device is not configured./) + subject.handle + end + end + end +end diff --git a/test/y2network/widgets/kernel_module_test.rb b/test/y2network/widgets/kernel_module_test.rb index b47f29aae..e7a4e07ec 100644 --- a/test/y2network/widgets/kernel_module_test.rb +++ b/test/y2network/widgets/kernel_module_test.rb @@ -21,15 +21,9 @@ require "cwm/rspec" require "y2network/widgets/kernel_module" -require "y2network/interface_config_builder" describe Y2Network::Widgets::KernelModule do - let(:builder) do - res = Y2Network::InterfaceConfigBuilder.new - res.type = "eth" - res - end - subject { described_class.new(builder) } + subject { described_class.new(["virtio_net", "alt"], "virtio_net") } include_examples "CWM::ComboBox" end diff --git a/test/y2network/widgets/mtu_test.rb b/test/y2network/widgets/mtu_test.rb index 3f460782a..b83f74545 100644 --- a/test/y2network/widgets/mtu_test.rb +++ b/test/y2network/widgets/mtu_test.rb @@ -21,9 +21,11 @@ require "cwm/rspec" require "y2network/widgets/mtu" +require "y2network/interface_config_builder" describe Y2Network::Widgets::MTU do - subject { described_class.new({}) } + let(:builder) { Y2Network::InterfaceConfigBuilder.for("eth") } + subject { described_class.new(builder) } include_examples "CWM::ComboBox" end diff --git a/test/y2network/widgets/renaming_mechanism_test.rb b/test/y2network/widgets/renaming_mechanism_test.rb new file mode 100644 index 000000000..dbe5f666e --- /dev/null +++ b/test/y2network/widgets/renaming_mechanism_test.rb @@ -0,0 +1,66 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/renaming_mechanism" +require "y2network/interface_config_builder" +require "y2network/physical_interface" + +describe Y2Network::Widgets::RenamingMechanism do + subject { described_class.new(builder) } + + let(:builder) do + instance_double(Y2Network::InterfaceConfigBuilder, interface: interface, renaming_mechanism: mechanism) + end + + let(:interface) { Y2Network::PhysicalInterface.new("eth0") } + let(:hwinfo) { Y2Network::Hwinfo.new(mac: "01:23:45:67:89:ab", busid: "0000:08:00.0") } + let(:mechanism) { :mac } + + include_examples "CWM::CustomWidget" + + before do + allow(interface).to receive(:hardware).and_return(hwinfo) + allow(subject).to receive(:value).and_return(mechanism) + end + + describe "#store" do + before do + subject.init + end + + context "when the MAC is selected as renaming method" do + it "returns :mac as the renaming mechanism" do + expect(builder).to receive(:renaming_mechanism=).with(:mac) + subject.store + end + end + + context "when the BUS ID is selected as renaming method" do + let(:mechanism) { :bus_id } + + it "returns :bus_id as the renaming mechanism" do + expect(builder).to receive(:renaming_mechanism=).with(:bus_id) + subject.store + end + end + end +end diff --git a/test/y2network/widgets/s390_channels_test.rb b/test/y2network/widgets/s390_channels_test.rb new file mode 100644 index 000000000..729241d35 --- /dev/null +++ b/test/y2network/widgets/s390_channels_test.rb @@ -0,0 +1,111 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/widgets/s390_channels" +require "y2network/interface_config_builder" + +require "cwm/rspec" + +describe Y2Network::Widgets::S390ReadChannel do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + let(:read_channel) { "0.0.0700" } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" + + describe "#init" do + it "initializes the widget value with the device read channel" do + builder.read_channel = read_channel + expect(subject).to receive(:value=).with(read_channel) + subject.init + end + end + + describe "#store" do + before do + allow(subject).to receive(:value).and_return("0.0.0800") + end + + it "modifies the device read channel with the widget value" do + builder.read_channel = read_channel + expect { subject.store }.to change { builder.read_channel }.from(read_channel).to("0.0.0800") + end + end +end + +describe Y2Network::Widgets::S390WriteChannel do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + let(:write_channel) { "0.0.0701" } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" + + describe "#init" do + it "initializes the widget value with the device write channel" do + builder.write_channel = write_channel + expect(subject).to receive(:value=).with(write_channel) + subject.init + end + end + + describe "#store" do + before do + allow(subject).to receive(:value).and_return("0.0.0801") + end + + it "modifies the device write channel with the widget value" do + builder.write_channel = write_channel + expect { subject.store }.to change { builder.write_channel }.from(write_channel).to("0.0.0801") + end + end +end + +describe Y2Network::Widgets::S390DataChannel do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + let(:data_channel) { "0.0.0702" } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" + + describe "#init" do + it "initializes the widget value with the device data channel" do + builder.data_channel = data_channel + expect(subject).to receive(:value=).with(data_channel) + subject.init + end + end + + describe "#store" do + before do + allow(subject).to receive(:value).and_return("0.0.0802") + end + + it "modifies the device data channel with the widget value" do + builder.data_channel = data_channel + expect { subject.store }.to change { builder.data_channel }.from(data_channel).to("0.0.0802") + end + end +end + +describe Y2Network::Widgets::S390Channels do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + subject { described_class.new(builder) } + include_examples "CWM::CustomWidget" +end diff --git a/test/y2network/widgets/s390_common_test.rb b/test/y2network/widgets/s390_common_test.rb new file mode 100644 index 000000000..0c957b220 --- /dev/null +++ b/test/y2network/widgets/s390_common_test.rb @@ -0,0 +1,159 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "y2network/widgets/s390_common" +require "y2network/interface_config_builder" + +require "cwm/rspec" + +describe Y2Network::Widgets::S390LanCmdTimeout do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("lcs") } + subject { described_class.new(builder) } + include_examples "CWM::InputField" +end + +describe Y2Network::Widgets::S390Protocol do + let(:builder) do + res = Y2Network::InterfaceConfigBuilder.for("ctc") + res.name = "ctc0" + res.protocol = 1 + res + end + + subject { described_class.new(builder) } + + include_examples "CWM::ComboBox" + + describe "#init" do + it "initializes the widget value with the configured protocol" do + expect(subject).to receive(:value=).with("1") + subject.init + end + end + + describe "#store" do + before do + allow(subject).to receive(:value).and_return("4") + end + + it "modifies the builder protocol attribute with the widget value" do + expect { subject.store }.to change { builder.protocol }.from(1).to(4) + end + end + +end + +describe Y2Network::Widgets::S390PortNumber do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + subject { described_class.new(builder) } + + include_examples "CWM::ComboBox" +end + +describe Y2Network::Widgets::S390Attributes do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" +end + +describe Y2Network::Widgets::S390Layer2 do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + let(:layer2_support) { true } + let(:layer2_address) { "00:00:00:00:00:00" } + let(:layer2_support_widget) do + instance_double("Y2Network::WidgetsS390Layer2Support", + value: layer2_support, widget_id: "layer2_support") + end + + let(:layer2_address_widget) do + instance_double("Y2Network::WidgetsS390Layer2Address", + value: layer2_address, widget_id: "layer2_address") + end + + subject { described_class.new(builder) } + + before do + allow(subject).to receive(:support_widget).and_return(layer2_support_widget) + allow(subject).to receive(:mac_address_widget).and_return(layer2_address_widget) + end + + include_examples "CWM::CustomWidget" + + describe "#handle" do + context "when the event handled is for the layer2_support widget" do + it "refresh the mac address widget" do + expect(subject).to receive(:refresh) + subject.handle("ID" => "layer2_support") + end + end + + it "returns nil" do + allow(subject).to receive(:refresh) + expect(subject.handle("ID" => "layer2_address")).to be_nil + expect(subject.handle("ID" => "layer2_support")).to be_nil + end + end + + describe "#validate" do + context "when the layer2 support is not enabled" do + let(:layer2_support) { false } + it "returns true" do + expect(subject.validate).to eql(true) + end + end + + context "when the layer2 support is enabled" do + context "and the MAC provided is valid" do + let(:layer2_address) { "02:00:00:00:01:FD" } + + it "returns true" do + expect(subject.validate).to eql(true) + end + end + + context "and the MAC address provided is invalid" do + it "returns false" do + allow(Yast::Popup).to receive(:Error) + expect(subject.validate).to eql(false) + end + + it "reports an error" do + expect(Yast::Popup).to receive(:Error).with(/MAC address provided is not valid/) + expect(subject.validate).to eql(false) + end + end + end + end +end + +describe Y2Network::Widgets::S390Layer2Support do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + subject { described_class.new(builder) } + + include_examples "CWM::CheckBox" +end + +describe Y2Network::Widgets::S390Layer2Address do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("qeth") } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" +end diff --git a/test/y2network/widgets/udev_rules_test.rb b/test/y2network/widgets/udev_rules_test.rb index 092912429..0d6915c1d 100644 --- a/test/y2network/widgets/udev_rules_test.rb +++ b/test/y2network/widgets/udev_rules_test.rb @@ -21,14 +21,67 @@ require "cwm/rspec" require "y2network/widgets/udev_rules" +require "y2network/dialogs/rename_interface" +require "y2network/interface_config_builder" describe Y2Network::Widgets::UdevRules do - subject { described_class.new({}) } + subject { described_class.new(builder) } + + let(:builder) { instance_double(Y2Network::InterfaceConfigBuilder, name: "eth0") } + let(:dialog_ret) { :ok } + let(:dialog) { instance_double(Y2Network::Dialogs::RenameInterface, run: dialog_ret) } before do - allow(Yast::LanItems).to receive(:current_udev_name).and_return("hell666") - allow(Yast::EditNicName).to receive(:new).and_return(double(run: "heaven010")) + allow(Y2Network::Dialogs::RenameInterface).to receive(:new).and_return(dialog) end include_examples "CWM::CustomWidget" + include Yast::UIShortcuts + + describe "#init" do + it "initializes the input field with the current name" do + expect(subject).to receive(:value=).with("eth0") + subject.init + end + end + + describe "#handle" do + it "opens the rename interface dialog" do + expect(dialog).to receive(:run) + subject.handle + end + + context "when the dialog returns :ok" do + it "updates the current value" do + expect(subject).to receive(:value=).with(builder.name) + subject.handle + end + end + + context "when the dialog does not returns :ok" do + let(:dialog_ret) { :cancel } + + it "does not update the current value" do + expect(subject).to_not receive(:value=) + subject.handle + end + end + end + + describe "#value=" do + it "updates the input field with the given name" do + expect(Yast::UI).to receive(:ChangeWidget).with(Id(:udev_rules_name), :Value, "eth1") + subject.value = "eth1" + end + end + + describe "#value" do + before do + expect(Yast::UI).to receive(:QueryWidget).with(Id(:udev_rules_name), :Value).and_return("eth1") + end + + it "returns the value from the input field" do + expect(subject.value).to eq("eth1") + end + end end diff --git a/test/y2network/widgets/vlan_interface_test.rb b/test/y2network/widgets/vlan_interface_test.rb index ed95f2add..921dda5b5 100644 --- a/test/y2network/widgets/vlan_interface_test.rb +++ b/test/y2network/widgets/vlan_interface_test.rb @@ -27,5 +27,9 @@ let(:builder) { Y2Network::InterfaceConfigBuilder.for("vlan") } subject { described_class.new(builder) } + before do + allow(builder).to receive(:yast_config).and_return(Y2Network::Config.new(source: :testing)) + end + include_examples "CWM::ComboBox" end diff --git a/test/y2network/widgets/wireless_auth_test.rb b/test/y2network/widgets/wireless_auth_test.rb new file mode 100644 index 000000000..df67d27b4 --- /dev/null +++ b/test/y2network/widgets/wireless_auth_test.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/wireless_auth" +require "y2network/interface_config_builder" + +describe Y2Network::Widgets::WirelessAuth do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end diff --git a/test/y2network/widgets/wireless_eap_mode_test.rb b/test/y2network/widgets/wireless_eap_mode_test.rb new file mode 100644 index 000000000..e87b5cfed --- /dev/null +++ b/test/y2network/widgets/wireless_eap_mode_test.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/wireless_eap" +require "y2network/interface_config_builder" + +describe Y2Network::Widgets::WirelessEapMode do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::ComboBox" +end diff --git a/test/y2network/widgets/wireless_eap_test.rb b/test/y2network/widgets/wireless_eap_test.rb new file mode 100644 index 000000000..116a3d0f6 --- /dev/null +++ b/test/y2network/widgets/wireless_eap_test.rb @@ -0,0 +1,73 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/wireless_eap" +require "y2network/interface_config_builder" + +describe Y2Network::Widgets::WirelessEap do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end + +describe Y2Network::Widgets::EapPeap do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end + +describe Y2Network::Widgets::EapTls do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end + +describe Y2Network::Widgets::EapTtls do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end + +describe Y2Network::Widgets::EapPassword do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::Password" +end + +describe Y2Network::Widgets::EapUser do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" +end + +describe Y2Network::Widgets::EapAnonymousUser do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" +end diff --git a/test/y2network/widgets/wireless_essid_test.rb b/test/y2network/widgets/wireless_essid_test.rb new file mode 100644 index 000000000..adf6d6031 --- /dev/null +++ b/test/y2network/widgets/wireless_essid_test.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/wireless_essid" +require "y2network/interface_config_builder" + +describe Y2Network::Widgets::WirelessEssid do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end diff --git a/test/y2network/widgets/wireless_expert_test.rb b/test/y2network/widgets/wireless_expert_test.rb new file mode 100644 index 000000000..36d9c7777 --- /dev/null +++ b/test/y2network/widgets/wireless_expert_test.rb @@ -0,0 +1,52 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/wireless_expert" +require "y2network/interface_config_builder" + +describe Y2Network::Widgets::WirelessChannel do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::ComboBox" +end + +describe Y2Network::Widgets::WirelessBitRate do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::ComboBox" +end + +describe Y2Network::Widgets::WirelessAccessPoint do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::InputField" +end + +describe Y2Network::Widgets::WirelessAPScanMode do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::IntField" +end diff --git a/test/y2network/widgets/wireless_test.rb b/test/y2network/widgets/wireless_test.rb new file mode 100644 index 000000000..d8329cf7d --- /dev/null +++ b/test/y2network/widgets/wireless_test.rb @@ -0,0 +1,31 @@ +# Copyright (c) [2019] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "cwm/rspec" + +require "y2network/widgets/wireless" +require "y2network/interface_config_builder" + +describe Y2Network::Widgets::Wireless do + let(:builder) { Y2Network::InterfaceConfigBuilder.for("wlan") } + subject { described_class.new(builder) } + + include_examples "CWM::CustomWidget" +end diff --git a/test/yaml_defaults_test.rb b/test/yaml_defaults_test.rb deleted file mode 100755 index 627565de2..000000000 --- a/test/yaml_defaults_test.rb +++ /dev/null @@ -1,62 +0,0 @@ -#! /usr/bin/env rspec - -require_relative "test_helper" - -require "yast" - -Yast.import "LanItems" - -describe "LanItems#InitS390VarsByDefaults" do - Yast.import "Arch" - - subject(:lan_items) { Yast::LanItems } - - it "sets defaults for s390 as expected" do - allow(Yast::Arch) - .to receive(:s390) - .and_return(true) - - # we need to be sure that LanItems' initialization is done *now* - # to accept arch mocking which is needed for loading reasonable - # defaults - lan_items.main - lan_items.InitS390VarsByDefaults - - expect(lan_items.chan_mode).to eql "0" - expect(lan_items.qeth_layer2).to be false - expect(lan_items.qeth_macaddress).to eql "00:00:00:00:00:00" - expect(lan_items.ipa_takeover).to be false - end -end - -describe "LanItems#SetDeviceVars" do - subject(:lan_items) { Yast::LanItems } - - it "sets generic defaults as expected" do - lan_items.SetDeviceVars({}, lan_items.instance_variable_get("@SysconfigDefaults")) - - expect(lan_items.bootproto).to eql "static" - expect(lan_items.startmode).to be_empty - end -end - -describe "LanItems#request_firmware" do - subject(:lan_items) { Yast::LanItems } - - it "loads module to firmware mapping properly" do - lan_items.main - - mapping = lan_items.instance_variable_get("@request_firmware") - expect(mapping["b43"]).to eql "b43-fwcutter" - end -end - -describe "YAML data files" do - globs = Yast.y2paths.map { |p| "#{p}/data/**/*.yml" } - yml_filenames = Dir.glob(globs) - yml_filenames.each do |f| - it "parse without error: #{f}" do - expect { YAML.load_file(f) }.to_not raise_error - end - end -end