* This rather heavily reworks how database shutdowns works. It adds much more intelligent shutdown, tracking who is using the database, being able to mark a database as "offline" and waiting for users of the database to disconnect before it shuts down.

* Also removed the variables for the database name and DB user name, setting them statically now.
* Created Database->shutdown() to more kindly stop a local database server.
* Added 'check_db_in_use_states()' to anvil-daemon to clean any stale entries marking a database as in use.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 3 years ago
parent 6b3461bd66
commit 3fd0db15bf
  1. 6
      Anvil/Tools.pm
  2. 378
      Anvil/Tools/Database.pm
  3. 14
      cgi-bin/striker
  4. 4
      notes
  5. 9
      share/words.xml
  6. 69
      tools/anvil-daemon
  7. 4
      tools/striker-manage-peers
  8. 4
      tools/striker-prep-database

@ -874,15 +874,9 @@ sub _set_defaults
locking_reap_age => 300, locking_reap_age => 300,
log_transactions => 0, log_transactions => 0,
maximum_batch_size => 25000, maximum_batch_size => 25000,
# NOTE: Do NOT change this unless you are certain all machines that use this host
# have been likewise updated!
name => "anvil",
read_uuid => "", read_uuid => "",
test_table => "hosts", test_table => "hosts",
timestamp => "", timestamp => "",
# NOTE: Do NOT change this unless you are certain all machines that use this host
# have been likewise updated!
user => "admin",
use_handle => "", use_handle => "",
}, },
host_type => "", host_type => "",

@ -91,6 +91,7 @@ my $THIS_FILE = "Database.pm";
# read_variable # read_variable
# refresh_timestamp # refresh_timestamp
# resync_databases # resync_databases
# shutdown
# update_host_status # update_host_status
# write # write
# _add_to_local_config # _add_to_local_config
@ -333,9 +334,9 @@ sub backup_database
} }
my $start_time = time; my $start_time = time;
my $dump_file = $anvil->data->{path}{directories}{pgsql}."/".$anvil->data->{sys}{database}{name}."_db_dump.".$anvil->Get->host_uuid().".sql"; my $dump_file = $anvil->data->{path}{directories}{pgsql}."/anvil_db_dump.".$anvil->Get->host_uuid().".sql";
$dump_file =~ s/\/\//\//g; $dump_file =~ s/\/\//\//g;
my $dump_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{pg_dump}." ".$anvil->data->{sys}{database}{name}." > ".$dump_file."\""; my $dump_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{pg_dump}." anvil > ".$dump_file."\"";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
dump_file => $dump_file, dump_file => $dump_file,
dump_call => $dump_call, dump_call => $dump_call,
@ -1032,7 +1033,7 @@ sub configure_pgsql
# Does the database user exist? # Does the database user exist?
my $create_user = 1; my $create_user = 1;
my $database_user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; my $database_user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "admin";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { database_user => $database_user }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { database_user => $database_user }});
if (not $database_user) if (not $database_user)
{ {
@ -1099,7 +1100,7 @@ sub configure_pgsql
# Create the database, if needed. # Create the database, if needed.
my $create_database = 1; my $create_database = 1;
my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { database_name => $database_name }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { database_name => $database_name }});
(my $database_list, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c 'SELECT datname FROM pg_catalog.pg_database;'\"", source => $THIS_FILE, line => __LINE__}); (my $database_list, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c 'SELECT datname FROM pg_catalog.pg_database;'\"", source => $THIS_FILE, line => __LINE__});
@ -1329,10 +1330,6 @@ sub connect
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::host_uuid" => $anvil->data->{sys}{host_uuid} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::host_uuid" => $anvil->data->{sys}{host_uuid} }});
} }
# This will be used in a few cases where the local DB ID is needed (or the lack of it being set
# showing we failed to connect to the local DB).
$anvil->data->{sys}{database}{local_uuid} = "";
# This will be set to '1' if either DB needs to be initialized or if the last_updated differs on any node. # This will be set to '1' if either DB needs to be initialized or if the last_updated differs on any node.
$anvil->data->{sys}{database}{resync_needed} = 0; $anvil->data->{sys}{database}{resync_needed} = 0;
@ -1377,6 +1374,8 @@ sub connect
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }});
next if ((not $uuid) or (not $anvil->Validate->uuid({uuid => $uuid}))); next if ((not $uuid) or (not $anvil->Validate->uuid({uuid => $uuid})));
# Have we been asked to connect to a specific DB? If so, and if this isn't the requested
# UUID, skip it.
if (($db_uuid) && ($db_uuid ne $uuid)) if (($db_uuid) && ($db_uuid ne $uuid))
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0191", variables => { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0191", variables => {
@ -1406,6 +1405,9 @@ sub connect
password => $anvil->Log->is_secure($password), password => $anvil->Log->is_secure($password),
}}); }});
my $is_local = $anvil->Network->is_local({debug => $debug, host => $host});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
# If there's no password, skip. # If there's no password, skip.
if (not $password) if (not $password)
{ {
@ -1417,7 +1419,7 @@ sub connect
# usual), set it as if we had read it from the config file using the default. # usual), set it as if we had read it from the config file using the default.
if (not $anvil->data->{database}{$uuid}{name}) if (not $anvil->data->{database}{$uuid}{name})
{ {
$anvil->data->{database}{$uuid}{name} = $anvil->data->{sys}{database}{name}; $anvil->data->{database}{$uuid}{name} = "anvil";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "database::${uuid}::name" => $anvil->data->{database}{$uuid}{name} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "database::${uuid}::name" => $anvil->data->{database}{$uuid}{name} }});
} }
@ -1512,6 +1514,7 @@ sub connect
AutoCommit => 1, AutoCommit => 1,
pg_enable_utf8 => 1 pg_enable_utf8 => 1
}); }; }); };
$test = "" if not defined $test;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:test' => $test, 's1:test' => $test,
's2:$@' => $@, 's2:$@' => $@,
@ -1579,8 +1582,6 @@ sub connect
elsif ($dbh =~ /^DBI::db=HASH/) elsif ($dbh =~ /^DBI::db=HASH/)
{ {
# Woot! # Woot!
$anvil->data->{sys}{database}{connections}++;
push @{$successful_connections}, $uuid;
$anvil->data->{cache}{database_handle}{$uuid} = $dbh; $anvil->data->{cache}{database_handle}{$uuid} = $dbh;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
dbh => $dbh, dbh => $dbh,
@ -1600,6 +1601,11 @@ sub connect
$anvil->Database->read({set => $dbh}); $anvil->Database->read({set => $dbh});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'anvil->Database->read' => $anvil->Database->read }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'anvil->Database->read' => $anvil->Database->read }});
} }
if (not $anvil->data->{sys}{database}{read_uuid})
{
$anvil->data->{sys}{database}{read_uuid} = $uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::read_uuid" => $anvil->data->{sys}{database}{read_uuid} }});
}
# Read the DB identifier and then check that we've not already connected to this DB. # Read the DB identifier and then check that we've not already connected to this DB.
my $query = "SELECT system_identifier FROM pg_control_system();"; my $query = "SELECT system_identifier FROM pg_control_system();";
@ -1661,16 +1667,52 @@ sub connect
"cache::database_handle::${uuid}" => $anvil->data->{cache}{database_handle}{$uuid}, "cache::database_handle::${uuid}" => $anvil->data->{cache}{database_handle}{$uuid},
}}); }});
# Before I continue, see if this database is going offline.
my ($active_value, undef, undef) = $anvil->Database->read_variable({
debug => $debug,
uuid => $uuid,
variable_name => "database::".$uuid."::active",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { active_value => $active_value }});
if (not $active_value)
{
# If we're "retry", we just started up.
if (($retry) && ($is_local))
{
# Set the variable saying we're active.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0698"});
my $variable_uuid = $anvil->Database->insert_or_update_variables({
uuid => $uuid,
variable_name => "database::".$uuid."::active",
variable_value => "1",
variable_default => "0",
variable_description => "striker_0294",
variable_section => "database",
variable_source_uuid => "NULL",
variable_source_table => "",
});
}
else
{
# Don't use this database.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0699", variables => { host => $uuid }});
$anvil->data->{cache}{database_handle}{$uuid}->disconnect;
delete $anvil->data->{cache}{database_handle}{$uuid};
if ($anvil->data->{sys}{database}{read_uuid} eq $uuid)
{
$anvil->data->{sys}{database}{read_uuid} = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sys::database::read_uuid" => $anvil->data->{sys}{database}{read_uuid} }});
}
next;
}
}
# Set the first ID to be the one I read from later. Alternatively, if this host is # Set the first ID to be the one I read from later. Alternatively, if this host is
# local, use it. # local, use it.
if (($host eq $anvil->Get->host_name) or if (($is_local) or (not $anvil->data->{sys}{database}{read_uuid}))
($host eq $anvil->Get->short_host_name) or
($host eq "localhost") or
($host eq "127.0.0.1") or
(not $anvil->data->{sys}{database}{read_uuid}))
{ {
$anvil->data->{sys}{database}{read_uuid} = $uuid; $anvil->data->{sys}{database}{read_uuid} = $uuid;
$anvil->data->{sys}{database}{local_uuid} = $uuid;
$anvil->Database->read({set => $anvil->data->{cache}{database_handle}{$uuid}}); $anvil->Database->read({set => $anvil->data->{cache}{database_handle}{$uuid}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
@ -1696,15 +1738,38 @@ sub connect
'anvil->Database->read' => $anvil->Database->read, 'anvil->Database->read' => $anvil->Database->read,
"sys::database::timestamp" => $anvil->data->{sys}{database}{timestamp}, "sys::database::timestamp" => $anvil->data->{sys}{database}{timestamp},
}}); }});
# Record this as successful
$anvil->data->{sys}{database}{connections}++;
push @{$successful_connections}, $uuid;
} }
# Before we try to connect, see if this is a local database and, if so, make sure it's setup. # Before we try to connect, see if this is a local database and, if so, make sure it's setup.
my $is_local = $anvil->Network->is_local({debug => $debug, host => $host});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { is_local => $is_local }});
if ($is_local) if ($is_local)
{ {
$anvil->data->{sys}{database}{read_uuid} = $uuid; $anvil->data->{sys}{database}{read_uuid} = $uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::read_uuid" => $anvil->data->{sys}{database}{read_uuid} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::read_uuid" => $anvil->data->{sys}{database}{read_uuid} }});
# If we're a striker, set the variable saying we're active if we need to.
my ($active_value, undef, undef) = $anvil->Database->read_variable({
debug => $debug,
uuid => $uuid,
variable_name => "database::".$uuid."::active",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { active_value => $active_value }});
if (not $active_value)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0698"});
my $variable_uuid = $anvil->Database->insert_or_update_variables({
variable_name => "database::".$uuid."::active",
variable_value => "1",
variable_default => "0",
variable_description => "striker_0294",
variable_section => "database",
variable_source_uuid => "NULL",
variable_source_table => "",
});
}
} }
elsif (not $anvil->data->{sys}{database}{read_uuid}) elsif (not $anvil->data->{sys}{database}{read_uuid})
{ {
@ -1750,7 +1815,7 @@ sub connect
} }
# If we're a striker and no connections were found, start our database. # If we're a striker and no connections were found, start our database.
if (($local_host_type eq "striker") && (not $anvil->data->{sys}{database}{connections}) && ($db_count > 1)) if (($local_host_type eq "striker") && (not $anvil->data->{sys}{database}{connections}))
{ {
# Tell the user we're going to try to load and start. # Tell the user we're going to try to load and start.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0650"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0650"});
@ -1760,7 +1825,7 @@ sub connect
my $backup_age = 0; my $backup_age = 0;
my $youngest_dump = 0; my $youngest_dump = 0;
my $directory = $anvil->data->{path}{directories}{pgsql}; my $directory = $anvil->data->{path}{directories}{pgsql};
my $db_name = $anvil->data->{sys}{database}{name}; my $db_name = "anvil";
my $dump_files = []; my $dump_files = [];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }});
@ -5599,7 +5664,7 @@ sub initialize
}}); }});
# This just makes some logging cleaner below. # This just makes some logging cleaner below.
my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
my $say_server = $anvil->data->{database}{$uuid}{host}.":".$anvil->data->{database}{$uuid}{port}." -> ".$database_name; my $say_server = $anvil->data->{database}{$uuid}{host}.":".$anvil->data->{database}{$uuid}{port}." -> ".$database_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_server => $say_server }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_server => $say_server }});
@ -5644,7 +5709,7 @@ sub initialize
}}); }});
# Read in the SQL file and replace #!variable!name!# with the database owner name. # Read in the SQL file and replace #!variable!name!# with the database owner name.
my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "admin";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user => $user }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user => $user }});
my $sql = $anvil->Storage->read_file({file => $sql_file}); my $sql = $anvil->Storage->read_file({file => $sql_file});
@ -14219,7 +14284,7 @@ sub load_database
} }
# Drop the existing database. # Drop the existing database.
my $drop_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{dropdb}." ".$anvil->data->{sys}{database}{name}."\""; my $drop_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{dropdb}." anvil\"";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { drop_call => $drop_call }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { drop_call => $drop_call }});
$output = ""; $output = "";
$return_code = ""; $return_code = "";
@ -14259,7 +14324,7 @@ sub load_database
} }
# Recreate the DB. # Recreate the DB.
my $create_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{createdb}." --owner ".$anvil->data->{sys}{database}{user}." ".$anvil->data->{sys}{database}{name}."\""; my $create_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{createdb}." --owner "."admin"." anvil\"";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { create_call => $create_call }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { create_call => $create_call }});
$output = ""; $output = "";
$return_code = ""; $return_code = "";
@ -14287,7 +14352,7 @@ sub load_database
} }
# Finally, load the database. # Finally, load the database.
my $load_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." ".$anvil->data->{sys}{database}{name}." < ".$load_file."\""; my $load_call = $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." anvil < ".$load_file."\"";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { load_call => $load_call }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { load_call => $load_call }});
$output = ""; $output = "";
$return_code = ""; $return_code = "";
@ -14458,7 +14523,7 @@ sub locking
# If I have been asked to check, we will return the variable_uuid if a lock is set. # If I have been asked to check, we will return the variable_uuid if a lock is set.
if ($check) if ($check)
{ {
my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => $variable_name}); my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({debug => $debug, variable_name => $variable_name});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
lock_value => $lock_value, lock_value => $lock_value,
variable_uuid => $variable_uuid, variable_uuid => $variable_uuid,
@ -14473,7 +14538,7 @@ sub locking
{ {
# We check to see if there is a lock before we clear it. This way we don't log that we # We check to see if there is a lock before we clear it. This way we don't log that we
# released a lock unless we really released a lock. # released a lock unless we really released a lock.
my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => $variable_name}); my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({debug => $debug, line => __LINE__, variable_name => $variable_name});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
lock_value => $lock_value, lock_value => $lock_value,
variable_uuid => $variable_uuid, variable_uuid => $variable_uuid,
@ -14532,7 +14597,7 @@ sub locking
}}); }});
# Log that we've renewed the lock. # Log that we've renewed the lock.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0044", variables => { host => $anvil->Get->host_name }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0044", variables => { host => $anvil->Get->host_name }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { set => $set }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { set => $set }});
return($set); return($set);
@ -14547,7 +14612,7 @@ sub locking
$waiting = 0; $waiting = 0;
# See if we had a lock. # See if we had a lock.
my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => $variable_name}); my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({debug => $debug, variable_name => $variable_name});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
waiting => $waiting, waiting => $waiting,
lock_value => $lock_value, lock_value => $lock_value,
@ -15161,20 +15226,33 @@ sub mark_active
return(0); return(0);
} }
my $value = "false"; my $value = $set ? 1 : 0;
if ($set)
{
$value = "true";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { value => $value }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { value => $value }});
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}})
{
# TODO: When unsetting, should we just go directly to a deletion? This method gets us the
# state_uuid though, which is convenient.
my $pid = $$;
my $state_name = "db_in_use::".$uuid."::".$pid;
my $state_uuid = $anvil->Database->insert_or_update_states({ my $state_uuid = $anvil->Database->insert_or_update_states({
state_name => "db_in_use", state_name => $state_name,
state_host_uuid => $anvil->data->{sys}{host_uuid}, state_host_uuid => $anvil->data->{sys}{host_uuid},
state_note => $value, state_note => $value,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }});
return($state_uuid);
# Being a state, if we're clearing, now delete the entry.
# NOTE: The 'state' table has no history schema
if (not $set)
{
# Broadly clear all states that are '0' now.
my $query = "DELETE FROM states WHERE state_name LIKE 'db_in_use%' AND state_note != '1';";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
}
}
return(0);
} }
@ -15374,13 +15452,28 @@ sub query
}}); }});
# Make logging code a little cleaner # Make logging code a little cleaner
my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
my $say_server = $anvil->data->{database}{$uuid}{host}.":".$anvil->data->{database}{$uuid}{port}." -> ".$database_name; "s1:database::${uuid}::name" => $anvil->data->{database}{$uuid}{name},
"s2:database::${uuid}::host" => $anvil->data->{database}{$uuid}{host},
"s3:database::${uuid}::port" => $anvil->data->{database}{$uuid}{port},
}});
my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
my $say_server = $anvil->data->{database}{$uuid}{host}.":";
$say_server .= $anvil->data->{database}{$uuid}{port}." -> ";
$say_server .= $database_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:database_name" => $database_name,
"s2:say_server" => $say_server,
}});
if (not $uuid) if (not $uuid)
{ {
# No database to talk to... # No database to talk to...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0072"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0072", variables => {
query => $query,
source => $source,
line => $line,
}});
return("!!error!!"); return("!!error!!");
} }
elsif (not defined $anvil->data->{cache}{database_handle}{$uuid}) elsif (not defined $anvil->data->{cache}{database_handle}{$uuid})
@ -15581,14 +15674,21 @@ sub read_variable
my $variable_name = $parameter->{variable_name} ? $parameter->{variable_name} : ""; my $variable_name = $parameter->{variable_name} ? $parameter->{variable_name} : "";
my $variable_source_uuid = $parameter->{variable_source_uuid} ? $parameter->{variable_source_uuid} : ""; my $variable_source_uuid = $parameter->{variable_source_uuid} ? $parameter->{variable_source_uuid} : "";
my $variable_source_table = $parameter->{variable_source_table} ? $parameter->{variable_source_table} : ""; my $variable_source_table = $parameter->{variable_source_table} ? $parameter->{variable_source_table} : "";
my $uuid = $parameter->{uuid} ? $parameter->{uuid} : $anvil->data->{sys}{database}{read_uuid}; my $uuid = $parameter->{uuid} ? $parameter->{uuid} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
uuid => $uuid,
variable_uuid => $variable_uuid, variable_uuid => $variable_uuid,
variable_name => $variable_name, variable_name => $variable_name,
variable_source_uuid => $variable_source_uuid, variable_source_uuid => $variable_source_uuid,
variable_source_table => $variable_source_table, variable_source_table => $variable_source_table,
}}); }});
if ((not $uuid) && ($anvil->data->{sys}{database}{read_uuid}))
{
$uuid = $anvil->data->{sys}{database}{read_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }});
}
if (not $variable_source_uuid) if (not $variable_source_uuid)
{ {
$variable_source_uuid = "NULL"; $variable_source_uuid = "NULL";
@ -15638,7 +15738,7 @@ AND
my $variable_value = ""; my $variable_value = "";
my $mtime = ""; my $mtime = "";
my $modified_date = ""; my $modified_date = "";
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); my $results = $anvil->Database->query({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results}; my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
results => $results, results => $results,
@ -15788,18 +15888,18 @@ sub resync_databases
$column4 = $1."y_uuid"; $column4 = $1."y_uuid";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { column4 => $column4 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { column4 => $column4 }});
} }
my $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = ".$anvil->Database->quote($anvil->data->{sys}{database}{name})." AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name = ".$anvil->Database->quote($column1).";"; my $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name = ".$anvil->Database->quote($column1).";";
if ($column4) if ($column4)
{ {
$query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = ".$anvil->Database->quote($anvil->data->{sys}{database}{name})." AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2)." OR column_name = ".$anvil->Database->quote($column3)." OR column_name = ".$anvil->Database->quote($column4).");"; $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2)." OR column_name = ".$anvil->Database->quote($column3)." OR column_name = ".$anvil->Database->quote($column4).");";
} }
elsif ($column3) elsif ($column3)
{ {
$query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = ".$anvil->Database->quote($anvil->data->{sys}{database}{name})." AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2)." OR column_name = ".$anvil->Database->quote($column3).");"; $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2)." OR column_name = ".$anvil->Database->quote($column3).");";
} }
elsif ($column2) elsif ($column2)
{ {
$query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = ".$anvil->Database->quote($anvil->data->{sys}{database}{name})." AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2).");"; $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->Database->quote($column1)." OR column_name = ".$anvil->Database->quote($column2).");";
} }
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
my $uuid_column = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; my $uuid_column = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
@ -16206,6 +16306,182 @@ sub resync_databases
} }
=head2 shutdown
This gracefully shuts down the local database, waiting for active connections to exit before doing so. This call only works on a Striker dashboard. It creates a dump file of the database as part of the shutdown. It always returns C<< 0 >>.
B<< Note >>: This will not return until the database is stopped. This can take some time as it waits for all connections to close, with a C<< 600 >> second (five minute) timeout.
This method takes no parameters.
=cut
sub shutdown
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->shutdown()" }});
# Are we a striker?
my $host_type = $anvil->Get->host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_type => $host_type }});
if ($host_type ne "striker")
{
# Not a dashboard, nothing to do.
return(0);
}
# Is the local databsae running?
my $running = $anvil->System->check_daemon({
debug => $debug,
daemon => $anvil->data->{sys}{daemon}{postgresql},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { running => $running }});
if (not $running)
{
# Already stopped.
return(0);
}
# Set the variable to say we're shutting down.
my $host_uuid = $anvil->Database->quote($anvil->Get->host_uuid);
$host_uuid =~ s/^'(.*)'$/$1/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }});
my $variable_uuid = $anvil->Database->insert_or_update_variables({
variable_name => "database::".$host_uuid."::active",
variable_value => "0",
variable_default => "0",
variable_description => "striker_0294",
variable_section => "database",
variable_source_uuid => "NULL",
variable_source_table => "",
});
# Now wait for all clients to disconnect.
my $waiting = 1;
my $query = "SELECT state_uuid, state_name FROM states WHERE state_name LIKE 'db_in_use::".$host_uuid."::%' AND state_note = '1';";
my $wait_time = 600;
my $stop_waiting = time + $wait_time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:time' => time,
's2:wait_time' => $wait_time,
's3:stop_waiting' => $stop_waiting,
}});
while($waiting)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
my $pids = "";
my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }});
if ($count)
{
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)
{
# Do the same checks we do in anvil-daemon
$anvil->System->pids();
foreach my $row (@{$results})
{
my $state_uuid = $row->[0];
my $state_name = $row->[1];
my $state_pid = ($state_name =~ /db_in_use::.*?::(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:state_uuid' => $state_uuid,
's2:state_name' => $state_name,
's3:state_pid' => $state_pid,
's4:our_pid' => $$,
}});
if ($state_pid eq $$)
{
# This is us, ignore it.
next;
}
if (not exists $anvil->data->{pids}{$state_pid})
{
# Reap the 'db_is_use'.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0140", variables => { pid => $state_pid }});
my $query = "DELETE FROM states WHERE state_uuid = ".$anvil->Database->quote($state_uuid).";";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => 2, query => $query, source => $THIS_FILE, line => __LINE__});
}
else
{
my $command = $anvil->data->{pids}{$state_pid}{command};
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0142", variables => { command => $command }});
$pids .= $state_pid.",";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pids => $pids }});
}
}
$pids =~ s/,$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pids => $pids }});
}
}
# If there's no count, we're done.
if (not $pids)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0697"});
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { waiting => $waiting }});
}
elsif (time > $stop_waiting)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0141", variables => { wait_time => $wait_time }});
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { waiting => $waiting }});
}
else
{
sleep 3;
}
}
# Delete all jobs on our local database, and then stop the DB
$query = "DELETE FROM history.jobs; DELETE FROM jobs;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => $debug, uuid => $host_uuid, query => $query, source => $THIS_FILE, line => __LINE__});
# Mark ourself as no longer using the DB
my $pid = $$;
my $state_name = "db_in_use::".$host_uuid."::".$pid;
my $state_uuid = $anvil->Database->insert_or_update_states({
state_name => $state_name,
state_host_uuid => $anvil->data->{sys}{host_uuid},
state_note => "0",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { state_uuid => $state_uuid }});
$query = "DELETE FROM states WHERE state_name LIKE 'db_in_use%' AND state_note != '1';";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => $debug, uuid => $host_uuid, query => $query, source => $THIS_FILE, line => __LINE__});
# Close our own connection.
$anvil->Database->locking({debug => $debug, release => 1});
$anvil->data->{cache}{database_handle}{$host_uuid}->disconnect;
delete $anvil->data->{cache}{database_handle}{$host_uuid};
# Stop the daemon.
my $return_code = $anvil->System->stop_daemon({daemon => $anvil->data->{sys}{daemon}{postgresql}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { return_code => $return_code }});
if ($return_code eq "0")
{
# Stopped the daemon.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0660"});
}
return(0);
}
=head2 update_host_status =head2 update_host_status
This is a variant on C<< insert_or_update_hosts >> designed only to update the power status of a host. This is a variant on C<< insert_or_update_hosts >> designed only to update the power status of a host.
@ -16330,7 +16606,7 @@ sub write
### NOTE: The careful checks below are to avoid autovivication biting our arses later. ### NOTE: The careful checks below are to avoid autovivication biting our arses later.
# Make logging code a little cleaner # Make logging code a little cleaner
my $database_name = $anvil->data->{sys}{database}{name}; my $database_name = "anvil";
my $say_server = $anvil->Words->string({key => "log_0129"}); my $say_server = $anvil->Words->string({key => "log_0129"});
if (($uuid) && (exists $anvil->data->{database}{$uuid}) && (defined $anvil->data->{database}{$uuid}{name}) && ($anvil->data->{database}{$uuid}{name})) if (($uuid) && (exists $anvil->data->{database}{$uuid}) && (defined $anvil->data->{database}{$uuid}{name}) && ($anvil->data->{database}{$uuid}{name}))
{ {
@ -17224,7 +17500,7 @@ sub _find_column
return('!!error!!') if not $table; return('!!error!!') if not $table;
my $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = ".$anvil->Database->quote($anvil->data->{sys}{database}{name})." AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name LIKE '\%_".$search_column."';"; my $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'anvil' AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name LIKE '\%_".$search_column."';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results}; my $count = @{$results};
@ -17636,7 +17912,7 @@ sub _test_access
} }
# Make logging code a little cleaner # Make logging code a little cleaner
my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
my $say_server = $anvil->data->{database}{$uuid}{host}.":".$anvil->data->{database}{$uuid}{port}." -> ".$database_name; my $say_server = $anvil->data->{database}{$uuid}{host}.":".$anvil->data->{database}{$uuid}{port}." -> ".$database_name;
# Log our test # Log our test
@ -17689,7 +17965,7 @@ sub _test_access
{ {
# We don't test this connection because, if it's down, we'll know # We don't test this connection because, if it's down, we'll know
# when it is tested. # when it is tested.
my $database_name = defined $anvil->data->{database}{$this_uuid}{name} ? $anvil->data->{database}{$this_uuid}{name} : $anvil->data->{sys}{database}{name}; my $database_name = defined $anvil->data->{database}{$this_uuid}{name} ? $anvil->data->{database}{$this_uuid}{name} : "anvil";
my $say_server = $anvil->data->{database}{$this_uuid}{host}.":".$anvil->data->{database}{$this_uuid}{port}." -> ".$database_name; my $say_server = $anvil->data->{database}{$this_uuid}{host}.":".$anvil->data->{database}{$this_uuid}{port}." -> ".$database_name;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0193", variables => { server => $say_server }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0193", variables => { server => $say_server }});
@ -17710,7 +17986,7 @@ sub _test_access
{ {
# We don't test this connection because, if it's down, we'll know # We don't test this connection because, if it's down, we'll know
# when it is tested. # when it is tested.
my $database_name = defined $anvil->data->{database}{$this_uuid}{name} ? $anvil->data->{database}{$this_uuid}{name} : $anvil->data->{sys}{database}{name}; my $database_name = defined $anvil->data->{database}{$this_uuid}{name} ? $anvil->data->{database}{$this_uuid}{name} : "anvil";
my $say_server = $anvil->data->{database}{$this_uuid}{host}.":".$anvil->data->{database}{$this_uuid}{port}." -> ".$database_name; my $say_server = $anvil->data->{database}{$this_uuid}{host}.":".$anvil->data->{database}{$this_uuid}{port}." -> ".$database_name;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0194", variables => { server => $say_server }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0194", variables => { server => $say_server }});

@ -6645,7 +6645,7 @@ sub process_sync_page
next if not $anvil->Validate->subnet_mask({subnet_mask => $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask}}); next if not $anvil->Validate->subnet_mask({subnet_mask => $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask}});
my ($network_type, $network_number) = ($interface =~ /^(.*?)(\d+)_/); my ($network_type, $network_number) = ($interface =~ /^(.*?)(\d+)_/);
my $database_user = $anvil->data->{database}{$host_uuid}{user} ? $anvil->data->{database}{$host_uuid}{user} : $anvil->data->{sys}{database}{user}; my $database_user = $anvil->data->{database}{$host_uuid}{user} ? $anvil->data->{database}{$host_uuid}{user} : "admin";
my $database_port = $anvil->data->{database}{$host_uuid}{port}; my $database_port = $anvil->data->{database}{$host_uuid}{port};
my $network_key = $network_type eq "bcn" ? "striker_0018" : "striker_0022"; my $network_key = $network_type eq "bcn" ? "striker_0018" : "striker_0022";
my $say_network = $anvil->Words->string({key => $network_key, variables => { number => $network_number }}); my $say_network = $anvil->Words->string({key => $network_key, variables => { number => $network_number }});
@ -6682,8 +6682,8 @@ sub process_sync_page
my $host = $anvil->data->{database}{$uuid}{host} ? $anvil->data->{database}{$uuid}{host} : ""; # This should fail my $host = $anvil->data->{database}{$uuid}{host} ? $anvil->data->{database}{$uuid}{host} : ""; # This should fail
my $port = $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432; my $port = $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432;
my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "admin";
my $ping = $anvil->data->{database}{$uuid}{ping} ? $anvil->data->{database}{$uuid}{ping} : 1; my $ping = $anvil->data->{database}{$uuid}{ping} ? $anvil->data->{database}{$uuid}{ping} : 1;
my $password = $anvil->data->{database}{$uuid}{password} ? $anvil->data->{database}{$uuid}{password} : ""; my $password = $anvil->data->{database}{$uuid}{password} ? $anvil->data->{database}{$uuid}{password} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -6759,8 +6759,8 @@ sub delete_sync_peer
my $uuid = $anvil->data->{cgi}{'delete'}{value}; my $uuid = $anvil->data->{cgi}{'delete'}{value};
my $host_name = $anvil->Get->host_name_from_uuid({host_uuid => $uuid}); my $host_name = $anvil->Get->host_name_from_uuid({host_uuid => $uuid});
my $host = $anvil->data->{database}{$uuid}{host} ? $anvil->data->{database}{$uuid}{host} : ""; # This should fail my $host = $anvil->data->{database}{$uuid}{host} ? $anvil->data->{database}{$uuid}{host} : ""; # This should fail
my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "admin";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
uuid => $uuid, uuid => $uuid,
host_name => $host_name, host_name => $host_name,
@ -6816,10 +6816,10 @@ sub add_sync_peer
my ($anvil) = @_; my ($anvil) = @_;
# Break up the user, host and port. If anything goes wrong, we'll set an error and send it back. # Break up the user, host and port. If anything goes wrong, we'll set an error and send it back.
my $pgsql_user = $anvil->data->{sys}{database}{user}; my $pgsql_user = "admin";
my $host = $anvil->data->{cgi}{new_peer_access}{value}; my $host = $anvil->data->{cgi}{new_peer_access}{value};
my $password = $anvil->data->{cgi}{new_peer_password}{value}; my $password = $anvil->data->{cgi}{new_peer_password}{value};
my $db_name = $anvil->data->{sys}{database}{name}; my $db_name = "anvil";
my $ping = $anvil->data->{cgi}{new_peer_ping}{value} eq "on" ? 1 : 0; my $ping = $anvil->data->{cgi}{new_peer_ping}{value} eq "on" ? 1 : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
pgsql_user => $pgsql_user, pgsql_user => $pgsql_user,

@ -655,6 +655,10 @@ virsh attach-interface win2019_test bridge ifn_bridge1 --live --model virtio
# Detach a network interface: # Detach a network interface:
virsh detach-interface win2019_test bridge --mac 52:54:00:ee:b5:1d virsh detach-interface win2019_test bridge --mac 52:54:00:ee:b5:1d
# Attach disks
virsh attach-disk srv34-nas /dev/drbd/by-res/srv34-nas/1 vdb --persistent --targetbus virtio --sourcetype block --subdriver raw
# Change the MTU of a device; # Change the MTU of a device;
ip link set <dev> mtu 9000 ip link set <dev> mtu 9000

@ -1389,7 +1389,7 @@ The database connection error was:
<key name="log_0069">The connection to the database: [#!variable!name!#] on host: [#!variable!host!#:#!variable!port!#] was refused. Is the database server running?</key> <key name="log_0069">The connection to the database: [#!variable!name!#] on host: [#!variable!host!#:#!variable!port!#] was refused. Is the database server running?</key>
<key name="log_0070">The connection to the database: [#!variable!name!#] on host: [#!variable!host!#:#!variable!port!#] failed because the name could not be translated to an IP address. Is this database server's host name in '/etc/hosts'?</key> <key name="log_0070">The connection to the database: [#!variable!name!#] on host: [#!variable!host!#:#!variable!port!#] failed because the name could not be translated to an IP address. Is this database server's host name in '/etc/hosts'?</key>
<key name="log_0071">Successfully Connected to the database: [#!variable!name!#] (id: [#!variable!uuid!#]) on host: [#!variable!host!#:#!variable!port!#].</key> <key name="log_0071">Successfully Connected to the database: [#!variable!name!#] (id: [#!variable!uuid!#]) on host: [#!variable!host!#:#!variable!port!#].</key>
<key name="log_0072"><![CDATA[[ Error ] - The method Database->query() was called without a database ID to query and 'sys::database::read_uuid' doesn't contain a database ID, either. Are any databases available?]]></key> <key name="log_0072"><![CDATA[[ Error ] - The method Database->query() was called without a database ID to query and 'sys::database::read_uuid' doesn't contain a database ID, either. Are any databases available? The query source was: [#!variable!source!#:#!variable!line!#] -> [#!variable!query!#].]]></key>
<key name="log_0073"><![CDATA[[ Error ] - The method Database->query() was asked to query the database with UUID: [#!variable!uuid!#] but there is no file handle open to the database. Was the connection lost?]]></key> <key name="log_0073"><![CDATA[[ Error ] - The method Database->query() was asked to query the database with UUID: [#!variable!uuid!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0074">About to run: [#!variable!uuid!#]:[#!variable!query!#]</key> <key name="log_0074">About to run: [#!variable!uuid!#]:[#!variable!query!#]</key>
<key name="log_0075"><![CDATA[[ Error ] - Failed to prepare the database query: [#!variable!query!#] on: [#!variable!server!#]. The error was: [#!variable!db_error!#]. Note that if the query reports '--', the query was listed as containing sensitive data and '$anvil->Log->secure' is not set.]]></key> <key name="log_0075"><![CDATA[[ Error ] - Failed to prepare the database query: [#!variable!query!#] on: [#!variable!server!#]. The error was: [#!variable!db_error!#]. Note that if the query reports '--', the query was listed as containing sensitive data and '$anvil->Log->secure' is not set.]]></key>
@ -2089,6 +2089,9 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0694">The connection to: [#!variable!host!#] for the resource: [#!variable!resource!#] is in the connection state: [#!variable!connection_state!#]. Will try to connect to the peer and up the resource now.</key> <key name="log_0694">The connection to: [#!variable!host!#] for the resource: [#!variable!resource!#] is in the connection state: [#!variable!connection_state!#]. Will try to connect to the peer and up the resource now.</key>
<key name="log_0695">About to request the start of the resource: [#!variable!resource!#] on: [#!variable!host!#].</key> <key name="log_0695">About to request the start of the resource: [#!variable!resource!#] on: [#!variable!host!#].</key>
<key name="log_0696">The peer: [#!variable!peer!#] is defined in the resource: [#!variable!resource!#] but we don't connect to it, ignoring it.</key> <key name="log_0696">The peer: [#!variable!peer!#] is defined in the resource: [#!variable!resource!#] but we don't connect to it, ignoring it.</key>
<key name="log_0697">All clients using our database are gone, ready to stop the postgresql daemon.</key>
<key name="log_0698">[ Note ] - Marking our database as active.</key>
<key name="log_0699">[ Note ] - The Striker database host: [#!variable!host!#] is inactive, skipping it.</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. --> <!-- 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> <key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>
@ -2781,6 +2784,7 @@ If you are comfortable that the target has changed for a known reason, you can s
<key name="striker_0291">This is the number of bytes transmitted (tx) by a network interface since it was last started.</key> <key name="striker_0291">This is the number of bytes transmitted (tx) by a network interface since it was last started.</key>
<key name="striker_0292">Stay Off</key> <key name="striker_0292">Stay Off</key>
<key name="striker_0293">This is the command used to provision the referenced server.</key> <key name="striker_0293">This is the command used to provision the referenced server.</key>
<key name="striker_0294">This indicates if a Striker's DB is available to be used.</key>
<!-- These are generally units and appended to numbers --> <!-- These are generally units and appended to numbers -->
<key name="suffix_0001">#!variable!number!#/sec</key> <key name="suffix_0001">#!variable!number!#/sec</key>
@ -3124,6 +3128,9 @@ We will sleep a bit and try again.
<key name="warning_0137">[ Warning ] - Timed out waiting for the connections to the peers, and the local resource(s) is not in 'UpToDate' state. Booting the server will likely fail.</key> <key name="warning_0137">[ Warning ] - Timed out waiting for the connections to the peers, and the local resource(s) is not in 'UpToDate' state. Booting the server will likely fail.</key>
<key name="warning_0138">[ Warning ] - Timed out waiting for the connections to the peers.</key> <key name="warning_0138">[ Warning ] - Timed out waiting for the connections to the peers.</key>
<key name="warning_0139">[ Warning ] - We're using: [#!variable!ram_used!#] (#!variable!ram_used_bytes!# Bytes). but there is a job: [#!variable!job_command!#] is runnng, which might be why the RAM is high. NOT exiting while this program is running.</key> <key name="warning_0139">[ Warning ] - We're using: [#!variable!ram_used!#] (#!variable!ram_used_bytes!# Bytes). but there is a job: [#!variable!job_command!#] is runnng, which might be why the RAM is high. NOT exiting while this program is running.</key>
<key name="warning_0140">[ Warning ] - A no-longer active PID: [#!variable!pid!#] had marked our database as "in_use", but the PID is gone now. Reaping the flag.</key>
<key name="warning_0141">[ Warning ] - We waited for: [#!variable!wait_time!#] seconds for all users of the local database to exit. Giving up waiting and taking the database down now.</key>
<key name="warning_0142">[ Warning ] - The command: [#!variable!command!#] is still using our database.</key>
<!-- The entries below here are not sequential, but use a key to find the entry. --> <!-- The entries below here are not sequential, but use a key to find the entry. -->
<!-- Run 'striker-parse-os-list to find new entries. --> <!-- Run 'striker-parse-os-list to find new entries. -->

@ -606,6 +606,9 @@ sub handle_periodic_tasks
# Check if any files have been uploaded to /mnt/shared/incoming on striker # Check if any files have been uploaded to /mnt/shared/incoming on striker
check_incoming($anvil); check_incoming($anvil);
# Check for stale db_in_use states.
check_db_in_use_states($anvil);
} }
# Now check to see if it's time to run less frequent tasks. # Now check to see if it's time to run less frequent tasks.
@ -639,6 +642,9 @@ sub handle_periodic_tasks
} }
elsif ($uuid eq $host_uuid) elsif ($uuid eq $host_uuid)
{ {
# This won't return until we're down.
$anvil->Database->shutdown({debug => 2});
=cut
### TODO: We need to have a way to tell clients to disconnect ### TODO: We need to have a way to tell clients to disconnect
### and then shutdown cleanly. This "Wait for an hour" ### and then shutdown cleanly. This "Wait for an hour"
### is a kludge. ### is a kludge.
@ -674,6 +680,7 @@ sub handle_periodic_tasks
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0660"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0660"});
} }
} }
=cut
} }
} }
} }
@ -807,6 +814,66 @@ sub handle_periodic_tasks
return(0); return(0);
} }
### NOTE: This logic plays out in a slightly different way in Database->shutdown().
# Check for stale db_in_use states.
sub check_db_in_use_states
{
my ($anvil) = @_;
# We only reap db_in_use entries for us.
$anvil->System->pids();
my $host_uuid = $anvil->Database->quote($anvil->Get->host_uuid);
$host_uuid =~ s/^'(.*)'$/$1/;
my $query = "
SELECT
state_uuid,
state_name,
state_note
FROM
states
WHERE
state_name LIKE 'db_in_use::".$host_uuid."::%'
;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
count => $count,
}});
if ($count)
{
foreach my $row (@{$results})
{
my $state_uuid = $row->[0];
my $state_name = $row->[1];
my $state_note = $row->[2];
my $state_pid = ($state_name =~ /db_in_use::.*?::(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:state_uuid' => $state_uuid,
's2:state_name' => $state_name,
's3:state_note' => $state_note,
's4:state_pid' => $state_pid,
}});
if (not exists $anvil->data->{pids}{$state_pid})
{
# Reap the 'db_is_use'.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0140", variables => { pid => $state_pid }});
my $query = "DELETE FROM states WHERE state_uuid = ".$anvil->Database->quote($state_uuid).";";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
$anvil->Database->write({debug => 2, query => $query, source => $THIS_FILE, line => __LINE__});
}
### TODO: What are the chances of a PID being reused in the minute between
### the program's death and us detecting it? Should we filter the
### 'pids::<pid>::command' value against our programs and scan agents?
}
}
return(0);
}
# On dashboards, this checks to see if any files are in /mnt/shared/incoming and, if so, that they've been processed. # On dashboards, this checks to see if any files are in /mnt/shared/incoming and, if so, that they've been processed.
sub check_incoming sub check_incoming
{ {
@ -1348,7 +1415,7 @@ sub prep_database
my $prep_database = 1; my $prep_database = 1;
foreach my $uuid (keys %{$anvil->data->{database}}) foreach my $uuid (keys %{$anvil->data->{database}})
{ {
my $dump_file = $anvil->data->{path}{directories}{pgsql}."/".$anvil->data->{sys}{database}{name}."_db_dump.".$uuid.".sql"; my $dump_file = $anvil->data->{path}{directories}{pgsql}."/anvil_db_dump.".$uuid.".sql";
$dump_file =~ s/\/\//\//g; $dump_file =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dump_file => $dump_file }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dump_file => $dump_file }});
if (-e $dump_file) if (-e $dump_file)

@ -89,8 +89,8 @@ foreach my $host (sort {$a cmp $b} keys %{$anvil->data->{sorted}{db}})
{ {
my $uuid = $anvil->data->{sorted}{db}{$host}; my $uuid = $anvil->data->{sorted}{db}{$host};
my $port = $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432; my $port = $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432;
my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : $anvil->data->{sys}{database}{name}; my $name = $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "anvil";
my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : $anvil->data->{sys}{database}{user}; my $user = $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "admin";
my $password = $anvil->data->{database}{$uuid}{password} ? $anvil->data->{database}{$uuid}{password} : ""; my $password = $anvil->data->{database}{$uuid}{password} ? $anvil->data->{database}{$uuid}{password} : "";
print $anvil->Words->string({key => "message_0032", variables => { print $anvil->Words->string({key => "message_0032", variables => {
peer => $user."\@".$host.":".$port, peer => $user."\@".$host.":".$port,

@ -278,7 +278,7 @@ if ($local_uuid)
# Does the database user exist? # Does the database user exist?
my $create_user = 1; my $create_user = 1;
my $database_user = $anvil->data->{database}{$local_uuid}{user} ? $anvil->data->{database}{$local_uuid}{user} : $anvil->data->{sys}{database}{user}; my $database_user = $anvil->data->{database}{$local_uuid}{user} ? $anvil->data->{database}{$local_uuid}{user} : "admin";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_user => $database_user }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_user => $database_user }});
if (not $database_user) if (not $database_user)
{ {
@ -374,7 +374,7 @@ if ($local_uuid)
# Create the database, if needed. # Create the database, if needed.
my $create_database = 1; my $create_database = 1;
my $database_name = $anvil->data->{database}{$local_uuid}{name} ? $anvil->data->{database}{$local_uuid}{name} : $anvil->data->{sys}{database}{name}; my $database_name = $anvil->data->{database}{$local_uuid}{name} ? $anvil->data->{database}{$local_uuid}{name} : "anvil";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_name => $database_name }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_name => $database_name }});
if (not $database_name) if (not $database_name)
{ {

Loading…
Cancel
Save