* Lots of work done to add software RAID support to kickstart. Still in progress, however.

* Work on anvil-manage-install-target to enable/disable dhcpd. Also to only refresh the RPM repo periodically. Fixed a bug where it always reported that the kickstart files were not updated, even when they were.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 6 years ago
parent 8468215831
commit 9cf70f847c
  1. 255
      html/skins/alteeve/pxe.txt
  2. 5
      notes
  3. 238
      scripts/plan_partitions
  4. 8
      share/words.xml
  5. 96
      tools/anvil-manage-install-target

@ -44,7 +44,7 @@ eula --agreed
firstboot --disable firstboot --disable
# Reboot when the install is done. # Reboot when the install is done.
reboot #reboot
# Install from the source Striker # Install from the source Striker
url --url=#!variable!url!# url --url=#!variable!url!#
@ -128,7 +128,7 @@ rsync -av /var/log /mnt/sysimage/root/install_logs/var/
# 1 - Target type not specified. # 1 - Target type not specified.
# 2 - Failed to find a drive to install on. # 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 same, bare # NOTE: This is restricted to what is available during an anaconda install session. That is to say, bare
# minimum. # minimum.
# TODO: If multiple matching drives are found (same medium and size, build an appropriate RAID array. # 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 # TODO: in pre, wipefs on all disks to clear old LVM and DRBD data
@ -140,32 +140,42 @@ use warnings;
# Set to '1' for verbose output # Set to '1' for verbose output
my $debug = #!variable!debug!#; my $debug = #!variable!debug!#;
### NOTE: This must be set to 'striker', 'node' or 'dr' when incorporated into a kickstart %pre script! ### NOTE: This must be set to 'striker', 'node' or 'dr'! Wither set '$type' or use the appropriate argument.
my $type = "#!variable!type!#"; 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) if ($type =~ /striker/i)
{ {
print "#!string!message_0103!#\n"; print "-=] Finding install drive(s) for a Striker dashboard.\n";
$type = "striker"; $type = "striker";
} }
elsif ($type =~ /node/i) elsif ($type =~ /node/i)
{ {
print "#!string!message_0104!#\n"; print "-=] Finding install drive(s) for an Anvil! node.\n";
$type = "node"; $type = "node";
} }
elsif ($type =~ /dr/i) elsif ($type =~ /dr/i)
{ {
print "#!string!message_0105!#\n"; print "-=] Finding install drive(s) for a DR (disaster recovery) host.\n";
$type = "dr"; $type = "dr";
} }
else else
{ {
print "#!string!message_0106!#\n"; print "
[ Error ] - Target type not specified!
Usage: ".$0." {striker,node,dr}
";
exit(1); exit(1);
} }
my $device = {}; my $device = {};
# We might want to add HCTL (Host:Channel:Target:Lun for SCSI) and/or SUBSYSTEMS later # We might want to add HCTL (Host:Channel:Target:Lun for SCSI) and/or SUBSYSTEMS later
my $drives = {};
my $target = ""; my $target = "";
my $lsblk = system_call("/bin/lsblk --bytes --paths --pairs --output NAME,RM,HOTPLUG,TYPE,SIZE,TRAN,ROTA"); my $lsblk = system_call("/bin/lsblk --bytes --paths --pairs --output NAME,RM,HOTPLUG,TYPE,SIZE,TRAN,ROTA");
foreach my $line (split/\n/, $lsblk) foreach my $line (split/\n/, $lsblk)
@ -186,15 +196,31 @@ foreach my $line (split/\n/, $lsblk)
transport => $transport, transport => $transport,
rotational => $rotational, rotational => $rotational,
}; };
my $hr_size = hr_size($device->{$path}{size});
$device->{$path}{hr_size} = $hr_size;
if ($device->{$path}{rotational}) if ($device->{$path}{rotational})
{ {
print "#!string!message_0107!#\n"; if (not $device->{$path}{transport})
{
print "Analyzing platter or virtual drive: [".$path."] of the size: [".$device->{$path}{size}." (".$device->{$path}{hr_size}.")]\n";
} }
else else
{ {
print "#!string!message_0108!#\n"; 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 ### Usage selection priority
# on Striker, we'll simply use whatever is the biggest avalable drive. # on Striker, we'll simply use whatever is the biggest avalable drive.
@ -215,7 +241,7 @@ if ($type eq "striker")
} }
if ($use_drive) if ($use_drive)
{ {
print "#!string!message_0109!#\n"; print "Selected the largest disk: [".$use_drive."], which has a capacity of: [".hr_size($device->{$use_drive}{size})."]\n";
} }
} }
else else
@ -281,11 +307,11 @@ else
{ {
if ($selected_is_platter) if ($selected_is_platter)
{ {
print "#!string!message_0110!#\n"; print "Selected the smallest platter drive: [".$use_drive."], which has a capacity of: [".hr_size($device->{$use_drive}{size})."]\n";
} }
else else
{ {
print "#!string!message_0111!#\n"; 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";
} }
} }
} }
@ -293,7 +319,7 @@ else
# Did we find a disk to use? # Did we find a disk to use?
if (not $use_drive) if (not $use_drive)
{ {
print "#!string!message_0112!#\n"; print "[ Error ] - Failed to find any fixed drives (platter or USB, not removable) to install onto. Unable to proceed.\n";
exit(2); exit(2);
} }
@ -322,29 +348,194 @@ if ((-e "/sys/class/dmi/id/product_uuid") && (-r "/sys/class/dmi/id/product_uuid
} }
} }
# Finally, we've got our output. ### NOTE: RAID 0 is not RAID (literally or in this case). So '0' means 'no raid'
my $say_grow = $type eq "striker" ? "--grow " : ""; # 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 $root_size = 0;
my $vg_name = $type."_".$id; 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;
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]\n" if $debug;
# Build the partition file.
my $partition_file = "/tmp/plan_partitions.out"; my $partition_file = "/tmp/plan_partitions.out";
my $partition_body = "zerombr my $partition_body = "
clearpart --all --drives=".$use_drive." clearpart --all --drives=".$say_use_drive."
ignoredisk --only-use=".$use_drive." ignoredisk --only-use=".$say_use_drive."
bootloader --location=mbr --driveorder=".$use_drive." --boot-drive=".$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 # Partitions
part biosboot --fstype=biosboot --size=1 part biosboot --fstype=biosboot --size=2
part /boot --fstype=ext4 --size=1024 --asprimary --ondisk=".$use_drive." part pv.01 --fstype=lvmpv --size=100 --ondisk=".$use_drive." --grow
part pv.01 --fstype=lvmpv --size=100 --asprimary --ondisk=".$use_drive." --grow
# LVM Volume groups # LVM Volume groups
volgroup ".$vg_name." --pesize=4096 pv.01 volgroup ".$vg_name." --pesize=4096 pv.01
# LVM logical volumes # LVM logical volumes
logvol swap --fstype=swap --size=8188 --name=lv_swap --vgname=".$vg_name." logvol swap --fstype=swap --size=".$swap_size." --name=lv_swap --vgname=".$vg_name."
logvol / --fstype=xfs --size=40960 --name=lv_root --vgname=".$vg_name." ".$say_grow." logvol / --fstype=xfs --size=".$root_size." --name=lv_root --vgname=".$vg_name."
";
}
else
{
$partition_body .= "
# LVM PV
";
my $say_raid = "";
for (my $i = 0; $i < $count; $i++)
{
my $disk_number = $i + 1;
$partition_body .= "part raid.1".$disk_number." --size 100 --grow --ondisk=".$drives->{by_hr_size}{$hr_size}->[$i]."\n";
$say_raid .= "raid.1".$disk_number." ";
}
$partition_body .= "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=".$root_size." --name=lv_root --vgname=".$vg_name."
"; ";
print __LINE__."; [ Debug ] - partition_body: [".$partition_body."]\n" if $debug; }
print "#!string!message_0113!#\n";
=cut
# Wipe out the start of each disk and make it a GPT labelled disk.
# See: https://access.redhat.com/solutions/55652
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 $parted_out1 = system_call("/sbin/parted --script ".$path." mklabel gpt");
print __LINE__."; [ Debug ] - parted mklabel output
================================================================================
".$parted_out1."
================================================================================\n" if $debug;
my $parted_out2 = system_call("/sbin/parted --script ".$path." print free");
print __LINE__."; [ Debug ] - parted print output showing new layout.
================================================================================
".$parted_out2."
================================================================================\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("/sbin/udevadm settle");
system_call("/bin/sync");
=cut
# 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. # Write it to the temp file that the kickstart's %include will look for.
my $shell_call = $partition_file; my $shell_call = $partition_file;
@ -352,7 +543,7 @@ 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"; open (my $file_handle, ">", $shell_call) or die "Failed to write: [".$shell_call."], the error was: ".$!."\n";
print $file_handle $partition_body; print $file_handle $partition_body;
close $file_handle; close $file_handle;
print "#!string!message_0114!#\n"; print "Completed successfully, exiting.\n";
# We're done. # We're done.
exit(0); exit(0);
@ -489,7 +680,7 @@ TEXT HELP
ENDTEXT ENDTEXT
kernel fedora28/vmlinuz kernel fedora28/vmlinuz
# NOTE: add ' rd.debug' below for debugging # NOTE: add ' rd.debug' below for debugging
append initrd=fedora28/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.sshd append initrd=fedora28/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 label node
menu label #!string!message_0086!# menu label #!string!message_0086!#
@ -497,7 +688,7 @@ TEXT HELP
#!string!message_0087!# #!string!message_0087!#
ENDTEXT ENDTEXT
kernel fedora28/vmlinuz kernel fedora28/vmlinuz
append initrd=fedora28/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.sshd append initrd=fedora28/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 label node
menu label #!string!message_0088!# menu label #!string!message_0088!#
@ -505,7 +696,7 @@ TEXT HELP
#!string!message_0089!# #!string!message_0089!#
ENDTEXT ENDTEXT
kernel fedora28/vmlinuz kernel fedora28/vmlinuz
append initrd=fedora28/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.sshd append initrd=fedora28/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 label rescue
menu label #!string!message_0090!# menu label #!string!message_0090!#
@ -513,7 +704,7 @@ TEXT HELP
#!string!message_0091!# #!string!message_0091!#
ENDTEXT ENDTEXT
kernel fedora28/vmlinuz kernel fedora28/vmlinuz
append initrd=fedora28initrd.img ip=dhcp root=live:#!variable!base_url!#/os/LiveOS/squashfs.img rescue append initrd=fedora28/initrd.img ip=dhcp root=live:#!variable!base_url!#/os/LiveOS/squashfs.img rescue inst.repo=#!variable!base_url!#/os/ ip=dhcp inst.sshd
label fedora label fedora
menu label #!string!message_0092!# menu label #!string!message_0092!#
@ -521,7 +712,7 @@ TEXT HELP
#!string!message_0093!# #!string!message_0093!#
ENDTEXT ENDTEXT
kernel fedora28/vmlinuz kernel fedora28/vmlinuz
append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.repo=#!variable!base_url!#/os/ ip=dhcp inst.gpt inst.sshd
label next label next
menu default menu default

@ -56,6 +56,9 @@ firewall-cmd --zone=BCN1 --change-interface=bcn1_bond1
# Set the IFN as the default zone (as that is what will most likely be edited by a user) # Set the IFN as the default zone (as that is what will most likely be edited by a user)
firewall-cmd --set-default-zone=IFN1 firewall-cmd --set-default-zone=IFN1
# Allow tftpd
firewall-cmd --zone=BCN1 --add-service=tftp --permanent
# Allow routing/masq'ing through the IFN1 (provide net access to the BCN) # Allow routing/masq'ing through the IFN1 (provide net access to the BCN)
firewall-cmd --zone=IFN1 --add-masquerade firewall-cmd --zone=IFN1 --add-masquerade
@ -76,6 +79,8 @@ firewall-cmd --permanent --add-service=postgresql
firewall-cmd --reload firewall-cmd --reload
firewall-cmd --state [running (rc: 0),not running (rc:252)] firewall-cmd --state [running (rc: 0),not running (rc:252)]
firewall-cmd --zone=BCN1 --add-service=tftp
Ports we care about Ports we care about

@ -8,7 +8,7 @@
# 1 - Target type not specified. # 1 - Target type not specified.
# 2 - Failed to find a drive to install on. # 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 same, bare # NOTE: This is restricted to what is available during an anaconda install session. That is to say, bare
# minimum. # minimum.
# TODO: If multiple matching drives are found (same medium and size, build an appropriate RAID array. # 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 # TODO: in pre, wipefs on all disks to clear old LVM and DRBD data
@ -20,29 +20,34 @@ use warnings;
# Set to '1' for verbose output # Set to '1' for verbose output
my $debug = 0; my $debug = 0;
### NOTE: This must be set to 'striker', 'node' or 'dr' when incorporated into a kickstart %pre script! ### NOTE: This must be set to 'striker', 'node' or 'dr'! Wither set '$type' or use the appropriate argument.
my $type = ""; my $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) if ($type =~ /striker/i)
{ {
print "Finding install drive for a Striker dashboard.\n"; print "-=] Finding install drive(s) for a Striker dashboard.\n";
$type = "striker"; $type = "striker";
} }
elsif ($type =~ /node/i) elsif ($type =~ /node/i)
{ {
print "Finding install drive for an Anvil! node.\n"; print "-=] Finding install drive(s) for an Anvil! node.\n";
$type = "node"; $type = "node";
} }
elsif ($type =~ /dr/i) elsif ($type =~ /dr/i)
{ {
print "Finding install drive for a DR (disaster recovery) host.\n"; print "-=] Finding install drive(s) for a DR (disaster recovery) host.\n";
$type = "dr"; $type = "dr";
} }
else else
{ {
print " print "
Error: Target type not specified. [ Error ] - Target type not specified!
Be sure that '\$type' is set to 'striker', 'node' or 'dr' in the \%pre section
of the kickstart script Usage: ".$0." {striker,node,dr}
"; ";
exit(1); exit(1);
} }
@ -50,6 +55,7 @@ of the kickstart script
my $device = {}; my $device = {};
# We might want to add HCTL (Host:Channel:Target:Lun for SCSI) and/or SUBSYSTEMS later # We might want to add HCTL (Host:Channel:Target:Lun for SCSI) and/or SUBSYSTEMS later
my $drives = {};
my $target = ""; my $target = "";
my $lsblk = system_call("/bin/lsblk --bytes --paths --pairs --output NAME,RM,HOTPLUG,TYPE,SIZE,TRAN,ROTA"); my $lsblk = system_call("/bin/lsblk --bytes --paths --pairs --output NAME,RM,HOTPLUG,TYPE,SIZE,TRAN,ROTA");
foreach my $line (split/\n/, $lsblk) foreach my $line (split/\n/, $lsblk)
@ -70,14 +76,30 @@ foreach my $line (split/\n/, $lsblk)
transport => $transport, transport => $transport,
rotational => $rotational, rotational => $rotational,
}; };
my $hr_size = hr_size($device->{$path}{size});
$device->{$path}{hr_size} = $hr_size;
if ($device->{$path}{rotational}) if ($device->{$path}{rotational})
{ {
print "Analyzing platter drive: [".$path."], using the transport: [".$device->{$path}{transport}."], of the size: [".$device->{$path}{size}." (".hr_size($device->{$path}{size}).")]\n"; 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 else
{ {
print "Analyzing solid-state drive: [".$path."], using the transport: [".$device->{$path}{transport}."], of the size: [".$device->{$path}{size}." (".hr_size($device->{$path}{size}).")]\n"; 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 ### Usage selection priority
@ -206,28 +228,202 @@ if ((-e "/sys/class/dmi/id/product_uuid") && (-r "/sys/class/dmi/id/product_uuid
} }
} }
# Finally, we've got our output. ### NOTE: RAID 0 is not RAID (literally or in this case). So '0' means 'no raid'
my $say_grow = $type eq "striker" ? "--grow " : ""; # 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 $root_size = 0;
my $vg_name = $type."_".$id; 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;
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]\n" if $debug;
# Build the partition file.
my $partition_file = "/tmp/plan_partitions.out"; my $partition_file = "/tmp/plan_partitions.out";
my $partition_body = "zerombr my $partition_body = "zerombr
clearpart --all --drives=".$use_drive." clearpart --all --drives=".$say_use_drive."
ignoredisk --only-use=".$use_drive." ignoredisk --only-use=".$say_use_drive."
bootloader --location=mbr --driveorder=".$use_drive." --boot-drive=".$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 # Partitions
part biosboot --fstype=biosboot --size=1 part biosboot --fstype=biosboot --size=2
part /boot --fstype=ext4 --size=1024 --asprimary --ondisk=".$use_drive." part pv.01 --fstype=lvmpv --size=100 --ondisk=".$use_drive." --grow
part pv.01 --fstype=lvmpv --size=100 --asprimary --ondisk=".$use_drive." --grow
# LVM Volume groups # LVM Volume groups
volgroup ".$vg_name." --pesize=4096 pv.01 volgroup ".$vg_name." --pesize=4096 pv.01
# LVM logical volumes # LVM logical volumes
logvol swap --fstype=swap --size=8188 --name=lv_swap --vgname=".$vg_name." logvol swap --fstype=swap --size=".$swap_size." --name=lv_swap --vgname=".$vg_name."
logvol / --fstype=xfs --size=40960 --name=lv_root --vgname=".$vg_name." ".$say_grow." logvol / --fstype=xfs --size=".$root_size." --name=lv_root --vgname=".$vg_name."
"; ";
print __LINE__."; [ Debug ] - partition_body: [".$partition_body."]\n" if $debug; }
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
";
my $say_raid = "";
for (my $i = 0; $i < $count; $i++)
{
my $disk_number = $i + 1;
$partition_body .= "part raid.1".$disk_number." --size 100 --grow --ondisk=".$drives->{by_hr_size}{$hr_size}->[$i]."\n";
$say_raid .= "raid.1".$disk_number." ";
}
$partition_body .= "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=".$root_size." --name=lv_root --vgname=".$vg_name."
";
}
=cut
# Wipe out the start of each disk and make it a GPT labelled disk.
# See: https://access.redhat.com/solutions/55652
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 $parted_out1 = system_call("/sbin/parted --script ".$path." mklabel gpt");
print __LINE__."; [ Debug ] - parted mklabel output
================================================================================
".$parted_out1."
================================================================================\n" if $debug;
my $parted_out2 = system_call("/sbin/parted --script ".$path." print free");
print __LINE__."; [ Debug ] - parted print output showing new layout.
================================================================================
".$parted_out2."
================================================================================\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("/sbin/udevadm settle");
system_call("/bin/sync");
=cut
# Write out the file.
print __LINE__."; [ Debug ] - partition_body:
================================================================================
".$partition_body."
================================================================================\n";
print "Writing out the partition plan to: [".$partition_file."]\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. # Write it to the temp file that the kickstart's %include will look for.

@ -222,7 +222,10 @@ NOTE: Please be patient!
About to try to download aproximately: [#!variable!packages!#] packages needed to: About to try to download aproximately: [#!variable!packages!#] packages needed to:
- [#!variable!directory!#]. - [#!variable!directory!#].
</key> </key>
<key name="message_0121"></key> <key name="message_0121">Successfully enabled the Install Target function.</key>
<key name="message_0122">Successfully disabled the Install Target function.</key>
<key name="message_0123">The Install Target function is enabled.</key>
<key name="message_0124">The Install Target function is disabled.</key>
<!-- Log entries --> <!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key> <key name="log_0001">Starting: [#!variable!program!#].</key>
@ -498,6 +501,7 @@ The body of the file: [#!variable!file!#] does not match the new body. The file
<key name="log_0233">There was a problem updating file: [#!variable!file!#], expected the write to return '0' but got: [#!variable!return!#]. Please check the logs for details.</key> <key name="log_0233">There was a problem updating file: [#!variable!file!#], expected the write to return '0' but got: [#!variable!return!#]. Please check the logs for details.</key>
<key name="log_0234">Failed to backup the file: [#!variable!source!#] to: [#!variable!destination!#]. Details may be found in the logs above.</key> <key name="log_0234">Failed to backup the file: [#!variable!source!#] to: [#!variable!destination!#]. Details may be found in the logs above.</key>
<key name="log_0235">Not updating the local repository on this run. Use '#!variable!program!# --refresh' to force a refresh of the local repository.</key> <key name="log_0235">Not updating the local repository on this run. Use '#!variable!program!# --refresh' to force a refresh of the local repository.</key>
<key name="log_0236">Skipping the RPM repository refresh. The next scheduled refresh will be done in: [#!variable!next_refresh!#] second(s). Use '#!variable!program!# --refresh' to force an immediate refresh.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>
@ -727,6 +731,8 @@ The update appears to have not completed successfully. The output was:
==== ====
</key> </key>
<key name="error_0046">This Striker system is not configured yet. This tool will not be available until it is.</key> <key name="error_0046">This Striker system is not configured yet. This tool will not be available until it is.</key>
<key name="error_0047">Failed to start the Install Target feature. Got a non-zero return code when starting: [#!data!sys::daemon::dhcpd!#] (got: [#!variable!rc!#]).</key>
<key name="error_0048">Failed to stop the Install Target feature. Got a non-zero return code when starting: [#!data!sys::daemon::dhcpd!#] (got: [#!variable!rc!#]).</key>
<!-- These are units, words and so on used when displaying information. --> <!-- These are units, words and so on used when displaying information. -->
<key name="unit_0001">Yes</key> <key name="unit_0001">Yes</key>

@ -23,6 +23,8 @@
# 7 = A package failed to download to our repo # 7 = A package failed to download to our repo
# 8 = No database connection available. # 8 = No database connection available.
# 9 = The system isn't configured yet. # 9 = The system isn't configured yet.
# 10 = Failed to start dhcpd.
# 11 = Failed to stop dhcpd.
# #
# TODO: # TODO:
# - Support building the install target by mounting the ISO and checking /mnt/shared/incoming for needed # - Support building the install target by mounting the ISO and checking /mnt/shared/incoming for needed
@ -63,6 +65,45 @@ if (($< != 0) && ($> != 0))
$anvil->nice_exit({code => 5}); $anvil->nice_exit({code => 5});
} }
# If the user just wants a status, check and exit.
if ($anvil->data->{switches}{status})
{
my $dhcpd_running = $anvil->System->check_daemon({daemon => $anvil->data->{sys}{daemon}{dhcpd}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dhcpd_running => $dhcpd_running }});
if ($dhcpd_running)
{
print $anvil->Words->string({key => "message_0123"})."\n";
$anvil->nice_exit({code => 0});
}
else
{
print $anvil->Words->string({key => "message_0124"})."\n";
$anvil->nice_exit({code => 0});
}
}
### NOTE: Enabling is handled at the end, after checks/updates are applied
# If we're being asked to disable, just do so and exit.
if ($anvil->data->{switches}{disable})
{
my $return_code = $anvil->System->stop_daemon({daemon => $anvil->data->{sys}{daemon}{dhcpd}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { return_code => $return_code }});
if ($return_code)
{
# non-0 means something went wrong.
print $anvil->Words->string({key => "error_0047", variables => { rc => $return_code }})."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, key => "error_0048", variables => { rc => $return_code }});
$anvil->nice_exit({exit_code => 8});
}
else
{
# Success!
print $anvil->Words->string({key => "message_0122"})."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, key => "message_0122"});
$anvil->nice_exit({code => 0});
}
}
# Connect to the database(s). # Connect to the database(s).
$anvil->Database->connect; $anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
@ -112,6 +153,26 @@ setup_boot_environment($anvil);
# Store the RPMs in repo directory. # Store the RPMs in repo directory.
update_install_source($anvil); update_install_source($anvil);
# If we've been asked to enable or disable the install target, do so now.
if ($anvil->data->{switches}{enable})
{
my $return_code = $anvil->System->start_daemon({daemon => $anvil->data->{sys}{daemon}{dhcpd}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { return_code => $return_code }});
if ($return_code)
{
# non-0 means something went wrong.
print $anvil->Words->string({key => "error_0047", variables => { rc => $return_code }})."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, key => "error_0047", variables => { rc => $return_code }});
$anvil->nice_exit({exit_code => 8});
}
else
{
# Success!
print $anvil->Words->string({key => "message_0121"})."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, key => "message_0121"});
}
}
# We're done # We're done
$anvil->nice_exit({exit_code => 0}); $anvil->nice_exit({exit_code => 0});
@ -156,6 +217,15 @@ sub check_refresh
return(0); return(0);
} }
# If the user has asked to enable or disable, skip.
$anvil->data->{switches}{enable} = "" if not defined $anvil->data->{switches}{enable};
$anvil->data->{switches}{disable} = "" if not defined $anvil->data->{switches}{disable};
if (($anvil->data->{switches}{enable}) or ($anvil->data->{switches}{disable}))
{
# Skip.
return(0);
}
# See when the last time we refreshed the local package cache # See when the last time we refreshed the local package cache
my ($unixtime, $variable_uuid, $modified_date) = $anvil->Database->read_variable({ my ($unixtime, $variable_uuid, $modified_date) = $anvil->Database->read_variable({
variable_name => "install-target::refreshed", variable_name => "install-target::refreshed",
@ -168,6 +238,12 @@ sub check_refresh
modified_date => $modified_date, modified_date => $modified_date,
}}); }});
if (($unixtime eq "") or ($unixtime =~ /\D/))
{
$unixtime = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { unixtime => $unixtime }});
}
### TODO: Allow the user to set a "refresh time" that will wait until the local time is after a ### TODO: Allow the user to set a "refresh time" that will wait until the local time is after a
### certain time before refreshing. Also, allow the user to disable auto-refresh entirely. ### certain time before refreshing. Also, allow the user to disable auto-refresh entirely.
# If the database variable 'install-target::refresh' is not set, or is more than 24 hours old, # If the database variable 'install-target::refresh' is not set, or is more than 24 hours old,
@ -186,16 +262,23 @@ sub check_refresh
} }
my $time_now = time; my $time_now = time;
my $next_scan = $unixtime + $anvil->data->{'install-manifest'}{'refresh-period'}; my $next_scan = $unixtime + $anvil->data->{'install-manifest'}{'refresh-period'};
my $difference = ($next_scan - $time_now);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:time_now' => $time_now, 's1:time_now' => $time_now,
's2:next_scan' => $next_scan, 's2:next_scan' => $next_scan,
's3:difference' => $difference,
}}); }});
if ((not $variable_uuid) or ($unixtime =~ /^\d+/) or ($time_now > $next_scan)) if ((not $variable_uuid) or ($unixtime =~ /^\d+/) or ($difference < 0))
{ {
$anvil->data->{switches}{refresh} = 1; $anvil->data->{switches}{refresh} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::refresh" => $anvil->data->{switches}{refresh} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::refresh" => $anvil->data->{switches}{refresh} }});
return(0); return(0);
} }
elsif ($difference > 0)
{
# Log when the next scan will happen
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0236", variables => { next_refresh => $anvil->Convert->add_commas({number => $difference}) }});
}
# If the refresh fails, we'll update the last unixtime to be 24 hours from now, so that we can try # If the refresh fails, we'll update the last unixtime to be 24 hours from now, so that we can try
# again. We intentionally ignore 'install-manifest::refresh-period', that's only used when the # again. We intentionally ignore 'install-manifest::refresh-period', that's only used when the
@ -355,7 +438,7 @@ sub setup_boot_environment
### Generate kickstart files. ### Generate kickstart files.
foreach my $type ("striker", "node", "dr") foreach my $type ("striker", "node", "dr")
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { type => $type }});
my $say_type = "#!string!message_0115!#"; my $say_type = "#!string!message_0115!#";
if ($type eq "node") if ($type eq "node")
{ {
@ -377,7 +460,7 @@ sub setup_boot_environment
password => $anvil->data->{kickstart}{password} ? $anvil->data->{kickstart}{password} : $anvil->data->{defaults}{kickstart}{password}, password => $anvil->data->{kickstart}{password} ? $anvil->data->{kickstart}{password} : $anvil->data->{defaults}{kickstart}{password},
debug => 0, # This isn't the same as the rest of our code, just '1' or '0'. debug => 0, # This isn't the same as the rest of our code, just '1' or '0'.
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { kickstart_body => $kickstart_body }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { kickstart_body => $kickstart_body }});
# Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means # Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means
# something went wrong. # something went wrong.
@ -386,14 +469,14 @@ sub setup_boot_environment
body => $kickstart_body, body => $kickstart_body,
file => $kickstart_file, file => $kickstart_file,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { kickstart_success => $kickstart_success }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { kickstart_success => $kickstart_success }});
if (not $kickstart_success) if (not $kickstart_success)
{ {
# Failed. # Failed.
print $anvil->Words->string({key => "error_0043", variables => { file => $kickstart_file }})."\n"; print $anvil->Words->string({key => "error_0043", variables => { file => $kickstart_file }})."\n";
$anvil->nice_exit({code => 3}); $anvil->nice_exit({code => 3});
} }
elsif ($dhcpd_conf_success eq "1") elsif ($kickstart_success eq "1")
{ {
# Updated # Updated
$anvil->Storage->change_mode({target => $kickstart_file, mode => "0664"}); $anvil->Storage->change_mode({target => $kickstart_file, mode => "0664"});
@ -401,7 +484,7 @@ sub setup_boot_environment
print $anvil->Words->string({key => "message_0097", variables => { file => $kickstart_file }})."\n"; print $anvil->Words->string({key => "message_0097", variables => { file => $kickstart_file }})."\n";
} }
elsif ($dhcpd_conf_success eq "2") elsif ($kickstart_success eq "2")
{ {
# Update not needed. # Update not needed.
print $anvil->Words->string({key => "message_0096", variables => { file => $kickstart_file }})."\n"; print $anvil->Words->string({key => "message_0096", variables => { file => $kickstart_file }})."\n";
@ -808,6 +891,7 @@ sub update_install_source
# Update the refresh time to now. # Update the refresh time to now.
$anvil->Database->insert_or_update_variables({ $anvil->Database->insert_or_update_variables({
debug => 2,
variable_name => "install-target::refreshed", variable_name => "install-target::refreshed",
variable_value => time, variable_value => time,
variable_default => "", variable_default => "",

Loading…
Cancel
Save