You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2831 lines
127 KiB
2831 lines
127 KiB
#!/usr/bin/perl |
|
# |
|
# This program will manage servers; Changing RAM, CPU cores, Growing virtual disks, adding virtual disks, |
|
# inserting and ejecting ISO images into virtual optical media. |
|
# |
|
# Exit codes; |
|
# 0 = Normal exit. |
|
# 1 = No database connection. |
|
# |
|
# TODO: |
|
# |
|
# USAGE: |
|
# - Show |
|
# - anvil-manage-server-storage --server srv01-fs37 |
|
# - ISO |
|
# - anvil-manage-server-storage --server srv01-fs37 --optical sda --insert /mnt/shared/files/CentOS-5.11-x86_64-bin-DVD-1of2.iso |
|
# - anvil-manage-server-storage --server srv01-fs37 --optical sda --eject |
|
# - Disk |
|
# - anvil-manage-server-storage --server srv01-fs37 --disk vdb --grow {+10GiB,150GiB} |
|
# |
|
|
|
use strict; |
|
use warnings; |
|
use Anvil::Tools; |
|
require POSIX; |
|
use Term::Cap; |
|
use Text::Diff; |
|
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(); |
|
|
|
### TODO: Remove this before final release |
|
$anvil->Log->level({set => 2}); |
|
$anvil->Log->secure({set => 1}); |
|
########################################## |
|
|
|
# Read switches (target ([user@]host[:port]) and the file with the target's password. |
|
$anvil->Get->switches({list => [ |
|
"add", |
|
"anvil", |
|
"confirm", |
|
"disk", |
|
"eject", |
|
"grow", |
|
"insert", |
|
"optical", |
|
"server", |
|
"storage-group", |
|
], 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 }}); |
|
|
|
# Connect to the database(s). If we have no connections, we'll proceed anyway as one of the 'run_once' tasks |
|
# is to setup the database server. |
|
$anvil->Database->connect(); |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); |
|
if (not $anvil->data->{sys}{database}{connections}) |
|
{ |
|
# No databases, 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_0305"}); |
|
sleep 10; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
$anvil->Database->get_hosts(); |
|
$anvil->Database->get_anvils(); |
|
$anvil->Database->get_servers(); |
|
|
|
if ($anvil->data->{switches}{anvil}) |
|
{ |
|
# Make sure they asked for a real anvil. |
|
$anvil->Get->anvil_from_switch({string => $anvil->data->{switches}{anvil}}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"switches::anvil_name" => $anvil->data->{switches}{anvil_name}, |
|
"switches::anvil_uuid" => $anvil->data->{switches}{anvil_uuid}, |
|
}}); |
|
} |
|
|
|
if (not $anvil->data->{switches}{server}) |
|
{ |
|
# Show the list of servers. |
|
show_server_list($anvil); |
|
print "\nPlease specify which server you want to modify using '--server <name or uuid>'.\n\n"; |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
|
|
validate_server($anvil); |
|
|
|
if ($anvil->data->{switches}{optical}) |
|
{ |
|
manage_optical($anvil); |
|
} |
|
elsif ($anvil->data->{switches}{disk}) |
|
{ |
|
manage_disk($anvil); |
|
} |
|
else |
|
{ |
|
show_server_details($anvil); |
|
} |
|
|
|
$anvil->nice_exit({exit_code => 0}); |
|
|
|
|
|
############################################################################################################# |
|
# Functions # |
|
############################################################################################################# |
|
|
|
sub load_storage |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Host and server |
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
|
|
# We need to know which volume groups the disks are on so that we can report how much free space is |
|
# available for either growing or adding a disk. |
|
$anvil->Get->available_resources({ |
|
debug => 2, |
|
anvil_uuid => $anvil->data->{switches}{anvil_uuid}, |
|
}); |
|
|
|
# Load LVM and Storage Group data |
|
$anvil->Database->get_lvm_data({debug => 2}); |
|
|
|
# Load DRBD data from the database. |
|
$anvil->Database->get_drbd_data({debug => 2}); |
|
|
|
# Load DRBD resource data so that we can map DRBD resources / volumes to volume groups. |
|
my $drbd_resource = ""; |
|
foreach my $device_path (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{device}}) |
|
{ |
|
my $on_lv = $anvil->data->{server}{$short_host_name}{$server_name}{device}{$device_path}{on_lv}; |
|
$drbd_resource = $anvil->data->{server}{$short_host_name}{$server_name}{device}{$device_path}{resource}; |
|
my $device_target = $anvil->data->{server}{$short_host_name}{$server_name}{device}{$device_path}{target}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:device_path' => $device_path, |
|
's2:on_lv' => $on_lv, |
|
's3:drbd_resource' => $drbd_resource, |
|
's4:device_target' => $device_target, |
|
}}); |
|
} |
|
# There should only ever be one, but it's not impossible for there to be multiple. |
|
foreach my $drbd_resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{drbd}{resource}}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drbd_resource => $drbd_resource }}); |
|
|
|
# Get the DRBD volume data |
|
load_drbd_data($anvil, $drbd_resource); |
|
} |
|
|
|
return($drbd_resource); |
|
} |
|
|
|
sub manage_disk |
|
{ |
|
my ($anvil) = @_; |
|
|
|
my $anvil_uuid = defined $anvil->data->{switches}{anvil_uuid} ? $anvil->data->{switches}{anvil_uuid} : $anvil->Cluster->get_anvil_uuid(); |
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $from_source = get_definition_source($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
anvil_uuid => $anvil_uuid, |
|
short_host_name => $short_host_name, |
|
server_name => $server_name, |
|
from_source => $from_source, |
|
}}); |
|
if (not $anvil->data->{switches}{disk}) |
|
{ |
|
# If there's only one optical drive, select it automatically |
|
my $count = keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
|
if ($count == 1) |
|
{ |
|
foreach my $device_target (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}}) |
|
{ |
|
$anvil->data->{switches}{disk} = $device_target; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"switches::drive" => $anvil->data->{switches}{disk}, |
|
}}); |
|
last; |
|
} |
|
} |
|
|
|
if (not $anvil->data->{switches}{disk}) |
|
{ |
|
# Can't proceed. |
|
print "\n[ Error ] - Please indicate the disk drive to work with using '--disk --drive <target_device>'.\n\n"; |
|
show_server_details($anvil); |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
my $drbd_resource = load_storage($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drbd_resource => $drbd_resource }}); |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
}}); |
|
foreach my $volume_number (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}}) |
|
{ |
|
my $device_path = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{device_path}; |
|
next if $device_path eq "DELETED"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:volume_number' => $volume_number, |
|
's2:device_path' => $device_path, |
|
}}); |
|
|
|
my $device_minor = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{device_minor}; |
|
my $volume_size = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{volume_size}; |
|
my $backing_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{backing_disk}; |
|
my $meta_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{meta_disk}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:device_minor' => $device_minor, |
|
's2:volume_size' => $volume_size, |
|
's3:backing_disk' => $backing_disk, |
|
's4:meta_disk' => $meta_disk, |
|
}}); |
|
|
|
# Which volume group is the backing device in? |
|
foreach my $this_scan_lvm_lv_name (sort {$a cmp $b} keys %{$anvil->data->{lvm}{host_name}{$short_host_name}{lv}}) |
|
{ |
|
next if not $this_scan_lvm_lv_name; |
|
my $this_scan_lvm_lv_path = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$this_scan_lvm_lv_name}{scan_lvm_lv_path}; |
|
my $this_scan_lvm_lv_on_vg = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$this_scan_lvm_lv_name}{scan_lvm_lv_on_vg}; |
|
my $this_scan_lvm_lv_uuid = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$this_scan_lvm_lv_name}{scan_lvm_lv_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:this_scan_lvm_lv_name' => $this_scan_lvm_lv_name, |
|
's2:this_scan_lvm_lv_path' => $this_scan_lvm_lv_path, |
|
's3:this_scan_lvm_lv_on_vg' => $this_scan_lvm_lv_on_vg, |
|
's4:this_scan_lvm_lv_uuid' => $this_scan_lvm_lv_uuid, |
|
}}); |
|
|
|
if ($backing_disk eq $this_scan_lvm_lv_path) |
|
{ |
|
# What's the VG's UUID? |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:this_scan_lvm_lv_on_vg' => $this_scan_lvm_lv_on_vg, |
|
}}); |
|
if (exists $anvil->data->{lvm}{host_name}{$short_host_name}{vg}{$this_scan_lvm_lv_on_vg}) |
|
{ |
|
my $scan_lvm_vg_uuid = $anvil->data->{lvm}{host_name}{$short_host_name}{vg}{$this_scan_lvm_lv_name}{scan_lvm_vg_uuid}; |
|
my $storage_group_uuid = $anvil->data->{lvm}{host_name}{$short_host_name}{vg}{$this_scan_lvm_lv_name}{storage_group_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:scan_lvm_vg_uuid' => $scan_lvm_vg_uuid, |
|
's2:storage_group_uuid' => $storage_group_uuid, |
|
}}); |
|
} |
|
} |
|
} |
|
|
|
# How much space can this LV grow into (this factors DR is already protected). |
|
$anvil->data->{server_name}{$server_name}{drbd_resource}{$drbd_resource}{volume}{$volume_number}{free_space} = get_max_free_space($anvil, $drbd_resource, $volume_number); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"server_name::${server_name}::drbd_resource::${drbd_resource}::volume::${volume_number}::free_space" => $anvil->data->{server_name}{$server_name}{drbd_resource}{$drbd_resource}{volume}{$volume_number}{free_space}." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{server_name}{$server_name}{drbd_resource}{$drbd_resource}{volume}{$volume_number}{free_space}}).")", |
|
}}); |
|
} |
|
} |
|
} |
|
|
|
if (($anvil->data->{switches}{grow}) or ($anvil->data->{switches}{remove})) |
|
{ |
|
my $device_target = $anvil->data->{switches}{disk}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device_target => $device_target }}); |
|
|
|
if ($anvil->data->{switches}{disk} eq "#!SET!#") |
|
{ |
|
# User didn't specify a device. |
|
show_server_details($anvil); |
|
print "\n[ Error ] - Please specify the disk drive target you want to work on.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
elsif (not exists $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}{$device_target}) |
|
{ |
|
# Invalid device target |
|
show_server_details($anvil); |
|
print "\n[ Error ] - The disk drive target: [".$device_target."] wasn't found.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
my $device = "disk"; |
|
my $alias = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{alias}; |
|
my $boot_order = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{boot_order}; |
|
my $say_boot = $boot_order eq "99" ? "--" : sprintf("%02d", $boot_order); |
|
my $type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{type}; |
|
my $address_type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{type}; |
|
my $address_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{bus}; |
|
my $driver_name = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{name}; |
|
my $device_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{device_bus}; |
|
my $driver_type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{type}; |
|
my $address_domain = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{domain}; |
|
my $address_slot = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{slot}; |
|
my $address_function = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{function}; |
|
my $device_path = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{path}; |
|
my $driver_io = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{io}; |
|
my $driver_cache = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{cache}; |
|
my $on_lv = $anvil->data->{server}{$short_host_name}{$server_name}{device}{$device_path}{on_lv}; |
|
my $drbd_volume = $anvil->data->{lvm}{host_name}{$short_host_name}{lv_path}{$on_lv}{drbd}{volume}; |
|
my $max_free_space = $anvil->data->{server_name}{$server_name}{drbd_resource}{$drbd_resource}{volume}{$drbd_volume}{free_space}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's01:device_target' => $device_target, |
|
's02:alias' => $alias, |
|
's03:boot_order' => $boot_order, |
|
's04:say_boot' => $say_boot, |
|
's05:type' => $type, |
|
's06:address_type' => $address_type, |
|
's07:address_bus' => $address_bus, |
|
's08:driver_name' => $driver_name, |
|
's09:device_bus' => $device_bus, |
|
's10:driver_type' => $driver_type, |
|
's11:address_domain' => $address_domain, |
|
's12:address_slot' => $address_slot, |
|
's13:address_function' => $address_function, |
|
's14:device_path' => $device_path, |
|
's15:driver_io' => $driver_io, |
|
's16:driver_cache' => $driver_cache, |
|
's17:on_lv' => $on_lv, |
|
's18:drbd_volume' => $drbd_volume, |
|
's19:max_free_space' => $max_free_space." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space}).")", |
|
}}); |
|
#print "- Target: [".$device_target."], boot: [".$say_boot."], path: [".$device_path."], cache: [".$driver_cache."], driver type: [".$driver_type."]\n"; |
|
print "- Target: [".$device_target."], boot: [".$say_boot."], path: [".$device_path."], Available space: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space})."]\n"; |
|
|
|
# What are we doing? |
|
if ($anvil->data->{switches}{grow}) |
|
{ |
|
manage_disk_grow($anvil, $drbd_resource, $drbd_volume, $max_free_space); |
|
} |
|
} |
|
elsif ($anvil->data->{switches}{add}) |
|
{ |
|
manage_disk_add($anvil, $drbd_resource); |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
sub manage_disk_add |
|
{ |
|
my ($anvil, $drbd_resource) = @_; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drbd_resource => $drbd_resource }}); |
|
|
|
my $anvil_uuid = defined $anvil->data->{switches}{anvil_uuid} ? $anvil->data->{switches}{anvil_uuid} : $anvil->Cluster->get_anvil_uuid(); |
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $from_source = get_definition_source($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
anvil_uuid => $anvil_uuid, |
|
short_host_name => $short_host_name, |
|
server_name => $server_name, |
|
from_source => $from_source, |
|
}}); |
|
|
|
# Are they asking for an available amount of space? |
|
my $error_note = q| |
|
[ Note ] - The size can be in percent, ie: '50%' or '100%', a number in bytes, or a human-readable size. |
|
- Human readable sizes must NOT have a space between the number and letter suffix. Also, base2 |
|
- vs base10 notation! Ie: '1GiB' = 1,073,741,824 bytes', '1GB' == '1,000,000,000 bytes'. A single |
|
- letter used to denote size will be interpreted as base2. ie: '1G == 1GiB'. |
|
|; |
|
|
|
# Do we have a storage group? |
|
if (not $anvil->data->{switches}{'storage-group'}) |
|
{ |
|
print "Please specify a storage group to use to add the new drive to.\n"; |
|
short_storage_groups($anvil, $anvil_uuid); |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Make sure that the passed |
|
my $storage_group_switch = $anvil->data->{switches}{'storage-group'}; |
|
my $storage_group_uuid = ""; |
|
my $storage_group_name = ""; |
|
if (exists $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_switch}) |
|
{ |
|
$storage_group_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_switch}{storage_group_uuid}; |
|
$storage_group_name = $storage_group_switch; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
storage_group_uuid => $storage_group_uuid, |
|
storage_group_name => $storage_group_name, |
|
}}); |
|
} |
|
elsif (exists $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_switch}) |
|
{ |
|
$storage_group_uuid = $storage_group_switch; |
|
$storage_group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_switch}{group_name}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
storage_group_uuid => $storage_group_uuid, |
|
storage_group_name => $storage_group_name, |
|
}}); |
|
} |
|
|
|
# Did we get a valid disk size? |
|
my $free_space = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{free_space}; |
|
my $add_size = $anvil->data->{switches}{add}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
if ($add_size =~ /^(\d+)%$/) |
|
{ |
|
# This is valid |
|
my $percent = ".".$1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { percent => $percent }}); |
|
|
|
$add_size = int($free_space * $percent); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
elsif ($add_size !~ /\d/) |
|
{ |
|
# No digits, probably didn't set a value at all. |
|
print "\n[ Error ] - Please specify the size you would like to grow this disk by. The maximum size is: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $free_space})."].\n"; |
|
print $error_note."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
elsif ($add_size !~ /^\d+$/) |
|
{ |
|
# Size is not in bytes, try to convert it. |
|
my $bytes = $anvil->Convert->human_readable_to_bytes({ |
|
debug => 2, |
|
size => $add_size, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'bytes' => $bytes }}); |
|
if ($bytes =~ /^\d+$/) |
|
{ |
|
$add_size = $bytes; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
else |
|
{ |
|
# Not a valid size. |
|
print "\n[ Error ] - The requested size: [".$add_size."] could not be interpreted.\n"; |
|
print $error_note."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
# Still here? We're good to go. |
|
my $lv_command_size = 0; |
|
my $hr_size = $anvil->Convert->bytes_to_human_readable({'bytes' => $add_size}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { hr_size => $hr_size }}); |
|
if ($add_size eq "100%") |
|
{ |
|
# This is valid |
|
$add_size = "-l +100\%FREE"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
else |
|
{ |
|
$hr_size =~ s/\s+//g; |
|
$add_size = "-L +".$hr_size; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
|
|
# What's the next free drive in the system, and what's the next free volume number? |
|
my $new_device_target = ""; |
|
my $target_prefix = ""; |
|
my $disk_device_bus = ""; |
|
my $disk_cache = ""; |
|
my $disk_io_policy = ""; |
|
my $drive_letter = "a"; |
|
foreach my $device_target (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}}) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device_target => $device_target }}); |
|
if (not $disk_device_bus) |
|
{ |
|
$target_prefix = ($device_target =~ /^(\w+)\w$/)[0]; |
|
$disk_device_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}{$device_target}{device_bus}; |
|
$disk_io_policy = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}{$device_target}{driver}{io}; |
|
$disk_cache = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}{$device_target}{driver}{cache}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
target_prefix => $target_prefix, |
|
disk_device_bus => $disk_device_bus, |
|
disk_io_policy => $disk_io_policy, |
|
disk_cache => $disk_cache, |
|
}}); |
|
last; |
|
} |
|
} |
|
for (0..25) |
|
{ |
|
my $test_device = $target_prefix.$drive_letter; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { test_device => $test_device }}); |
|
if (not exists $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{disk}{target}{$test_device}) |
|
{ |
|
# Found a free one. |
|
$new_device_target = $test_device; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_device_target => $new_device_target }}); |
|
last; |
|
} |
|
$drive_letter++; |
|
} |
|
|
|
if (not $new_device_target) |
|
{ |
|
print "\n[ Error ] - Failed to find a new target device name.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
my $next_drbd_volume = ""; |
|
foreach my $this_host (sort {$a cmp $b} keys %{$anvil->data->{drbd}{drbd_node}}) |
|
{ |
|
my $host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host}); |
|
my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:this_host' => $this_host, |
|
's2:host_uuid' => $host_uuid, |
|
's3:short_host_name' => $short_host_name, |
|
}}); |
|
|
|
if ($next_drbd_volume eq "") |
|
{ |
|
my $test_drbd_volume = 0; |
|
for (0..100) |
|
{ |
|
if (not $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$test_drbd_volume}{device_path}) |
|
{ |
|
# This is free. |
|
$next_drbd_volume = $test_drbd_volume; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { next_drbd_volume => $next_drbd_volume }}); |
|
last; |
|
} |
|
$test_drbd_volume++; |
|
next; |
|
} |
|
} |
|
|
|
if ($next_drbd_volume eq "") |
|
{ |
|
print "\n[ Error ] - Failed to find a new DRBD volume to use.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
### TODO: Make this work without the peer node being online. |
|
# The server is allowed to be running, but both nodes and any DR hosts this is replicating to |
|
# needs to be online. |
|
my $all_online = check_drbd_peer_access($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_online => $all_online }}); |
|
|
|
if (not $all_online) |
|
{ |
|
print "\n[ Error ] - Adding a new disk requires all peers to be online.\n"; |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) |
|
{ |
|
my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? "up." : "down!"; |
|
print " - Peer: [".$short_host_name."] is ".$say_access."\n"; |
|
} |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Still alive? Ask the user to confirm. |
|
print "- New drive target: [".$new_device_target."], size: [".$hr_size."], bus: [".$disk_device_bus."], cache: [".$disk_cache."], IO policy: [".$disk_io_policy."]\n"; |
|
print "- Preparing to add a the drive: [".$drbd_resource."/".$next_drbd_volume."] using the storage group: [".$storage_group_name."]...\n"; |
|
if (not $anvil->data->{switches}{confirm}) |
|
{ |
|
print $anvil->Words->string({key => "message_0021"})." "; |
|
my $answer = <STDIN>; |
|
chomp($answer); |
|
if ($answer !~ /^y/i) |
|
{ |
|
print "Aborting.\n"; |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
|
|
# Test that we've lost access while waiting for the answer. |
|
my $all_online = check_drbd_peer_access($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_online => $all_online }}); |
|
|
|
if (not $all_online) |
|
{ |
|
print "\n[ Error ] - It would appear that we've lost access to a peer while waiting for the answer.\n"; |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) |
|
{ |
|
my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? "up." : "down!"; |
|
print " - Peer: [".$short_host_name."] is ".$say_access."\n"; |
|
} |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
# Get the next free minor number |
|
my ($free_minor, undef) = $anvil->DRBD->get_next_resource({ |
|
debug => 2, |
|
minor_only => 1, |
|
anvil_uuid => $anvil_uuid, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { free_minor => $free_minor }}); |
|
|
|
# Create the new LVs |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
my $vg_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_name}; |
|
my $vg_internal_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_internal_uuid}; |
|
my $new_lv_name = $server_name."_".$next_drbd_volume; |
|
my $backing_disk = "/dev/".$vg_name."/".$new_lv_name; |
|
my $shell_call = "if [ -e '".$backing_disk."' ]; then echo 'LV: [".$backing_disk."] already exists.'; else ".$anvil->data->{path}{exe}{lvcreate}." ".$add_size." -n ".$new_lv_name." ".$vg_name."; fi;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
's3:vg_name' => $vg_name, |
|
's4:vg_internal_uuid' => $vg_internal_uuid, |
|
's5:new_lv_name' => $new_lv_name, |
|
's6:backing_disk' => $backing_disk, |
|
's7:shell_call' => $shell_call, |
|
}}); |
|
|
|
# Record this for updating the DRBD resource. |
|
$anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{minor} = $free_minor; |
|
$anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{backing_disk} = $backing_disk; |
|
$anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{seen} = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new_drbd::${short_host_name}::resource::${drbd_resource}::volume::${next_drbd_volume}::minor" => $anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{minor}, |
|
"new_drbd::${short_host_name}::resource::${drbd_resource}::volume::${next_drbd_volume}::backing_disk" => $anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{backing_disk}, |
|
"new_drbd::${short_host_name}::resource::${drbd_resource}::volume::${next_drbd_volume}::seen" => $anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{seen}, |
|
}}); |
|
|
|
# This lets us insert the new volume as needed. |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
}}); |
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
print " - Creating the new local LV: [".$backing_disk."]..."; |
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print " Error!\n"; |
|
print "[ FAILED ] - When trying to create the new local logical volume: [".$backing_disk."]\n"; |
|
print "[ FAILED ] - using the command: [".$shell_call."]\n"; |
|
print "[ FAILED ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==========\n"; |
|
print $output."\n"; |
|
print "==========\n"; |
|
print "The creation of the new replicatedd disk is incomplete, manual intervention is required!!\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
} |
|
else |
|
{ |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
my $use_network = $anvil->data->{peer}{$short_host_name}{access}{network}; |
|
print " - Creating the new LV on the peer: [".$short_host_name.":".$backing_disk."], via: [".$use_ip." (".$use_network.")]"; |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print " Error!\n"; |
|
print "[ FAILED ] - When trying to create the peer: [".$short_host_name."]'s logical volume: [".$backing_disk."]\n"; |
|
print "[ FAILED ] - using the command: [".$shell_call."]\n"; |
|
print "[ FAILED ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==] STDOUT [========\n"; |
|
print $output."\n"; |
|
print "==] STDERR [========\n"; |
|
print $error."\n"; |
|
print "====================\n"; |
|
print "The creation of the new replicated disk is incomplete, manual intervention is required!!\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
} |
|
} |
|
} |
|
|
|
# Update the DRBD config file. |
|
my $new_res_file = ""; |
|
my $drbd_res_file = $anvil->data->{path}{directories}{drbd_resources}."/".$drbd_resource.".res"; |
|
my $drbd_res_body = $anvil->Storage->read_file({file => $drbd_res_file}); |
|
my $in_on_host = ""; |
|
my $in_volume = ""; |
|
foreach my $line (split /\n/, $drbd_res_body) |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); |
|
if ($line =~ /on\s+(.*?)\s/) |
|
{ |
|
$in_on_host = $1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); |
|
|
|
$new_res_file .= $line."\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { new_res_file => $new_res_file }}); |
|
next; |
|
} |
|
|
|
if (($in_on_host) && ($line =~ /volume\s+(\d+)\s/)) |
|
{ |
|
$in_volume = $1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_volume => $in_volume }}); |
|
|
|
$new_res_file .= $line."\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { new_res_file => $new_res_file }}); |
|
|
|
$anvil->data->{new_drbd}{$in_on_host}{resource}{$drbd_resource}{volume}{$in_volume}{seen} = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new_drbd::${in_on_host}::resource::${drbd_resource}::volume::${in_volume}::seen" => $anvil->data->{new_drbd}{$in_on_host}{resource}{$drbd_resource}{volume}{$in_volume}{seen}, |
|
}}); |
|
next; |
|
} |
|
|
|
if ($line =~ /}/) |
|
{ |
|
if ($in_volume) |
|
{ |
|
$in_volume = ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_volume => $in_volume }}); |
|
} |
|
elsif ($in_on_host) |
|
{ |
|
# This is where we insert the new volume, if we've not seen it yet. |
|
if (not $anvil->data->{new_drbd}{$in_on_host}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{seen}) |
|
{ |
|
# Insert the line. |
|
$new_res_file .= $line." |
|
|
|
volume ".$next_drbd_volume." { |
|
device /dev/drbd_".$drbd_resource."_".$next_drbd_volume." minor ".$anvil->data->{new_drbd}{$short_host_name}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{minor}."; |
|
disk ".$anvil->data->{new_drbd}{$in_on_host}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{backing_disk}."; |
|
meta-disk internal; |
|
} |
|
"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { new_res_file => $new_res_file }}); |
|
|
|
$anvil->data->{new_drbd}{$in_on_host}{resource}{$drbd_resource}{volume}{$next_drbd_volume}{seen} = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"new_drbd::${in_on_host}::resource::${drbd_resource}::volume::${in_volume}::seen" => $anvil->data->{new_drbd}{$in_on_host}{resource}{$drbd_resource}{volume}{$in_volume}{seen}, |
|
}}); |
|
next; |
|
} |
|
} |
|
} |
|
|
|
$new_res_file .= $line."\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { new_res_file => $new_res_file }}); |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_res_file => $new_res_file }}); |
|
|
|
my $difference = diff \$drbd_res_body, \$new_res_file, { STYLE => 'Unified' }; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $difference }}); |
|
|
|
# Write the file to a test file and verify it's sane, |
|
my $test_file = $anvil->data->{path}{directories}{temp}."/test-".$drbd_resource.".res"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { test_file => $test_file }}); |
|
my ($problem) = $anvil->Storage->write_file({ |
|
debug => 2, |
|
backup => 0, |
|
overwrite => 1, |
|
file => $test_file, |
|
body => $new_res_file, |
|
user => "root", |
|
group => "root", |
|
mode => "0644", |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
|
|
|
# Validate. |
|
print "- Testing the updated DRBD resource config file to ensure the new volumes are cromulent..."; |
|
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." --config-to-test ".$test_file." --config-to-exclude ".$drbd_res_file." sh-nop"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
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, |
|
}}); |
|
if ($return_code) |
|
{ |
|
# Something went wrong. |
|
print " Failed! |
|
[ Error ] - The new DRBD resource config appears to be invalid, which is likely a program error. The new |
|
[ Error ] - config was written to the test file: [".$test_file."]. |
|
[ Error ] - The test to confirm it was valid exited with the return code: [".$return_code."], expected '0'. |
|
[ Error ] - The output, if anything, was: |
|
==== |
|
".$output." |
|
==== |
|
"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
print " Success!\n"; |
|
|
|
# Remove the test file. |
|
unlink $test_file; |
|
|
|
# Backup the res file so we can tell the user where the current config was backed up to in |
|
# case they need to restore it. |
|
print "- Writing out the updated DRBD config file.\n"; |
|
my ($backup_file) = $anvil->Storage->backup({file => $drbd_res_file}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { backup_file => $backup_file }}); |
|
|
|
# Write out the new file. |
|
($problem) = $anvil->Storage->write_file({ |
|
debug => 2, |
|
backup => 0, |
|
overwrite => 1, |
|
file => $drbd_res_file, |
|
body => $new_res_file, |
|
user => "root", |
|
group => "root", |
|
mode => "0644", |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { backup_file => $backup_file }}); |
|
# |
|
# Copy this to our peers. |
|
print "- Copying the new resource file to out peers.\n"; |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
next if $host_uuid eq $anvil->Get->host_uuid; |
|
|
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
my $destination = "root\@".$use_ip.":".$anvil->data->{path}{directories}{drbd_resources}."/"; |
|
$destination =~ s/\/\//\//g; |
|
print " - Copying: [".$drbd_res_file."] to: [".$short_host_name.":".$destination."] via: [".$use_ip."]\n"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
use_ip => $use_ip, |
|
destination => $destination, |
|
}}); |
|
|
|
my $failed = $anvil->Storage->rsync({ |
|
debug => 2, |
|
destination => $destination, |
|
source => $drbd_res_file, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }}); |
|
|
|
if ($failed) |
|
{ |
|
print " |
|
[ Error ] - There was a problem copying the new config file! Unable to proceed. |
|
[ Error ] - Manual intervention to complete the update is required! |
|
"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
} |
|
|
|
# Create the metadata. |
|
print "- Creating the replicated storage metadata on the new backing devices now.\n"; |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." --force create-md --max-peers=3 ".$drbd_resource."/".$next_drbd_volume; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
's3:shell_call' => $shell_call, |
|
}}); |
|
|
|
# Create the metadata, but don't exit on failure in case the metadata was created in |
|
# a previous pass. |
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
print " - Creating the meta-data on the new local volume: [".$next_drbd_volume."]..."; |
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
|
|
### Return codes |
|
# 0 == Success |
|
# 1 == ? |
|
# 3 == Configuration not found. |
|
if ($return_code) |
|
{ |
|
print " Warning!\n"; |
|
print "[ Warning ] - When trying to create the local meta-data on: [".$drbd_resource."/".$next_drbd_volume."]\n"; |
|
print "[ Warning ] - using the command: [".$shell_call."]\n"; |
|
print "[ Warning ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==========\n"; |
|
print $output."\n"; |
|
print "==========\n"; |
|
print "We will try to proceed anyway.\n"; |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
} |
|
else |
|
{ |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
my $use_network = $anvil->data->{peer}{$short_host_name}{access}{network}; |
|
print " - Creating the meta-data on the peer: [".$short_host_name.":".$drbd_resource."/".$next_drbd_volume."], via: [".$use_ip." (".$use_network.")]"; |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print " Warning!\n"; |
|
print "[ Warning ] - When trying to create the peer: [".$short_host_name."]'s meta-data on: [".$drbd_resource."/".$next_drbd_volume."]\n"; |
|
print "[ Warning ] - using the command: [".$shell_call."]\n"; |
|
print "[ Warning ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==] STDOUT [========\n"; |
|
print $output."\n"; |
|
print "==] STDERR [========\n"; |
|
print $error."\n"; |
|
print "====================\n"; |
|
print "We will try to proceed anyway.\n"; |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
} |
|
} |
|
} |
|
|
|
### NOTE: The call to 'drbdadm adjust <res>' hangs, hard, until the same command is run on the peers. |
|
### To deal with this, we register jobs to run 'anvil-special-operations' on the peers, then we |
|
### call adjust here. |
|
# Adjust to start/connect. |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
my $shell_call = $anvil->data->{path}{exe}{'anvil-special-operations'}." --task refresh-drbd-resource --resource ".$drbd_resource.$anvil->Log->switches; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
's3:shell_call' => $shell_call, |
|
}}); |
|
next if $host_uuid eq $anvil->Get->host_uuid; |
|
|
|
my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ |
|
debug => 2, |
|
job_command => $shell_call, |
|
job_data => "adjust=".$drbd_resource, |
|
job_name => "server::add_disk::rescan", |
|
job_title => "job_0465", |
|
job_description => "job_0466", |
|
job_progress => 0, |
|
job_host_uuid => $host_uuid, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); |
|
|
|
print "- Registered a job with job UUID: [".$job_uuid."] to reload the resource config on the host: [".$short_host_name."].\n"; |
|
} |
|
} |
|
|
|
print "- Adjusting the local resource: [".$drbd_resource."] to pick up the new config.\n"; |
|
print "[ NOTE ] - If this hangs, make sure 'anvil-daemon' is running on the peers.\n"; |
|
$shell_call = $anvil->data->{path}{exe}{drbdadm}." adjust ".$drbd_resource; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
($output, $return_code) = $anvil->System->call({ |
|
background => 1, |
|
shell_call => $shell_call, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
|
|
# Find which node is currently Primary and use that host to force primary to start sync. If none, |
|
# force here. |
|
print "- Waiting for all peers to connect the new volume...\n"; |
|
my $waiting = 1; |
|
my $wait_until = time + 300; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { wait_until => $wait_until }}); |
|
while ($waiting) |
|
{ |
|
$anvil->DRBD->get_status({debug => 2}); |
|
my $peers_connected = 1; |
|
my $disks_ready = 0; |
|
my $drbd_peer_count = keys %{$anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drbd_peer_count => $drbd_peer_count }}); |
|
if (not $drbd_peer_count) |
|
{ |
|
$peers_connected = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peers_connected => $peers_connected }}); |
|
} |
|
foreach my $this_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}}) |
|
{ |
|
my $host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host_name}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:this_host_name' => $this_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
}}); |
|
next if $host_uuid eq $anvil->Get->host_uuid; |
|
|
|
my $connection_state = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$this_host_name}{'connection-state'}; |
|
my $node_id = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$this_host_name}{'peer-node-id'}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:connection_state' => $connection_state, |
|
's2:node_id' => $node_id, |
|
}}); |
|
|
|
if (lc($connection_state) ne "connected") |
|
{ |
|
$peers_connected = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peers_connected => $peers_connected }}); |
|
} |
|
} |
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peers_connected => $peers_connected }}); |
|
if ($peers_connected) |
|
{ |
|
# Make sure all disks are attached. |
|
$disks_ready = 1; |
|
$anvil->data->{peers}{$short_host_name}{disk_state} = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{devices}{volume}{$next_drbd_volume}{'disk-state'}; |
|
$anvil->data->{peers}{$short_host_name}{role} = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{role}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"peers::${short_host_name}::disk_state" => $anvil->data->{peers}{$short_host_name}{disk_state}, |
|
"peers::${short_host_name}::role" => $anvil->data->{peers}{$short_host_name}{role}, |
|
}}); |
|
foreach my $peer_name (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}}) |
|
{ |
|
next if not defined $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{volume}{$next_drbd_volume}{'peer-disk-state'}; |
|
my $peer_disk_state = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{volume}{$next_drbd_volume}{'peer-disk-state'}; |
|
my $replication_state = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{volume}{$next_drbd_volume}{'replication-state'}; |
|
my $role = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{'peer-role'}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:peer_name' => $peer_name, |
|
's2:peer_disk_state' => $peer_disk_state, |
|
's3:replication_state' => $replication_state, |
|
's4:role' => $role, |
|
}}); |
|
|
|
# If the volume is 'Negotiating', disconnect and reconnect the peer. |
|
if (lc($peer_disk_state) eq "negotiating") |
|
{ |
|
print "- Problem!\n"; |
|
($peer_disk_state, $role) = reconnect_resource($anvil, $short_host_name, $peer_name, $drbd_resource, $next_drbd_volume); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
peer_disk_state => $peer_disk_state, |
|
role => $role, |
|
}}); |
|
|
|
$disks_ready = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); |
|
} |
|
|
|
# If it's not established, keep waiting |
|
if (lc($replication_state) ne "established") |
|
{ |
|
$disks_ready = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); |
|
} |
|
if ((not $peer_disk_state) or (lc($peer_disk_state) eq "diskless")) |
|
{ |
|
$disks_ready = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); |
|
} |
|
|
|
$anvil->data->{peers}{$peer_name}{disk_state} = $peer_disk_state; |
|
$anvil->data->{peers}{$peer_name}{role} = $role; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"peers::${peer_name}::disk_state" => $anvil->data->{peers}{$peer_name}{disk_state}, |
|
}}); |
|
} |
|
} |
|
|
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); |
|
if ($disks_ready) |
|
{ |
|
$waiting = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }}); |
|
} |
|
else |
|
{ |
|
if (time > $wait_until) |
|
{ |
|
print "\nFailed!\n[ Error ] - The peers did not connect in the expected period of time.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
sleep 2; |
|
} |
|
} |
|
|
|
print "- Peers are connected! Checking if the new volume requires initial sync.\n"; |
|
my $all_inconsistent = 1; |
|
my $primary_on_host = ""; |
|
foreach my $peer_name (sort {$a cmp $b} keys %{$anvil->data->{peers}}) |
|
{ |
|
my $disk_state = $anvil->data->{peers}{$peer_name}{disk_state}; |
|
my $role = $anvil->data->{peers}{$peer_name}{role}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
peer_name => $peer_name, |
|
disk_state => $disk_state, |
|
role => $role, |
|
}}); |
|
|
|
if (lc($disk_state) ne "inconsistent") |
|
{ |
|
$all_inconsistent = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_inconsistent => $all_inconsistent }}); |
|
} |
|
if (lc($role) eq "primary") |
|
{ |
|
$primary_on_host = $peer_name; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { primary_on_host => $primary_on_host }}); |
|
} |
|
} |
|
|
|
if ($all_inconsistent) |
|
{ |
|
print "- Initial sync required!\n"; |
|
|
|
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." new-current-uuid --force-resync ".$drbd_resource."/".$next_drbd_volume; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
# Which node should be forced primary? |
|
my $already_primary = 1; |
|
if (not $primary_on_host) |
|
{ |
|
# We'll make it primary. |
|
$primary_on_host = $short_host_name; |
|
$already_primary = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
primary_on_host => $primary_on_host, |
|
already_primary => $already_primary, |
|
}}); |
|
} |
|
|
|
my $primary_on_host_uuid = $anvil->Get->host_uuid_from_name({host_name => $primary_on_host}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { primary_on_host_uuid => $primary_on_host_uuid }}); |
|
if ($primary_on_host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
print "- Forcing primary locally... "; |
|
my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
# Return code of '0' is success. |
|
if ($return_code) |
|
{ |
|
print "Failed! |
|
[ Error ] - There was a problem trying to force the new volume: [".$drbd_resource."/".$next_drbd_volume."] to Primary. |
|
[ Error ] - Attempted this using the shell call: [".$shell_call."]. |
|
[ Error ] - Expected the return code '0' but got: [".$return_code."]. The output, if any, was: |
|
========== |
|
".$output." |
|
========== |
|
[ Error ] - Once corrected, please manually add the new volume to the server. |
|
"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Now demote it again. |
|
$shell_call = $anvil->data->{path}{exe}{drbdadm}." secondary ".$drbd_resource; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
($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, |
|
}}); |
|
print "Success!\n"; |
|
} |
|
else |
|
{ |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
my $use_network = $anvil->data->{peer}{$short_host_name}{access}{network}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
use_ip => $use_ip, |
|
use_network => $use_network, |
|
}}); |
|
print " - The resource is primary on the peer: [".$short_host_name."], forcing primary there via: [".$use_ip." (".$use_network.")]"; |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print "Failed! |
|
[ Error ] - There was a problem trying to force the new volume: [".$drbd_resource."/".$next_drbd_volume."] to Primary. |
|
[ Error ] - Attempted this using the shell call: [".$shell_call."]. |
|
[ Error ] - Expected the return code '0' but got: [".$return_code."]. The output, if any, was: |
|
========== |
|
".$output." |
|
========== |
|
[ Error ] - Once corrected, please manually add the new volume to the server. |
|
"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Now demote it again. |
|
$shell_call = $anvil->data->{path}{exe}{drbdadm}." secondary ".$drbd_resource; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
print "Success!\n"; |
|
} |
|
} |
|
else |
|
{ |
|
print "- Initial sync does not appear to be required.\n"; |
|
} |
|
|
|
# Is the server running? If so, where. |
|
print "- Ready to add the new disk. Checking if the server is running...\n"; |
|
my $server_host = ""; |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
my $shell_call = $anvil->data->{path}{exe}{'anvil-special-operations'}." --task refresh-drbd-resource --resource ".$drbd_resource.$anvil->Log->switches; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
's3:shell_call' => $shell_call, |
|
}}); |
|
|
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
$anvil->Server->find(); |
|
} |
|
else |
|
{ |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
$anvil->Server->find({target => $use_ip }); |
|
} |
|
|
|
if ((exists $anvil->data->{server}{location}{$server_name}) && |
|
($anvil->data->{server}{location}{$server_name}{host_name})) |
|
{ |
|
my $this_host = defined $anvil->data->{server}{location}{$server_name}{host_name} ? $anvil->data->{server}{location}{$server_name}{host_name} : ""; |
|
my $server_status = defined $anvil->data->{server}{location}{$server_name}{status} ? $anvil->data->{server}{location}{$server_name}{status} : ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
this_host => $this_host, |
|
server_status => $server_status, |
|
}}); |
|
if ($server_status eq "running") |
|
{ |
|
# Found it. |
|
$server_host = $this_host; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host => $server_host }}); |
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
print "- The server is running on this host, we'll attach the disk here.\n"; |
|
} |
|
else |
|
{ |
|
print "- The server is running on: [".$server_host."], we'll attach the disk there.\n"; |
|
} |
|
last; |
|
} |
|
} |
|
} |
|
} |
|
|
|
my $offline = 0; |
|
if (not $server_host) |
|
{ |
|
print "- The server isn't running anywhere, we'll attach the disk offline on this host.\n"; |
|
$server_host = $short_host_name; |
|
$offline = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
server_host => $server_host, |
|
offline => $offline, |
|
}}); |
|
} |
|
|
|
$shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." attach-disk ".$server_name." "; |
|
$shell_call .= "/dev/drbd/by-res/".$drbd_resource."/".$next_drbd_volume." ".$new_device_target." "; |
|
$shell_call .= "--persistent --targetbus ".$disk_device_bus." "; |
|
$shell_call .= "--cache ".$disk_cache." "; |
|
$shell_call .= "--io ".$disk_io_policy." "; |
|
$shell_call .= "--sourcetype block --subdriver raw"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
offline => $offline, |
|
shell_call => $shell_call, |
|
}}); |
|
if ($offline) |
|
{ |
|
# Define the VM, if needed, then add the drive, dump the config and push it out. |
|
print "- Defining the server: [".$server_name."] to prepare for 'virsh' modification of the server.\n"; |
|
update_definition($anvil, "define", ""); |
|
|
|
# Update the definition. |
|
print "- Adding the drive to the definition now.\n"; |
|
($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, |
|
}}); |
|
|
|
print "- Updating the stored definition and undefining the server now...\n"; |
|
update_definition($anvil, "undefine", ""); |
|
print "Done!\n"; |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
else |
|
{ |
|
# Add the drive live, dump the new definition and push it out. |
|
my $host_uuid = $anvil->Get->host_uuid_from_name({host_name => $server_host}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }}); |
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
# Do the add here. |
|
update_definition($anvil, "define", ""); |
|
print "- Adding the drive to the server directly...\n"; |
|
($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. |
|
print "- Failed!\n"; |
|
print "Expected the return code '0', but got: [".$return_code."]. The command output, if anything, was:\n"; |
|
print "========\n"; |
|
print $output."\n"; |
|
print "========\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Get the new XML |
|
print "- Reading the updated server definition\n"; |
|
$shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server_name; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
(my $virsh_definition, $return_code) = $anvil->System->call({shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
virsh_definition => $virsh_definition, |
|
return_code => $return_code, |
|
}}); |
|
|
|
# Make sure the $output is valid XML. |
|
print "- Validating the updated definition\n"; |
|
my $problem = $anvil->Server->parse_definition({ |
|
server => $server_name, |
|
source => "from_virsh", |
|
definition => $virsh_definition, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
|
|
|
if ($problem) |
|
{ |
|
# Failed?! |
|
print " |
|
[ Error ] - The definition file read hear appears to be invalid after trying to attach the disk! It is unsafe |
|
[ Error ] - to update the on disk and in DB definition. It's likely the attach has failed. |
|
[ Error ] - Manual update to the server is likely required now. |
|
"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
print "- Updating the stored definition and undefining the server now...\n"; |
|
update_definition($anvil, "undefine", $virsh_definition); |
|
print "Done!\n"; |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
else |
|
{ |
|
# Do the add on the target. |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_ip => $use_ip }}); |
|
print " - Adding the drivve to the server using its host: [".$server_host."] via: [".$use_ip."]...\n"; |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
|
|
# Get the updated definition file. |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server_name; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
(my $virsh_definition, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
virsh_definition => $virsh_definition, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
|
|
# Make sure the $output is valid XML. |
|
my $problem = $anvil->Server->parse_definition({ |
|
server => $server_name, |
|
source => "from_virsh", |
|
definition => $virsh_definition, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
|
|
|
if ($problem) |
|
{ |
|
# Failed?! |
|
print " |
|
[ Error ] - The definition file read from the remote host appears to be invalid after trying to attach the |
|
[ Error ] - disk! It is unsafe to update the on disk and in DB definition. It's likely the attach has failed. |
|
[ Error ] - Manual update to the server is likely required now. |
|
"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
$anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{xml} = $virsh_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"server::${short_host_name}::${server_name}::from_virsh::xml" => $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{xml}, |
|
}}); |
|
|
|
print "- Updating the stored definition and undefining the server now...\n"; |
|
update_definition($anvil, "undefine", $virsh_definition); |
|
print "Done!\n"; |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
sub reconnect_resource |
|
{ |
|
my ($anvil, $short_host_name, $peer_name, $drbd_resource, $next_drbd_volume) = @_; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
short_host_name => $short_host_name, |
|
peer_name => $peer_name, |
|
drbd_resource => $drbd_resource, |
|
next_drbd_volume => $next_drbd_volume, |
|
}}); |
|
|
|
my $peer_disk_state = ""; |
|
my $role = ""; |
|
|
|
# Log into the peer and disconnect, then reconnect. Then wait for the disk state to update, and |
|
# return the disk state. |
|
print " - The peer: [".$peer_name."], is stuck 'Negotiating'; disconnecting... "; |
|
my $use_ip = $anvil->data->{peer}{$peer_name}{access}{ip}; |
|
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." disconnect ".$drbd_resource; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
use_ip => $use_ip, |
|
shell_call => $shell_call, |
|
}}); |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
|
|
print " reconnecting... "; |
|
sleep 1; |
|
$shell_call = $anvil->data->{path}{exe}{drbdadm}." connect ".$drbd_resource; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
use_ip => $use_ip, |
|
shell_call => $shell_call, |
|
}}); |
|
($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
print " Done.\n"; |
|
|
|
# Now wait |
|
print "- Waiting for the disk state to be updated:\n"; |
|
my $waiting = 1; |
|
my $wait_until = time + 300; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { wait_until => $wait_until }}); |
|
while ($waiting) |
|
{ |
|
$anvil->DRBD->get_status({debug => 2}); |
|
my $disks_ready = 1; |
|
my $connection_state = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{'connection-state'}; |
|
if (not defined $connection_state) |
|
{ |
|
print "Short host name: [".$short_host_name."], resource: [".$drbd_resource."], peer: [".$peer_name."]\n"; |
|
print Dumper %{$anvil->data->{drbd}{status}}; |
|
die; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { connection_state => $connection_state }}); |
|
if (lc($connection_state) eq "connected") |
|
{ |
|
# Connected, what's the disk state for the new volume now? |
|
print " - Connected, checking volume: [".$drbd_resource."/".$next_drbd_volume."] disk state: "; |
|
$peer_disk_state = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{volume}{$next_drbd_volume}{'peer-disk-state'}; |
|
my $replication_state = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{volume}{$next_drbd_volume}{'replication-state'}; |
|
$role = $anvil->data->{drbd}{status}{$short_host_name}{resource}{$drbd_resource}{connection}{$peer_name}{'peer-role'}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:peer_disk_state' => $peer_disk_state, |
|
's2:replication_state' => $replication_state, |
|
's3:role' => $role, |
|
}}); |
|
|
|
if (lc($replication_state) ne "established") |
|
{ |
|
print "Not Connected yet.\n"; |
|
$disks_ready = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); |
|
} |
|
elsif ((not $peer_disk_state) or (lc($peer_disk_state) eq "diskless")) |
|
{ |
|
print "Disk not ready yet.\n"; |
|
$disks_ready = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); |
|
} |
|
|
|
if ($disks_ready) |
|
{ |
|
print "Ready!\n- Disk state is: [".$peer_disk_state."]\n"; |
|
$waiting = 0; |
|
$anvil->data->{peers}{$peer_name}{disk_state} = $peer_disk_state; |
|
$anvil->data->{peers}{$peer_name}{role} = $role; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
waiting => 0, |
|
"peers::${peer_name}::disk_state" => $anvil->data->{peers}{$peer_name}{disk_state}, |
|
"peers::${peer_name}::role" => $anvil->data->{peers}{$peer_name}{role}, |
|
}}); |
|
|
|
return($peer_disk_state, $role); |
|
} |
|
} |
|
|
|
if (time > $wait_until) |
|
{ |
|
$waiting = 0; |
|
print "- Timed out waiting for the peer to connect.\n"; |
|
return($peer_disk_state, $role); |
|
} |
|
sleep 3; |
|
} |
|
|
|
return($peer_disk_state, $role); |
|
} |
|
|
|
sub manage_disk_grow |
|
{ |
|
my ($anvil, $drbd_resource, $drbd_volume, $max_free_space) = @_; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:drbd_resource' => $drbd_resource, |
|
's2:drbd_volume' => $drbd_volume, |
|
's3:max_free_space' => $max_free_space, |
|
}}); |
|
|
|
my $anvil_uuid = defined $anvil->data->{switches}{anvil_uuid} ? $anvil->data->{switches}{anvil_uuid} : $anvil->Cluster->get_anvil_uuid(); |
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $from_source = get_definition_source($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
anvil_uuid => $anvil_uuid, |
|
short_host_name => $short_host_name, |
|
server_name => $server_name, |
|
from_source => $from_source, |
|
}}); |
|
|
|
# Are they asking for an available amount of space? |
|
my $error_note = q| |
|
[ Note ] - The size can be in percent, ie: '50%' or '100%', a number in bytes, or a human-readable size. |
|
- Human readable sizes must NOT have a space between the number and letter suffix. Also, base2 |
|
- vs base10 notation! Ie: '1GiB' = 1,073,741,824 bytes', '1GB' == '1,000,000,000 bytes'. A single |
|
- letter used to denote size will be interpreted as base2. ie: '1G == 1GiB'. |
|
|; |
|
|
|
my $add_size = $anvil->data->{switches}{grow}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
if ($add_size =~ /^(\d+)%$/) |
|
{ |
|
# This is valid |
|
my $percent = ".".$1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { percent => $percent }}); |
|
|
|
$add_size = int($max_free_space * $percent); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
elsif ($add_size !~ /\d/) |
|
{ |
|
# No digits, probably didn't set a value at all. |
|
print "\n[ Error ] - Please specify the size you would like to grow this disk by. The maximum size is: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space})."].\n"; |
|
print $error_note."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
elsif ($add_size !~ /^\d+$/) |
|
{ |
|
# Size is not in bytes, try to convert it. |
|
my $bytes = $anvil->Convert->human_readable_to_bytes({ |
|
debug => 2, |
|
size => $add_size, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'bytes' => $bytes }}); |
|
if ($bytes =~ /^\d+$/) |
|
{ |
|
$add_size = $bytes; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
else |
|
{ |
|
# Not a valid size. |
|
print "\n[ Error ] - The requested size: [".$add_size."] could not be interpreted.\n"; |
|
print $error_note."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
# Make sure they're asking for a reasonable size |
|
if ($add_size < 4194304) |
|
{ |
|
# Must be a typo, this is less than the size of a single extent. |
|
print "\n[ Error ] - The requested size: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $add_size})."] is too small, it's less than an single extent.\n"; |
|
print $error_note."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
elsif ($add_size > $max_free_space) |
|
{ |
|
# Not enough space. |
|
print "\n[ Error ] - The requested size: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $add_size})."] is too large. The available size is: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space})."]\n"; |
|
print $error_note."\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
### TODO: Make this work without the peer node being online. |
|
# The server is allowed to be running, but both nodes and any DR hosts this is replicating to |
|
# needs to be online. |
|
my $all_online = check_drbd_peer_access($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_online => $all_online }}); |
|
|
|
if (not $all_online) |
|
{ |
|
print "\n[ Error ] - Growing the storage requires all peers to be online.\n"; |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) |
|
{ |
|
my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? "up." : "down!"; |
|
print " - Peer: [".$short_host_name."] is ".$say_access."\n"; |
|
} |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Still here? We're good to go. |
|
my $lv_command_size = 0; |
|
my $hr_size = $anvil->Convert->bytes_to_human_readable({'bytes' => $add_size}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { hr_size => $hr_size }}); |
|
if ($add_size eq "100%") |
|
{ |
|
# This is valid |
|
$add_size = "-l +100\%FREE"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
else |
|
{ |
|
$hr_size =~ s/\s+//g; |
|
$add_size = "-L +".$hr_size; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { add_size => $add_size }}); |
|
} |
|
|
|
print "- Preparing to grow the storage by: [".$hr_size."]...\n"; |
|
if (not $anvil->data->{switches}{confirm}) |
|
{ |
|
print $anvil->Words->string({key => "message_0021"})." "; |
|
my $answer = <STDIN>; |
|
chomp($answer); |
|
if ($answer !~ /^y/i) |
|
{ |
|
print "Aborting.\n"; |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
|
|
# Test that we've lost access while waiting for the answer. |
|
my $all_online = check_drbd_peer_access($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_online => $all_online }}); |
|
|
|
if (not $all_online) |
|
{ |
|
print "\n[ Error ] - It would appear that we've lost access to a peer while waiting for the answer.\n"; |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) |
|
{ |
|
my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? "up." : "down!"; |
|
print " - Peer: [".$short_host_name."] is ".$say_access."\n"; |
|
} |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
my $backing_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$drbd_volume}{backing_disk}; |
|
my $shell_call = $anvil->data->{path}{exe}{lvextend}." ".$add_size." ".$backing_disk; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
's3:backing_disk' => $backing_disk, |
|
's4:shell_call' => $shell_call, |
|
}}); |
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
print " - Extending local LV: [".$backing_disk."]..."; |
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print " Error!\n"; |
|
print "[ FAILED ] - When trying to grow the local logical volume: [".$backing_disk."]\n"; |
|
print "[ FAILED ] - using the command: [".$shell_call."]\n"; |
|
print "[ FAILED ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==========\n"; |
|
print $output."\n"; |
|
print "==========\n"; |
|
print "The extension of the resource is incomplete, manual intervention is required!!\n"; |
|
print "[ Warning ] - Do NOT re-run this command! The backing devices may not have mis-matched sized!\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
} |
|
else |
|
{ |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
my $use_network = $anvil->data->{peer}{$short_host_name}{access}{network}; |
|
print " - Extending peer: [".$short_host_name.":".$backing_disk."], via: [".$use_ip." (".$use_network.")]"; |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print " Error!\n"; |
|
print "[ FAILED ] - When trying to grow the peer's logical volume: [".$backing_disk."]\n"; |
|
print "[ FAILED ] - using the command: [".$shell_call."]\n"; |
|
print "[ FAILED ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==] STDOUT [========\n"; |
|
print $output."\n"; |
|
print "==] STDERR [========\n"; |
|
print $error."\n"; |
|
print "====================\n"; |
|
print "The extension of the resource is incomplete, manual intervention is required!!\n"; |
|
print "[ Warning ] - Do NOT re-run this command! The backing devices may not have mis-matched sized!\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
} |
|
} |
|
} |
|
|
|
# Locally, we'll call DRBD to resize. |
|
print "- Extending backing devices complete. Now extending DRBD resource/volume... "; |
|
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." resize ".$drbd_resource."/".$drbd_volume; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
if ($return_code) |
|
{ |
|
print " Error!\n"; |
|
print "[ FAILED ] - When trying to grow the DRBD device: [".$drbd_resource."/".$drbd_volume."]\n"; |
|
print "[ FAILED ] - using the command: [".$shell_call."]\n"; |
|
print "[ FAILED ] - The return code: [".$return_code."] was received, expected '0'. Output, if any:\n"; |
|
print "==========\n"; |
|
print $output."\n"; |
|
print "==========\n"; |
|
print "The extension of the resource is incomplete, manual intervention is required!!\n"; |
|
print "[ Note ] - All backing devices have been grown. Manually resolving the drbd grow\n"; |
|
print "[ Note ] - error should complete the drive expansion!\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
else |
|
{ |
|
print " Done!\n"; |
|
} |
|
|
|
# Call scan-lvm and scan-drbd to make sure the databases are updated. |
|
print "- Calling scancore agents to ensure the database has the new storage config recorded.\n"; |
|
foreach my $agent ("scan-drbd", "scan-lvm") |
|
{ |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0740", variables => { agent => $agent }}); |
|
|
|
my $shell_call = $anvil->data->{path}{directories}{scan_agents}."/".$agent."/".$agent.$anvil->Log->switches(); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
foreach my $host_type ("node", "dr") |
|
{ |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
}}); |
|
if ($host_uuid eq $anvil->Get->host_uuid) |
|
{ |
|
print " - Running scan agent: [".$agent."] locally..."; |
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
print " Done!\n"; |
|
} |
|
else |
|
{ |
|
my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; |
|
my $use_network = $anvil->data->{peer}{$short_host_name}{access}{network}; |
|
print " - Running scan agent: [".$agent."] on: [".$short_host_name."] via: [".$use_ip." (".$use_network.")]..."; |
|
my ($output, $error, $return_code) = $anvil->Remote->call({ |
|
shell_call => $shell_call, |
|
target => $use_ip, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
error => $error, |
|
return_code => $return_code, |
|
}}); |
|
print " Done!\n"; |
|
} |
|
} |
|
} |
|
} |
|
|
|
print "[ Success ] - Expansion is complete!\n"; |
|
print "[ Note ] - Depending on your OS, you may need to power the server off, and then power it back on\n"; |
|
print "[ Note ] - for the new space to be visible. Typically, powering off the server from the guest OS\n"; |
|
print "[ Note ] - and waiting for the Anvil! to boot it back up will do the job nicely.\n"; |
|
|
|
return(0); |
|
} |
|
|
|
sub manage_optical |
|
{ |
|
my ($anvil) = @_; |
|
|
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $from_source = get_definition_source($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
short_host_name => $short_host_name, |
|
server_name => $server_name, |
|
from_source => $from_source, |
|
}}); |
|
|
|
if (not $anvil->data->{switches}{optical}) |
|
{ |
|
# If there's only one optical drive, select it automatically |
|
my $count = keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
|
if ($count == 1) |
|
{ |
|
foreach my $device_target (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}}) |
|
{ |
|
$anvil->data->{switches}{optical} = $device_target; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"switches::drive" => $anvil->data->{switches}{optical}, |
|
}}); |
|
last; |
|
} |
|
} |
|
|
|
if (not $anvil->data->{switches}{optical}) |
|
{ |
|
# Can't proceed. |
|
print "\n[ Error ] - Please indicate the optical drive to work with using '--optical <target_device>'.\n\n"; |
|
show_server_details($anvil); |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
my $device_target = $anvil->data->{switches}{optical}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device_target => $device_target }}); |
|
|
|
if ($anvil->data->{switches}{optical} eq "#!SET!#") |
|
{ |
|
# User didn't specify a device. |
|
show_server_details($anvil); |
|
print "\n[ Error ] - Please specify the optical disk target you want to work on.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
elsif (not exists $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}) |
|
{ |
|
# Invalid device target |
|
show_server_details($anvil); |
|
print "\n[ Error ] - The optical device target: [".$device_target."] wasn't found.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
my $eject_first = 0; |
|
my $alias = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{alias}; |
|
my $boot_order = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{boot_order}; |
|
my $say_boot = $boot_order eq "99" ? "--" : sprintf("%02d", $boot_order); |
|
my $type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{type}; |
|
my $address_type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{address}{type}; |
|
my $address_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{address}{bus}; |
|
my $driver_name = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{driver}{name}; |
|
my $device_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{device_bus}; |
|
my $driver_type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{driver}{type}; |
|
my $address_controller = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{address}{controller}; |
|
my $address_unit = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{address}{unit}; |
|
my $address_target = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{address}{target}; |
|
my $device_path = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{cdrom}{target}{$device_target}{path}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's01:device_target' => $device_target, |
|
's02:alias' => $alias, |
|
's03:boot_order' => $boot_order, |
|
's04:say_boot' => $say_boot, |
|
's05:type' => $type, |
|
's06:address_type' => $address_type, |
|
's07:address_bus' => $address_bus, |
|
's08:driver_name' => $driver_name, |
|
's09:device_bus' => $device_bus, |
|
's10:driver_type' => $driver_type, |
|
's11:address_controller' => $address_controller, |
|
's12:address_unit' => $address_unit, |
|
's13:address_target' => $address_target, |
|
's14:device_path' => $device_path, |
|
}}); |
|
|
|
# Without a --source, the result is an eject. |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." change-media ".$server_name." --path ".$device_target." --update --live --config"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
if ($anvil->data->{switches}{insert}) |
|
{ |
|
# Make sure the new target exists. |
|
my $iso = $anvil->data->{switches}{insert}; |
|
if (not -f $iso) |
|
{ |
|
print "[ Error ] - The target: [".$iso."] doesn't exist, can't insert it into the optical drive.\n"; |
|
update_definition($anvil, "undefine", ""); |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
else |
|
{ |
|
$shell_call .= " --source ".$anvil->data->{switches}{insert}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
} |
|
} |
|
|
|
# If the server is running, update the on-disk and in-DB definition. |
|
print "Defining the server: [".$server_name."] to prepare for 'virsh' modification of the server.\n"; |
|
update_definition($anvil, "define", ""); |
|
|
|
# Now we can modify the server using virsh. |
|
if ($anvil->data->{switches}{insert}) |
|
{ |
|
print "- Inserting: [".$anvil->data->{switches}{insert}."] into the drive: [".$device_target."].\n"; |
|
} |
|
elsif ($anvil->data->{switches}{eject}) |
|
{ |
|
print "- Ejecting: [".$device_path."] from: [".$device_target."].\n"; |
|
} |
|
else |
|
{ |
|
# Show the ISO in the drive. |
|
if ($device_path) |
|
{ |
|
print "- Drive: [".$device_target."] has the disc image: [".$device_path."] inserted currently.\n"; |
|
print "- Use '--eject' to eject the disc.\n"; |
|
print "- Use '--insert /mnt/shared/files/<file>' to replace it with a different disc image.\n"; |
|
} |
|
else |
|
{ |
|
print "- Drive: [".$device_target."] currently has no disc image inserted.\n"; |
|
print "- Use '--insert /mnt/shared/files/<file>' to insert a disc image.\n"; |
|
} |
|
$anvil->nice_exit({exit_code => 0}); |
|
} |
|
my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:output' => $output, |
|
's2:return_code' => $return_code, |
|
}}); |
|
print "'virsh' Output: [".$output."]\n"; |
|
|
|
print "Updating the stored definition and undefining the server now:\n"; |
|
update_definition($anvil, "undefine", ""); |
|
print "Done!\n"; |
|
|
|
return(0); |
|
} |
|
|
|
sub get_definition_source |
|
{ |
|
my ($anvil) = @_; |
|
|
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $server_uuid = $anvil->data->{switches}{server_uuid}; |
|
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state}; |
|
my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid}; |
|
my $from_source = ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
short_host_name => $short_host_name, |
|
server_name => $server_name, |
|
server_uuid => $server_uuid, |
|
server_state => $server_state, |
|
from_source => $from_source, |
|
}}); |
|
|
|
$anvil->data->{server}{$short_host_name}{$server_name}{from_virsh} = "" if not exists $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}; |
|
$anvil->data->{server}{$short_host_name}{$server_name}{from_disk} = "" if not exists $anvil->data->{server}{$short_host_name}{$server_name}{from_disk}; |
|
$anvil->data->{server}{$short_host_name}{$server_name}{from_db} = "" if not exists $anvil->data->{server}{$short_host_name}{$server_name}{from_db}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"server::${short_host_name}::${server_name}::from_virsh" => $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}, |
|
"server::${short_host_name}::${server_name}::from_disk" => $anvil->data->{server}{$short_host_name}{$server_name}{from_disk}, |
|
"server::${short_host_name}::${server_name}::from_db" => $anvil->data->{server}{$short_host_name}{$server_name}{from_db}, |
|
}}); |
|
|
|
if (($server_state eq "running") && |
|
($server_host_uuid eq $anvil->Get->host_uuid()) && |
|
(exists $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh})) |
|
{ |
|
$from_source = "from_virsh"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }}); |
|
} |
|
elsif (exists $anvil->data->{server}{$short_host_name}{$server_name}{from_disk}) |
|
{ |
|
$from_source = "from_disk"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }}); |
|
} |
|
else |
|
{ |
|
$from_source = "from_db"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }}); |
|
} |
|
|
|
return($from_source); |
|
} |
|
|
|
sub show_server_details |
|
{ |
|
my ($anvil) = @_; |
|
|
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $host_uuid = $anvil->Get->host_uuid; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
's2:server_name' => $server_name, |
|
}}); |
|
|
|
# Load storage data |
|
my $drbd_resource = load_storage($anvil); |
|
|
|
my $from_source = get_definition_source($anvil); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { from_source => $from_source }}); |
|
foreach my $device ("disk", "cdrom") |
|
{ |
|
if ($device eq "disk") |
|
{ |
|
print "\nDisk Drives:\n"; |
|
} |
|
else |
|
{ |
|
print "\nOptical Drives:\n"; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device => $device }}); |
|
next if $device ne "cdrom" && $device ne "disk"; |
|
foreach my $device_target (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}}) |
|
{ |
|
my $alias = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{alias}; |
|
my $boot_order = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{boot_order}; |
|
my $say_boot = $boot_order eq "99" ? "--" : sprintf("%02d", $boot_order); |
|
my $type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{type}; |
|
my $address_type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{type}; |
|
my $address_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{bus}; |
|
my $driver_name = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{name}; |
|
my $device_bus = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{device_bus}; |
|
my $driver_type = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{type}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's01:device_target' => $device_target, |
|
's02:alias' => $alias, |
|
's03:boot_order' => $boot_order, |
|
's04:say_boot' => $say_boot, |
|
's05:type' => $type, |
|
's06:address_type' => $address_type, |
|
's07:address_bus' => $address_bus, |
|
's08:driver_name' => $driver_name, |
|
's09:device_bus' => $device_bus, |
|
's10:driver_type' => $driver_type, |
|
}}); |
|
if ($device eq "disk") |
|
{ |
|
my $address_domain = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{domain}; |
|
my $address_slot = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{slot}; |
|
my $address_function = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{function}; |
|
my $device_path = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{path}; |
|
my $driver_io = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{io}; |
|
my $driver_cache = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{driver}{cache}; |
|
my $on_lv = $anvil->data->{server}{$short_host_name}{$server_name}{device}{$device_path}{on_lv}; |
|
my $drbd_resource = $anvil->data->{server}{$short_host_name}{$server_name}{device}{$device_path}{resource}; |
|
my $drbd_volume = $anvil->data->{lvm}{host_name}{$short_host_name}{lv_path}{$on_lv}{drbd}{volume}; |
|
my $resource_size = $anvil->data->{drbd}{host_name}{$short_host_name}{resource_name}{$drbd_resource}{volume}{$drbd_volume}{volume_size}; |
|
my $lv_name = $anvil->data->{lvm}{host_name}{$short_host_name}{lv_path}{$on_lv}{scan_lvm_lv_name}; |
|
my $lv_size = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$lv_name}{scan_lvm_lv_size}; |
|
my $metadata_size = $lv_size - $resource_size; |
|
my $max_free_space = get_max_free_space($anvil, $drbd_resource, $drbd_volume); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's01:address_domain' => $address_domain, |
|
's02:address_slot' => $address_slot, |
|
's03:address_function' => $address_function, |
|
's04:device_path' => $device_path, |
|
's05:driver_io' => $driver_io, |
|
's06:driver_cache' => $driver_cache, |
|
's07:on_lv' => $on_lv, |
|
's08:lv_name' => $lv_name, |
|
's09:drbd_resource' => $drbd_resource, |
|
's10:drbd_volume' => $drbd_volume, |
|
's11:resource_size' => $anvil->Convert->bytes_to_human_readable({'bytes' => $resource_size})." (".$anvil->Convert->add_commas({number => $resource_size}).")", |
|
's12:lv_size' => $anvil->Convert->bytes_to_human_readable({'bytes' => $lv_size})."] (".$anvil->Convert->add_commas({number => $lv_size}).")", |
|
's13:metadata_size' => $anvil->Convert->bytes_to_human_readable({'bytes' => $metadata_size})."] (".$anvil->Convert->add_commas({number => $metadata_size}).")", |
|
's14:max_free_space' => $max_free_space." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space}).")", |
|
}}); |
|
|
|
print "- Target: [".$device_target."], boot: [".$say_boot."], Replication Volume: [".$drbd_resource."/".$drbd_volume."]\n"; |
|
print " |- Resource / LV / Metadata sizes: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $resource_size})." / ".$anvil->Convert->bytes_to_human_readable({'bytes' => $lv_size})." / ".$anvil->Convert->bytes_to_human_readable({'bytes' => $metadata_size})."], free space: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space})."]\n"; |
|
#print " |- Resource / LV / Metadata sizes: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $resource_size})." / ".$anvil->Convert->bytes_to_human_readable({'bytes' => $lv_size})." / ".$anvil->Convert->bytes_to_human_readable({'bytes' => $metadata_size})."], free space: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space})."], cache: [".$driver_cache."], IO Policy: [".$driver_io."]\n"; |
|
} |
|
else |
|
{ |
|
my $address_controller = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{controller}; |
|
my $address_unit = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{unit}; |
|
my $address_target = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{address}{target}; |
|
my $device_path = "<empty>"; |
|
if (defined $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{path}) |
|
{ |
|
$device_path = $anvil->data->{server}{$short_host_name}{$server_name}{$from_source}{device}{$device}{target}{$device_target}{path}; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:address_controller' => $address_controller, |
|
's2:address_unit' => $address_unit, |
|
's3:address_target' => $address_target, |
|
's4:device_path' => $device_path, |
|
}}); |
|
print "- Target: [".$device_target."], boot: [".$say_boot."], ISO: [".$device_path."]\n"; |
|
} |
|
} |
|
} |
|
print "\n"; |
|
|
|
print "Sub-Nodes:\n"; |
|
show_volume($anvil, $drbd_resource, "node"); |
|
|
|
my $dr_count = keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{dr}{short_host_name}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dr_count => $dr_count }}); |
|
if ($dr_count) |
|
{ |
|
print "DR Hosts:\n"; |
|
show_volume($anvil, $drbd_resource, "dr"); |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
sub check_drbd_peer_access |
|
{ |
|
my ($anvil) = @_; |
|
|
|
my $all_online = 1; |
|
foreach my $this_host (sort {$a cmp $b} keys %{$anvil->data->{drbd}{drbd_node}}) |
|
{ |
|
my $host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host}); |
|
my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:this_host' => $this_host, |
|
's2:host_uuid' => $host_uuid, |
|
's3:short_host_name' => $short_host_name, |
|
}}); |
|
next if $host_uuid eq $anvil->Get->host_uuid; |
|
|
|
# This is used to store the IP we used to access the peer. If no access is available, this |
|
# shows which hosts are not available. |
|
$anvil->data->{peer}{$short_host_name}{access_ip} = ""; |
|
$anvil->data->{peer}{$short_host_name}{access_network} = ""; |
|
|
|
my $access = 0; |
|
my $matches = $anvil->Network->find_access({ |
|
debug => 2, |
|
target => $this_host, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matches => $matches }}); |
|
foreach my $preferred_network ("bcn", "mn", "ifn", "sn") |
|
{ |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { preferred_network => $preferred_network }}); |
|
foreach my $network_name (sort {$a cmp $b} keys %{$anvil->data->{network_access}}) |
|
{ |
|
next if $access; |
|
next if $network_name !~ /^$preferred_network/; |
|
my $target_ip = $anvil->data->{network_access}{$network_name}{target_ip_address}; |
|
my $test_access = $anvil->Remote->test_access({target => $target_ip}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:network_name' => $network_name, |
|
's2:target_ip' => $target_ip, |
|
's3:test_access' => $test_access, |
|
}}); |
|
|
|
if ($test_access) |
|
{ |
|
# We're good. |
|
$access = 1; |
|
$anvil->data->{peer}{$short_host_name}{access}{ip} = $target_ip; |
|
$anvil->data->{peer}{$short_host_name}{access}{network} = $network_name; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:access' => $access, |
|
"s2:peer::${short_host_name}::access::ip" => $anvil->data->{peer}{$short_host_name}{access}{ip}, |
|
"s3:peer::${short_host_name}::access::network" => $anvil->data->{peer}{$short_host_name}{access}{network}, |
|
}}); |
|
} |
|
} |
|
} |
|
|
|
if (not $access) |
|
{ |
|
$all_online = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_online => $all_online }}); |
|
} |
|
} |
|
|
|
return($all_online); |
|
} |
|
|
|
sub get_max_free_space |
|
{ |
|
my ($anvil, $drbd_resource, $drbd_volume) = @_; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's01:drbd_resource' => $drbd_resource, |
|
's02:drbd_volume' => $drbd_volume, |
|
}}); |
|
|
|
my $max_free_space = 0; |
|
foreach my $this_host (sort {$a cmp $b} keys %{$anvil->data->{drbd}{drbd_node}}) |
|
{ |
|
my $drbd_path = $anvil->data->{drbd}{drbd_node}{$this_host}{config}{resource}{$drbd_resource}{volume}{$drbd_volume}{drbd_path}; |
|
my $drbd_path_by_res = $anvil->data->{drbd}{drbd_node}{$this_host}{config}{resource}{$drbd_resource}{volume}{$drbd_volume}{drbd_path_by_res}; |
|
my $backing_lv = $anvil->data->{drbd}{drbd_node}{$this_host}{config}{resource}{$drbd_resource}{volume}{$drbd_volume}{backing_lv}; |
|
my $lv_name = $anvil->data->{lvm}{host_name}{$this_host}{lv_path}{$backing_lv}{scan_lvm_lv_name} ? $anvil->data->{lvm}{host_name}{$this_host}{lv_path}{$backing_lv}{scan_lvm_lv_name} : ""; |
|
my $on_vg = $anvil->data->{lvm}{host_name}{$this_host}{lv}{$lv_name}{scan_lvm_lv_on_vg} ? $anvil->data->{lvm}{host_name}{$this_host}{lv}{$lv_name}{scan_lvm_lv_on_vg} : ""; |
|
my $vg_free_space = $anvil->data->{lvm}{host_name}{$this_host}{vg}{$on_vg}{scan_lvm_vg_free} ? $anvil->data->{lvm}{host_name}{$this_host}{vg}{$on_vg}{scan_lvm_vg_free} : 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's01:this_host' => $this_host, |
|
's02:drbd_path' => $drbd_path, |
|
's03:drbd_path_by_res' => $drbd_path_by_res, |
|
's04:backing_lv' => $backing_lv, |
|
's05:on_vg' => $on_vg, |
|
's06:vg_free_space' => $vg_free_space." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_free_space}).")", |
|
}}); |
|
if ((not $max_free_space) or ($vg_free_space < $max_free_space)) |
|
{ |
|
$max_free_space = $vg_free_space; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
max_free_space => $max_free_space." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space}).")", |
|
}}); |
|
} |
|
} |
|
|
|
return($max_free_space); |
|
} |
|
|
|
sub load_drbd_data |
|
{ |
|
my ($anvil, $drbd_resource) = @_; |
|
|
|
my $query = " |
|
SELECT |
|
a.host_uuid, |
|
b.scan_drbd_resource_xml, |
|
c.scan_drbd_volume_number, |
|
c.scan_drbd_volume_device_path, |
|
c.scan_drbd_volume_device_minor, |
|
c.scan_drbd_volume_size |
|
FROM |
|
hosts a, |
|
scan_drbd_resources b, |
|
scan_drbd_volumes c |
|
WHERE |
|
a.host_uuid = b.scan_drbd_resource_host_uuid |
|
AND |
|
b.scan_drbd_resource_uuid = c.scan_drbd_volume_scan_drbd_resource_uuid |
|
AND |
|
b.scan_drbd_resource_name = ".$anvil->Database->quote($drbd_resource)." |
|
;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
|
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); |
|
my $count = @{$results}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
results => $results, |
|
count => $count, |
|
}}); |
|
foreach my $row (@{$results}) |
|
{ |
|
my $host_uuid = $row->[0]; |
|
my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name}; |
|
my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; |
|
my $scan_drbd_volume_number = $row->[2]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:host_uuid' => $host_uuid, |
|
's2:host_type' => $host_type, |
|
's3:short_host_name' => $short_host_name, |
|
's4:scan_drbd_volume_number' => $scan_drbd_volume_number, |
|
}}); |
|
|
|
$anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$scan_drbd_volume_number}{device_path} = $row->[3]; |
|
$anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$scan_drbd_volume_number}{device_minor} = $row->[4]; |
|
$anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$scan_drbd_volume_number}{volume_size} = $row->[5]; |
|
$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid} = $host_uuid; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"s1:drbd_resource::${drbd_resource}::host_uuid::${host_uuid}::volume_number::${scan_drbd_volume_number}::device_path" => $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$scan_drbd_volume_number}{device_path}, |
|
"s2:drbd_resource::${drbd_resource}::host_uuid::${host_uuid}::volume_number::${scan_drbd_volume_number}::device_minor" => $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$scan_drbd_volume_number}{device_minor}, |
|
"s3:drbd_resource::${drbd_resource}::host_uuid::${host_uuid}::volume_number::${scan_drbd_volume_number}::volume_size" => $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$scan_drbd_volume_number}{volume_size}, |
|
"s4:drbd_resource::${drbd_resource}::host_type::${host_type}::short_host_name::${short_host_name}::host_uuid" => $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}, |
|
}}); |
|
|
|
if (not exists $anvil->data->{drbd_resource}{$drbd_resource}{xml}) |
|
{ |
|
$anvil->data->{drbd_resource}{$drbd_resource}{xml} = $row->[1]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"drbd_resource::${drbd_resource}::xml" => $anvil->data->{drbd_resource}{$drbd_resource}{xml}, |
|
}}); |
|
$anvil->DRBD->parse_resource({ |
|
debug => 2, |
|
xml => $anvil->data->{drbd_resource}{$drbd_resource}{xml}, |
|
}); |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
sub show_volume |
|
{ |
|
my ($anvil, $drbd_resource, $host_type) = @_; |
|
|
|
my $anvil_uuid = defined $anvil->data->{switches}{anvil_uuid} ? $anvil->data->{switches}{anvil_uuid} : $anvil->Cluster->get_anvil_uuid(); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }}); |
|
foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) |
|
{ |
|
my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:host_uuid' => $host_uuid, |
|
}}); |
|
print " |- Name: [".$short_host_name."], UUID: [".$host_uuid."]\n"; |
|
foreach my $volume_number (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}}) |
|
{ |
|
my $device_path = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{device_path}; |
|
my $device_minor = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{device_minor}; |
|
my $volume_size = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{volume_size}; |
|
my $backing_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{backing_disk}; |
|
my $meta_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{meta_disk}; |
|
my $storage_group_uuid = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{storage_group_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:volume_number' => $volume_number, |
|
's2:device_path' => $device_path, |
|
's3:device_minor' => $device_minor, |
|
's4:volume_size' => $volume_size, |
|
's5:backing_disk' => $backing_disk, |
|
's6:meta_disk' => $meta_disk, |
|
's7:storage_group_uuid' => $storage_group_uuid, |
|
}}); |
|
my $storage_group_name = "<unknown>"; |
|
my $storage_group_size = 0; |
|
my $storage_group_free_space = 0; |
|
if (($storage_group_uuid) && ($anvil_uuid) && (exists $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid})) |
|
{ |
|
$storage_group_name = $anvil->data->{storage_groups}{storage_group_uuid}{$storage_group_uuid}{storage_group_name}; |
|
$storage_group_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; |
|
$storage_group_free_space = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:storage_group_size' => $storage_group_size, |
|
's2:storage_group_free_space' => $storage_group_free_space, |
|
}}); |
|
print " |- Volume: [".$volume_number."], backing device: [".$backing_disk."], DRBD minor: [".$device_minor."], size: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $volume_size})."]\n"; |
|
print " ^- In storage group: [".$storage_group_name."], size: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $storage_group_size})."], free: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $storage_group_free_space})."]\n"; |
|
|
|
# How much space is available in the storage group this volume is in? |
|
foreach my $this_scan_lvm_lv_name (sort {$a cmp $b} keys %{$anvil->data->{lvm}{host_name}{$short_host_name}{lv}}) |
|
{ |
|
my $this_scan_lvm_lv_path = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$this_scan_lvm_lv_name}{scan_lvm_lv_path}; |
|
my $this_scan_lvm_lv_on_vg = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$this_scan_lvm_lv_name}{scan_lvm_lv_on_vg}; |
|
my $this_scan_lvm_lv_uuid = $anvil->data->{lvm}{host_name}{$short_host_name}{lv}{$this_scan_lvm_lv_name}{scan_lvm_lv_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:this_scan_lvm_lv_name' => $this_scan_lvm_lv_name, |
|
's2:this_scan_lvm_lv_path' => $this_scan_lvm_lv_path, |
|
's3:this_scan_lvm_lv_on_vg' => $this_scan_lvm_lv_on_vg, |
|
's4:this_scan_lvm_lv_uuid' => $this_scan_lvm_lv_uuid, |
|
}}); |
|
if ($backing_disk eq $this_scan_lvm_lv_path) |
|
{ |
|
# What's the VG's UUID? |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:this_scan_lvm_lv_on_vg' => $this_scan_lvm_lv_on_vg, |
|
}}); |
|
if (exists $anvil->data->{lvm}{host_name}{$short_host_name}{vg}{$this_scan_lvm_lv_on_vg}) |
|
{ |
|
my $scan_lvm_vg_internal_uuid = $anvil->data->{lvm}{host_name}{$short_host_name}{vg}{$this_scan_lvm_lv_on_vg}{scan_lvm_vg_internal_uuid}; |
|
my $storage_group_uuid = $anvil->data->{lvm}{host_name}{$short_host_name}{vg}{$this_scan_lvm_lv_on_vg}{storage_group_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:scan_lvm_vg_internal_uuid' => $scan_lvm_vg_internal_uuid, |
|
's2:storage_group_uuid' => $storage_group_uuid, |
|
}}); |
|
|
|
if ($storage_group_uuid) |
|
{ |
|
my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; |
|
my $free_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size}; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
sub validate_server |
|
{ |
|
my ($anvil) = @_; |
|
|
|
$anvil->Get->server_from_switch({ |
|
debug => 2, |
|
string => $anvil->data->{switches}{server}, |
|
anvil_uuid => $anvil->data->{switches}{anvil_uuid}, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"switches::server_name" => $anvil->data->{switches}{server_name}, |
|
"switches::server_uuid" => $anvil->data->{switches}{server_uuid}, |
|
}}); |
|
|
|
if (not $anvil->data->{switches}{server_uuid}) |
|
{ |
|
show_server_list($anvil); |
|
if ($anvil->data->{switches}{anvil_uuid}) |
|
{ |
|
# Not found on the requested Anvil! node. |
|
print "\nThe server: [".$anvil->data->{switches}{server}."] was not found on the Anvil! node: [".$anvil->data->{switches}{anvil_name}."]. Valid servers are above.\n\n"; |
|
} |
|
else |
|
{ |
|
# Not found at all. |
|
print "\nThe server: [".$anvil->data->{switches}{server}."] was not found. Valid servers are above.\n\n"; |
|
} |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
print "Working with the server: [".$anvil->data->{switches}{server_name}."], UUID: [".$anvil->data->{switches}{server_uuid}."]\n"; |
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $server_uuid = $anvil->data->{switches}{server_uuid}; |
|
my $server_definition = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml}; |
|
my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid}; |
|
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state}; |
|
my $anvil_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:server_name' => $server_name, |
|
's3:server_uuid' => $server_uuid, |
|
's4:server_definition' => $server_definition, |
|
's5:server_host_uuid' => $server_host_uuid, |
|
's6:server_state' => $server_state, |
|
's7:anvil_uuid' => $anvil_uuid, |
|
}}); |
|
if (not $anvil->data->{switches}{anvil_uuid}) |
|
{ |
|
$anvil->data->{switches}{anvil_uuid} = $anvil_uuid; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:switches::anvil_uuid' => $anvil->data->{switches}{anvil_uuid}, |
|
}}); |
|
} |
|
|
|
if ($server_state eq "DELETED") |
|
{ |
|
print "The server: [".$server_name."] has been deleted.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Parse the definition. |
|
$anvil->Server->parse_definition({ |
|
debug => 3, |
|
host => $short_host_name, |
|
server => $server_name, |
|
source => "from_db", |
|
definition => $server_definition, |
|
}); |
|
|
|
# Can we read the XML definition? |
|
$anvil->Server->get_status({ |
|
debug => 2, |
|
server => $server_name, |
|
host => $short_host_name, |
|
}); |
|
|
|
if (not $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{xml}) |
|
{ |
|
# The server isn't actually running... Not here anyway. |
|
if ($server_state eq "running") |
|
{ |
|
my $server_host_name = $anvil->Database->get_host_from_uuid({ |
|
short => 1, |
|
host_uuid => $server_host_uuid, |
|
}); |
|
|
|
print "The server: [".$server_name."] appears to be running on: [".$server_host_name."], please run this on that host.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# Update the definition on disk and in the DB, and define or undefine if requested. |
|
sub update_definition |
|
{ |
|
my ($anvil, $task, $definition) = @_; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
task => $task, |
|
definition => $definition, |
|
}}); |
|
|
|
my $short_host_name = $anvil->Get->short_host_name; |
|
my $server_name = $anvil->data->{switches}{server_name}; |
|
my $server_uuid = $anvil->data->{switches}{server_uuid}; |
|
my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid}; |
|
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state}; |
|
my $definition_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server_name.".xml"; |
|
my $server_running_here = 0; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:short_host_name' => $short_host_name, |
|
's2:server_name' => $server_name, |
|
's3:server_uuid' => $server_uuid, |
|
's5:server_host_uuid' => $server_host_uuid, |
|
's6:server_state' => $server_state, |
|
's7:definition_file' => $definition_file, |
|
}}); |
|
|
|
# See if the server is running locally |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." list --name"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:output' => $output, |
|
's2:return_code' => $return_code, |
|
}}); |
|
foreach my $this_server (split/\n/, $output) |
|
{ |
|
$this_server = $anvil->Words->clean_spaces({string => $this_server}); |
|
next if not $this_server; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { this_server => $this_server }}); |
|
|
|
if ($this_server eq $server_name) |
|
{ |
|
$server_running_here = 1; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_running_here => $server_running_here }}); |
|
last; |
|
} |
|
} |
|
|
|
my $disk_definition = $anvil->data->{server}{$short_host_name}{$server_name}{from_disk}{xml} ? $anvil->data->{server}{$short_host_name}{$server_name}{from_disk}{xml} : ""; |
|
my $virsh_definition = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{xml} ? $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{xml} : ""; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
's1:disk_definition' => $disk_definition, |
|
's2:virsh_definition' => $virsh_definition, |
|
}}); |
|
my $use_definition = ""; |
|
if ($definition) |
|
{ |
|
# Use the passed in definition. |
|
$use_definition = $definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
} |
|
else |
|
{ |
|
$use_definition = $virsh_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
if (not $use_definition) |
|
{ |
|
if (($server_running_here) or (($server_state eq "running") && ($virsh_definition))) |
|
{ |
|
# Get the live definition |
|
if ($server_running_here) |
|
{ |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server_name; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
my ($live_virsh_definition, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
live_virsh_definition => $live_virsh_definition, |
|
return_code => $return_code, |
|
}}); |
|
|
|
my ($problem) = $anvil->Server->parse_definition({ |
|
server => $server_name, |
|
source => "from_virsh", |
|
definition => $live_virsh_definition, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
|
if (not $problem) |
|
{ |
|
$use_definition = $live_virsh_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
|
|
$anvil->Server->parse_definition({ |
|
debug => 3, |
|
host => $short_host_name, |
|
server => $server_name, |
|
source => "from_virsh", |
|
definition => $live_virsh_definition, |
|
}); |
|
} |
|
else |
|
{ |
|
$use_definition = $virsh_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
|
|
$anvil->Server->parse_definition({ |
|
debug => 3, |
|
host => $short_host_name, |
|
server => $server_name, |
|
source => "from_virsh", |
|
definition => $virsh_definition, |
|
}); |
|
} |
|
} |
|
else |
|
{ |
|
$use_definition = $virsh_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
|
|
$anvil->Server->parse_definition({ |
|
debug => 3, |
|
host => $short_host_name, |
|
server => $server_name, |
|
source => "from_virsh", |
|
definition => $virsh_definition, |
|
}); |
|
} |
|
} |
|
else |
|
{ |
|
$use_definition = $disk_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { use_definition => $use_definition }}); |
|
|
|
$anvil->Server->parse_definition({ |
|
debug => 3, |
|
host => $short_host_name, |
|
server => $server_name, |
|
source => "from_disk", |
|
definition => $virsh_definition, |
|
}); |
|
} |
|
} |
|
|
|
if (not $use_definition) |
|
{ |
|
# What?! |
|
print "[ Error ] - Failed to find an on disk or from virsh definition for the server: [".$server_name."]. Unable to proceed.\n"; |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# Update the stored definition |
|
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml} = $use_definition; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"servers::server_uuid::${server_uuid}::server_definition_xml" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml}, |
|
}}); |
|
|
|
# Update the on-disk definition |
|
my ($failed) = $anvil->Storage->write_file({ |
|
secure => 1, |
|
file => $definition_file, |
|
body => $use_definition, |
|
overwrite => 1, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }}); |
|
my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({ |
|
debug => 2, |
|
server_definition_xml => $use_definition, |
|
server_definition_server_uuid => $server_uuid, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server_definition_uuid => $server_definition_uuid }}); |
|
|
|
if ($task eq "define") |
|
{ |
|
# Define the server. |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." define ".$definition_file, |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
} |
|
elsif ($task eq "undefine") |
|
{ |
|
# Undefine the server. |
|
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." undefine ".$server_name, |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); |
|
|
|
my ($output, $return_code) = $anvil->System->call({ |
|
debug => 2, |
|
shell_call => $shell_call, |
|
source => $THIS_FILE, |
|
line => __LINE__, |
|
}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
output => $output, |
|
return_code => $return_code, |
|
}}); |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
sub show_server_list |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Loop through all Anvil! nodes, then all server in that Anvil! |
|
foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) |
|
{ |
|
my $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid}; |
|
my $anvil_description = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
anvil_name => $anvil_name, |
|
anvil_uuid => $anvil_uuid, |
|
anvil_description => $anvil_description, |
|
}}); |
|
if (($anvil->data->{switches}{anvil_uuid}) && ($anvil->data->{switches}{anvil_uuid} ne $anvil_uuid)) |
|
{ |
|
next; |
|
} |
|
print "\nAnvil! Node: [".$anvil_name."], UUID: [".$anvil_uuid."] - Description: [".$anvil_description."]\n"; |
|
|
|
my $server_count = 0; |
|
if (exists $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}) |
|
{ |
|
$server_count = keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_count => $server_count }}); |
|
} |
|
if (not $server_count) |
|
{ |
|
print "- No servers on this node yet\n"; |
|
} |
|
else |
|
{ |
|
foreach my $server_name (sort {$a cmp $b} keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}}) |
|
{ |
|
my $server_uuid = $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$server_name}{server_uuid}; |
|
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state}; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
server_name => $server_name, |
|
server_uuid => $server_uuid, |
|
server_state => $server_state, |
|
}}); |
|
next if $server_state eq "DELETED"; |
|
print "^- Server: [".$server_name."], UUID: [".$server_uuid."]\n"; |
|
} |
|
} |
|
} |
|
|
|
return(0); |
|
}
|
|
|