#!/usr/bin/perl # # This renames a server (and the DRBD resources and LVs below it). Given the nature of this program, it runs # on the node directly, and SSH's into the peer(s) to update the DRBD config files and rename LVs. Normally, # this should run on Node 1. # # Exit codes; # 0 = Normal exit. # 1 = Any problem that causes an early exit. # # TODO: # use strict; use warnings; use Anvil::Tools; use Data::Dumper; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) { $running_directory =~ s/^\./$ENV{PWD}/; } # Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. $| = 1; 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->data->{switches}{'job-uuid'} = ""; $anvil->data->{switches}{'new-name'} = ""; $anvil->data->{switches}{'server'} = ""; $anvil->data->{switches}{'server-uuid'} = ""; $anvil->Get->switches; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'}, 'switches::new-name' => $anvil->data->{switches}{'new-name'}, 'switches::server' => $anvil->data->{switches}{'server'}, 'switches::server-uuid' => $anvil->data->{switches}{'server-uuid'}, }}); $anvil->Database->connect(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, secure => 0, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { # No databases, update the job, sleep for a bit and then exit. The daemon will pick it up and try # again after we exit. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0075"}); sleep 10; $anvil->nice_exit({exit_code => 1}); } # If we don't have a job UUID, try to find one. if (not $anvil->data->{switches}{'job-uuid'}) { # Load the job data. $anvil->data->{switches}{'job-uuid'} = $anvil->Job->get_job_uuid({program => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }}); } # If we still don't have a job-uuit, go into interactive mode. if ($anvil->data->{switches}{'job-uuid'}) { # Load the job data. $anvil->Job->clear(); $anvil->Job->get_job_details(); $anvil->Job->update_progress({ progress => 1, job_picked_up_by => $$, job_picked_up_at => time, message => "message_0234", }); # Pull out the job data. foreach my $line (split/\n/, $anvil->data->{jobs}{job_data}) { if ($line =~ /server=(.*?)$/) { $anvil->data->{switches}{'server'} = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::server' => $anvil->data->{switches}{'server'}, }}); } if ($line =~ /server-uuid=(.*?)$/) { $anvil->data->{switches}{'server-uuid'} = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::server-uuid' => $anvil->data->{switches}{'server-uuid'}, }}); } if ($line =~ /new-name=(.*?)$/) { $anvil->data->{switches}{'new-name'} = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::new-name' => $anvil->data->{switches}{'new-name'}, }}); } } } # Make sure we're in an Anvil! $anvil->data->{sys}{anvil_uuid} = $anvil->Cluster->get_anvil_uuid(); if (not $anvil->data->{sys}{anvil_uuid}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0260"}); $anvil->Job->update_progress({progress => 100, message => "error_0260"}); $anvil->nice_exit({exit_code => 1}); } # Now check that we have a server. If it's a server_uuid, read the server name. $anvil->Database->get_servers(); if ($anvil->data->{switches}{'server-uuid'}) { # Convert the server_uuid to a server_name. my $server_uuid = $anvil->data->{switches}{'server-uuid'}; if (not exists $anvil->data->{servers}{server_uuid}{$server_uuid}) { # Invalid server UUID. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0269", variables => { server_uuid => $anvil->data->{switches}{'server-uuid'}, }}); $anvil->Job->update_progress({progress => 100, message => "error_0269,!!server_uuid!".$anvil->data->{switches}{'server-uuid'}."!!"}); $anvil->nice_exit({exit_code => 1}); } $anvil->data->{switches}{'server'} = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::server' => $anvil->data->{switches}{'server'}, }}); } # Do we have a server name? if (not $anvil->data->{switches}{'server'}) { # Unable to proceed. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0276"}); $anvil->Job->update_progress({progress => 100, message => "error_0276"}); $anvil->nice_exit({exit_code => 1}); } # Do we have a new server name? if (not $anvil->data->{switches}{'new-name'}) { # Unable to proceed. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0279"}); $anvil->Job->update_progress({progress => 100, message => "error_0279"}); $anvil->nice_exit({exit_code => 1}); } # Make sure there are no spaces in the name $anvil->data->{switches}{'new-name'} =~ s/^\s+//; $anvil->data->{switches}{'new-name'} =~ s/\s$//; if ($anvil->data->{switches}{'new-name'} =~ /\s/) { # Bad new server name $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0280", variables => { new_name => $anvil->data->{switches}{'new-name'}, }}); $anvil->Job->update_progress({progress => 100, message => "error_0280,!!new_name!".$anvil->data->{switches}{'new-name'}."!!"}); $anvil->nice_exit({exit_code => 1}); } # Make sure the new name isn't in use. my $anvil_uuid = $anvil->data->{sys}{anvil_uuid}; my $new_server_name = $anvil->data->{switches}{'new-name'}; if (exists $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$new_server_name}) { # Conflicting server name $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0289", variables => { server => $new_server_name }}); $anvil->Job->update_progress({progress => 100, message => "error_0289,!!server!".$new_server_name."!!"}); $anvil->nice_exit({exit_code => 1}); } # We're going to need a server UUID. If we don't have it, find it from the current name. if (not $anvil->data->{switches}{'server-uuid'}) { # Convert the server name to a server_uuid. my $server_name = $anvil->data->{switches}{'server'}; if (not exists $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$server_name}) { # Invalid server UUID. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0277", variables => { server => $server_name }}); $anvil->Job->update_progress({progress => 100, message => "error_0277,!!server!".$server_name."!!"}); $anvil->nice_exit({exit_code => 1}); } $anvil->data->{switches}{'server-uuid'} = $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$server_name}{server_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::server-uuid' => $anvil->data->{switches}{'server-uuid'}, }}); } # Are we a node? $anvil->data->{sys}{host_type} = $anvil->Get->host_type(); if ($anvil->data->{sys}{host_type} ne "node") { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0264"}); $anvil->Job->update_progress({progress => 100, message => "error_0264"}); $anvil->nice_exit({exit_code => 1}); } # This is copied from anvil-boot-server, but it works here as well. We can't use 'pcs' without pacemaker # being up. wait_for_pacemaker($anvil); # Now we're ready. gather_server_data($anvil); # Verify that the server is off everywhere. verify_server_is_off($anvil); # Now start renaming things. rename_server($anvil); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0281"}); $anvil->Job->update_progress({progress => 100, message => "job_0281"}); $anvil->nice_exit({exit_code => 0}); ############################################################################################################# # Functions # ############################################################################################################# # This does the actual rename. It removes the resource from the cluster, makes sure the DRBD resource is down # on all machines, renames the XML definition file sub rename_server { my ($anvil) = @_; # Delete the server from pacemaker my $old_server_name = $anvil->data->{switches}{'server'}; my $new_server_name = $anvil->data->{switches}{'new-name'}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_server_name => $old_server_name, new_server_name => $new_server_name, }}); ### NOTE: From this point on, failing out is a bad idea. Anything going wrong here wil mean the ### server could become inaccessible. As such, any errors need to be very verbose. my $old_definition_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$old_server_name.".xml"; my $new_definition_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$new_server_name.".xml"; my $old_drbd_resource_file = $anvil->data->{path}{directories}{drbd_resources}."/".$old_server_name.".res"; my $new_drbd_resource_file = $anvil->data->{path}{directories}{drbd_resources}."/".$new_server_name.".res"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_definition_file => $old_definition_file, new_definition_file => $new_definition_file, old_drbd_resource_file => $old_drbd_resource_file, new_drbd_resource_file => $new_drbd_resource_file, }}); my $progress = 40; # Log into the peer(s) and rename the LV, the rename our own LV. foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{rename_server}{host}}) { my $peer_ip = $anvil->data->{rename_server}{host}{$host_name}{is_peer} ? $anvil->data->{rename_server}{host}{$host_name}{use_ip} : ""; my $password = $anvil->data->{rename_server}{host}{$host_name}{is_peer} ? $anvil->data->{rename_server}{host}{$host_name}{password} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ip => $peer_ip, password => $anvil->Log->is_secure($password), }}); # Read the old DRBD resource file. my $old_drbd_resource_body = $anvil->Storage->read_file({ cache => 0, file => $old_drbd_resource_file, force_read => 1, password => $password, target => $peer_ip, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_drbd_resource_body => $old_drbd_resource_body }}); if ($old_drbd_resource_body eq "!!error!!") { # Failed to read the file. Could we have already written the file? $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0282", variables => { file => $old_drbd_resource_file, host => $host_name, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "error_0282,!!file!".$old_drbd_resource_file."!!,!!host!".$host_name."!!"}); $old_drbd_resource_body = $anvil->Storage->read_file({ cache => 0, file => $new_drbd_resource_file, force_read => 1, password => $password, target => $peer_ip, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_drbd_resource_body => $old_drbd_resource_body }}); if ($old_drbd_resource_body eq "!!error!!") { # No luck there, either. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0282", variables => { file => $new_drbd_resource_file, host => $host_name, }}); $anvil->Job->update_progress({progress => 100, message => "error_0282,!!file!".$new_drbd_resource_file."!!,!!host!".$host_name."!!"}); $anvil->nice_exit({exit_code => 1}); } } # Read the old definition file. my $old_definition_body = $anvil->Storage->read_file({ cache => 0, file => $old_definition_file, force_read => 1, password => $password, target => $peer_ip, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_definition_body => $old_definition_body }}); if ($old_definition_body eq "!!error!!") { # Failed to read the file. Could we have already written the file? $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0282", variables => { file => $old_definition_file, host => $host_name, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "error_0282,!!file!".$old_definition_file."!!,!!host!".$host_name."!!"}); $old_definition_body = $anvil->Storage->read_file({ cache => 0, file => $new_definition_file, force_read => 1, password => $password, target => $peer_ip, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_definition_body => $old_definition_body }}); if ($old_definition_body eq "!!error!!") { # No luck there, either. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0282", variables => { file => $new_definition_file, host => $host_name, }}); $anvil->Job->update_progress({progress => 100, message => "error_0282,!!file!".$new_definition_file."!!,!!host!".$host_name."!!"}); $anvil->nice_exit({exit_code => 1}); } } # Loop through the volumes and update the new definition and resource file bodies. my $new_drbd_resource_body = $old_drbd_resource_body; my $new_definition_body = $old_definition_body; foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{rename_server}{host}{$host_name}{volume}}) { my $old_device_path = $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{old_device_path}; my $new_device_path = $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{new_device_path}; my $old_backing_disk = $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{old_backing_disk}; my $new_backing_disk = $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{new_backing_disk}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_device_path => $old_device_path, new_device_path => $new_device_path, old_backing_disk => $old_backing_disk, new_backing_disk => $new_backing_disk, }}); $new_drbd_resource_body =~ s/$old_device_path/$new_device_path/sg; $new_drbd_resource_body =~ s/$old_backing_disk/$new_backing_disk/sg; $new_drbd_resource_body =~ s/$old_server_name/$new_server_name/sg; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drbd_resource_body => $new_drbd_resource_body }}); $new_definition_body =~ s/$old_device_path/$new_device_path/sg; $new_definition_body =~ s/$old_backing_disk/$new_backing_disk/sg; $new_definition_body =~ s/$old_server_name/$new_server_name/sg; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_definition_body => $new_definition_body }}); # Rename the LV, if needed. my $shell_call = $anvil->data->{path}{exe}{lvdisplay}." ".$old_backing_disk; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); my $output = ""; my $return_code = ""; if ($anvil->data->{rename_server}{host}{$host_name}{is_peer}) { # Remote call ($output, my $error, $return_code) = $anvil->Remote->call({ debug => 2, shell_call => $shell_call, target => $peer_ip, password => $password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error => $error, output => $output, return_code => $return_code, }}); } else { ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); } if (not $return_code) { # Old LV exists, rename $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0305", variables => { host_name => $host_name, old_lv => $old_backing_disk, new_lv => $new_backing_disk, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "job_0305,!!host_name!".$host_name."!!,!!old_lv!".$old_backing_disk."!!,!!new_lv!".$new_backing_disk."!!"}); my $shell_call = $anvil->data->{path}{exe}{lvrename}." --autobackup y --yes ".$old_backing_disk." ".$new_backing_disk; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); # We don't verify the LV create here, we'll check it below. if ($anvil->data->{rename_server}{host}{$host_name}{is_peer}) { # Remote call my ($output, $error, $return_code) = $anvil->Remote->call({ debug => 2, shell_call => $shell_call, target => $peer_ip, password => $password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error => $error, output => $output, return_code => $return_code, }}); } else { my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); } } # Verify the new LV exists. $output = ""; $return_code = ""; $shell_call = $anvil->data->{path}{exe}{lvdisplay}." ".$new_backing_disk; if ($anvil->data->{rename_server}{host}{$host_name}{is_peer}) { # Remote call ($output, my $error, $return_code) = $anvil->Remote->call({ debug => 2, shell_call => $shell_call, target => $peer_ip, password => $password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error => $error, output => $output, return_code => $return_code, }}); } else { ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); } if ($return_code) { # Something went wrong. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0283", variables => { old_lv => $old_backing_disk, new_lv => $new_backing_disk, host_name => $host_name, }}); $anvil->Job->update_progress({progress => 100, message => "error_0283,!!host_name!".$host_name."!!,!!old_lv!".$old_backing_disk."!!,!!new_lv!".$new_backing_disk."!!"}); $anvil->nice_exit({exit_code => 1}); } else { # Success $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0306", variables => { host_name => $host_name, new_lv => $new_backing_disk, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "job_0306,!!host_name!".$host_name."!!,!!new_lv!".$new_backing_disk."!!"}); } } # Write out the new DRBD resource files. my ($problem) = $anvil->Storage->write_file({ debug => 2, backup => 1, overwrite => 1, body => $new_drbd_resource_body, file => $new_drbd_resource_file, group => "root", user => "root", mode => "0644", password => $password, target => $peer_ip, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); if ($problem) { # Failed. Details of why will already be in the logs. $anvil->Job->update_progress({progress => 100, message => "error_0287,!!target!".$host_name."!!,!!file!".$new_drbd_resource_file."!!"}); $anvil->nice_exit({exit_code => 1}); } else { # Success $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0307", variables => { host_name => $host_name, file => $new_drbd_resource_file, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "job_0307,!!host_name!".$host_name."!!,!!file!".$new_drbd_resource_file."!!"}); # Delete the old file. my ($problem) = $anvil->Storage->delete_file({ debug => 2, file => $old_drbd_resource_file, password => $password, target => $peer_ip, }); if ($problem) { # Details of why will be in the logs. $anvil->Job->update_progress({progress => 100, message => "error_0285,!!target!".$host_name."!!,!!file!".$old_drbd_resource_file."!!"}); $anvil->nice_exit({exit_code => 1}); } } # Write out the new definition files. undef $problem; ($problem) = $anvil->Storage->write_file({ debug => 2, backup => 1, overwrite => 1, body => $new_definition_body, file => $new_definition_file, group => "root", user => "root", mode => "0644", password => $password, target => $peer_ip, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); if ($problem) { # Failed. Details of why will already be in the logs. $anvil->Job->update_progress({progress => 100, message => "error_0287,!!target!".$host_name."!!,!!file!".$new_definition_file."!!"}); $anvil->nice_exit({exit_code => 1}); } else { # Success $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0307", variables => { host_name => $host_name, file => $new_definition_file, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "job_0307,!!host_name!".$host_name."!!,!!file!".$new_definition_file."!!"}); # Delete the old file. my ($problem) = $anvil->Storage->delete_file({ debug => 2, file => $old_definition_file, password => $password, target => $peer_ip, }); if ($problem) { # Details of why will be in the logs. $anvil->Job->update_progress({progress => 100, message => "error_0285,!!target!".$host_name."!!,!!file!".$old_definition_file."!!"}); $anvil->nice_exit({exit_code => 1}); } } # Make a 'virsh undefine ' just in case the old name is still defined. We don't care # if this succeeds or fails as the server should not be defined anyway. my $shell_call = $anvil->data->{path}{exe}{virsh}." undefine ".$old_server_name; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); my $output = ""; my $return_code = ""; if ($anvil->data->{rename_server}{host}{$host_name}{is_peer}) { # Remote call $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0310", variables => { host_name => $host_name, server_name => $old_server_name, }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "job_0310,!!host_name!".$host_name."!!,!!server_name!".$old_server_name."!!"}); ($output, my $error, $return_code) = $anvil->Remote->call({ debug => 2, shell_call => $shell_call, target => $peer_ip, password => $password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error => $error, output => $output, return_code => $return_code, }}); } else { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0309", variables => { server_name => $old_server_name }}); $progress += 3 if $progress < 90; $anvil->Job->update_progress({progress => $progress, message => "job_0309,!!server_name!".$old_server_name."!!"}); ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); } } # Delete the old server from pacemaker. my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{pcs}." resource delete ".$old_server_name}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); if (not $return_code) { # Success! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0587", variables => { server_name => $old_server_name }}); $anvil->Job->update_progress({progress => 90, message => "job_0304,!!server_name!".$old_server_name."!!"}); } else { # Unexpected return code, bail out. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => 'err', key => "error_0226", variables => { server_name => $old_server_name, return_code => $return_code, output => $output, }}); $anvil->Job->update_progress({progress => 100, message => "job_0304,!!server_name!".$old_server_name."!!,!!return_code!".$return_code."!!,!!output!".$output."!!"}); $anvil->nice_exit({exit_code => 1}); } # Add the server back to pacemaker my $resource_command = $anvil->data->{path}{exe}{pcs}." resource create ".$new_server_name." ocf:alteeve:server name=\"".$new_server_name."\" meta allow-migrate=\"true\" target-role=\"stopped\" op monitor interval=\"60\" start timeout=\"300\" on-fail=\"block\" stop timeout=\"86400\" on-fail=\"block\" migrate_to timeout=\"86400\""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { resource_command => $resource_command }}); $output = ""; $return_code = ""; ($output, $return_code) = $anvil->System->call({shell_call => $resource_command}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); if ($return_code) { # Failed. $anvil->Job->update_progress({progress => 100, message => "error_0288,!!server_name!".$new_server_name."!!,!!return_code!".$return_code."!!,!!output!".$output."!!"}); $anvil->nice_exit({exit_code => 1}); } else { # Success $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0308", variables => { server_name => $new_server_name }}); $anvil->Job->update_progress({progress => 95, message => "job_0308,!!server_name!".$old_server_name."!!"}); } # Rename the server in the database. my $query = " UPDATE servers SET server_name = ".$anvil->Database->quote($new_server_name).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE server_uuid = ".$anvil->Database->quote($anvil->data->{switches}{'server-uuid'})." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0311", variables => { server_name => $new_server_name }}); $anvil->Job->update_progress({progress => 99, message => "job_0311,!!server_name!".$old_server_name."!!"}); return(0); } # Calls virsh locally and on peer(s) to ensure that the server is not running. sub verify_server_is_off { my ($anvil) = @_; # Is the server running from pacemaker's perspective? my $waiting = 1; my $old_server_name = $anvil->data->{switches}{'server'}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_server_name => $old_server_name }}); while ($waiting) { $waiting = 0; $anvil->Cluster->parse_cib({debug => 2}); if (not exists $anvil->data->{cib}{parsed}{data}{server}{$old_server_name}) { # Server wasn't found in the cluster config. Wat?! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0281"}); $anvil->Job->update_progress({progress => 100, message => "error_0281"}); $anvil->nice_exit({exit_code => 1}); } my $status = $anvil->data->{cib}{parsed}{data}{server}{$old_server_name}{status}; my $host_name = $anvil->data->{cib}{parsed}{data}{server}{$old_server_name}{host_name}; my $role = $anvil->data->{cib}{parsed}{data}{server}{$old_server_name}{role}; my $active = $anvil->data->{cib}{parsed}{data}{server}{$old_server_name}{active}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 's1:old_server_name' => $old_server_name, 's2:status' => $status, 's2:host_name' => $host_name, 's4:role' => $role, 's5:active' => $active, }}); if ($status ne "off") { $waiting = 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0299", variables => { server => $old_server_name, status => $status, }}); $anvil->Job->update_progress({progress => 22, message => "job_0299,!!server!".$old_server_name."!!,!!status!".$status."!!"}); sleep 10; } } # Now check virsh. $waiting = 1; while ($waiting) { $waiting = 0; $anvil->Server->find({refresh => 1}); foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{rename_server}{host}}) { next if $anvil->data->{rename_server}{host}{$host_name}{is_peer}; my $peer_ip = $anvil->data->{rename_server}{host}{$host_name}{use_ip}; my $password = $anvil->data->{rename_server}{host}{$host_name}{password}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ip => $peer_ip, password => $anvil->Log->is_secure($password), }}); $anvil->Server->find({ refresh => 0, target => $peer_ip, password => $password, }); } if ((exists $anvil->data->{server}{location}{$old_server_name}) && ($anvil->data->{server}{location}{$old_server_name}{status} ne "shut off")) { my $status = $anvil->data->{server}{location}{$old_server_name}{status}; my $host = $anvil->data->{server}{location}{$old_server_name}{host_name}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0299", variables => { server => $old_server_name, status => $status, host => $host, }}); $anvil->Job->update_progress({progress => 26, message => "job_0299,!!server!".$old_server_name."!!,!!status!".$status."!!,!!host!".$host."!!"}); sleep 10; } } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0300", variables => { server => $old_server_name }}); $anvil->Job->update_progress({progress => 28, message => "job_0300,!!server!".$old_server_name."!!"}); # Now make sure the DRBD resource is down on all machines. my $short_host_name = $anvil->Get->short_host_name(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { short_host_name => $short_host_name }}); # Wait until the resource is not sync'ing (if it is at all). $waiting = 1; while ($waiting) { # (Re)fresh my view of the storage. $waiting = 0; $anvil->DRBD->get_status({debug => 2}); foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{rename_server}{host}}) { next if not $anvil->data->{rename_server}{host}{$host_name}{is_peer}; my $peer_ip = $anvil->data->{rename_server}{host}{$host_name}{use_ip}; my $password = $anvil->data->{rename_server}{host}{$host_name}{password}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ip => $peer_ip, password => $anvil->Log->is_secure($password), }}); $anvil->DRBD->get_status({ debug => 2, target => $peer_ip, password => $password, }); } # Now check to see if anything is sync'ing. foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 's1:old_server_name' => $old_server_name, 's2:host_name' => $host_name, }}); next if not exists $anvil->data->{drbd}{status}{$host_name}{resource}{$old_server_name}; foreach my $peer_name (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$host_name}{resource}{$old_server_name}{connection}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_name => $peer_name }}); foreach my $volume (sort {$a cmp $b} %{$anvil->data->{drbd}{status}{$host_name}{resource}{$old_server_name}{connection}{$peer_name}{volume}}) { next if not exists $anvil->data->{drbd}{status}{$host_name}{resource}{$old_server_name}{connection}{$peer_name}{volume}{$volume}{'replication-state'}; my $replication_state = $anvil->data->{drbd}{status}{$host_name}{resource}{$old_server_name}{connection}{$peer_name}{volume}{$volume}{'replication-state'}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { volume => $volume, replication_state => $replication_state, }}); if ($replication_state =~ /Sync/i) { $waiting = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0301", variables => { source_host => $host_name, peer_host => $peer_name, resource => $old_server_name, volume => $volume, replication_state => $replication_state, }}); $anvil->Job->update_progress({progress => 30, message => "job_0301,!!source_host!".$host_name."!!,!!peer_host!".$peer_name."!!,!!resource!".$old_server_name."!!,!!volume!".$volume."!!,!!replication_state!".$replication_state."!!"}); } } } } if ($waiting) { sleep 10; } } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0302"}); $anvil->Job->update_progress({progress => 33, message => "job_0302"}); # Shut down the peers first foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{rename_server}{host}}) { next if not $anvil->data->{rename_server}{host}{$host_name}{is_peer}; my $peer_ip = $anvil->data->{rename_server}{host}{$host_name}{use_ip}; my $password = $anvil->data->{rename_server}{host}{$host_name}{password}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ip => $peer_ip, password => $anvil->Log->is_secure($password), }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0303", variables => { peer => $host_name, resource => $old_server_name, ip => $peer_ip, }}); $anvil->Job->update_progress({progress => 35, message => "job_0303,!!peer!".$host_name."!!,!!resource!".$old_server_name."!!,!!ip!".$peer_ip."!!"}); $anvil->DRBD->manage_resource({ debug => 2, resource => $old_server_name, task => "down", target => $peer_ip, password => $password, }); } $anvil->DRBD->manage_resource({ debug => 2, resource => $old_server_name, task => "down", }); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0304"}); $anvil->Job->update_progress({progress => 38, message => "job_0304"}); return(0); } # This figures out the names of the definition and DRBD resource files, LV names and other details that will # be needed to rename the server. This will abort if anything seems wrong. sub gather_server_data { my ($anvil) = @_; my $old_server_name = $anvil->data->{switches}{'server'}; my $new_server_name = $anvil->data->{switches}{'new-name'}; # Parse the DRBD resource file to see if we have a DR target for this server. $anvil->DRBD->gather_data({debug => 2}); $anvil->Database->get_hosts(); # We'll store our name for finding matches later. my $local_drbd_node_name = ""; foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$old_server_name}{host}}) { my $host_uuid = $anvil->Get->host_uuid_from_name({host_name => $host_name}); my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; my $peer = $host_uuid eq $anvil->Get->host_uuid ? 0 : 1; $anvil->data->{rename_server}{host}{$host_name}{host_uuid} = $host_uuid; $anvil->data->{rename_server}{host}{$host_name}{host_type} = $host_type; $anvil->data->{rename_server}{host}{$host_name}{is_peer} = $peer; $anvil->data->{rename_server}{host}{$host_name}{use_ip} = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "rename_server::host::${host_name}::host_uuid" => $anvil->data->{rename_server}{host}{$host_name}{host_uuid}, "rename_server::host::${host_name}::host_type" => $anvil->data->{rename_server}{host}{$host_name}{host_type}, "rename_server::host::${host_name}::is_peer" => $anvil->data->{rename_server}{host}{$host_name}{is_peer}, }}); if (not $peer) { $local_drbd_node_name = $host_name; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_drbd_node_name => $local_drbd_node_name }}); $anvil->Network->load_ips({ host => $local_drbd_node_name, host_uuid => $host_uuid, }); } foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$old_server_name}{host}{$host_name}{volume}}) { my $old_device_path = $anvil->data->{new}{resource}{$old_server_name}{host}{$host_name}{volume}{$volume}{device_path}; my $new_device_path = $old_device_path; $new_device_path =~ s/$old_server_name/$new_server_name/g; my $old_backing_disk = $anvil->data->{new}{resource}{$old_server_name}{host}{$host_name}{volume}{$volume}{backing_disk}; my $new_backing_disk = $old_backing_disk; $new_backing_disk =~ s/$old_server_name/$new_server_name/g; $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{old_device_path} = $old_device_path; $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{new_device_path} = $new_device_path; $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{old_backing_disk} = $old_backing_disk; $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{new_backing_disk} = $new_backing_disk; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "rename_server::host::${host_name}::volume::${volume}::old_device_path" => $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{old_device_path}, "rename_server::host::${host_name}::volume::${volume}::new_device_path" => $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{new_device_path}, "rename_server::host::${host_name}::volume::${volume}::old_backing_disk" => $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{old_backing_disk}, "rename_server::host::${host_name}::volume::${volume}::new_backing_disk" => $anvil->data->{rename_server}{host}{$host_name}{volume}{$volume}{new_backing_disk}, }}); } } # Make sure we can talk to peers. my $waiting = 1; while($waiting) { $waiting = 0; foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{rename_server}{host}}) { next if not $anvil->data->{rename_server}{host}{$host_name}{is_peer}; my $host_uuid = $anvil->data->{rename_server}{host}{$host_name}{host_uuid}; my $anvil_uuid = $anvil->data->{sys}{anvil_uuid}; my $password = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_password}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_name => $host_name, host_uuid => $host_uuid, anvil_uuid => $anvil_uuid, password => $anvil->Log->is_secure($password), }}); $anvil->Network->load_ips({ host => $host_name, host_uuid => $host_uuid, }); my $peer_ip = ""; my ($match) = $anvil->Network->find_matches({ debug => 2, first => $local_drbd_node_name, second => $host_name, }); my $access = 0; if ($match) { # Yup! foreach my $interface (sort {$a cmp $b} keys %{$match->{$host_name}}) { my $peer_ip = $match->{$host_name}{$interface}{ip}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ip => $peer_ip }}); $access = $anvil->Remote->test_access({ target => $peer_ip, password => $password, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }}); if ($access) { $anvil->data->{rename_server}{host}{$host_name}{use_ip} = $peer_ip; $anvil->data->{rename_server}{host}{$host_name}{password} = $password; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "rename_server::host::${host_name}::use_ip" => $anvil->data->{rename_server}{host}{$host_name}{use_ip}, }}); last; } } } if (not $access) { # Unable to reach this peer, so we need to keep waiting. $waiting = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0297", variables => { host_name => $host_name }}); $anvil->Job->update_progress({progress => 18, message => "job_0297,!!host_name!".$host_name."!!"}); } } if ($waiting) { sleep 10; } } # All peer(s) are ready! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0298"}); $anvil->Job->update_progress({progress => 20, message => "job_0298"}); return(0); } sub wait_for_pacemaker { my ($anvil) = @_; # We need to rename the server in the cluster, and we need both nodes up to do it. my $waiting = 1; while($waiting) { my $problem = $anvil->Cluster->parse_cib({debug => 2}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); if (not $problem) { my $local_name = $anvil->data->{cib}{parsed}{'local'}{name}; my $peer_name = $anvil->data->{cib}{parsed}{peer}{name}; my $local_ready = $anvil->data->{cib}{parsed}{data}{node}{$local_name}{node_state}{ready}; my $peer_ready = $anvil->data->{cib}{parsed}{data}{node}{$local_name}{node_state}{ready}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_name => $local_name, peer_name => $peer_name, local_ready => $local_ready, peer_ready => $peer_ready, }}); if (($local_ready) && ($peer_ready)) { # We're good. $waiting = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0295"}); $anvil->Job->update_progress({progress => 15, message => "job_0295"}); } else { # One or both nods are not online yet. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0296", variables => { local_name => $local_name, peer_name => $peer_name, local_ready => $local_ready, peer_ready => $peer_ready, }}); $anvil->Job->update_progress({progress => 10, message => "job_0296,!!local_name!".$local_name."!!,!!peer_name!".$peer_name."!!,!!local_ready!".$local_ready."!!,!!peer_ready!".$peer_ready."!!"}); } } else { # Cluster hasn't started. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "job_0277"}); $anvil->Job->update_progress({progress => 5, message => "job_0277"}); } if ($waiting) { sleep 10; } } return(0); }