anvil/tools/striker-manage-install-target
Digimer 911f7cfb6a This is another big commit with a lot of DB work. Getting closer to sorting out the frequent resyncs.
* Changes Database->connect to always use the first DB connected to, not the local one if that applies. This treats the first DB (sorted by UUID) as "primary" and the second (or third...) as more of a backup.
* Moved db_in_use and lock_request to use the 'states' table instead of the variables table. These are set and removed so often that it was messing up things with resync's when the data is transient anyway. Fixed multiple bugs with both to better set and clear properly.
* Created Database->read_state() to assist with the above changes.
* Updated Database->refresh_timestamp() to specifically check that the returned time stamp differs from the previously used one, looping until they differ if needed.
* Disabled striker-manage-install-target when called to update the repos, as the Install Target function doesn't work at this point.

Signed-off-by: Digimer <digimer@alteeve.ca>
2022-06-16 20:10:43 -04:00

2870 lines
96 KiB
Perl
Executable File

#!/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({check_for_resync => 1});
$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 => "apache", group => "apache" });
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_name>/<arch>/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);
}