From ca34010f2d34205493489a14f42d1e2334cc8077 Mon Sep 17 00:00:00 2001 From: Shim Shtein Date: Tue, 19 Dec 2023 12:45:48 +0200 Subject: [PATCH] Fixes #37367 - Switch to 'network' directive instead of ifcfg --- app/models/nic/base.rb | 6 +- app/models/nic/interface.rb | 4 + .../foreman/template_snapshot_service.rb | 7 + .../finish/kickstart_default_finish.erb | 8 - .../provision/kickstart_default.erb | 95 +---- .../provision/kickstart_ovirt.erb | 26 +- .../snippet/kickstart_network_interface.erb | 108 ++++++ config/initializers/safemode_jail.rb | 10 +- ...ickstart_default_finish.host4dhcp.snap.txt | 4 - .../AutoYaST_SLES_default.host4dhcp.snap.txt | 6 + .../AutoYaST_default.host4dhcp.snap.txt | 6 + .../Kickstart_default.host4and6dhcp.snap.txt | 6 +- .../Kickstart_default.host4dhcp.snap.txt | 6 +- .../Kickstart_default.host4static.snap.txt | 6 +- .../Kickstart_default.host6dhcp.snap.txt | 6 +- .../Kickstart_default.host6static.snap.txt | 6 +- .../Kickstart_default.rhel9_dhcp.snap.txt | 6 +- .../Kickstart_default.rocky8_dhcp.snap.txt | 6 +- .../Kickstart_default.rocky9_dhcp.snap.txt | 6 +- .../kickstart_network_interface_test.rb | 354 ++++++++++++++++++ 20 files changed, 536 insertions(+), 146 deletions(-) create mode 100644 app/views/unattended/provisioning_templates/snippet/kickstart_network_interface.erb create mode 100644 test/unit/foreman/templates/snippets/kickstart_network_interface_test.rb diff --git a/app/models/nic/base.rb b/app/models/nic/base.rb index b0e0c3f06ed..70bef808a53 100644 --- a/app/models/nic/base.rb +++ b/app/models/nic/base.rb @@ -119,9 +119,9 @@ class Base < ApplicationRecord end class Jail < ::Safemode::Jail allow :id, :subnet, :subnet6, :virtual?, :physical?, :mac, :ip, :ip6, :identifier, :attached_to, - :link, :tag, :domain, :vlanid, :mtu, :bond_options, :attached_devices, :mode, - :attached_devices_identifiers, :primary, :provision, :alias?, :inheriting_mac, - :children_mac_addresses, :nic_delay, :fqdn, :shortname, :type, :managed?, :bond?, :bridge?, :bmc? + :link, :tag, :domain, :bond_options, :attached_devices, :mode, + :attached_devices_identifiers, :primary, :provision, :inheriting_mac, + :children_mac_addresses, :nic_delay, :fqdn, :shortname, :type, :managed?, :bond?, :bmc?, :provision? end # include STI inheritance column in audits diff --git a/app/models/nic/interface.rb b/app/models/nic/interface.rb index a8dec6d6601..4c8fa312404 100644 --- a/app/models/nic/interface.rb +++ b/app/models/nic/interface.rb @@ -26,6 +26,10 @@ class Interface < Base alias_method :network, :subnet_network alias_method :network6, :subnet6_network + class Jail < Nic::Base::Jail + allow :mtu, :vlanid, :bridge?, :alias? + end + def vlanid # Determine a vlanid according to the following cascading rules: # 1. if the interface has a tag, use that as the vlanid diff --git a/app/services/foreman/template_snapshot_service.rb b/app/services/foreman/template_snapshot_service.rb index 4287b68be05..abdbf2a4de6 100644 --- a/app/services/foreman/template_snapshot_service.rb +++ b/app/services/foreman/template_snapshot_service.rb @@ -114,6 +114,7 @@ def host4dhcp name: 'snapshot-ipv4-dhcp-el7', subnet: FactoryBot.build(:subnet_ipv4_dhcp_for_snapshots), interfaces: [ipv4_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end @@ -122,6 +123,7 @@ def host4static name: 'snapshot-ipv4-static-el7', subnet: FactoryBot.build(:subnet_ipv4_static_for_snapshots), interfaces: [ipv4_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end @@ -130,6 +132,7 @@ def host6dhcp name: 'snapshot-ipv6-dhcp-el7', subnet: FactoryBot.build(:subnet_ipv6_dhcp_for_snapshots), interfaces: [ipv6_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end @@ -138,6 +141,7 @@ def host6static name: 'snapshot-ipv6-static-el7', subnet: FactoryBot.build(:subnet_ipv6_static_for_snapshots), interfaces: [ipv6_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end @@ -147,6 +151,7 @@ def host4and6dhcp subnet: FactoryBot.build(:subnet_ipv4_dhcp_for_snapshots), subnet6: FactoryBot.build(:subnet_ipv6_dhcp_for_snapshots), interfaces: [ipv46_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end @@ -155,6 +160,7 @@ def debian4dhcp name: 'snapshot-ipv4-dhcp-deb10', subnet: FactoryBot.build(:subnet_ipv4_dhcp_for_snapshots), interfaces: [ipv4_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end @@ -179,6 +185,7 @@ def rhel9_dhcp name: 'snapshot-ipv4-dhcp-rhel9', subnet: FactoryBot.build(:subnet_ipv4_dhcp_for_snapshots), interfaces: [ipv4_interface]) + host.stubs(:managed_interfaces).returns(host.interfaces) define_host_params(host) end diff --git a/app/views/unattended/provisioning_templates/finish/kickstart_default_finish.erb b/app/views/unattended/provisioning_templates/finish/kickstart_default_finish.erb index 8fe6bcd3a15..f4d5c983590 100644 --- a/app/views/unattended/provisioning_templates/finish/kickstart_default_finish.erb +++ b/app/views/unattended/provisioning_templates/finish/kickstart_default_finish.erb @@ -33,14 +33,6 @@ description: | %> <%= snippet_if_exists(template_name + " custom pre") -%> -<% if @host.subnet.respond_to?(:dhcp_boot_mode?) -%> -<%= snippet 'kickstart_networking_setup' %> -<% if (rhel_compatible && os_major >= 8) -%> -systemctl restart NetworkManager -<% else -%> -service network restart -<% end -%> -<% end -%> <% if @host.provision_method == 'image' && root_pass.present? -%> # Install the root password diff --git a/app/views/unattended/provisioning_templates/provision/kickstart_default.erb b/app/views/unattended/provisioning_templates/provision/kickstart_default.erb index 429191f1e28..5c4c9a17c9e 100644 --- a/app/views/unattended/provisioning_templates/provision/kickstart_default.erb +++ b/app/views/unattended/provisioning_templates/provision/kickstart_default.erb @@ -114,85 +114,22 @@ selinux --<%= host_param('selinux-mode') || host_param('selinux') || 'enforcing' keyboard <%= host_param('keyboard') || 'us' %> <% - network_options = [] - nameservers = [] - subnet4 = iface.subnet - subnet6 = iface.subnet6 - - # device and hostname - if iface.bond? && rhel_compatible && os_major >= 6 - network_options.push("--device=#{iface.identifier}") - else - network_options.push("--device=#{iface.mac || iface.identifier}") - end - network_options.push("--hostname #{@host.name}") - - # single stack - if subnet4 && !subnet6 - network_options.push("--noipv6") - elsif !subnet4 && subnet6 - network_options.push("--noipv4") - end - - # dual stack MTU check - raise("IPv4 and IPv6 subnets have different MTU") if subnet4 && subnet6 && subnet4.mtu.present? && subnet6.mtu.present? && subnet4.mtu != subnet6.mtu - - # IPv4 - if (subnet4 && !subnet4.dhcp_boot_mode?) || @static - network_options.push("--bootproto static") - network_options.push("--ip=#{iface.ip}") - network_options.push("--netmask=#{subnet4.mask}") - network_options.push("--gateway=#{subnet4.gateway}") - elsif subnet4 && subnet4.dhcp_boot_mode? - network_options.push("--bootproto dhcp") - end - if subnet4 - nameservers.concat(subnet4.dns_servers) - network_options.push("--mtu=#{subnet4.mtu}") if subnet4.mtu.present? - end - - # IPv6 - if rhel_compatible && os_major >= 6 - if (subnet6 && !subnet6.dhcp_boot_mode?) || @static6 - network_options.push("--ipv6=#{iface.ip6}/#{subnet6.cidr}") - network_options.push("--ipv6gateway=#{subnet6.gateway}") - elsif subnet6 && subnet6.dhcp_boot_mode? - if host_param_true?('use-slaac') - network_options.push("--ipv6 auto") - else - network_options.push("--ipv6 dhcp") - end - end - if subnet6 - nameservers.concat(subnet6.dns_servers) - network_options.push("--mtu=#{subnet6.mtu}") if subnet6.mtu.present? - end - end - - # bond - if iface.bond? && rhel_compatible && os_major >= 6 - bond_slaves = iface.attached_devices_identifiers.join(',') - network_options.push("--bondslaves=#{bond_slaves}") - network_options.push("--bondopts=mode=#{iface.mode},#{iface.bond_options.tr(' ', ',')}") - end - - # VLAN (only on physical is recognized) - if iface.virtual? && iface.tag.present? && iface.attached_to.present? - if rhel_compatible && os_major == 6 - network_options.push("--vlanid=#{iface.tag}") - else - network_options.push("--interfacename=vlan#{iface.tag}") - end - end - - # DNS - if nameservers.size > 0 - network_options.push("--nameserver=#{nameservers.join(',')}") - else - network_options.push("--nodns") - end +# start with provisioning interface, then other non-bond non-bridge interfaces and the bonds + bridges at the end +@host.interfaces.reject{ |iface| iface.bmc? }.sort_by { |iface| (iface.bond? || iface.bridge?) ? 0 : iface.provision? ? 20 : 10 }.each do |iface| +-%> +<%= snippet( + 'kickstart_network_interface', + variables: { + iface: iface, + host: @host, + use_slaac: host_param_true?('use-slaac'), + static: @static, + static6: @static6 + } + ) -%> +<% +end -%> -network <%= network_options.join(' ') %> rootpw --iscrypted <%= root_pass %> <% if host_param_true?('disable-firewall') -%> @@ -300,8 +237,6 @@ exec < /dev/tty3 > /dev/tty3 chvt 3 ( logger "Starting anaconda <%= @host %> postinstall" -<%= snippet 'kickstart_networking_setup' %> - <%= snippet 'ntp' %> <%= snippet 'yum_proxy' %> diff --git a/app/views/unattended/provisioning_templates/provision/kickstart_ovirt.erb b/app/views/unattended/provisioning_templates/provision/kickstart_ovirt.erb index f548795c93b..f3176a41f90 100644 --- a/app/views/unattended/provisioning_templates/provision/kickstart_ovirt.erb +++ b/app/views/unattended/provisioning_templates/provision/kickstart_ovirt.erb @@ -51,13 +51,24 @@ end liveimg --url=<%= liveimg_url %> -<% subnet = @host.subnet -%> -<% if subnet.respond_to?(:dhcp_boot_mode?) -%> -<% dhcp = subnet.dhcp_boot_mode? && !@static -%> -<% else -%> -<% dhcp = !@static -%> -<% end -%> -network --bootproto <%= dhcp ? 'dhcp' : "static --ip=#{@host.ip} --netmask=#{subnet.mask} --gateway=#{subnet.gateway} --nameserver=#{subnet.dns_servers.join(',')}" %> --hostname <%= @host %> --device=<%= @host.mac -%> +<% +# start with provisioning interface, then other non-bond interfaces and the bonds at the end +@host.managed_interfaces.sort_by { |iface| (iface.bond? || iface.bridge?) 0 : iface.provision? 20 : 10 }.each |iface| do +%> + <%= snippet( + 'kickstart_network_interface', + variables: { + iface: iface, + host: @host, + use_slaac: false, + static: @static, + static6: @static6 + } + ) -%> +<% +end +-%> + rootpw --iscrypted <%= root_pass %> <% if host_param_true?('disable-firewall') -%> @@ -77,7 +88,6 @@ reboot %post --log=/root/ks.post.log --erroronfail nodectl init <%= snippet 'redhat_register' %> -<%= snippet 'kickstart_networking_setup' %> <%= snippet 'efibootmgr_netboot' %> <% if host_param_true?('host_registration_insights') -%> <%= snippet 'insights' -%> diff --git a/app/views/unattended/provisioning_templates/snippet/kickstart_network_interface.erb b/app/views/unattended/provisioning_templates/snippet/kickstart_network_interface.erb new file mode 100644 index 00000000000..9c173090e41 --- /dev/null +++ b/app/views/unattended/provisioning_templates/snippet/kickstart_network_interface.erb @@ -0,0 +1,108 @@ +<%# +name: kickstart_network_interface +model: ProvisioningTemplate +snippet: true +model: ProvisioningTemplate +kind: snippet +description: | + Generates network directive for a given interface. It is not expected to be used directly. +-%> +<%# + # Variables: iface, host, use_slaac, static, static6 +-%> +<%if @iface.managed? -%> +<% +network_options = [] +nameservers = [] +subnet4 = @iface.subnet +subnet6 = @iface.subnet6 + +rhel_compatible = @host.operatingsystem.family == 'Redhat' && @host.operatingsystem.name != 'Fedora' +is_fedora = @host.operatingsystem.name == 'Fedora' +os_major = @host.operatingsystem.major.to_i + +# device and hostname +if @iface.bond? || @iface.bridge? + network_options.push("--device=#{@iface.identifier}") +else + network_options.push("--device=#{@iface.mac || @iface.identifier}") +end +network_options.push("--hostname #{@host.name}") + +# single stack +network_options.push("--noipv6") if subnet4 && !subnet6 +network_options.push("--noipv4") if !subnet4 && subnet6 + +# dual stack MTU check +raise("IPv4 and IPv6 subnets have different MTU") if subnet4 && subnet6 && subnet4.mtu.present? && subnet6.mtu.present? && subnet4.mtu != subnet6.mtu + +# mtu method is taking into account both ipv4 and ipv6 stacks +if @iface.respond_to?(:mtu) && @iface.mtu + network_options.push("--mtu=#{@iface.mtu}") +end + +# IPv4 +if subnet4 + if !subnet4.dhcp_boot_mode? || @static + network_options.push("--bootproto static") + network_options.push("--ip=#{@iface.ip}") + network_options.push("--netmask=#{subnet4.mask}") + network_options.push("--gateway=#{subnet4.gateway}") + elsif subnet4.dhcp_boot_mode? + network_options.push("--bootproto dhcp") + end + + nameservers.concat(subnet4.dns_servers) +end + +# IPv6 +if subnet6 + if !subnet6.dhcp_boot_mode? || @static6 + network_options.push("--ipv6=#{@iface.ip6}/#{subnet6.cidr}") + network_options.push("--ipv6gateway=#{subnet6.gateway}") + elsif subnet6.dhcp_boot_mode? + if @use_slaac + network_options.push("--ipv6 auto") + else + network_options.push("--ipv6 dhcp") + end + end + + nameservers.concat(subnet6.dns_servers) +end + +# bond +if @iface.bond? + bond_slaves = @iface.attached_devices_identifiers.join(',') + network_options.push("--bondslaves=#{bond_slaves}") + network_options.push("--bondopts=mode=#{@iface.mode},#{@iface.bond_options.tr(' ', ',')}") +end + +# bridge +if @iface.bridge? + bridge_slaves = @iface.attached_devices_identifiers.join(',') + network_options.push("--bridgeslaves=#{bridge_slaves}") + # Currently no support for bridge options in the interface. +end + +# VLAN (only on physical is recognized) +if @iface.virtual? && @iface.tag.present? && @iface.attached_to.present? + network_options.push("--vlanid=#{@iface.tag}") + network_options.push("--interfacename=vlan#{@iface.tag}") if (is_fedora && os_major >= 21) || (rhel_compatible && os_major >= 7) +end + +# DNS +if nameservers.empty? + network_options.push("--nodns") +else + network_options.push("--nameserver=#{nameservers.join(',')}") +end + +# Search domain - available from Fedora 39 (RHEL 10) +if @iface.domain && ((is_fedora && os_major >= 39) || (rhel_compatible && os_major >= 10)) + network_options.push("--ipv4-dns-search=#{@iface.domain}") if subnet4 + network_options.push("--ipv6-dns-search=#{@iface.domain}") if subnet6 +end +-%> +<%= "network #{network_options.join(' ')}\n" -%> +<% end -%> diff --git a/config/initializers/safemode_jail.rb b/config/initializers/safemode_jail.rb index 34576c8ac49..cd67430bdb0 100644 --- a/config/initializers/safemode_jail.rb +++ b/config/initializers/safemode_jail.rb @@ -1,15 +1,15 @@ # Permit safemode template rendering to have basic read-only access over # model relations class ActiveRecord::AssociationRelation::Jail < Safemode::Jail - allow :[], :each, :first, :to_a, :map, :find_in_batches, :size, :group_by, :ids + allow :[], :each, :first, :to_a, :map, :find_in_batches, :size, :group_by, :ids, :sort_by, :select, :reject end class ActiveRecord::Relation::Jail < Safemode::Jail - allow :[], :each, :first, :to_a, :map, :find_in_batches, :size, :group_by, :ids + allow :[], :each, :first, :to_a, :map, :find_in_batches, :size, :group_by, :ids, :select, :reject end class ActiveRecord::Associations::CollectionProxy::Jail < Safemode::Jail - allow :[], :each, :first, :to_a, :map, :find_in_batches, :size, :group_by, :ids + allow :[], :each, :first, :to_a, :map, :find_in_batches, :size, :group_by, :ids, :sort_by, :select, :reject end class ActiveRecord::Batches::BatchEnumerator::Jail < Safemode::Jail @@ -23,3 +23,7 @@ class URI::Generic::Jail < Safemode::Jail class ActiveSupport::TimeWithZone::Jail < Safemode::Jail allow(*Safemode.core_jail_methods(Time).uniq) end + +class Array::Jail < Safemode::Jail + allow :sort_by, :select, :reject +end diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/finish/Kickstart_default_finish.host4dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/finish/Kickstart_default_finish.host4dhcp.snap.txt index 1018307b2dc..89e5d2fa38a 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/finish/Kickstart_default_finish.host4dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/finish/Kickstart_default_finish.host4dhcp.snap.txt @@ -3,10 +3,6 @@ -service network restart - - - diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_SLES_default.host4dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_SLES_default.host4dhcp.snap.txt index fc5785ad575..3625d8ac056 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_SLES_default.host4dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_SLES_default.host4dhcp.snap.txt @@ -16,6 +16,12 @@ + + dhcp + eth0 + no + auto + diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_default.host4dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_default.host4dhcp.snap.txt index 348e096ff33..aa794a3cfcd 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_default.host4dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/AutoYaST_default.host4dhcp.snap.txt @@ -16,6 +16,12 @@ + + dhcp + eth0 + no + auto + diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4and6dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4and6dhcp.snap.txt index 0231879d37c..1b8ae75652b 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4and6dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4and6dhcp.snap.txt @@ -12,7 +12,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-6-dhcp-el7 --noipv6 --bootproto dhcp --mtu=1142 --nameserver=192.168.42.2,192.168.42.3 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-6-dhcp-el7 --noipv6 --mtu=1142 --bootproto dhcp --nameserver=192.168.42.2,192.168.42.3 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -66,10 +66,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv4-6-dhcp-el7 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4dhcp.snap.txt index d46358589a8..3a77ebe2d28 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4dhcp.snap.txt @@ -12,7 +12,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-el7 --noipv6 --bootproto dhcp --mtu=1142 --nameserver=192.168.42.2,192.168.42.3 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-el7 --noipv6 --mtu=1142 --bootproto dhcp --nameserver=192.168.42.2,192.168.42.3 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -66,10 +66,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv4-dhcp-el7 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4static.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4static.snap.txt index b32d7c02779..a7a76b8f2e2 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4static.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host4static.snap.txt @@ -12,7 +12,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-static-el7 --noipv6 --bootproto static --ip=192.168.42.42 --netmask=255.255.255.0 --gateway=192.168.42.1 --mtu=1242 --nameserver=192.168.42.2,192.168.42.3 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-static-el7 --noipv6 --mtu=1242 --bootproto static --ip=192.168.42.42 --netmask=255.255.255.0 --gateway=192.168.42.1 --nameserver=192.168.42.2,192.168.42.3 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -66,10 +66,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv4-static-el7 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6dhcp.snap.txt index eefa7a29ae1..faa259b4e0e 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6dhcp.snap.txt @@ -12,7 +12,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv6-dhcp-el7 --noipv6 --bootproto dhcp --mtu=1342 --nameserver=2001:db8:42::8,2001:db8:42::4 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv6-dhcp-el7 --noipv6 --mtu=1342 --bootproto dhcp --nameserver=2001:db8:42::8,2001:db8:42::4 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -66,10 +66,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv6-dhcp-el7 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6static.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6static.snap.txt index 670ce754d3f..b5f1e22d587 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6static.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.host6static.snap.txt @@ -12,7 +12,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv6-static-el7 --noipv6 --bootproto static --ip=2001:db8:42::42 --netmask=ffff:ffff:ffff:: --gateway=2001:db8:42::1 --mtu=1442 --nameserver=2001:db8:42::8,2001:db8:42::4 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv6-static-el7 --noipv6 --mtu=1442 --bootproto static --ip=2001:db8:42::42 --netmask=ffff:ffff:ffff:: --gateway=2001:db8:42::1 --nameserver=2001:db8:42::8,2001:db8:42::4 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -66,10 +66,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv6-static-el7 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rhel9_dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rhel9_dhcp.snap.txt index afcc93123d0..44bcd444c51 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rhel9_dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rhel9_dhcp.snap.txt @@ -11,7 +11,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-rhel9 --noipv6 --bootproto dhcp --mtu=1142 --nameserver=192.168.42.2,192.168.42.3 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-rhel9 --noipv6 --mtu=1142 --bootproto dhcp --nameserver=192.168.42.2,192.168.42.3 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -65,10 +65,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv4-dhcp-rhel9 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky8_dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky8_dhcp.snap.txt index ef09090eae8..5fd2fdb986e 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky8_dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky8_dhcp.snap.txt @@ -11,7 +11,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-rocky8 --noipv6 --bootproto dhcp --mtu=1142 --nameserver=192.168.42.2,192.168.42.3 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-rocky8 --noipv6 --mtu=1142 --bootproto dhcp --nameserver=192.168.42.2,192.168.42.3 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -64,10 +64,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv4-dhcp-rocky8 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky9_dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky9_dhcp.snap.txt index 12d06a466e3..365c0d597c3 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky9_dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/provision/Kickstart_default.rocky9_dhcp.snap.txt @@ -11,7 +11,7 @@ lang en_US.UTF-8 selinux --enforcing keyboard us -network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-rocky9 --noipv6 --bootproto dhcp --mtu=1142 --nameserver=192.168.42.2,192.168.42.3 +network --device=00-f0-54-1a-7e-e0 --hostname snapshot-ipv4-dhcp-rocky9 --noipv6 --mtu=1142 --bootproto dhcp --nameserver=192.168.42.2,192.168.42.3 rootpw --iscrypted $1$rtd8Ub7R$5Ohzuy8WXlkaK9cA2T1wb0 firewall --service=ssh @@ -63,10 +63,6 @@ chvt 3 ( logger "Starting anaconda snapshot-ipv4-dhcp-rocky9 postinstall" - - - - echo "Updating system time" systemctl enable --now chronyd /usr/bin/chronyc -a makestep diff --git a/test/unit/foreman/templates/snippets/kickstart_network_interface_test.rb b/test/unit/foreman/templates/snippets/kickstart_network_interface_test.rb new file mode 100644 index 00000000000..a6fb328622e --- /dev/null +++ b/test/unit/foreman/templates/snippets/kickstart_network_interface_test.rb @@ -0,0 +1,354 @@ +require 'test_helper' + +class KickstartNetworkInterfaceTest < ActiveSupport::TestCase + def renderer + @renderer ||= Foreman::Renderer::SafeModeRenderer + end + + def render_template(iface, host:, use_slaac:, static:, static6:) + @snippet ||= File.read(Rails.root.join('app', 'views', 'unattended', 'provisioning_templates', 'snippet', 'kickstart_network_interface.erb')) + + source = OpenStruct.new( + name: 'Test', + content: @snippet + ) + + scope = Class.new(Foreman::Renderer::Scope::Provisioning).send( + :new, + host: host, + source: source, + variables: { + iface: iface, + host: host, + use_slaac: use_slaac, + static: static, + static6: static6, + }) + + renderer.render(source, scope) + end + + setup do + os = FactoryBot.create(:for_snapshots_rhel9, :with_provision, :with_associations) + + @host = FactoryBot.create(:host, :managed, :build => true, :operatingsystem => os, + :interfaces => [ + FactoryBot.build(:nic_managed, :primary => true), + FactoryBot.build(:nic_managed, :provision => true), + ]) + end + + describe '#network' do + test 'should return a network line for an interface' do + actual = render_template( + @host.managed_interfaces.first, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_match(/network/, actual) + end + + test 'should skip non-managed interfaces' do + iface = FactoryBot.build(:nic_base, primary: true, managed: false) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_empty actual + end + + test 'should create bond interface' do + iface = FactoryBot.build( + :nic_bond, + primary: true, + identifier: 'test_bond', + attached_devices: ['bonded_slave1', 'bonded_slave2'], + mode: 'test_mode', + bond_options: 'option_a=foo option_b=bar' + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(bondslaves_match = /--bondslaves=([^ ]*)/.match(actual)) + assert_match(/bonded_slave1/, bondslaves_match[1]) + assert_match(/bonded_slave2/, bondslaves_match[1]) + assert_not_nil(bondopts_match = /--bondopts=([^ ]*)/.match(actual)) + assert_match(/mode=test_mode,/, bondopts_match[1]) + assert_match(/,option_a=foo,option_b=bar/, bondopts_match[1]) + end + + test 'should create bridge interface' do + iface = FactoryBot.build( + :nic_bridge, + primary: true, + identifier: 'test_bridge', + attached_devices: ['bridged_slave1', 'bridged_slave2'], + attrs: {bridge: true} + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(bridgeslaves_match = /--bridgeslaves=([^ ]*)/.match(actual)) + assert_match(/bridged_slave1/, bridgeslaves_match[1]) + assert_match(/bridged_slave2/, bridgeslaves_match[1]) + end + + test 'should set correct noipv6 flag' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet: FactoryBot.build(:subnet_ipv4) + ) + + iface.subnet6 = nil + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_match(/--noipv6/, actual) + end + + test 'should set correct noipv4 flag' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet6: FactoryBot.build(:subnet_ipv6) + ) + + iface.subnet = nil + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_match(/--noipv4/, actual) + end + + test 'should use static ipv4 configuration' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet: FactoryBot.build(:subnet_ipv4_static_for_snapshots) + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: true, + static6: false + ) + + assert_match(/--ip/, actual) + assert_match(/--netmask/, actual) + assert_match(/--gateway/, actual) + assert_not_nil(bootproto_match = /--bootproto ([^ ]*)/.match(actual)) + assert_match(/static/, bootproto_match[1]) + end + + test 'should use dhcp ipv4 configuration' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet: FactoryBot.build(:subnet_ipv4_with_domains) + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(bootproto_match = /--bootproto ([^ ]*)/.match(actual)) + assert_match(/dhcp/, bootproto_match[1]) + end + + test 'should use static ipv6 configuration' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet6: FactoryBot.build(:subnet_ipv6_static_for_snapshots), + ip6: '2001:db8:42::2' + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: true + ) + + assert_not_nil(ipv6_match = %r{--ipv6=([^/]*)/([^ ]*)}.match(actual)) + assert_match(iface.ip6, ipv6_match[1]) + assert_match(iface.subnet6.cidr.to_s, ipv6_match[2]) + assert_not_nil(gateway_match = /--ipv6gateway=([^ ]*)/.match(actual)) + assert_match(iface.subnet6.gateway, gateway_match[1]) + end + + test 'should use dhcp ipv6 configuration' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet6: FactoryBot.build(:subnet_ipv6_with_domains) + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(ipv6_match = /--ipv6 ([^ ]*)/.match(actual)) + assert_match(/dhcp/, ipv6_match[1]) + end + + test 'should use auto ipv6 configuration' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet6: FactoryBot.build(:subnet_ipv6_dhcp_for_snapshots) + ) + + actual = render_template( + iface, + host: @host, + use_slaac: true, + static: false, + static6: false + ) + + assert_not_nil(ipv6_match = /--ipv6 ([^ ]*)/.match(actual)) + assert_match(/auto/, ipv6_match[1]) + end + + test 'should set vlan options' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + virtual: true, + tag: '333', + attached_to: 'test_iface1' + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(vlan_match = /--vlanid=([^ ]*)/.match(actual)) + assert_match(/333/, vlan_match[1]) + assert_not_nil(interfacename_match = /--interfacename=([^ ]*)/.match(actual)) + assert_match(/vlan333/, interfacename_match[1]) + end + + test 'should set DNS servers' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet: FactoryBot.build(:subnet_ipv4_static_for_snapshots) + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(nameserver_match = /--nameserver=([^ ]*)/.match(actual)) + # order is not promised for nameserver list + assert_match(/192.168.42.2/, nameserver_match[1]) + assert_match(/192.168.42.3/, nameserver_match[1]) + end + + test 'should set nodns flag' do + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet: FactoryBot.build(:subnet_ipv4) + ) + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_match(/--nodns/, actual) + end + + test 'should set search domain' do + os = FactoryBot.create( + :for_snapshots_rhel9, + :with_provision, + :with_associations, + name: 'RHEL', + major: '10', + minor: '0', + type: 'Redhat', + title: 'Red Hat Enterprise Linux 10.0' + ) + + @host.operatingsystem = os + + iface = FactoryBot.build( + :nic_managed, + primary: true, + subnet: FactoryBot.build(:subnet_ipv4) + ) + + iface.domain = FactoryBot.build(:domain, name: 'test.com') + + actual = render_template( + iface, + host: @host, + use_slaac: false, + static: false, + static6: false + ) + + assert_not_nil(dns_search_match = /--ipv4-dns-search=([^ ]*)/.match(actual)) + assert_match(/test.com/, dns_search_match[1]) + end + end +end