#!/usr/bin/perl # # This manages the DHCP, PXE, tftp daemons, kickstart files and and the install sources on Striker # dashboards. Collectively, this provides the ability for other machines in the Anvil! to be built or rebuilt # without access to the Internet. # # The initial configuration and the ability to get updates does check for an Internet connection. # Alternatively, /mnt/files/ will be checked for a # # Examples; # - Call with '--enable' to enable the install target. # - Call with '--disable' to disable the install target. # - Call with '--refresh' to check for updates from upstream repositories. # # Exit codes; # 0 = Normal exit. # 1 = Alteeve repo not installed and not installable. # 2 = BCN IP not found, unable to configure yet. # 3 = Failed to write or update a configuration file. # 4 = Not a striker dashboard (or isn't yet). # 5 = Not running as the 'root' user. # 6 = The 'comps,xml' file was not found. # 7 = A package failed to download to our repo # 8 = No database connection available. # 9 = The system isn't configured yet. # 10 = Failed to start dhcpd. # 11 = Failed to stop dhcpd. # 12 = Failed to rename the /var/www/html/rhel8 to ./centos8 # # Switchs # # --check - Normally, '--status' will exit after checking to see if it's running or not. This switch # tells it to proceed with a check of the configuration (and possibly update the RPM repo) # after reporting the status. # --disable - Disable the "Install Manifest" feature. # --enable - Enable the "Install Manifest" feature. # --force - This is the same as '--refresh', forcing the update of the RPM repo. # --job-uuid - If set, the UUID passed will be read and updated as the update runs. # --no-refresh - If a refresh of the RPM repository would have been called, this switch will prevent it from # happening. # --refresh - This tells this run to refresh the RPM repository, regardless of whether it would have run # otherwise. # --status - This checks to see if dhcpd is running or not, then exits (effectively checking if the # "Install Target" feature is running). # # NOTE: This is currently broken. Re-enable when a solution to anvil-striker-extra is found. # # TODO: # - Support building the install target by mounting the ISO and checking /mnt/shared/incoming for needed # files. We can dump the files into a temporary directory, createrepo it, then use the same logic as we use # for internet loads. We can use '/mnt/shared/incoming' for handling updated RPMs to updating air-gapped # systems, too. use strict; use warnings; use Anvil::Tools; # Disable buffering $| = 1; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) { $running_directory =~ s/^\./$ENV{PWD}/; } my $anvil = Anvil::Tools->new(); # Read switches $anvil->data->{switches}{check} = 0; $anvil->data->{switches}{disable} = ""; $anvil->data->{switches}{enable} = ""; $anvil->data->{switches}{force} = ""; $anvil->data->{switches}{'job-uuid'} = ""; $anvil->data->{switches}{'no-refresh'} = 0; $anvil->data->{switches}{refresh} = 0; $anvil->data->{switches}{status} = ""; $anvil->Get->switches(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'switches::check' => $anvil->data->{switches}{check}, 'switches::disable' => $anvil->data->{switches}{disable}, 'switches::enable' => $anvil->data->{switches}{enable}, 'switches::force' => $anvil->data->{switches}{force}, 'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'}, 'switches::no-refresh' => $anvil->data->{switches}{'no-refresh'}, 'switches::refresh' => $anvil->data->{switches}{refresh}, 'switches::status' => $anvil->data->{switches}{status}, }}); # Make sure we're running as 'root' # $< == real UID, $> == effective UID if (($< != 0) && ($> != 0)) { # Not root $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0005"}); $anvil->nice_exit({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 => 3, list => { dhcpd_running => $dhcpd_running }}); if ($dhcpd_running) { print $anvil->Words->string({key => "message_0123"})."\n"; print "status=1\n"; if (not $anvil->data->{switches}{check}) { $anvil->nice_exit({exit_code => 0}); } } else { print $anvil->Words->string({key => "message_0124"})."\n"; print "status=0\n"; if (not $anvil->data->{switches}{check}) { $anvil->nice_exit({exit_code => 0}); } } } # Connect to the database(s). $anvil->Database->connect(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { # No databases, exit. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0003"}); $anvil->nice_exit({exit_code => 8}); } update_progress($anvil, 0, "clear"); update_progress($anvil, 1, "log_0239,!!job-uuid!".$anvil->data->{switches}{'job-uuid'}."!!"); # If we're being asked to disable, just do so and exit. if ($anvil->data->{switches}{disable}) { my $exit_code = 0; my $job_message = "message_0125"; my $return_code = $anvil->System->stop_daemon({daemon => $anvil->data->{sys}{daemon}{dhcpd}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { return_code => $return_code }}); if ($return_code) { # non-0 means something went wrong. $job_message = "message_0126"; $exit_code = 8; 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 }}); } 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"}); } # Record the new state $anvil->Database->insert_or_update_variables({ variable_name => "install-target::enabled", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", variable_value => "disabled", variable_default => "unavailable", variable_description => "striker_0110", variable_section => "system", }); ### NOTE: There is no complex job processing here. If we've been asked to enable or disable, it only ### takes a second to do it. update_progress($anvil, 100, $job_message); $anvil->nice_exit({exit_code => $exit_code}); } # Exit if we're not configured yet my $configured = $anvil->System->check_if_configured; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { configured => $configured }}); if (not $configured) { print $anvil->Words->string({key => "error_0046"})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, key => "error_0046"}); update_progress($anvil, 100, "log_0240,!!job-uuid!".$anvil->data->{switches}{'job-uuid'}."!!"); $anvil->nice_exit({exit_code => 9}); } ### TODO: Remove this when re-enabled. print $anvil->Words->string({key => "message_0291"})."\n"; update_progress($anvil, 100, "message_0291"); $anvil->nice_exit({exit_code => 0}); # Figure out what this machine is. load_os_info($anvil); # If this isn't a Striker dashboard, exit. if ($anvil->Get->host_type ne "striker") { print $anvil->Words->string({key => "error_0044"})."\n"; update_progress($anvil, 100, "error_0044"); $anvil->nice_exit({exit_code => 4}); } # Calling with 'refresh' takes time, so we only do it when asked. print $anvil->Words->string({key => "message_0102"})."\n"; load_packages($anvil); # Setup PXE/tftp/dhcpd config. setup_boot_environment($anvil); # Store the RPMs in repo directory. 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 $exit_code = 0; my $job_message = "message_0127"; my $return_code = $anvil->System->start_daemon({debug => 2, 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. $exit_code = 8; $job_message = "message_0128"; 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 }}); } 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"}); } # Record the new state $anvil->Database->insert_or_update_variables({ variable_name => "install-target::enabled", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", variable_value => "enabled", variable_default => "unavailable", variable_description => "striker_0110", variable_section => "system", }); ### NOTE: There is no complex job processing here. If we've been asked to enable or disable, it only ### takes a second to do it. update_progress($anvil, 90, $job_message); if ($exit_code) { update_progress($anvil, 100, ""); $anvil->nice_exit({exit_code => $exit_code}); } } # We're done print $anvil->Words->string({key => "message_0025"})."\n"; update_progress($anvil, 100, "message_0025"); $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->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-stream/) { $anvil->data->{host_os}{os_name} = "CentOS Stream ".$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 { my ($anvil, $progress, $message) = @_; if (not $anvil->data->{switches}{'job-uuid'}) { return(0); } $anvil->Job->update_progress({ progress => $progress, message => $message, job_uuid => $anvil->data->{switches}{'job-uuid'}, }); return(0); } sub check_refresh { my ($anvil) = @_; # If '--no-refresh' is passed, don't refresh if ($anvil->data->{switches}{'no-refresh'}) { return(0); update_progress($anvil, 90, ""); } # Setup the packages directory $anvil->data->{path}{directories}{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 => { "path::directories::packages" => $anvil->data->{path}{directories}{packages} }}); # Default to 'no' $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "switches::refresh" => $anvil->data->{switches}{refresh}, "switches::force" => $anvil->data->{switches}{force}, }}); # If refresh isn't set, we're out. if (not $anvil->data->{switches}{refresh}) { return(0); } # It it's forced, the user is insisting on it. if ($anvil->data->{switches}{force}) { return(0); } # If it's been disabled in anvil.conf, exit. $anvil->data->{'install-manifest'}{'refresh-packages'} = 1 if not defined $anvil->data->{'install-manifest'}{'refresh-packages'}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "install-manifest::refresh-packages" => $anvil->data->{'install-manifest'}{'refresh-packages'} }}); if (not $anvil->data->{'install-manifest'}{'refresh-packages'}) { # We're out. $anvil->data->{switches}{refresh} = 0; print $anvil->Words->string({key => "log_0235"})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0235"}); return(0); } # If the Packages directory doesn't exist, we'll need to refresh. if (not -e $anvil->data->{path}{directories}{packages}) { # Source isn't configured, set it up $anvil->data->{switches}{refresh} = 1; print $anvil->Words->string({key => "log_0237"})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0237"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "switches::refresh" => $anvil->data->{switches}{refresh} }}); return(0); } # See when the last time we refreshed the local package cache my ($unixtime, $variable_uuid, $modified_date) = $anvil->Database->read_variable({ variable_name => "install-target::refreshed", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { unixtime => $unixtime, variable_uuid => $variable_uuid, modified_date => $modified_date, }}); if (($unixtime eq "") or ($unixtime =~ /\D/)) { $unixtime = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { unixtime => $unixtime }}); } ### 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. # If the database variable 'install-target::refresh' is not set, or is more than 24 hours old, # refresh. $anvil->data->{'install-manifest'}{'refresh-period'} = 86400 if not defined $anvil->data->{'install-manifest'}{'refresh-period'}; $anvil->data->{'install-manifest'}{'refresh-period'} =~ s/,//g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'install-manifest::refresh-period' => $anvil->data->{'install-manifest'}{'refresh-period'}, }}); if ($anvil->data->{'install-manifest'}{'refresh-period'} =~ /\D/) { $anvil->data->{'install-manifest'}{'refresh-period'} = 86400; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'install-manifest::refresh-period' => $anvil->data->{'install-manifest'}{'refresh-period'}, }}); } my $time_now = time; 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 => 3, list => { 's1:time_now' => $time_now, 's2:next_scan' => $next_scan, 's3:difference' => $difference, }}); if ((not $variable_uuid) or ($unixtime !~ /^\d+/) or ($difference < 0)) { # It's been long enough (or it's the first time), refresh. $anvil->data->{switches}{refresh} = 1; my $variables = { seconds => $anvil->Convert->add_commas({number => $anvil->data->{'install-manifest'}{'refresh-period'}}) }; print $anvil->Words->string({key => "log_0238", variables => $variables})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0238", variables => $variables}); return(0); } elsif ($difference > 0) { # Log when the next scan will happen $anvil->data->{switches}{refresh} = 0; my $variables = { next_refresh => $anvil->Convert->add_commas({number => $difference}) }; print $anvil->Words->string({key => "log_0236", variables => $variables})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0236", variables => $variables}); } # 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 # refresh succeeded. if ($unixtime =~ /^\d+$/) { $anvil->data->{sys}{retry_time} = $unixtime + 86400; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::retry_time" => $anvil->data->{sys}{retry_time} }}); } else { # No previous time, so start with the current time $anvil->data->{sys}{retry_time} = time; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::retry_time" => $anvil->data->{sys}{retry_time} }}); } return(0); } sub setup_boot_environment { my ($anvil) = @_; # Get my BCN IP and active OS. $anvil->Network->get_ips(); my $bcn_interface = ""; my $local_host = $anvil->Get->short_host_name(); foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface => $interface }}); next if $interface !~ /^bcn/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "network::local::${interface}::ip" => $anvil->data->{network}{$local_host}{interface}{$interface}{ip} }}); if ($anvil->Validate->ipv4({ip => $anvil->data->{network}{$local_host}{interface}{$interface}{ip} })) { $bcn_interface = $interface; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bcn_interface => $bcn_interface }}); } last if $bcn_interface; } if (not $bcn_interface) { # Can't set this up yet. print $anvil->Words->string({key => "error_0042"})."\n"; update_progress($anvil, 100, "error_0042"); $anvil->nice_exit({exit_code => 2}); } my $bcn_ip = $anvil->data->{network}{$local_host}{interface}{$bcn_interface}{ip}; my $bcn_subnet_mask = $anvil->data->{network}{$local_host}{interface}{$bcn_interface}{subnet_mask}; my $bcn_network = $anvil->Network->get_network({ip => $bcn_ip, subnet_mask => $bcn_subnet_mask}); my $dns = $anvil->data->{network}{$local_host}{interface}{$bcn_interface}{dns} ? $anvil->data->{network}{$local_host}{interface}{$bcn_interface}{dns} : $anvil->data->{defaults}{network}{dns}; my $domain = "localdomain"; my $base_url = "http://".$bcn_ip."/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}; if ($anvil->Get->host_name =~ /\./) { $domain = $anvil->Get->host_name; $domain =~ s/^.*?\.//; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bcn_interface => $bcn_interface, bcn_ip => $bcn_ip, bcn_subnet_mask => $bcn_subnet_mask, bcn_network => $bcn_network, dns => $dns, domain => $domain, base_url => $base_url, }}); ### NOTE: The DNS range is a bit tricky, so for now, we'll assume that the BCN is always a /16 ### network. Someday this might change, and if so, we'll need to make this a lot smarter. my $striker_number = ($anvil->Get->short_host_name =~ /striker(\d+)/)[0]; $striker_number = 1 if not $striker_number; $striker_number =~ s/^0//; my $third_octet = (10 * $striker_number) + 4; $third_octet = 254 if $third_octet > 254; my $first_part = ($bcn_network =~ /^(\d+\.\d+)\./)[0].".".$third_octet; my $range = $first_part.".10 ".$first_part.".250"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 's1:striker_number' => $striker_number, 's2:third_octet' => $third_octet, 's3:first_part' => $first_part, 's4:range' => $range, }}); ### DHCP server config my $dhcpd_conf_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "dhcpd_conf", variables => { dns => $dns, domain => $domain, network => $bcn_network, range => $range, router => $bcn_ip, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { dhcpd_conf_body => $dhcpd_conf_body }}); # Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means something # went wrong. my $dhcpd_conf_success = $anvil->Storage->update_file({ body => $dhcpd_conf_body, file => $anvil->data->{path}{configs}{'dhcpd.conf'}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { dhcpd_conf_success => $dhcpd_conf_success }}); if (not $dhcpd_conf_success) { # Failed. print $anvil->Words->string({key => "error_0043", variables => { file => $anvil->data->{path}{configs}{'dhcpd.conf'} }})."\n"; update_progress($anvil, 100, "error_0043"); $anvil->nice_exit({exit_code => 3}); } elsif ($dhcpd_conf_success eq "1") { # Restart dhcpd. print $anvil->Words->string({key => "message_0095", variables => { daemon => "dhcpd", file => $anvil->data->{path}{configs}{'dhcpd.conf'}, }})."\n"; update_progress($anvil, 10, "message_0095,!!daemon!dhcpd!!,!!file!".$anvil->data->{path}{configs}{'dhcpd.conf'}."!!"); $anvil->System->restart_daemon({daemon => $anvil->data->{sys}{daemon}{dhcpd}}); } elsif ($dhcpd_conf_success eq "2") { # Update not needed. print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{'dhcpd.conf'} }})."\n"; update_progress($anvil, 10, "message_0096,!!file!".$anvil->data->{path}{configs}{'dhcpd.conf'}."!!"); } ### 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 }}); # Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means something # went wrong. my $bios_default_success = $anvil->Storage->update_file({ body => $bios_default_body, file => $anvil->data->{path}{configs}{pxe_default}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bios_default_success => $bios_default_success }}); if (not $bios_default_success) { # Failed. print $anvil->Words->string({key => "error_0043", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n"; update_progress($anvil, 100, "error_0043,!!file!".$anvil->data->{path}{configs}{pxe_default}."!!"); $anvil->nice_exit({exit_code => 3}); } elsif ($bios_default_success eq "1") { # Updated print $anvil->Words->string({key => "message_0097", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n"; } elsif ($bios_default_success eq "2") { # Update not needed. print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n"; } ### PXE UEFI 'grub.cfg' file. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "host_os::os_type" => $anvil->data->{host_os}{os_type} }}); my $say_os = "#!string!brand_0010!#"; if ($anvil->data->{host_os}{os_type} eq "centos8") { $say_os = "#!string!brand_0011!#"; } elsif ($anvil->data->{host_os}{os_type} eq "centos-stream8") { $say_os = "#!string!brand_0012!#"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_os => $say_os }}); my $uefi_grub_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "tftp_grub", variables => { base_url => $base_url, say_os => $say_os, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uefi_grub_body => $uefi_grub_body }}); # Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means something # went wrong. my $uefi_grub_success = $anvil->Storage->update_file({ body => $uefi_grub_body, file => $anvil->data->{path}{configs}{pxe_grub}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uefi_grub_success => $uefi_grub_success }}); if (not $uefi_grub_success) { # Failed. print $anvil->Words->string({key => "error_0043", variables => { file => $anvil->data->{path}{configs}{pxe_grub} }})."\n"; update_progress($anvil, 100, "error_0043,!!file!".$anvil->data->{path}{configs}{pxe_grub}."!!"); $anvil->nice_exit({exit_code => 3}); } elsif ($uefi_grub_success eq "1") { # Updated print $anvil->Words->string({key => "message_0097", variables => { file => $anvil->data->{path}{configs}{pxe_grub} }})."\n"; } elsif ($uefi_grub_success eq "2") { # Update not needed. print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{pxe_grub} }})."\n"; } ### TODO: Add repos for all known strikers # Build the repository file body. my $repo_file = "/etc/yum.repos.d/".$anvil->Get->short_host_name.".repo"; my $repo_body = "[".$anvil->Get->short_host_name."-repo]\n"; $repo_body .= "name=".$anvil->Get->host_name." #!string!message_0153!#\n"; # Add the IPs. $anvil->Network->get_ips({}); my $first_line = 1; foreach my $in_iface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}}) { my $ip = $anvil->data->{network}{$local_host}{interface}{$in_iface}{ip}; if ($ip) { my $prefix = $first_line ? "baseurl=" : " "; $first_line = 0; $repo_body .= $prefix."http://".$ip."/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/\n"; } } $repo_body .= "enabled=1\n"; $repo_body .= "gpgcheck=0\n"; $repo_body .= "skip_if_unavailable=1\n"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { repo_file => $repo_file, repo_body => $repo_body, }}); ### Generate kickstart files. my $progress = 10; foreach my $type ("striker", "node", "dr") { $progress += 2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { type => $type, progress => $progress }}); my $say_type = "#!string!message_0115!#"; if ($type eq "node") { $say_type = "#!string!message_0116!#"; } elsif ($type eq "dr") { $say_type = "#!string!message_0117!#"; } my $kickstart_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "kickstart", variables => { type => $type, say_type => $say_type, host_name => "new-".$type.".".$domain, os => $anvil->data->{host_os}{os_type}, url => $base_url."/os/", keyboard => $anvil->data->{kickstart}{keyboard} ? $anvil->data->{kickstart}{keyboard} : $anvil->data->{defaults}{kickstart}{keyboard}, timezone => $anvil->data->{kickstart}{timezone} ? $anvil->data->{kickstart}{timezone} : $anvil->data->{defaults}{kickstart}{timezone}, password => $anvil->data->{kickstart}{password} ? $anvil->data->{kickstart}{password} : $anvil->data->{defaults}{kickstart}{password}, repo_file => $repo_file, repo_body => $repo_body, 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 => 3, list => { kickstart_body => $kickstart_body }}); # Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means # something went wrong. my $kickstart_file = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/kickstart/".$type.".ks"; my $kickstart_success = $anvil->Storage->update_file({ body => $kickstart_body, file => $kickstart_file, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { kickstart_success => $kickstart_success }}); if (not $kickstart_success) { # Failed. print $anvil->Words->string({key => "error_0043", variables => { file => $kickstart_file }})."\n"; update_progress($anvil, 100, "error_0043,!!file!".$kickstart_file."!!"); $anvil->nice_exit({exit_code => 3}); } elsif ($kickstart_success eq "1") { # Updated $anvil->Storage->change_mode({path => $kickstart_file, mode => "0664"}); $anvil->Storage->change_owner({path => $kickstart_file, user => "striker-ui-api", group => "striker-ui-api" }); print $anvil->Words->string({key => "message_0097", variables => { file => $kickstart_file }})."\n"; update_progress($anvil, $progress, "message_0097,!!file!".$kickstart_file."!!"); } elsif ($kickstart_success eq "2") { # Update not needed. print $anvil->Words->string({key => "message_0096", variables => { file => $kickstart_file }})."\n"; update_progress($anvil, $progress, "message_0096,!!file!".$kickstart_file."!!"); } # progress is '16' as it leaves this loop } # Configure apache to show hidden (dot) files. my $old_autoindex_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'autoindex.conf'}}); my $new_autoindex_conf = ""; my $update_autoindex_conf = 0; $progress = 18; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { old_autoindex_conf => $old_autoindex_conf }}); foreach my $line (split/\n/, $old_autoindex_conf) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }}); if (($line =~ /^IndexIgnore /) && ($line =~ / \.\?\?\* /)) { $line =~ s/ \.\?\?\* / /; $update_autoindex_conf = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "<< line" => $line, update_autoindex_conf => $update_autoindex_conf, }}); } if (($line =~ /^IndexOptions /) && ($line !~ / NameWidth=/)) { # Allow long file names to show without being truncated $line .= " NameWidth=*"; $update_autoindex_conf = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "<< line" => $line, update_autoindex_conf => $update_autoindex_conf, }}); } if (($line =~ /^IndexOptions /) && ($line !~ / FoldersFirst/)) { # Sort folders before files $line .= " FoldersFirst"; $update_autoindex_conf = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "<< line" => $line, update_autoindex_conf => $update_autoindex_conf, }}); } if (($line =~ /^IndexOptions /) && ($line !~ / IgnoreCase/)) { # Sort filenames without putting capitalized first letters at the start of the list. $line .= " IgnoreCase"; $update_autoindex_conf = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "<< line" => $line, update_autoindex_conf => $update_autoindex_conf, }}); } $new_autoindex_conf .= $line."\n"; } if ($update_autoindex_conf) { # Update and reload apache $anvil->Storage->backup({debug => 2, file => $anvil->data->{path}{configs}{'autoindex.conf'}}); my $return = $anvil->Storage->write_file({ debug => 2, body => $new_autoindex_conf, file => $anvil->data->{path}{configs}{'autoindex.conf'}, overwrite => 1, mode => "0644", user => "root", group => "root" }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'return' => $return }}); if ($return) { # Something went wrong. print $anvil->Words->string({key => "log_0233", variables => { file => $anvil->data->{path}{configs}{'autoindex.conf'}, 'return' => $return }})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0233", variables => { file => $anvil->data->{path}{configs}{'autoindex.conf'}, 'return' => $return }}); update_progress($anvil, "100", "log_0233,!!file!".$anvil->data->{path}{configs}{'autoindex.conf'}."!!,!!return!".$return."!!"); $anvil->nice_exit({exit_code => 3}); } print $anvil->Words->string({key => "message_0095", variables => { daemon => "httpd", file => $anvil->data->{path}{configs}{'autoindex.conf'} }})."\n"; $anvil->System->reload_daemon({daemon => $anvil->data->{sys}{daemon}{httpd}}); update_progress($anvil, $progress, "message_0095,!!daemon!httpd!!,!!file!".$anvil->data->{path}{configs}{'autoindex.conf'}."!!"); } else { # Update not needed. print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{'autoindex.conf'} }})."\n"; update_progress($anvil, $progress, "message_0096,!!file!".$anvil->data->{path}{configs}{'autoindex.conf'}."!!"); } # Update dnf to save downloaded files for later repo building efficiency and to skip upstream repos # that aren't available at a given time. my $keepcache_seen = 0; my $skip_seen = 0; my $old_dnf_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'dnf.conf'}}); my $new_dnf_conf = ""; $progress = 20; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { old_dnf_conf => $old_dnf_conf }}); foreach my $line (split/\n/, $old_dnf_conf) { ### NOTE: We don't examin the value on purpose. If the user changes these, we'll respect it. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }}); if ($line =~ /^keepcache=/) { $keepcache_seen = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { keepcache_seen => $keepcache_seen }}); } elsif ($line =~ /^skip_if_unavailable=/) { $skip_seen = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { skip_seen => $skip_seen }}); } $new_dnf_conf .= $line."\n"; } if ((not $keepcache_seen) or (not $skip_seen)) { # Update needed. if (not $keepcache_seen) { $new_dnf_conf .= "keepcache=1\n"; } if (not $skip_seen) { $new_dnf_conf .= "skip_if_unavailable=1\n"; } $anvil->Storage->backup({debug => 2, file => $anvil->data->{path}{configs}{'dnf.conf'}}); my $return = $anvil->Storage->write_file({ debug => 2, body => $new_dnf_conf, file => $anvil->data->{path}{configs}{'dnf.conf'}, overwrite => 1, mode => "0644", user => "root", group => "root" }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'return' => $return }}); if ($return) { # Something went wrong. print $anvil->Words->string({key => "log_0233", variables => { file => $anvil->data->{path}{configs}{'dnf.conf'}, 'return' => $return }})."\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0233", variables => { file => $anvil->data->{path}{configs}{'dnf.conf'}, 'return' => $return }}); update_progress($anvil, 100, "log_0233,!!file!".$anvil->data->{path}{configs}{'dnf.conf'}."!!,!!return!".$return."!!"); $anvil->nice_exit({exit_code => 3}); } print $anvil->Words->string({key => "message_0097", variables => { file => $anvil->data->{path}{configs}{'dnf.conf'} }})."\n"; update_progress($anvil, $progress, "message_0097,!!file!".$anvil->data->{path}{configs}{'dnf.conf'}."!!"); } else { # Update not needed. print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{'dnf.conf'} }})."\n"; update_progress($anvil, $progress, "message_0096,!!file!".$anvil->data->{path}{configs}{'dnf.conf'}."!!"); } ### Check that daemons are enabled/disabled. ### NOTE: We don't manage dhcpd. We leave it off, but if the user enabled it, respect that. # Make sure tftp is enabled. $progress = 22; if (-e $anvil->data->{path}{systemd}{tftp_enabled_symlink}) { print $anvil->Words->string({key => "message_0099", variables => { daemon => $anvil->data->{sys}{daemon}{tftp} }})."\n"; update_progress($anvil, $progress, "message_0099,!!daemon!".$anvil->data->{sys}{daemon}{tftp}."!!"); } else { print $anvil->Words->string({key => "message_0098", variables => { daemon => $anvil->data->{sys}{daemon}{tftp} }})."\n"; update_progress($anvil, $progress, "message_0098,!!daemon!".$anvil->data->{sys}{daemon}{tftp}."!!"); $anvil->System->enable_daemon({daemon => $anvil->data->{sys}{daemon}{tftp}}); $anvil->System->start_daemon({daemon => $anvil->data->{sys}{daemon}{tftp}}); } $progress = 24; if (-e $anvil->data->{path}{systemd}{httpd_enabled_symlink}) { print $anvil->Words->string({key => "message_0099", variables => { daemon => $anvil->data->{sys}{daemon}{httpd} }})."\n"; update_progress($anvil, $progress, "message_0099,!!daemon!".$anvil->data->{sys}{daemon}{httpd}."!!"); } else { print $anvil->Words->string({key => "message_0098", variables => { daemon => $anvil->data->{sys}{daemon}{httpd} }})."\n"; update_progress($anvil, $progress, "message_0098,!!daemon!".$anvil->data->{sys}{daemon}{httpd}."!!"); $anvil->System->enable_daemon({daemon => $anvil->data->{sys}{daemon}{httpd}}); $anvil->System->start_daemon({daemon => $anvil->data->{sys}{daemon}{httpd}}); } # Make sure the syslinux files needed for creating the PXE boot menu are in place. $progress = 26; if (not -e $anvil->data->{path}{directories}{tftpboot}."/vesamenu.c32") { # Copy the syslinux files print $anvil->Words->string({key => "message_0100"})."\n"; update_progress($anvil, $progress, "message_0100"); $anvil->Storage->rsync({ debug => 3, source => $anvil->data->{path}{directories}{syslinux}."/*", destination => $anvil->data->{path}{directories}{tftpboot}."/", }); } else { print $anvil->Words->string({key => "message_0101"})."\n"; update_progress($anvil, $progress, "message_0101"); } ### TODO: Add UEFI # Copy the background splash image for the PXE boot images. my $bios_splash = $anvil->data->{path}{directories}{skins}."/".$anvil->Template->skin."/images/bios-splash.jpg"; if (-e $bios_splash) { # We don't bother checking if this needs to be updated because rsync can figure it out more # efficiently. $anvil->Storage->rsync({ debug => 3, source => $bios_splash, destination => $anvil->data->{path}{directories}{tftpboot}."/splash.jpg", }); } return(0); } ### NOTE: This will probably be removed... sub check_alteeve_repo { my ($anvil) = @_; my $repo_file = "/etc/yum.repos.d/alteeve-anvil.repo"; my $repo_url = "https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { repo_file => $repo_file, repo_url => $repo_url, }}); # If the repo file doesn't exist, try to install it. if (not -e $repo_file) { # Install the repo my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{dnf}." -y install ".$repo_url }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); } # If it still doesn't exist, we're done. if (not -e $repo_file) { # Die print $anvil->Words->string({key => "error_0041"})."\n"; update_progress($anvil, 100, "error_0041"); $anvil->nice_exit({exit_code => 1}); } return(0); } # This uses yumdownloader to push the files into '/var/www/html///os sub update_install_source { my ($anvil) = @_; # Job progress is at '26' coming into here # Should we refresh the local repo? check_refresh($anvil); if (not $anvil->data->{switches}{refresh}) { return(0); update_progress($anvil, 90, ""); } # 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 if (not $anvil->data->{sys}{internet}) { # 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."!!"); # If this host is not a RHEL host, add the HA packages to the main packages. If it is RHEL, # we'll pull the packages from a node. if ($anvil->data->{host_os}{os_type} ne "rhel8") { foreach my $letter (sort {$a cmp $b} keys %{$anvil->data->{ha_packages}}) { foreach my $package (sort {$a cmp $b} @{$anvil->data->{packages}{$letter}}) { # Push the package onto the normal array. push @{$anvil->data->{packages}{$letter}}, $package; } } delete $anvil->data->{ha_packages}; } foreach my $letter (sort {$a cmp $b} keys %{$anvil->data->{packages}}) { $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 => 2, 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 => 3, shell_call => $shell_call, background => 1, stdout_file => $stdout_file, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { handle => $handle }}); # 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) { # 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) { $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) { $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) { 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, }}); 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) { # 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: /) { $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", }); # 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({exit_code => 7}); } # Progress is '82' leaving this loop } # If this is a RHEL host, we'll now look for a node to download HA packages from. if ($anvil->data->{host_os}{os_type} eq "rhel8") { # Try to find a node to download the RPMs on. update_progress($anvil, ++$progress, "message_0184"); my $use_node_name = ""; my $use_node_ip = ""; my $use_password = ""; my $local_short_host_name = $anvil->Get->short_host_name; $anvil->Network->load_ips({ debug => 3, host => $local_short_host_name, }); my $query = " SELECT a.host_uuid, a.host_name, b.anvil_password FROM hosts a, anvils b WHERE a.host_type = 'node' AND ( a.host_uuid = b.anvil_node1_host_uuid OR a.host_uuid = b.anvil_node2_host_uuid ) ORDER BY a.host_name ASC ;"; my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $count = @{$results}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { results => $results, count => $count, }}); foreach my $row (@{$results}) { my $host_uuid = $row->[0]; my $host_name = $row->[1]; my $anvil_password = $row->[2]; my $short_host_name = $host_name; $short_host_name =~ s/\..*$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid, host_name => $host_name, anvil_password => $anvil->Log->is_secure($anvil_password), short_host_name => $short_host_name, }}); $anvil->Network->load_ips({ debug => 2, host_uuid => $host_uuid, host => $short_host_name, }); my $access = 0; my ($match) = $anvil->Network->find_matches({ debug => 2, first => $local_short_host_name, second => $short_host_name, source => $THIS_FILE, line => __LINE__, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { match => $match }}); if (ref($match) eq "HASH") { my $keys = keys %{$match}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'keys' => $keys }}); } if (ref($match) eq "HASH") { foreach my $interface (sort {$a cmp $b} keys %{$match->{$short_host_name}}) { my $remote_ip = $match->{$short_host_name}{$interface}{ip}; my ($pinged, $average_time) = $anvil->Network->ping({ ping => $remote_ip, count => 1, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { remote_ip => $remote_ip, pinged => $pinged, average_time => $average_time, }}); if ($pinged) { my $access = $anvil->Remote->test_access({ target => $remote_ip, password => $anvil_password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }}); if ($access) { my $internet = $anvil->Network->check_internet({ debug => 3, target => $remote_ip, password => $anvil_password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { internet => $internet }}); if ($internet) { my ($os_type, $os_arch) = $anvil->Get->os_type({ debug => 3, target => $remote_ip, password => $anvil_password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { os_type => $os_type, os_arch => $os_arch, }}); if (($anvil->data->{host_os}{os_type} eq $os_type) && ($os_arch eq $anvil->data->{host_os}{os_arch})) { update_progress($anvil, ++$progress, "message_0185,!!node_name!".$host_name."!!"); $use_node_name = $host_name; $use_node_ip = $remote_ip; $use_password = $anvil_password; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_node_name => $use_node_name, use_node_ip => $use_node_ip, use_password => $anvil->Log->is_secure($use_password), }}); last; } } } } } } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_node_ip => $use_node_ip }}); if ($use_node_ip) { foreach my $letter (sort {$a cmp $b} keys %{$anvil->data->{ha_packages}}) { my $download_path = "/tmp/Packages/".$letter; my $local_path = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/Packages/".$letter; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { letter => $letter, download_path => $download_path, local_path => $local_path, }}); # This is the directory we'll download the packages to on the node. $anvil->Storage->make_directory({ debug => 3, directory => $download_path, target => $use_node_ip, password => $use_password, mode => "0775", }); 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->{ha_packages}{$letter}}) { # Append the package to the active shell call. $packages .= $package." "; } $packages =~ s/ $//; $shell_call .= " ".$packages; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # None of the HA packages are large os it's not worth trying to monitor the downlaods # in real time. As such, we'll make a standard remote call. my ($output, $error, $return_code) = $anvil->Remote->call({ debug => 3, target => $use_node_ip, password => $use_password, shell_call => $shell_call, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, error => $error, return_code => $return_code, }}); if (not $return_code) { # Success! Copy the files. my $failed = $anvil->Storage->rsync({ debug => 3, source => "root\@".$use_node_ip.":".$download_path."/*", destination => $local_path."/", password => $use_password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }}); if (not $failed) { update_progress($anvil, ++$progress, "message_0187,!!letter!".$letter."!!"); } } } update_progress($anvil, ++$progress, "message_0188"); } else { # No nodes found. update_progress($anvil, ++$progress, "message_0186"); } } } # Create the repodata print $anvil->Words->string({key => "message_0118"})."\n"; my $repo_path = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os"; my $repo_data_path = $repo_path."/repodata"; my $comps_xml = $repo_path."/comps.xml"; my $modules_yaml = $repo_path."/modules.yaml"; my $target_comps = $repo_data_path."/comps.xml"; if (not -e $comps_xml) { # We can't install properly without the comps.xml file, it provides grouping needed by the # guest OS install. print $anvil->Words->string({key => "message_0119", variables => { comps_xml => $comps_xml }})."\n"; update_progress($anvil, 100, "message_0119,!!comps_xml!".$comps_xml."!!"); $anvil->nice_exit({exit_code => 6}); } $anvil->Storage->copy_file({ debug => 2, source_file => $comps_xml, target_file => $target_comps, overwrite => 1, }); if (not -e $target_comps) { # Something appears to have happened and it failed to copy. print $anvil->Words->string({key => "message_0129", variables => { comps_xml => $comps_xml, target_comps => $target_comps }})."\n"; update_progress($anvil, 100, "message_0129,!!comps_xml!".$comps_xml."!!,!!target_comps!".$target_comps."!!"); $anvil->nice_exit({exit_code => 6}); } update_progress($anvil, 85, ""); my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{createrepo_c}." -g ".$comps_xml." ".$repo_path }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }}); print $anvil->Words->string({key => "message_0130"})."\n"; update_progress($anvil, 90, "message_0130"); ### NOTE: This doesn't work for libssh2 yet (haven't figured out how to add it to 'modules.yaml' ### sourced from RHEL 8.1 ISO yet). Once that's fixed, remove 'module_hotfixes=1' from ### Striker->get_local_repo(). $output = ""; ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{modifyrepo_c}." --mdtype=modules ".$modules_yaml." ".$repo_data_path }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }}); print $anvil->Words->string({key => "message_0159"})."\n"; update_progress($anvil, 95, "message_0159"); # Update the refresh time to now. $anvil->Database->insert_or_update_variables({ debug => 2, variable_name => "install-target::refreshed", variable_value => time, variable_default => "", variable_description => "striker_0106", variable_section => "system", variable_source_uuid => $anvil->Get->host_uuid, variable_source_table => "hosts", }); return(0); } # sub load_packages { my ($anvil) = @_; # Read in any packages the user wants us to add. $anvil->data->{packages}{users} = []; if ((exists $anvil->data->{striker}{repo}{'extra-packages'}) && ($anvil->data->{striker}{repo}{'extra-packages'})) { foreach my $package (split/,/, $anvil->data->{striker}{repo}{'extra-packages'}) { $package =~ s/^\s+//; $package =~ s/\s+$//; next if $package eq ""; push @{$anvil->data->{packages}{users}}, $package } } ### TODO: Download 'alteeve-release.noarch' directly. ### NOTE: If/when we support other archs, removing '.x86_64/.noarch' would cause all available archs ### to be downloaded (including .ix86, which would waste space...). Decide if it's best to ### explicitely declare archs vs using space/bandwidth to just grab all available. # This is the list of packages we need to download. $anvil->data->{packages} = { a => [ "abattis-cantarell-fonts.noarch", "accountsservice-libs.x86_64", "accountsservice.x86_64", "acl.x86_64", "adwaita-cursor-theme.noarch", "adwaita-gtk2-theme.x86_64", "adwaita-icon-theme.noarch", "alsa-lib.x86_64", "annobin.x86_64", "anvil-core.noarch", "anvil-dr.noarch", "anvil-node.noarch", "anvil-striker.noarch", "apr-util-bdb.x86_64", "apr-util-openssl.x86_64", "apr-util.x86_64", "apr.x86_64", "aspell.x86_64", "at-spi2-atk.x86_64", "at-spi2-core.x86_64", "atk.x86_64", "audit-libs.x86_64", "audit.x86_64", "augeas-libs.x86_64", "authselect-libs.x86_64", "authselect.x86_64", "autogen-libopts.x86_64", "avahi-glib.x86_64", "avahi-libs.x86_64", ], b => [ "basesystem.noarch", "bash-completion.noarch", "bash.x86_64", "binutils.x86_64", "bind-export-libs.x86_64", "bind-libs-lite.x86_64", "bind-libs.x86_64", "bind-license.noarch", "bind-utils.x86_64", "bluez-libs.x86_64", "bluez-obexd.x86_64", "bluez.x86_64", "bolt.x86_64", "boost-atomic.x86_64", "boost-chrono.x86_64", "boost-date-time.x86_64", "boost-iostreams.x86_64", "boost-program-options.x86_64", "boost-random.x86_64", "boost-regex.x86_64", "boost-system.x86_64", "boost-thread.x86_64", "bpg-dejavu-sans-fonts.noarch", "brotli.x86_64", "bubblewrap.x86_64", "bzip2-libs.x86_64", "bzip2.x86_64", ], c => [ "c-ares.x86_64", "ca-certificates.noarch", "cairo-gobject.x86_64", "cairo.x86_64", "celt051.x86_64", "checkpolicy.x86_64", "cheese-libs.x86_64", "chkconfig.x86_64", "chrony.x86_64", "cifs-utils.x86_64", "clutter-gst3.x86_64", "clutter-gtk.x86_64", "clutter.x86_64", "cockpit-bridge.x86_64", "cockpit-machines.noarch", "cockpit-packagekit.noarch", "cockpit-system.noarch", "cockpit-ws.x86_64", "cockpit.x86_64", "cogl.x86_64", "color-filesystem.noarch", "colord-gtk.x86_64", "colord-libs.x86_64", "colord.x86_64", "coreutils-common.x86_64", "coreutils.x86_64", "cpio.x86_64", "cpp.x86_64", "cracklib-dicts.x86_64", "cracklib.x86_64", "createrepo_c-libs.x86_64", "createrepo_c.x86_64", "cronie-anacron.x86_64", "cronie.x86_64", "crontabs.noarch", "crypto-policies.noarch", "crypto-policies-scripts.noarch", "cryptsetup-libs.x86_64", "cups-libs.x86_64", "cups-pk-helper.x86_64", "curl.x86_64", "cyrus-sasl-gssapi.x86_64", "cyrus-sasl-lib.x86_64", "cyrus-sasl-md5.x86_64", "cyrus-sasl-plain.x86_64", "cyrus-sasl.x86_64", ], d => [ "dbus-common.noarch", "dbus-daemon.x86_64", "dbus-glib.x86_64", "dbus-libs.x86_64", "dbus-tools.x86_64", "dbus-x11.x86_64", "dbus.x86_64", "dbxtool.x86_64", "dconf.x86_64", "dejavu-fonts-common.noarch", "dejavu-sans-fonts.noarch", "dejavu-sans-mono-fonts.noarch", "dejavu-serif-fonts.noarch", "desktop-file-utils.x86_64", "device-mapper-event-libs.x86_64", "device-mapper-event.x86_64", "device-mapper-libs.x86_64", "device-mapper-multipath-libs.x86_64", "device-mapper-multipath.x86_64", "device-mapper-persistent-data.x86_64", "device-mapper.x86_64", "dhcp-client.x86_64", "dhcp-common.noarch", "dhcp-libs.x86_64", "dhcp-server.x86_64", "diffutils.x86_64", "dmidecode.x86_64", "dnf-data.noarch", "dnf-plugin-subscription-manager.x86_64", "dnf-plugins-core.noarch", "dnf.noarch", "dnsmasq.x86_64", "dosfstools.x86_64", "dracut-config-rescue.x86_64", "dracut-network.x86_64", "dracut-squash.x86_64", "dracut.x86_64", "drbd90-utils.x86_64", "drpm.x86_64", "dwz.x86_64", ], e => [ "e2fsprogs-libs.x86_64", "e2fsprogs.x86_64", "edk2-ovmf.noarch", "efi-filesystem.noarch", "efi-srpm-macros.noarch", "efivar-libs.x86_64", "efibootmgr.x86_64", "elfutils.x86_64", "elfutils-default-yama-scope.noarch", "elfutils-libelf.x86_64", "elfutils-libelf-devel.x86_64", "elfutils-libs.x86_64", "emacs-filesystem.noarch", "enchant.x86_64", "enchant2.x86_64 ", "ethtool.x86_64", "evolution-data-server-langpacks.noarch", "evolution-data-server.x86_64", "expat.x86_64", "expect.x86_64", ], f => [ "fence-agents-all.x86_64", "fence-agents-amt-ws.noarch", "fence-agents-apc-snmp.noarch", "fence-agents-apc.noarch", "fence-agents-bladecenter.noarch", "fence-agents-brocade.noarch", "fence-agents-cisco-mds.noarch", "fence-agents-cisco-ucs.noarch", "fence-agents-common.noarch", "fence-agents-compute.noarch", "fence-agents-drac5.noarch", "fence-agents-eaton-snmp.noarch", "fence-agents-emerson.noarch", "fence-agents-eps.noarch", "fence-agents-heuristics-ping.noarch", "fence-agents-hpblade.noarch", "fence-agents-ibmblade.noarch", "fence-agents-ifmib.noarch", "fence-agents-ilo-moonshot.noarch", "fence-agents-ilo-mp.noarch", "fence-agents-ilo-ssh.noarch", "fence-agents-ilo2.noarch", "fence-agents-intelmodular.noarch", "fence-agents-ipdu.noarch", "fence-agents-ipmilan.noarch", "fence-agents-kdump.x86_64", "fence-agents-mpath.noarch", "fence-agents-redfish.x86_64", "fence-agents-rhevm.noarch", "fence-agents-rsa.noarch", "fence-agents-rsb.noarch", "fence-agents-sbd.noarch", "fence-agents-scsi.noarch", "fence-agents-virsh.noarch", "fence-agents-vmware-rest.noarch", "fence-agents-vmware-soap.noarch", "fence-agents-wti.noarch", "fence-virt.x86_64", "file-libs.x86_64", "file.x86_64", "filesystem.x86_64", "findutils.x86_64", "fipscheck-lib.x86_64", "fipscheck.x86_64", "firefox.x86_64", "firewalld-filesystem.noarch", "firewalld.noarch", "flac-libs.x86_64", "flatpak.x86_64", "fontconfig.x86_64", "fontpackages-filesystem.noarch", "freeipmi.x86_64", "freetype.x86_64", "fribidi.x86_64", "fuse.x86_64", "fuse-libs.x86_64", ], g => [ "gawk.x86_64", "GConf2.x86_64", "gc.x86_64", "gcc.x86_64", "gcr.x86_64", "gdb-headless.x86_64", "gdbm-libs.x86_64", "gdbm.x86_64", "gdisk.x86_64", "gdk-pixbuf2-modules.x86_64", "gdk-pixbuf2.x86_64", "gdm.x86_64", "genisoimage.x86_64", "geoclue2-libs.x86_64", "geoclue2.x86_64", "geocode-glib.x86_64", "geolite2-city.noarch", "geolite2-country.noarch", "gettext-libs.x86_64", "gettext.x86_64", "ghc-srpm-macros.noarch", "gjs.x86_64", "glib-networking.x86_64", "glib2.x86_64", "glibc-common.x86_64", "glibc-devel.x86_64", "glibc-langpack-en.x86_64", "glibc.x86_64", "glibc-devel.x86_64", "glibc-headers.x86_64", "glusterfs.x86_64", "glusterfs-api.x86_64", "glusterfs-cli.x86_64", "glusterfs-client-xlators.x86_64", "glusterfs-libs.x86_64", "glx-utils.x86_64", "gmp.x86_64", "gnome-bluetooth-libs.x86_64", "gnome-bluetooth.x86_64", "gnome-control-center-filesystem.noarch", "gnome-control-center.x86_64", "gnome-desktop3.x86_64", "gnome-keyring-pam.x86_64", "gnome-keyring.x86_64", "gnome-online-accounts.x86_64", "gnome-session-wayland-session.x86_64", "gnome-session-xsession.x86_64", "gnome-session.x86_64", "gnome-settings-daemon.x86_64", "gnome-shell.x86_64", "gnome-terminal.x86_64", "gnome-themes-standard.x86_64", "gnupg2-smime.x86_64", "gnupg2.x86_64", "gnutls-dane.x86_64", "gnutls-utils.x86_64", "gnutls.x86_64", "go-srpm-macros.noarch", "gobject-introspection.x86_64", "gpgme.x86_64", "gpm-libs.x86_64", "gpm.x86_64", "graphite2.x86_64", "grep.x86_64", "grilo.x86_64", "groff-base.x86_64", "grub2-common.noarch", "grub2-efi-x64.x86_64", "grub2-pc-modules.noarch", "grub2-pc.x86_64", "grub2-tools-extra.x86_64", "grub2-tools-minimal.x86_64", "grub2-tools.x86_64", "grubby.x86_64", "gsettings-desktop-schemas.x86_64", "gsm.x86_64", "gssproxy.x86_64", "gstreamer1-plugins-base.x86_64", "gstreamer1.x86_64", "gtksourceview3.x86_64", "gtk-update-icon-cache.x86_64", "gtk-vnc2.x86_64", "gtk2.x86_64", "gtk3.x86_64", "guile.x86_64", "gvfs.x86_64", "gvfs-client.x86_64", "gvnc.x86_64", "gzip.x86_64", ], h => [ "hardlink.x86_64", "harfbuzz-icu.x86_64", "harfbuzz.x86_64", "hdparm.x86_64", "hicolor-icon-theme.noarch", "hostname.x86_64", "htop.x86_64", "httpd-filesystem.noarch", "httpd-tools.x86_64", "httpd.x86_64", "hunspell-en-GB.noarch", "hunspell-en-US.noarch", "hunspell-en.noarch", "hunspell.x86_64", "hwdata.noarch", "hyphen.x86_64", ], i => [ "ibus-gtk2.x86_64", "ibus-gtk3.x86_64", "ibus-libs.x86_64", "ibus-setup.noarch", "ibus.x86_64", "iio-sensor-proxy.x86_64", "ima-evm-utils.x86_64", "info.x86_64", "initscripts.x86_64", "ipcalc.x86_64", "ipmitool.x86_64", "iproute-tc.x86_64", "iproute.x86_64", "iprutils.x86_64", "ipset-libs.x86_64", "ipset.x86_64", "iptables-ebtables.x86_64", "iptables-libs.x86_64", "iptables.x86_64", "iputils.x86_64", "ipxe-roms-qemu.noarch", "irqbalance.x86_64", "iscsi-initiator-utils-iscsiuio.x86_64", "iscsi-initiator-utils.x86_64", "isns-utils-libs.x86_64", "isl.x86_64", "iso-codes.noarch", "iwl100-firmware.noarch", "iwl1000-firmware.noarch", "iwl105-firmware.noarch", "iwl135-firmware.noarch", "iwl2000-firmware.noarch", "iwl2030-firmware.noarch", "iwl3160-firmware.noarch", "iwl3945-firmware.noarch", "iwl4965-firmware.noarch", "iwl5000-firmware.noarch", "iwl5150-firmware.noarch", "iwl6000-firmware.noarch", "iwl6000g2a-firmware.noarch", "iwl6050-firmware.noarch", "iwl7260-firmware.noarch", ], j => [ "jansson.x86_64", "jasper-libs.x86_64", "jbigkit-libs.x86_64", "json-c.x86_64", "json-glib.x86_64", ], k => [ "kbd-legacy.noarch", "kbd-misc.noarch", "kbd.x86_64", "kernel-core.x86_64", "kernel-doc.noarch", "kernel-devel.x86_64", "kernel-headers.x86_64", "kernel-modules.x86_64", "kernel-tools-libs.x86_64", "kernel-tools.x86_64", "kernel.x86_64", "kexec-tools.x86_64", "keyutils-libs.x86_64", "keyutils.x86_64", "kmod-drbd.x86_64", "kmod-libs.x86_64", "kmod.x86_64", "kpartx.x86_64", "krb5-libs.x86_64", ], l => [ "langpacks-en.noarch", "lcms2.x86_64", "less.x86_64", "libICE.x86_64", "libSM.x86_64", "libX11-common.noarch", "libX11-xcb.x86_64", "libX11.x86_64", "libXau.x86_64", "libXcomposite.x86_64", "libXcursor.x86_64", "libXdamage.x86_64", "libXdmcp.x86_64", "libXext.x86_64", "libXfixes.x86_64", "libXfont2.x86_64", "libXft.x86_64", "libXi.x86_64", "libXinerama.x86_64", "libXmu.x86_64", "libXrandr.x86_64", "libXrender.x86_64", "libXt.x86_64", "libXtst.x86_64", "libXv.x86_64", "libXxf86misc.x86_64", "libXxf86vm.x86_64", "libacl.x86_64", "libaio.x86_64", "libappstream-glib.x86_64", "libarchive.x86_64", "libassuan.x86_64", "libasyncns.x86_64", "libatasmart.x86_64", "libatomic_ops.x86_64", "libattr.x86_64", "libbabeltrace.x86_64", "libbasicobjects.x86_64", "libblkid.x86_64", "libbytesize.x86_64", "libbluray.x86_64", "libcacard.x86_64", "libcanberra-gtk3.x86_64", "libcanberra.x86_64", "libcap-ng.x86_64", "libcap.x86_64", "libcdio.x86_64", "libcdio-paranoia.x86_64", "libcgroup.x86_64", "libblockdev.x86_64", "libblockdev-crypto.x86_64", "libblockdev-fs.x86_64", "libblockdev-loop.x86_64", "libblockdev-mdraid.x86_64", "libblockdev-part.x86_64", "libblockdev-swap.x86_64", "libblockdev-utils.x86_64", "libcollection.x86_64", "libcom_err.x86_64", "libcomps.x86_64", "libcroco.x86_64", "libcurl.x86_64", "libdaemon.x86_64", "libdatrie.x86_64", "libdb-utils.x86_64", "libdb.x86_64", "libdhash.x86_64", "libdnf.x86_64", "libdrm.x86_64", "libedit.x86_64", "libepoxy.x86_64", "liberation-fonts-common.noarch", "liberation-sans-fonts.noarch", "libestr.x86_64", "libevdev.x86_64", "libevent.x86_64", "libfastjson.x86_64", "libfdisk.x86_64", "libffi.x86_64", "libfontenc.x86_64", "libgcc.x86_64", "libgcrypt.x86_64", "libgdata.x86_64", "libglvnd-egl.x86_64", "libglvnd-gles.x86_64", "libglvnd-glx.x86_64", "libglvnd.x86_64", "libgnomekbd.x86_64", "libgomp.x86_64", "libgpg-error.x86_64", "libgtop2.x86_64", "libgudev.x86_64", "libgusb.x86_64", "libgweather.x86_64", "libibumad.x86_64", "libibverbs.x86_64", "libical.x86_64", "libicu.x86_64", "libidn2.x86_64", "libimobiledevice.x86_64", "libini_config.x86_64", "libinput.x86_64", "libipt.x86_64", "libiscsi.x86_64", "libjpeg-turbo.x86_64", "libkcapi-hmaccalc.x86_64", "libkcapi.x86_64", "libksba.x86_64", "libldb.x86_64", "libmaxminddb.x86_64", "libmcpp.x86_64", "libmetalink.x86_64", "libmnl.x86_64", "libmodman.x86_64", "libmodulemd.x86_64", "libmodulemd1.x86_64", "libmount.x86_64", "libmpc.x86_64", "libndp.x86_64", "libnetfilter_conntrack.x86_64", "libnfnetlink.x86_64", "libnfsidmap.x86_64", "libnftnl.x86_64", "libnghttp2.x86_64", "libnl3-cli.x86_64", "libnl3.x86_64", "libnma.x86_64", "libnotify.x86_64", "libnsl2.x86_64", "liboauth.x86_64", "libogg.x86_64", "libosinfo.x86_64", "libpath_utils.x86_64", "libpcap.x86_64", "libpciaccess.x86_64", "libpipeline.x86_64", "libpkgconf.x86_64", "libplist.x86_64", "libpmem.x86_64", "libpng.x86_64", "libpq.x86_64", "libproxy.x86_64", "libpsl.x86_64", "libpwquality.x86_64", "libqb.x86_64", "libquvi-scripts.noarch", "libquvi.x86_64", "librados2.x86_64", "librbd1.x86_64", "librdmacm.x86_64", "libref_array.x86_64", "librepo.x86_64", "libreport-filesystem.x86_64", "librhsm.x86_64", "librsvg2.x86_64", "libseccomp.x86_64", "libsecret.x86_64", "libselinux-utils.x86_64", "libselinux.x86_64", "libsemanage.x86_64", "libsepol.x86_64", "libsigsegv.x86_64", "libsmartcols.x86_64", "libsmbclient.x86_64", "libsndfile.x86_64", "libsolv.x86_64", "libsoup.x86_64", "libss.x86_64", "libssh.x86_64", "libssh-config.noarch", "libssh2.x86_64", "libsss_autofs.x86_64", "libsss_certmap.x86_64", "libsss_idmap.x86_64", "libsss_nss_idmap.x86_64", "libsss_sudo.x86_64", "libstdc++.x86_64", "libstemmer.x86_64", "libsysfs.x86_64", "libtalloc.x86_64", "libtasn1.x86_64", "libtdb.x86_64", "libteam.x86_64", "libtevent.x86_64", "libthai.x86_64", "libtheora.x86_64", "libtiff.x86_64", "libtirpc.x86_64", "libtool-ltdl.x86_64", "libudisks2.x86_64", "libunistring.x86_64", "libusal.x86_64", "libusbmuxd.x86_64", "libusbx.x86_64", "libuser.x86_64", "libutempter.x86_64", "libuuid.x86_64", "libverto-libevent.x86_64", "libverto.x86_64", "libvirt-bash-completion.x86_64", "libvirt-client.x86_64", "libvirt-daemon-config-network.x86_64", "libvirt-daemon-config-nwfilter.x86_64", "libvirt-daemon-driver-interface.x86_64", "libvirt-daemon-driver-network.x86_64", "libvirt-daemon-driver-nodedev.x86_64", "libvirt-daemon-driver-nwfilter.x86_64", "libvirt-daemon-driver-qemu.x86_64", "libvirt-daemon-driver-secret.x86_64", "libvirt-daemon-driver-storage.x86_64", "libvirt-daemon-driver-storage-core.x86_64", "libvirt-daemon-driver-storage-disk.x86_64", "libvirt-daemon-driver-storage-gluster.x86_64", "libvirt-daemon-driver-storage-iscsi.x86_64", "libvirt-daemon-driver-storage-iscsi-direct.x86_64", "libvirt-daemon-driver-storage-logical.x86_64", "libvirt-daemon-driver-storage-mpath.x86_64", "libvirt-daemon-driver-storage-rbd.x86_64", "libvirt-daemon-driver-storage-scsi.x86_64", "libvirt-daemon-kvm.x86_64", "libvirt-daemon.x86_64", "libvirt-docs.x86_64", "libvirt-glib.x86_64", "libvirt-libs.x86_64", "libvirt.x86_64", "libvisual.x86_64", "libvorbis.x86_64", "libwacom-data.noarch", "libwacom.x86_64", "libwayland-client.x86_64", "libwayland-cursor.x86_64", "libwayland-egl.x86_64", "libwayland-server.x86_64", "libwbclient.x86_64", "libwebp.x86_64", "libwsman1.x86_64", "libxcb.x86_64", "libxcrypt.x86_64", "libxcrypt-devel.x86_64", "libxkbcommon-x11.x86_64", "libxkbcommon.x86_64", "libxkbfile.x86_64", "libxklavier.x86_64", "libxml2.x86_64", "libxshmfence.x86_64", "libxslt.x86_64", "libyaml.x86_64", "libzstd.x86_64", "linux-firmware.noarch", "linuxconsoletools.x86_64", "llvm-libs.x86_64", "logrotate.x86_64", "lshw.x86_64", "lsscsi.x86_64", "lua-expat.x86_64", "lua-json.noarch", "lua-libs.x86_64", "lua-lpeg.x86_64", "lua-socket.x86_64", "lua.x86_64", "lvm2-libs.x86_64", "lvm2.x86_64", "lz4-libs.x86_64", "lzo.x86_64", "lzop.x86_64", ], 'm' => [ "mailcap.noarch", "mailx.x86_64", "make.x86_64", "man-db.x86_64", "mcpp.x86_64", "mdadm.x86_64", "mesa-dri-drivers.x86_64", "mesa-filesystem.x86_64", "mesa-libEGL.x86_64", "mesa-libGL.x86_64", "mesa-libgbm.x86_64", "mesa-libglapi.x86_64", "microcode_ctl.x86_64", "mlocate.x86_64", "mobile-broadband-provider-info.noarch", "mokutil.x86_64", "mod_http2.x86_64", "ModemManager-glib.x86_64", "mozilla-filesystem.x86_64", "mozjs52.x86_64", "mozjs60.x86_64", "mpfr.x86_64", "mtdev.x86_64", "mtools.x86_64", "mutter.x86_64", ], n => [ "ncurses-base.noarch", "ncurses-libs.x86_64", "ncurses.x86_64", "net-snmp-libs.x86_64", "net-snmp-utils.x86_64", "net-tools.x86_64", "netcf-libs.x86_64", "nettle.x86_64", "NetworkManager-libnm.x86_64", "NetworkManager-team.x86_64", "NetworkManager-tui.x86_64", "NetworkManager-wifi.x86_64", "NetworkManager.x86_64", "newt.x86_64", "nfs-utils.x86_64", "nftables.x86_64", "nm-connection-editor.x86_64", "nmap-ncat.x86_64", "nmap.x86_64", "npth.x86_64", "nspr.x86_64", "nss.x86_64", "nss-softokn-freebl.x86_64", "nss-softokn.x86_64", "nss-sysinit.x86_64", "nss-util.x86_64", "numactl-libs.x86_64", "numad.x86_64", "nvme-cli.x86_64", ], o => [ "ocaml-srpm-macros.noarch", "openblas-srpm-macros.noarch", "openldap.x86_64", "openssh-clients.x86_64", "openssh-server.x86_64", "openssh.x86_64", "openssl-libs.x86_64", "openssl-pkcs11.x86_64", "openssl.x86_64", "openwsman-python3.x86_64", "opus.x86_64", "orc.x86_64", "os-prober.x86_64", "osinfo-db-tools.x86_64", "osinfo-db.noarch", "overpass-fonts.noarch", ], p => [ "p11-kit-trust.x86_64", "p11-kit.x86_64", "PackageKit-glib.x86_64", "PackageKit.x86_64", "pam.x86_64", "pango.x86_64", "parted.x86_64", "passwd.x86_64", "patch.x86_64", "pciutils-libs.x86_64", "pciutils.x86_64", "pcre.x86_64", "pcre2.x86_64", "perl-aliased.noarch", "perl-Algorithm-C3.noarch", "perl-Algorithm-Diff.noarch", "perl-Authen-SASL.noarch", "perl-B-Hooks-EndOfScope.noarch", "perl-CGI.noarch", "perl-Capture-Tiny.noarch", "perl-Carp.noarch", "perl-Class-C3.noarch", "perl-Class-Data-Inheritable.noarch", "perl-Class-Method-Modifiers.noarch", "perl-Compress-Raw-Bzip2.x86_64", "perl-Compress-Raw-Zlib.x86_64", "perl-Convert-ASN1.noarch", "perl-Curses.x86_64", "perl-Curses-UI.noarch", "perl-DBD-Pg.x86_64", "perl-DBI.x86_64", "perl-Data-Dump.noarch", "perl-Data-Dumper.x86_64", "perl-Data-Dumper-Concise.noarch", "perl-Data-OptList.noarch", "perl-Date-Manip.noarch", "perl-Data-Validate-Domain.noarch", "perl-Data-Validate-IP.noarch", "perl-Devel-ArgNames.noarch", "perl-Devel-Caller.x86_64", "perl-Devel-GlobalDestruction.noarch", "perl-Devel-LexAlias.x86_64", "perl-Devel-StackTrace.noarch", "perl-Digest-HMAC.noarch", "perl-Digest-MD5.x86_64", "perl-Digest-SHA.x86_64", "perl-Digest-SHA1.x86_64", "perl-Digest.noarch", "perl-Dist-CheckConflicts.noarch", "perl-Email-Date-Format.noarch", "perl-Email-Find.noarch", "perl-Email-Valid.noarch", "perl-Encode-Locale.noarch", "perl-Encode.x86_64", "perl-Errno.x86_64", "perl-Eval-Closure.noarch", "perl-Eval-WithLexicals.noarch", "perl-Exception-Class.noarch", "perl-Exporter.noarch", "perl-Exporter-Declare.noarch", "perl-File-BaseDir", "perl-File-DesktopEntry", "perl-File-Listing.noarch", "perl-File-MimeInfo.noarch", "perl-File-Path.noarch", "perl-File-Temp.noarch", "perl-Filter.x86_64", "perl-Filter-Simple.noarch", "perl-Future.noarch", "perl-Getopt-Long.noarch", "perl-GSSAPI.x86_64", "perl-HTML-FromText.noarch", "perl-HTML-Parser.x86_64", "perl-HTML-Strip.x86_64", "perl-HTML-Tagset.noarch", "perl-HTTP-Cookies.noarch", "perl-HTTP-Date.noarch", "perl-HTTP-Message.noarch", "perl-HTTP-Negotiate.noarch", "perl-HTTP-Tiny.noarch", "perl-Import-Into.noarch", "perl-IO.x86_64", "perl-IO-Compress.noarch", "perl-IO-HTML.noarch", "perl-IO-Socket-IP.noarch", "perl-IO-Socket-SSL.noarch", "perl-IO-Tty.x86_64", "perl-IPC-SysV.x86_64", "perl-JSON.noarch", "perl-JSON-PP.noarch", "perl-LDAP.noarch", "perl-LWP-MediaTypes.noarch", "perl-Log-Contextual.noarch", "perl-Log-Dispatch-FileRotate.noarch", "perl-Log-Dispatch.noarch", "perl-Log-Journald.x86_64", "perl-Log-Log4perl.noarch", "perl-Meta-Builder.noarch", "perl-MIME-Base64.x86_64", "perl-MIME-Lite.noarch", "perl-MIME-Types.noarch", "perl-MRO-Compat.noarch", "perl-Mail-Sender.noarch", "perl-Mail-Sendmail.noarch", "perl-MailTools.noarch", "perl-Math-BigInt.noarch", "perl-Math-Complex.noarch", "perl-Module-Implementation.noarch", "perl-Module-Runtime.noarch", "perl-Moo.noarch", "perl-Mozilla-CA.noarch", "perl-NTLM.noarch", "perl-Net-Domain-TLD.noarch", "perl-Net-HTTP.noarch", "perl-Net-Netmask.noarch", "perl-Net-OpenSSH.noarch", "perl-Net-SMTP-SSL.noarch", "perl-Net-SSH2.x86_64", "perl-Net-SSLeay.x86_64", "perl-NetAddr-IP.x86_64", "perl-Object-Remote.noarch", "perl-Package-Generator.noarch", "perl-Package-Stash-XS.x86_64", "perl-Package-Stash.noarch", "perl-PadWalker.x86_64", "perl-Params-Util.x86_64", "perl-Params-Validate.x86_64", "perl-Params-ValidationCompiler.noarch", "perl-PathTools.x86_64", "perl-Pod-Escapes.noarch", "perl-Pod-Perldoc.noarch", "perl-Pod-Simple.noarch", "perl-Pod-Usage.noarch", "perl-Proc-Simple.noarch", "perl-Ref-Util-XS.x86_64", "perl-Ref-Util.noarch", "perl-Mail-RFC822-Address.noarch", "perl-Role-Tiny.noarch", "perl-Scalar-List-Utils.x86_64", "perl-SelfLoader.noarch", "perl-Socket.x86_64", "perl-Socket6.x86_64", "perl-Specio.noarch", "perl-srpm-macros.noarch", "perl-Storable.x86_64", "perl-strictures.noarch", "perl-String-ShellQuote.noarch", "perl-Sub-Exporter-Progressive.noarch", "perl-Sub-Exporter.noarch", "perl-Sub-Identify.x86_64", "perl-Sub-Install.noarch", "perl-Sub-Quote.noarch", "perl-Sys-Syslog.x86_64", "perl-Sys-Virt.x86_64", "perl-Term-ANSIColor.noarch", "perl-Term-Cap.noarch", "perl-Text-Balanced.noarch", "perl-TermReadKey.x86_64", "perl-Test-Simple.noarch", "perl-Text-Diff.noarch", "perl-Text-ParseWords.noarch", "perl-Text-Soundex.x86_64", "perl-Text-Tabs+Wrap.noarch", "perl-Text-Unidecode.noarch", "perl-Time-HiRes.x86_64", "perl-Time-Local.noarch", "perl-TimeDate.noarch", "perl-Try-Tiny.noarch", "perl-URI.noarch", "perl-UUID-Tiny.noarch", "perl-Unicode-Normalize.x86_64", "perl-Variable-Magic.x86_64", "perl-WWW-RobotRules.noarch", "perl-XML-LibXML.x86_64", "perl-XML-NamespaceSupport.noarch", "perl-XML-Parser.x86_64", "perl-XML-SAX-Base.noarch", "perl-XML-SAX.noarch", "perl-XML-Simple.noarch", "perl-constant.noarch", "perl-interpreter.x86_64", "perl-libnet.noarch", "perl-libs.x86_64", "perl-libwww-perl.noarch", "perl-macros.x86_64", "perl-namespace-autoclean.noarch", "perl-namespace-clean.noarch", "perl-parent.noarch", "perl-podlators.noarch", "perl-threads-shared.x86_64", "perl-threads.x86_64", "perl-version.x86_64", "perltidy.noarch", "pigz.x86_64", "pinentry-gtk.x86_64", "pinentry.x86_64", "pipewire-libs.x86_64", "pipewire.x86_64", "pixman.x86_64", "pkgconf-m4.noarch", "pkgconf-pkg-config.x86_64", "pkgconf.x86_64", "platform-python.x86_64", "platform-python-pip.noarch", "platform-python-setuptools.noarch", "plymouth-core-libs.x86_64", "plymouth-scripts.x86_64", "plymouth.x86_64", "policycoreutils-python-utils.noarch", "policycoreutils.x86_64", "polkit-libs.x86_64", "polkit-pkla-compat.x86_64", "polkit.x86_64", "popt.x86_64", "postfix.x86_64", "postgresql-contrib.x86_64", "postgresql-plperl.x86_64", "postgresql-server.x86_64", "postgresql.x86_64", "prefixdevname.x86_64", "procps-ng.x86_64", "psmisc.x86_64", "publicsuffix-list-dafsa.noarch", "pulseaudio-libs-glib2.x86_64", "pulseaudio-libs.x86_64", "pulseaudio-module-bluetooth.x86_64", "pulseaudio.x86_64", "python-srpm-macros.noarch", "python3-asn1crypto.noarch", "python3-audit.x86_64", "python3-argcomplete.noarch", "python3-bind.noarch", "python3-cairo.x86_64", "python3-cffi.x86_64", "python3-chardet.noarch", "python3-configobj.noarch", "python3-cryptography.x86_64", "python3-dateutil.noarch", "python3-dbus.x86_64", "python3-decorator.noarch", "python3-dmidecode.x86_64", "python3-dnf-plugins-core.noarch", "python3-dnf.noarch", "python3-ethtool.x86_64", "python3-firewall.noarch", "python3-gobject-base.x86_64", "python3-gobject.x86_64", "python3-gpg.x86_64", "python3-hawkey.x86_64", "python3-html5lib.noarch", "python3-idna.noarch", "python3-iniparse.noarch", "python3-inotify.noarch", "python3-libcomps.x86_64", "python3-libdnf.x86_64", "python3-librepo.x86_64", "python3-libs.x86_64", "python3-libselinux.x86_64", "python3-libsemanage.x86_64", "python3-libvirt.x86_64", "python3-libxml2.x86_64", "python3-linux-procfs.noarch", "python3-lxml.x86_64", "python3-nftables.x86_64", "python3-perf.x86_64", "python3-pexpect.noarch", "python3-pip.noarch", "python3-pip-wheel.noarch", "python3-ply.noarch", "python3-policycoreutils.noarch", "python3-ptyprocess.noarch", "python3-pyOpenSSL.noarch", "python3-pycparser.noarch", "python3-pycurl.x86_64", "python3-pyparsing.noarch", "python3-pysocks.noarch", "python3-pyudev.noarch", "python3-pyyaml.x86_64", "python3-requests.noarch", "python3-rpm.x86_64", "python3-rpm-macros.noarch", "python3-schedutils.x86_64", "python3-setools.x86_64", "python3-setuptools.noarch", "python3-setuptools-wheel.noarch", "python3-six.noarch", "python3-slip-dbus.noarch", "python3-slip.noarch", "python3-subscription-manager-rhsm.x86_64", "python3-suds.noarch", "python3-syspurpose.x86_64", "python3-systemd.x86_64", "python3-unbound.x86_64", "python3-urllib3.noarch", "python3-webencodings.noarch", "python36.x86_64", ], 'q' => [ "qemu-guest-agent.x86_64", "qemu-img.x86_64", "qemu-kvm.x86_64", "qemu-kvm-block-curl.x86_64", "qemu-kvm-block-gluster.x86_64", "qemu-kvm-block-iscsi.x86_64", "qemu-kvm-block-rbd.x86_64", "qemu-kvm-block-ssh.x86_64", "qemu-kvm-common.x86_64", "qemu-kvm-core.x86_64", "qemu-kvm.x86_64", "qt5-srpm-macros.noarch", "quota-nls.noarch", "quota.x86_64", ], r => [ "radvd.x86_64", "rdma-core.x86_64", "readline.x86_64", "redhat-rpm-config.noarch", "rest.x86_64", "rootfiles.noarch", "rpcbind.x86_64", "rpm-build.x86_64", "rpm-build-libs.x86_64", "rpmdevtools.noarch", "rpm-libs.x86_64", "rpm-plugin-selinux.x86_64", "rpm-plugin-systemd-inhibit.x86_64", "rpm.x86_64", "rsync.x86_64", "rsyslog.x86_64", "rtkit.x86_64", "ruby-irb.noarch", "ruby-libs.x86_64", "ruby.x86_64", "rubygem-bigdecimal.x86_64", "rubygem-did_you_mean.noarch", "rubygem-io-console.x86_64", "rubygem-json.x86_64", "rubygem-openssl.x86_64", "rubygem-psych.x86_64", "rubygem-rdoc.noarch", "rubygems.noarch", "rust-srpm-macros.noarch", ], 's' => [ "samba-client-libs.x86_64", "samba-common-libs.x86_64", "samba-common.noarch", "sbc.x86_64", "sbd.x86_64", "screen.x86_64", "SDL.x86_64", "seabios-bin.noarch", "seavgabios-bin.noarch", "sed.x86_64", "selinux-policy-targeted.noarch", "selinux-policy.noarch", "setroubleshoot-plugins.noarch", "setroubleshoot-server.x86_64", "setup.noarch", "sg3_utils-libs.x86_64", "sg3_utils.x86_64", "sgabios-bin.noarch", "shadow-utils.x86_64", "shared-mime-info.x86_64", "shim-x64.x86_64", "slang.x86_64", "smartmontools.x86_64", "snappy.x86_64", "sound-theme-freedesktop.noarch", "speexdsp.x86_64", "spice-glib.x86_64", "spice-gtk3.x86_64", "spice-server.x86_64", "sqlite-libs.x86_64", "squashfs-tools.x86_64", "sscg.x86_64", "sssd-client.x86_64", "sssd-common.x86_64", "sssd-kcm.x86_64", "sssd-nfs-idmap.x86_64", "startup-notification.x86_64", "subscription-manager-cockpit.noarch", "subscription-manager-rhsm-certificates.x86_64", "subscription-manager.x86_64", "sudo.x86_64", "switcheroo-control.x86_64", "syslinux-nonlinux.noarch", "syslinux.x86_64", "systemd-container.x86_64", "systemd-libs.x86_64", "systemd-pam.x86_64", "systemd-udev.x86_64", "systemd.x86_64", ], t => [ "tar.x86_64", "tcl.x86_64", "teamd.x86_64", "telnet.x86_64", "tftp-server.x86_64", "timedatex.x86_64", "tmux.x86_64", "totem-pl-parser.x86_64", "trousers-lib.x86_64", "trousers.x86_64", "tuned.noarch", "tzdata.noarch", ], u => [ "udisks2.x86_64", "unbound-libs.x86_64", "unzip.x86_64", "upower.x86_64", "usbredir.x86_64", "usbutils.x86_64", "usermode.x86_64", "userspace-rcu.x86_64", "util-linux.x86_64", "uuid.x86_64", ], v => [ "vim-common.x86_64", "vim-enhanced.x86_64", "vim-filesystem.noarch", "vim-minimal.x86_64", "vino.x86_64", "virt-install.noarch", "virt-manager-common.noarch", "virt-manager.noarch", "virt-top.x86_64", "virt-what.x86_64", "volume_key-libs.x86_64", "vte-profile.x86_64", "vte291.x86_64", ], w => [ "webkit2gtk3.x86_64", "webkit2gtk3-jsc.x86_64", "webrtc-audio-processing.x86_64", "wget.x86_64", "which.x86_64", "woff2.x86_64", "wpa_supplicant.x86_64", ], x => [ "xcb-util.x86_64", "xdg-desktop-portal.x86_64", "xdg-desktop-portal-gtk.x86_64", "xfsprogs.x86_64", "xkeyboard-config.noarch", "xml-common.noarch", "xorg-x11-server-Xwayland.x86_64", "xorg-x11-server-common.x86_64", "xorg-x11-server-utils.x86_64", "xorg-x11-xauth.x86_64", "xorg-x11-xinit.x86_64", "xorg-x11-xkb-utils.x86_64", "xz-libs.x86_64", "xz.x86_64", ], 'y' => [ "yajl.x86_64", "yum.noarch", "yum-utils.noarch", ], z => [ "zenity.x86_64", "zlib.x86_64", "zlib-devel.x86_64", "zstd.x86_64", ], }; # These packages can't be downloaded on RHEL Striker dashboads as they usually are not entitled to $anvil->data->{ha_packages} = { c => [ "clufter-bin.x86_64", "clufter-common.noarch", "corosync.x86_64", "corosynclib.x86_64", ], l => [ "libknet1.x86_64", "libknet1-compress-bzip2-plugin.x86_64", "libknet1-compress-lz4-plugin.x86_64", "libknet1-compress-lzma-plugin.x86_64", "libknet1-compress-lzo2-plugin.x86_64", "libknet1-compress-plugins-all.x86_64", "libknet1-compress-zlib-plugin.x86_64", "libknet1-crypto-nss-plugin.x86_64", "libknet1-crypto-openssl-plugin.x86_64", "libknet1-crypto-plugins-all.x86_64", "libknet1-plugins-all.x86_64", "libnozzle1.x86_64", ], p => [ "pacemaker.x86_64", "pacemaker-cli.x86_64", "pacemaker-cluster-libs.x86_64", "pacemaker-libs.x86_64", "pacemaker-schemas.noarch", "pcs.x86_64", "python3-clufter.noarch", ], r => [ "resource-agents.x86_64", ], }; my ($os_type, $os_arch) = $anvil->Get->os_type(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { os_type => $os_type, os_arch => $os_arch, }}); if ($os_type eq "rhel8") { push @{$anvil->data->{packages}{c}}, "cockpit-dashboard.noarch"; push @{$anvil->data->{packages}{r}}, "redhat-backgrounds.noarch"; push @{$anvil->data->{packages}{r}}, "redhat-indexhtml.noarch"; push @{$anvil->data->{packages}{r}}, "redhat-logos-httpd.noarch"; push @{$anvil->data->{packages}{r}}, "redhat-logos.x86_64"; push @{$anvil->data->{packages}{r}}, "redhat-release.x86_64"; } elsif ($os_type eq "centos-stream8") { 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-stream-release.noarch"; } 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-linux-release.noarch"; push @{$anvil->data->{packages}{c}}, "cockpit-dashboard.noarch"; } # Now see if the base OS has changed and, if so, rename directories my $rhel8_test_source = $anvil->data->{path}{directories}{html}."/rhel8"; my $centos8_test_source = $anvil->data->{path}{directories}{html}."/centos8"; my $centos_stream8_test_source = $anvil->data->{path}{directories}{html}."/centos-stream8"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { rhel8_test_source => $rhel8_test_source, centos8_test_source => $centos8_test_source, centos_stream8_test_source => $centos_stream8_test_source, }}); my $source = ""; my $target = ""; if ((-e $rhel8_test_source) && ($os_type ne "rhel8")) { # This isn't RHEL 8, we need to rename the directories $source = "rhel8"; $target = $os_type; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { source => $source, target => $target, }}); } elsif ((-e $centos8_test_source) && ($os_type ne "centos8")) { # This isn't CentOS 8, we need to rename the directories $source = "centos8"; $target = $os_type; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { source => $source, target => $target, }}); } elsif ((-e $centos_stream8_test_source) && ($os_type ne "centos-stream8")) { # This isn't CentOS Stream 8, rename the directories $source = "centos-stream8"; $target = $os_type; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { source => $source, target => $target, }}); } # If 'source' is set, we need to rename. if ($source) { # While we're here, we will need to rename /var/www/html/rhel8 to /var/www/html/centos8, as # 'centos8' and '/var/lib/tftpboot/rhel8' as 'centos8'. foreach my $directory ("html", "tftpboot") { my $source_directory = $anvil->data->{path}{directories}{$directory}."/".$source; my $target_directory = $anvil->data->{path}{directories}{$directory}."/".$target; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { source_directory => $source_directory, target_directory => $target_directory, }}); # We check to see if the target directory already exists. If it does, the user may be # supporting multiple distros and we don't want to clobber that if ((-e $source_directory) && (not -e $target_directory)) { my $shell_call = $anvil->data->{path}{exe}{mv}." ".$source_directory." ".$target_directory; $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 $target_directory) { # Success! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0475", variables => { source => $source_directory, target => $target_directory, }}); } else { # Failed :( $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0116", variables => { source => $source_directory, target => $target_directory, }}); $anvil->nice_exit({exit_code => 12}); } } } } update_progress($anvil, 5, "log_0241"); return(0); }