* Got the code in scan-server to the point where it _should_ now gracefully and automatically detect changes to a server's definition originatin from the database (via Striker), directly editing the on-disk definition file, or editing via libvirt tools (like virt-manager). Still needs to be tested though.

* Updated Server->migrate_virsh() to set 'servers' -> 'server_state' to 'migrating' and clear it again once the migation completes. Also added support for cold (frozen) versus live migrations.
* Updated Cluster->parse_cib() to check if a server with the server_state set to 'migrating' isn't actually migrating anymore and, if not, to clear that state. This is needed as scan-server will blindly ignore/skip any migrating server, and if a migration call is interrupted, the state could get stuck.
* Updated the 'servers' database table (and associated Database methods) to add columns for;
** server_ram_in_use      - tracking RAM used by a running server
** server_configured_ram  - RAM allocated to a running server (used with the above to alert a user and track _currently_ available RAM)
** server_updated_by_user - To be set by Striker tools to indicate when the user made a change that needs to push out to nodes / running server.
** server_boot_time       - Tracks the unixtime when the server booted (to track uptime even if the server migrates across nodes).
* Created Get->anvil_name_from_uuid() to easily convert an Anvil! UUID into a name. Also created ->host_uuid_from_name() to translate a host name into a host UUID.
* Created Server->get_runtime() that translates a server name into a process ID and then uses that to determine how long (in seconds) it has been running. This is used when a server transitions from 'shut off' to 'running' to determine exactly when the server booted (current time - runtime).
* Renamed all 'Server->parse_definition' calls that used 'from_memory' to 'from_virsh' to clarify the data source.
* Made scan-hardware smarter about RAM change alerts.
* Updated scancore to load agent strings on startup so that processing pending alerts works properly.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 4 years ago
parent 1a1fa7ce88
commit 46f1a05789
  1. 53
      Anvil/Tools/Cluster.pm
  2. 106
      Anvil/Tools/Database.pm
  3. 122
      Anvil/Tools/Get.pm
  4. 5
      Anvil/Tools/Remote.pm
  5. 216
      Anvil/Tools/Server.pm
  6. 8
      Anvil/Tools/System.pm
  7. 19
      notes
  8. 19
      ocf/alteeve/server
  9. 3
      scancore-agents/scan-cluster/scan-cluster
  10. 90
      scancore-agents/scan-hardware/scan-hardware
  11. 30
      scancore-agents/scan-hardware/scan-hardware.xml
  12. 808
      scancore-agents/scan-server/scan-server
  13. 61
      scancore-agents/scan-server/scan-server.xml
  14. 20
      share/anvil.sql
  15. 1
      share/words.xml
  16. 51
      tools/scancore
  17. 16
      tools/test.pl

@ -405,7 +405,7 @@ sub get_peers
if ($host_uuid eq $anvil_node1_host_uuid)
{
# Found our Anvil!, and we're node 1.
$found = 1;
$found = 1;
$anvil->data->{sys}{anvil}{i_am} = "node1";
$anvil->data->{sys}{anvil}{peer_is} = "node2";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
@ -603,8 +603,7 @@ sub migrate_server
return('!!error!!');
}
# TODO: Record that the server is migrating
### NOTE: A server's state is set in Server->migrate_virsh(), so we don't need to do it here.
# change the constraint to trigger the move.
if ($node)
{
@ -1199,7 +1198,55 @@ sub parse_cib
# Migrating
# Stopping
$status = "running";
# If the role is NOT 'migating', check to see if it's marked as such in the database.
if ($role ne "migrating")
{
$anvil->Database->get_servers({debug => $debug});
my $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug});
my $server_uuid = "";
foreach my $this_server_uuid (keys %{$anvil->data->{servers}{server_uuid}})
{
if (($server eq $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name}) &&
($anvil_uuid eq $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid}))
{
# Found it.
$server_uuid = $this_server_uuid;
if ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_state} eq "migrating")
{
# We need to clean up a stale migration state. It may
# not actually be 'running', but if not, scan-server
# will clean it up. So long as the state is
# 'migrating', scan-server won't touch it.
$anvil->Database->insert_or_update_servers({
debug => $debug,
server_uuid => $server_uuid,
server_name => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name},
server_anvil_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid},
server_clean_stop => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop},
server_start_after_server_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid},
server_start_delay => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_delay},
server_host_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid},
server_state => "running",
server_live_migration => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration},
server_pre_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_file_uuid},
server_pre_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments},
server_post_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid},
server_post_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments},
server_ram_in_use => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use},
server_configured_ram => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram},
server_updated_by_user => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user},
server_boot_time => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time},
});
}
last;
}
}
}
}
$anvil->data->{cib}{parsed}{data}{server}{$server}{status} = $status;
$anvil->data->{cib}{parsed}{data}{server}{$server}{host_name} = $host_name;
$anvil->data->{cib}{parsed}{data}{server}{$server}{host_id} = $host_id;

@ -3568,6 +3568,10 @@ This loads all known servers from the database.
servers::server_uuid::<server_uuid>::server_pre_migration_arguments
servers::server_uuid::<server_uuid>::server_post_migration_file_uuid
servers::server_uuid::<server_uuid>::server_post_migration_arguments
servers::server_uuid::<server_uuid>::server_ram_in_use
servers::server_uuid::<server_uuid>::server_configured_ram
servers::server_uuid::<server_uuid>::server_updated_by_user
servers::server_uuid::<server_uuid>::server_boot_time
This method takes no parameters.
@ -3600,6 +3604,10 @@ SELECT
server_pre_migration_arguments,
server_post_migration_file_uuid,
server_post_migration_arguments,
server_ram_in_use,
server_configured_ram,
server_updated_by_user,
server_boot_time
FROM
servers
;";
@ -3626,6 +3634,10 @@ FROM
my $server_pre_migration_arguments = $row->[10];
my $server_post_migration_file_uuid = $row->[11];
my $server_post_migration_arguments = $row->[12];
my $server_ram_in_use = $row->[13];
my $server_configured_ram = $row->[14];
my $server_updated_by_user = $row->[15];
my $server_boot_time = $row->[16];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
server_uuid => $server_uuid,
server_name => $server_name,
@ -3640,11 +3652,16 @@ FROM
server_pre_migration_arguments => $server_pre_migration_arguments,
server_post_migration_file_uuid => $server_post_migration_file_uuid,
server_post_migration_arguments => $server_post_migration_arguments,
server_ram_in_use => $server_ram_in_use,
server_configured_ram => $server_configured_ram,
server_updated_by_user => $server_updated_by_user,
server_boot_time => $server_boot_time,
}});
# Record the data in the hash, too.
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_name} = $server_name;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid} = $server_anvil_uuid;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop} = $server_clean_stop;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid} = $server_start_after_server_uuid;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_delay} = $server_start_delay;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid} = $server_host_uuid;
@ -3654,6 +3671,10 @@ FROM
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments} = $server_pre_migration_arguments;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid} = $server_post_migration_file_uuid;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments} = $server_post_migration_arguments;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use} = $server_ram_in_use;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram} = $server_configured_ram;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user} = $server_updated_by_user;
$anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time} = $server_boot_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"servers::server_uuid::${server_uuid}::server_anvil_uuid" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid},
"servers::server_uuid::${server_uuid}::server_clean_stop" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop},
@ -3666,6 +3687,10 @@ FROM
"servers::server_uuid::${server_uuid}::server_pre_migration_arguments" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments},
"servers::server_uuid::${server_uuid}::server_post_migration_file_uuid" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid},
"servers::server_uuid::${server_uuid}::server_post_migration_arguments" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments},
"servers::server_uuid::${server_uuid}::server_ram_in_use" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use},
"servers::server_uuid::${server_uuid}::server_configured_ram" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram},
"servers::server_uuid::${server_uuid}::server_updated_by_user" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user},
"servers::server_uuid::${server_uuid}::server_boot_time" => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time},
}});
}
@ -9244,16 +9269,17 @@ B<< Note >>: If the server is migating, this should be the old host until after
=head3 server_state (required)
This is the current status of the server. Valid values are;
This is the current status of the server. The values come from C<< virsh >> and are:
* starting
* running
* stopping
* off
* migrating
* DELETED
B<< Note >>: The C<< DELETED >> state is special, in that it marks the server as no longer exists.
* running - The domain is currently running on a CPU
* idle - The domain is idle, and not running or runnable. This can be caused because the domain is waiting on IO (a traditional wait state) or has gone to sleep because there was nothing else for it to do.
* paused - The domain has been paused, usually occurring through the administrator running virsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor.
* in shutdown - The domain is in the process of shutting down, i.e. the guest operating system has been notified and should be in the process of stopping its operations gracefully.
* shut off - The domain is not running. Usually this indicates the domain has been shut down completely, or has not been started.
* crashed - The domain has crashed, which is always a violent ending. Usually this state can only occur if the domain has been configured not to restart on crash.
* pmsuspended - The domain has been suspended by guest power management, e.g. entered into s3 state.
* migrating - B<< Note >>: This is special, in that it's set by the Anvil! while a server is migrating between hosts.
* DELETED - B<< Note >>: This is special, in that it marks the server as no longer exists and comes from the Anvil!, not C<< virsh >>.
=head3 server_live_migration (optional, default '1')
@ -9261,7 +9287,7 @@ Normally, when a server migrates the server keeps running, with changes to memor
In some cases, with servers that have a lot of RAM or very quickly change the memory contents, a migation could take a very long time to complete, if it ever does at all.
For cases where a server can't be live migrated, set this to C<< 0 >>. When set to C<< 0 >>, the server is frozen before the RAM copy begins, and thawed on the new host when the migration is complete. In this way, the migration can be completed over a relatively short time.
For cases where a server can't be live migrated, set this to C<< 0 >>. When set to C<< 0 >>, the server is frozen before the RAM copy begins, and thawed on the new host when the migration is complete. In this way, the migration can be completed over a relatively short time. The tradeoff is that connections to the server could time out.
B<< Note >>: Depending on the BCN network speed and the amount of RAM to copy, the server could be in a frozen state long enough for client connections to timeout. The server itself should handle the freeze fine in most modern systems.
@ -9281,6 +9307,28 @@ This is set to the C<< files >> -> C<< file_uuid >> of a script to run B<< AFTER
These are arguments to pass to the post-migration script above.
=head3 server_ram_in_use (optional, default '0')
This column, along with C<< server_configured_ram >>, is used to handle when the amount of RAM (in bytes) allocated to a server in the on-disk definition differs from the amount of RAM currently used by a running server. This can occur when a user has changed the allocated RAM but the server has not yet been poewr cycled to pick up the change.
B<< Note >>: If this is set to C<< 0 >>, it doesn't mean that the server has no RAM!
The only time this and C<< server_configured_ram >> matters is when both are set to non-zero values and differ. Otherwise, these columns are ignored and the RAM is parsed from the XML definition data stored in the associated C<< server_definitions >> -> C<< server_definition_xml >>.
=head3 server_configured_ram (optional, default '0')
This is used to store the amount of RAM (in bytes) allocated to a server as stored in the on-disk XML file.
See C<< server_ram_in_use >> for more information.
=head3 server_updated_by_user (optional, default 0)
This is set to a unix timestamp when the user last updated the definition. When set, scan-server will check this value against the age of the definition file on disk. If this is newer and the running definition is different from the database definition, the database definition will be used to update the on-disk definition.
=head3 server_boot_time (optional, default 0)
This is the unix time (since epoch) when the server booted. It is calculated by checking the C<< ps -p <pid> -o etimes= >> when a server is seen to be running when it had be last seen as off. If a server that had been running is seen to be off, this is set back to 0.
=cut
sub insert_or_update_servers
{
@ -9307,6 +9355,10 @@ sub insert_or_update_servers
my $server_pre_migration_arguments = defined $parameter->{server_pre_migration_arguments} ? $parameter->{server_pre_migration_arguments} : "";
my $server_post_migration_file_uuid = defined $parameter->{server_post_migration_file_uuid} ? $parameter->{server_post_migration_file_uuid} : "";
my $server_post_migration_arguments = defined $parameter->{server_post_migration_arguments} ? $parameter->{server_post_migration_arguments} : "";
my $server_ram_in_use = defined $parameter->{server_ram_in_use} ? $parameter->{server_ram_in_use} : 0;
my $server_configured_ram = defined $parameter->{server_configured_ram} ? $parameter->{server_configured_ram} : 0;
my $server_updated_by_user = defined $parameter->{server_updated_by_user} ? $parameter->{server_updated_by_user} : 0;
my $server_boot_time = defined $parameter->{server_boot_time} ? $parameter->{server_boot_time} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
'delete' => $delete,
uuid => $uuid,
@ -9325,6 +9377,10 @@ sub insert_or_update_servers
server_pre_migration_arguments => $server_pre_migration_arguments,
server_post_migration_file_uuid => $server_post_migration_file_uuid,
server_post_migration_arguments => $server_post_migration_arguments,
server_ram_in_use => $server_ram_in_use,
server_configured_ram => $server_configured_ram,
server_updated_by_user => $server_updated_by_user,
server_boot_time => $server_boot_time,
}});
if (not $server_uuid)
@ -9429,6 +9485,10 @@ INSERT INTO
server_pre_migration_arguments,
server_post_migration_file_uuid,
server_post_migration_arguments,
server_ram_in_use,
server_configured_ram,
server_updated_by_user,
server_boot_time,
modified_date
) VALUES (
".$anvil->Database->quote($server_uuid).",
@ -9444,6 +9504,10 @@ INSERT INTO
".$anvil->Database->quote($server_pre_migration_arguments).",
".$anvil->Database->quote($server_post_migration_file_uuid).",
".$anvil->Database->quote($server_post_migration_arguments).",
".$anvil->Database->quote($server_ram_in_use).",
".$anvil->Database->quote($server_configured_ram).",
".$anvil->Database->quote($server_updated_by_user).",
".$anvil->Database->quote($server_boot_time).",
".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
);
";
@ -9467,6 +9531,10 @@ SELECT
server_pre_migration_arguments,
server_post_migration_file_uuid,
server_post_migration_arguments,
server_ram_in_use,
server_configured_ram,
server_updated_by_user,
server_boot_time
FROM
servers
WHERE
@ -9500,6 +9568,10 @@ WHERE
my $old_server_pre_migration_arguments = $row->[9];
my $old_server_post_migration_file_uuid = $row->[10];
my $old_server_post_migration_arguments = $row->[11];
my $old_server_ram_in_use = $row->[12];
my $old_server_configured_ram = $row->[13];
my $old_server_updated_by_user = $row->[14];
my $old_server_boot_time = $row->[15];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
old_server_name => $old_server_name,
old_server_anvil_uuid => $old_server_anvil_uuid,
@ -9513,6 +9585,10 @@ WHERE
old_server_pre_migration_arguments => $old_server_pre_migration_arguments,
old_server_post_migration_file_uuid => $old_server_post_migration_file_uuid,
old_server_post_migration_arguments => $old_server_post_migration_arguments,
old_server_ram_in_use => $old_server_ram_in_use,
old_server_configured_ram => $old_server_configured_ram,
old_server_updated_by_user => $old_server_updated_by_user,
old_server_boot_time => $old_server_boot_time,
}});
# Anything change?
@ -9527,7 +9603,11 @@ WHERE
($old_server_pre_migration_file_uuid ne $server_pre_migration_file_uuid) or
($old_server_pre_migration_arguments ne $server_pre_migration_arguments) or
($old_server_post_migration_file_uuid ne $server_post_migration_file_uuid) or
($old_server_post_migration_arguments ne $server_post_migration_arguments))
($old_server_post_migration_arguments ne $server_post_migration_arguments) or
($old_server_ram_in_use ne $server_ram_in_use) or
($old_server_configured_ram ne $server_configured_ram) or
($old_server_updated_by_user ne $server_updated_by_user) or
($old_server_boot_time ne $server_boot_time))
{
# Something changed, save.
my $query = "
@ -9546,6 +9626,10 @@ SET
server_pre_migration_arguments = ".$anvil->Database->quote($server_pre_migration_arguments).",
server_post_migration_file_uuid = ".$anvil->Database->quote($server_post_migration_file_uuid).",
server_post_migration_arguments = ".$anvil->Database->quote($server_post_migration_arguments).",
server_ram_in_use = ".$anvil->Database->quote($server_ram_in_use).",
server_configured_ram = ".$anvil->Database->quote($server_configured_ram).",
server_updated_by_user = ".$anvil->Database->quote($server_updated_by_user).",
server_boot_time = ".$anvil->Database->quote($server_boot_time).",
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
WHERE
server_uuid = ".$anvil->Database->quote($server_uuid)."

@ -16,6 +16,7 @@ our $VERSION = "3.0.0";
my $THIS_FILE = "Get.pm";
### Methods;
# anvil_name_from_uuid
# anvil_version
# bridges
# cgi
@ -24,6 +25,7 @@ my $THIS_FILE = "Get.pm";
# free_memory
# host_name
# host_name_from_uuid
# host_uuid_from_name
# host_type
# host_uuid
# md5sum
@ -100,6 +102,57 @@ sub parent
# Public methods #
#############################################################################################################
=head2 anvil_name_from_uuid
This takes a Anvil! UUID and returns the Anvil! name (as recorded in the C<< anvils >> table). If the entry is not found, an empty string is returned.
Parameters;
=head3 anvil_uuid (required)
This is the C<< anvils >> -> C<< anvil_uuid >> to translate into the Anvil! name.
=cut
sub anvil_name_from_uuid
{
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 => "Get->anvil_name_from_uuid()" }});
my $anvil_name = "";
my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }});
my $query = "
SELECT
anvil_name
FROM
anvils
WHERE
anvil_uuid = ".$anvil->Database->quote($anvil_uuid).";
";
$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,
}});
if ($count == 1)
{
# Found it
$anvil_name = defined $results->[0]->[0] ? $results->[0]->[0] : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_name => $anvil_name }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_name => $anvil_name }});
return($anvil_name);
}
=head2 anvil_version
This reads to C<< VERSION >> file of a local or remote machine. If the version file isn't found, C<< 0 >> is returned.
@ -828,6 +881,75 @@ WHERE
return($host_name);
}
=head2 host_uuid_from_name
This takes a host name and looks for a UUID from the C<< hosts >> table). If the entry is not found, an empty string is returned.
Parameters;
=head3 host_name (required)
This is the host name to translate into a C<< host_uuid >>. If an exact match isn't found, the short host name will be used to try to find a match.
=cut
sub host_uuid_from_name
{
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 => "Get->host_uuid_from_name()" }});
my $host_uuid = "";
my $host_name = defined $parameter->{host_name} ? $parameter->{host_name} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_name => $host_name }});
my $short_host_name = $host_name;
$short_host_name =~ s/\..*$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { short_host_name => $short_host_name }});
# We use Database->get_hosts(), first looping for an exact match. If that fails, we'll loop again,
# reducing all host names to short version.
$anvil->Database->get_hosts({debug => 2});
if (exists $anvil->data->{sys}{hosts}{by_name}{$host_name})
{
$host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name};
}
else
{
foreach my $this_host_uuid (keys %{$anvil->data->{hosts}{host_uuid}})
{
my $this_host_name = $anvil->data->{hosts}{host_uuid}{$this_host_uuid}{host_name}
my $this_short_host_name = $this_host_name;
$this_short_host_name =~ s/\..*$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
this_host_name => $this_host_name,
this_short_host_name => $this_short_host_name,
}});
if ($host_name eq $this_host_name)
{
# Found it.
$host_uuid = $this_host_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }});
last;
}
elsif ($short_host_name eq $this_short_host_name)
{
# This is probably it, but we'll keep looping to be sure.
$host_uuid = $this_host_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }});
}
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }});
return($host_uuid);
}
=head2 free_memory
This returns, in bytes, host much free memory is available on the local system.

@ -720,7 +720,10 @@ sub call
This attempts to log into the target to verify that the target is up and reachable. It returns C<< 1 >> on access, C<< 0 >> otherwise.
my $access = $anvil->Remote->test_access({});
my $access = $anvil->Remote->test_access({
target => "remote_host",
password => "secret",
});
Parameters;

@ -14,6 +14,7 @@ my $THIS_FILE = "Server.pm";
### Methods;
# boot_virsh
# find
# get_runtime
# get_status
# map_network
# parse_definition
@ -291,9 +292,72 @@ sub find
return(0);
}
=head2 get_runtime
This returns the number of seconds that a (virtual) server has been running on this host.
If the server is not found to be running locally, C<< 0 >> is returned. Otherwise, the number of seconds is returned.
B<< Note >>: This is NOT the overall runtime! If the server migrated, it will return the number of seconds that the server has been on this host, which could vary dramatically from the guest's actual runtime!
Parameters;
=head3 server (required)
This is the name of the server whose runtime we are checking.
=cut
sub get_runtime
{
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 => "Server->get_status()" }});
my $runtime = 0;
my $server = defined $parameter->{server} ? $parameter->{server} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
server => $server,
}});
# To find the runtime, we first need to find the PID.
my $server_pid = 0;
my $shell_call = $anvil->data->{path}{exe}{ps}." aux";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
foreach my $line (split/\n/, $output)
{
$line = $anvil->Words->clean_spaces({ string => $line });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
if ($line =~ /^qemu\s+(\d+)\s.*?guest=\Q$server\E,/)
{
$server_pid = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { server_pid => $server_pid }});
last;
}
}
if ($server_pid)
{
my $shell_call = $anvil->data->{path}{exe}{ps}." -p ".$server_pid." -o etimes=";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
foreach my $line (split/\n/, $output)
{
$runtime = $anvil->Words->clean_spaces({ string => $line });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { runtime => $runtime }});
}
}
return($runtime);
}
=head2 get_status
This reads in a server's XML definition file from disk, if available, and from memory, if the server is running. The XML is analyzed and data is stored under C<< server::<target>::<server_name>::from_disk::x >> for data from the on-disk XML and C<< server::<target>>::<server_name>::from_memory::x >>.
This reads in a server's XML definition file from disk, if available, and from memory, if the server is running. The XML is analyzed and data is stored under C<< server::<target>::<server_name>::from_disk::x >> for data from the on-disk XML and C<< server::<target>>::<server_name>::from_virsh::x >>.
Any pre-existing data on the server is flushed before the new information is processed.
@ -353,7 +417,7 @@ sub get_status
{
delete $anvil->data->{server}{$host}{$server};
}
$anvil->data->{server}{$host}{$server}{from_memory}{host} = "";
$anvil->data->{server}{$host}{$server}{from_virsh}{host} = "";
# We're going to map DRBD devices to resources, so we need to collect that data now.
$anvil->DRBD->get_devices({
@ -365,22 +429,22 @@ sub get_status
});
# Is this a local call or a remote call?
my $shell_call = $anvil->data->{path}{exe}{virsh}." dumpxml ".$server;
my $shell_call = $anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server;
my $this_host = $anvil->Get->short_host_name;
if ($anvil->Network->is_local({host => $target}))
{
# Local.
($anvil->data->{server}{$host}{$server}{from_memory}{xml}, $anvil->data->{server}{$host}{$server}{from_memory}{return_code}) = $anvil->System->call({shell_call => $shell_call});
($anvil->data->{server}{$host}{$server}{from_virsh}{xml}, $anvil->data->{server}{$host}{$server}{from_virsh}{return_code}) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"server::${host}::${server}::from_memory::xml" => $anvil->data->{server}{$host}{$server}{from_memory}{xml},
"server::${host}::${server}::from_memory::return_code" => $anvil->data->{server}{$host}{$server}{from_memory}{return_code},
"server::${host}::${server}::from_virsh::xml" => $anvil->data->{server}{$host}{$server}{from_virsh}{xml},
"server::${host}::${server}::from_virsh::return_code" => $anvil->data->{server}{$host}{$server}{from_virsh}{return_code},
}});
}
else
{
# Remote call.
$this_host = $target;
($anvil->data->{server}{$host}{$server}{from_memory}{xml}, my $error, $anvil->data->{server}{$host}{$server}{from_memory}{return_code}) = $anvil->Remote->call({
($anvil->data->{server}{$host}{$server}{from_virsh}{xml}, my $error, $anvil->data->{server}{$host}{$server}{from_virsh}{return_code}) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
@ -390,25 +454,25 @@ sub get_status
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error => $error,
"server::${host}::${server}::from_memory::xml" => $anvil->data->{server}{$host}{$server}{from_memory}{xml},
"server::${host}::${server}::from_memory::return_code" => $anvil->data->{server}{$host}{$server}{from_memory}{return_code},
"server::${host}::${server}::from_virsh::xml" => $anvil->data->{server}{$host}{$server}{from_virsh}{xml},
"server::${host}::${server}::from_virsh::return_code" => $anvil->data->{server}{$host}{$server}{from_virsh}{return_code},
}});
}
# If the return code was non-zero, we can't parse the XML.
if ($anvil->data->{server}{$host}{$server}{from_memory}{return_code})
if ($anvil->data->{server}{$host}{$server}{from_virsh}{return_code})
{
$anvil->data->{server}{$host}{$server}{from_memory}{xml} = "";
$anvil->data->{server}{$host}{$server}{from_virsh}{xml} = "";
}
else
{
$anvil->data->{server}{$host}{$server}{from_memory}{host} = $this_host;
$anvil->data->{server}{$host}{$server}{from_virsh}{host} = $this_host;
$anvil->Server->parse_definition({
debug => $debug,
host => $this_host,
server => $server,
source => "from_memory",
definition => $anvil->data->{server}{$host}{$server}{from_memory}{xml},
source => "from_virsh",
definition => $anvil->data->{server}{$host}{$server}{from_virsh}{xml},
});
}
@ -548,10 +612,10 @@ sub map_network
# This is used in the hash reference when storing the data.
my $host = $target ? $target : $anvil->Get->short_host_name();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
foreach my $mac (sort {$a cmp $b} keys %{$anvil->data->{server}{$host}{$server}{from_memory}{device}{interface}})
foreach my $mac (sort {$a cmp $b} keys %{$anvil->data->{server}{$host}{$server}{from_virsh}{device}{interface}})
{
my $device = $anvil->data->{server}{$host}{$server}{from_memory}{device}{interface}{$mac}{target};
my $bridge = $anvil->data->{server}{$host}{$server}{from_memory}{device}{interface}{$mac}{bridge};
my $device = $anvil->data->{server}{$host}{$server}{from_virsh}{device}{interface}{$mac}{target};
my $bridge = $anvil->data->{server}{$host}{$server}{from_virsh}{device}{interface}{$mac}{bridge};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:device' => $device,
's2:mac' => $mac,
@ -683,10 +747,54 @@ sub migrate_virsh
});
}
# Mark this server as being in a migration state.
$anvil->Database->get_servers({debug => 2});
my $server_uuid = "";
my $old_server_state = "";
foreach my $this_server_uuid (keys %{$anvil->data->{servers}{server_uuid}})
{
if ($server eq $anvil->data->{servers}{server_uuid}{$this_server_uuid}{server_name})
{
$server_uuid = $this_server_uuid;
last;
}
}
if ($server_uuid)
{
if ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_state} ne "migrating")
{
$old_server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
$anvil->Database->insert_or_update_servers({
debug => $debug,
server_uuid => $server_uuid,
server_name => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name},
server_anvil_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid},
server_clean_stop => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop},
server_start_after_server_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid},
server_start_delay => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_delay},
server_host_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid},
server_state => "migrating",
server_live_migration => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration},
server_pre_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_file_uuid},
server_pre_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments},
server_post_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid},
server_post_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments},
server_ram_in_use => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use},
server_configured_ram => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram},
server_updated_by_user => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user},
server_boot_time => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time},
});
}
}
# The virsh command switches host names to IPs and needs to have both the source and target IPs in
# the known_hosts file to work.
my $target_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target_ip => $target_ip }});
my $live_migrate = not $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration} ? "" : "--live";
my $target_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
target_ip => $target_ip,
live_migrate => $live_migrate,
}});
foreach my $host ($target, $target_ip)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host => $host }});
@ -695,7 +803,7 @@ sub migrate_virsh
target => $host,
});
}
my $migration_command = $anvil->data->{path}{exe}{virsh}." migrate --undefinesource --tunnelled --p2p --live ".$server." qemu+ssh://".$target."/system";
my $migration_command = $anvil->data->{path}{exe}{virsh}." migrate --undefinesource --tunnelled --p2p ".$live_migrate." ".$server." qemu+ssh://".$target."/system";
if ($source)
{
my $source_ip = $anvil->Convert->host_name_to_ip({debug => $debug, host_name => $source});
@ -709,7 +817,7 @@ sub migrate_virsh
});
}
$migration_command = $anvil->data->{path}{exe}{virsh}." -c qemu+ssh://root\@".$source."/system migrate --undefinesource --tunnelled --p2p --live ".$server." qemu+ssh://".$target."/system";
$migration_command = $anvil->data->{path}{exe}{virsh}." -c qemu+ssh://root\@".$source."/system migrate --undefinesource --tunnelled --p2p ".$live_migrate." ".$server." qemu+ssh://".$target."/system";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { migration_command => $migration_command }});
@ -719,6 +827,9 @@ sub migrate_virsh
output => $output,
return_code => $return_code,
}});
# Before we update, re-scan servers as some time may have passed.
$anvil->Database->get_servers({debug => 2});
if ($return_code)
{
# Something went wrong.
@ -728,6 +839,31 @@ sub migrate_virsh
return_code => $return_code,
output => $output,
}});
# Revert the migrating state.
if ($server_uuid)
{
$anvil->Database->insert_or_update_servers({
debug => $debug,
server_uuid => $server_uuid,
server_name => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name},
server_anvil_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid},
server_clean_stop => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop},
server_start_after_server_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid},
server_start_delay => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_delay},
server_host_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid},
server_state => $old_server_state,
server_live_migration => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration},
server_pre_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_file_uuid},
server_pre_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments},
server_post_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid},
server_post_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments},
server_ram_in_use => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use},
server_configured_ram => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram},
server_updated_by_user => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user},
server_boot_time => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time},
});
}
}
else
{
@ -735,6 +871,40 @@ sub migrate_virsh
$success = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { success => $success }});
# Revert the server state and update the server host.
my $server_host_uuid = $anvil->Get->host_uuid_from_name({host_name => $target});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_uuid => $server_host_uuid }});
if (not $server_host_uuid)
{
# We didn't find the target's host_uuid, so use the old one and let scan-server
# handle it.
$server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_host_uuid => $server_host_uuid }});
}
if ($server_uuid)
{
$anvil->Database->insert_or_update_servers({
debug => $debug,
server_uuid => $server_uuid,
server_name => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name},
server_anvil_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid},
server_clean_stop => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_clean_stop},
server_start_after_server_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_after_server_uuid},
server_start_delay => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_start_delay},
server_host_uuid => $server_host_uuid,
server_state => $old_server_state,
server_live_migration => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_live_migration},
server_pre_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_file_uuid},
server_pre_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_pre_migration_arguments},
server_post_migration_file_uuid => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_file_uuid},
server_post_migration_arguments => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_post_migration_arguments},
server_ram_in_use => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_ram_in_use},
server_configured_ram => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_configured_ram},
server_updated_by_user => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user},
server_boot_time => $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time},
});
}
}
# Switch off dual-primary.
@ -770,7 +940,7 @@ This is the source of the XML. This is done to simplify looking for differences
The XML was read from a file on the host's storage.
=head4 C<< from_memory >>
=head4 C<< from_virsh >>
The XML was dumped by C<< virsh >> from memory.
@ -1136,7 +1306,7 @@ sub parse_definition
# - Model: [pcie-root-port]
# - Target chassis: [2], port: [0x11]
# - Bus type: [pci], domain: [0x0000], bus: [0x00], slot: [0x02], function: [0x1]
# server::test_server::from_memory::address::virtio-serial::controller::0::bus::0::port::2: [channel - spicevmc]
# server::test_server::from_virsh::address::virtio-serial::controller::0::bus::0::port::2: [channel - spicevmc]
# $anvil->data->{server}{$target}{$server}{$source}{address}{$address_type}{controller}{$type}{bus}{$address_bus}{bus}{$address_bus}{slot}{$address_slot}{function}{$address_function}{domain} = $address_domain;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
# "server::${target}::${server}::${source}::address::${address_type}::controller::${type}::bus::${address_bus}::slot::${address_slot}::function::${address_function}::domain" => $anvil->data->{server}{$target}{$server}{$source}{address}{$address_type}{controller}{$type}{bus}{$address_bus}{bus}{$address_bus}{slot}{$address_slot}{function}{$address_function}{domain},

@ -3217,15 +3217,15 @@ sub manage_firewall
=head2 pids
This parses 'ps aux' and stores the information about running programs in C<< pids::<pid_number>::<data> >>.
This parses C<< ps aux >> and stores the information about running programs in C<< pids::<pid_number>::<data> >>.
Optionally, if the C<< program_name >> parameter is set, an array of PIDs for that program will be returned.
Parameters;
=head3 ignore_me (optional)
=head3 ignore_me (optional, default '0')
If set to '1', the PID of this program is ignored.
If set to C<< 1 >>, the PID of this program is ignored.
=head3 program_name (optional)
@ -3240,7 +3240,7 @@ sub pids
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "System->pids()" }});
my $ignore_me = defined $parameter->{ignore_me} ? $parameter->{ignore_me} : "";
my $ignore_me = defined $parameter->{ignore_me} ? $parameter->{ignore_me} : 0;
my $program_name = defined $parameter->{program_name} ? $parameter->{program_name} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
ignore_me => $ignore_me,

19
notes

@ -271,7 +271,6 @@ DROP FUNCTION history_servers() CASCADE;
DROP TABLE history.servers;
DROP TABLE servers;
-- This stores servers made available to Anvil! systems and DR hosts.
CREATE TABLE servers (
server_uuid uuid not null primary key,
server_name text not null, -- This is the server's name. It can change without re-uploading the server.
@ -280,12 +279,16 @@ CREATE TABLE servers (
server_start_after_server_uuid uuid not null, -- This can be the server_uuid of another server. If set, this server will boot 'server_start_delay' seconds after the referenced server boots. A value of '00000000-0000-0000-0000-000000000000' will tell 'anvil-safe-start' to not boot the server at all. If a server is set not to start, any dependent servers will also stay off.
server_start_delay integer not null default 0, -- See above.
server_host_uuid uuid not null, -- This is the current hosts -> host_uuid for this server. If the server is off, this will be blank.
server_state text not null, -- This is the current state of this server.
server_state text not null, -- This is the current state of this server, as reported by 'virsh list --all' (see: man virsh -> GENERIC COMMANDS -> --list)
server_live_migration boolean not null default TRUE, -- When false, servers will be stopped and then rebooted when a migration is requested. Also, when false, preventative migrations will not happen.
server_pre_migration_file_uuid uuid not null, -- This is set to the files -> file_uuid of a script to run BEFORE migrating a server. If the file isn't found or can't run, the script is ignored.
server_pre_migration_arguments text not null, -- These are arguments to pass to the pre-migration script
server_post_migration_file_uuid uuid not null, -- This is set to the files -> file_uuid of a script to run AFTER migrating a server. If the file isn't found or can't run, the script is ignored.
server_post_migration_arguments text not null, -- These are arguments to pass to the post-migration script
server_ram_in_use numeric not null, -- This is the amount of RAM currently used by the server. If the server is off, then this is the amount of RAM last used when the server was running.
server_configured_ram numeric not null, -- This is the amount of RAM allocated to the server in the on-disk definition file. This should always match the table above, but allows us to track when a user manually updated the allocated RAM in the on-disk definition, but that hasn't yet been picked up by the server
server_updated_by_user numeric not null, -- This is set to a unix timestamp when the user last updated the definition. When set, scan-server will check this value against the age of the definition file on disk. If this is newer and the running definition is different from the database definition, the database definition will be used to update the on-disk definition.
server_boot_time numeric not null, -- This is the unix time (since epoch) when the server booted. It is calculated by checking the 'ps -p <pid> -o etimes=' when a server is seen to be running when it had be last seen as off. If a server that had been running is seen to be off, this is set back to 0.
modified_date timestamp with time zone not null,
FOREIGN KEY(server_anvil_uuid) REFERENCES anvils(anvil_uuid),
@ -311,6 +314,10 @@ CREATE TABLE history.servers (
server_pre_migration_arguments text,
server_post_migration_file_uuid uuid,
server_post_migration_arguments text,
server_ram_in_use numeric,
server_configured_ram numeric,
server_updated_by_user numeric,
server_boot_time numeric,
modified_date timestamp with time zone not null
);
ALTER TABLE history.servers OWNER TO admin;
@ -335,6 +342,10 @@ BEGIN
server_pre_migration_arguments,
server_post_migration_file_uuid,
server_post_migration_arguments,
server_ram_in_use,
server_configured_ram,
server_updated_by_user,
server_boot_time,
modified_date)
VALUES
(history_servers.server_uuid,
@ -349,6 +360,10 @@ BEGIN
history_servers.server_pre_migration_arguments,
history_servers.server_post_migration_file_uuid,
history_servers.server_post_migration_arguments,
history_servers.server_ram_in_use,
history_servers.server_configured_ram,
history_servers.server_updated_by_user,
history_servers.server_boot_time,
history_servers.modified_date);
RETURN NULL;
END;

@ -1113,6 +1113,16 @@ sub server_status
found => $found,
'state' => $state,
}});
=cut
* Server states;
running - The domain is currently running on a CPU
idle - The domain is idle, and not running or runnable. This can be caused because the domain is waiting on IO (a traditional wait state) or has gone to sleep because there was nothing else for it to do.
paused - The domain has been paused, usually occurring through the administrator running virsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor.
in shutdown - The domain is in the process of shutting down, i.e. the guest operating system has been notified and should be in the process of stopping its operations gracefully.
shut off - The domain is not running. Usually this indicates the domain has been shut down completely, or has not been started.
crashed - The domain has crashed, which is always a violent ending. Usually this state can only occur if the domain has been configured not to restart on crash.
pmsuspended - The domain has been suspended by guest power management, e.g. entered into s3 state.
=cut
### TODO: Should we treat 'idle' same as crashed?
if ($state eq "crashed")
@ -1205,8 +1215,7 @@ sub migrate_server
# The actual migration command will involve enabling dual primary, then beginning the migration. The
# virsh call will depend on if we're pushing or pulling. Once the migration completes, regardless of
# success or failure, dual primary will be disabled again.
my $migration_command = "";
my $migrated = 0;
my $migrated = 0;
if ($target)
{
# Can I even connect to the target?
@ -1527,7 +1536,7 @@ sub validate_storage
{
my ($anvil) = @_;
# When checking on a running server, use 'from_memory'.
# When checking on a running server, use 'from_virsh'.
my $server = $anvil->data->{environment}{OCF_RESKEY_name};
my $target = defined $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} ? $anvil->data->{environment}{OCF_RESKEY_CRM_meta_migrate_target} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -1536,9 +1545,9 @@ sub validate_storage
}});
my $local_host = $anvil->Get->short_host_name();
my $xml_source = "from_disk";
if ($anvil->data->{server}{$local_host}{$server}{from_memory}{host})
if ($anvil->data->{server}{$local_host}{$server}{from_virsh}{host})
{
$xml_source = "from_memory";
$xml_source = "from_virsh";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
server => $server,

@ -12,7 +12,6 @@
# 1 = Startup failure (not running as root, no DB, bad file read, etc)
#
# TODO:
# -
#
use strict;
@ -117,7 +116,7 @@ sub collect_data
# Pick out core cluster details.
my $problem = $anvil->Cluster->parse_cib({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { offline => $offline }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
my $cluster_name = $anvil->data->{cib}{parsed}{data}{cluster}{name};
my $stonith_enabled = $anvil->data->{cib}{parsed}{data}{stonith}{enabled};

@ -769,33 +769,78 @@ sub find_changes
}
if ($new_scan_hardware_ram_total ne $old_scan_hardware_ram_total)
{
# RAM (from dmidecode) changed could be a hardware upgrade, or it could be from a
# failed modules. As such, we set this to 'warning'.
$update = 1;
my $say_new_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)";
my $say_old_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)";
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0012,!!new!".$say_new_ram."!!,!!old!".$say_old_ram."!!"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0012", variables => { new => $say_new_ram, old => $say_old_ram}});
# If the RAM (from dmidecode) has increased, it's a safe upgrade. If it's a decrease,
# it's likely a failure.
if ($old_scan_hardware_ram_total > $new_scan_hardware_ram_total)
{
# It dropped, alarm
$update = 1;
my $difference = $old_scan_hardware_ram_total - $new_scan_hardware_ram_total;
my $variables = {
new => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)",
old => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)",
difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)",
};
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0012", message_variables => $variables});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0012", variables => $variables});
}
else
{
# Increased, probably an upgrade
$update = 1;
my $difference = $new_scan_hardware_ram_total - $old_scan_hardware_ram_total;
my $variables = {
new => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)",
old => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)",
difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)",
};
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0029", message_variables => $variables});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0029", variables => $variables});
}
}
if ($new_scan_hardware_memory_total ne $old_scan_hardware_memory_total)
{
# Memory (/proc/meminfo) changed could be a hardware upgrade, or it could be from a
# failed modules. As such, we set this to 'warning'.
$update = 1;
my $say_new_memory = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)";
my $say_old_memory = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)";
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0013,!!new!".$say_new_memory."!!,!!old!".$say_old_memory."!!"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0013", variables => { new => $say_new_memory, old => $say_old_memory}});
# If the difference is less than 1 GiB, or the amount has grown, ignore it.
if ($old_scan_hardware_memory_total > $new_scan_hardware_memory_total)
{
my $difference = $old_scan_hardware_memory_total - $new_scan_hardware_memory_total;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference}) }});
if ($difference > 1073741824)
{
# Memory (/proc/meminfo) changed could be a hardware upgrade, or it
# could be from a failed modules. As such, we set this to 'warning'.
$update = 1;
my $variables = {
new => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)",
old => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)",
difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)",
};
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0013", message_variables => $variables});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0013", variables => $variables});
}
}
}
if ($new_scan_hardware_swap_total ne $old_scan_hardware_swap_total)
{
# Memory (/proc/meminfo) changed could be a hardware upgrade, or it could be from a
# failed modules. As such, we set this to 'warning'.
$update = 1;
my $say_new_swap = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)";
my $say_old_swap = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)";
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0014,!!new!".$say_new_swap."!!,!!old!".$say_old_swap."!!"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0014", variables => { new => $say_new_swap, old => $say_old_swap}});
# If the difference is less than 1 GiB, or the amount has grown, ignore it.
if ($old_scan_hardware_swap_total > $new_scan_hardware_swap_total)
{
my $difference = $old_scan_hardware_swap_total - $new_scan_hardware_swap_total;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference}) }});
if ($difference > 1073741824)
{
# Memory (/proc/meminfo) changed could be a hardware upgrade, or it
# could be from a failed modules. As such, we set this to 'warning'.
$update = 1;
my $variables = {
new => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)",
old => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)",
difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)",
};
$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0014", message_variables => $variables});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0014", variables => $variables});
}
}
}
if ($new_scan_hardware_led_id ne $old_scan_hardware_led_id)
{
@ -959,7 +1004,7 @@ SET
scan_hardware_led_id = ".$anvil->Database->quote($new_scan_hardware_led_id).",
scan_hardware_led_css = ".$anvil->Database->quote($new_scan_hardware_led_css).",
scan_hardware_led_error = ".$anvil->Database->quote($new_scan_hardware_led_error).",
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})."
WHERE
scan_hardware_uuid = ".$anvil->Database->quote($scan_hardware_uuid)."
;";
@ -1351,7 +1396,6 @@ sub read_last_scan
{
my ($anvil) = @_;
# This calls up the entry for this host. There will only be one.
my $query = "
SELECT

@ -64,17 +64,20 @@ The differences are:
- New: [#!variable!new!# thread(s)]
- Old: [#!variable!old!# thread(s)]
</key>
<key name="scan_hardware_alert_0012">The amount of RAM (as reported by dmidecode) on the system has changed. If there was a hardware upgrade, then this is safe to ignore. If it was unexpected, a RAM module may have failed.
- New: [#!variable!new!#]
- Old: [#!variable!old!#]
<key name="scan_hardware_alert_0012">The amount of RAM (as reported by dmidecode) on the system has dropped. If it was unexpected, a RAM module may have failed.
- New: ...... [#!variable!new!#]
- Old: ...... [#!variable!old!#]
- Difference: [#!variable!difference!#]
</key>
<key name="scan_hardware_alert_0013">The amount of memory (as reported by /proc/meminfo) on the system has changed. If there was a hardware upgrade, then this is safe to ignore. If it was unexpected, a RAM module may have failed.
- New: [#!variable!new!#]
- Old: [#!variable!old!#]
<key name="scan_hardware_alert_0013">The amount of memory (as reported by /proc/meminfo) on the system has dropped.
- New: ...... [#!variable!new!#]
- Old: ...... [#!variable!old!#]
- Difference: [#!variable!difference!#]
</key>
<key name="scan_hardware_alert_0014">The amount of memory (as reported by /proc/meminfo) on the system has changed. If there was a hardware upgrade, then this is safe to ignore. If it was unexpected, a RAM module may have failed.
- New: [#!variable!new!#]
- Old: [#!variable!old!#]
<key name="scan_hardware_alert_0014">The amount of swap (as reported by /proc/meminfo) on the system has dropped.
- New: ...... [#!variable!new!#]
- Old: ...... [#!variable!old!#]
- Difference: [#!variable!difference!#]
</key>
<key name="scan_hardware_alert_0015">The ID LED (identification light) state has changed;
- New: [#!variable!new!#]
@ -88,11 +91,11 @@ The differences are:
- New: [#!variable!new!#]
- Old: [#!variable!old!#]
</key>
<key name="scan_hardware_alert_0018">The amount of available memory (as reported by /proc/meminfo) has changed (this is common and expected);
<key name="scan_hardware_alert_0018">The amount of free memory (as reported by /proc/meminfo) has changed (this is common and expected);
- New: [#!variable!new!#]
- Old: [#!variable!old!#]
</key>
<key name="scan_hardware_alert_0019">The amount of available swap space (as reported by /proc/meminfo) has changed (this is common and expected);
<key name="scan_hardware_alert_0019">The amount of free swap space (as reported by /proc/meminfo) has changed (this is common and expected);
- New: [#!variable!new!#]
- Old: [#!variable!old!#]
</key>
@ -146,6 +149,11 @@ If the RAM is being updated, this alert will clear once this node has been upgra
- Peer's RAM: [#!variable!peer_ram!#]
</key>
<key name="scan_hardware_alert_0028">The amount of RAM on both nodes is back to being the same. They both have: [#!variable!ram!#] now.</key>
<key name="scan_hardware_alert_0029">The amount of RAM (as reported by dmidecode) on the system has increased. Likely the system was upgraded.
- New: ...... [#!variable!new!#]
- Old: ...... [#!variable!old!#]
- Difference: [#!variable!difference!#]
</key>
<!-- Log entries -->
<key name="scan_hardware_log_0001">Starting: [#!variable!program!#].</key>

@ -48,8 +48,9 @@ if (($< != 0) && ($> != 0))
$anvil->nice_exit({exit_code => 1});
}
$anvil->data->{scancore}{'scan-server'}{disable} = 0;
$anvil->data->{switches}{force} = 0;
$anvil->data->{scancore}{'scan-server'}{disable} = 0;
$anvil->data->{scancore}{'scan-server'}{'auto-undefine'} = 1;
$anvil->data->{switches}{force} = 0;
$anvil->Storage->read_config();
@ -90,10 +91,10 @@ if ($host_type eq "striker")
$anvil->nice_exit({exit_code => 0});
}
# Read the data.
# This is more than data collection in most agents, as it actually handles the changes on the fly
collect_data($anvil);
# Read last scan
# Now look for changes
$anvil->nice_exit({exit_code => 0});
@ -102,8 +103,8 @@ $anvil->nice_exit({exit_code => 0});
# Functions #
#############################################################################################################
# This reads in all the data we can find on the local system
# This reads in all the data we can find about servers running locally. This is more than data collection in
# most agents, as it actually handles the changes on the fly.
sub collect_data
{
my ($anvil) = @_;
@ -118,241 +119,696 @@ sub collect_data
}
# Load data we know about
$anvil->Database->get_anvils({debug => 2});
$anvil->Database->get_servers({debug => 2});
$anvil->Database->get_server_definitions({debug => 2});
# Find our Anvil! UUID
my $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }});
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list --all", source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
foreach my $line (split/\n/, $output)
{
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
### TODO: We may want to handle crashed / idle servers.
=cut
* Server states;
running - The domain is currently running on a CPU
idle - The domain is idle, and not running or runnable. This can be caused because the domain is waiting on IO (a traditional wait state) or has gone to sleep because there was nothing else for it to do.
paused - The domain has been paused, usually occurring through the administrator running virsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor.
in shutdown - The domain is in the process of shutting down, i.e. the guest operating system has been notified and should be in the process of stopping its operations gracefully.
shut off - The domain is not running. Usually this indicates the domain has been shut down completely, or has not been started.
crashed - The domain has crashed, which is always a violent ending. Usually this state can only occur if the domain has been configured not to restart on crash.
pmsuspended - The domain has been suspended by guest power management, e.g. entered into s3 state.
- Ones we set:
migrating - Set and cleared by Server->migrate_virsh();
DELETED - Marks a server as no longer existing
=cut
if ($line =~ /^\d+ (.*?) (.*)$/)
{
my $server = $1;
my $state = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
server => $server,
'state' => $state,
my $server_name = $1;
my $server_state = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
server_name => $server_name,
server_state => $server_state,
}});
$anvil->data->{'scan-server'}{server}{$server}{'state'} = $state;
$anvil->data->{'scan-server'}{server}{$server}{undefine} = 0;
# Parse out the server UUID.
my $virsh_definition = get_and_parse_virsh_definition($anvil, $server_name);
# Does the XML definition file exist yet?
my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server_name.".xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { xml_file => $xml_file }});
if (not -e $xml_file)
{
# No, generate it. This will also load and parse the file after it's written.
my $on_disk_definition = update_on_disk_definition($anvil, $server_name, $virsh_definition);
}
# We'll compare the memory allocated to the server from the on-disk definition and the memory
# currently used as reported by the in-memory definition and use that to update the
# 'server_ram_in_use' and 'server_configured_ram' columns.
my $server_uuid = $anvil->data->{server}{$target}{$server_name}{'from_virsh'}{info}{uuid};
my $server_ram_in_use = $anvil->data->{server}{$target}{$server_name}{'from_virsh'}{memory};
my $server_configured_ram = $anvil->data->{server}{$target}{$server_name}{'from_disk'}{memory};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"scan-server::server::${server}::state" => $anvil->data->{'scan-server'}{server}{$server}{'state'},
"scan-server::server::${server}::undefine" => $anvil->data->{'scan-server'}{server}{$server}{undefine},
server_uuid => $server_uuid,
server_ram_in_use => $anvil->Convert->add_commas({number => $server_ram_in_use})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $server_ram_in_use}).")",
server_configured_ram => $anvil->Convert->add_commas({number => $server_configured_ram})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $server_configured_ram}).")",
}});
}
# If we saw the server boot (or saw it for the first time at all), this will store
# the boot time.
my $boot_time = 0;
# Is this server in the database already?
if (exists $anvil->data->{servers}{server_uuid}{$server_uuid})
{
# Yes, see if we're looking at a state change
if ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_state} eq "migrating")
{
# Ignore this server, scan_cluster will clear this when the migration is
# complete. We don't touch anything during a migration.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_message_0001", variables => { server => $server_name }});
next;
}
elsif ($anvil->data->{servers}{server_uuid}{$server_uuid}{server_state} eq "shut off")
{
# It booted. Record the start time.
my $runtime = $anvil->Server->get_runtime({server => $server_name});
$boot_time = time - $runtime;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
runtime => $runtime,
server_boot_time => $server_boot_time." (".$anvil->Get->date_and_time({use_time => $boot_time}).")",
}});
}
}
else
{
# It's not in the database yet, add it.
my $runtime = $anvil->Server->get_runtime({server => $server});
$server_boot_time = time - $runtime;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
runtime => $runtime,
server_boot_time => $server_boot_time." (".$anvil->Get->date_and_time({use_time => $boot_time}).")",
}});
my $got_server_uuid = $anvil->Database->insert_or_update_servers({
debug => 2,
server_uuid => $server_uuid,
server_name => $server_name,
server_anvil_uuid => $anvil_uuid,
server_host_uuid => $anvil->Get->host_uuid,
server_state => $server_state,
server_ram_in_use => $server_ram_in_use,
server_configured_ram => $server_configured_ram,
server_boot_time => $server_boot_time,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { got_server_uuid => $got_server_uuid }});
# Store the definition from memory
my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({
debug => 2,
server_definition_xml => $virsh_definition,
server_definition_server_uuid => $server_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }});
# Register an alert.
my $variables = {
server => $server_name,
definition => $virsh_definition,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0006", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0006",
message_variables => $variables,
set_by => $THIS_FILE,
});
# Reload the servers.
$anvil->Database->get_servers({debug => 2});
$anvil->Database->get_server_definitions({debug => 2});
}
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_uuid} = $server_uuid;
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_boot_time} = $boot_time ? $boot_time : $anvil->data->{servers}{server_uuid}{$server_uuid}{server_boot_time};
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_state} = $server_state;
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_anvil_uuid} = $anvil_uuid;
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_host_uuid} = $anvil->Get->host_uuid;
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_ram_in_use} = $server_ram_in_use;
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_configured_ram} = $server_configured_ram;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"scan-server::server_name::${server_name}::server_uuid" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_uuid},
"scan-server::server_name::${server_name}::server_boot_time" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_boot_time},
"scan-server::server_name::${server_name}::server_state" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_state},
"scan-server::server_name::${server_name}::server_anvil_uuid" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_anvil_uuid},
"scan-server::server_name::${server_name}::server_host_uuid" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_host_uuid},
"scan-server::server_name::${server_name}::server_ram_in_use" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_ram_in_use},
"scan-server::server_name::${server_name}::server_configured_ram" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_configured_ram},
}});
}
if ($line =~ /- (.*?) shut off/)
{
# The server is shut off. If this is a node, it shouldn't be defined.
my $server = $1;
my $server_name = $1;
my $host_type = $anvil->Get->host_type;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
if ($host_type eq "node")
{
$anvil->data->{'scan-server'}{server}{$server}{undefine} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"scan-server::server::${server}::undefine" => $anvil->data->{'scan-server'}{server}{$server}{undefine},
}});
# The definition was likely changed by the user while it was running. We
# should have already picked up the changes, but just to be safe, check for
# differences.
my ($virsh_definition, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server_name, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $virsh_definition, return_code => $return_code }});
# The definition will have certainly changed, so update it as well.
update_definitions_from_virsh($anvil, $server_name, $server_uuid, $virsh_definition);
# Now undefine the server
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." undefine ".$server_name, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
}
# Set the boot time back to zero.
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_boot_time} = 0;
}
}
# Now loop through the found servers and read their XML definition.
foreach my $server (sort {$a cmp $b} keys %{$anvil->data->{'scan-server'}{server}})
# Now loop through the found servers and see if any definitions have changed and need to be
# propogated out.
foreach my $server_name (sort {$a cmp $b} keys %{$anvil->data->{'scan-server'}{server_name}})
{
my ($definition, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." dumpxml ".$server, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $definition, return_code => $definition }});
my $target = $anvil->Get->short_host_name;
my $source = "from_memory";
my $problem = $anvil->Server->parse_definition({
debug => 2,
server => $server,
source => $source,
definition => $definition,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ($problem)
{
# Wat?
next;
}
my $server_uuid = $anvil->data->{server}{$target}{$server}{$source}{info}{uuid};
my $server_name = $anvil->data->{server}{$target}{$server}{$source}{info}{name};
# Set the XML file path and read the UUID
my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server_name.".xml";
my $server_uuid = $anvil->data->{'scan-server'}{server_name}{$server_name}{server_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
server_uuid => $server_uuid,
server_name => $server_name,
server_uuid => $server_uuid,
xml_file => $xml_file,
}});
# See if this server is in the database.
if (not exists $anvil->data->{servers}{server_uuid}{$server_uuid})
{
# Add it. I'll need my anvil_uuid first.
my $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }});
my $got_server_uuid = $anvil->Database->insert_or_update_servers({
debug => 2,
server_uuid => $server_uuid,
server_name => $server_name,
server_anvil_uuid => $anvil_uuid,
server_clean_stop => "",
server_start_after_server_uuid => "",
server_start_delay => 0,
server_host_uuid => $anvil->Get->host_uuid,
server_state => $anvil->data->{'scan-server'}{server}{$server}{'state'},
server_live_migration => 1,
server_pre_migration_file_uuid => "",
server_pre_migration_arguments => "",
server_post_migration_file_uuid => "",
server_post_migration_arguments => "",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { got_server_uuid => $got_server_uuid }});
# Get the definitions
my $virsh_definition = get_and_parse_virsh_definition($anvil, $server_name);
my $on_disk_definition = get_and_parse_disk_definition($anvil, $server_name);
my $database_definition = get_and_parse_database_definition($anvil, $server_name, $server_uuid);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { on_disk_definition => $on_disk_definition }});
# If the 'server_updated_by_user' value is newer than the file age, and there is a difference in the definition, update the file.
my $current_time = time;
my $user_update_time = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_updated_by_user};
my $user_update_age = $current_time - $user_update_time;
my $database_modified_time = $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{unix_modified_time};
my $database_age = $current_time - $database_modified_time;
my $file_modified_time = $anvil->data->{file_stat}{$xml_file}{modified_time};
my $file_age = $current_time - $file_modified_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:current_time' => $current_time,
's2:user_update_time' => $user_update_time,
's3:user_update_age' => $user_update_age,
's4:database_modified_time' => $database_modified_time,
's5:database_age' => $database_age,
's6:file_modified_time' => $file_modified_time,
's7:file_age' => $file_age,
}});
if ((not $got_server_uuid) or ($got_server_uuid eq "!!error!!"))
# If the user edited the server via Striker or directly via the on-disk definition, update
# the other and then load the changes into virsh.
if ($database_definition ne $on_disk_definition)
{
# Either the change came from Striker (check $user_update_age) or the change happened
# on disk (check $file_modified_time). Which ever is more recent wins.
if ($user_update_age < $file_modified_time)
{
# What?
next;
# Get the diffs before we update
my $disk_difference = diff \$database_definition, \$on_disk_definition, { STYLE => 'Unified' };
my $virsh_difference = diff \$database_definition, \$virsh_definition, { STYLE => 'Unified' };
# The user updated the definition from Striker, so update the file and then push the new file into virsh.
$on_disk_definition = update_on_disk_definition($anvil, $server_name, $database_definition);
$file_modified_time = $anvil->data->{file_stat}{$xml_file}{modified_time};
$file_age = $current_time - $file_modified_time;
my $variables = {
server => $server_name,
definition_file => $xml_file,
disk_difference => $disk_difference,
virsh_difference => $virsh_difference,
new_definition => $database_definition,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0001", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0001",
message_variables => $variables,
set_by => $THIS_FILE,
});
}
else
{
# Store the definition from memory
my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({
debug => 2,
server_definition_xml => $definition,
server_definition_server_uuid => $server_uuid,
# Get the diffs before we update
my $db_difference = diff \$on_disk_definition, \$database_definition, { STYLE => 'Unified' };
my $virsh_difference = diff \$database_definition, \$virsh_definition, { STYLE => 'Unified' };
# The disk was updated, so push the change into the database and into virsh.
$database_definition = update_database_definition($anvil, $server_name, $server_uuid, $on_disk_definition);
my $variables = {
server => $server_name,
db_difference => $db_difference,
virsh_difference => $virsh_difference,
new_definition => $on_disk_definition,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0002", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0002",
message_variables => $variables,
set_by => $THIS_FILE,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }});
if ((not $server_definition_uuid) or ($server_definition_uuid ne "!!error!!"))
{
# What?
next;
}
}
# Reload the servers.
$anvil->Database->get_servers({debug => 2});
$anvil->Database->get_server_definitions({debug => 2});
# In either case, update the virsh definition from the disk now (either to load the
# changes that originated from the database or from a direct file edit).
$virsh_definition = redefine_server_from_disk($anvil, $server_name);
}
# If the defition in the database isn't the same as the definition in memory, update it.
if ($definition ne $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml})
elsif (($virsh_definition ne $database_definition) && ($virsh_definition ne $on_disk_definition))
{
# Register a notice-level alert.
my $difference = diff \$anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml}, \$definition, { STYLE => 'Unified' };
my $variables = {
server => $server,
difference => $difference,
# The last possible source of change is a direct edit of the definition via a virsh
# tool, like virt-manager or a command line virsh call. Either way, the virsh
# definition won't match either the database or the on-disk. So in this case, we
# use the virsh definition to update both.
my $disk_difference = diff \$virsh_definition, \$on_disk_definition, { STYLE => 'Unified' };
my $db_difference = diff \$virsh_definition, \$database_definition, { STYLE => 'Unified' };
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
disk_difference => $disk_difference,
db_difference => $db_difference,
}});
$on_disk_definition = update_on_disk_definition($anvil, $server_name, $virsh_definition);
$database_definition = update_database_definition($anvil, $server_name, $server_uuid, $virsh_definition);
my $variables = {
server => $server_name,
definition_file => $xml_file,
disk_difference => $disk_difference,
db_difference => $db_difference,
new_definition => $virsh_definition,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0001", variables => $variables});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0003", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0001",
message => "scan_server_alert_0003",
message_variables => $variables,
set_by => $THIS_FILE,
});
# Update the definition in the database.
my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({
debug => 2,
server_definition_xml => $definition,
server_definition_server_uuid => $server_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }});
if ((not $server_definition_uuid) or ($server_definition_uuid ne "!!error!!"))
{
# What?
next;
}
$anvil->Database->get_server_definitions({debug => 2});
}
### NOTE: If the disk version differs from the in-memory or in-database copy, and the
### age of the file on disk is newer than the age of the database record (which will
### match the in-memory definition by this point), we'll leave the disk version alone.
### This is done so that a manual edit of the definition file won't be immediately
### overwritten by the definition in memory.
# If the file doesn't exist, we'll write it out.
# If it does exist, but differs from the version in memory, we'll write the file.
my $write_file = 0;
my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml";
if (not -e $xml_file)
### TODO: Left off here. Check the RAM in use in the live XML (not --inactive) to see if it diffes from the config file.
# Now that definition updates are dealth with, has anything else changed?
my $current_state = $anvil->data->{'scan-server'}{server_name}{$server_name}{server_state};
my $last_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
}
$anvil->Cluster->get_peers();
my $peer_is = $anvil->data->{sys}{anvil}{peer_is};
my $peer_name = $anvil->data->{sys}{anvil}{$peer_is}{host_name};
my $peer_uuid = $anvil->data->{sys}{anvil}{$peer_is}{host_uuid};
my $dr_name = $anvil->data->{sys}{anvil}{dr1}{host_name};
my $dr_uuid = $anvil->data->{sys}{anvil}{dr1}{host_uuid};
my $password = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_password};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:peer_is' => $peer_is,
's2:peer_name' => $peer_name,
's3:peer_uuid' => $peer_uuid,
's4:dr_name' => $dr_name,
's5:dr_uuid' => $dr_uuid,
's6:password' => $anvil->Log->is_secure($password),
}});
# If we can access our peer or DR host, see what servers are running on them. We do no parsing or
# processing of servers on other machines, we only care what servers are running.
my $peer_access = $anvil->Remote->test_access({
target => $peer_name,
password => $password,
});
my $dr_access = $anvil->Remote->test_access({
target => $dr_name,
password => $password,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
peer_access => $peer_access,
dr_access => $dr_access,
}});
if ($peer_access)
{
# Get a list of servers running on our peer.
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => 2,
target => $peer_name,
password => $password,
shell_call => $anvil->data->{path}{exe}{virsh}." list --all",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
error => $error,
output => $output,
return_code => $return_code,
}});
foreach my $line (split/\n/, $output)
{
$write_file = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { write_file => $write_file }});
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /^\d+ (.*?) (.*)$/)
{
my $server_name = $1;
my $server_state = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
server_name => $server_name,
server_state => $server_state,
}});
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_state} = $server_state;
$anvil->data->{'scan-server'}{server_name}{$server_name}{undefine} = 0;
$anvil->data->{'scan-server'}{server_name}{$server_name}{host} = $peer_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"scan-server::server_name::${server_name}::server_state" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_state},
"scan-server::server_name::${server_name}::undefine" => $anvil->data->{'scan-server'}{server_name}{$server_name}{undefine},
"scan-server::server_name::${server_name}::host" => $anvil->data->{'scan-server'}{server_name}{$server_name}{host},
}});
}
}
else
}
if ($dr_access)
{
# Get a list of servers running on the DR host.
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => 2,
target => $dr_name,
password => $password,
shell_call => $anvil->data->{path}{exe}{virsh}." list --all",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
error => $error,
output => $output,
return_code => $return_code,
}});
foreach my $line (split/\n/, $output)
{
my $on_disk_definition = $anvil->Storage->read_file({
debug => 2,
file => $xml_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { on_disk_definition => $on_disk_definition }});
if (($on_disk_definition ne "!!error!!") && ($on_disk_definition) && ($definition ne $on_disk_definition))
{
$anvil->Storage->get_file_stats({
debug => 2,
file_path => $xml_file,
});
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
my $current_time = time;
my $file_modified_time = $anvil->data->{file_stat}{$xml_file}{modified_time};
my $database_modified_time = $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{unix_modified_time};
my $file_age = $current_time - $file_modified_time;
my $database_age = $current_time - $database_modified_time;
if ($line =~ /^\d+ (.*?) (.*)$/)
{
my $server_name = $1;
my $server_state = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:current_time' => $current_time,
's2:file_modified_time' => $file_modified_time,
's3:database_modified_time' => $database_modified_time,
's4:file_age' => $file_age,
's5:database_age' => $database_age,
server_name => $server_name,
server_state => $server_state,
}});
# Compare the age of the on-disk file with the age of the database record. If
# the database age is newer, update the on-disk copy.
if ($file_age >= $database_age)
{
# Update the disk copy.
my $difference = diff \$anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml}, \$definition, { STYLE => 'Unified' };
my $variables = {
server => $server,
difference => $difference,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0002", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0002",
message_variables => $variables,
set_by => $THIS_FILE,
});
$write_file = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { write_file => $write_file }});
}
$anvil->data->{'scan-server'}{server_name}{$server_name}{server_state} = $server_state;
$anvil->data->{'scan-server'}{server_name}{$server_name}{undefine} = 0;
$anvil->data->{'scan-server'}{server_name}{$server_name}{host} = $dr_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"scan-server::server_name::${server_name}::server_state" => $anvil->data->{'scan-server'}{server_name}{$server_name}{server_state},
"scan-server::server_name::${server_name}::undefine" => $anvil->data->{'scan-server'}{server_name}{$server_name}{undefine},
"scan-server::server_name::${server_name}::host" => $anvil->data->{'scan-server'}{server_name}{$server_name}{host},
}});
}
}
}
if ($write_file)
# Now, loop through all servers associated with our Anvil!. As we loop through, any servers we didn't
# see will be considered to be off.
foreach my $server_uuid (sort {$a cmp $b} keys %{$anvil->data->{servers}{server_uuid}})
{
my $server_name = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name};
my $host_anvil_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_anvil_uuid};
my $host_anvil_name = $anvil->Get->anvil_name_from_uuid({anvil_uuid => $host_anvil_uuid});
my $server_host_uuid = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_host_uuid};
my $server_host_name = $anvil->Get->host_name_from_uuid({host_uuid => $server_host_uuid});
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:name' => $name,
's2:host_anvil_uuid' => $host_anvil_uuid,
's3:host_anvil_name' => $host_anvil_name,
's4:server_host_uuid' => $server_host_uuid,
's5:server_host_name' => $server_host_name,
's6:server_state' => $server_state,
}});
# Skip if this isn't our Anvil!
next if $host_anvil_uuid ne $anvil_uuid;
# If we saw the server in the previous scan, skip it.
my $is_off = 0;
if (not $anvil->data->{'scan-server'}{server}{$server_name})
{
my $return = $anvil->Storage->write_file({
debug => 3,
body => $definition,
file => $xml_file,
overwrite => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'return' => $return }});
if ($return)
# This server isn't running anywhere. If it was last seen to be running, mark it as
# wnow off.
if ($server_state ne "shut off")
{
# Mark it as being off now.
my $query = "UPDATE servers SET server_state = 'shut off' WHERE server_uuid = ".$anvil->Database->quote($server_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
# Now register an alert.
my $variables = { server => $server_name };
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0007", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0007",
message_variables => $variables,
set_by => $THIS_FILE,
});
}
}
else
{
my $seen_state = $anvil->data->{'scan-server'}{server}{$server_name}{server_state};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { seen_state => $seen_state }});
# If it needs to be undefined, do so now.
if (($anvil->data->{'scan-server'}{server}{$server_name}{undefine}) && ($anvil->data->{scancore}{'scan-server'}{'auto-undefine'}))
{
# Something went wrong.
next;
# It's off, and should be undefined. Dump the defition to archive and underfine.
my $backup_file = $anvil->data->{path}{directories}{shared}{archives}."/".$server_name.".pre-undefine.".$anvil->Get->date_and_time({file_name => 1}).".xml";
my $shell_call = $anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server_name." > ".$backup_file;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list --all", source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
# Register an alert.
my $variables = {
server => $server_name,
backup => $backup_file,
};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_server_alert_0005", variables => $variables});
$anvil->Alert->register({
alert_level => "notice",
message => "scan_server_alert_0005",
message_variables => $variables,
set_by => $THIS_FILE,
});
}
### TODO: left off here
# If anything has changed, register an alert and update.
# Nothing else should have changed, but just in case...
$anvil->Database->insert_or_update_servers({
debug => 2,
server_uuid => $server_uuid,
server_ram_in_use => $anvil->data->{'scan-server'}{server}{$server_name}{server_ram_in_use},
server_configured_ram => $anvil->data->{'scan-server'}{server}{$server_name}{server_configured_ram},
server_anvil_uuid => $anvil->data->{'scan-server'}{server}{$server_name}{server_anvil_uuid},
server_clean_stop => $anvil->data->{'scan-server'}{server}{$server_name}{server_clean_stop},
server_host_uuid => $anvil->data->{'scan-server'}{server}{$server_name}{server_host_uuid},
server_start_after_server_uuid => $anvil->data->{'scan-server'}{server}{$server_name}{server_start_after_server_uuid},
server_start_delay => $anvil->data->{'scan-server'}{server}{$server_name}{server_start_delay},
server_live_migration => $anvil->data->{'scan-server'}{server}{$server_name}{server_live_migration},
server_pre_migration_file_uuid => $anvil->data->{'scan-server'}{server}{$server_name}{server_pre_migration_file_uuid},
server_pre_migration_arguments => $anvil->data->{'scan-server'}{server}{$server_name}{server_pre_migration_arguments},
server_post_migration_file_uuid => $anvil->data->{'scan-server'}{server}{$server_name}{server_post_migration_file_uuid},
server_post_migration_arguments => $anvil->data->{'scan-server'}{server}{$server_name}{server_post_migration_arguments},
}});
}
}
return(0);
}
# This reads the definition file from the database and parses it.
sub get_and_parse_database_definition
{
my ($anvil, $server_name, $server_uuid) = @_;
my $database_definition = $anvil->data->{server_definitions}{server_definition_server_uuid}{$server_uuid}{server_definition_xml};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_definition => $database_definition }});
$anvil->Server->parse_definition({
debug => 2,
server_name => $server_name,
source => "from_db",
definition => $database_definition,
});
return($database_definition);
}
# This reads the definition file for a given server and parses it, returning the definition XML.
sub get_and_parse_disk_definition
{
my ($anvil, $server_name) = @_;
my $on_disk_definition = $anvil->Storage->read_file({file => $xml_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { on_disk_definition => $on_disk_definition }});
$anvil->Server->parse_definition({
debug => 2,
server_name => $server_name,
source => "from_disk",
definition => $on_disk_definition,
});
return($on_disk_definition);
}
# This dumps the definition for a given server and parses it, returning the definition XML.
sub get_and_parse_virsh_definition
{
my ($anvil, $server_name) = @_;
my ($virsh_definition, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." dumpxml --inactive ".$server_name, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $virsh_definition, return_code => $return_code }});
$anvil->Server->parse_definition({
debug => 2,
server_name => $server_name,
source => "from_virsh",
definition => $virsh_definition,
});
return($virsh_definition);
}
# This defines the server using the on-disk XML file. Effectively, this updates the 'inactive' XML definition
# in virsh.
sub redefine_server_from_disk
{
my ($anvil, $server_name) = @_;
my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server_name.".xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { xml_file => $xml_file }});
# Push the new definition into virsh (it won't take effect until a reboot likely, but it will update
# the 'inactive' definition immediately.
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." defined ".$xml_file, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
# Now undefine the server again so it disappears when stopped.
($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." undefine ".$server_name, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
# Re-read and parse the new (inactive) definition
$virsh_definition = get_and_parse_virsh_definition($anvil, $server_name);
return($virsh_definition);
}
# This takes the XML definition from virsh and sees if it's different from the version in the database or on
# disk, updating them if so.
sub update_definitions_from_virsh
{
my ($anvil, $server_name, $server_uuid, $virsh_definition) = @_;
my $problem = $anvil->Server->parse_definition({
debug => 2,
server_name => $server_name,
source => "from_virsh",
definition => $virsh_definition,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if (not $problem)
{
# Parsed successfully. Update the database (it'll check if something actually changed).
my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({
debug => 2,
server_definition_xml => $virsh_definition,
server_definition_server_uuid => $server_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }});
# Reload the database definitions
$anvil->Database->get_server_definitions({debug => 2});
my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server_name.".xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { xml_file => $xml_file }});
# Just write out the file. If nothing else, it'll update the mtime.
my $return = $anvil->Storage->write_file({
body => $virsh_definition,
file => $xml_file,
overwrite => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'return' => $return }});
}
return(0);
}
# This updates the database definition and parses the new definition.
sub update_database_definition
{
my ($anvil, $server_name, $server_uuid, $new_definition) = @_;
my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({
debug => 2,
server_definition_xml => $new_definition,
server_definition_server_uuid => $server_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }});
my $problem = $anvil->Server->parse_definition({
debug => 2,
server_name => $server_name,
source => "new_definition",
definition => $virsh_definition,
});
return($new_definition);
}
# This writes an XML definition to the on-disk definition file, then loads the file stats and parses the
# definition.
sub update_on_disk_definition
{
my ($anvil, $server_name, $new_definition) = @_;
my $xml_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server_name.".xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { xml_file => $xml_file }});
# Write the file
my $return = $anvil->Storage->write_file({
debug => 2,
body => $new_definition,
file => $xml_file,
overwrite => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'return' => $return }});
$anvil->Storage->get_file_stats({
debug => 2,
file_path => $xml_file,
});
# Read the file back in and parse it.
my $on_disk_definition = get_and_parse_disk_definition($anvil, $server_name);
return($on_disk_definition);
}

@ -15,19 +15,62 @@ NOTE: All string keys MUST be prefixed with the agent name! ie: 'scan_server_log
<!-- Alert entries -->
<key name="scan_server_alert_0001">
The definition file for the server: [#!variable!server!#] has changed relative to the database record. This is usually harmless.
The differences are:
====
#!variable!difference!#
====
The definition for the server: [#!variable!server!#] was changed via Striker.
- Pushing the new version to the on-disk definition file [#!variable!definition_file!#]
- Also updating definition used by the hypervisor.
- Note: You may need to reboot or power cycle for the changes to take effect.
- The changes are:
==[ Disk ]============
#!variable!disk_difference!#
==[ Hypervisor ]======
#!variable!virsh_difference!#
==[ New Definition ]==
#!variable!new_difference!#
======================
</key>
<key name="scan_server_alert_0002">
The definition file for the server: [#!variable!server!#] has changed relative to disk record. This is usually harmless.
The differences are:
The on-disk definition for the server: [#!variable!server!#] was directly edited.
- Pushing the new version to the database definition.
- Also updating definition used by the hypervisor.
- Note: You may need to reboot or power cycle for the changes to take effect.
- The changes are:
==[ Database ]========
#!variable!db_difference!#
==[ Hypervisor ]======
#!variable!virsh_difference!#
==[ New Definition ]==
#!variable!new_difference!#
======================
</key>
<key name="scan_server_alert_0003">
The definition for the server: [#!variable!server!#] was edited outside of the Anvil! system. This usually means it was updated using Virtual Machine Manager (or another libvirt tool like the virsh shell).
- Pushing the new version to the on-disk definition file [#!variable!definition_file!#]
- Pushing the new version to the database definition as well.
- Note: You may need to reboot or power cycle for the changes to take effect.
- The changes are:
==[ Disk ]============
#!variable!disk_difference!#
==[ Database ]========
#!variable!db_difference!#
==[ New Definition ]==
#!variable!new_difference!#
======================
</key>
<key name="scan_server_alert_0004">The active definition for the server: [#!variable!server!#] now matches the definition on disk. Changes should now be applied.</key>
<key name="scan_server_alert_0005"></key> <!-- free -->
<key name="scan_server_alert_0006">
A new server named: [#!variable!server!#] has been found. The definition XML is:
====
#!variable!difference!#
#!variable!definition!#
====
</key>
<key name="scan_server_alert_0007">The server: [#!variable!server!#] has been shut off.</key>
<!-- Log entries -->
<key name="scan_server_log_0001">Starting: [#!variable!program!#].</key>
@ -35,7 +78,7 @@ The differences are:
<key name="scan_server_log_0003">The libvirtd daemon is not running. Exiting.</key>
<!-- Message entries (usually meant to be alerts) -->
<key name="scan_server_message_0001">We're node 2, and node 1 is running as well. Exiting as only one node needs to run this agent.</key>
<key name="scan_server_message_0001">The server: [#!variable!server!#] is migrating, skipping scanning it.</key>
<!-- Units -->
<key name="scan_server_unit_0001"></key>

@ -1244,12 +1244,16 @@ CREATE TABLE servers (
server_start_after_server_uuid uuid not null, -- This can be the server_uuid of another server. If set, this server will boot 'server_start_delay' seconds after the referenced server boots. A value of '00000000-0000-0000-0000-000000000000' will tell 'anvil-safe-start' to not boot the server at all. If a server is set not to start, any dependent servers will also stay off.
server_start_delay integer not null default 0, -- See above.
server_host_uuid uuid not null, -- This is the current hosts -> host_uuid for this server. If the server is off, this will be blank.
server_state text not null, -- This is the current state of this server.
server_live_migration boolean not null default TRUE, -- When false, servers will be stopped and then rebooted when a migration is requested. Also, when false, preventative migrations will not happen.
server_state text not null, -- This is the current state of this server, as reported by 'virsh list --all' (see: man virsh -> GENERIC COMMANDS -> --list)
server_live_migration boolean not null default TRUE, -- When false, servers will be frozen for a migration, instead of being migrated while the server is migrating. During a cold migration, the server will be unresponsive, so connections to it could time out. However, by being frozen the migration will complete faster.
server_pre_migration_file_uuid uuid not null, -- This is set to the files -> file_uuid of a script to run BEFORE migrating a server. If the file isn't found or can't run, the script is ignored.
server_pre_migration_arguments text not null, -- These are arguments to pass to the pre-migration script
server_post_migration_file_uuid uuid not null, -- This is set to the files -> file_uuid of a script to run AFTER migrating a server. If the file isn't found or can't run, the script is ignored.
server_post_migration_arguments text not null, -- These are arguments to pass to the post-migration script
server_ram_in_use numeric not null, -- This is the amount of RAM currently used by the server. If the server is off, then this is the amount of RAM last used when the server was running.
server_configured_ram numeric not null, -- This is the amount of RAM allocated to the server in the on-disk definition file. This should always match the table above, but allows us to track when a user manually updated the allocated RAM in the on-disk definition, but that hasn't yet been picked up by the server
server_updated_by_user numeric not null, -- This is set to a unix timestamp when the user last updated the definition (via striker). When set, scan-server will check this value against the age of the definition file on disk. If this is newer, the on-disk defition will be updated. On the host with the server (if any), the new definition will be loaded into virsh as well.
server_boot_time numeric not null, -- This is the unix time (since epoch) when the server booted. It is calculated by checking the 'ps -p <pid> -o etimes=' when a server is seen to be running when it had be last seen as off. If a server that had been running is seen to be off, this is set back to 0.
modified_date timestamp with time zone not null,
FOREIGN KEY(server_anvil_uuid) REFERENCES anvils(anvil_uuid),
@ -1275,6 +1279,10 @@ CREATE TABLE history.servers (
server_pre_migration_arguments text,
server_post_migration_file_uuid uuid,
server_post_migration_arguments text,
server_ram_in_use numeric,
server_configured_ram numeric,
server_updated_by_user numeric,
server_boot_time numeric,
modified_date timestamp with time zone not null
);
ALTER TABLE history.servers OWNER TO admin;
@ -1299,6 +1307,10 @@ BEGIN
server_pre_migration_arguments,
server_post_migration_file_uuid,
server_post_migration_arguments,
server_ram_in_use,
server_configured_ram,
server_updated_by_user,
server_boot_time,
modified_date)
VALUES
(history_servers.server_uuid,
@ -1313,6 +1325,10 @@ BEGIN
history_servers.server_pre_migration_arguments,
history_servers.server_post_migration_file_uuid,
history_servers.server_post_migration_arguments,
history_servers.server_ram_in_use,
history_servers.server_configured_ram,
history_servers.server_updated_by_user,
history_servers.server_boot_time,
history_servers.modified_date);
RETURN NULL;
END;

@ -1063,6 +1063,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
#!variable!difference!#
====
</key>
<key name="log_0557">Scan agent: [#!variable!agent_name!#] exited after: [#!variable!runtime!#] seconds with the return code: [#!variable!return_code!#].</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>

@ -66,6 +66,10 @@ wait_for_database($anvil);
# If we're not configured, sleep.
wait_until_configured($anvil);
# Load the strings from all the agents we know about before we process alerts so that we can include their
# messages in any emails we're going to send.
load_agent_strings($anvil);
# Send a startup message immediately
$anvil->Email->check_config();
$anvil->Alert->register({alert_level => "notice", message => "message_0179", set_by => $THIS_FILE});
@ -167,17 +171,23 @@ sub call_agents
# Read the words file so that we can generate alerts later.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0251", variables => {
agent_name => $agent_name,
file => $agent_path,
file => $agent_words,
}});
$anvil->Words->read({file => $agent_words});
}
# Set the timeout.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
agent_name => $agent_name,
"scancore::${agent_name}::timeout" => $anvil->data->{scancore}{$agent_name}{timeout},
}});
# Now call the agent.
my $start_time = time;
my $timeout = $default_timeout;
if ((exists $anvil->data->{scancore}{agent}{$agent_name}{agent_runtime}) && ($anvil->data->{scancore}{agent}{$agent_name}{agent_runtime} =~ /^\d+$/))
if (($anvil->data->{scancore}{$agent_name}{timeout}) && ($anvil->data->{scancore}{$agent_name}{timeout} =~ /^\d+$/))
{
$timeout = $anvil->data->{scancore}{agent}{$agent_name}{agent_runtime};
$timeout = $anvil->data->{scancore}{$agent_name}{timeout};
}
my $shell_call = $anvil->data->{path}{exe}{timeout}." ".$timeout." ".$agent_path;
if ($anvil->data->{sys}{'log'}{level})
@ -197,6 +207,11 @@ sub call_agents
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0557", variables => {
agent_name => $agent_name,
runtime => (time - $start_time),
return_code => $return_code,
}});
# If the return code is '124', timeout popped.
if ($return_code eq "124")
@ -244,6 +259,36 @@ sub exit_if_sums_changed
return(0);
}
# This loads the strings from all the agents we know about.
sub load_agent_strings
{
my ($anvil) = @_;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"path::directories::scan_agents" => $anvil->data->{path}{directories}{scan_agents},
}});
scan_directory($anvil, $anvil->data->{path}{directories}{scan_agents});
# Now loop through the agents I found and call them.
foreach my $agent_name (sort {$a cmp $b} keys %{$anvil->data->{scancore}{agent}})
{
my $agent_words = $anvil->data->{scancore}{agent}{$agent_name}.".xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { agent_words => $agent_words }});
if ((-e $agent_words) && (-r $agent_words))
{
# Read the words file.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0251", variables => {
agent_name => $agent_name,
file => $agent_words,
}});
$anvil->Words->read({file => $agent_words});
}
}
return(0);
}
# Handle pre-run tasks.
sub prepare_for_run
{

@ -34,6 +34,22 @@ $anvil->Storage->get_file_stats({
file_path => "/root/test",
});
my $server = "srv07-el6";
my $runtime = $anvil->Server->get_runtime({
debug => 2,
server => $server,
});
if ($runtime)
{
my $boot_time = time - $runtime;
print "Server: [".$server."] has been running for: [".$runtime."] seconds.\n";
print "- Booted at: [".$anvil->Get->date_and_time({use_time => $boot_time})."]\n";
}
else
{
print "The server: [".$server."] isn't running.\n";
}
# $anvil->Cluster->shutdown_server({
# debug => 2,
# server => "srv07-el6",

Loading…
Cancel
Save