From 7773e5f9b8b7970fb3705b25847b8e2a38d9933f Mon Sep 17 00:00:00 2001 From: digimer Date: Mon, 30 Jan 2023 11:30:36 -0500 Subject: [PATCH 1/8] * Updated logging in DRBD->get_devices(). * Added a check and exit if anvil-manage-dr is asked to protect a server on a machine that doesn't know about that server. Signed-off-by: digimer --- Anvil/Tools/DRBD.pm | 6 +++++- Anvil/Tools/Server.pm | 6 ++++-- share/words.xml | 1 + tools/anvil-manage-dr | 12 +++++++++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Anvil/Tools/DRBD.pm b/Anvil/Tools/DRBD.pm index 457abf43..76dc739f 100644 --- a/Anvil/Tools/DRBD.pm +++ b/Anvil/Tools/DRBD.pm @@ -1492,7 +1492,7 @@ LIMIT 1 my $value = $option_ref->{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { variable => $variable, - value => $variable, + value => $value, }}); if ($variable eq "auto-promote") { @@ -1581,10 +1581,14 @@ LIMIT 1 if (($anvil->data->{drbd}{config}{$host}{host}) && ($anvil->data->{drbd}{config}{$host}{host} eq $this_host)) { + $anvil->data->{drbd}{config}{$host}{by_res}{$by_res}{on} = $lv_path; + $anvil->data->{drbd}{config}{$host}{by_res}{$by_res}{resource} = $this_resource; $anvil->data->{drbd}{config}{$host}{drbd_path}{$drbd_path}{on} = $lv_path; $anvil->data->{drbd}{config}{$host}{drbd_path}{$drbd_path}{resource} = $this_resource; $anvil->data->{drbd}{config}{$host}{lv_path}{$lv_path}{under} = $drbd_path; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "drbd::config::${host}::by_res::${by_res}::on" => $anvil->data->{drbd}{config}{$host}{by_res}{$by_res}{on}, + "drbd::config::${host}::by_res::${by_res}::resource" => $anvil->data->{drbd}{config}{$host}{by_res}{$by_res}{resource}, "drbd::config::${host}::drbd_path::${drbd_path}::on" => $anvil->data->{drbd}{config}{$host}{drbd_path}{$drbd_path}{on}, "drbd::config::${host}::drbd_path::${drbd_path}::resource" => $anvil->data->{drbd}{config}{$host}{drbd_path}{$drbd_path}{resource}, "drbd::config::${host}::lv_path::${lv_path}::under" => $anvil->data->{drbd}{config}{$host}{lv_path}{$lv_path}{under}, diff --git a/Anvil/Tools/Server.pm b/Anvil/Tools/Server.pm index 76bfec78..d3b169a7 100644 --- a/Anvil/Tools/Server.pm +++ b/Anvil/Tools/Server.pm @@ -1832,8 +1832,10 @@ sub parse_definition my $on_lv = defined $anvil->data->{drbd}{config}{$host}{drbd_path}{$device_path}{on} ? $anvil->data->{drbd}{config}{$host}{drbd_path}{$device_path}{on} : ""; my $resource = defined $anvil->data->{drbd}{config}{$host}{drbd_path}{$device_path}{resource} ? $anvil->data->{drbd}{config}{$host}{drbd_path}{$device_path}{resource} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - on_lv => $on_lv, - resource => $resource, + 's1:host' => $host, + 's2:device_path' => $device_path, + 's3:on_lv' => $on_lv, + 's4:resource' => $resource, }}); if ((not $resource) && ($anvil->data->{drbd}{config}{$host}{'by-res'}{$device_path}{resource})) { diff --git a/share/words.xml b/share/words.xml index 2341622f..baddc0d7 100644 --- a/share/words.xml +++ b/share/words.xml @@ -2859,6 +2859,7 @@ Proceed? [y/N] Proceed? [y/N] This is a test alert message sent at alert level: [#!variable!level!#]. Failed to send a test alert at level: [#!variable!level!#]. Is anyone listening at that level? Is the mail server configured? + The DRBD config file was not found. A protect job needs to be run from the Anvil! node hosting the server to be protected. Normal Password diff --git a/tools/anvil-manage-dr b/tools/anvil-manage-dr index 5fe20a2f..5ee9e41d 100755 --- a/tools/anvil-manage-dr +++ b/tools/anvil-manage-dr @@ -2174,7 +2174,7 @@ sub process_protect }); $anvil->DRBD->gather_data({debug => 2}); - + my $server_ram = $anvil->data->{server}{$short_host_name}{$server_name}{'from_db'}{memory}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_ram => $anvil->Convert->add_commas({number => $server_ram})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $server_ram}).")", @@ -2182,6 +2182,7 @@ sub process_protect foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{drbd}{resource}}) { + next if $resource eq ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { resource => $resource }}); foreach my $this_host_name (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$resource}{host}}) @@ -2559,6 +2560,15 @@ sub process_protect my $record_job = 0; if (not $anvil->data->{switches}{Yes}) { + # If there's no config file, this might be being run from DR which doesn't have info about + # the resource yet. + if ((not $config_file) && (not $anvil->data->{switches}{'job-uuid'})) + { + # Bail out. + print $anvil->Words->string({key => "message_0308"})."\n"; + $anvil->nice_exit({exit_code => 1}); + } + # Ask the user to confirm. print "\n".$anvil->Words->string({key => "message_0021"})."\n"; my $answer = ; From 9751c883cbe8785d2dd6445063b84953121b5f45 Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 1 Feb 2023 23:19:38 -0500 Subject: [PATCH 2/8] * Updated Cluster->assemble_storage_groups() to remove refrences to anvil_dr1_host_uuid. Also added the logic for auto-adding DR host's VGs to a storage group. Commented it out though as, for now, this might be a bad idea. Needs more thought. * Fixed a bug in Database->get_storage_group_data() to load hosts data when needed. Also fixed a bug where new members didn't return the new storage_group_member_uuid. * Updated anvil-manage-host to use the new switch handler. Signed-off-by: digimer --- Anvil/Tools/Cluster.pm | 175 +++++++++++++++++++++++++++++++--------- Anvil/Tools/Database.pm | 8 +- tools/anvil-manage-dr | 2 + tools/anvil-manage-host | 31 +++---- 4 files changed, 156 insertions(+), 60 deletions(-) diff --git a/Anvil/Tools/Cluster.pm b/Anvil/Tools/Cluster.pm index 11a2c55e..3b9fe02a 100644 --- a/Anvil/Tools/Cluster.pm +++ b/Anvil/Tools/Cluster.pm @@ -299,8 +299,7 @@ sub assemble_storage_groups SELECT anvil_name, anvil_node1_host_uuid, - anvil_node2_host_uuid, - anvil_dr1_host_uuid + anvil_node2_host_uuid FROM anvils WHERE @@ -321,15 +320,13 @@ WHERE } # Get the details. - my $anvil_name = $results->[0]->[0]; - my $node1_host_uuid = $results->[0]->[1]; - my $node2_host_uuid = $results->[0]->[2]; - my $dr1_host_uuid = defined $results->[0]->[3] ? $results->[0]->[3] : ""; + my $anvil_name = $results->[0]->[0]; + my $node1_host_uuid = $results->[0]->[1]; + my $node2_host_uuid = $results->[0]->[2]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_name => $anvil_name, node1_host_uuid => $node1_host_uuid, node2_host_uuid => $node2_host_uuid, - dr1_host_uuid => $dr1_host_uuid, }}); # Load known storage groups. @@ -337,20 +334,9 @@ WHERE # Look for ungrouped VGs and see if we can group them by matching identical sizes together. my $hosts = [$node1_host_uuid, $node2_host_uuid]; - if ($dr1_host_uuid) - { - push @{$hosts}, $dr1_host_uuid; - } - else - { - # No DR. - $anvil->data->{ungrouped_vg_count}{dr1} = 0; - } foreach my $host_uuid (@{$hosts}) { - my $this_is = "node1"; - if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } - elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } + my $this_is = $host_uuid eq $node2_host_uuid ? "node2" : "node1"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); $anvil->data->{ungrouped_vg_count}{$this_is} = 0; @@ -432,13 +418,13 @@ ORDER BY my $reload_storage_groups = 0; foreach my $scan_lvm_vg_size (sort {$a cmp $b} keys %{$anvil->data->{ungrouped_vgs}}) { - # If there are two or three VGs, we can create a group. + # If there are two VGs, we can create a group. my $count = keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { scan_lvm_vg_size => $scan_lvm_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_vg_size}).")", count => $count, }}); - if (($count == 2) or ($count == 3)) + if ($count == 2) { # Create the volume group ... group. First we need a group number my $storage_group_uuid = $anvil->Database->insert_or_update_storage_groups({ @@ -447,11 +433,10 @@ ORDER BY }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); + # Now add the VGs as members. foreach my $host_uuid (keys %{$anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}}) { - my $this_is = "node1"; - if ($host_uuid eq $node2_host_uuid) { $this_is = "node2"; } - elsif ($host_uuid eq $dr1_host_uuid) { $this_is = "dr1"; } + my $this_is = $host_uuid eq $node2_host_uuid ? "node2" : "node1"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_is => $this_is }}); my $storage_group_member_vg_uuid = $anvil->data->{ungrouped_vgs}{$scan_lvm_vg_size}{host_uuid}{$host_uuid}{vg_internal_uuid}; @@ -482,27 +467,21 @@ ORDER BY } # If there's only one VG on each node that is ungrouped, group them even though they're not the same - # size. If DR also has only 1 VG ungrouped, it'll be added, too. + # size. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "ungrouped_vg_count::node1" => $anvil->data->{ungrouped_vg_count}{node1}, "ungrouped_vg_count::node2" => $anvil->data->{ungrouped_vg_count}{node2}, - "ungrouped_vg_count::dr1" => $anvil->data->{ungrouped_vg_count}{dr1}, }}); if (($anvil->data->{ungrouped_vg_count}{node1} == 1) && ($anvil->data->{ungrouped_vg_count}{node2} == 1)) { # We do! - my $storage_group_uuid = $anvil->Database->create_storage_group({ - debug => $debug, + my $storage_group_uuid = $anvil->Database->insert_or_update_storage_groups({ + debug => 2, storage_group_anvil_uuid => $anvil_uuid, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); - my $hosts = [$node1_host_uuid, $node2_host_uuid]; - if ($anvil->data->{ungrouped_vg_count}{dr1} == 1) - { - push @{$hosts}, $dr1_host_uuid; - } - foreach my $host_uuid (@{$hosts}) + foreach my $host_uuid ($node1_host_uuid, $node2_host_uuid) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); @@ -539,13 +518,131 @@ ORDER BY if ($reload_storage_groups) { $anvil->Database->get_storage_group_data({debug => $debug}); - my $storage_group_uuid = $anvil->Database->insert_or_update_storage_groups({ - debug => 2, - storage_group_anvil_uuid => $anvil_uuid, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_uuid => $storage_group_uuid }}); } + # Now loop through any attached DRs and add the VGs that are closest in sizes to the VGs we have in + # this Anvil! node. + $anvil->Database->get_dr_links({debug => 2}); + + # This was going to automatically add VGs from DR hosts to an Anvil's Storage Group, but given now + # that things can be linked in random ways, this doesn't seem to make sense anymore. So the code is + # (not complete but) preserved here in case we change out mind later. +=cut + foreach my $dr_link_host_name (sort {$a cmp $b} keys %{$anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_name}}) + { + my $dr_link_uuid = $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_name}{$dr_link_host_name}{dr_link_uuid}; + my $dr_link_host_uuid = $anvil->Database->get_host_uuid_from_string({debug => $debug, string => $dr_link_host_name}); + my $dr_link_short_host_name = $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{short_host_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:dr_link_host_name' => $dr_link_host_name, + 's2:dr_link_host_uuid' => $dr_link_host_uuid, + 's3:dr_link_short_host_name' => $dr_link_short_host_name, + 's4:dr_link_uuid' => $dr_link_uuid, + }}); + + # What VGs do these DR hosts have, and are they in any storage groups yet? + my $query = " +SELECT + scan_lvm_vg_uuid, + scan_lvm_vg_name, + scan_lvm_vg_size, + scan_lvm_vg_internal_uuid +FROM + scan_lvm_vgs +WHERE + scan_lvm_vg_host_uuid = ".$anvil->Database->quote($dr_link_uuid)." +AND + scan_lvm_vg_name != 'DELETED' +ORDER BY + scan_lvm_vg_size ASC +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + foreach my $row (@{$results}) + { + my $scan_lvm_vg_uuid = $row->[0]; + my $scan_lvm_vg_name = $row->[1]; + my $scan_lvm_vg_size = $row->[2]; + my $scan_lvm_vg_internal_uuid = $row->[3]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:scan_lvm_vg_uuid' => $scan_lvm_vg_uuid, + 's2:scan_lvm_vg_name' => $scan_lvm_vg_name, + 's3:scan_lvm_vg_size' => $scan_lvm_vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_vg_size}).")", + 's4:scan_lvm_vg_internal_uuid' => $scan_lvm_vg_internal_uuid, + }}); + + # Is this VG already in a storage group for this anvil! node? + my $vg_found = 0; + foreach my $storage_group_uuid (keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}}) + { + my $storage_group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{group_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:storage_group_uuid' => $storage_group_uuid, + 's2:storage_group_name' => $storage_group_name, + }}); + foreach my $this_host_uuid (sort {$a cmp $b} keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{short_host_name}}) + { + next if $this_host_uuid eq $dr_link_host_uuid; + my $storage_group_member_note = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$$this_host_uuid}{storage_group_member_note}; + my $storage_group_member_vg_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$this_host_uuid}{vg_internal_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:this_host_uuid' => $this_host_uuid, + 's2:storage_group_member_vg_uuid' => $storage_group_member_vg_uuid, + 's3:storage_group_member_note' => $storage_group_member_note, + }}); + next if $storage_group_member_note eq "DELETED"; + if ($storage_group_member_vg_uuid eq $scan_lvm_vg_internal_uuid) + { + $vg_found = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { vg_found => $vg_found }}); + } + } + } + + if (not $vg_found) + { + # Find the smallest + my $query = " +SELECT + scan_lvm_vg_uuid, + scan_lvm_vg_name, + scan_lvm_vg_size, + scan_lvm_vg_internal_uuid +FROM + scan_lvm_vgs +WHERE + scan_lvm_vg_host_uuid = ".$anvil->Database->quote($dr_link_uuid)." +AND + scan_lvm_vg_name != 'DELETED' +ORDER BY + scan_lvm_vg_size ASC +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + foreach my $row (@{$results}) + { + my $scan_lvm_vg_uuid = $row->[0]; + my $scan_lvm_vg_name = $row->[1]; + my $scan_lvm_vg_size = $row->[2]; + my $scan_lvm_vg_internal_uuid = $row->[3]; + } + } + } + } +=cut + return(0); } diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index a3fa059d..5d5ea7aa 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -5297,6 +5297,12 @@ sub get_storage_group_data $scan_lvm_exists = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { scan_lvm_exists => $scan_lvm_exists }}); } + + # Loads hosts, if it hasn't been before. + if (not exists $anvil->data->{hosts}{host_uuid}) + { + $anvil->Database->get_hosts({debug => $debug}); + } $query = " SELECT @@ -13409,7 +13415,7 @@ AND if (not $storage_group_member_uuid) { # INSERT - my $storage_group_member_uuid = $anvil->Get->uuid(); + $storage_group_member_uuid = $anvil->Get->uuid(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_uuid => $storage_group_member_uuid }}); my $query = " diff --git a/tools/anvil-manage-dr b/tools/anvil-manage-dr index 5ee9e41d..b28d93dc 100755 --- a/tools/anvil-manage-dr +++ b/tools/anvil-manage-dr @@ -6,6 +6,8 @@ # This behaviour is likely to change later as it's not ideal. # # TODO: --remove is not yet implemented, this needs to be done. Use anvil-delete-server for methods to delete. +# EXAMPLES: +# - anvil-manage-dr --link --dr-host vm-a01dr02 --anvil vm-anvil-01 # # Exit codes; # 0 = Normal exit. diff --git a/tools/anvil-manage-host b/tools/anvil-manage-host index ddc964db..28f37a86 100755 --- a/tools/anvil-manage-host +++ b/tools/anvil-manage-host @@ -24,26 +24,17 @@ if (($running_directory =~ /^\./) && ($ENV{PWD})) my $anvil = Anvil::Tools->new(); # Read switches -$anvil->data->{switches}{'age-out-database'} = ""; -$anvil->data->{switches}{'check-configured'} = ""; -$anvil->data->{switches}{'check-database'} = ""; -$anvil->data->{switches}{'database-active'} = ""; -$anvil->data->{switches}{'database-inactive'} = ""; -$anvil->data->{switches}{'mark-configured'} = ""; -$anvil->data->{switches}{'mark-unconfigured'} = ""; -$anvil->data->{switches}{'resync-database'} = ""; -$anvil->Get->switches; -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); -$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - 'switches::age-out-database' => $anvil->data->{switches}{'age-out-database'}, - 'switches::check-configured' => $anvil->data->{switches}{'check-configured'}, - 'switches::check-database' => $anvil->data->{switches}{'check-database'}, - 'switches::database-active' => $anvil->data->{switches}{'database-active'}, - 'switches::database-inactive' => $anvil->data->{switches}{'database-inactive'}, - 'switches::mark-configured' => $anvil->data->{switches}{'mark-configured'}, - 'switches::mark-unconfigured' => $anvil->data->{switches}{'mark-unconfigured'}, - 'switches::resync-database' => $anvil->data->{switches}{'resync-database'}, -}}); +$anvil->Get->switches({list => [ + "age-out-database", + "check-configured", + "check-database", + "database-active", + "database-inactive", + "mark-configured", + "mark-unconfigured", + "resync-database"], man => $THIS_FILE}); +$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }}); $anvil->Database->connect({all => 1}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); From 7710d9d10989bfc1a7917f4f346f94390b03c07c Mon Sep 17 00:00:00 2001 From: digimer Date: Fri, 3 Feb 2023 22:05:34 -0500 Subject: [PATCH 3/8] * Created the new anvil-manage-server-storage tool which will specifically handle managing a server's disks. * Created DRBD->parse_resource() to pass a specific DRBD resource's XML data. * Fixed a bug in Get->available_resources() so that if the threads is lower than CPU cores, the cores are used as the total available to VMs. * Fixed bugs in Get->server_from_switch() where it just wasn't working properly. * Updated scan_drbd to not reset a resource's size to 0-bytes when a resource goes offline. Signed-off-by: digimer --- Anvil/Tools/DRBD.pm | 248 +++++++++++++++++ Anvil/Tools/Get.pm | 61 ++++- Anvil/Tools/Server.pm | 18 +- man/Makefile.am | 1 + man/anvil-manage-server-storage.8 | 0 scancore-agents/scan-drbd/scan-drbd | 10 + tools/Makefile.am | 2 +- tools/anvil-daemon | 2 +- tools/anvil-manage-server-storage | 398 ++++++++++++++++++++++++++++ 9 files changed, 724 insertions(+), 16 deletions(-) create mode 100644 man/anvil-manage-server-storage.8 create mode 100755 tools/anvil-manage-server-storage diff --git a/Anvil/Tools/DRBD.pm b/Anvil/Tools/DRBD.pm index 76dc739f..cb175545 100644 --- a/Anvil/Tools/DRBD.pm +++ b/Anvil/Tools/DRBD.pm @@ -24,6 +24,7 @@ my $THIS_FILE = "DRBD.pm"; # get_next_resource # get_status # manage_resource +# parse_resource # reload_defaults # remove_backing_lv # resource_uuid @@ -2361,6 +2362,253 @@ sub manage_resource return($return_code); } + +=head2 parse_resource + +This takes the XML from a specific DRBD resource and parses it. + +Parameters; + +=head3 xml (required) + +This is the XML to parse, generally as stored in the C<< scan_drbd_resources >> -> C<< scan_drbd_resource_xml >>. + +=cut +sub parse_resource +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "DRBD->parse_resource()" }}); + + my $xml = defined $parameter->{xml} ? $parameter->{xml} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + xml => $xml, + }}); + + if (not $xml) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "DRBD->parse_resource()", parameter => "xml" }}); + return("!!error!!"); + } + + local $@; + my $dom = eval { XML::LibXML->load_xml(string => $xml); }; + if ($@) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0253", variables => { + xml => $xml, + error => $@, + }}); + return(1); + } + else + { + # Successful parse! +=cut + + + + /dev/drbd_srv01-fs37_0 + /dev/cs_vm-a01n01/srv01-fs37_0 + internal + + + /dev/drbd_srv01-fs37_1 + /dev/cs_vm-a01n01/srv01-fs37_1 + internal + +
(null)
+
+ + + /dev/drbd_srv01-fs37_0 + /dev/cs_vm-a01n02/srv01-fs37_0 + internal + + + /dev/drbd_srv01-fs37_1 + /dev/cs_vm-a01n02/srv01-fs37_1 + internal + +
(null)
+
+ + + /dev/drbd_srv01-fs37_0 + /dev/cs_vm-a01dr01/srv01-fs37_0 + internal + + + /dev/drbd_srv01-fs37_1 + /dev/cs_vm-a01dr01/srv01-fs37_1 + internal + +
(null)
+
+ +
10.101.10.1
+
10.101.10.2
+
+
+
+
+
+ +
10.201.10.1
+
10.201.10.3
+
+
+
+
+
+ +
10.201.10.2
+
10.201.10.3
+
+
+
+
+
+
+=cut + foreach my $name ($dom->findnodes('/resource')) + { + my $resource = $name->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { resource => $resource }}); + + foreach my $host ($name->findnodes('./host')) + { + my $this_host_name = $host->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }}); + + # Record the details under the hosts + foreach my $volume_vnr ($host->findnodes('./volume')) + { + my $volume = $volume_vnr->{vnr}; + my $meta_disk = $volume_vnr->findvalue('./meta-disk'); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:volume' => $volume, + 's2:meta_disk' => $meta_disk, + }}); + + my $host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host_name}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + + $anvil->data->{new}{resource}{$resource}{host_name}{$this_host_name}{host_uuid} = $host_uuid; + $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{device_path} = $volume_vnr->findvalue('./device'); + $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{backing_disk} = $volume_vnr->findvalue('./disk'); + $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{device_minor} = $volume_vnr->findvalue('./device/@minor'); + $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{meta_disk} = $meta_disk; + $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{size} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:new::resource::${resource}::host_name::${this_host_name}::host_uuid" => $anvil->data->{new}{resource}{$resource}{host_name}{$this_host_name}{host_uuid}, + "s2:new::resource::${resource}::host_uuid::${host_uuid}::volume_number::${volume}::device_path" => $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{device_path}, + "s3:new::resource::${resource}::host_uuid::${host_uuid}::volume_number::${volume}::backing_disk" => $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{backing_disk}, + "s4:new::resource::${resource}::host_uuid::${host_uuid}::volume_number::${volume}::device_minor" => $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{device_minor}, + "s5:new::resource::${resource}::host_uuid::${host_uuid}::volume_number::${volume}::meta_disk" => $anvil->data->{new}{resource}{$resource}{host_uuid}{$host_uuid}{volume_number}{$volume}{meta_disk}, + }}); + } + } + foreach my $connection ($name->findnodes('./connection')) + { + my $host1_name = ""; + my $host1_ip_address = ""; + my $host1_tcp_port = ""; + my $host2_name = ""; + my $host2_ip_address = ""; + my $host2_tcp_port = ""; + my $peer = ""; + foreach my $host ($connection->findnodes('./host')) + { + my $this_host_name = $host->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }}); + if (not $host1_name) + { + $host1_name = $this_host_name; + $host1_ip_address = $host->findvalue('./address'); + $host1_tcp_port = $host->findvalue('./address/@port'); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + host1_name => $host1_name, + host1_ip_address => $host1_ip_address, + host1_tcp_port => $host1_tcp_port, + }}); + } + else + { + $host2_name = $this_host_name; + $host2_ip_address = $host->findvalue('./address'); + $host2_tcp_port = $host->findvalue('./address/@port'); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + host2_name => $host2_name, + host2_ip_address => $host2_ip_address, + host2_tcp_port => $host2_tcp_port, + }}); + + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_ip_address} = $host1_ip_address; + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_tcp_port} = $host1_tcp_port; + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_ip_address} = $host2_ip_address; + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_tcp_port} = $host2_tcp_port; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host1_ip_address" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_ip_address}, + "s2:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host1_tcp_port" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host1_tcp_port}, + "s3:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_ip_address" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_ip_address}, + "s4:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_tcp_port" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_tcp_port}, + }}); + + foreach my $proxy ($host->findnodes('./proxy')) + { + my $host_name = $proxy->{hostname}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_name => $host_name }}); + + # This should always be the target, but lets be safe/careful + next if $host_name ne $host2_name; + + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_inside_ip_address} = $proxy->findvalue('./inside'); + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_inside_tcp_port} = $proxy->findvalue('./inside/@port'); + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_outside_ip_address} = $proxy->findvalue('./outside'); + $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_outside_tcp_port} = $proxy->findvalue('./outside/@port'); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_inside_ip_address" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_inside_ip_address}, + "s2:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_inside_tcp_port" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_inside_tcp_port}, + "s3:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_outside_ip_address" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_outside_ip_address}, + "s4:new::resource::${resource}::host1_to_host2::${host1_name}::${host2_name}::host2_outside_tcp_port" => $anvil->data->{new}{resource}{$resource}{host1_to_host2}{$host1_name}{$host2_name}{host2_outside_tcp_port}, + }}); + + $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{inside}{ip_address} = $proxy->findvalue('./inside'); + $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{inside}{tcp_port} = $proxy->findvalue('./inside/@port'); + $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{outside}{ip_address} = $proxy->findvalue('./outside'); + $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{outside}{tcp_port} = $proxy->findvalue('./outside/@port'); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::resource::${resource}::proxy::${host_name}::inside::ip_address" => $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{inside}{ip_address}, + "new::resource::${resource}::proxy::${host_name}::inside::tcp_port" => $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{inside}{tcp_port}, + "new::resource::${resource}::proxy::${host_name}::outside::ip_address" => $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{outside}{ip_address}, + "new::resource::${resource}::proxy::${host_name}::outside::tcp_port" => $anvil->data->{new}{resource}{$resource}{proxy}{$host_name}{outside}{tcp_port}, + }}); + } + } + } + } + } + } + + return(0); +} + + =head2 reload_defaults This switches DRBD back to running using the values in the config files. Specifically, it calls C<< drbdadm adjust all >>. diff --git a/Anvil/Tools/Get.pm b/Anvil/Tools/Get.pm index 4df0a8fe..2fddbbda 100644 --- a/Anvil/Tools/Get.pm +++ b/Anvil/Tools/Get.pm @@ -643,7 +643,7 @@ WHERE # How many cores? if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}) or - ($scan_hardware_cpu_cores < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores})) + ($scan_hardware_cpu_cores < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores})) { $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores} = $scan_hardware_cpu_cores; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { @@ -651,15 +651,25 @@ WHERE }}); } if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}) or - ($scan_hardware_cpu_threads < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads})) + ($scan_hardware_cpu_threads < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads})) { $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = $scan_hardware_cpu_threads; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "anvil_resources::${anvil_uuid}::cpu::threads" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}, }}); } + + # If there are less threads than cores, set the cores to be equal to threads. + if ($anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} < $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}) + { + $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads} = $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{cores}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvil_resources::${anvil_uuid}::cpu::threads" => $anvil->data->{anvil_resources}{$anvil_uuid}{cpu}{threads}, + }}); + } + if ((not $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}) or - ($scan_hardware_ram_total < $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware})) + ($scan_hardware_ram_total < $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware})) { $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available} = $scan_hardware_ram_total; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { @@ -2114,9 +2124,9 @@ sub server_from_switch server_string => $server_string, }}); - if ((not $server_string) && ($anvil->data->{switches}{'anvil'})) + if ((not $server_string) && ($anvil->data->{switches}{'server'})) { - $server_string = $anvil->data->{switches}{'anvil'}; + $server_string = $anvil->data->{switches}{'server'}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { server_string => $server_string }}); } if (not $server_string) @@ -2125,10 +2135,16 @@ sub server_from_switch return("!!error!!", ""); } + $anvil->Database->get_anvils({debug => $debug}); $anvil->Database->get_servers({debug => $debug}); $anvil->data->{switches}{server_name} = "" if not exists $anvil->data->{switches}{server_name}; $anvil->data->{switches}{server_uuid} = "" if not exists $anvil->data->{switches}{server_uuid}; - if (exists $anvil->data->{servers}{server_uuid}{$server_string}{server_name}) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + server_string => $server_string, + "switches::server_name" => $anvil->data->{switches}{server_name}, + "switches::server_uuid" => $anvil->data->{switches}{server_uuid}, + }}); + if (exists $anvil->data->{servers}{server_uuid}{$server_string}) { # Found it by UUID. $anvil->data->{switches}{server_name} = $anvil->data->{anvils}{server_uuid}{$server_string}{server_name}; @@ -2138,14 +2154,33 @@ sub server_from_switch "switches::server_uuid" => $anvil->data->{switches}{server_uuid}, }}); } - elsif (exists $anvil->data->{anvils}{server_uuid}{$server_string}) + else { - $anvil->data->{switches}{server_name} = $server_string; - $anvil->data->{switches}{server_uuid} = $anvil->data->{anvils}{server_uuid}{$server_string}{server_uuid}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "switches::server_name" => $anvil->data->{switches}{server_name}, - "switches::server_uuid" => $anvil->data->{switches}{server_uuid}, - }}); + # If we have an anvil_uuid, see if the server exists there. + foreach my $this_anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) + { + my $this_anvil_uuid = $anvil->data->{anvils}{anvil_name}{$this_anvil_name}{anvil_uuid}; + if (($anvil_uuid) && ($anvil_uuid ne $this_anvil_uuid)) + { + next; + } + foreach my $this_server_name (sort {$a cmp $b} keys %{$anvil->data->{servers}{anvil_uuid}{$this_anvil_uuid}{server_name}}) + { + my $this_server_uuid = $anvil->data->{servers}{anvil_uuid}{$this_anvil_uuid}{server_name}{$this_server_name}{server_uuid}; + if (($server_string eq $this_server_name) or + ($server_string eq $this_server_uuid)) + { + # Found it + $anvil->data->{switches}{server_name} = $this_server_name; + $anvil->data->{switches}{server_uuid} = $this_server_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "switches::server_name" => $anvil->data->{switches}{server_name}, + "switches::server_uuid" => $anvil->data->{switches}{server_uuid}, + }}); + last; + } + } + } } return($anvil->data->{switches}{server_name}, $anvil->data->{switches}{server_uuid}); diff --git a/Anvil/Tools/Server.pm b/Anvil/Tools/Server.pm index d3b169a7..7efc0176 100644 --- a/Anvil/Tools/Server.pm +++ b/Anvil/Tools/Server.pm @@ -1385,7 +1385,7 @@ sub parse_definition my $server = defined $parameter->{server} ? $parameter->{server} : ""; my $source = defined $parameter->{source} ? $parameter->{source} : ""; my $definition = defined $parameter->{definition} ? $parameter->{definition} : ""; - my $host = defined $parameter->{host} ? $parameter->{host} : $anvil->Get->short_host_name; + my $host = defined $parameter->{host} ? $parameter->{host} : ""; my $target = $anvil->Get->short_host_name(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid, @@ -1395,6 +1395,12 @@ sub parse_definition host => $host, }}); + if (not $target) + { + $target = $anvil->Get->short_host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target => $target }}); + } + if (not $server) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Server->parse_definition()", parameter => "server" }}); @@ -1805,6 +1811,16 @@ sub parse_definition "server::${target}::${server}::${source}::device::${device}::target::${device_target}::type" => $anvil->data->{server}{$target}{$server}{$source}{device}{$device}{target}{$device_target}{type}, }}); + if (($boot_order) && ($boot_order =~ /^\d+$/)) + { + $anvil->data->{server}{$target}{$server}{$source}{boot_order}{$boot_order}{device_target} = $device_target; + $anvil->data->{server}{$target}{$server}{$source}{boot_order}{$boot_order}{device_type} = $device; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "server::${target}::${server}::${source}::boot_order::${boot_order}::device_target" => $anvil->data->{server}{$target}{$server}{$source}{boot_order}{$boot_order}{device_target}, + "server::${target}::${server}::${source}::boot_order::${boot_order}::device_type" => $anvil->data->{server}{$target}{$server}{$source}{boot_order}{$boot_order}{device_type}, + }}); + } + # Record type-specific data if ($device eq "disk") { diff --git a/man/Makefile.am b/man/Makefile.am index d614bf43..401676d5 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -19,6 +19,7 @@ dist_man8_MANS = \ anvil-manage-files.8 \ anvil-manage-keys.1 \ anvil-manage-server.8 \ + anvil-manage-server-storage.8 \ anvil-manage-storage-groups.8 \ scancore.8 \ striker-initialize-host.8 diff --git a/man/anvil-manage-server-storage.8 b/man/anvil-manage-server-storage.8 new file mode 100644 index 00000000..e69de29b diff --git a/scancore-agents/scan-drbd/scan-drbd b/scancore-agents/scan-drbd/scan-drbd index 8307ba6c..69cdb8e6 100755 --- a/scancore-agents/scan-drbd/scan-drbd +++ b/scancore-agents/scan-drbd/scan-drbd @@ -914,6 +914,16 @@ sub process_volumes "s4:old_scan_drbd_volume_size" => $old_scan_drbd_volume_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_drbd_volume_size}).")", }}); + # If the $new_scan_drbd_volume_size is '0', the device is down. Don't update it so we can + # recall the last known size. + if ($new_scan_drbd_volume_size == 0) + { + $new_scan_drbd_volume_size = $old_scan_drbd_volume_size; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + new_scan_drbd_volume_size => $new_scan_drbd_volume_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_drbd_volume_size}).")", + }}); + } + my $update = 0; if ($new_scan_drbd_volume_device_path ne $old_scan_drbd_volume_device_path) { diff --git a/tools/Makefile.am b/tools/Makefile.am index e0e2b7ea..96f7b2e1 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -26,6 +26,7 @@ dist_sbin_SCRIPTS = \ anvil-manage-keys \ anvil-manage-power \ anvil-manage-server \ + anvil-manage-server-storage \ anvil-manage-storage-groups \ anvil-migrate-server \ anvil-network-profiler \ @@ -76,4 +77,3 @@ sharedir = ${datarootdir}/anvil dist_share_DATA = striker-auto-initialize-all.example -# -rwxr-xr-x. 1 digimer digimer 34K Feb 1 2020 tool-fio-tester diff --git a/tools/anvil-daemon b/tools/anvil-daemon index 9b20a131..bcd15b75 100755 --- a/tools/anvil-daemon +++ b/tools/anvil-daemon @@ -766,7 +766,7 @@ sub check_db_in_use_states my ($anvil) = @_; # We only reap db_in_use entries for us. - $anvil->System->pids(); + $anvil->System->pids({debug => 2}); my $query = " SELECT state_uuid, diff --git a/tools/anvil-manage-server-storage b/tools/anvil-manage-server-storage new file mode 100755 index 00000000..06eeaf66 --- /dev/null +++ b/tools/anvil-manage-server-storage @@ -0,0 +1,398 @@ +#!/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: +# + +use strict; +use warnings; +use Anvil::Tools; +require POSIX; +use Term::Cap; +use Data::Dumper; + +my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; +my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; +if (($running_directory =~ /^\./) && ($ENV{PWD})) +{ + $running_directory =~ s/^\./$ENV{PWD}/; +} + +# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. +$| = 1; + +my $anvil = Anvil::Tools->new(); + +### TODO: Remove this before final release +$anvil->Log->level({set => 2}); +$anvil->Log->secure({set => 1}); +########################################## + +# Read switches (target ([user@]host[:port]) and the file with the target's password. +$anvil->Get->switches({list => [ + "add", + "anvil", + "grow", + "server", + ], 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}); +} + +# If we don't have a job UUID, try to find one. +if (not $anvil->data->{switches}{'job-uuid'}) +{ + # Load the job data. + $anvil->data->{switches}{'job-uuid'} = $anvil->Job->get_job_uuid({program => $THIS_FILE}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }}); +} + +$anvil->Database->get_hosts(); +$anvil->Database->get_anvils(); +$anvil->Database->get_servers(); + +if ($anvil->data->{switches}{anvil}) +{ + # Make sure they asked for a real anvil. + $anvil->Get->anvil_from_switch({string => $anvil->data->{switches}{anvil}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "switches::anvil_name" => $anvil->data->{switches}{anvil_name}, + "switches::anvil_uuid" => $anvil->data->{switches}{anvil_uuid}, + }}); +} + +if (not $anvil->data->{switches}{server}) +{ + # Show the list of servers. + show_server_list($anvil); + print "\nPlease specify which server you want to modify using '--server '.\n\n"; + $anvil->nice_exit({exit_code => 0}); +} +manage_server($anvil); + +$anvil->nice_exit({exit_code => 0}); + + +############################################################################################################# +# Functions # +############################################################################################################# + +sub manage_server +{ + my ($anvil) = @_; + + $anvil->Get->server_from_switch({ + debug => 2, + string => $anvil->data->{switches}{server}, + anvil_uuid => $anvil->data->{switches}{anvil_uuid}, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "switches::server_name" => $anvil->data->{switches}{server_name}, + "switches::server_uuid" => $anvil->data->{switches}{server_uuid}, + }}); + + if (not $anvil->data->{switches}{server_uuid}) + { + show_server_list($anvil); + if ($anvil->data->{switches}{anvil_uuid}) + { + # Not found on the requested Anvil! node. + print "\nThe server: [".$anvil->data->{switches}{server}."] was not found on the Anvil! node: [".$anvil->data->{switches}{anvil_name}."]. Valid servers are above.\n\n"; + } + else + { + # Not found at all. + print "\nThe server: [".$anvil->data->{switches}{server}."] was not found. Valid servers are above.\n\n"; + } + $anvil->nice_exit({exit_code => 1}); + } + + print "Working with the server: [".$anvil->data->{switches}{server_name}."], UUID: [".$anvil->data->{switches}{server_uuid}."]\n"; + my $short_host_name = $anvil->Get->short_host_name; + my $server_name = $anvil->data->{switches}{server_name}; + my $server_uuid = $anvil->data->{switches}{server_uuid}; + my $server_definition = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_definition_xml}; + $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, + }}); + + # Parse the definition. + $anvil->Server->parse_definition({ + debug => 3, + host => $short_host_name, + server => $server_name, + source => "from_virsh", + definition => $server_definition, + }); + + #$anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{boot_order} + foreach my $device ("disk", "cdrom") + { + if ($device eq "disk") + { + print "\nDisk Drives:\n"; + } + else + { + print "\nOptical Drives:\n"; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { device => $device }}); + next if $device ne "cdrom" && $device ne "disk"; + foreach my $device_target (sort {$a cmp $b} keys %{$anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}}) + { + my $alias = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{alias}; + my $boot_order = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{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_virsh}{device}{$device}{target}{$device_target}{type}; + my $address_type = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{type}; + my $address_bus = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{bus}; + my $driver_name = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{driver}{name}; + my $device_bus = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{device_bus}; + my $driver_type = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{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_virsh}{device}{$device}{target}{$device_target}{address}{domain}; + my $address_slot = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{slot}; + my $address_function = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{function}; + my $device_path = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{path}; + my $driver_io = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{driver}{io}; + my $driver_cache = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{driver}{cache}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:address_domain' => $address_domain, + 's2:address_slot' => $address_slot, + 's3:address_function' => $address_function, + 's4:device_path' => $device_path, + 's5:driver_io' => $driver_io, + 's6:driver_cache' => $driver_cache, + }}); + print "- Target: [".$device_target."], boot: [".$say_boot."], path: [".$device_path."], cache: [".$driver_cache."], driver type: [".$driver_type."]\n"; + } + else + { + my $address_controller = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{controller}; + my $address_unit = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{unit}; + my $address_target = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{address}{target}; + my $device_path = $anvil->data->{server}{$short_host_name}{$server_name}{from_virsh}{device}{$device}{target}{$device_target}{path}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:address_controller' => $address_controller, + 's2:address_unit' => $address_unit, + 's3:address_target' => $address_target, + 's4:device_path' => $device_path, + }}); + print "- Target: [".$device_target."], boot: [".$say_boot."], ISO: [".$device_path."]\n"; + } + } + } + print "\n"; + + 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, + }}); + } + 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 + # Get the DRBD volume data + 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}, + }); + } + } + + print "Sub-Nodes:\n"; + show_volume($anvil, $drbd_resource, "node"); + + my $dr_count = keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{dr}{short_host_name}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dr_count => $dr_count }}); + if ($dr_count) + { + print "DR Hosts:\n"; + show_volume($anvil, $drbd_resource, "dr"); + } + + return(0); +} + +sub show_volume +{ + my ($anvil, $drbd_resource, $host_type) = @_; + + foreach my $short_host_name (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}}) + { + my $host_uuid = $anvil->data->{drbd_resource}{$drbd_resource}{host_type}{$host_type}{short_host_name}{$short_host_name}{host_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:short_host_name' => $short_host_name, + 's2:host_uuid' => $host_uuid, + }}); + print " |- Name: [".$short_host_name."], UUID: [".$host_uuid."]\n"; + foreach my $volume_number (sort {$a cmp $b} keys %{$anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}}) + { + my $device_path = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{device_path}; + my $device_minor = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{device_minor}; + my $volume_size = $anvil->data->{drbd_resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{volume_size}; + my $backing_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{backing_disk}; + my $meta_disk = $anvil->data->{new}{resource}{$drbd_resource}{host_uuid}{$host_uuid}{volume_number}{$volume_number}{meta_disk}; + $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, + }}); + print " ^- Volume: [".$volume_number."], backing device: [".$backing_disk."], DRBD minor: [".$device_minor."], size: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $volume_size})."]\n"; + } + } + + return(0); +} + +sub show_server_list +{ + my ($anvil) = @_; + + # Loop through all Anvil! nodes, then all server in that Anvil! + foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) + { + my $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid}; + my $anvil_description = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + anvil_name => $anvil_name, + anvil_uuid => $anvil_uuid, + anvil_description => $anvil_description, + }}); + if (($anvil->data->{switches}{anvil_uuid}) && ($anvil->data->{switches}{anvil_uuid} ne $anvil_uuid)) + { + next; + } + print "\nAnvil! Node: [".$anvil_name."], UUID: [".$anvil_uuid."] - Description: [".$anvil_description."]\n"; + + my $server_count = 0; + if (exists $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}) + { + $server_count = keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_count => $server_count }}); + } + if (not $server_count) + { + print "- No servers on this node yet\n"; + } + else + { + foreach my $server_name (sort {$a cmp $b} keys %{$anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}}) + { + my $server_uuid = $anvil->data->{servers}{anvil_uuid}{$anvil_uuid}{server_name}{$server_name}{server_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + server_name => $server_name, + server_uuid => $server_uuid, + }}); + print "^- Server: [".$server_name."], UUID: [".$server_uuid."]\n"; + } + } + } + + return(0); +} From 645f54ab893df6bb15a14eecc0aef8d176352058 Mon Sep 17 00:00:00 2001 From: digimer Date: Tue, 14 Feb 2023 02:29:40 -0500 Subject: [PATCH 4/8] This commit has more changes than I would normally like, but it's all linked to changing file uploads to rsync serially. * To update file handling for the new DR host linking mechanism, file_locations -> file_location_anvil_uuid was changed to file_location_host_uuid. This required a fair number of changes elsewhere to handle this, with a particular noted change to Database->get_anvils() to look at host_uuid's for the subnodes in an Anvil! and, if either is marked as needing a file, make sure the peer is as well. Similarly, any linked DRs are set to have the file as well. * Created a new Network->find_access that simply takes a target host name or UUID, and it returns a list of networks and IPs that the target can be accessed by. * Updated Network->load_ips() to find the network interface being used for traffic so that things like the interface speed can be recorded, even when an IP is on a bridge or bond. Unrelated, but in this commit, is a restoration of calling scan agents with a timeout now that the virsh hang issue has been resolved. Signed-off-by: digimer --- Anvil/Tools/Database.pm | 336 ++++++++++++++++++++++-------------- Anvil/Tools/Network.pm | 240 +++++++++++++++++++++++--- Anvil/Tools/ScanCore.pm | 16 +- Anvil/Tools/Storage.pm | 2 +- notes | 2 - share/anvil.sql | 28 +-- share/words.xml | 21 ++- tools/anvil-manage-files | 2 +- tools/anvil-sync-shared | 258 ++++++++++++++++++--------- tools/anvil-version-changes | 135 ++++++++++++++- tools/striker-purge-target | 11 +- 11 files changed, 770 insertions(+), 281 deletions(-) diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 5d5ea7aa..8bb634c2 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -386,7 +386,7 @@ sub backup_database =head2 check_file_locations -This method checks to see that there is a corresponding entry in C<< file_locations >> for all Anvil! systems and files in the database. Any that are found to be missing will be set to C<< file_location_active >> -> c<< true >>. +This method checks to see that there is a corresponding entry in C<< file_locations >> for all hosts and files in the database. Any that are found to be missing will be set to C<< file_location_active >> -> c<< true >>. This method takes no parameters. @@ -400,32 +400,32 @@ sub check_file_locations $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->check_file_locations()" }}); # Get all the Anvil! systems we know of. - $anvil->Database->get_anvils({debug => $debug}); + $anvil->Database->get_hosts({debug => $debug}); $anvil->Database->get_files({debug => $debug}); $anvil->Database->get_file_locations({debug => $debug}); - foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) + foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{hosts}{host_name}}) { - my $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid}; + my $host_uuid = $anvil->data->{hosts}{host_name}{$host_name}{host_uuid}; foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{files}{file_name}}) { my $file_uuid = $anvil->data->{files}{file_name}{$file_name}{file_uuid}; # Does this file exist for this Anvil! system? - if (not exists $anvil->data->{file_locations}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_location_uuid}) + if (not exists $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}) { # Add this entry. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0613", variables => { - anvil_name => $anvil_name, - file_name => $file_name, + host_name => $host_name, + file_name => $file_name, }}); my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ - debug => $debug, - file_location_file_uuid => $file_uuid, - file_location_anvil_uuid => $anvil_uuid, - file_location_active => 1, + debug => $debug, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuid => $file_location_uuid }}); } @@ -2710,7 +2710,6 @@ SELECT anvil_password, anvil_node1_host_uuid, anvil_node2_host_uuid, - anvil_dr1_host_uuid, modified_date FROM anvils "; @@ -2737,8 +2736,7 @@ WHERE my $anvil_password = $row->[3]; my $anvil_node1_host_uuid = defined $row->[4] ? $row->[4] : ""; my $anvil_node2_host_uuid = defined $row->[5] ? $row->[5] : ""; - my $anvil_dr1_host_uuid = defined $row->[6] ? $row->[6] : ""; - my $modified_date = $row->[7]; + my $modified_date = $row->[6]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid, anvil_name => $anvil_name, @@ -2746,7 +2744,6 @@ WHERE anvil_password => $anvil->Log->is_secure($anvil_password), anvil_node1_host_uuid => $anvil_node1_host_uuid, anvil_node2_host_uuid => $anvil_node2_host_uuid, - anvil_dr1_host_uuid => $anvil_dr1_host_uuid, modified_date => $modified_date, }}); @@ -2756,7 +2753,6 @@ WHERE $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_password} = $anvil_password; $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid} = $anvil_node1_host_uuid; $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid} = $anvil_node2_host_uuid; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid} = $anvil_dr1_host_uuid; $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{modified_date} = $modified_date; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "anvils::anvil_uuid::${anvil_uuid}::anvil_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name}, @@ -2764,7 +2760,6 @@ WHERE "anvils::anvil_uuid::${anvil_uuid}::anvil_password" => $anvil->Log->is_secure($anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_password}), "anvils::anvil_uuid::${anvil_uuid}::anvil_node1_host_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}, "anvils::anvil_uuid::${anvil_uuid}::anvil_node2_host_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}, - "anvils::anvil_uuid::${anvil_uuid}::anvil_dr1_host_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}, "anvils::anvil_uuid::${anvil_uuid}::modified_date" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{modified_date}, }}); @@ -2775,7 +2770,6 @@ WHERE $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_password} = $anvil_password; $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid} = $anvil_node1_host_uuid; $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid} = $anvil_node2_host_uuid; - $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid} = $anvil_dr1_host_uuid; $anvil->data->{anvils}{anvil_name}{$anvil_name}{modified_date} = $modified_date; $anvil->data->{anvils}{anvil_name}{$anvil_name}{query_time} = time; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { @@ -2784,7 +2778,6 @@ WHERE "anvils::anvil_name::${anvil_name}::anvil_password" => $anvil->Log->is_secure($anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_password}), "anvils::anvil_name::${anvil_name}::anvil_node1_host_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid}, "anvils::anvil_name::${anvil_name}::anvil_node2_host_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid}, - "anvils::anvil_name::${anvil_name}::anvil_dr1_host_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid}, "anvils::anvil_name::${anvil_name}::modified_date" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{modified_date}, "anvils::anvil_name::${anvil_name}::query_time" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{query_time}, }}); @@ -2811,60 +2804,6 @@ WHERE "anvils::host_uuid::${anvil_node2_host_uuid}::role" => $anvil->data->{anvils}{host_uuid}{$anvil_node2_host_uuid}{role}, }}); } - ### TODO: Remove this once the switch over to 'dr_links' is done. - if ($anvil_dr1_host_uuid) - { - $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_name} = $anvil_name; - $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_uuid} = $anvil_uuid; - $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{role} = "dr1"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "anvils::host_uuid::${anvil_dr1_host_uuid}::anvil_name" => $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_name}, - "anvils::host_uuid::${anvil_dr1_host_uuid}::anvil_uuid" => $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{anvil_uuid}, - "anvils::host_uuid::${anvil_dr1_host_uuid}::role" => $anvil->data->{anvils}{host_uuid}{$anvil_dr1_host_uuid}{role}, - }}); - } - - # Track the files on this Anvil! - foreach my $file_location_uuid (keys %{$anvil->data->{file_locations}{file_location_uuid}}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "file_locations::file_location_uuid::${file_location_uuid}::file_location_anvil_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_anvil_uuid}, - "file_locations::file_location_uuid::${file_location_uuid}::file_location_active" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}, - }}); - next if $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_anvil_uuid} ne $anvil_uuid; - next if not $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; - - my $file_uuid = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid}; - - next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; - - my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - file_uuid => $file_uuid, - file_name => $file_name, - }}); - - # If the file was deleted, this won't exist - next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name} = $file_name; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_size}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_md5sum}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name}, - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_directory" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory}, - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_size" => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}})." (".$anvil->Convert->add_commas({number => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}}).")", - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_md5sum" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum}, - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_type" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type}, - }}); - - # Make it so that we can list the files by name. - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid} = $file_uuid; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "anvils::anvil_uuid::${anvil_uuid}::file_name::${file_name}::file_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}, - }}); - } # Process DR hosts this Anvil! is allowed to use. if (exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}) @@ -2891,6 +2830,135 @@ WHERE }}); } } + + # Track the files on this Anvil! + foreach my $file_location_uuid (keys %{$anvil->data->{file_locations}{file_location_uuid}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuid => $file_location_uuid }}); + + # If either node is set have this file, make sure both are. + my $file_uuid = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid}; + my $anvil_needs_file = 0; + $anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid} = 0; + $anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid} = 0; + if ((exists $anvil->data->{file_locations}{host_uuid}{$anvil_node1_host_uuid}{file_uuid}{$file_uuid}) && + ($anvil->data->{file_locations}{host_uuid}{$anvil_node1_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid})) + { + my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$anvil_node1_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; + my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_location_uuid => $file_location_uuid, + file_location_active => $file_location_active, + }}); + + if (not $file_location_active) + { + $anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid} = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "needs_file::${file_uuid}::${anvil_node1_host_uuid}" => $anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid}, + }}); + } + } + if ((exists $anvil->data->{file_locations}{host_uuid}{$anvil_node2_host_uuid}{file_uuid}{$file_uuid}) && + ($anvil->data->{file_locations}{host_uuid}{$anvil_node2_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid})) + { + my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$anvil_node2_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; + my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_location_uuid => $file_location_uuid, + file_location_active => $file_location_active, + }}); + + if (not $file_location_active) + { + $anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid} = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "needs_file::${file_uuid}::${anvil_node2_host_uuid}" => $anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid}, + }}); + } + } + + # If either node has the file, both nodes (and all DRs) need the file. + if (($anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid}) or ($anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid})) + { + $anvil_needs_file = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_needs_file => $anvil_needs_file }}); + } + + foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid) + { + # If the Anvil! needs the file, make sure that both nodes and any connected DRs have it as well. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + host_uuid => $host_uuid, + }}); + if ($anvil_needs_file) + { + if (not $anvil->{needs_file}{$file_uuid}{$host_uuid}) + { + $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_location_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + } + } + } + + # If the Anvil! node needs the file, record it as being on this Anvil! node. + if ($anvil_needs_file) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "file_locations::file_location_uuid::${file_location_uuid}::file_location_host_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_host_uuid}, + "file_locations::file_location_uuid::${file_location_uuid}::file_location_active" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}, + }}); + next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; + + my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_uuid => $file_uuid, + file_name => $file_name, + }}); + + # If the file was deleted, this won't exist + next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name} = $file_name; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_size}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_md5sum}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name}, + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_directory" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory}, + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_size" => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}})." (".$anvil->Convert->add_commas({number => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}}).")", + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_md5sum" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum}, + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_type" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type}, + }}); + + # Make it so that we can list the files by name. + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid} = $file_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::anvil_uuid::${anvil_uuid}::file_name::${file_name}::file_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}, + }}); + + # Make sure linked DR hosts have this file, also. + foreach my $host_uuid (keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}}) + { + my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; + my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + + if (not $file_location_active) + { + $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_location_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + } + } + } + } } return(0); @@ -3238,7 +3306,7 @@ WHERE This loads the known install file_locations into the C<< anvil::data >> hash at: * file_locations::file_location_uuid::::file_location_file_uuid -* file_locations::file_location_uuid::::file_location_anvil_uuid +* file_locations::file_location_uuid::::file_location_host_uuid * file_locations::file_location_uuid::::file_location_active * file_locations::file_location_uuid::::modified_date @@ -3264,7 +3332,7 @@ sub get_file_locations SELECT file_location_uuid, file_location_file_uuid, - file_location_anvil_uuid, + file_location_host_uuid, file_location_active, modified_date FROM @@ -3279,35 +3347,35 @@ FROM }}); foreach my $row (@{$results}) { - my $file_location_uuid = $row->[0]; - my $file_location_file_uuid = $row->[1]; - my $file_location_anvil_uuid = $row->[2]; - my $file_location_active = $row->[3]; - my $modified_date = $row->[4]; + my $file_location_uuid = $row->[0]; + my $file_location_file_uuid = $row->[1]; + my $file_location_host_uuid = $row->[2]; + my $file_location_active = $row->[3]; + my $modified_date = $row->[4]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - file_location_uuid => $file_location_uuid, - file_location_file_uuid => $file_location_file_uuid, - file_location_anvil_uuid => $file_location_anvil_uuid, - file_location_active => $file_location_active, - modified_date => $modified_date, + file_location_uuid => $file_location_uuid, + file_location_file_uuid => $file_location_file_uuid, + file_location_host_uuid => $file_location_host_uuid, + file_location_active => $file_location_active, + modified_date => $modified_date, }}); # Record the data in the hash, too. - $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid} = $file_location_file_uuid; - $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_anvil_uuid} = $file_location_anvil_uuid; - $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active} = $file_location_active; - $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{modified_date} = $modified_date; + $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid} = $file_location_file_uuid; + $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_host_uuid} = $file_location_host_uuid; + $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active} = $file_location_active; + $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{modified_date} = $modified_date; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "file_locations::file_location_uuid::${file_location_uuid}::file_location_file_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid}, - "file_locations::file_location_uuid::${file_location_uuid}::file_location_anvil_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_anvil_uuid}, - "file_locations::file_location_uuid::${file_location_uuid}::file_location_active" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}, - "file_locations::file_location_uuid::${file_location_uuid}::modified_date" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{modified_date}, + "file_locations::file_location_uuid::${file_location_uuid}::file_location_file_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid}, + "file_locations::file_location_uuid::${file_location_uuid}::file_location_host_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_host_uuid}, + "file_locations::file_location_uuid::${file_location_uuid}::file_location_active" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}, + "file_locations::file_location_uuid::${file_location_uuid}::modified_date" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{modified_date}, }}); # Make it easy to find files by anvil and file UUID. - $anvil->data->{file_locations}{anvil_uuid}{$file_location_anvil_uuid}{file_uuid}{$file_location_file_uuid}{file_location_uuid} = $file_location_uuid; + $anvil->data->{file_locations}{host_uuid}{$file_location_host_uuid}{file_uuid}{$file_location_file_uuid}{file_location_uuid} = $file_location_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "file_locations::anvil_uuid::${file_location_anvil_uuid}::file_uuid::${file_location_file_uuid}::file_location_uuid" => $anvil->data->{file_locations}{anvil_uuid}{$file_location_anvil_uuid}{file_uuid}{$file_location_file_uuid}{file_location_uuid}, + "file_locations::host_uuid::${file_location_host_uuid}::file_uuid::${file_location_file_uuid}::file_location_uuid" => $anvil->data->{file_locations}{host_uuid}{$file_location_host_uuid}{file_uuid}{$file_location_file_uuid}{file_location_uuid}, }}); } @@ -7924,7 +7992,7 @@ INSERT INTO This updates (or inserts) a record in the 'file_locations' table. The C<< file_location_uuid >> referencing the database row will be returned. -This table is used to track which files on Striker dashboards need to be on given Anvil! members. +This table is used to track which files on Striker dashboards need to be on given Anvil! members and DR hosts. If there is an error, an empty string is returned. @@ -7938,9 +8006,9 @@ If not passed, a check will be made to see if an existing entry is found for C<< This is the C<< files >> -> C<< file_uuid >> being referenced. -=head3 file_location_anvil_uuid (required) +=head3 file_location_host_uuid (required) -This is the C<< anvils >> -> C<< anvil_uuid >> being referenced. +This is the C<< hosts >> -> C<< host_uuid >> being referenced. =head3 file_location_active (required) @@ -7957,21 +8025,21 @@ sub insert_or_update_file_locations my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_file_locations()" }}); - my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; - my $file = defined $parameter->{file} ? $parameter->{file} : ""; - my $line = defined $parameter->{line} ? $parameter->{line} : ""; - my $file_location_uuid = defined $parameter->{file_location_uuid} ? $parameter->{file_location_uuid} : ""; - my $file_location_file_uuid = defined $parameter->{file_location_file_uuid} ? $parameter->{file_location_file_uuid} : ""; - my $file_location_anvil_uuid = defined $parameter->{file_location_anvil_uuid} ? $parameter->{file_location_anvil_uuid} : ""; - my $file_location_active = defined $parameter->{file_location_active} ? $parameter->{file_location_active} : ""; + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $file_location_uuid = defined $parameter->{file_location_uuid} ? $parameter->{file_location_uuid} : ""; + my $file_location_file_uuid = defined $parameter->{file_location_file_uuid} ? $parameter->{file_location_file_uuid} : ""; + my $file_location_host_uuid = defined $parameter->{file_location_host_uuid} ? $parameter->{file_location_host_uuid} : ""; + my $file_location_active = defined $parameter->{file_location_active} ? $parameter->{file_location_active} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - uuid => $uuid, - file => $file, - line => $line, - file_location_uuid => $file_location_uuid, - file_location_file_uuid => $file_location_file_uuid, - file_location_anvil_uuid => $file_location_anvil_uuid, - file_location_active => $file_location_active, + uuid => $uuid, + file => $file, + line => $line, + file_location_uuid => $file_location_uuid, + file_location_file_uuid => $file_location_file_uuid, + file_location_host_uuid => $file_location_host_uuid, + file_location_active => $file_location_active, }}); if (not $file_location_file_uuid) @@ -7980,10 +8048,10 @@ sub insert_or_update_file_locations $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_file_locations()", parameter => "file_location_file_uuid" }}); return(""); } - if (not $file_location_anvil_uuid) + if (not $file_location_host_uuid) { # Throw an error and exit. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_file_locations()", parameter => "file_location_anvil_uuid" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_file_locations()", parameter => "file_location_host_uuid" }}); return(""); } if (($file_location_active ne "0") && ($file_location_active ne "1")) @@ -8002,9 +8070,9 @@ SELECT FROM file_locations WHERE - file_location_file_uuid = ".$anvil->Database->quote($file_location_file_uuid)." + file_location_file_uuid = ".$anvil->Database->quote($file_location_file_uuid)." AND - file_location_anvil_uuid = ".$anvil->Database->quote($file_location_anvil_uuid)." + file_location_host_uuid = ".$anvil->Database->quote($file_location_host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); @@ -8035,15 +8103,15 @@ INSERT INTO ( file_location_uuid, file_location_file_uuid, - file_location_anvil_uuid, + file_location_host_uuid, file_location_active, modified_date ) VALUES ( ".$anvil->Database->quote($file_location_uuid).", ".$anvil->Database->quote($file_location_file_uuid).", - ".$anvil->Database->quote($file_location_anvil_uuid).", + ".$anvil->Database->quote($file_location_host_uuid).", ".$anvil->Database->quote($file_location_active).", - ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." + ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." ); "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); @@ -8055,7 +8123,7 @@ INSERT INTO my $query = " SELECT file_location_file_uuid, - file_location_anvil_uuid, + file_location_host_uuid, file_location_active, FROM file_locations @@ -8078,31 +8146,31 @@ WHERE } foreach my $row (@{$results}) { - my $old_file_location_file_uuid = $row->[0]; - my $old_file_location_anvil_uuid = $row->[1]; - my $old_file_location_active = $row->[2]; + my $old_file_location_file_uuid = $row->[0]; + my $old_file_location_host_uuid = $row->[1]; + my $old_file_location_active = $row->[2]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - old_file_location_file_uuid => $old_file_location_file_uuid, - old_file_location_anvil_uuid => $old_file_location_anvil_uuid, - old_file_location_active => $old_file_location_active, + old_file_location_file_uuid => $old_file_location_file_uuid, + old_file_location_host_uuid => $old_file_location_host_uuid, + old_file_location_active => $old_file_location_active, }}); # Anything change? - if (($old_file_location_file_uuid ne $file_location_file_uuid) or - ($old_file_location_anvil_uuid ne $file_location_anvil_uuid) or - ($old_file_location_active ne $file_location_active)) + if (($old_file_location_file_uuid ne $file_location_file_uuid) or + ($old_file_location_host_uuid ne $file_location_host_uuid) or + ($old_file_location_active ne $file_location_active)) { # Something changed, save. my $query = " UPDATE file_locations SET - file_location_file_uuid = ".$anvil->Database->quote($file_location_file_uuid).", - file_location_anvil_uuid = ".$anvil->Database->quote($file_location_anvil_uuid).", - file_location_active = ".$anvil->Database->quote($file_location_active).", - modified_date = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." + file_location_file_uuid = ".$anvil->Database->quote($file_location_file_uuid).", + file_location_host_uuid = ".$anvil->Database->quote($file_location_host_uuid).", + file_location_active = ".$anvil->Database->quote($file_location_active).", + modified_date = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." WHERE - file_location_uuid = ".$anvil->Database->quote($file_location_uuid)." + file_location_uuid = ".$anvil->Database->quote($file_location_uuid)." "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); diff --git a/Anvil/Tools/Network.pm b/Anvil/Tools/Network.pm index f6a1a792..0bbb6c68 100644 --- a/Anvil/Tools/Network.pm +++ b/Anvil/Tools/Network.pm @@ -18,6 +18,7 @@ my $THIS_FILE = "Network.pm"; # check_network # check_internet # download +# find_access # find_matches # find_target_ip # get_company_from_mac @@ -1186,6 +1187,169 @@ sub download } +=head2 find_access + +This takes a host's UUID or name, and finds networks that this host can reach it on. If the target is not found in the database, C<< !!error!! >> is returned. Otherwise, the number of matches found is returned. + +B<< Note >>: This requires that the target has recorded it's network in the database. + +It was written to be a saner version of C<< Network->find_matches() >> + +Matches will be stored as: + +* network_access::::local_ip_address = +* network_access::::local_subnet_mask = +* network_access::::local_interface = +* network_access::::local_speed = +* network_access::::target_ip_address = +* network_access::::target_subnet_mask = +* network_access::::target_interface = +* network_access::::target_speed = + +Where C<< network_name >> will be C<< bcnX >>, C<< ifnX >>, etc. + +Paramters; + +=head3 target (required) + +This is the host we're looking for connection options with. + +=cut +sub find_access +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->find_access()" }}); + + my $target = defined $parameter->{target} ? $parameter->{target} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + target => $target, + }}); + + if (not $target) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->find_access()", parameter => "target" }}); + return("!!error!!"); + } + + # Take the target and find the host_uuid and host_name. + my $target_host_uuid = $anvil->Database->get_host_uuid_from_string({debug => $debug, string => $target}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target_host_uuid => $target_host_uuid }}); + + if (not $target_host_uuid) + { + # Bad target. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0414", variables => { target => $target }}); + return("!!error!!"); + } + + my $host_uuid = $anvil->Get->host_uuid; + my $short_host_name = $anvil->Get->short_host_name; + my $target_short_host_name = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{short_host_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + host_uuid => $host_uuid, + short_host_name => $short_host_name, + target_short_host_name => $target_short_host_name, + }}); + + # Load our IPs + $anvil->Network->load_ips({ + debug => $debug, + host => $short_host_name, + }); + # Load our target's IPs. + $anvil->Network->load_ips({ + debug => $debug, + host => $target_short_host_name, + host_uuid => $target_host_uuid, + }); + + # Loop through the first, and on each interface with an IP/subnet mask, look for a match in the second. + foreach my $local_interface (sort {$b cmp $a} keys %{$anvil->data->{network}{$short_host_name}{interface}}) + { + my $local_ip = $anvil->data->{network}{$short_host_name}{interface}{$local_interface}{ip}; + my $local_subnet_mask = $anvil->data->{network}{$short_host_name}{interface}{$local_interface}{subnet_mask}; + my $local_speed = $anvil->data->{network}{$short_host_name}{interface}{$local_interface}{speed}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + local_interface => $local_interface, + local_ip => $local_ip, + local_subnet_mask => $local_subnet_mask, + local_speed => $local_speed, + }}); + + if (($local_ip) && ($local_subnet_mask)) + { + # Look for a match. + my $local_network = $anvil->Network->get_network({ + debug => $debug, + ip => $local_ip, + subnet_mask => $local_subnet_mask, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { local_network => $local_network }}); + + foreach my $target_interface (sort {$b cmp $a} keys %{$anvil->data->{network}{$target_short_host_name}{interface}}) + { + my $target_ip = $anvil->data->{network}{$target_short_host_name}{interface}{$target_interface}{ip}; + my $target_subnet_mask = $anvil->data->{network}{$target_short_host_name}{interface}{$target_interface}{subnet_mask}; + my $target_speed = $anvil->data->{network}{$target_short_host_name}{interface}{$target_interface}{speed}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + target_interface => $target_interface, + target_ip => $target_ip, + target_subnet_mask => $target_subnet_mask, + target_speed => $target_speed, + }}); + if (($target_ip) && ($target_subnet_mask)) + { + # Do we have a match? + my $target_network = $anvil->Network->get_network({ + debug => $debug, + ip => $target_ip, + subnet_mask => $target_subnet_mask, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + local_network => $local_network, + target_network => $target_network, + }}); + + if ($local_network eq $target_network) + { + # Match! + my $network_name = $target_interface; + $network_name =~ s/^(\w+\d+)_.*$/$1/; + + $anvil->data->{network_access}{$network_name}{local_interface} = $local_interface; + $anvil->data->{network_access}{$network_name}{local_speed} = $local_speed; + $anvil->data->{network_access}{$network_name}{local_ip_address} = $local_ip; + $anvil->data->{network_access}{$network_name}{local_subnet_mask} = $local_subnet_mask; + $anvil->data->{network_access}{$network_name}{target_interface} = $target_interface; + $anvil->data->{network_access}{$network_name}{target_speed} = $target_speed; + $anvil->data->{network_access}{$network_name}{target_ip_address} = $target_ip; + $anvil->data->{network_access}{$network_name}{target_subnet_mask} = $target_subnet_mask; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:network_access::${network_name}::local_interface" => $anvil->data->{network_access}{$network_name}{local_interface}, + "s2:network_access::${network_name}::local_speed" => $anvil->data->{network_access}{$network_name}{local_speed}, + "s3:network_access::${network_name}::local_ip_address" => $anvil->data->{network_access}{$network_name}{local_ip_address}, + "s4:network_access::${network_name}::local_subnet_mask" => $anvil->data->{network_access}{$network_name}{local_subnet_mask}, + "s5:network_access::${network_name}::target_interface" => $anvil->data->{network_access}{$network_name}{target_interface}, + "s6:network_access::${network_name}::target_speed" => $anvil->data->{network_access}{$network_name}{target_speed}, + "s7:network_access::${network_name}::target_ip_address" => $anvil->data->{network_access}{$network_name}{target_ip_address}, + "s8:network_access::${network_name}::target_subnet_mask" => $anvil->data->{network_access}{$network_name}{target_subnet_mask}, + }}); + } + } + } + } + } + + my $matches = keys %{$anvil->data->{network_access}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { matches => $matches }}); + return($matches); +} + + + =head2 find_matches This takes two hash keys from prior C<< Network->get_ips() >> or C<< ->load_ips() >> runs and finds which are on the same network. @@ -2763,7 +2927,7 @@ sub load_ips $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Network->load_ips()" }}); my $clear = defined $parameter->{clear} ? $parameter->{clear} : 1; - my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid; + my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : ""; my $host = defined $parameter->{host} ? $parameter->{host} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { clear => $clear, @@ -2773,8 +2937,8 @@ sub load_ips if (not $host_uuid) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Network->load_ips()", parameter => "ip" }}); - return(""); + $host_uuid = $anvil->Get->host_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); } if (not $host) @@ -2832,9 +2996,41 @@ AND ip_address_on_uuid => $ip_address_on_uuid, }}); - my $interface_name = ""; - my $interface_mac = ""; + my $interface_name = ""; + my $interface_mac = ""; + my $network_interface_uuid = ""; if ($ip_address_on_type eq "interface") + { + $network_interface_uuid = $ip_address_on_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { network_interface_uuid => $network_interface_uuid }}); + } + else + { + my $query = ""; + if ($ip_address_on_type eq "bridge") + { + # What's the bridge UUID? + $query = "SELECT bond_active_interface FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; + } + else + { + $query = "SELECT bond_active_interface FROM bonds WHERE bond_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + my $active_interface = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_interface => $active_interface }}); + + if ($active_interface) + { + my $query = "SELECT network_interface_uuid FROM network_interfaces WHERE network_interface_host_uuid = ".$anvil->Database->quote($host_uuid)." AND network_interface_name = ".$anvil->Database->quote($active_interface).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + $network_interface_uuid = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_interface => $active_interface }}); + } + } + + if ($network_interface_uuid) { my $query = " SELECT @@ -2852,7 +3048,7 @@ SELECT FROM network_interfaces WHERE - network_interface_uuid = ".$anvil->Database->quote($ip_address_on_uuid)." + network_interface_uuid = ".$anvil->Database->quote($network_interface_uuid)." AND network_interface_operational != 'DELETED' ;"; @@ -2872,22 +3068,22 @@ AND interface_mac => $interface_mac, }}); - $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid} = $results->[0]->[0]; - $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac; - $anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address; - $anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask} = $ip_address_subnet_mask; - $anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway} = $ip_address_default_gateway; - $anvil->data->{network}{$host}{interface}{$interface_name}{gateway} = $ip_address_gateway; - $anvil->data->{network}{$host}{interface}{$interface_name}{dns} = $ip_address_dns; - $anvil->data->{network}{$host}{interface}{$interface_name}{type} = $ip_address_on_type; - $anvil->data->{network}{$host}{interface}{$interface_name}{speed} = $results->[0]->[3]; - $anvil->data->{network}{$host}{interface}{$interface_name}{mtu} = $results->[0]->[4]; - $anvil->data->{network}{$host}{interface}{$interface_name}{link_state} = $results->[0]->[5]; - $anvil->data->{network}{$host}{interface}{$interface_name}{operational} = $results->[0]->[6]; - $anvil->data->{network}{$host}{interface}{$interface_name}{duplex} = $results->[0]->[7]; - $anvil->data->{network}{$host}{interface}{$interface_name}{medium} = $results->[0]->[8]; - $anvil->data->{network}{$host}{interface}{$interface_name}{bond_uuid} = $results->[0]->[9]; - $anvil->data->{network}{$host}{interface}{$interface_name}{bridge_uuid} = $results->[0]->[10]; + $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid} = $results->[0]->[0]; + $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac; + $anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address; + $anvil->data->{network}{$host}{interface}{$interface_name}{subnet_mask} = $ip_address_subnet_mask; + $anvil->data->{network}{$host}{interface}{$interface_name}{default_gateway} = $ip_address_default_gateway; + $anvil->data->{network}{$host}{interface}{$interface_name}{gateway} = $ip_address_gateway; + $anvil->data->{network}{$host}{interface}{$interface_name}{dns} = $ip_address_dns; + $anvil->data->{network}{$host}{interface}{$interface_name}{type} = $ip_address_on_type; + $anvil->data->{network}{$host}{interface}{$interface_name}{speed} = $results->[0]->[3]; + $anvil->data->{network}{$host}{interface}{$interface_name}{mtu} = $results->[0]->[4]; + $anvil->data->{network}{$host}{interface}{$interface_name}{link_state} = $results->[0]->[5]; + $anvil->data->{network}{$host}{interface}{$interface_name}{operational} = $results->[0]->[6]; + $anvil->data->{network}{$host}{interface}{$interface_name}{duplex} = $results->[0]->[7]; + $anvil->data->{network}{$host}{interface}{$interface_name}{medium} = $results->[0]->[8]; + $anvil->data->{network}{$host}{interface}{$interface_name}{bond_uuid} = defined $results->[0]->[9] ? $results->[0]->[9] : ""; + $anvil->data->{network}{$host}{interface}{$interface_name}{bridge_uuid} = defined $results->[0]->[10] ? $results->[0]->[10] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "network::${host}::interface::${interface_name}::network_interface_uuid" => $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid}, "network::${host}::interface::${interface_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address}, diff --git a/Anvil/Tools/ScanCore.pm b/Anvil/Tools/ScanCore.pm index 968c5ba7..d1b670a5 100644 --- a/Anvil/Tools/ScanCore.pm +++ b/Anvil/Tools/ScanCore.pm @@ -404,15 +404,13 @@ sub call_scan_agents $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }}); # Tell the user this agent is about to run... -# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $debug, key => "log_0252", variables => { -# agent_name => $agent_name, -# timeout => $timeout, -# }}); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $debug, key => "log_0701", variables => { agent_name => $agent_name }}); - ### TODO: Timeout, when called / set here, was hanging virsh calls. Unknown why yet, but temp - ### fix is to just not use timeouts for calls. - #my ($output, $return_code) = $anvil->System->call({timeout => $timeout, shell_call => $shell_call}); - my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $debug, key => "log_0252", variables => { + agent_name => $agent_name, + timeout => $timeout, + }}); +# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $debug, key => "log_0701", variables => { agent_name => $agent_name }}); + my ($output, $return_code) = $anvil->System->call({timeout => $timeout, shell_call => $shell_call}); +# my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output, return_code => $return_code }}); foreach my $line (split/\n/, $output) { diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index ec31d35b..29553250 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -5246,7 +5246,7 @@ Parameters; This is the full path to the file. If the file is not found, C<< !!error!! >> is returned. -=head3 delay (optional, default '2') +=head3 delay (optional, default '10') This is how long to wait before checking to see if the file has changed. diff --git a/notes b/notes index b3c19c85..5aa2fe5a 100644 --- a/notes +++ b/notes @@ -1,6 +1,4 @@ -Add 'lsof' and 'strace' to Required - When pairing Striker, make sure new config goes to all known nodes! diff --git a/share/anvil.sql b/share/anvil.sql index da41e482..70074137 100644 --- a/share/anvil.sql +++ b/share/anvil.sql @@ -1230,24 +1230,24 @@ CREATE TRIGGER trigger_files -- This tracks which files on Strikers should be on Anvils! CREATE TABLE file_locations ( - file_location_uuid uuid not null primary key, - file_location_file_uuid uuid not null, -- This is file to be moved to (or restored to) this machine. - file_location_anvil_uuid uuid not null, -- This is the sum as calculated when the file_location is first uploaded. Once recorded, it can't change. - file_location_active boolean not null default TRUE, -- This is set to true when the file should be on Anvil! machines, triggering rsyncs when needed. When set to false, the file will be deleted from members, if they exist. - modified_date timestamp with time zone not null, + file_location_uuid uuid not null primary key, + file_location_file_uuid uuid not null, -- This is file to be moved to (or restored to) this machine. + file_location_host_uuid uuid not null, -- This is the sum as calculated when the file_location is first uploaded. Once recorded, it can't change. + file_location_active boolean not null default TRUE, -- This is set to true when the file should be on Anvil! machines, triggering rsyncs when needed. When set to false, the file will be deleted from members, if they exist. + modified_date timestamp with time zone not null, FOREIGN KEY(file_location_file_uuid) REFERENCES files(file_uuid), - FOREIGN KEY(file_location_anvil_uuid) REFERENCES anvils(anvil_uuid) + FOREIGN KEY(file_location_host_uuid) REFERENCES hosts(host_uuid) ); ALTER TABLE file_locations OWNER TO admin; CREATE TABLE history.file_locations ( - history_id bigserial, - file_location_uuid uuid, - file_location_file_uuid text, - file_location_anvil_uuid uuid, - file_location_active boolean, - modified_date timestamp with time zone not null + history_id bigserial, + file_location_uuid uuid, + file_location_file_uuid text, + file_location_host_uuid uuid, + file_location_active boolean, + modified_date timestamp with time zone not null ); ALTER TABLE history.file_locations OWNER TO admin; @@ -1260,13 +1260,13 @@ BEGIN INSERT INTO history.file_locations (file_location_uuid, file_location_file_uuid, - file_location_anvil_uuid, + file_location_host_uuid, file_location_active, modified_date) VALUES (history_file_locations.file_location_uuid, history_file_locations.file_location_file_uuid, - history_file_locations.file_location_anvil_uuid, + history_file_locations.file_location_host_uuid, history_file_locations.file_location_active, history_file_locations.modified_date); RETURN NULL; diff --git a/share/words.xml b/share/words.xml index baddc0d7..25cae436 100644 --- a/share/words.xml +++ b/share/words.xml @@ -586,6 +586,7 @@ The definition data passed in was: '. Available servers are;]]> + @@ -2236,7 +2237,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is: We're online as: [#!variable!node_name!#], but we're not quorate yet. Continuing to wait. We're online as: [#!variable!node_name!#] and quorate! We're not online yet. Waiting for 'in_ccm/crmd/join': [#!variable!in_ccm!#/#!variable!crmd!#/#!variable!join!#]. ('in_ccm' = consensus cluster member, communication layer. 'crmd' = cluster resource manager daemon is up, 'join' = allowed to host resources). - The file: [#!variable!file_name!#] is not recorded for the Anvil! [#!variable!anvil_name!#] yet. Registering it now as not sync'ed to this Anvil! system. + The file: [#!variable!file_name!#] is not recorded for the host [#!variable!host_name!#] yet. Registering it now as not sync'ed to this system. Asking 'anvil-boot-server' to boot the servers now. We were asked to delete the file: [#!variable!file!#], but it doesn't exist, so nothing to do. The file: [#!variable!file!#] has been successfully removed. @@ -2657,8 +2658,11 @@ About to try to download aproximately: [#!variable!packages!#] packages needed t Moving the file: [#!variable!file!#] to: [#!data!path::directories::shared::files!#]. Calculating the md5sum. This can take a little while for large files, please be patient. The md5sum is: [#!variable!md5sum!#]. Storing details in the database. - Copying the file over to: [#!variable!host!#]. Please be patient, this could take a bit for large files. - Registering the file to be downloaded to the Anvil!: [#!variable!anvil_name!#]. Anvil! members will sync this file shortly. Member machines that are not online will sync the file when they do return. + +Copying the file: [#!variable!source_file!#] over to: [#!variable!host!#:#!variable!target_directory!#] using: [#!variable!ip_address!# (#!variable!network!#)]. +The file size is: [#!variable!size!#], and the link speed appears to be: [#!variable!link_speed!#], for an estimated copy time of about: [#!variable!eta_copy_time!#]. +Please be patient, this could take a bit for large files. + Registering the file to be downloaded to host: [#!variable!host!#]. Hosts that are not online will sync the file when they do return. Upload is complete! Processing the pull of a file from Striker. We're a DR host and there are: [#!variable!strikers!#] dashboards, so we will wait to pull the file until after the nodes are done. We're currently waiting on; Node 1? [#!variable!node1_waiting!#], Node 2? [#!variable!node2_waiting!#]. We'll check again at: [#!variable!wait_until!#]. @@ -2860,6 +2864,8 @@ Proceed? [y/N] This is a test alert message sent at alert level: [#!variable!level!#]. Failed to send a test alert at level: [#!variable!level!#]. Is anyone listening at that level? Is the mail server configured? The DRBD config file was not found. A protect job needs to be run from the Anvil! node hosting the server to be protected. + Waiting a bit to make sure the file: [#!variable!file!#] is done uploading... + Upload complete. Normal Password @@ -3264,6 +3270,15 @@ If you are comfortable that the target has changed for a known reason, you can s Zbps Ybps Bytes + Bytes/sec + KiB/sec + MiB/sec + GiB/sec + TiB/sec + PiB/sec + EiB/sec + ZiB/sec + YiB/sec Test diff --git a/tools/anvil-manage-files b/tools/anvil-manage-files index 4a0257c1..7ec94178 100755 --- a/tools/anvil-manage-files +++ b/tools/anvil-manage-files @@ -567,7 +567,7 @@ sub check_incoming my $file_md5sum = $recorded_md5sum; if ((not $file_uuid) or ($file_size != $recorded_size)) { - # It's possible the file is still uploading, so sleep for 2 seconds and see if the + # It's possible the file is still uploading, so sleep for a bit and see if the # size is still changing. my $last_size = $file_size; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { diff --git a/tools/anvil-sync-shared b/tools/anvil-sync-shared index a802d90b..32ce105f 100755 --- a/tools/anvil-sync-shared +++ b/tools/anvil-sync-shared @@ -48,10 +48,11 @@ if (not $anvil->data->{switches}{'job-uuid'}) $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }}); } -# If we still don't have a job-uuid, go into interactive mode. +# If we have a job-uuid, process it. if ($anvil->data->{switches}{'job-uuid'}) { # Load the job data. + $anvil->Database->get_anvils({debug => 3}); $anvil->Job->get_job_details({debug => 3}); $anvil->Job->clear({debug => 3}); @@ -440,21 +441,31 @@ sub process_incoming_file $anvil->nice_exit({exit_code => 1}); } + # Make sure the file actually done uploading. + $anvil->data->{sys}{progress} = 5; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0309,!!file!".$file."!!", + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0309", variables => { file => $file }}); + $anvil->Storage->_wait_if_changing({ + debug => 2, + file => $file, + }); + # Move it over to files. - $anvil->data->{sys}{progress} = 10; + $anvil->data->{sys}{progress} = 5; $anvil->Job->update_progress({ progress => $anvil->data->{sys}{progress}, message => "message_0192,!!file!".$file."!!", }); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0192", variables => { file => $file }}); - $anvil->Storage->move_file({ debug => 2, overwrite => 1, source_file => $file, target_file => $anvil->data->{path}{directories}{shared}{files}."/", }); - my $file_name = ($file =~ /\/.*\/(.*?)$/)[0]; my $target_file = $anvil->data->{path}{directories}{shared}{files}."/".$file_name; @@ -561,88 +572,169 @@ sub process_incoming_file }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); - # Now copy this to our peers. - foreach my $host_uuid (sort {$a cmp $b} keys %{$anvil->data->{database}}) + # Now copy this to our peers. We're going to do this serially so that we don't overwhelm the system, + # Any hosts not currently online will have a job registered. + $anvil->Database->get_hosts; + my $host_uuid = $anvil->Get->host_uuid(); + my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}; + my $target_directory = $anvil->data->{path}{directories}{shared}{files}."/"; + foreach my $do_host_type ("striker", "node", "dr") { - # Periodically, autovivication causes and empty key to appear. - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }}); - next if ((not $host_uuid) or (not $anvil->Validate->uuid({uuid => $host_uuid}))); - next if $host_uuid eq $anvil->Get->host_uuid; - - my $host = $anvil->data->{database}{$host_uuid}{host}; - my $password = $anvil->data->{database}{$host_uuid}{password}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - host => $host, - password => $anvil->Log->is_secure($password), - }}); - - my $striker_name = $anvil->Get->host_name_from_uuid({host_uuid => $host_uuid}); - my $say_host = $striker_name." (".$host.")"; - - # Rsync the file. - $anvil->data->{sys}{progress} += 10; - $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; - $anvil->Job->update_progress({ - progress => $anvil->data->{sys}{progress}, - message => "message_0195,!!host!".$say_host."!!", - }); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0195", variables => { host => $say_host }}); - - $anvil->Storage->rsync({ - debug => 2, - source => $target_file, - destination => "root\@".$host.":".$anvil->data->{path}{directories}{shared}{files}."/", - try_again => 1, - }); - } - - # Tell other Anvil! systems to download this file. - $anvil->Database->get_anvils({debug => 2}); - 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_node1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid}; - my $anvil_node2_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid}; - my $anvil_dr1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - anvil_uuid => $anvil_uuid, - anvil_node1_host_uuid => $anvil_node1_host_uuid, - anvil_node2_host_uuid => $anvil_node2_host_uuid, - anvil_dr1_host_uuid => $anvil_dr1_host_uuid, - }}); - - $anvil->data->{sys}{progress} += 5; - $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; - $anvil->Job->update_progress({ - progress => $anvil->data->{sys}{progress}, - message => "message_0196,!!anvil_name!".$anvil_name."!!", - }); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0196", variables => { anvil_name => $anvil_name }}); - - my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ - debug => 2, - file_location_file_uuid => $file_uuid, - file_location_anvil_uuid => $anvil_uuid, - file_location_active => 1, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }}); - - # Register a job to call anvil-sync-shared - foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid, $anvil_dr1_host_uuid) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { do_host_type => $do_host_type }}); + foreach my $target_host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) { - next if not $host_uuid; - my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ - file => $THIS_FILE, - line => __LINE__, - job_command => $anvil->data->{path}{exe}{'anvil-sync-shared'}.$anvil->Log->switches, - job_data => "file_uuid=".$file_uuid, - job_name => "storage::pull_file", - job_title => "job_0132", - job_description => "job_0133", - job_progress => 0, - job_host_uuid => $host_uuid, + my $target_host_uuid = $anvil->data->{sys}{hosts}{by_name}{$target_host_name}; + my $target_host_type = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{host_type}; + my $target_short_host_name = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{short_host_name}; + next if $target_host_uuid eq $host_uuid; + next if $target_host_type ne $do_host_type; + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:target_host_name' => $target_host_name, + 's2:target_host_uuid' => $target_host_uuid, + 's3:target_host_type' => $target_host_type, + 's4:target_short_host_name' => $target_short_host_name, + }}); + + my $matches = $anvil->Network->find_access({ + debug => 2, + target => $target_short_host_name, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matches => $matches }}); + next if not $matches; + next if $matches =~ /\D/; + + # Find a matching IP. + # We prefer to use least to most used networks, with the IFN being the last choice. + my $copied = 0; + foreach my $network ("mn", "bcn", "sn", "ifn") + { + next if $copied; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network => $network }}); + foreach my $network_name (sort {$a cmp $b} keys %{$anvil->data->{network_access}}) + { + next if $copied; + next if $network_name !~ /^$network/i; + my $local_interface = $anvil->data->{network_access}{$network_name}{local_interface}; + my $local_speed = $anvil->data->{network_access}{$network_name}{local_speed}; + my $local_ip_address = $anvil->data->{network_access}{$network_name}{local_ip_address}; + my $local_subnet_mask = $anvil->data->{network_access}{$network_name}{local_subnet_mask}; + my $target_interface = $anvil->data->{network_access}{$network_name}{target_interface}; + my $target_speed = $anvil->data->{network_access}{$network_name}{target_speed}; + my $target_ip_address = $anvil->data->{network_access}{$network_name}{target_ip_address}; + my $target_subnet_mask = $anvil->data->{network_access}{$network_name}{target_subnet_mask}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:local_interface' => $local_interface, + 's2:local_speed' => $local_speed, + 's3:local_ip_address' => $local_ip_address, + 's4:local_subnet_mask' => $local_subnet_mask, + 's5:target_interface' => $target_interface, + 's6:target_speed' => $target_speed, + 's7:target_ip_address' => $target_ip_address, + 's8:target_subnet_mask' => $target_subnet_mask, + }}); + + my $access = $anvil->Remote->test_access({target => $target_ip_address}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }}); + + if ($access) + { + ### Rsync! + # Estimate how long this will take. First, get the speed in + # Mbps and turn it into bytes per second, then into bytes per + # second. We'll take 10% off, then calculate how many seconds + # the copy will take. + my $link_mbps = $target_speed > $local_speed ? $target_speed : $local_speed; + my $link_bps = $link_mbps * 1000000; + my $link_bytes_sec = int($link_bps / 8); + my $adjusted_byptes_sec = int($link_bytes_sec * 0.9); + my $copy_seconds = int($file_size / $adjusted_byptes_sec); + my $say_copy_time = $anvil->Convert->time({'time' => $copy_seconds, translate => 1}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:link_mbps' => $anvil->Convert->add_commas({number => $link_mbps})." ".$anvil->Words->string({string => "#!string!suffix_0050!#"}), + 's2:link_bps' => $anvil->Convert->add_commas({number => $link_bps})." ".$anvil->Words->string({string => "#!string!suffix_0048!#"}), + 's3:link_bytes_sec' => $anvil->Convert->add_commas({number => $link_bytes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), + 's4:adjusted_byptes_sec' => $anvil->Convert->add_commas({number => $adjusted_byptes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), + 's5:copy_seconds' => $anvil->Convert->add_commas({number => $copy_seconds})." ".$anvil->Words->string({string => "#!string!suffix_0007!#"}), + 's6:say_copy_time' => $say_copy_time, + }}); + + my $variables = { + host => $target_short_host_name, + network => $network_name, + ip_address => $target_ip_address, + source_file => $file, + target_directory => $target_directory, + size => $anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}), + link_speed => $anvil->Convert->add_commas({number => $link_mbps})." ".$anvil->Words->string({string => "#!string!suffix_0050!#"}), + eta_copy_time => $say_copy_time, + }; + $anvil->data->{sys}{progress} += 2; + $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0195", + variables => $variables, + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0195", variables => $variables}); + my $problem = $anvil->Storage->rsync({ + debug => 2, + source => $file, + destination => "root\@".$target_ip_address.":".$target_directory, + try_again => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); + if (not $problem) + { + $copied = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { copied => $copied }}); + + $anvil->data->{sys}{progress} += 5; + $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0310", + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0310"}); + } + } + } + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { copied => $copied }}); + if (not $copied) + { + # Failed to connect, register a job instead. + my $variables = { host => $target_host_name }; + $anvil->data->{sys}{progress} += 5; + $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0196", + variables => $variables, + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0196", variables => $variables}); + my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ + file => $THIS_FILE, + line => __LINE__, + job_command => $anvil->data->{path}{exe}{'anvil-sync-shared'}.$anvil->Log->switches, + job_data => "file_uuid=".$file_uuid, + job_name => "storage::pull_file", + job_title => "job_0132", + job_description => "job_0133", + job_progress => 0, + job_host_uuid => $target_host_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); + } + + # Mark the file as being on this host + my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ + debug => 2, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }}); } } diff --git a/tools/anvil-version-changes b/tools/anvil-version-changes index 66fe4ff3..ea0d4e4d 100755 --- a/tools/anvil-version-changes +++ b/tools/anvil-version-changes @@ -62,6 +62,10 @@ $anvil->nice_exit({exit_code => 0}); sub striker_checks { my ($anvil) = @_; + + # This replaces anvil_uuid with host_uuid to support more granular location info to support the new + # multi-target DR system + update_file_locations($anvil); # This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override' update_notifications($anvil); @@ -365,6 +369,133 @@ CREATE TRIGGER trigger_storage_group_members return(0); } +# This replaces anvil_uuid with host_uuid to support more granular location info to support the new +# multi-target DR system +sub update_file_locations +{ + my ($anvil) = @_; + + my $updated = 0; + foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) + { + my $query = "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'file_locations' AND column_name = 'file_location_anvil_uuid';"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + uuid => $uuid, + count => $count, + }}); + + # Wipe it out, then re-add + if ($count) + { + $updated = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { updated => $updated }}); + + my $queries = []; + push @{$queries}, "DROP TABLE public.file_locations;"; + push @{$queries}, "DROP TABLE history.file_locations;"; + push @{$queries}, "DROP FUNCTION history_file_locations() CASCADE;"; + push @{$queries}, q|CREATE TABLE file_locations ( + file_location_uuid uuid not null primary key, + file_location_file_uuid uuid not null, -- This is file to be moved to (or restored to) this machine. + file_location_host_uuid uuid not null, -- This is the sum as calculated when the file_location is first uploaded. Once recorded, it can't change. + file_location_active boolean not null default TRUE, -- This is set to true when the file should be on Anvil! machines, triggering rsyncs when needed. When set to false, the file will be deleted from members, if they exist. + modified_date timestamp with time zone not null, + + FOREIGN KEY(file_location_file_uuid) REFERENCES files(file_uuid), + FOREIGN KEY(file_location_host_uuid) REFERENCES hosts(host_uuid) +); +ALTER TABLE file_locations OWNER TO admin; + +CREATE TABLE history.file_locations ( + history_id bigserial, + file_location_uuid uuid, + file_location_file_uuid text, + file_location_host_uuid uuid, + file_location_active boolean, + modified_date timestamp with time zone not null +); +ALTER TABLE history.file_locations OWNER TO admin; + +CREATE FUNCTION history_file_locations() RETURNS trigger +AS $$ +DECLARE + history_file_locations RECORD; +BEGIN + SELECT INTO history_file_locations * FROM file_locations WHERE file_location_uuid = new.file_location_uuid; + INSERT INTO history.file_locations + (file_location_uuid, + file_location_file_uuid, + file_location_host_uuid, + file_location_active, + modified_date) + VALUES + (history_file_locations.file_location_uuid, + history_file_locations.file_location_file_uuid, + history_file_locations.file_location_host_uuid, + history_file_locations.file_location_active, + history_file_locations.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_file_locations() OWNER TO admin; + +CREATE TRIGGER trigger_file_locations + AFTER INSERT OR UPDATE ON file_locations + FOR EACH ROW EXECUTE PROCEDURE history_file_locations(); +|; + foreach my $query (@{$queries}) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }}); + } + $anvil->Database->write({debug => 2, uuid => $uuid, query => $queries, source => $THIS_FILE, line => __LINE__}); + } + } + + # Make sure all files are linked to all nodes and dr_hosts + if ($updated) + { + $anvil->Database->get_hosts(); + $anvil->Database->get_files(); + $anvil->Database->get_file_locations(); + foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{files}{file_name}}) + { + my $file_uuid = $anvil->data->{files}{file_name}{$file_name}{file_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:file_name' => $file_name, + 's2:file_uuid' => $file_uuid, + }}); + foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) + { + my $host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name}; + my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; + my $host_key = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_key}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:host_name' => $host_name, + 's2:host_uuid' => $host_uuid, + 's3:host_type' => $host_type, + 's4:host_key' => $host_key, + }}); + next if $host_type eq "striker"; + next if $host_key eq "DELETED"; + + my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ + debug => 2, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }}); + } + } + } + + return(0); +} + # This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override' sub update_notifications { @@ -373,9 +504,9 @@ sub update_notifications foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) { my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'notifications';"; - $anvil->Log->variables({source => $THIS_FILE, uuid => $uuid, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); - my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); if ($count) diff --git a/tools/striker-purge-target b/tools/striker-purge-target index 11bd6b5e..e0bc6cc7 100755 --- a/tools/striker-purge-target +++ b/tools/striker-purge-target @@ -281,18 +281,9 @@ WHERE } } - # If deleting an Anvil!, we need to clear any Anvil! references from file_locations and storage groups. + # If deleting an Anvil!, we need to clear any Anvil! references from storage groups. if (($table eq "anvils") && ($anvil->data->{purge}{anvil_uuid})) { - # - my $query = "DELETE FROM history.file_locations WHERE file_location_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); - push @{$queries}, $query; - - $query = "DELETE FROM file_locations WHERE file_location_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); - push @{$queries}, $query; - # Storage groups $query = " SELECT From 254f7ef4e2950057e10b2f8278b0fa6e080b3310 Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 22 Feb 2023 02:13:19 -0500 Subject: [PATCH 5/8] This should fix the tracking of what files belong where, using the new DR links system. It also should finish (though testing is still needed) the serial rsync issue. * Created Database->track_files() as a dedicated method as trying to verify the existence of file_locations during Database->load_anvils() was fragile and prone to recursive loops. * Updated Database->insert_or_update_file_locations() to take an anvil_uuid and recursively call for each host, to maintain compatibility with the old ways, and make it simpler to add an entry for both sub-nodes in an Anvil!. * Created Storage->push_file() that takes a file and rsync's it to all other machines, or creates a job for the file to be pulled if the target can't be accessed. * Updated anvil-manage-files and anvil-sync-shared to use the new Storage->push_files and Database->track_files methods. Signed-off-by: digimer --- Anvil/Tools/Database.pm | 468 +++++++++++++++++++++++++++------------ Anvil/Tools/Storage.pm | 266 ++++++++++++++++++++++ notes | 5 + tools/anvil-manage-files | 61 +++-- tools/anvil-sync-shared | 171 +------------- 5 files changed, 645 insertions(+), 326 deletions(-) diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 8bb634c2..8d2cd92e 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -97,6 +97,7 @@ my $THIS_FILE = "Database.pm"; # refresh_timestamp # resync_databases # shutdown +# track_files # update_host_status # write # _add_to_local_config @@ -401,8 +402,8 @@ sub check_file_locations # Get all the Anvil! systems we know of. $anvil->Database->get_hosts({debug => $debug}); - $anvil->Database->get_files({debug => $debug}); - $anvil->Database->get_file_locations({debug => $debug}); + #$anvil->Database->get_files({debug => $debug}); + #$anvil->Database->get_file_locations({debug => $debug}); foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{hosts}{host_name}}) { @@ -2830,135 +2831,6 @@ WHERE }}); } } - - # Track the files on this Anvil! - foreach my $file_location_uuid (keys %{$anvil->data->{file_locations}{file_location_uuid}}) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuid => $file_location_uuid }}); - - # If either node is set have this file, make sure both are. - my $file_uuid = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid}; - my $anvil_needs_file = 0; - $anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid} = 0; - $anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid} = 0; - if ((exists $anvil->data->{file_locations}{host_uuid}{$anvil_node1_host_uuid}{file_uuid}{$file_uuid}) && - ($anvil->data->{file_locations}{host_uuid}{$anvil_node1_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid})) - { - my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$anvil_node1_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; - my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - file_location_uuid => $file_location_uuid, - file_location_active => $file_location_active, - }}); - - if (not $file_location_active) - { - $anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid} = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "needs_file::${file_uuid}::${anvil_node1_host_uuid}" => $anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid}, - }}); - } - } - if ((exists $anvil->data->{file_locations}{host_uuid}{$anvil_node2_host_uuid}{file_uuid}{$file_uuid}) && - ($anvil->data->{file_locations}{host_uuid}{$anvil_node2_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid})) - { - my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$anvil_node2_host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; - my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - file_location_uuid => $file_location_uuid, - file_location_active => $file_location_active, - }}); - - if (not $file_location_active) - { - $anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid} = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "needs_file::${file_uuid}::${anvil_node2_host_uuid}" => $anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid}, - }}); - } - } - - # If either node has the file, both nodes (and all DRs) need the file. - if (($anvil->{needs_file}{$file_uuid}{$anvil_node1_host_uuid}) or ($anvil->{needs_file}{$file_uuid}{$anvil_node2_host_uuid})) - { - $anvil_needs_file = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_needs_file => $anvil_needs_file }}); - } - - foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid) - { - # If the Anvil! needs the file, make sure that both nodes and any connected DRs have it as well. - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - host_uuid => $host_uuid, - }}); - if ($anvil_needs_file) - { - if (not $anvil->{needs_file}{$file_uuid}{$host_uuid}) - { - $anvil->Database->insert_or_update_file_locations({ - debug => $debug, - file_location_file_uuid => $file_location_uuid, - file_location_host_uuid => $host_uuid, - file_location_active => 1, - }); - } - } - } - - # If the Anvil! node needs the file, record it as being on this Anvil! node. - if ($anvil_needs_file) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "file_locations::file_location_uuid::${file_location_uuid}::file_location_host_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_host_uuid}, - "file_locations::file_location_uuid::${file_location_uuid}::file_location_active" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}, - }}); - next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; - - my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - file_uuid => $file_uuid, - file_name => $file_name, - }}); - - # If the file was deleted, this won't exist - next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name} = $file_name; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_size}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_md5sum}; - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name}, - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_directory" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory}, - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_size" => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}})." (".$anvil->Convert->add_commas({number => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}}).")", - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_md5sum" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum}, - "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_type" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type}, - }}); - - # Make it so that we can list the files by name. - $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid} = $file_uuid; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "anvils::anvil_uuid::${anvil_uuid}::file_name::${file_name}::file_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}, - }}); - - # Make sure linked DR hosts have this file, also. - foreach my $host_uuid (keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}}) - { - my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; - my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; - - if (not $file_location_active) - { - $anvil->Database->insert_or_update_file_locations({ - debug => $debug, - file_location_file_uuid => $file_location_uuid, - file_location_host_uuid => $host_uuid, - file_location_active => 1, - }); - } - } - } - } } return(0); @@ -3372,6 +3244,18 @@ FROM "file_locations::file_location_uuid::${file_location_uuid}::modified_date" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{modified_date}, }}); + ### TODO: Remove this when the WebUI is updated. + # If this host is a node in an Anvil!, set the old 'file_location_anvil_uuid' to maintain + # backwards compatibility. + if ((exists $anvil->data->{hosts}{host_uuid}{$file_location_host_uuid}) && + ($anvil->data->{hosts}{host_uuid}{$file_location_host_uuid}{anvil_uuid})) + { + $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_anvil_uuid} = $anvil->data->{hosts}{host_uuid}{$file_location_host_uuid}{anvil_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "file_locations::file_location_uuid::${file_location_uuid}::file_location_anvil_uuid" => $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_anvil_uuid}, + }}); + } + # Make it easy to find files by anvil and file UUID. $anvil->data->{file_locations}{host_uuid}{$file_location_host_uuid}{file_uuid}{$file_location_file_uuid}{file_location_uuid} = $file_location_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { @@ -8002,6 +7886,12 @@ Parameters; If not passed, a check will be made to see if an existing entry is found for C<< file_location_file_uuid >>. If found, that entry will be updated. If not found, a new record will be inserted. +=head3 file_location_anvil_uuid (required) + +This is the C<< anvils >> -> C<< anvil_uuid >> being referenced. This works by figuring out which hosts are a member of the Anvil! node, and which DR hosts are linked, and makes a recursive call to this method for each of their C<< hosts >> -> C<< host_uuid >>. + +B<< Note >>: When this is used, a comma-separated list of C<< host_uuid=file_location_uuid >> is returned. + =head3 file_location_file_uuid (required) This is the C<< files >> -> C<< file_uuid >> being referenced. @@ -8025,21 +7915,23 @@ sub insert_or_update_file_locations my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_file_locations()" }}); - my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; - my $file = defined $parameter->{file} ? $parameter->{file} : ""; - my $line = defined $parameter->{line} ? $parameter->{line} : ""; - my $file_location_uuid = defined $parameter->{file_location_uuid} ? $parameter->{file_location_uuid} : ""; - my $file_location_file_uuid = defined $parameter->{file_location_file_uuid} ? $parameter->{file_location_file_uuid} : ""; - my $file_location_host_uuid = defined $parameter->{file_location_host_uuid} ? $parameter->{file_location_host_uuid} : ""; - my $file_location_active = defined $parameter->{file_location_active} ? $parameter->{file_location_active} : ""; + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $file_location_uuid = defined $parameter->{file_location_uuid} ? $parameter->{file_location_uuid} : ""; + my $file_location_anvil_uuid = defined $parameter->{file_location_anvil_uuid} ? $parameter->{file_location_anvil_uuid} : ""; + my $file_location_file_uuid = defined $parameter->{file_location_file_uuid} ? $parameter->{file_location_file_uuid} : ""; + my $file_location_host_uuid = defined $parameter->{file_location_host_uuid} ? $parameter->{file_location_host_uuid} : ""; + my $file_location_active = defined $parameter->{file_location_active} ? $parameter->{file_location_active} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - uuid => $uuid, - file => $file, - line => $line, - file_location_uuid => $file_location_uuid, - file_location_file_uuid => $file_location_file_uuid, - file_location_host_uuid => $file_location_host_uuid, - file_location_active => $file_location_active, + uuid => $uuid, + file => $file, + line => $line, + file_location_uuid => $file_location_uuid, + file_location_anvil_uuid => $file_location_anvil_uuid, + file_location_file_uuid => $file_location_file_uuid, + file_location_host_uuid => $file_location_host_uuid, + file_location_active => $file_location_active, }}); if (not $file_location_file_uuid) @@ -8061,6 +7953,59 @@ sub insert_or_update_file_locations return(""); } + # If we've got an Anvil! uuid, find out the hosts and DR links connected to the Anvil! are found and + # this method is recursively called for each host. + if ($file_location_anvil_uuid) + { + $anvil->Database->get_anvils({debug => $debug}); + if (not exists $anvil->data->{anvils}{anvil_uuid}{$file_location_anvil_uuid}) + { + # Bad Anvil! UUID. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0169", variables => { anvil_uuid => $file_location_anvil_uuid }}); + return(""); + } + my $hosts = (); + push @{$hosts}, $anvil->data->{anvils}{anvil_uuid}{$file_location_anvil_uuid}{anvil_node1_host_uuid}; + push @{$hosts}, $anvil->data->{anvils}{anvil_uuid}{$file_location_anvil_uuid}{anvil_node2_host_uuid}; + if (exists $anvil->data->{dr_links}{by_anvil_uuid}{$file_location_anvil_uuid}) + { + foreach my $dr_link_host_uuid (sort {$a cmp $b} keys %{$anvil->data->{dr_links}{by_anvil_uuid}{$file_location_anvil_uuid}{dr_link_host_uuid}}) + { + my $dr_link_uuid = $anvil->data->{dr_links}{by_anvil_uuid}{$file_location_anvil_uuid}{dr_link_host_uuid}{$dr_link_host_uuid}{dr_link_uuid}; + my $dr_link_note = $anvil->data->{dr_links}{dr_link_uuid}{$dr_link_uuid}{dr_link_note}; + my $dr_link_short_host_name = $anvil->data->{hosts}{host_uuid}{$dr_link_host_uuid}{short_host_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:dr_link_host_uuid" => $dr_link_host_uuid, + "s2:dr_link_uuid" => $dr_link_uuid, + "s3:dr_link_note" => $dr_link_note, + "s4:dr_link_short_host_name" => $dr_link_short_host_name, + }}); + + next if $dr_link_note eq "DELETED"; + push @{$hosts}, $dr_link_host_uuid; + } + } + my $file_location_uuids = ""; + foreach my $host_uuid (@{$hosts}) + { + my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_location_file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => $file_location_active, + }); + $file_location_uuids .= $host_uuid."=".$file_location_uuid.","; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_location_uuid => $file_location_uuid, + file_location_uuids => $file_location_uuids, + }}); + } + $file_location_uuids =~ s/,$//; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuids => $file_location_uuids }}); + + return($file_location_uuids); + } + # If we don't have a UUID, see if we can find one for the given md5sum. if (not $file_location_uuid) { @@ -8124,7 +8069,7 @@ INSERT INTO SELECT file_location_file_uuid, file_location_host_uuid, - file_location_active, + file_location_active FROM file_locations WHERE @@ -17575,6 +17520,237 @@ AND } +=head2 track_file + +This looks at all files in the database, and then for all Anvil! systems and linked DR hosts, ensures that there's a corresponding C<< file_locations >> entry. + +This method takes no parameters. + +=cut +sub track_files +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->track_files()" }}); + + my $anvils = keys %{$anvil->data->{anvils}{anvil_name}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvils => $anvils }}); + if (not $anvils) + { + $anvil->Database->get_anvils({debug => $debug}); + } + + my $files = keys %{$anvil->data->{files}{file_uuid}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { files => $files }}); + if (not $files) + { + $anvil->Database->get_files({debug => $debug}); + } + + my $file_locations = keys %{$anvil->data->{file_locations}{file_location_uuid}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_locations => $file_locations }}); + if (not $file_locations) + { + $anvil->Database->get_file_locations({debug => $debug}); + } + + my $dr_link_uuid = keys %{$anvil->data->{dr_links}{dr_link_uuid}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { dr_link_uuid => $dr_link_uuid }}); + if (not $dr_link_uuid) + { + $anvil->Database->get_dr_links({debug => $debug}); + } + + 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_uuid}{$anvil_uuid}{anvil_description}; + my $anvil_node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; + my $anvil_node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:anvil_name' => $anvil_name, + 's2:anvil_uuid' => $anvil_uuid, + 's3:anvil_description' => $anvil_description, + 's4:anvil_node1_host_uuid' => $anvil_node1_host_uuid, + 's5:anvil_node2_host_uuid' => $anvil_node2_host_uuid, + }}); + + # Loop through all files and see if there's a corresponding file_location for each sub-node. + my $reload = 0; + foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{files}{file_name}}) + { + my $file_uuid = $anvil->data->{files}{file_name}{$file_name}{file_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:file_name' => $file_name, + 's2:file_uuid' => $file_uuid, + }}); + + foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + + if ((not exists $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}) or + ($anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid})) + { + # Add it + $reload = 1; + my ($file_location_uuid) = $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + reload => $reload, + file_location_uuid => $file_location_uuid, + }}); + } + } + } + if ($reload) + { + $anvil->Database->get_file_locations({debug => $debug}); + } + + # Track the files on this Anvil! + foreach my $file_location_uuid (keys %{$anvil->data->{file_locations}{file_location_uuid}}) + { + my $file_uuid = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_file_uuid}; + my $file_type = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type}; + my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_location_uuid => $file_location_uuid, + file_uuid => $file_uuid, + file_type => $file_type, + file_name => $file_name, + }}); + next if $file_type eq "DELETED"; + + ### TODO - Left off here, not adding DR links. + my $anvil_needs_file = 0; + foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + + if ((exists $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}) && + ($anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid})) + { + my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; + my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_location_uuid => $file_location_uuid, + file_location_active => $file_location_active, + }}); + + if ($file_location_active) + { + $anvil_needs_file = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_needs_file => $anvil_needs_file }}); + } + } + } + + # If either node wanted the file, both nodes and all linked DRs need it. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_needs_file => $anvil_needs_file }}); + if ($anvil_needs_file) + { + # Update the hosts + foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + + my ($file_location_uuid) = $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuid => $file_location_uuid }}); + } + + # Make sure linked DR hosts have this file, also. + foreach my $host_uuid (keys %{$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{dr_host}}) + { + my $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; + my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + + if (not $file_location_active) + { + my ($file_location_uuid) = $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuid => $file_location_uuid }}); + } + } + + # If the file was deleted, this won't exist + next if not exists $anvil->data->{files}{file_uuid}{$file_uuid}; + + # Record that this Anvil! node has this file. + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name} = $file_name; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_size}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_md5sum}; + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type} = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_name" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_name}, + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_directory" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_directory}, + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_size" => $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}})." (".$anvil->Convert->add_commas({number => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_size}}).")", + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_md5sum" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_md5sum}, + "anvils::anvil_uuid::${anvil_uuid}::file_uuid::${file_uuid}::file_type" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_type}, + }}); + + # Make it so that we can list the files by name. + $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid} = $file_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "anvils::anvil_uuid::${anvil_uuid}::file_name::${file_name}::file_uuid" => $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{file_name}{$file_name}{file_uuid}, + }}); + + # Make sure linked DR hosts have this file, also. + foreach my $host_uuid (keys %{$anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}}) + { + my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + host_name => $host_name, + host_uuid => $host_uuid, + }}); + + my $file_location_uuid = ""; + my $file_location_active = 0; + if (exists $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}) + { + $file_location_uuid = $anvil->data->{file_locations}{host_uuid}{$host_uuid}{file_uuid}{$file_uuid}{file_location_uuid}; + $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active}; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_location_uuid => $file_location_uuid, + file_location_active => $file_location_active, + }}); + + if (not $file_location_active) + { + my ($file_location_uuid) = $anvil->Database->insert_or_update_file_locations({ + debug => $debug, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file_location_uuid => $file_location_uuid }}); + } + } + } + } + } + + return(0); +} + + =head2 update_host_status This is a variant on C<< insert_or_update_hosts >> designed only to update the power status of a host. diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 29553250..f2055da6 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -35,6 +35,7 @@ my $THIS_FILE = "Storage.pm"; # move_file # parse_df # parse_lsblk +# push_file # read_config # read_file # read_mode @@ -3451,6 +3452,271 @@ sub parse_lsblk } +=head2 push_file + +This takes a file and pushes it to all other machines in the cluster, serially. For machines that can't be accessed, a job is registered to pull the file. + +If C<< switches::job-uuid >> is set, the corresponding job will be updated. The progress assumes that C<< sys::progress >> is set. + +Parameters; + +=head3 file (required) + +This is the source file to copy from locally and push it to all peers' C<< /mnt/shared/files/ >> directory. + +=cut +sub push_file +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Storage->push_file()" }}); + + # Setup default values + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $file_uuid = defined $parameter->{file_uuid} ? $parameter->{file_uuid} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file => $file, + file_uuid => $file_uuid, + }}); + + if (not $file) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->push_file()", parameter => "file" }}); + return("!!error!!"); + } + if (not -f $file) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0105", variables => { file => $file }}); + return("!!error!!"); + } + + $anvil->Database->get_files({debug => $debug}); + my $file_size = 0; + my ($file_directory, $file_name) = ($file =~ /^(.*)\/(.*?)$/); + $file_directory =~ s/\/$//g; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:file_directory' => $file_directory, + 's2:file_name' => $file_name, + }}); + if (not $file_uuid) + { + # Can we find the file? + foreach my $this_file_uuid (keys %{$anvil->data->{files}{file_uuid}}) + { + my $this_file_name = $anvil->data->{files}{file_uuid}{$this_file_uuid}{file_name}; + my $this_file_directory = $anvil->data->{files}{file_uuid}{$this_file_uuid}{file_directory}; + $this_file_directory =~ s/\/$//g; + my $this_file_size = $anvil->data->{files}{file_uuid}{$this_file_uuid}{file_size}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + this_file_uuid => $this_file_uuid, + this_file_directory => $this_file_directory, + this_file_name => $this_file_name, + this_file_size => $this_file_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $this_file_size}).")", + }}); + + if (($file_name eq $this_file_name) && + ($file_directory eq $this_file_directory)) + { + # Found it. + $file_uuid = $this_file_uuid; + $file_size = $this_file_size; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_uuid => $file_uuid, + file_size => $file_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}).")", + }}); + last; + } + } + } + + if (not $file_uuid) + { + $file_size = (stat($file))[7]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + file_size => $file_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $file_size}).")", + }}); + } + + # Now copy this to our peers. We're going to do this serially so that we don't overwhelm the system, + # Any hosts not currently online will have a job registered. + $anvil->Database->get_hosts; + my $host_uuid = $anvil->Get->host_uuid(); + my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}; + my $target_directory = $anvil->data->{path}{directories}{shared}{files}."/"; + foreach my $do_host_type ("striker", "node", "dr") + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { do_host_type => $do_host_type }}); + foreach my $target_host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) + { + my $target_host_uuid = $anvil->data->{sys}{hosts}{by_name}{$target_host_name}; + my $target_host_type = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{host_type}; + my $target_short_host_name = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{short_host_name}; + next if $target_host_uuid eq $host_uuid; + next if $target_host_type ne $do_host_type; + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:target_host_name' => $target_host_name, + 's2:target_host_uuid' => $target_host_uuid, + 's3:target_host_type' => $target_host_type, + 's4:target_short_host_name' => $target_short_host_name, + }}); + + my $matches = $anvil->Network->find_access({ + debug => 2, + target => $target_short_host_name, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matches => $matches }}); + next if not $matches; + next if $matches =~ /\D/; + + # Find a matching IP. + # We prefer to use least to most used networks, with the IFN being the last choice. + my $copied = 0; + foreach my $network ("mn", "bcn", "sn", "ifn") + { + next if $copied; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network => $network }}); + foreach my $network_name (sort {$a cmp $b} keys %{$anvil->data->{network_access}}) + { + next if $copied; + next if $network_name !~ /^$network/i; + my $local_interface = $anvil->data->{network_access}{$network_name}{local_interface}; + my $local_speed = $anvil->data->{network_access}{$network_name}{local_speed}; + my $local_ip_address = $anvil->data->{network_access}{$network_name}{local_ip_address}; + my $local_subnet_mask = $anvil->data->{network_access}{$network_name}{local_subnet_mask}; + my $target_interface = $anvil->data->{network_access}{$network_name}{target_interface}; + my $target_speed = $anvil->data->{network_access}{$network_name}{target_speed}; + my $target_ip_address = $anvil->data->{network_access}{$network_name}{target_ip_address}; + my $target_subnet_mask = $anvil->data->{network_access}{$network_name}{target_subnet_mask}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:local_interface' => $local_interface, + 's2:local_speed' => $local_speed, + 's3:local_ip_address' => $local_ip_address, + 's4:local_subnet_mask' => $local_subnet_mask, + 's5:target_interface' => $target_interface, + 's6:target_speed' => $target_speed, + 's7:target_ip_address' => $target_ip_address, + 's8:target_subnet_mask' => $target_subnet_mask, + }}); + + my $access = $anvil->Remote->test_access({target => $target_ip_address}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }}); + + if ($access) + { + ### Rsync! + # Estimate how long this will take. First, get the speed in + # Mbps and turn it into bytes per second, then into bytes per + # second. We'll take 10% off, then calculate how many seconds + # the copy will take. + my $link_mbps = $target_speed > $local_speed ? $target_speed : $local_speed; + my $link_bps = $link_mbps * 1000000; + my $link_bytes_sec = int($link_bps / 8); + my $adjusted_byptes_sec = int($link_bytes_sec * 0.9); + my $copy_seconds = int($file_size / $adjusted_byptes_sec); + my $say_copy_time = $anvil->Convert->time({'time' => $copy_seconds, translate => 1}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:link_mbps' => $anvil->Convert->add_commas({number => $link_mbps})." ".$anvil->Words->string({string => "#!string!suffix_0050!#"}), + 's2:link_bps' => $anvil->Convert->add_commas({number => $link_bps})." ".$anvil->Words->string({string => "#!string!suffix_0048!#"}), + 's3:link_bytes_sec' => $anvil->Convert->add_commas({number => $link_bytes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), + 's4:adjusted_byptes_sec' => $anvil->Convert->add_commas({number => $adjusted_byptes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), + 's5:copy_seconds' => $anvil->Convert->add_commas({number => $copy_seconds})." ".$anvil->Words->string({string => "#!string!suffix_0007!#"}), + 's6:say_copy_time' => $say_copy_time, + }}); + + my $variables = { + host => $target_short_host_name, + network => $network_name, + ip_address => $target_ip_address, + source_file => $file, + target_directory => $target_directory, + size => $anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}), + link_speed => $anvil->Convert->add_commas({number => $link_mbps})." ".$anvil->Words->string({string => "#!string!suffix_0050!#"}), + eta_copy_time => $say_copy_time, + }; + $anvil->data->{sys}{progress} += 2; + $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0195", + variables => $variables, + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0195", variables => $variables}); + my $problem = $anvil->Storage->rsync({ + debug => 2, + source => $file, + destination => "root\@".$target_ip_address.":".$target_directory, + try_again => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); + if (not $problem) + { + $copied = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { copied => $copied }}); + + $anvil->data->{sys}{progress} += 5; + $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0310", + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0310"}); + } + } + } + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); + if ($file_uuid) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { copied => $copied }}); + if (not $copied) + { + # Failed to connect, register a job instead. + my $variables = { host => $target_host_name }; + $anvil->data->{sys}{progress} += 5; + $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; + $anvil->Job->update_progress({ + progress => $anvil->data->{sys}{progress}, + message => "message_0196", + variables => $variables, + }); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0196", variables => $variables}); + my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ + file => $THIS_FILE, + line => __LINE__, + job_command => $anvil->data->{path}{exe}{'anvil-sync-shared'}.$anvil->Log->switches, + job_data => "file_uuid=".$file_uuid, + job_name => "storage::pull_file", + job_title => "job_0132", + job_description => "job_0133", + job_progress => 0, + job_host_uuid => $target_host_uuid, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); + } + + # Mark the file as being on this host + if ($do_host_type ne "striker") + { + my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ + debug => 2, + file_location_file_uuid => $file_uuid, + file_location_host_uuid => $host_uuid, + file_location_active => 1, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }}); + } + } + } + } + + return(0); +} + + =head2 read_config This method is used to read 'Anvil::Tools' style configuration files. These configuration files are in the format: diff --git a/notes b/notes index 5aa2fe5a..c4012abd 100644 --- a/notes +++ b/notes @@ -1,4 +1,9 @@ +Common queries; +* SELECT a.job_uuid, b.host_name, a.job_command, a.job_data, a.job_progress, a.job_status FROM jobs a, hosts b WHERE a.job_host_uuid = b.host_uuid AND a.job_progress != 100; +* SELECT a.host_name, b.file_name, c.file_location_active FROM hosts a, files b, file_locations c WHERE a.host_uuid = c.file_location_host_uuid AND b.file_uuid = c.file_location_file_uuid ORDER BY b.file_name ASC, a.host_name ASC; +* SELECT a.dr_link_uuid, b.host_name, c.anvil_name, a.dr_link_note FROM dr_links a, hosts b, anvils c WHERE a.dr_link_host_uuid = b.host_uuid AND a.dr_link_anvil_uuid = c.anvil_uuid ORDER BY c.anvil_name ASC, b.host_name ASC; + When pairing Striker, make sure new config goes to all known nodes! diff --git a/tools/anvil-manage-files b/tools/anvil-manage-files index 7ec94178..1145ebed 100755 --- a/tools/anvil-manage-files +++ b/tools/anvil-manage-files @@ -564,7 +564,8 @@ sub check_incoming }}); # Calculate the md5sum? - my $file_md5sum = $recorded_md5sum; + my $added_to_db = 0; + my $file_md5sum = $recorded_md5sum; if ((not $file_uuid) or ($file_size != $recorded_size)) { # It's possible the file is still uploading, so sleep for a bit and see if the @@ -608,7 +609,7 @@ sub check_incoming # Insert or update the files entry. ($file_uuid) = $anvil->Database->insert_or_update_files({ - debug => 3, + debug => 2, file_uuid => $file_uuid, file_name => $file_name, file_directory => $file_directory, @@ -617,15 +618,21 @@ sub check_incoming file_mtime => $file_mtime, file_type => $say_mimetype, }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); + + if ($file_uuid) + { + $added_to_db = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { added_to_db => $added_to_db }}); + } } # If we still don't have a file UUID for some reason, skip this file. - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); next if not $file_uuid; # Are we in the incoming directory? If so, move the file. - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { full_path => $full_path, incoming_directory => $incoming_directory, }}); @@ -640,28 +647,34 @@ sub check_incoming my $target = $say_mimetype eq "definition" ? $anvil->data->{path}{directories}{shared}{definitions} : $anvil->data->{path}{directories}{shared}{files}; $target .= "/"; $target =~ s/\/\//\//g; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { target => $target }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { target => $target }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0268", variables => { file => $full_path, target => $target, }}); - # Wait in case it's still being uploaded. - $anvil->Storage->_wait_if_changing({ - file => $full_path, - last_size => $file_size, - }); - - $anvil->Storage->move_file({ - debug => 3, + my $problem = $anvil->Storage->move_file({ + debug => 2, source_file => $full_path, target_file => $target, }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); + if (not $problem) + { + $full_path = $target."/".$file_name; + $file_directory = $target; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + file_directory => $file_directory, + full_path => $full_path, + }}); + } # Update the file_directory. - ($file_uuid) = $anvil->Database->insert_or_update_files({ - debug => 3, + $target =~ s/\/$//; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { target => $target }}); + ($file_uuid) = $anvil->Database->insert_or_update_files({ + debug => 2, file_uuid => $file_uuid, file_name => $file_name, file_directory => $target, @@ -670,7 +683,20 @@ sub check_incoming file_mtime => $file_mtime, file_type => $say_mimetype, }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file_uuid => $file_uuid }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); + } + + if ($added_to_db) + { + # Push it to all other hosts now. + $anvil->Storage->push_file({ + debug => 2, + file => $full_path, + file_uuid => $file_uuid, + }); + + # Call track_files, it'll make sure the file_locations are setup. + $anvil->Database->track_files({debug => 2}); } } @@ -729,6 +755,7 @@ WHERE { $query .= " file_name = ".$anvil->Database->quote($file_name)."\n"; } + $query .= ";"; $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}; diff --git a/tools/anvil-sync-shared b/tools/anvil-sync-shared index 32ce105f..fd1f72fe 100755 --- a/tools/anvil-sync-shared +++ b/tools/anvil-sync-shared @@ -574,169 +574,14 @@ sub process_incoming_file # Now copy this to our peers. We're going to do this serially so that we don't overwhelm the system, # Any hosts not currently online will have a job registered. - $anvil->Database->get_hosts; - my $host_uuid = $anvil->Get->host_uuid(); - my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}; - my $target_directory = $anvil->data->{path}{directories}{shared}{files}."/"; - foreach my $do_host_type ("striker", "node", "dr") - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { do_host_type => $do_host_type }}); - foreach my $target_host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) - { - my $target_host_uuid = $anvil->data->{sys}{hosts}{by_name}{$target_host_name}; - my $target_host_type = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{host_type}; - my $target_short_host_name = $anvil->data->{hosts}{host_uuid}{$target_host_uuid}{short_host_name}; - next if $target_host_uuid eq $host_uuid; - next if $target_host_type ne $do_host_type; - - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - 's1:target_host_name' => $target_host_name, - 's2:target_host_uuid' => $target_host_uuid, - 's3:target_host_type' => $target_host_type, - 's4:target_short_host_name' => $target_short_host_name, - }}); - - my $matches = $anvil->Network->find_access({ - debug => 2, - target => $target_short_host_name, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matches => $matches }}); - next if not $matches; - next if $matches =~ /\D/; - - # Find a matching IP. - # We prefer to use least to most used networks, with the IFN being the last choice. - my $copied = 0; - foreach my $network ("mn", "bcn", "sn", "ifn") - { - next if $copied; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network => $network }}); - foreach my $network_name (sort {$a cmp $b} keys %{$anvil->data->{network_access}}) - { - next if $copied; - next if $network_name !~ /^$network/i; - my $local_interface = $anvil->data->{network_access}{$network_name}{local_interface}; - my $local_speed = $anvil->data->{network_access}{$network_name}{local_speed}; - my $local_ip_address = $anvil->data->{network_access}{$network_name}{local_ip_address}; - my $local_subnet_mask = $anvil->data->{network_access}{$network_name}{local_subnet_mask}; - my $target_interface = $anvil->data->{network_access}{$network_name}{target_interface}; - my $target_speed = $anvil->data->{network_access}{$network_name}{target_speed}; - my $target_ip_address = $anvil->data->{network_access}{$network_name}{target_ip_address}; - my $target_subnet_mask = $anvil->data->{network_access}{$network_name}{target_subnet_mask}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - 's1:local_interface' => $local_interface, - 's2:local_speed' => $local_speed, - 's3:local_ip_address' => $local_ip_address, - 's4:local_subnet_mask' => $local_subnet_mask, - 's5:target_interface' => $target_interface, - 's6:target_speed' => $target_speed, - 's7:target_ip_address' => $target_ip_address, - 's8:target_subnet_mask' => $target_subnet_mask, - }}); - - my $access = $anvil->Remote->test_access({target => $target_ip_address}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { access => $access }}); - - if ($access) - { - ### Rsync! - # Estimate how long this will take. First, get the speed in - # Mbps and turn it into bytes per second, then into bytes per - # second. We'll take 10% off, then calculate how many seconds - # the copy will take. - my $link_mbps = $target_speed > $local_speed ? $target_speed : $local_speed; - my $link_bps = $link_mbps * 1000000; - my $link_bytes_sec = int($link_bps / 8); - my $adjusted_byptes_sec = int($link_bytes_sec * 0.9); - my $copy_seconds = int($file_size / $adjusted_byptes_sec); - my $say_copy_time = $anvil->Convert->time({'time' => $copy_seconds, translate => 1}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - 's1:link_mbps' => $anvil->Convert->add_commas({number => $link_mbps})." ".$anvil->Words->string({string => "#!string!suffix_0050!#"}), - 's2:link_bps' => $anvil->Convert->add_commas({number => $link_bps})." ".$anvil->Words->string({string => "#!string!suffix_0048!#"}), - 's3:link_bytes_sec' => $anvil->Convert->add_commas({number => $link_bytes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), - 's4:adjusted_byptes_sec' => $anvil->Convert->add_commas({number => $adjusted_byptes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), - 's5:copy_seconds' => $anvil->Convert->add_commas({number => $copy_seconds})." ".$anvil->Words->string({string => "#!string!suffix_0007!#"}), - 's6:say_copy_time' => $say_copy_time, - }}); - - my $variables = { - host => $target_short_host_name, - network => $network_name, - ip_address => $target_ip_address, - source_file => $file, - target_directory => $target_directory, - size => $anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}), - link_speed => $anvil->Convert->add_commas({number => $link_mbps})." ".$anvil->Words->string({string => "#!string!suffix_0050!#"}), - eta_copy_time => $say_copy_time, - }; - $anvil->data->{sys}{progress} += 2; - $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; - $anvil->Job->update_progress({ - progress => $anvil->data->{sys}{progress}, - message => "message_0195", - variables => $variables, - }); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0195", variables => $variables}); - my $problem = $anvil->Storage->rsync({ - debug => 2, - source => $file, - destination => "root\@".$target_ip_address.":".$target_directory, - try_again => 1, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); - if (not $problem) - { - $copied = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { copied => $copied }}); - - $anvil->data->{sys}{progress} += 5; - $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; - $anvil->Job->update_progress({ - progress => $anvil->data->{sys}{progress}, - message => "message_0310", - }); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0310"}); - } - } - } - } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { copied => $copied }}); - if (not $copied) - { - # Failed to connect, register a job instead. - my $variables = { host => $target_host_name }; - $anvil->data->{sys}{progress} += 5; - $anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90; - $anvil->Job->update_progress({ - progress => $anvil->data->{sys}{progress}, - message => "message_0196", - variables => $variables, - }); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0196", variables => $variables}); - my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ - file => $THIS_FILE, - line => __LINE__, - job_command => $anvil->data->{path}{exe}{'anvil-sync-shared'}.$anvil->Log->switches, - job_data => "file_uuid=".$file_uuid, - job_name => "storage::pull_file", - job_title => "job_0132", - job_description => "job_0133", - job_progress => 0, - job_host_uuid => $target_host_uuid, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); - } - - # Mark the file as being on this host - my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ - debug => 2, - file_location_file_uuid => $file_uuid, - file_location_host_uuid => $host_uuid, - file_location_active => 1, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }}); - } - } + $anvil->Storage->push_file({ + debug => 2, + file => $file, + file_uuid => $file_uuid, + }); + + # Call track_files, it'll make sure the file_locations are setup. + $anvil->Database->track_files({debug => 2}); # Done! $anvil->Job->update_progress({ From ab3e8afe6e963a86cff684948d1527aa0edb498e Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 22 Feb 2023 13:21:29 -0500 Subject: [PATCH 6/8] Fixed a bug in Storage->push_file() where file path wasn't updated from incoming to files, preventing the push to other hosts from working. Also fixed a minor issue where the file size was sometimes 0, making transfer calculations useless. Signed-off-by: digimer --- Anvil/Tools/Storage.pm | 6 ++++-- tools/anvil-sync-shared | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index f2055da6..f325390e 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -3531,7 +3531,7 @@ sub push_file } } - if (not $file_uuid) + if ((not $file_uuid) or (not $file_size)) { $file_size = (stat($file))[7]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { @@ -3624,6 +3624,7 @@ sub push_file 's4:adjusted_byptes_sec' => $anvil->Convert->add_commas({number => $adjusted_byptes_sec})." ".$anvil->Words->string({string => "#!string!suffix_0060!#"}), 's5:copy_seconds' => $anvil->Convert->add_commas({number => $copy_seconds})." ".$anvil->Words->string({string => "#!string!suffix_0007!#"}), 's6:say_copy_time' => $say_copy_time, + 's7:file_size' => $anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}), }}); my $variables = { @@ -3699,12 +3700,13 @@ sub push_file } # Mark the file as being on this host + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { do_host_type => $do_host_type }}); if ($do_host_type ne "striker") { my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({ debug => 2, file_location_file_uuid => $file_uuid, - file_location_host_uuid => $host_uuid, + file_location_host_uuid => $target_host_uuid, file_location_active => 1, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }}); diff --git a/tools/anvil-sync-shared b/tools/anvil-sync-shared index fd1f72fe..4fb02354 100755 --- a/tools/anvil-sync-shared +++ b/tools/anvil-sync-shared @@ -576,7 +576,7 @@ sub process_incoming_file # Any hosts not currently online will have a job registered. $anvil->Storage->push_file({ debug => 2, - file => $file, + file => $target_file, file_uuid => $file_uuid, }); From 147f31aeebcf7786e6f0ea43e3ff415a9c60c84e Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 22 Feb 2023 18:37:13 -0500 Subject: [PATCH 7/8] * Added a loop when calling 'anvil-change-password' in a loop as there appears to be an unknown condition where during setup, this is called but never actually runs. Signed-off-by: digimer --- tools/anvil-configure-host | 49 +++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/tools/anvil-configure-host b/tools/anvil-configure-host index ed03a1fb..f6bcb496 100755 --- a/tools/anvil-configure-host +++ b/tools/anvil-configure-host @@ -174,21 +174,48 @@ sub update_passwords } else { - my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $anvil->data->{path}{exe}{'anvil-change-password'}." -y --password-file ".$temp_file.$anvil->Log->switches }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); - foreach my $line (split/\n/, $output) + my $wait_until = time + 120; + my $waiting = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + waiting => $waiting, + wait_until => $wait_until, + }}); + while ($waiting) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + my $shell_call = $anvil->data->{path}{exe}{'anvil-change-password'}." -y --password-file ".$temp_file.$anvil->Log->switches; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); + + my ($output, $return_code) = $anvil->System->call({debug => 2, timeout => 15, shell_call => $shell_call }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }}); + foreach my $line (split/\n/, $output) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); + } + + if ($return_code) + { + # Something went wrong + if (time > $wait_until) + { + # Give up. + $waiting = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }}); + } + else + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0011", variables => { return_code => $return_code }}); + } + } + else + { + # Success + $waiting = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }}); + } } # Unlink the temp file. unlink $temp_file; - - if ($return_code) - { - # Something went wrong - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0011", variables => { return_code => $return_code }}); - } } } @@ -408,7 +435,7 @@ ORDER BY }}); # An undefined interface will have the MAC address value set to '1', ignore those. - next if $variable_value = 1; + next if $variable_value == 1; if ($variable_name =~ /form::config_step2::(.*?)_mac_to_set::value/) { From 5dbdd20d7e3b99def3781cf490dc6ded626ad90e Mon Sep 17 00:00:00 2001 From: digimer Date: Thu, 23 Feb 2023 00:39:43 -0500 Subject: [PATCH 8/8] * Fixed a bug in Network->load_ips() where the IP address on a bridge or bond was having the device name recorded incorrectly. Signed-off-by: digimer --- Anvil/Tools/Network.pm | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/Anvil/Tools/Network.pm b/Anvil/Tools/Network.pm index 0bbb6c68..b7126177 100644 --- a/Anvil/Tools/Network.pm +++ b/Anvil/Tools/Network.pm @@ -2747,7 +2747,7 @@ AND "network::${host}::interface::${bond_name}::down_delay" => $anvil->data->{network}{$host}{interface}{$bond_name}{down_delay}, "network::${host}::interface::${bond_name}::mac_address" => $anvil->data->{network}{$host}{interface}{$bond_name}{mac_address}, "network::${host}::interface::${bond_name}::operational" => $anvil->data->{network}{$host}{interface}{$bond_name}{operational}, - "network::${host}::interface::${bond_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{bridge}, + "network::${host}::interface::${bond_name}::bridge_uuid" => $anvil->data->{network}{$host}{interface}{$bond_name}{bridge_uuid}, "network::${host}::interface::${bond_name}::type" => $anvil->data->{network}{$host}{interface}{$bond_name}{type}, }}); } @@ -2996,6 +2996,8 @@ AND ip_address_on_uuid => $ip_address_on_uuid, }}); + my $bridge_name = ""; + my $bond_name = ""; my $interface_name = ""; my $interface_mac = ""; my $network_interface_uuid = ""; @@ -3010,15 +3012,28 @@ AND if ($ip_address_on_type eq "bridge") { # What's the bridge UUID? - $query = "SELECT bond_active_interface FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; + $query = "SELECT bond_name, bond_active_interface FROM bonds WHERE bond_bridge_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; + + # Get the bridge name, also. + if (1) + { + my $query = "SELECT bridge_name FROM bridges WHERE bridge_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; + $bridge_name = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { bridge_name => $bridge_name }}); + } } else { - $query = "SELECT bond_active_interface FROM bonds WHERE bond_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; + $query = "SELECT bond_name, bond_active_interface FROM bonds WHERE bond_uuid = ".$anvil->Database->quote($ip_address_on_uuid).";"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $active_interface = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { active_interface => $active_interface }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $bond_name = $results->[0]->[0]; + my $active_interface = $results->[0]->[1]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + interface_name => $interface_name, + active_interface => $active_interface, + }}); if ($active_interface) { @@ -3068,6 +3083,18 @@ AND interface_mac => $interface_mac, }}); + # If this is a bridge or a bond, use that name for the interface. + if ($bridge_name) + { + $interface_name = $bridge_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface_name => $interface_name }}); + } + if ($bond_name) + { + $interface_name = $bond_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { interface_name => $interface_name }}); + } + $anvil->data->{network}{$host}{interface}{$interface_name}{network_interface_uuid} = $results->[0]->[0]; $anvil->data->{network}{$host}{interface}{$interface_name}{mac_address} = $interface_mac; $anvil->data->{network}{$host}{interface}{$interface_name}{ip} = $ip_address_address;