From 3346d31194f9563b091ed7f9a0c0f9c9a839ebb6 Mon Sep 17 00:00:00 2001 From: Digimer Date: Tue, 7 Dec 2021 20:03:39 -0500 Subject: [PATCH] * Created Get->kernel_release() that returns the current kernel release (version) in use on the host or on a remote system. * Created DRBD->_initialize_drbd() to makes sure the DRBD kernel module can load and tries to build the module, if necessary. This is meant to provide support for clients that can't access needed internet resource (or the internet at all). Signed-off-by: Digimer --- Anvil/Tools.pm | 2 + Anvil/Tools/DRBD.pm | 113 ++++++++++++++++++++++++++++++++++++++++++ Anvil/Tools/Get.pm | 89 +++++++++++++++++++++++++++++++++ Anvil/Tools/System.pm | 1 + share/words.xml | 6 +++ tools/anvil-daemon | 6 ++- 6 files changed, 216 insertions(+), 1 deletion(-) diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 16d4ad8c..27f7540b 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -1102,6 +1102,7 @@ sub _set_paths units => "/usr/lib/systemd/system", }, exe => { + akmods => "/usr/sbin/akmods", 'alteeve-repo-setup' => "/usr/sbin/alteeve-repo-setup", 'anvil-boot-server' => "/usr/sbin/anvil-boot-server", 'anvil-change-password' => "/usr/sbin/anvil-change-password", @@ -1197,6 +1198,7 @@ sub _set_paths md5sum => "/usr/bin/md5sum", 'mkdir' => "/usr/bin/mkdir", modifyrepo_c => "/usr/bin/modifyrepo_c", + modprobe => "/usr/sbin/modprobe", mv => "/usr/bin/mv", nmap => "/usr/bin/nmap", nmcli => "/bin/nmcli", diff --git a/Anvil/Tools/DRBD.pm b/Anvil/Tools/DRBD.pm index 263579b9..247be8e4 100644 --- a/Anvil/Tools/DRBD.pm +++ b/Anvil/Tools/DRBD.pm @@ -26,6 +26,7 @@ my $THIS_FILE = "DRBD.pm"; # reload_defaults # resource_uuid # update_global_common +# _initialize_kmod # =pod @@ -1835,6 +1836,7 @@ sub get_status return(0); } + =head2 manage_resource This takes a task, C<< up >>, C<< down >>, C<< primary >>, or C<< secondary >> and a resource name and acts on the request. @@ -3029,3 +3031,114 @@ sub update_global_common ############################################################################################################# # Private functions # ############################################################################################################# + +=head2 _initialize_kmod + +This checks to see if the C<< drbd >> kernel module can load. If not, a check is made to see if an RPM that matches the kernel exists. If so, it is installed. If not, C<< akmods >> is asked to build and install the drbd kernel module. + +Returns C<< 0 >> is the module loads or is already loaded. C<< !!error!! >> if not. + +=cut +sub _initialize_kmod +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "DRBD->_initialize_kmod()" }}); + + my $kernel_release = $anvil->Get->kernel_release({debug => $debug}); + my $shell_call = $anvil->data->{path}{exe}{modprobe}." drbd"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + kernel_release => $kernel_release, + shell_call => $shell_call, + }}); + + my ($output, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + output => $output, + return_code => $return_code, + }}); + + if (not $return_code) + { + # Loaded fine + return(0); + } + else + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0676"}); + my $install = 0; + my $shell_call = $anvil->data->{path}{exe}{dnf}." -q search kmod-drbd-".$kernel_release; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); + + my ($output, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + output => $output, + return_code => $return_code, + }}); + foreach my $line (split/\n/, $output) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }}); + if ($line =~ /Name Exactly/) + { + # We can install. + $install = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { install => $install }}); + last; + } + } + + # Install or build? + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { install => $install }}); + if ($install) + { + ### TODO: Should this be a background process? + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0677"}); + my $shell_call = $anvil->data->{path}{exe}{dnf}." -y install kmod-drbd-".$kernel_release; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); + + my ($output, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + output => $output, + return_code => $return_code, + }}); + } + else + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0678"}); + my $shell_call = $anvil->data->{path}{exe}{akmods}." --force"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); + + my ($output, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + output => $output, + return_code => $return_code, + }}); + } + + # In either case, try again. + $output = undef; + $return_code = undef; + $shell_call = $anvil->data->{path}{exe}{modprobe}." drbd"; + ($output, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + output => $output, + return_code => $return_code, + }}); + + if (not $return_code) + { + # Loaded fine + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0679"}); + return(0); + } + else + { + # Failed + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "warning_0132"}); + } + } + + return('!!error!!'); +} diff --git a/Anvil/Tools/Get.pm b/Anvil/Tools/Get.pm index 19564b66..187eef77 100644 --- a/Anvil/Tools/Get.pm +++ b/Anvil/Tools/Get.pm @@ -31,6 +31,7 @@ my $THIS_FILE = "Get.pm"; # host_uuid_from_name # host_type # host_uuid +# kernel_release # md5sum # os_type # server_uuid_from_name @@ -1777,6 +1778,94 @@ sub host_uuid return($anvil->{HOST}{UUID}); } + +=head2 kernel_release + +This returns the kernel release (same output as C<>) on the local or remote host. If there is a problem, C<< !!error!! >> is returned. + +Parameters; + +=head3 password (optional) + +This is the password to use when connecting to a remote machine. If not set, but C<< target >> is, an attempt to connect without a password will be made. + +=head3 port (optional) + +This is the TCP port to use when connecting to a remote machine. If not set, but C<< target >> is, C<< 22 >> will be used. + +=head3 remote_user (optional, default root) + +If C<< target >> is set, this will be the user we connect to the remote machine as. + +=head3 target (optional) + +This is the IP or host name of the machine to read the kernel release. If this is not set, the local system's kernel release is checked. + +=cut +sub kernel_release +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "System->kernel_release()" }}); + + my $password = defined $parameter->{password} ? $parameter->{password} : ""; + my $port = defined $parameter->{port} ? $parameter->{port} : ""; + my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root"; + my $target = defined $parameter->{target} ? $parameter->{target} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + target => $target, + port => $port, + remote_user => $remote_user, + password => $anvil->Log->is_secure($password), + }}); + + my $kernel_release = ""; + my $return_code = ""; + my $shell_call = $anvil->data->{path}{exe}{uname}." --kernel-release"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); + if ($anvil->Network->is_local({host => $target})) + { + # Local call + ($kernel_release, $return_code) = $anvil->System->call({debug => $debug, shell_call => $shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + kernel_release => $kernel_release, + return_code => $return_code, + }}); + } + else + { + # Remote call + ($kernel_release, my $error, $return_code) = $anvil->Remote->call({ + debug => $debug, + shell_call => $shell_call, + target => $target, + port => $port, + password => $password, + remote_user => $remote_user, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + kernel_release => $kernel_release, + error => $error, + return_code => $return_code, + }}); + + if ($return_code) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "err", key => "error_0356", variables => { + target => $target, + output => $kernel_release, + return_code => $return_code, + }}); + $kernel_release = "!!error!!"; + } + } + + return($kernel_release); +} + + =head2 md5sum This returns the C<< md5sum >> of a given file. diff --git a/Anvil/Tools/System.pm b/Anvil/Tools/System.pm index e4483c6c..23004517 100644 --- a/Anvil/Tools/System.pm +++ b/Anvil/Tools/System.pm @@ -3152,6 +3152,7 @@ sub host_name return($host_name, $descriptive); } + =head2 maintenance_mode This sets, clears or checks if the local system is in maintenance mode. Any system in maintenance mode will not be used by normal Anvil! tasks. diff --git a/share/words.xml b/share/words.xml index ba99971f..54e38203 100644 --- a/share/words.xml +++ b/share/words.xml @@ -501,6 +501,7 @@ The output, if any, was; ==== Failed to load the database file: [#!variable!file!#]. Deleting it so it's not considered in the next load attempt. + Failed to read the kernel release on the host: [#!variable!target!#]. The return code was: [#!variable!return_code!#] (expected '0') and the release output, if any, was: [#!variable!output!#]. @@ -2066,6 +2067,10 @@ The file: [#!variable!file!#] needs to be updated. The difference is: The host: [#!variable!host_name!#] has good power and temperature readings. Booting it back up now. The resync has completed in: [#!variable!took!#] second(s). Log->secure' is not set. ]]> + [ Note ] - The DRBD kernel module failed to load. It is possible the kernel was updated. We will check to see if we can install a pre-built RPM, or if we need to build one ourselves. + Found an installable DRBD kernel module RPM that matches the current kernel. Installing it now. + [ Note ] - We need to build the DRBD kernel module. This can take a few minutes, please be patient! Use 'journalctl -f' to monitor the build process. + Successfully built and installed the new DRBD kernel module! The host name: [#!variable!target!#] does not resolve to an IP address. @@ -3091,6 +3096,7 @@ We will sleep a bit and try again. [ Warning ] - The storage group: [#!variable!storage_group_name!#] had the host: [#!variable!host_name!#] as a member. This host is not a member (anymore?) of the Anvil!: [#!variable!anvil_name!#]. Removing it from the storage group now. [ Warning ] - The postgresql server is not installed yet. Sleeping for a bit, then will check again. + [ Warning ] - Failed to build or install the DRBD kernel module! It is very likely that this machine will be able to run any servers until this is fixed. diff --git a/tools/anvil-daemon b/tools/anvil-daemon index 2db260ce..8b1aa0b0 100755 --- a/tools/anvil-daemon +++ b/tools/anvil-daemon @@ -1126,11 +1126,12 @@ sub handle_special_cases { my ($anvil) = @_; - # RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 my $host_type = $anvil->Get->host_type(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); if ($host_type ne "striker") { + ### TODO: Test that this is fixed. The bug is now ERRATA + # RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 # We're a node or DR host. We need to touch this file. my $work_around_file = "/etc/qemu/firmware/50-edk2-ovmf-cc.json"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { work_around_file => $work_around_file }}); @@ -1147,6 +1148,9 @@ sub handle_special_cases group => "root", }); } + + # Make sure DRBD compiled after a kernel upgrade. + $anvil->DRBD->_initialize_kmod({debug => 2}); } return(0);