Merge pull request #644 from ClusterLabs/upgrade-tools

Upgrade tools
main
Digimer 8 months ago committed by GitHub
commit e4bd962715
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      Anvil/Tools.pm
  2. 2
      Anvil/Tools/Database.pm
  3. 53
      Anvil/Tools/Network.pm
  4. 90
      Anvil/Tools/Remote.pm
  5. 1
      Anvil/Tools/Validate.pm
  6. 22
      man/anvil-join-anvil.8
  7. 35
      man/striker-purge-target.8
  8. 18
      scancore-agents/scan-server/scan-server
  9. 7
      share/words.xml
  10. 6
      tools/anvil-daemon
  11. 401
      tools/anvil-join-anvil
  12. 2
      tools/striker-update-cluster

@ -1061,6 +1061,7 @@ sub _set_paths
'autoindex.conf' => "/etc/httpd/conf.d/autoindex.conf",
'cib.xml' => "/var/lib/pacemaker/cib/cib.xml",
'corosync.conf' => "/etc/corosync/corosync.conf",
'corosync-authkey' => "/etc/corosync/authkey",
'dhcpd.conf' => "/etc/dhcp/dhcpd.conf",
'dnf.conf' => "/etc/dnf/dnf.conf",
'drbd-proxy.license' => "/etc/drbd-proxy.license",

@ -3687,7 +3687,7 @@ WHERE
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
my $count = ref($results) eq "ARRAY" ? @{$results} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results,
count => $count,

@ -3237,6 +3237,8 @@ This method loads and stores the same data as the C<< get_ips >> method, but doe
C<< Note >>: IP addresses that have been deleted will be marked so by C<< ip >> being set to C<< DELETED >>.
C<< Note >>: If there network has a device name, from network manager, that is the name used for C<< <iface_name> >>.
The loaded data will be stored as:
* C<< network::<host>::interface::<iface_name>::ip >> - If an IP address is set
@ -3343,6 +3345,7 @@ AND
my $bridge_name = "";
my $bond_name = "";
my $interface_name = "";
my $interface_device = "";
my $interface_mac = "";
my $network_interface_uuid = "";
if ($ip_address_on_type eq "interface")
@ -3381,11 +3384,24 @@ AND
if ($active_interface)
{
my $query = "SELECT network_interface_uuid FROM network_interfaces WHERE network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)." AND network_interface_name = ".$anvil->Database->quote($active_interface).";";
my $query = "
SELECT
network_interface_uuid
FROM
network_interfaces
WHERE
network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)."
AND
(
network_interface_name = ".$anvil->Database->quote($active_interface)."
OR
network_interface_device = ".$anvil->Database->quote($active_interface)."
)
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$network_interface_uuid = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_interface => $active_interface }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { network_interface_uuid => $network_interface_uuid }});
}
}
@ -3407,6 +3423,7 @@ AND
SELECT
network_interface_uuid,
network_interface_name,
network_interface_device,
network_interface_mac_address,
network_interface_speed,
network_interface_mtu,
@ -3433,13 +3450,24 @@ AND
next if not $count;
$interface_name = $results->[0]->[1];
$interface_mac = $results->[0]->[2];
$interface_device = $results->[0]->[2];
$interface_mac = $results->[0]->[3];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
interface_name => $interface_name,
interface_device => $interface_device,
interface_mac => $interface_mac,
}});
# If we've got an interface device, use that for the hash.
if ($interface_device)
{
$interface_name = $interface_device;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface_name => $interface_name }});
}
$anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid} = $results->[0]->[0];
$anvil->data->{network}{$host}{interface}{$interface_name}{interface_name} = $results->[0]->[1];
$anvil->data->{network}{$host}{interface}{$interface_name}{interface_device} = $results->[0]->[2];
$anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac;
$anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address;
$anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask} = $ip_address_subnet_mask;
@ -3447,16 +3475,18 @@ AND
$anvil->data->{network}{$host}{interface}{$interface_name}{gateway} = $ip_address_gateway;
$anvil->data->{network}{$host}{interface}{$interface_name}{dns} = $ip_address_dns;
$anvil->data->{network}{$host}{interface}{$interface_name}{type} = "interface";
$anvil->data->{network}{$host}{interface}{$interface_name}{speed} = $results->[0]->[3];
$anvil->data->{network}{$host}{interface}{$interface_name}{mtu} = $results->[0]->[4];
$anvil->data->{network}{$host}{interface}{$interface_name}{link_state} = $results->[0]->[5];
$anvil->data->{network}{$host}{interface}{$interface_name}{operational} = $results->[0]->[6];
$anvil->data->{network}{$host}{interface}{$interface_name}{duplex} = $results->[0]->[7];
$anvil->data->{network}{$host}{interface}{$interface_name}{medium} = $results->[0]->[8];
$anvil->data->{network}{$host}{interface}{$interface_name}{bond_uuid} = defined $results->[0]->[9] ? $results->[0]->[9] : "";
$anvil->data->{network}{$host}{interface}{$interface_name}{bridge_uuid} = defined $results->[0]->[10] ? $results->[0]->[10] : "";
$anvil->data->{network}{$host}{interface}{$interface_name}{speed} = $results->[0]->[4];
$anvil->data->{network}{$host}{interface}{$interface_name}{mtu} = $results->[0]->[5];
$anvil->data->{network}{$host}{interface}{$interface_name}{link_state} = $results->[0]->[6];
$anvil->data->{network}{$host}{interface}{$interface_name}{operational} = $results->[0]->[7];
$anvil->data->{network}{$host}{interface}{$interface_name}{duplex} = $results->[0]->[8];
$anvil->data->{network}{$host}{interface}{$interface_name}{medium} = $results->[0]->[9];
$anvil->data->{network}{$host}{interface}{$interface_name}{bond_uuid} = defined $results->[0]->[10] ? $results->[0]->[10] : "";
$anvil->data->{network}{$host}{interface}{$interface_name}{bridge_uuid} = defined $results->[0]->[11] ? $results->[0]->[11] : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"network::${host}::interface::${interface_name}::network_interface_uuid" => $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid},
"network::${host}::interface::${interface_name}::interface_name" => $anvil->data->{network}{$host}{interface}{$interface_name}{interface_name},
"network::${host}::interface::${interface_name}::interface_device" => $anvil->data->{network}{$host}{interface}{$interface_name}{interface_device},
"network::${host}::interface::${interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address},
"network::${host}::interface::${interface_name}::ip" => $anvil->data->{network}{$host}{interface}{$interface_name}{ip},
"network::${host}::interface::${interface_name}::subnet_mask" => $anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask},
@ -3578,7 +3608,6 @@ AND
}
=head2 manage_firewall
B<< NOTE >>: So far, only C<< check >> is implemented.

@ -130,7 +130,7 @@ sub add_target_to_known_hosts
}});
# Get the local user's home
my $users_home = $anvil->Get->users_home({debug => ($debug + 1), user => $user});
my $users_home = $anvil->Get->users_home({debug => 3, user => $user});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { users_home => $users_home }});
if (not $users_home)
{
@ -148,7 +148,7 @@ sub add_target_to_known_hosts
{
# Yup, see if the target is there already,
$known_machine = $anvil->Remote->_check_known_hosts_for_target({
debug => ($debug + 1),
debug => $debug,
target => $target,
port => $port,
known_hosts => $known_hosts,
@ -163,7 +163,7 @@ sub add_target_to_known_hosts
{
# We don't know about this machine yet, so scan it.
my $added = $anvil->Remote->_call_ssh_keyscan({
debug => ($debug + 1),
debug => $debug,
target => $target,
port => $port,
user => $user,
@ -172,7 +172,6 @@ sub add_target_to_known_hosts
if (not $added)
{
# Failed to add. :(
my $say_user = $user;
if (($say_user =~ /^\d+$/) && (getpwuid($user)))
{
@ -1219,6 +1218,13 @@ sub _check_known_hosts_for_target
return($known_machine)
}
### NOTE: This is called by ocf:alteeve:server, so there might not be a database available.
# Make sure we've loaded hosts.
if (($anvil->data->{sys}{database}{read_uuid}) && (not exists $anvil->data->{hosts}{host_uuid}))
{
$anvil->Database->get_hosts({debug => $debug});
}
# read it in and search.
my $body = $anvil->Storage->read_file({debug => $debug, file => $known_hosts});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { body => $body }});
@ -1227,12 +1233,84 @@ sub _check_known_hosts_for_target
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { line => $line }});
# This is wider scope now to catch hosts using other hashes than 'ssh-rsa'
if (($line =~ /$target /) or ($line =~ /\[$target\]:$port /))
if (($line =~ /$target (.*)$/) or ($line =~ /\[$target\]:$port (.*)$/))
{
# We already know this machine (or rather, we already have a fingerprint for
# this machine).
my $current_key = $anvil->Words->clean_spaces({string => $1});
my $is_host_name = $anvil->Validate->host_name({debug => 3, name => $target});
my $is_ip = $anvil->Validate->ipv4({debug => 3, ip => $target});
$known_machine = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { known_machine => $known_machine }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
current_key => $current_key,
is_host_name => $is_host_name,
is_ip => $is_ip,
known_machine => $known_machine,
}});
# If we're already planning to delete
next if $delete_if_found;
# If we don't have any DBs to read from, we're also done.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
'sys::database::read_uuid' => $anvil->data->{sys}{database}{read_uuid},
}});
next if not $anvil->data->{sys}{database}{read_uuid};
my $target_host_uuid = "";
my $target_host_name = "";
if ($is_ip)
{
($target_host_uuid, $target_host_name) = $anvil->Get->host_from_ip_address({debug => 2, ip_address => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
target_host_uuid => $target_host_uuid,
target_host_name => $target_host_name,
}});
}
elsif ($is_host_name)
{
$target_host_name = $target;
$target_host_uuid = $anvil->Get->host_uuid_from_name({debug => 3, host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
target_host_uuid => $target_host_uuid,
target_host_name => $target_host_name,
}});
}
if ($target_host_uuid)
{
# If we have a host_key and it doesn't match the one we just read, delete it.
my $host_key = $anvil->Words->clean_spaces({string => $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{host_key}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
's1:host_key' => $host_key,
's2:current_key' => $current_key,
}});
my ($current_key_type, $current_key_string) = ($current_key =~ /(.*?)\s+(.*)$/);
my ($host_key_type, $host_key_string) = ($host_key =~ /(.*?)\s+(.*)$/);
$host_key_string =~ s/\s.*$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
's1:current_key_type' => $current_key_type,
's2:host_key_type' => $host_key_type,
's3:current_key_string' => $current_key_string,
's4:host_key_string' => $host_key_string,
}});
# If the key type is the same, but the string is not, delete the old key.
if (($current_key_type eq $host_key_type) && ($current_key_string ne $host_key_string))
{
# It's changed, clear the old one.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0851", variables => {
known_hosts => $known_hosts,
target => $target,
key_type => $current_key_type,
old_key => $current_key_string,
new_key => $host_key_string,
}});
$delete_if_found = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { delete_if_found => $delete_if_found }});
}
}
}
}

@ -527,7 +527,6 @@ sub ip
my $ip = defined $parameter->{ip} ? $parameter->{ip} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { ip => $ip }});
my $ipv4 = $anvil->Validate->ipv4({ip => $ip, debug => $debug});
my $ipv6 = not $ipv4 ? $anvil->Validate->ipv6({ip => $ip, debug => $debug}) : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {

@ -1,6 +1,6 @@
.\" Manpage for the Anvil! node assembly tool
.\" Contact mkelly@alteeve.com to report issues, concerns or suggestions.
.TH anvil-join-anvil "8" "August 10 2023" "Anvil! Intelligent Availability™ Platform"
.TH anvil-join-anvil "8" "April 11 2024" "Anvil! Intelligent Availability™ Platform"
.SH NAME
anvil-join-anvil \- This tool assembles two subnodes into a node.
.SH SYNOPSIS
@ -9,10 +9,6 @@ anvil-join-anvil \- This tool assembles two subnodes into a node.
.SH DESCRIPTION
This program takes two subnodes and merges them into an Anvil! node. This can be two new subnodes, or an existing subnode with a replacement subnode after a subnode failure.
.TP
.B Note:
.TP
As if this time, this tool only runs from a job registered in the database. As such, the job must be recorded using the Striker web interface
.TP
.SH OPTIONS
.TP
\-?, \-h, \fB\-\-help\fR
@ -25,9 +21,25 @@ When logging, record sensitive data, like passwords.
Set the log level to 1, 2 or 3 respectively. Be aware that level 3 generates a significant amount of log data.
.SS "Commands:"
.TP
\fB\-\-as\-machine\fR <'node1' or 'node2'>
.TP
When using \fB\-\-rejoin\fR, this is the subnode role that this host will take.
.TP
.B NOTE:
.TP
The previous subnode that held this position will be purged! All data associated with the previous subnode will be deleted from the Anvil! database.
.TP
\fB\-\-job\-uuid\fR
.TP
This is the job UUID that will be run.
.TP
\fB\-\-manifest\fR <name or uuid>
.TP
If this is given, then this host will be joined to the manifest. This is required if \fB\-\-rejoin\fR is used.
.TP
\fB\-\-rejoin\fR
.TP
If this is set, the host will be (re)joined to an existing Anvil! node. This is used to bring this host into an Anvil! subnode, typically after a subnode failure / rebuild.
.IP
.SH AUTHOR
Written by Madison Kelly, Alteeve staff and the Anvil! project contributors.

@ -0,0 +1,35 @@
.\" Manpage for the Striker purge tool
.\" Contact mkelly@alteeve.com to report issues, concerns or suggestions.
.TH striker-purge-target "8" "April 11 2024" "Anvil! Intelligent Availability™ Platform"
.SH NAME
striker-purge-target \- This tool purges the target from all Anvil! databases.
.SH SYNOPSIS
.B striker-purge-target
\fI\,<command> \/\fR[\fI\,options\/\fR]
.SH DESCRIPTION
This tool can be used to purge a host or an Anvil! node (and both subnodes) from the Anvil! database.
.TP
.SH OPTIONS
.TP
\-?, \-h, \fB\-\-help\fR
Show this man page.
.TP
\fB\-\-log-secure\fR
When logging, record sensitive data, like passwords.
.TP
\-v, \-vv, \-vvv
Set the log level to 1, 2 or 3 respectively. Be aware that level 3 generates a significant amount of log data.
.SS "Commands:"
.TP
\fB\-\-anvil\fR
.TP
This purges the Anvil! node (and both subnodes) from the Anvil! database.
.TP
\fB\-\-host\fR <host name or UUID>
.TP
This purges a specific host from the Anvil! databases.
.IP
.SH AUTHOR
Written by Madison Kelly, Alteeve staff and the Anvil! project contributors.
.SH "REPORTING BUGS"
Report bugs to users@clusterlabs.org

@ -115,6 +115,24 @@ sub connect_to_virsh
target => $target,
});
# Also convert the target to an IP, if needed, and make sure that's added also.
my $is_ip = $anvil->Validate->ipv4({debug => 2, ip => $target});
my $check_ip = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { is_ip => $is_ip }});
if (not $is_ip)
{
$check_ip = $anvil->Convert->host_name_to_ip({debug => 2, host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { check_ip => $check_ip }});
if ($check_ip)
{
$anvil->Remote->test_access({
debug => 2,
target => $check_ip,
});
}
}
my $record_locator = "scan_server::qemu::".$target."::no_access";
my $is_local = $anvil->Network->is_local({host => $target });
$anvil->data->{qemu}{$target}{connection} = "";

@ -1387,7 +1387,7 @@ Failure! The return code: [#!variable!return_code!#] was received ('0' was expec
<key name="job_0161">* Please enter a number between 1 and #!variable!max_cores!#.</key>
<key name="job_0162">-=] Available cores / threads: [#!variable!cores!# / #!variable!threads!#]</key>
<key name="job_0163"> - Node #!variable!core!# CPU Model: [#!variable!model!#]</key>
<key name="job_0164">#!free!#</key>
<key name="job_0164">Node 1 hasn't started the cluster yet. We might be rejoining a cluster, so we'll try joining now.</key>
<key name="job_0165">RAM: ........... [#!variable!ram!#]</key>
<key name="job_0166">* Please enter a valid amount up to: [#!variable!ram_total!# / #!variable!ram_available!#].</key>
<key name="job_0167">-=] Available RAM: [#!variable!ram_available!#]
@ -1765,6 +1765,7 @@ Note: This is a permanent action! If you protect this server again later, a full
<key name="job_0482">Waiting for the peer's hostname to be: [#!variable!peer_name!#] in the database.</key>
<key name="job_0483">The peer's hostname is: [#!variable!peer_name!#], proceeding.</key>
<key name="job_0484">The peer's hostname is currently: [#!variable!old_peer_name!#], waiting for it to be changed to: [#!variable!peer_name!#]... Will check again shortly.</key>
<key name="job_0485">The corosync authkey file does not exist locally, but it does exist on the peer. Copying the file to here.</key>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>
@ -2734,6 +2735,10 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0848">The fence level exists and it's the same fence agent, nothing to do.</key>
<key name="log_0849">The fence level exists, but it's for the fence name: [#!variable!old_fence_name!#], agent: [#!variable!old_fence_agent!#], deleting it.</key>
<key name="log_0850">Creating the fence level: [#!variable!key_name!#] for the agent: [#!variable!fence_agent!#] using the device(s): [#!variable!devices!#]</key>
<key name="log_0851">The key in: [#!variable!known_hosts!#] for: [#!variable!target!#] of the type: [#!variable!key_type!#] has changed.
The key in the database has been updated, so we're going to swap the key. The change is;
old key: [#!variable!old_key!#]
new key: [#!variable!new_key!#]</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>

@ -1617,7 +1617,7 @@ sub run_jobs
my $job_status = $anvil->data->{jobs}{modified_date}{$modified_date}{job_uuid}{$job_uuid}{job_status};
my $started_seconds_ago = $job_picked_up_at ? (time - $job_picked_up_at) : 0;
my $updated_seconds_ago = $job_updated ? (time - $job_updated) : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => {
's01:job_uuid' => $job_uuid,
's02:job_command' => $job_command,
's03:short_command' => $short_command,
@ -1821,13 +1821,13 @@ sub run_jobs
{
# Is the job_uuid associated with this command done?
my $started_job_uuid = $anvil->data->{jobs_started}{$short_command};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { started_job_uuid => $started_job_uuid }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { started_job_uuid => $started_job_uuid }});
if (exists $anvil->data->{jobs}{running}{$started_job_uuid})
{
# If the previously running job and this job have the same
# UUID, it failed and needs to restart.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => {
job_uuid => $job_uuid,
started_job_uuid => $started_job_uuid,
"jobs::running::${started_job_uuid}::job_progress" => $anvil->data->{jobs}{running}{$started_job_uuid}{job_progress},

@ -38,7 +38,13 @@ my $anvil = Anvil::Tools->new();
# Read switches (target ([user@]host[:port]) and the file with the target's password. If the password is
# passed directly, it will be used. Otherwise, the password will be read from the database.
$anvil->Get->switches;
$anvil->Get->switches({list => [
"as-machine",
"join",
"manifest",
"rejoin",
], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->Database->connect();
@ -52,6 +58,13 @@ if (not $anvil->data->{sys}{database}{connections})
$anvil->nice_exit({exit_code => 1});
}
# If there isn't a job UUID, see if the user is asking to rejoin this subnode to a cluster.
if ($anvil->data->{switches}{rejoin})
{
process_rejoin($anvil);
$anvil->nice_exit({exit_code => 0});
}
# Get the job details
load_job($anvil);
@ -102,10 +115,293 @@ $anvil->Database->insert_or_update_variables({
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
# Rejoin this host to an existing subnode.
sub process_rejoin
{
my ($anvil) = @_;
print "-=] Rejoin this host: [".$anvil->Get->short_host_name."] as a subnode in an existing Anvil! node.\n";
my $host_uuid = $anvil->Get->host_uuid();
my $as_machine = $anvil->data->{switches}{'as-machines'};
my $manifest = $anvil->data->{switches}{manifest};
my $manifest_name = "";
my $manifest_uuid = "";
my $anvil_uuid = "";
my $rebuild = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:host_uuid' => $host_uuid,
's2:as_machine' => $as_machine,
's3:manifest' => $manifest,
}});
# Load data
$anvil->Database->get_hosts();
$anvil->Database->get_anvils();
$anvil->Database->get_manifests();
# In case we're being re-installed, see if we can find our own data.
my $old_manifest_uuid = "";
if (exists $anvil->data->{anvils}{host_uuid}{$host_uuid})
{
# Found it.
my $anvil_name = $anvil->data->{anvils}{host_uuid}{$host_uuid}{anvil_name};
$anvil_uuid = $anvil->data->{anvils}{host_uuid}{$host_uuid}{anvil_uuid};
my $old_role = $anvil->data->{anvils}{host_uuid}{$host_uuid}{role};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:anvil_name' => $anvil_name,
's2:anvil_uuid' => $anvil_uuid,
}});
print "This host used to be in the Anvil! [".$anvil_name."] as: [".$old_role."]\n";
if (exists $anvil->data->{manifests}{manifest_name}{$anvil_name})
{
# Found the manifest.
$old_manifest_uuid = $anvil->data->{manifests}{manifest_name}{$anvil_name}{manifest_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_manifest_uuid => $old_manifest_uuid }});
print "The manifest was found with the UUID: [".$old_manifest_uuid."]\n";
}
else
{
# Didn't find the manifest, something is wrong.
print "The manifest was NOT found (was it deleted?).\n";
print "- The manifes must be created for this host to rejoin the Anvil! node.\n";
print "- Alternatively, this host must be removed for the Anvil!\n";
print "[ Error ] - Unable to proceed at this time.\n";
$anvil->nice_exit({exit_code => 1});
}
# Does it match?
if (($manifest) && (($manifest ne $anvil_name) && ($manifest ne $old_manifest_uuid)))
{
# They asked for this machine to be joined to a different Anvil!.
print "[ Error ] - You asked to join the Anvil! node: [".$manifest."].\n";
print "[ Error ] - This host must be removed from: [".$anvil_name."] first.\n";
print "[ Error ] - Unable to proceed at this time.\n";
$anvil->nice_exit({exit_code => 1});
}
if (($as_machine) && ($as_machine ne $old_role))
{
print "[ Error ] - You asked to join the Anvil! node: [".$manifest."] as: [".$as_machine."]\n";
print "[ Error ] - This host was previously: [".$old_role."], so it needs to be that role again.\n";
print "[ Error ] - Alternatively, remove this host from that Anvil! and try again.\n";
print "[ Error ] - Unable to proceed at this time.\n";
$anvil->nice_exit({exit_code => 1});
}
$manifest = $anvil_name;
$as_machine = $old_role;
$rebuild = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:manifest' => $manifest,
's2:as_machine' => $as_machine,
's3:rebuild' => $rebuild,
}});
}
if ($manifest)
{
# Did we get a valid manifest?
if (exists $anvil->data->{manifests}{manifest_name}{$manifest})
{
$manifest_name = $manifest;
$manifest_uuid = $anvil->data->{manifests}{manifest_name}{$manifest}{manifest_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:manifest_name' => $manifest_name,
's2:manifest_uuid' => $manifest_uuid,
}});
}
elsif (exists $anvil->data->{manifests}{manifest_uuid}{$manifest})
{
$manifest_name = $anvil->data->{manifests}{manifest_uuid}{$manifest}{manifest_name};
$manifest_uuid = $manifest;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:manifest_name' => $manifest_name,
's2:manifest_uuid' => $manifest_uuid,
}});
}
else
{
print "[ Error ] - The manifest: [".$manifest."] was not found in the database.\n";
print "[ Error ] - Try again without this switch to see the available manifests.\n";
$anvil->nice_exit({exit_code => 1});
}
}
else
{
# Show the existing manifests.
my $count = keys %{$anvil->data->{manifests}{manifest_name}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
foreach my $this_manifest_name (sort {$a cmp $b} keys %{$anvil->data->{manifests}{manifest_name}})
{
my $this_manifest_uuid = $anvil->data->{manifests}{manifest_name}{$this_manifest_name}{manifest_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:this_manifest_name' => $this_manifest_name,
's2:this_manifest_uuid' => $this_manifest_uuid,
}});
if ($count == 1)
{
$manifest_name = $this_manifest_name;
$manifest_uuid = $this_manifest_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:manifest_name' => $manifest_name,
's2:manifest_uuid' => $manifest_uuid,
}});
}
else
{
print "- Manifest: [".$manifest_name."] (uuid: [".$manifest_uuid."])\n";
}
}
print "- Which manifest do you want to join this host to?\n";
my $answer = <STDIN>;
chomp($answer);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }});
# Did we get a valid manifest?
if (exists $anvil->data->{manifests}{manifest_name}{$answer})
{
$manifest_name = $answer;
$manifest_uuid = $anvil->data->{manifests}{manifest_name}{$answer}{manifest_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:manifest_name' => $manifest_name,
's2:manifest_uuid' => $manifest_uuid,
}});
}
elsif (exists $anvil->data->{manifests}{manifest_uuid}{$answer})
{
$manifest_name = $anvil->data->{manifests}{manifest_uuid}{$answer}{manifest_name};
$manifest_uuid = $answer;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:manifest_name' => $manifest_name,
's2:manifest_uuid' => $manifest_uuid,
}});
}
else
{
if ($answer)
{
print "[ Error ] - Your answer: [".$answer."] doesn't match a valid manifest.\n";
}
print "[ Error ] - Please try again (you can use --manifest <name or UUID> to avoind this prompt).\n";
$anvil->nice_exit({exit_code => 1});
}
}
if (not $as_machine)
{
print "- Will this node be 'node1' or 'node2'?\n";
my $answer = <STDIN>;
chomp($answer);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }});
if ($answer eq "node1")
{
$as_machine = "node1";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { as_machine => $as_machine }});
}
elsif ($answer eq "node2")
{
$as_machine = "node2";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { as_machine => $as_machine }});
}
else
{
print "[ Error ] - Please enter 'node1' or 'node2'.\n";
print "[ Error ] - Please try again (you can use '--as-manchine node{1,2}' to avoind this prompt).\n";
$anvil->nice_exit({exit_code => 1});
}
}
if (not $anvil_uuid)
{
$anvil_uuid = $anvil->data->{anvils}{anvil_name}{$manifest_name}{anvil_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }});
if (not $anvil_uuid)
{
print "[ Error ] - Failed to find an Anvil! UUID for the node: [".$manifest_name."].\n";
print "[ Error ] - Has the Anvil! been deleted from the database?\n";
print "[ Error ] - Unable to proceed.\n";
$anvil->nice_exit({exit_code => 1});
}
}
print "\n";
print "-=] Joining: [".$manifest_name."] as: [".$as_machine."] (Anvil! UUID: [".$anvil_uuid."]\n";
if ($rebuild)
{
print "[ Note ] - This is a rebuild, the previous data recorded by this host will be\n";
print " preserved.\n";
}
else
{
print "[ Warning ] - This will replace the old subnode, and all the previous data\n";
print " associated with it!\n";
print "[ Warning ] - Be certain the old host will NOT come back! If it does, it can\n";
print " cause confusion with the Anvil! node!\n";
}
if (($anvil->data->{switches}{y}) or ($anvil->data->{switches}{yes}))
{
print "[ Note ] - Confirmed by switch, proceeding.\b";
}
else
{
print $anvil->Words->string({key => "message_0021"})." ";
my $answer = <STDIN>;
chomp($answer);
if ($answer !~ /^y/i)
{
print $anvil->Words->string({key => "message_0022"})."\n";
$anvil->nice_exit({exit_code => 0});
}
}
# If this isn't a rebuild, purge the old host.
if (not $rebuild)
{
my $node_key = $as_machine eq "node1" ? "anvil_node1_host_uuid" : "anvil_node2_host_uuid";
my $old_host_uuid = $anvil->data->{anvils}{anvil_name}{$manifest_name}{$node_key} ? $anvil->data->{anvils}{anvil_name}{$manifest_name}{$node_key} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:node_key' => $node_key,
's2:old_host_uuid' => $old_host_uuid,
}});
if ($old_host_uuid)
{
print "[ Note ] - Purging the old host: [".$old_host_uuid."] from the database.\n";
print "[ Note ] - Please be patient!\n";
my $shell_call = $anvil->data->{path}{exe}{'striker-purge-target'}." ".$anvil->Log->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
}
}
# Register a job.
my ($job_uuid) = $anvil->Database->insert_or_update_jobs({
job_host_uuid => $host_uuid,
job_command => $anvil->data->{path}{exe}{'anvil-join-anvil'}.$anvil->Log->switches,
job_data => "as_machine=".$as_machine.",manifest_uuid=".$manifest_uuid.",anvil_uuid=".$anvil_uuid,
job_name => "join_anvil::".$as_machine,
job_title => "job_0072",
job_description => "job_0073",
job_progress => 0,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }});
print "Registering a job. This host should (re)join the Anvil! node shortly.\n";
return(0);
}
# Make sure the hosts file has entries for all nets for both subnodes
sub wait_for_etc_hosts
{
@ -559,6 +855,30 @@ sub configure_pacemaker
return(0);
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { 'path::configs::corosync-authkey' => $anvil->data->{path}{configs}{'corosync-authkey'} }});
if (not -f $anvil->data->{path}{configs}{'corosync-authkey'})
{
# In case we're rebuilding, see if the peer already has the '/etc/corosync/authkey' file.
my $corosync_authkey = $anvil->Storage->read_file({
file => $anvil->data->{path}{configs}{'corosync-authkey'},
target => $peer_host_name,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { corosync_authkey => $corosync_authkey }});
if ($corosync_authkey ne "!!error!!")
{
# Write the file out.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0485"});
$anvil->Storage->write_file({
debug => 2,
body => $corosync_authkey,
file => $anvil->data->{path}{configs}{'corosync-authkey'},
user => "root",
group => "root",
mode => "0400",
});
}
}
### Run on both nodes.
# Enable pcsd and start the pcsd daemon.
my ($return_code) = $anvil->System->enable_daemon({daemon => "pcsd.service"});
@ -605,6 +925,7 @@ sub configure_pacemaker
# If there is no corosync.conf, see if the peer has it. If so, copy it. If not, we'll initialize the
# cluster shortly.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { 'path::configs::corosync.conf' => $anvil->data->{path}{configs}{'corosync.conf'} }});
if (not -e $anvil->data->{path}{configs}{'corosync.conf'})
{
my $corosync_conf = $anvil->Storage->read_file({
@ -625,6 +946,13 @@ sub configure_pacemaker
mode => "0644",
});
}
# Restart corosync
my ($return_code) = $anvil->System->restart_daemon({daemon => "corosync.service"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { return_code => $return_code }});
$return_code = undef;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0095", variables => { daemon => "corosync.service" }});
update_progress($anvil, ($anvil->data->{job}{progress} += 2), "job_0095,!!daemon!corosync.service!!");
}
# Node 1 initializes, node 2 waits.
@ -637,6 +965,12 @@ sub configure_pacemaker
# We loop until the peer finishes or the peer's job hit's 100.
my $tried_starting = 0;
my $both_online = 0;
my $try_starting = time + 30;
my $delay = 5;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
delay => $delay,
try_starting => $try_starting,
}});
until($both_online)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -708,12 +1042,75 @@ sub configure_pacemaker
}
if (not $both_online)
{
my $delay = 5;
if (time > $try_starting)
{
# Try starting pacemaker, in case we're rebuilding.
update_progress($anvil, ($anvil->data->{job}{progress} += 2), "job_0164");
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0164"});
$try_starting += 60;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { try_starting => $try_starting }});
my $shell_call = $anvil->data->{path}{exe}{pcs}." cluster start";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0816", variables => { delay => $delay }});
sleep $delay;
}
}
}
### TODO: Left off here; parse 'pcs cluster status' and if there's 'Unable to authenticate', re-auth.
my $auth = 0;
my $shell_call = $anvil->data->{path}{exe}{pcs}." cluster status";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
my $in_pcsd = 0;
foreach my $line (split/\n/, $output)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($in_pcsd)
{
if ($line =~ /Unable to authenticate/i)
{
$auth = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { auth => $auth }});
}
}
if ($line =~ /PCSD Status/i)
{
$in_pcsd = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_pcsd => $in_pcsd }});
}
last if $auth;
}
if ($auth)
{
# Also, re-auth. We need to run this on both hosts.
my $shell_call = $anvil->data->{path}{exe}{pcs}." host auth ".$node1_host_name." ".$node2_host_name." -u hacluster -p ".$escaped_password;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({secure => 1, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
}
}
else
{
# We're node 1, proceed with cluster setup.

@ -260,7 +260,7 @@ sub update_nodes
"s1:peer::${short_host_name}::access::ip" => $anvil->data->{peer}{$short_host_name}{access}{ip},
"s2:peer::${short_host_name}::access::network" => $anvil->data->{peer}{$short_host_name}{access}{network},
}});
print "- Access found over the: [".$network_name."] networking using the IP: [".$target_ip."]\n";
print "- Access found over the: [".$network_name."] network using the IP: [".$target_ip."]\n";
last;
}
}

Loading…
Cancel
Save