* OK, this time CentOS is actually supported. For reals.

** Needed to add a couple more packages to CentOS's package list.
** Changed the PXE kickstart template to create a dedicated '/boot' partition (raw partition or on RAID 1). This seems to be required now on 8.1.
** Added PXE's UEFI support to the template system (untested, but it's at least generated now).
* Filtered out 'debug' and 'verbose' options when configuring fence devices.
* Added an internet test to tools/striker-manage-install-target and skipped attempting to download packages when there's no internet. Also made loading the host OS info into a small function.
* Started creating the man pages.

Signed-off-by: Madison Kelly <mkelly@alteeve.ca>
main
Madison Kelly 5 years ago
parent 27f921289f
commit 3838babf57
  1. 1
      Anvil/Tools.pm
  2. 2
      Anvil/Tools/Striker.pm
  3. 35
      cgi-bin/striker
  4. 9
      html/skins/alteeve/anvil.html
  5. 47
      html/skins/alteeve/pxe.txt
  6. 8
      man/anvil-daemon.8
  7. 0
      man/anvil-manage-keys.1
  8. 0
      man/anvil.conf.5
  9. 1
      pxe/tftpboot/pxelinux/uefi
  10. 75
      share/words.xml
  11. 417
      tools/striker-manage-install-target
  12. 14
      tools/striker-parse-oui

@ -1063,6 +1063,7 @@ sub _set_paths
'pg_hba.conf' => "/var/lib/pgsql/data/pg_hba.conf",
'postgresql.conf' => "/var/lib/pgsql/data/postgresql.conf",
pxe_default => "/var/lib/tftpboot/pxelinux.cfg/default",
pxe_uefi => "/var/lib/tftpboot/pxelinux.cfg/uefi",
ssh_config => "/etc/ssh/ssh_config",
'type.dashboard' => "/etc/anvil/type.dashboard",
'type.dr' => "/etc/anvil/type.dr",

@ -156,12 +156,14 @@ sub get_fence_data
{
# We ignore some parameters that are not useful parameters in our case.
my $name = $hash_ref->{name};
next if $name =~ /debug/;
next if $name eq "delay";
next if $name eq "help";
next if $name eq "version";
next if $name eq "separator";
next if $name eq "plug";
next if $name =~ /snmp(.*?)_path/;
next if $name eq "verbose";
my $unique = exists $hash_ref->{unique} ? $hash_ref->{unique} : 0;
my $required = exists $hash_ref->{required} ? $hash_ref->{required} : 0;

@ -1537,6 +1537,30 @@ sub handle_new_manifest
return(0);
}
# This sanity checks the fence agent forms.
sub sanity_check_fence_agent_form
{
my ($anvil, $fence_agent) = @_;
### TODO: Left off here. Make the confirm form a table;
### Any set options that matches the default gets set to 'subtle'.
=cut
Fence Agent: $fence_agent
Devices:
| option | default | 1 | 2 | ...
+--------+---------+-------+-------+-----
| foo | bar | bar | baz |
=cut
my $sane = 1;
my $confirn_form = "";
foreach my $i (1..$anvil->data->{cgi}{fence_count}{value})
{
}
return($sane);
}
# This allows the user to tell us about fence devices.
sub process_fences
{
@ -1557,7 +1581,14 @@ sub process_fences
{
my $fence_agent = $anvil->data->{cgi}{fence_agent}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { fence_agent => $fence_agent }});
# Are we saving?
if ($anvil->data->{cgi}{save}{value})
{
# Sanity check
my $sane = sanity_check_fence_agent_form($anvil, $fence_agent);
}
my $agent_description = format_fence_description($anvil, $fence_agent, $anvil->data->{fences}{$fence_agent}{description});
# Walk through the list of options
@ -1594,7 +1625,7 @@ sub process_fences
# Set the cgi variable to the default, if not already set.
$anvil->data->{cgi}{$option_key}{alert} = "" if not defined $anvil->data->{cgi}{$option_key}{alert};
$anvil->data->{cgi}{$option_key}{value} = $default if not defined $anvil->data->{cgi}{$option_key}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"cgi::".$option_key."::value" => $anvil->data->{cgi}{$option_key}{value},
}});

@ -107,9 +107,12 @@
#!variable!options!#
<tr>
<td colspan="3">
<input type="submit" name="save" id="save" class="button" value="#!string!striker_0067!#">
<input type="hidden" name="anvil" id="anvil" value="true">
<input type="hidden" name="task" id="task" value="fences">
<input type="submit" name="save" id="save" value="#!string!striker_0067!#" class="button">
<input type="hidden" name="fence_agent" id="fence_agent" value="#!data!cgi::fence_agent::value!#">
<input type="hidden" name="fence_count" id="fence_count" value="#!data!cgi::fence_count::value!#">
<input type="hidden" name="add" id="add" value="true">
<input type="hidden" name="anvil" id="anvil" value="true">
<input type="hidden" name="task" id="task" value="fences">
</td>
</tr>
</table>

@ -523,6 +523,7 @@ else
### 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;
@ -560,7 +561,7 @@ if ($available_space < 40960)
}
# 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;
$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;
@ -572,7 +573,7 @@ if (($type ne "striker") && ($root_size > 40960))
# 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;
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";
@ -587,7 +588,8 @@ if (not $raid_level)
$partition_body .= "
# Partitions
part biosboot --fstype=biosboot --size=2
part pv.01 --fstype=lvmpv --size=100 --ondisk=".$use_drive." --grow
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
@ -611,14 +613,20 @@ else
$partition_body .= "
# LVM PV
";
my $say_raid = "";
### 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.1".$disk_number." --size 100 --grow --ondisk=".$drives->{by_hr_size}{$hr_size}->[$i]."\n";
$say_raid .= "raid.1".$disk_number." ";
$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 pv.01 --fstype=xfs --device=pv.01 --level=RAID".$raid_level." ".$say_raid."
$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
@ -846,3 +854,28 @@ 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 -->

@ -0,0 +1,8 @@
.TH anvil-daemon
.SH NAME
anvil-daemon - Main systemd daemon for the Anvil! IA platform. Provides all job management, monitoring and Striker back-end functions.
.SH SYNOPSIS
.B anvil-daemon
.SH DESCRIPTION
.B anvil-daemon
Main daemon for the M3 Anvil! Intelligent Availability\*(Tm platform. Usually managed via

@ -10,7 +10,6 @@ load_video
set gfxpayload=keep
insmod gzio
menuentry 'Install RHEL 8' --class rhel --class gnu-linux --class gnu --class os {
linuxefi rhel8/vmlinuz ip=dhcp inst.repo=http://10.1.4.1/rhel8/x86_64/iso/
initrdefi rhel8/initrd.img

@ -131,7 +131,7 @@ NOTE: Please be patient!
<key name="message_0081">Will boot the next device as configured in your BIOS in # second{,s}.</key>
<key name="message_0082"><![CDATA[Press the <tab> key to edit the boot parameters of the highlighted option.]]></key>
<key name="message_0083">Editing of this option is disabled.</key>
<key name="message_0084">^1. Install a Striker dashboard (#!data!sys::pxe::os_name!# #!data!sys::pxe::os_arch!#)</key>
<key name="message_0084">^1. Install a Striker dashboard (#!data!host_os::os_name!# #!data!host_os::os_arch!#)</key>
<!-- Keep help text wrapped within the area shown directly below for the 'help' section of PXE 'default' meny entry. -->
<!-- /===========================================================================================================\ -->
<key name="message_0085">
@ -143,7 +143,7 @@ NOTE: Please be patient!
*** THERE WILL BE NO FURTHER PROMPT! PROCEED CAREFULLY! ***
</key>
<!-- \===========================================================================================================/ -->
<key name="message_0086">^2. Install an Anvil! Node (#!data!sys::pxe::os_name!# #!data!sys::pxe::os_arch!#)</key>
<key name="message_0086">^2. Install an Anvil! Node (#!data!host_os::os_name!# #!data!host_os::os_arch!#)</key>
<!-- Keep help text wrapped within the area shown directly below for the 'help' section of PXE 'default' meny entry. -->
<!-- /===========================================================================================================\ -->
<key name="message_0087">
@ -156,7 +156,7 @@ NOTE: Please be patient!
*** THERE WILL BE NO FURTHER PROMPT! PROCEED CAREFULLY! ***
</key>
<!-- \===========================================================================================================/ -->
<key name="message_0088">^3. Install an Anvil! Disaster Recover Host (#!data!sys::pxe::os_name!# #!data!sys::pxe::os_arch!#)</key>
<key name="message_0088">^3. Install an Anvil! Disaster Recover Host (#!data!host_os::os_name!# #!data!host_os::os_arch!#)</key>
<key name="message_0089">
<!-- Keep help text wrapped within the area shown directly below for the 'help' section of PXE 'default' meny entry. -->
<!-- /===========================================================================================================\ -->
@ -179,11 +179,11 @@ NOTE: Please be patient!
No data on the target machine will be changed by this option.
</key>
<!-- \===========================================================================================================/ -->
<key name="message_0092">Install ^Standard #!data!sys::pxe::os_name!# #!data!sys::pxe::os_arch!# Install</key>
<key name="message_0092">Install ^Standard #!data!host_os::os_name!# #!data!host_os::os_arch!# Install</key>
<!-- Keep help text wrapped within the area shown directly below for the 'help' section of PXE 'default' meny entry. -->
<!-- /===========================================================================================================\ -->
<key name="message_0093">
This will start a standard install of #!data!sys::pxe::os_name!#.
This will start a standard install of #!data!host_os::os_name!#.
This option will not change anything on disk until and unless you choose to do so.
</key>
@ -813,7 +813,7 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0472">The unified fences metadata file: [#!data!path::data::fences_unified_metadata!#] doesn't exist yet. It will be created now.</key>
<key name="log_0473">The unified fences metadata file: [#!data!path::data::fences_unified_metadata!#] will be refreshed on user request (--refresh passed).</key>
<key name="log_0474">The unified fences metadata file: [#!data!path::data::fences_unified_metadata!#] old and will now be refreshed.</key>
<key name="log_0475">This is a CentOS machine, moving the directory: [#!data!path::directories::html!#/rhel8] to: [#!data!path::directories::html!#/centos8].</key>
<key name="log_0475">This is a CentOS machine, moving the directory: [#!variable!source!#] to: [#!variable!target!#].</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>
@ -1239,36 +1239,37 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
<key name="ok_0004">Saved the alert recipient information successfully!</key>
<!-- Warnings -->
<key name="warning_0001">The IP address will change. You will need to reconnect after applying these changes.</key>
<key name="warning_0002">The access information appears to not be valid.</key>
<key name="warning_0003">Test access to the peer (using SSH) failed. There may be details in the log file.</key>
<key name="warning_0004">Accessing the peer over SSH worked, but a test connection to the database failed.</key>
<key name="warning_0005">There was a problem reading the peer's UUID. Read: [#!variable!uuid!#], which appears to be invalid.</key>
<key name="warning_0006">An SSH connection was established to: [#!variable!target!#], but we failed to establish a channel. The last error was: [#!variable!error!#].</key>
<key name="warning_0007">The job: [#!variable!command!#] was picked up by: [#!variable!pid!#], but that process is not running and it appears to only be: [#!variable!percent!# %] complete. Restarting the job.</key>
<key name="warning_0008">Unable to find a local IP on the same subnet as the IP/host: [#!variable!host!#] given for the target. Bi-directional setup not currently possible.</key>
<key name="warning_0009">The subtask request for manipulating the 'Install Target' feature is not valid. It should be 'enabled' or 'disabled'</key> <!-- NOTE: 'enabled' and 'disabled' are variable values, don't translate them. -->
<key name="warning_0010">The IP address: [#!variable!ip_address!#] is not a valid IPv4 address</key>
<key name="warning_0011">The SSH port is not a valid (usually it is 22, but it has to be between 1 ~ 65536)</key>
<key name="warning_0012">Failed to log into the host. Is the IP or root user's password right?</key>
<key name="warning_0013"><![CDATA[The target's host key has changed. If the target has been rebuilt, or the target IP reused, the old key will need to be removed. <a href="?striker=true&task=keys" target="_new">Click here</a> to resolve.]]></key>
<key name="warning_0014">The host UUID: [#!variable!host_uuid!#] was not found in the #!data!path::json::all_status!# file on the local dashboard.</key>
<key name="warning_0015">To configure a host as either an Anvil! node or a disaster recovery host, there must be at least 6 network interfaces. This machine only has: [#!variable!interface_count!#] interfaces.</key>
<key name="warning_0016">No databases are available. Changes to the network interfaces will be cached.</key>
<key name="warning_0017">The subnet mask is not valid</key>
<key name="warning_0018">The IP address was specified, but the subnet mask was not</key>
<key name="warning_0019">The passed in parameter '#!variable!parameter!#': [#!variable!ip_address!#] is not a valid IPv4 address.</key>
<key name="warning_0020">The passed in parameter '#!variable!parameter!#': [#!variable!subnet_mask!#] is not a valid IPv4 subnet mask.</key>
<key name="warning_0021">All three networks require the first network pair to be defined.</key>
<key name="warning_0022">Only one network interface selected for a network pair.</key>
<key name="warning_0023">The outgoing mail server appear to not be a valid domain name or IP address.</key>
<key name="warning_0024">The outgoing mail server port is not valid. Must be 'mail_server:x' where x is 1 ~ 65535.</key>
<key name="warning_0025">There was a problem saving the mail server data. Please check the logs for more information.</key>
<key name="warning_0026">The recipient's email address appears to not be valid.</key>
<key name="warning_0027">There was a problem saving the alert recipient data. Please check the logs for more information.</key>
<key name="warning_0028">Failed to read the fence agent: [#!variable!agent!#] metadata. Ignoring it.</key>
<key name="warning_0029">While resync'ing the table: [#!variable!table!#] on: [#!variable!host_name!# (#!variable!host_uuid!#)], there was an entry found in the public schema (#!variable!column!# = #!variable!uuid!#) but not in the history schema. This shouldn't happen, and it probably a bug. Switching the query's schema from public to history for the query: [#!variable!query!#] is being dropped.</key>
<key name="warning_0030">Databse->insert_or_update_variables() was called with 'update_value_only' set, but the 'variable_uuid' wasn't passed or the 'variable_uuid' wasn't found given the 'variable_name'. Unable to update. Passed in values are logged below this message</key>
<key name="warning_0001">[ Warning ] - The IP address will change. You will need to reconnect after applying these changes.</key>
<key name="warning_0002">[ Warning ] - The access information appears to not be valid.</key>
<key name="warning_0003">[ Warning ] - Test access to the peer (using SSH) failed. There may be details in the log file.</key>
<key name="warning_0004">[ Warning ] - Accessing the peer over SSH worked, but a test connection to the database failed.</key>
<key name="warning_0005">[ Warning ] - There was a problem reading the peer's UUID. Read: [#!variable!uuid!#], which appears to be invalid.</key>
<key name="warning_0006">[ Warning ] - An SSH connection was established to: [#!variable!target!#], but we failed to establish a channel. The last error was: [#!variable!error!#].</key>
<key name="warning_0007">[ Warning ] - The job: [#!variable!command!#] was picked up by: [#!variable!pid!#], but that process is not running and it appears to only be: [#!variable!percent!# %] complete. Restarting the job.</key>
<key name="warning_0008">[ Warning ] - Unable to find a local IP on the same subnet as the IP/host: [#!variable!host!#] given for the target. Bi-directional setup not currently possible.</key>
<key name="warning_0009">[ Warning ] - The subtask request for manipulating the 'Install Target' feature is not valid. It should be 'enabled' or 'disabled'</key> <!-- NOTE: 'enabled' and 'disabled' are variable values, don't translate them. -->
<key name="warning_0010">[ Warning ] - The IP address: [#!variable!ip_address!#] is not a valid IPv4 address</key>
<key name="warning_0011">[ Warning ] - The SSH port is not a valid (usually it is 22, but it has to be between 1 ~ 65536)</key>
<key name="warning_0012">[ Warning ] - Failed to log into the host. Is the IP or root user's password right?</key>
<key name="warning_0013"><![CDATA[[ Warning ] - The target's host key has changed. If the target has been rebuilt, or the target IP reused, the old key will need to be removed. <a href="?striker=true&task=keys" target="_new">Click here</a> to resolve.]]></key>
<key name="warning_0014">[ Warning ] - The host UUID: [#!variable!host_uuid!#] was not found in the #!data!path::json::all_status!# file on the local dashboard.</key>
<key name="warning_0015">[ Warning ] - To configure a host as either an Anvil! node or a disaster recovery host, there must be at least 6 network interfaces. This machine only has: [#!variable!interface_count!#] interfaces.</key>
<key name="warning_0016">[ Warning ] - No databases are available. Changes to the network interfaces will be cached.</key>
<key name="warning_0017">[ Warning ] - The subnet mask is not valid</key>
<key name="warning_0018">[ Warning ] - The IP address was specified, but the subnet mask was not</key>
<key name="warning_0019">[ Warning ] - The passed in parameter '#!variable!parameter!#': [#!variable!ip_address!#] is not a valid IPv4 address.</key>
<key name="warning_0020">[ Warning ] - The passed in parameter '#!variable!parameter!#': [#!variable!subnet_mask!#] is not a valid IPv4 subnet mask.</key>
<key name="warning_0021">[ Warning ] - All three networks require the first network pair to be defined.</key>
<key name="warning_0022">[ Warning ] - Only one network interface selected for a network pair.</key>
<key name="warning_0023">[ Warning ] - The outgoing mail server appear to not be a valid domain name or IP address.</key>
<key name="warning_0024">[ Warning ] - The outgoing mail server port is not valid. Must be 'mail_server:x' where x is 1 ~ 65535.</key>
<key name="warning_0025">[ Warning ] - There was a problem saving the mail server data. Please check the logs for more information.</key>
<key name="warning_0026">[ Warning ] - The recipient's email address appears to not be valid.</key>
<key name="warning_0027">[ Warning ] - There was a problem saving the alert recipient data. Please check the logs for more information.</key>
<key name="warning_0028">[ Warning ] - Failed to read the fence agent: [#!variable!agent!#] metadata. Ignoring it.</key>
<key name="warning_0029">[ Warning ] - While resync'ing the table: [#!variable!table!#] on: [#!variable!host_name!# (#!variable!host_uuid!#)], there was an entry found in the public schema (#!variable!column!# = #!variable!uuid!#) but not in the history schema. This shouldn't happen, and it probably a bug. Switching the query's schema from public to history for the query: [#!variable!query!#] is being dropped.</key>
<key name="warning_0030">[ Warning ] - Databse->insert_or_update_variables() was called with 'update_value_only' set, but the 'variable_uuid' wasn't passed or the 'variable_uuid' wasn't found given the 'variable_name'. Unable to update. Passed in values are logged below this message</key>
<key name="warning_0031">[ Warning ] - No internet detected (couldn't ping: [#!variable!domain!#]). Skipping attempt to download RPMs.</key>
<!-- Errors -->
<key name="error_0001">There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network).</key>
@ -1410,7 +1411,7 @@ The error was:
<key name="error_0113">The unified metadata file: [#!data!path::data::fences_unified_metadata!#] was not found. There may have been a problem creating it.</key>
<key name="error_0114">This row's modified_date wasn't the first column returned in query: [#!variable!query!#]</key>
<key name="error_0115">This row's UUID column: [#!variable!uuid_column!#] wasn't the second column returned in query: [#!variable!query!#]</key>
<key name="error_0116">This is a CentOS machine, and tried to move the directory: [#!data!path::directories::html!#/rhel8] to: [#!data!path::directories::html!#/centos8], but that renane failed.</key>
<key name="error_0116">This is a CentOS machine, and tried to move the directory: [#!variable!source!#] to: [#!variable!target!#], but that renane failed.</key>
<!-- These are units, words and so on used when displaying information. -->
<key name="unit_0001">Yes</key>

@ -188,16 +188,8 @@ if (not $configured)
$anvil->nice_exit({exit_code => 9});
}
# Figure out what this machine is
my ($os_type, $os_arch) = $anvil->System->get_os_type();
$anvil->data->{host_os}{os_type} = $os_type;
$anvil->data->{host_os}{os_arch} = $os_arch;
$anvil->data->{host_os}{version} = ($os_type =~ /(\d+)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"host_os::os_type" => $anvil->data->{host_os}{os_type},
"host_os::os_arch" => $anvil->data->{host_os}{os_arch},
"host_os::version" => $anvil->data->{host_os}{version},
}});
# Figure out what this machine is.
load_os_info($anvil);
# If this isn't a Striker dashboard, exit.
if ($anvil->System->get_host_type ne "dashboard")
@ -271,6 +263,34 @@ $anvil->nice_exit({exit_code => 0});
# Private functions. #
#############################################################################################################
# This loads the OS info into hashes strings and templates will use later.
sub load_os_info
{
my ($anvil) = @_;
my ($os_type, $os_arch) = $anvil->System->get_os_type();
$anvil->data->{host_os}{os_type} = $os_type;
$anvil->data->{host_os}{os_arch} = $os_arch;
$anvil->data->{host_os}{version} = ($os_type =~ /(\d+)$/)[0];
$anvil->data->{host_os}{os_name} = "";
if ($os_type =~ /^rhel/)
{
$anvil->data->{host_os}{os_name} = "RHEL ".$anvil->data->{host_os}{version};
}
elsif ($os_type =~ /^centos/)
{
$anvil->data->{host_os}{os_name} = "CentOS ".$anvil->data->{host_os}{version};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"host_os::os_arch" => $anvil->data->{host_os}{os_arch},
"host_os::os_name" => $anvil->data->{host_os}{os_name},
"host_os::os_type" => $anvil->data->{host_os}{os_type},
"host_os::version" => $anvil->data->{host_os}{version},
}});
return(0);
}
# If this is being called as a job, this will allow the progress to be updated.
sub update_progress
{
@ -536,18 +556,6 @@ sub setup_boot_environment
update_progress($anvil, 10, "message_0096,!!file!".$anvil->data->{path}{configs}{'dhcpd.conf'}."!!");
}
### NOTE: Some of the strings in the 'tftp_bios' template expect 'sys::pxe::os_name'
my ($os_type, $os_arch) = $anvil->System->get_os_type();
$anvil->data->{sys}{pxe}{os_name} = "";
$anvil->data->{sys}{pxe}{os_arch} = $os_arch;
if ($os_type eq "rhel8")
{
$anvil->data->{sys}{pxe}{os_name} = "RHEL 8";
}
elsif ($os_type eq "centos8")
{
$anvil->data->{sys}{pxe}{os_name} = "CentOS 8";
}
### PXE BIOS 'default' file.
my $bios_default_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "tftp_bios", variables => { base_url => $base_url }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bios_default_body => $bios_default_body }});
@ -577,6 +585,35 @@ sub setup_boot_environment
print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n";
}
### PXE UEFI 'uefi' file.
my $uefi_default_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "tftp_uefi", variables => { base_url => $base_url }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uefi_default_body => $uefi_default_body }});
# Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means something
# went wrong.
my $uefi_default_success = $anvil->Storage->update_file({
body => $uefi_default_body,
file => $anvil->data->{path}{configs}{pxe_uefi},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uefi_default_success => $uefi_default_success }});
if (not $uefi_default_success)
{
# Failed.
print $anvil->Words->string({key => "error_0043", variables => { file => $anvil->data->{path}{configs}{pxe_uefi} }})."\n";
update_progress($anvil, 100, "error_0043,!!file!".$anvil->data->{path}{configs}{pxe_uefi}."!!");
$anvil->nice_exit({code => 3});
}
elsif ($dhcpd_conf_success eq "1")
{
# Updated
print $anvil->Words->string({key => "message_0097", variables => { file => $anvil->data->{path}{configs}{pxe_uefi} }})."\n";
}
elsif ($dhcpd_conf_success eq "2")
{
# Update not needed.
print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{pxe_uefi} }})."\n";
}
### TODO: Add repos for all known strikers
# Build the repository file body.
my $repo_file = "/etc/yum.repos.d/".$anvil->_short_host_name.".repo";
@ -948,165 +985,184 @@ sub update_install_source
update_progress($anvil, 90, "");
}
### TODO: Make sure this handles no internet access gracefully.
# Clear the dnf cache
my $success = 1;
my $progress = 30;
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{dnf}." clean expire-cache" });
my $packages = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/Packages/*";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
print $anvil->Words->string({key => "message_0077", variables => { directory => $packages }})."\n";
update_progress($anvil, $progress, "message_0077,!!directory!".$packages."!!");
# Test if there is internet access.
my $domain = "redhat.com";
if ($anvil->data->{host_os}{os_type} =~ /^centos/)
{
$domain = "mirrorlist.centos.org";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { domain => $domain }});
$anvil->data->{sys}{internet} = $anvil->Network->check_internet({domains => [$domain]});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sys::internet" => $anvil->data->{sys}{internet} }});
# Loop through each letter directory
foreach my $letter (sort {$a cmp $b} keys %{$anvil->data->{packages}})
if (not $anvil->data->{sys}{internet})
{
$progress += 2;
$progress = 90 if $progress > 90;
my $download_path = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/Packages/".$letter;
my $array_size = @{$anvil->data->{packages}{$letter}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
letter => $letter,
download_path => $download_path,
array_size => $array_size,
progress => $progress,
}});
if (not -e $download_path)
{
$anvil->Storage->make_directory({debug => 2, directory => $download_path, mode => "0775"});
}
my $say_packages = $anvil->Convert->add_commas({number => $array_size});
print $anvil->Words->string({key => "message_0120", variables => {
directory => $download_path,
packages => $say_packages,
}})."\n";
update_progress($anvil, $progress, "message_0120,!!directory!".$download_path."!!,!!packages!".$say_packages."!!");
my $packages = "";
my $shell_call = $anvil->data->{path}{exe}{dnf}." download --destdir ".$download_path." ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
foreach my $package (sort {$a cmp $b} @{$anvil->data->{packages}{$letter}})
{
# Append the package to the active shell call.
$packages .= $package." ";
}
$packages =~ s/ $//;
$shell_call .= " ".$packages."; 2>&1 ".$anvil->data->{path}{exe}{'echo'}." return_code:\$?";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
# Now call the command in the background, then we'll track the output.
my $stdout_file = "/tmp/".$THIS_FILE."_update_install_source.stdout";
if (-e $stdout_file)
{
unlink $stdout_file;
}
my ($handle, undef) = $anvil->System->call({
debug => 2,
shell_call => $shell_call,
background => 1,
stdout_file => $stdout_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { handle => $handle }});
# No internet.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "warning_0031", variables => { domain => $domain }});
update_progress($anvil, 50, "warning_0031,!!domain!".$domain."!!");
}
else
{
# Clear the dnf cache
my $success = 1;
my $progress = 30;
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{dnf}." clean expire-cache" });
my $packages = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/Packages/*";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
print $anvil->Words->string({key => "message_0077", variables => { directory => $packages }})."\n";
update_progress($anvil, $progress, "message_0077,!!directory!".$packages."!!");
# Now we'll loop, printing output, until the handle dies.
my $alive = 1;
my $last_stdout_line = 0;
my $error_out = "";
my $return_code = 255;
while($alive)
foreach my $letter (sort {$a cmp $b} keys %{$anvil->data->{packages}})
{
# Are we still alive?
$alive = $handle->poll();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { alive => $alive }});
$progress += 2;
$progress = 90 if $progress > 90;
my $download_path = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/Packages/".$letter;
my $array_size = @{$anvil->data->{packages}{$letter}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
letter => $letter,
download_path => $download_path,
array_size => $array_size,
progress => $progress,
}});
if (not -e $download_path)
{
$anvil->Storage->make_directory({debug => 2, directory => $download_path, mode => "0775"});
}
my $say_packages = $anvil->Convert->add_commas({number => $array_size});
print $anvil->Words->string({key => "message_0120", variables => {
directory => $download_path,
packages => $say_packages,
}})."\n";
update_progress($anvil, $progress, "message_0120,!!directory!".$download_path."!!,!!packages!".$say_packages."!!");
# Sleep (even if we're dead now, we want to give a second for the stdout file to be updated
# before processing it).
sleep 1;
my $packages = "";
my $shell_call = $anvil->data->{path}{exe}{dnf}." download --destdir ".$download_path." ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
foreach my $package (sort {$a cmp $b} @{$anvil->data->{packages}{$letter}})
{
# Append the package to the active shell call.
$packages .= $package." ";
}
$packages =~ s/ $//;
$shell_call .= " ".$packages."; 2>&1 ".$anvil->data->{path}{exe}{'echo'}." return_code:\$?";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
# print any new STDOUT lines
my $stdout = $anvil->Storage->read_file({force_read => 1, file => $stdout_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { stdout => $stdout }});
# Now call the command in the background, then we'll track the output.
my $stdout_file = "/tmp/".$THIS_FILE."_update_install_source.stdout";
if (-e $stdout_file)
{
unlink $stdout_file;
}
my ($handle, undef) = $anvil->System->call({
debug => 2,
shell_call => $shell_call,
background => 1,
stdout_file => $stdout_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { handle => $handle }});
my $this_stdout_line = 0;
foreach my $line (split/\n/, $stdout)
# Now we'll loop, printing output, until the handle dies.
my $alive = 1;
my $last_stdout_line = 0;
my $error_out = "";
my $return_code = 255;
while($alive)
{
$this_stdout_line++;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:this_stdout_line' => $this_stdout_line,
's2:last_stdout_line' => $last_stdout_line,
's3:line' => $line,
}});
if ($this_stdout_line > $last_stdout_line)
# Are we still alive?
$alive = $handle->poll();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { alive => $alive }});
# Sleep (even if we're dead now, we want to give a second for the stdout file to be updated
# before processing it).
sleep 1;
# print any new STDOUT lines
my $stdout = $anvil->Storage->read_file({force_read => 1, file => $stdout_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { stdout => $stdout }});
my $this_stdout_line = 0;
foreach my $line (split/\n/, $stdout)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
print $anvil->Words->string({key => "message_0078", variables => { line => $line }})."\n";
# In some cases, a bad local RPM can cause download failures. This
# checks for and removes them. It can take a few runs to clear out a
# set of bad files, but it will clear out eventually.
if ($line =~ / (.*?.rpm): Interrupted by header callback/i)
$this_stdout_line++;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:this_stdout_line' => $this_stdout_line,
's2:last_stdout_line' => $last_stdout_line,
's3:line' => $line,
}});
if ($this_stdout_line > $last_stdout_line)
{
my $rpm_name = $1;
my $rpm_path = $download_path."/".$rpm_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => {
rpm_name => $rpm_name,
rpm_path => $rpm_path,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
print $anvil->Words->string({key => "message_0078", variables => { line => $line }})."\n";
if (-e $rpm_path)
# In some cases, a bad local RPM can cause download failures. This
# checks for and removes them. It can take a few runs to clear out a
# set of bad files, but it will clear out eventually.
if ($line =~ / (.*?.rpm): Interrupted by header callback/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0469", variables => { rpm_path => $rpm_path }});
unlink $rpm_path;
my $rpm_name = $1;
my $rpm_path = $download_path."/".$rpm_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => {
rpm_name => $rpm_name,
rpm_path => $rpm_path,
}});
$error_out .= $anvil->Words->string({key => "log_0469", variables => { rpm_path => $rpm_path }})."\n";
if (-e $rpm_path)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "log_0469", variables => { rpm_path => $rpm_path }});
unlink $rpm_path;
$error_out .= $anvil->Words->string({key => "log_0469", variables => { rpm_path => $rpm_path }})."\n";
}
}
}
$last_stdout_line = $this_stdout_line;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { last_stdout_line => $last_stdout_line }});
# A none-zero return code indicates, likely, that a package failed to download.
if ($line =~ /^return_code:(\d+)$/)
{
$return_code = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { return_code => $return_code }});
if ($return_code)
$last_stdout_line = $this_stdout_line;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { last_stdout_line => $last_stdout_line }});
# A none-zero return code indicates, likely, that a package failed to download.
if ($line =~ /^return_code:(\d+)$/)
{
$return_code = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { return_code => $return_code }});
if ($return_code)
{
# Something went wrong.
$error_out .= $anvil->Words->string({key => "error_0063", variables => { packages => $packages, return_code => $return_code }})."\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { error_out => $error_out }});
}
}
if ($line =~ /^Error: /)
{
# Something went wrong.
$error_out .= $anvil->Words->string({key => "error_0063", variables => { packages => $packages, return_code => $return_code }})."\n";
$error_out .= $line."\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { error_out => $error_out }});
}
}
if ($line =~ /^Error: /)
{
$error_out .= $line."\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { error_out => $error_out }});
}
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { error_out => $error_out }});
if ($error_out)
{
# Bump the last successful time by 24 hours.
$anvil->Database->insert_or_update_variables({
variable_name => "install-target::refreshed",
variable_value => $anvil->data->{sys}{retry_time},
variable_default => "",
variable_description => "striker_0106",
variable_section => "system",
variable_source_uuid => $anvil->Get->host_uuid,
variable_source_table => "hosts",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { error_out => $error_out }});
if ($error_out)
{
# Bump the last successful time by 24 hours.
$anvil->Database->insert_or_update_variables({
variable_name => "install-target::refreshed",
variable_value => $anvil->data->{sys}{retry_time},
variable_default => "",
variable_description => "striker_0106",
variable_section => "system",
variable_source_uuid => $anvil->Get->host_uuid,
variable_source_table => "hosts",
});
# Something went wrong, exit.
print $anvil->Words->string({key => "error_0045", variables => { error => $error_out }})."\n";
update_progress($anvil, 100, "error_0045,!!error!".$error_out."!!");
$anvil->nice_exit({code => 7});
# Something went wrong, exit.
print $anvil->Words->string({key => "error_0045", variables => { error => $error_out }})."\n";
update_progress($anvil, 100, "error_0045,!!error!".$error_out."!!");
$anvil->nice_exit({code => 7});
}
# Progress is '82' leaving this loop
}
# Progress is '82' leaving this loop
}
# Create the repodata
@ -2397,28 +2453,47 @@ sub load_packages
elsif ($os_type eq "centos8")
{
push @{$anvil->data->{packages}{c}}, "centos-backgrounds.noarch";
push @{$anvil->data->{packages}{c}}, "centos-gpg-keys.noarch";
push @{$anvil->data->{packages}{c}}, "centos-indexhtml.noarch";
push @{$anvil->data->{packages}{c}}, "centos-logos-httpd.noarch";
push @{$anvil->data->{packages}{c}}, "centos-logos.x86_64";
push @{$anvil->data->{packages}{c}}, "centos-release.x86_64";
push @{$anvil->data->{packages}{c}}, "centos-repos.x86_64";
# While we're here, we will need to rename /var/www/html/rhel8 to /var/www/html/centos8, as
# 'rhel8' is used by anvil-striker-extra.
if ((-e $anvil->data->{path}{directories}{html}."/rhel8") && (not -e $anvil->data->{path}{directories}{html}."/centos8"))
# While we're here, we will need to rename /var/www/html/rhel8 to /var/www/html/rhel8, as
# 'centos8' and '/var/lib/tftpboot/rhel8' as 'centos8', as is used by anvil-striker-extra.
foreach my $directory ("html", "tftpboot")
{
my $shell_call = $anvil->data->{path}{exe}{mv}." ".$anvil->data->{path}{directories}{html}."/rhel8 ".$anvil->data->{path}{directories}{html}."/centos8";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
my $source = $anvil->data->{path}{directories}{$directory}."/rhel8";
my $target = $anvil->data->{path}{directories}{$directory}."/centos8";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
directory => $directory,
source => $source,
target => $target,
}});
my ($handle, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { handle => $handle, return_code => $return_code }});
if (-e $anvil->data->{path}{directories}{html}."/centos8")
if ((-e $anvil->data->{path}{directories}{$directory}."/rhel8") && (not -e $anvil->data->{path}{directories}{$directory}."/centos8"))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0475"});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0116"});
$anvil->nice_exit({code => 12});
my $shell_call = $anvil->data->{path}{exe}{mv}." ".$source." ".$target;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($handle, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { handle => $handle, return_code => $return_code }});
if (-e $anvil->data->{path}{directories}{$directory}."/centos8")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0475", variables => {
source => $source,
target => $target,
}});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0116", variables => {
source => $source,
target => $target,
}});
$anvil->nice_exit({code => 12});
}
}
}
}

@ -166,7 +166,7 @@ sub check_if_time
else
{
my ($unixtime, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => "parse-oui::parsed"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
unixtime => $unixtime,
variable_uuid => $variable_uuid,
modified_date => $modified_date,
@ -174,27 +174,27 @@ sub check_if_time
if (($unixtime eq "") or ($unixtime =~ /\D/))
{
$unixtime = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { unixtime => $unixtime }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { unixtime => $unixtime }});
}
# If the database variable 'parse-oui::parsed' is not set, or is more than 24 hours old,
# proceed with the parse.
$anvil->data->{'parse-oui'}{'parse-period'} = 86400 if not defined $anvil->data->{'parse-oui'}{'parse-period'};
$anvil->data->{'parse-oui'}{'parse-period'} =~ s/,//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'parse-oui::parse-period' => $anvil->data->{'parse-oui'}{'scan-period'},
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
'parse-oui::parse-period' => $anvil->data->{'parse-oui'}{'parse-period'},
}});
if ($anvil->data->{'parse-oui'}{'parse-period'} =~ /\D/)
{
$anvil->data->{'parse-oui'}{'parse-period'} = 86400;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
'parse-oui::parse-period' => $anvil->data->{'parse-oui'}{'parse-period'},
}});
}
my $time_now = time;
my $next_parse = $unixtime + $anvil->data->{'parse-oui'}{'parse-period'};
my $difference = ($next_parse - $time_now);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:time_now' => $time_now,
's2:next_parse' => $next_parse,
's3:difference' => $difference,
@ -208,7 +208,7 @@ sub check_if_time
{
# Log when the next scan will happen and then exit.
my $say_when = $anvil->Convert->time({'time' => $difference, long => 1});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0468", variables => { next_parse => $anvil->Convert->add_commas({number => $say_when}) }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, 'print' => 1, key => "log_0468", variables => { next_parse => $anvil->Convert->add_commas({number => $say_when}) }});
update_progress($anvil, 100, "log_0468,!!next_parse!".$anvil->Convert->add_commas({number => $say_when})."!!");
$anvil->nice_exit({exit_code => 3});
}

Loading…
Cancel
Save