<!-- start dhcpd_conf -->
### Global options
option domain-name "#!variable!domain!#";
option domain-name-servers #!variable!dns!#;
authoritative;
ddns-update-style none;

# refer to RFC4758 for possible arch option values
option arch code 93 = unsigned integer 16;

subnet #!variable!network!# netmask 255.255.0.0 {
	default-lease-time 600;
	max-lease-time     1200;
	range              #!variable!range!#;
	option routers     #!variable!router!#;
	next-server        #!variable!router!#;

	class "pxeclients" {
		match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
		next-server 192.168.0.1;
		if option pxe-system-type = 00:07 {
			filename "BOOTX64.efi";
		} else {
			filename "pxelinux.0";
		}
	}
}
<!-- end dhcpd_conf -->


<!-- start kickstart -->
### Alteeve's Niche! Inc. - Anvil! Intelligent Availability(tm) Platform
# License: GPLv2
# Target:  Network Install (PXE)
# OS:      #!variable!os!#
# Machine: #!variable!say_type!#
# NOTE:    Do no use any non-ASCII characters in this kickstart script.

# Install using text screens, most compatible with modest hardware, too.
text

### TODO: Might want to remove this for Striker.
# Agree to the eula
eula --agreed

# Don't run the "firstboot" tool, we should have everything configured.
firstboot --disable

# Reboot when the install is done.
reboot

# Install from the source Striker
url --url=#!variable!url!#

### TODO: These should be configurable eventually.
# Keyboard layouts
keyboard #!variable!keyboard!#
timezone #!variable!timezone!#

# NOTE: DON'T CHANGE THIS WITHOUT TESTING! The Anvil! code makes system calls and parses output. Changing
#       this could alter what the program can parse. The language of the Anvil! itself (and this most things
#       users will see) is controlled in /etc/anvil/anvil.conf
lang en_CA.UTF-8

# Network information
network --hostname=#!variable!host_name!#
# Root and admin passwords are '#!variable!password!#'
rootpw --plaintext #!variable!password!#
user --name=admin --password "#!variable!password!#" --plaintext --gecos "admin" --groups wheel

# TEMPORARY: Set selinux to permissive
selinux --permissive

# Partitioning plan is generated by the %pre script. 
%include /tmp/plan_partitions.out

%packages
@^minimal-environment
alteeve-el8-repo
#createrepo
#dhcp-server
#firefox
gcc
#gdm
#gnome-terminal
#httpd
#nmap
#perl-CGI
perl-Data-Dumper
#perl-interpreter
perl-NetAddr-IP
#postgresql-server
rsync
#syslinux
#syslinux-nonlinux
#tftp-server
vim
#virt-manager

%end

%addon com_redhat_kdump --disable --reserve-mb='auto'

%end

%anaconda
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
%end



#############################################################################################################
# %post, --nochroot scripts                                                                                 #
#############################################################################################################

### TODO: Might want to remove this pre-production. Useful for debugging until then.
# Record all the install logs for future reference. 
%post --nochroot
echo 'Copying all the anaconda related log files to /root/install/'

mkdir -p /mnt/sysimage/root/install_logs/var
mkdir -p /mnt/sysimage/root/install_logs/run
rsync -av /tmp         /mnt/sysimage/root/install_logs/
rsync -av /run/install /mnt/sysimage/root/install_logs/run/
rsync -av /var/log     /mnt/sysimage/root/install_logs/var/

echo 'Writing out the initial /etc/issue script.'
reset
cat << EOF > /mnt/sysimage/usr/sbin/anvil-update-issue
#!/usr/bin/perl
# 
# This parses the current IP addresses on the local system and writes them to /etc/issue so that they're seen
# by a user at the login prompt. This is meant to be useful during the initialization and setup stages, so 
# it's expected to run before the Anvil::Tools module is installed. As such, it doesn't use those modules.
# 

use strict;
use warnings;
use IO::Handle;

# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
\$| = 1;

my \$THIS_FILE           =  (\$0 =~ /^.*\/(.*)\$/)[0];
my \$running_directory   =  (\$0 =~ /^(.*?)\/\$THIS_FILE\$/)[0];
if ((\$running_directory =~ /^\./) && (\$ENV{PWD}))
{
        \$running_directory =~ s/^\./\$ENV{PWD}/;
}

my \$shell_call = "/usr/sbin/ip addr list";
my \$new_issue  = '\S
Kernel \r on an \m
';
my \$ips        = {};
my \$interface  = "";
open (my \$file_handle, \$shell_call." 2>&1 |") or die "Failed to call: [".\$shell_call."], error was: [".\$!."]\n";
while(<\$file_handle>)
{
        chomp;
        my \$line =  \$_;
           \$line =~ s/\n\$//;
           \$line =~ s/\r\$//;
        if (\$line =~ /^\d+: (.*?): </)
        {
                \$interface = \$1;
        }
        next if not \$interface;
        next if \$interface eq "lo";
        if (\$line =~ / inet (\d+\.\d+\.\d+\.\d+\/\d+) /)
        {
                my \$ip = \$1; 
                \$ips->{\$interface} = \$ip;
        }
}
close \$file_handle;

if (keys %{\$ips})
{
        \$new_issue .= "\nActive IPs:\n";
        foreach my \$interface (sort {\$a cmp \$b} keys %{\$ips})
        {
                \$new_issue .= "- ".\$interface.": ".\$ips->{\$interface}."\n";
        }
        \$new_issue .= "\n";
}

# Read in the current issue file and see if there is any difference.
my \$old_issue  = "";
my \$issue_file = "/etc/issue";
open (\$file_handle, "<", \$issue_file) or die "Failed to read: [".\$issue_file."], error was: [".\$!."]\n";
while(<\$file_handle>)
{
        ### NOTE: Don't chop this, we want to record exactly what we read
        \$old_issue .= \$_;
}
close \$file_handle;

my \$update = \$new_issue eq \$old_issue ? 0 : 1;
if (\$update)
{
        open (my \$file_handle, ">", \$issue_file) or die "Failed to write: [".\$issue_file."], the error was: [".\$!."]\n";
        print \$file_handle \$new_issue;
        close \$file_handle;
}

exit(0);
EOF
chmod 755 /mnt/sysimage/usr/sbin/anvil-update-issue

# Add this to crontab.
cat << EOF > /mnt/sysimage/var/spool/cron/root
MAILTO=""
* * * * * /usr/sbin/anvil-update-issue >> /var/log/anvil.cron 2>&1 
EOF
chown 0:0 /mnt/sysimage/var/spool/cron/root
chmod 0600 /mnt/sysimage/var/spool/cron/root

echo 'Writing out the initial /etc/issue script.'
reset
cat << EOF > /mnt/sysimage/etc/NetworkManager/dispatcher.d/ifup-local
#!/usr/bin/bash
# 
# Make sure that we update /etc/issue as soon as the network is up, before the
# logic in shown to the user after boot.

/usr/sbin/anvil-update-issue
EOF
chmod +x /mnt/sysimage/etc/NetworkManager/dispatcher.d/ifup-local

cat << EOF > /mnt/sysimage/#!variable!repo_file!#
#!variable!repo_body!#
EOF
%end


#############################################################################################################
# %pre scripts                                                                                              #
#############################################################################################################

### This is the small anaconda-friendly perl program that looks at the 
### available storage and chooses a drive to install on. Then it generates 
### the kickstart partition instructions and records them in:
### /tmp/plan_partitions.out 

### NOTE: This must be copied from 'scripts/plan_partitions', don't edit directly *EXCEPT* to set the '$type'
###       just below, which *MUST* be set, or the script will fail.
# - #!variable!debug!#
# - #!variable!type!#
%pre --interpreter /bin/perl
#!/bin/perl
# 
# This script is designed to identify hard drives and decide where and how to partition it for installation 
# during a kickstart install. 
# 
# Exit codes;
# 0 - Success
# 1 - Target type not specified.
# 2 - Failed to find a drive to install on.
# 
# NOTE: This is restricted to what is available during an anaconda install session. That is to say, bare 
#       minimum.
# TODO: If multiple matching drives are found (same medium and size, build an appropriate RAID array.
# TODO: in pre, wipefs on all disks to clear old LVM and DRBD data
# 

use strict;
use warnings;

# Set to '1' for verbose output 
my $debug = #!variable!debug!#;

### NOTE: This must be set to 'striker', 'node' or 'dr'! Wither set '$type' or use the appropriate argument.
my $type  = "#!variable!type!#";
if ((defined $ARGV[0]) && ((lc($ARGV[0]) eq "striker") or (lc($ARGV[0]) eq "node") or (lc($ARGV[0]) eq "dr")))
{
	$type  = $ARGV[0];
}
if ($type =~ /striker/i)
{
	print "-=] Finding install drive(s) for a Striker dashboard.\n";
	$type = "striker";
}
elsif ($type =~ /node/i)
{
	print "-=] Finding install drive(s) for an Anvil! node.\n";
	$type = "node";
}
elsif ($type =~ /dr/i)
{
	print "-=] Finding install drive(s) for a DR (disaster recovery) host.\n";
	$type = "dr";
}
else
{
	print "
[ Error ] - Target type not specified! 

Usage: ".$0." {striker,node,dr}

";
	exit(1);
}

my $device = {};

# We might want to add HCTL (Host:Channel:Target:Lun for SCSI) and/or SUBSYSTEMS later
my $drives = {};
my $target = "";
my $lsblk  = system_call("/bin/lsblk --bytes --paths --pairs --output NAME,RM,HOTPLUG,TYPE,SIZE,TRAN,ROTA");
foreach my $line (split/\n/, $lsblk)
{
	### NOTE: If a drive has no transport, is not removable, but is hotplugable and the device path is 
	###       mmcblk0, it is probably an SDCard. It doesn't seem to be a directly divinable state. We 
	###       don't currently plan to use them, but it might come to pass later.
	print __LINE__."; [ Debug ] - lsblk: [".$line."]\n" if $debug;
	my ($path, $removable, $hotplug, $type, $size, $transport, $rotational) = ($line =~ /NAME="(.*?)" RM="(\d)" HOTPLUG="(\d)" TYPE="(.*?)" SIZE="(\d+)" TRAN="(.*?)" ROTA="(\d)"/);
	print __LINE__."; [ Debug ] - Device: [".$path."], type: [".$type."], remvoable? [".$removable."], hotplug? [".$hotplug."], rotational? [".$rotational."], transport: [".$transport."], size: [".$size."]\n" if $debug;
	# Skip 'zramX' devices
	next if ($path =~ /^\/dev\/zram\d/);
	# Skip removable disks and anything that just isn't a disk at all.
	next if (($removable) or ($hotplug) or ($type ne "disk"));
	$device->{$path} = {
		type       => $type,
		size       => $size, 
		transport  => $transport, 
		rotational => $rotational, 
	};
	my $hr_size = hr_size($device->{$path}{size});
	$device->{$path}{hr_size} = $hr_size;
	
	if ($device->{$path}{rotational})
	{
		if (not $device->{$path}{transport})
		{
			print "Analyzing platter or virtual drive: [".$path."] of the size: [".$device->{$path}{size}." (".$device->{$path}{hr_size}.")]\n";
		}
		else
		{
			print "Analyzing platter drive: [".$path."], using the transport: [".$device->{$path}{transport}."], of the size: [".$device->{$path}{size}." (".$device->{$path}{hr_size}.")]\n";
		}
	}
	else
	{
		print "Analyzing solid-state drive: [".$path."], using the transport: [".$device->{$path}{transport}."], of the size: [".$device->{$path}{size}." (".$device->{$path}{hr_size}.")]\n";
	}

	if (not exists $drives->{by_hr_size}{$hr_size})
	{
		$drives->{by_hr_size}{$hr_size} = [];
	}
	push  @{$drives->{by_hr_size}{$hr_size}}, $path;
}

### Usage selection priority
# on Striker, we'll simply use whatever is the biggest avalable drive.
# on Node and DR, we'll prefer slowest first (rotational, sata before nvme/scsi), and smallest second. 
my $use_drive = "";
if ($type eq "striker")
{
	my $biggest_size = 0;
	foreach my $path (sort {$a cmp $b} keys %{$device})
	{
		print __LINE__."; [ Debug ] - path: [".$path."], ${path}::size: [".$device->{$path}{size}." (".hr_size($device->{$path}{size}).")] < biggest_size: [".$biggest_size." (".hr_size($biggest_size).")]\n" if $debug;
		if ($device->{$path}{size} > $biggest_size)
		{
			$biggest_size = $device->{$path}{size}; 
			$use_drive    = $path;
			print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], biggest_size: [".$biggest_size." (".hr_size($biggest_size).")]\n" if $debug;
		}
	}
	if ($use_drive)
	{
		print "Selected the largest disk: [".$use_drive."], which has a capacity of: [".hr_size($device->{$use_drive}{size})."]\n";
	}
}
else
{
	# Node and DR are handled the same
	my $first_disk_seen     = 0;
	my $smallest_size       = 0;
	my $selected_is_platter = 0;
	foreach my $path (sort {$a cmp $b} keys %{$device})
	{
		print __LINE__."; [ Debug ] - first_disk_seen: [".$first_disk_seen."], path: [".$path."], ${path}::rotational: [".$device->{$path}{rotational}."]\n" if $debug;
		if (not $first_disk_seen)
		{
			# Select this one
			$first_disk_seen     = 1;
			$use_drive           = $path;
			$smallest_size       = $device->{$path}{size};
			$selected_is_platter = $device->{$path}{rotational};
			print __LINE__."; [ Debug ] - first_disk_seen: [".$first_disk_seen."], use_drive: [".$use_drive."], selected_is_platter: [".$selected_is_platter."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug;
		}
		elsif ($device->{$path}{rotational})
		{
			# This takes priority 
			print __LINE__."; [ Debug ] - selected_is_platter: [".$selected_is_platter."]\n" if $debug;
			if ($selected_is_platter)
			{
				# Was the previously seen drive bigger?
				print __LINE__."; [ Debug ] - ".$path."::size: [".$first_disk_seen." (".hr_size($first_disk_seen).")], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug;
				if ($device->{$path}{size} < $smallest_size)
				{
					# This is smaller, use it.
					$use_drive     = $path;
					$smallest_size = $device->{$path}{size};
					print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug;
				}
			}
			else
			{
				# The previous drive is an SSD, so use this one regardless
				$use_drive           = $path;
				$smallest_size       = $device->{$path}{size};
				$selected_is_platter = $device->{$path}{rotational};
				print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], selected_is_platter: [".$selected_is_platter."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug;
			}
		}
		elsif (not $selected_is_platter)
		{
			# This is an SSD, but we haven't seen a platter drive yet, so use it if it is 
			# smaller.
			print __LINE__."; [ Debug ] - ".$path."::size: [".$first_disk_seen." (".hr_size($first_disk_seen).")], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug;
			if ($device->{$path}{size} < $smallest_size)
			{
				# This is smaller, use it.
				$use_drive     = $path;
				$smallest_size = $device->{$path}{size};
				print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug;
			}
		}
	}
	
	# Did we find a drive?
	if ($use_drive)
	{
		if ($selected_is_platter)
		{
			print "Selected the smallest platter drive: [".$use_drive."], which has a capacity of: [".hr_size($device->{$use_drive}{size})."]\n";
		}
		else
		{
			print "Selected the smallest solid-state drive: [".$use_drive."], which has a capacity of: [".hr_size($device->{$use_drive}{size})."] (no platter drives found)\n";
		}
	}
}

# Did we find a disk to use?
if (not $use_drive)
{
	print "[ Error ] - Failed to find any fixed drives (platter or USB, not removable) to install onto. Unable to proceed.\n";
	exit(2);
}

# Pick up a bit of a UUID to add to the volume group name.
my $id = time;
if ((-e "/sys/class/dmi/id/product_uuid") && (-r "/sys/class/dmi/id/product_uuid"))
{
	# We should be able to read the system UUID. If so, we'll take the starting part of the string for 
	# the short ID.
	my $uuid       = "";
	my $shell_call = "/sys/class/dmi/id/product_uuid";
	print __LINE__."; [ Debug ] - shell_call: [".$shell_call."]\n" if $debug;
	open (my $file_handle, "<", $shell_call) or die "Failed to read: [".$shell_call."], the error was: ".$!."\n";
	while(<$file_handle>)
	{
		chomp;
		$uuid = $_;
		print __LINE__."; [ Debug ] - uuid: [".$uuid."]\n" if $debug;
	}
	close $file_handle;
	
	if ($uuid =~ /^(\w+)-/)
	{
		$id = $1;
		print __LINE__."; [ Debug ] - id: [".$id."]\n" if $debug;
	}
}

### NOTE: RAID 0 is not RAID (literally or in this case). So '0' means 'no raid'
# If I have 2+ drives of the same size as 'use_drive', I will create a RAID array.
my $raid_level = 0;
my $hr_size    = $device->{$use_drive}{hr_size};
my $count      = @{$drives->{by_hr_size}{$hr_size}};
print __LINE__."; [ Debug ] - Drives of size: [".$hr_size."]: [".$count."].\n" if $debug;
if ($count == 0)
{
	$raid_level = 0;
}
elsif ($count == 2)
{
	$raid_level = 1;
}
elsif ($count == 4)
{
	$raid_level = 10;
}
elsif (($count == 3) or ($count == 5))
{
	$raid_level = 5;
}
elsif ($count > 5)
{
	$raid_level = 6;
}

my $say_use_drive = $use_drive;
if (not $raid_level)
{
	print "Building a standard partition layout for: [".$use_drive."] which is: [".$hr_size."]\n";
}
else
{
	print "Building a software RAID level: [".$raid_level."] array using the: [".$count."x] [".$hr_size."] drives;\n";
	$say_use_drive = "";
	foreach my $path (sort {$a cmp $b} @{$drives->{by_hr_size}{$hr_size}})
	{
		print "- ".$path."\n";
		$say_use_drive .= $path.",";
	}
	$say_use_drive =~ s/,$//;
}

### NOTE: kickstart sizes are in MiB
# Prepare some variables
my $swap_size = 8192;
my $boot_size = 1024;
my $root_size = 0;
my $vg_name   = $type."_".$id;

# If this machine has a small size, we'll cut back the swap and root sizes.
my $per_disk_space  = sprintf("%.2f", ($device->{$use_drive}{size} /= (2 ** 20)));
my $available_space = $per_disk_space;
print __LINE__."; [ Debug ] - per_disk_space: [".$per_disk_space." (".hr_size($per_disk_space * (2**20)).")], available_space: [".$available_space." (".hr_size($available_space * (2**20)).")]\n" if $debug;
if ($raid_level == 10)
{
	# Total == 2 x single disk
	$available_space *= 2;
	print __LINE__."; [ Debug ] - available_space: [".$available_space."]\n" if $debug;
}
elsif ($raid_level == 5)
{
	# Total == count x Disks - 1
	$available_space = ($per_disk_space * $count) - $per_disk_space;
	print __LINE__."; [ Debug ] - available_space: [".$available_space."]\n" if $debug;
}
elsif ($raid_level == 6)
{
	# Total == count x Disks - 2
	$available_space = ($per_disk_space * $count) - ($per_disk_space * 2);
	print __LINE__."; [ Debug ] - available_space: [".$available_space."]\n" if $debug;
}

# Now, how much space is available after taking some for BIOSBOOT and /boot ?
$available_space -= 2;
print __LINE__."; [ Debug ] - available_space: [".$available_space." (".hr_size($available_space * (2**20)).")]\n" if $debug;
if ($available_space < 40960)
{
	# Not enough space for the standard layout. 
	$swap_size = 4096;
	print __LINE__."; [ Debug ] - swap_size: [".$swap_size."]\n" if $debug;
}

# The left over space is for '/' (we'll shorten this up to 40GiB for nodes and DR hosts next)
$root_size = $available_space - $swap_size - $boot_size;
print __LINE__."; [ Debug ] - root_size: [".$root_size."]\n" if $debug;

print __LINE__."; [ Debug ] - type: [".$type."], root_size: [".$root_size."]\n" if $debug;
if (($type ne "striker") && ($root_size > 40960))
{
	$root_size = 40960;
	print __LINE__."; [ Debug ] - root_size: [".$root_size."]\n" if $debug;
}

# Round down to an event integer.
$root_size =~ s/\.\d+$//;
print __LINE__."; Assigning: [".hr_size($swap_size * (2**20))." (".$swap_size." MiB)], root_size: [".hr_size($root_size * (2**20))." (".$root_size.") MiB], boot_size: [".hr_size($boot_size * (2**20))." (".$boot_size.") MiB]\n" if $debug;

# Build the partition file.
my $partition_file = "/tmp/plan_partitions.out";
my $partition_body = "zerombr
clearpart --all --drives=".$say_use_drive."
ignoredisk --only-use=".$say_use_drive."
bootloader --location=mbr --driveorder=".$say_use_drive." --boot-drive=".$use_drive;

if (not $raid_level)
{
	# Finally, we've got our output.
	$partition_body .= "
# Partitions
part biosboot --fstype=biosboot --size=2
part /boot    --fstype=ext4     --size=1024 --ondisk=".$use_drive."
part pv.01    --fstype=lvmpv    --size=100  --ondisk=".$use_drive." --grow

# LVM Volume groups
volgroup ".$vg_name." --pesize=4096 pv.01

# LVM logical volumes
logvol swap --fstype=swap --size=".$swap_size."  --name=lv_swap --vgname=".$vg_name."
logvol /    --fstype=xfs  --size=100 --grow --maxsize=".$root_size." --name=lv_root --vgname=".$vg_name."
";
}
else
{
	$partition_body .= "
# biosboot
";

	for (my $i = 0; $i < $count; $i++)
	{
		$partition_body .= "part biosboot --fstype=biosboot --size=2 --ondisk=".$drives->{by_hr_size}{$hr_size}->[$i]."\n";
	}

	$partition_body .= "
# LVM PV
";
	### TODO: Test the /boot part.
	my $say_raid_boot = "";
	my $say_raid      = "";
	for (my $i = 0; $i < $count; $i++)
	{
		my $disk_number    =  $i + 1;
		   $partition_body .= "part raid.0".$disk_number." --size 1025        --ondisk=".$drives->{by_hr_size}{$hr_size}->[$i]."\n";
		   $partition_body .= "part raid.1".$disk_number." --size 100  --grow --ondisk=".$drives->{by_hr_size}{$hr_size}->[$i]."\n";
		   $say_raid_boot  .= "raid.0".$disk_number." ";
		   $say_raid       .= "raid.1".$disk_number." ";
	}
	$partition_body .= "
raid /boot --fstype=ext4 --device=boot  --level=RAID1 ".$say_raid_boot."
raid pv.01 --fstype=xfs  --device=pv.01 --level=RAID".$raid_level." ".$say_raid."

# LVM Volume groups
volgroup ".$vg_name." pv.01

# LVM logical volumes
logvol swap --fstype=swap --size=".$swap_size." --name=lv_swap --vgname=".$vg_name."
logvol /    --fstype=xfs  --size=100 --grow --maxsize=".$root_size." --name=lv_root --vgname=".$vg_name."
";
}


# Wipe out the start of each disk so that the install doesn't puke if it sees, for example, an mdadm 
# signature on the sole disk being used as an install target.
foreach my $path (split/,/, $say_use_drive)
{
	print "[ NOTE ] - Wiping the boot sector of: [".$path."] and configuring it for a GPT label.\n";
	my $dd_out      = system_call("/bin/dd bs=5120 count=1 if=/dev/zero of=".$path." oflag=dsync");
	print __LINE__."; [ Debug ] - dd output:
================================================================================
".$dd_out."
================================================================================\n" if $debug;
	my $partprobe_out = system_call("/sbin/partprobe --summary ".$path);
	print __LINE__."; [ Debug ] - partprobe summary.
================================================================================
".$partprobe_out."
================================================================================\n" if $debug;
	my $partx_out = system_call("/sbin/partx --update --verbose ".$path);
	print __LINE__."; [ Debug ] - parted print output showing new layout.
================================================================================
".$partx_out."
================================================================================\n" if $debug;
}
# Flush things out. The article says to blindly sleep 30, but it says to do so to make sure udev, partx and 
# others have updated. We're forcing the issue, which should be faster and safer.
system_call("/bin/sync");
system_call("/sbin/udevadm settle");


# Write out the file.
print __LINE__."; [ Debug ] - partition_body:
================================================================================
".$partition_body."
================================================================================\n";
print "Writing out the partition plan to: [".$partition_file."]\n";

# Write it to the temp file that the kickstart's %include will look for.
my $shell_call = $partition_file;
print __LINE__."; [ Debug ] - shell_call: [".$shell_call."]\n" if $debug;
open (my $file_handle, ">", $shell_call) or die "Failed to write: [".$shell_call."], the error was: ".$!."\n";
print $file_handle $partition_body;
close $file_handle;
print "Completed successfully, exiting.\n";

# We're done.
exit(0);


### Functions
# Make the size easier to read for users
sub hr_size
{
	my ($size) = @_;
	
	my $hr_size = $size;
	
	if ($size < 1023)
	{
		# Bytes
		$hr_size .= " B";
	}
	elsif ($size < (2 ** 20))
	{
		# Kibibyte
		$hr_size = sprintf("%.1f", ($size /= (2 ** 10)))." KiB";
	}
	elsif ($size < (2 ** 30))
	{
		# Mebibyte
		$hr_size = sprintf("%.2f", ($size /= (2 ** 20)))." MiB";
	}
	elsif ($size < (2 ** 40))
	{
		# Gibibyte
		$hr_size = sprintf("%.2f", ($size /= (2 ** 30)))." GiB";
	}
	elsif ($size < (2 ** 50))
	{
		# Tebibyte
		$hr_size = sprintf("%.2f", ($size /= (2 ** 40)))." TiB";
	}
	else
	{
		# Pebibyte or higher
		$hr_size = sprintf("%.3f", ($size /= (2 ** 40)))." PiB";
	}
	
	return($hr_size);
}

sub system_call
{
	my ($command) = @_;
	my $output = "";
	open (my $file_handle, $command." 2>&1 |") or die "Failed to call: [".$command."], error was: [".$!."]\n";
	while (<$file_handle>)
	{
		chomp;
		my $line =  $_;
      		   $line =~ s/\n$//;
      		   $line =~ s/\r$//;
		$output .= $line."\n";
	}
	close $file_handle;
	$output =~ s/\n$//s;

	return($output);
}
%end
<!-- end kickstart -->


<!-- start tftp_bios -->
# Tradional BIOS based PXE menu
# 
# Notes:
# - Use the 'vmlinuz' and 'initrd.img' from the Netinstall ISO. The others don't find the NIC, so can't net 
#   install.
#
# Use the high-colour menu system.
default vesamenu.c32

# Time out and use the default menu option. Defined as tenths of a second.
TIMEOUT 6000

# Prompt the user. Set to '1' to automatically choose the default option. This
# is really meant for files matched to MAC addresses.
PROMPT 0

# Set the boot menu to be 1024x768 with a nice background image. Be careful to
# ensure that all your user's can see this resolution! Default is 640x480.
MENU RESOLUTION 1024 768

# The background image
MENU BACKGROUND splash.jpg

MENU TITLE #!string!message_0080!#

# Below, the hash (#) character is replaced with the countdown timer. The
# '{,s}' allows for pluralizing a word and is used when the value is >= '2'.
MENU AUTOBOOT #!string!message_0081!#
MENU TABMSG   #!string!message_0082!#
MENU NOTABMSG #!string!message_0083!#

# The following options set the various colours used in the menu. All possible
# options are specified except for F# help options. The colour is expressed as
# two hex characters between '00' and 'ff' for alpha, red, green and blue
# respectively (#AARRGGBB).
# Format is: MENU COLOR <Item> <ANSI Seq.> <foreground> <background> <shadow type>
MENU COLOR screen      0  #00000000 #00000000 std      # background colour not covered by the splash image
MENU COLOR border      0  #bbd02724 #ee000000 std      # The wire-frame border
MENU COLOR title       0  #fff2f2f2 #ee000000 std      # Menu title text
MENU COLOR sel	       0  #fff2f2f2 #ee000000 std      # Selected menu option
MENU COLOR hotsel      0  #fffff2f2 #ee000000 std      # The selected hotkey (set with ^ in MENU LABEL)
MENU COLOR unsel       0  #ffc2c2c2 #ee000000 std      # Unselected menu options
MENU COLOR hotkey      0  #ffffc2c2 #ee000000 std      # Unselected hotkeys (set with ^ in MENU LABEL)
MENU COLOR tabmsg      0  #c0a2a2a2 #00000000 std      # Tab text
MENU COLOR timeout_msg 0  #ffc2c2c2 #00000000 std      # Timout text
MENU COLOR timeout     0  #ffffc2c2 #00000000 std      # Timout counter
MENU COLOR disabled    0  #80515151 #ee000000 std      # Disabled menu options, including SEPARATORs
MENU COLOR cmdmark     0  #c0444444 #ee000000 std      # Command line marker - The '> ' on the left when editing an option
MENU COLOR cmdline     0  #c0f2f2f2 #ee000000 std      # Command line - The text being edited
# Options below haven't been tested, descriptions may be lacking.
MENU COLOR scrollbar   0  #407f7f7f #00000000 std      # Scroll bar
MENU COLOR pwdborder   0  #80d02724 #20ffffff std      # Password box wire-frame border
MENU COLOR pwdheader   0  #80f7f7f7 #20ffffff std      # Password box header
MENU COLOR pwdentry    0  #80f2f2f2 #20ffffff std      # Password entry field
MENU COLOR help	       0  #c0f2f2f2 #c0000000 std      # Help text, if set via 'TEXT HELP ... ENDTEXT'


### NOTE: inst.repo is the image of the install media, looks for .treeinfo

label striker
menu label #!string!message_0084!#
TEXT HELP
#!string!message_0085!#
ENDTEXT
kernel #!data!host_os::os_type!#/vmlinuz
# NOTE: add ' rd.debug' below for debugging
append initrd=#!data!host_os::os_type!#/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/striker.ks inst.gpt inst.sshd

label node
menu label #!string!message_0086!#
TEXT HELP
#!string!message_0087!#
ENDTEXT
kernel #!data!host_os::os_type!#/vmlinuz
append initrd=#!data!host_os::os_type!#/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/node.ks inst.gpt inst.sshd

label node
menu label #!string!message_0088!#
TEXT HELP
#!string!message_0089!#
ENDTEXT
kernel #!data!host_os::os_type!#/vmlinuz
append initrd=#!data!host_os::os_type!#/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/dr.ks inst.gpt inst.sshd

label rescue
menu label #!string!message_0090!#
TEXT HELP
#!string!message_0091!#
ENDTEXT
kernel #!data!host_os::os_type!#/vmlinuz
append initrd=#!data!host_os::os_type!#/initrd.img ip=dhcp root=live:#!variable!base_url!#/LiveOS/squashfs.img rescue inst.repo=#!variable!base_url!#/os/ ip=dhcp inst.sshd

label #!data!host_os::os_type!#
menu label #!string!message_0092!#
TEXT HELP
#!string!message_0093!#
ENDTEXT
kernel #!data!host_os::os_type!#/vmlinuz
append initrd=#!data!host_os::os_type!#/initrd.img root=live:#!variable!base_url!#/images/install.img inst.repo=#!variable!base_url!#/os/ ip=dhcp inst.gpt inst.sshd

label next
menu default
menu label #!string!message_0094!#
localboot 0xffff
<!-- end tftp_bios -->


<!-- start tftp_uefi -->
function load_video {
	insmod efi_gop
	insmod efi_uga
	insmod video_bochs
	insmod video_cirrus
	insmod all_video
}

load_video
set gfxpayload=keep
insmod gzio

menuentry 'Install RHEL 8'  --class rhel --class gnu-linux --class gnu --class os {
	linuxefi #!data!host_os::os_type!#/vmlinuz ip=dhcp inst.repo=#!variable!base_url!#/#!data!host_os::os_type!#/#!data!host_os::os_arch!#/iso/ 
	initrdefi #!data!host_os::os_type!#/initrd.img
}

menuentry 'Install Striker'  --class rhel --class gnu-linux --class gnu --class os {
	linuxefi #!data!host_os::os_type!#/vmlinuz ip=dhcp inst.repo=#!variable!base_url!#/#!data!host_os::os_type!#/#!data!host_os::os_arch!#/iso/ root=live:#!variable!base_url!#/#!data!host_os::os_type!#/#!data!host_os::os_arch!#/os/images/install.img inst.ks=#!variable!base_url!#/#!data!host_os::os_type!#/#!data!host_os::os_arch!#/kickstart/striker.ks inst.sshd rd.debug
	initrdefi #!data!host_os::os_type!#/initrd.img
}
<!-- end tftp_uefi -->
