Skip to content

Commit

Permalink
Switch to 'network' directive instead of ifcfg
Browse files Browse the repository at this point in the history
Use kickstart's `network` directive to create interfaces according to
foreman's DB instead of using ifcfg files.
  • Loading branch information
ShimShtein committed Dec 19, 2023
1 parent 70b2144 commit f342ccf
Show file tree
Hide file tree
Showing 6 changed files with 475 additions and 96 deletions.
1 change: 1 addition & 0 deletions app/services/foreman/renderer/scope/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Base
include Foreman::Renderer::Scope::Macros::TemplateLogging
include Foreman::Renderer::Scope::Macros::SnippetRendering
include Foreman::Renderer::Scope::Macros::Transpilers
include Foreman::Renderer::Scope::Macros::Kickstart

def self.inherited(child_class)
loaders.each { |loader| child_class.register_loader(loader) }
Expand Down
118 changes: 118 additions & 0 deletions app/services/foreman/renderer/scope/macros/kickstart.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
module Foreman
module Renderer
module Scope
module Macros
module Kickstart
include Foreman::Renderer::Errors
extend ApipieDSL::Module

apipie :class, desc: 'Macros to use within a kickstart template' do
name 'Kickstart'
sections only: %w[all]
end

# For more information about KS network directive: https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#network
apipie :method, "Returns a kickstart 'network' directive for a specific interface" do
required :iface, Nic::Managed, "Managed interface object to represent"
keyword :rhel_compatible, [true, false], "Is the OS rhel-compatible?"
keyword :os_major, Integer, "OS major version number"
keyword :use_slaac, [true, false], "Is the interface configured using SLAAC"
keyword :static, [true, false], "Use static configuration for IPv4"
keyword :static6, [true, false], "Use static configuration for IPv6"
returns String, "'network' directive with all switches that represent the input NIC object"
example 'kickstart_network(iface, rhel_compatible: true, os_major: 10, use_slaac: false) #=> "network --bootproto=dhcp --device=ens3"'
end
def kickstart_network(iface, host:, rhel_compatible:, os_major:, use_slaac:, static:, static6:)
return nil unless iface.is_a?(Nic::Managed)

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

# mtu method is taking into account both ipv4 and ipv6 stacks
network_options.push("--mtu=#{iface.mtu}") if iface.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&.dhcp_boot_mode?
network_options.push("--bootproto dhcp")
end
if subnet4
nameservers.concat(subnet4.dns_servers)
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&.dhcp_boot_mode?
if use_slaac
network_options.push("--ipv6 auto")
else
network_options.push("--ipv6 dhcp")
end
end
if subnet6
nameservers.concat(subnet6.dns_servers)
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
network_options.push("--vlanid=#{iface.tag}")
network_options.push("--interfacename=vlan#{iface.tag}") if os_major > 6
end
end

# DNS
if !nameservers.empty?
network_options.push("--nameserver=#{nameservers.join(',')}")
else
network_options.push("--nodns")
end

# Search domain - available from Fedora 39 (RHEL 10)
if iface.domain && 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(' ')}"
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,85 +111,20 @@ 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 interfaces and the bonds at the end
@host.managed_interfaces.sort_by { |iface| iface.bond? 0 : iface.provision? 20 : 10 }.each |iface| do
%>
<%= kickstart_network(
iface,
host: @host,
rhel_compatible: rhel_compatible,
os_major: os_major,
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') -%>
Expand Down Expand Up @@ -297,8 +232,6 @@ exec < /dev/tty3 > /dev/tty3
chvt 3
(
logger "Starting anaconda <%= @host %> postinstall"
<%= snippet 'kickstart_networking_setup' %>
<%= snippet 'ntp' %>
<%= snippet 'yum_proxy' %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 -%>
<%
rhel_compatible = @host.operatingsystem.family == 'Redhat' && @host.operatingsystem.name != 'Fedora'
os_major = @host.operatingsystem.major.to_i
# start with provisioning interface, then other non-bond interfaces and the bonds at the end
@host.managed_interfaces.sort_by { |iface| iface.bond? 0 : iface.provision? 20 : 10 }.each |iface| do
%>
<%= kickstart_network(
iface,
host: @host,
rhel_compatible: rhel_compatible,
os_major: os_major,
use_slaac: false,
static: @static,
static6: @static6) %>
<%
end
-%>


rootpw --iscrypted <%= root_pass %>
<% if host_param_true?('disable-firewall') -%>
Expand All @@ -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' -%>
Expand Down
Loading

0 comments on commit f342ccf

Please sign in to comment.