#!/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(); # Read switches (target ([user@]host[:port]) and the file with the target's password. $anvil->Get->switches({list => [ "add", "anvil", "confirm", "disk", "eject", "job-uuid", "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); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0456"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0457"}); 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); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0458"}); $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); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0459", variables => { device_target => $device_target, }}); $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}).")", }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0749", variables => { device_target => $device_target, say_boot => $say_boot, device_path => $device_path, max_free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space}), }}); # 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 $say_up = $anvil->Words->string({key => 'message_0331'}); my $say_down = $anvil->Words->string({key => 'message_0332'}); # Do we have a storage group? if (not $anvil->data->{switches}{'storage-group'}) { print $anvil->Words->string({key => 'warning_0159'})."\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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0423", variables => { free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $free_space}), }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "message_0330"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0424", variables => { add_size => $add_size }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "message_0330"}); $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) { # Failed to find a new target device name $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0425"}); $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 "") { # Failed to find a new DRBD volume to use. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0426"}); $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) { ### TODO: Sleep for 60 and then reset the job_progress to 0 and exit so that we keep trying. # Adding a new disk requires all peers to be online. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0427"}); foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) { # Record the access my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? $say_up : $say_down; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0428", variables => { host_name => $short_host_name, access => $say_access, }}); } $anvil->nice_exit({exit_code => 1}); } # Still alive? Ask the user to confirm. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0428", variables => { new_device_target => $new_device_target, hr_size => $hr_size, disk_device_bus => $disk_device_bus, disk_cache => $disk_cache, disk_io_policy => $disk_io_policy, drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, storage_group_name => $storage_group_name, }}); if ((not $anvil->data->{switches}{confirm}) && (not $anvil->data->{switches}{'job-uuid'})) { print $anvil->Words->string({key => "message_0021"})." "; my $answer = ; chomp($answer); if ($answer !~ /^y/i) { print $anvil->Words->string({key => "message_0022"})."\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) { # It would appear that we've lost access to a peer while waiting for the answer. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0429"}); foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) { # Record the access my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? $say_up : $say_down; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0428", variables => { host_name => $short_host_name, access => $say_access, }}); } $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) { # Creating the new local LV $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0748", variables => { backing_disk => $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0460", variables => { backing_disk => $backing_disk, shell_call => $shell_call, return_code => $return_code, output => $output, }}); $anvil->nice_exit({exit_code => 1}); } else { # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } 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->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0756", variables => { host_name => $short_host_name, backing_disk => $backing_disk, use_ip => $use_ip, network => $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0461", variables => { host_name => $short_host_name, backing_disk => $backing_disk, shell_call => $shell_call, return_code => $return_code, output => $output, error => $error, }}); $anvil->nice_exit({exit_code => 1}); } else { # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } } } # 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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0751"}); 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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0430", variables => { test_file => $test_file, return_code => $return_code, output => $output, }}); $anvil->nice_exit({exit_code => 1}); } # Success! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0752"}); # 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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0757"}); 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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0753"}); 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; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0754", variables => { resource_file => $drbd_res_file, host_name => $short_host_name, destination => $destination, use_ip => $use_ip, }}); $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) { # There was a problem copying the new config file! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0431"}); $anvil->nice_exit({exit_code => 1}); } } } # Create the metadata. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0755"}); 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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0758", variables => { next_drbd_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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "alert", key => "warning_0160", variables => { drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, shell_call => $shell_call, return_code => $return_code, output => $output, }}); } else { # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } else { # Create the meta-data on the peer my $use_ip = $anvil->data->{peer}{$short_host_name}{access}{ip}; my $use_network = $anvil->data->{peer}{$short_host_name}{access}{network}; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0759", variables => { host_name => $short_host_name, drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, use_ip => $use_ip, network => $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "alert", key => "warning_0161", variables => { host_name => $short_host_name, drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, shell_call => $shell_call, return_code => $return_code, output => $output, error => $error, }}); } else { # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } } } ### NOTE: The call to 'drbdadm adjust ' 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 }}); # Record that we registered the job to reload the resource $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0760", variables => { host_name => $short_host_name, job_uuid => $job_uuid, }}); } } # Warn the user to check that anvil-daemon is running in case this hangs. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0761", variables => { drbd_resource => $drbd_resource }}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0762"}); 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) { # The peers did not connect in the expected period of time. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0432"}); $anvil->nice_exit({exit_code => 1}); } sleep 2; } } # Peers are connected! Checking if the new volume requires initial sync. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0763"}); 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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0764"}); 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) { # Forcing primary locally... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0765"}); 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) { # There was a problem trying to force the new volume to Primary. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0430", variables => { drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, shell_call => $shell_call, return_code => $return_code, output => $output, }}); $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, }}); # Success! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0752"}); } 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, }}); # The resource is primary on the peer, force Primary over there. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0766", variables => { host_name => $short_host_name, use_ip => $use_ip, use_network => $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0430", variables => { drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, shell_call => $shell_call, return_code => $return_code, output => $output, }}); $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, }}); # Success! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0752"}); } } else { # Initial sync does not appear to be required. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0767"}); } # Is the server running? If so, where. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0768"}); 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) { # The server is running on this host, we'll attach the disk here. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0769"}); } else { # The server is running on the peer, we'll attach the disk there. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0770", variables => { server_host => $server_host, }}); } last; } } } } my $offline = 0; if (not $server_host) { # The server isn't running anywhere, we'll attach the disk offline on this host. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0771"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0772", variables => { server_name => $server_name }}); update_definition($anvil, "define", ""); # Update the definition. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0773"}); ($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, }}); # Updating the stored definition and undefining the server now... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0774"}); update_definition($anvil, "undefine", ""); # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); $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", ""); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0775"}); ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); if ($return_code) { # Something went wrong. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0138", variables => { shell_call => $shell_call, return_code => $return_code, output => $output, }}); $anvil->nice_exit({exit_code => 1}); } # Get the new XML $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0776"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0777"}); 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?! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0434"}); $anvil->nice_exit({exit_code => 1}); } # Update the stored definition and undefining the server $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0778"}); update_definition($anvil, "undefine", $virsh_definition); # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); $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 }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0779", variables => { server_host => $server_host, use_ip => $use_ip, }}); 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?! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0435"}); $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}, }}); # Updating the stored definition and undefining the server $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0780"}); update_definition($anvil, "undefine", $virsh_definition); # Done! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0781", variables => { peer_name => $peer_name }}); 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, }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0782"}); 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, }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); # Now wait $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0783"}); 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'}; $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? $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0784", variables => { drbd_resource => $drbd_resource, next_drbd_volume => $next_drbd_volume, }}); $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") { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0785"}); $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")) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0786"}); $disks_ready = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disks_ready => $disks_ready }}); } if ($disks_ready) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0787", variables => { peer_disk_state => $peer_disk_state }}); $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) { # Timed out waiting for the peer to connect. $waiting = 0; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0788"}); 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 $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0436", variables => { max_free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space}), }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0789"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0437"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0789"}); $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0438", variables => { add_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $add_size}), }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0789"}); $anvil->nice_exit({exit_code => 1}); } elsif ($add_size > $max_free_space) { # Not enough space. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0439", variables => { add_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $add_size}), max_free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $max_free_space}), }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0789"}); $anvil->nice_exit({exit_code => 1}); } # Are they asking for an available amount of space? my $say_up = $anvil->Words->string({key => 'message_0331'}); my $say_down = $anvil->Words->string({key => 'message_0332'}); ### 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) { # All peers have to be online. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0440"}); foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) { my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? $say_up : $say_down; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0428", variables => { host_name => $short_host_name, access => $say_access, }}); } $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 }}); } # Preparing to grow the storage $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0790", variables => { size => $hr_size }}); if (not $anvil->data->{switches}{confirm}) { print $anvil->Words->string({key => "message_0021"})." "; my $answer = ; chomp($answer); if ($answer !~ /^y/i) { # Abort $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0022"}); $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0441"}); foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{peer}}) { my $say_access = $anvil->data->{peer}{$short_host_name}{access_ip} ? $say_up : $say_down; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0428", variables => { host_name => $short_host_name, access => $say_access, }}); } $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0791", variables => { backing_disk => $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0442", variables => { backing_disk => $backing_disk, shell_call => $shell_call, return_code => $return_code, output => $output, }}); $anvil->nice_exit({exit_code => 1}); } else { # Done $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } 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->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0792", variables => { host_name => $short_host_name, backing_disk => $backing_disk, use_ip => $use_ip, use_network => $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0443", variables => { backing_disk => $backing_disk, shell_call => $shell_call, return_code => $return_code, output => $output, error => $error, }}); $anvil->nice_exit({exit_code => 1}); } else { # Done $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } } } # Locally, we'll call DRBD to resize. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0793"}); 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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0444", variables => { drbd_resource => $drbd_resource, drbd_volume => $drbd_volume, shell_call => $shell_call, return_code => $return_code, output => $output, }}); $anvil->nice_exit({exit_code => 1}); } else { # Done $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } # Call scan-lvm and scan-drbd to make sure the databases are updated. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0794"}); 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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0795", variables => { agent => $agent }}); 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, }}); # Done $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } 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->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0796", variables => { agent => $agent, host_name => $short_host_name, use_ip => $use_ip, use_network => $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, }}); # Done $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); } } } } # Success! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0797"}); 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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0445"}); 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); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0446"}); $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); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0447", variables => { device_target => $device_target, }}); $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) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0448", variables => { iso => $iso, }}); 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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0798", variables => { server_name => $server_name }}); update_definition($anvil, "define", ""); # Now we can modify the server using virsh. if ($anvil->data->{switches}{insert}) { # Inserting $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0799", variables => { insert => $anvil->data->{switches}{insert}, device_target => $device_target, }}); } elsif ($anvil->data->{switches}{eject}) { # Ejecting $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0800", variables => { eject => $device_path, device_target => $device_target, }}); } else { # Show the ISO in the drive. if ($device_path) { # Disc is already in the drive $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0449", variables => { device_target => $device_target, device_path => $device_path, }}); } else { # Drive has no disc in it. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0450", variables => { device_target => $device_target, }}); } $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, }}); # Updating the stored definition and undefining the server now $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0801"}); update_definition($anvil, "undefine", ""); # Done $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0750"}); 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") { # Disk drives print "\n".$anvil->Words->string({key => "message_0334"})."\n"; } else { # Optical drives print "\n".$anvil->Words->string({key => "message_0335"})."\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 $anvil->Words->string({key => "message_0336", variables => { device_target => $device_target, say_boot => $say_boot, drbd_resource => $drbd_resource, drbd_volume => $drbd_volume, }})."\n"; print $anvil->Words->string({key => "message_0337", variables => { resource_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $resource_size}), lv_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $lv_size}), metadata_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $metadata_size}), max_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 = ""; 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 $anvil->Words->string({key => "message_0338", variables => { device_target => $device_target, say_boot => $say_boot, device_path => $device_path, }})."\n"; } } } print "\n"; print $anvil->Words->string({key => "message_0339"})."\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 $anvil->Words->string({key => "message_0340"})."\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", "any") { $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; if (($network_name !~ /^$preferred_network/) && ($preferred_network ne "any")) { next; } 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 $anvil->Words->string({key => "message_0341", variables => { host_name => $short_host_name, host_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 = ""; 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 $anvil->Words->string({key => "message_0342", variables => { volume_number => $volume_number, backing_disk => $backing_disk, device_minor => $device_minor, volume_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $volume_size}), storage_group_name => $storage_group_name, storage_group_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $storage_group_size}), storage_group_free_space => $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. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0451", variables => { server => $anvil->data->{switches}{server}, anvil => $anvil->data->{switches}{anvil_name}, }}); } else { # Not found at all. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0452", variables => { server => $anvil->data->{switches}{server}, }}); } $anvil->nice_exit({exit_code => 1}); } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0802", variables => { server_name => $anvil->data->{switches}{server_name}, server_uuid => $anvil->data->{switches}{server_uuid}, }}); 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") { # The server has been deleted $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0453", variables => { server => $server_name, }}); $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, }); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0454", variables => { server => $server_name, host_name => $server_host_name, }}); $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?! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0455", variables => { server => $server_name, }}); $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 "\n".$anvil->Words->string({key => "message_0343", variables => { anvil_name => $anvil_name, anvil_uuid => $anvil_uuid, anvil_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 $anvil->Words->string({key => "message_0344"})."\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 $anvil->Words->string({key => "message_0345", variables => { server_name => $server_name, server_uuid => $server_uuid, }})."\n"; } } } return(0); }