You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3810 lines
157 KiB
3810 lines
157 KiB
7 years ago
|
package Anvil::Tools::Database;
|
||
8 years ago
|
#
|
||
|
# This module contains methods related to databases.
|
||
|
#
|
||
|
|
||
|
use strict;
|
||
|
use warnings;
|
||
8 years ago
|
use DBI;
|
||
7 years ago
|
use Scalar::Util qw(weaken isweak);
|
||
8 years ago
|
use Data::Dumper;
|
||
|
|
||
|
our $VERSION = "3.0.0";
|
||
|
my $THIS_FILE = "Database.pm";
|
||
|
|
||
|
### Methods;
|
||
7 years ago
|
# archive_database
|
||
8 years ago
|
# check_lock_age
|
||
8 years ago
|
# configure_pgsql
|
||
8 years ago
|
# connect
|
||
8 years ago
|
# disconnect
|
||
8 years ago
|
# get_hosts
|
||
8 years ago
|
# get_local_id
|
||
8 years ago
|
# initialize
|
||
8 years ago
|
# insert_or_update_hosts
|
||
8 years ago
|
# insert_or_update_states
|
||
8 years ago
|
# insert_or_update_variables
|
||
8 years ago
|
# lock_file
|
||
8 years ago
|
# locking
|
||
|
# mark_active
|
||
8 years ago
|
# query
|
||
8 years ago
|
# read_variable
|
||
8 years ago
|
# resync_databases
|
||
8 years ago
|
# write
|
||
7 years ago
|
# _archive_table
|
||
8 years ago
|
# _find_behind_database
|
||
|
# _mark_database_as_behind
|
||
|
# _test_access
|
||
8 years ago
|
|
||
|
=pod
|
||
|
|
||
|
=encoding utf8
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
7 years ago
|
Anvil::Tools::Database
|
||
8 years ago
|
|
||
|
Provides all methods related to managing and accessing databases.
|
||
|
|
||
|
=head1 SYNOPSIS
|
||
|
|
||
7 years ago
|
use Anvil::Tools;
|
||
8 years ago
|
|
||
7 years ago
|
# Get a common object handle on all Anvil::Tools modules.
|
||
|
my $anvil = Anvil::Tools->new();
|
||
8 years ago
|
|
||
7 years ago
|
# Access to methods using '$anvil->Database->X'.
|
||
8 years ago
|
#
|
||
|
# Example using 'get_local_id()';
|
||
7 years ago
|
my $local_id = $anvil->Database->get_local_id;
|
||
8 years ago
|
|
||
|
=head1 METHODS
|
||
|
|
||
|
Methods in this module;
|
||
|
|
||
|
=cut
|
||
|
sub new
|
||
|
{
|
||
|
my $class = shift;
|
||
|
my $self = {};
|
||
|
|
||
|
bless $self, $class;
|
||
|
|
||
|
return ($self);
|
||
|
}
|
||
|
|
||
7 years ago
|
# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more
|
||
8 years ago
|
# sense in this case to think of it as a parent.
|
||
|
sub parent
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parent = shift;
|
||
|
|
||
|
$self->{HANDLE}{TOOLS} = $parent if $parent;
|
||
|
|
||
7 years ago
|
# Defend against memory leads. See Scalar::Util'.
|
||
|
if (not isweak($self->{HANDLE}{TOOLS}))
|
||
|
{
|
||
|
weaken($self->{HANDLE}{TOOLS});;
|
||
|
}
|
||
|
|
||
8 years ago
|
return ($self->{HANDLE}{TOOLS});
|
||
|
}
|
||
|
|
||
|
|
||
|
#############################################################################################################
|
||
|
# Public methods #
|
||
|
#############################################################################################################
|
||
|
|
||
7 years ago
|
=head2 archive_database
|
||
8 years ago
|
|
||
|
NOTE: Not implemented yet.
|
||
|
|
||
|
=cut
|
||
7 years ago
|
sub archive_database
|
||
8 years ago
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->archive_database()" }});
|
||
8 years ago
|
|
||
7 years ago
|
# If we don't have an array of tables, we have nothing to do.
|
||
7 years ago
|
if ((not exists $anvil->data->{sys}{database}{check_tables}) or (ref(@{$anvil->data->{sys}{database}{check_tables}} ne "ARRAY")))
|
||
7 years ago
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
7 years ago
|
# We'll use the list of tables created for _find_behind_databases()'s 'sys::database::check_tables'
|
||
|
# array, but in reverse so that tables with primary keys (first in the array) are archived last.
|
||
7 years ago
|
foreach my $table (reverse(@{$anvil->data->{sys}{database}{check_tables}}))
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { table => $table }});
|
||
7 years ago
|
|
||
7 years ago
|
$anvil->Database->_archive_table({table => $table});
|
||
7 years ago
|
}
|
||
7 years ago
|
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0126", variables => { method => "Database->archive_database()" }});
|
||
8 years ago
|
return(0);
|
||
|
}
|
||
|
|
||
|
=head2 check_lock_age
|
||
|
|
||
|
This checks to see if 'sys::database::local_lock_active' is set. If it is, its age is checked and if the age is >50% of sys::database::locking_reap_age, it will renew the lock.
|
||
|
|
||
|
=cut
|
||
|
sub check_lock_age
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->check_lock_age()" }});
|
||
8 years ago
|
|
||
|
# Make sure we've got the 'sys::database::local_lock_active' and 'reap_age' variables set.
|
||
7 years ago
|
if ((not defined $anvil->data->{sys}{database}{local_lock_active}) or ($anvil->data->{sys}{database}{local_lock_active} =~ /\D/))
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{database}{local_lock_active} = 0;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::database::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active} }});
|
||
8 years ago
|
}
|
||
7 years ago
|
if ((not $anvil->data->{sys}{database}{locking_reap_age}) or ($anvil->data->{sys}{database}{locking_reap_age} =~ /\D/))
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{database}{locking_reap_age} = 300;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::database::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active} }});
|
||
8 years ago
|
}
|
||
|
|
||
|
# If I have an active lock, check its age and also update the ScanCore lock file.
|
||
|
my $renewed = 0;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::database::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active} }});
|
||
|
if ($anvil->data->{sys}{database}{local_lock_active})
|
||
8 years ago
|
{
|
||
|
my $current_time = time;
|
||
7 years ago
|
my $lock_age = $current_time - $anvil->data->{sys}{database}{local_lock_active};
|
||
|
my $half_reap_age = int($anvil->data->{sys}{database}{locking_reap_age} / 2);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
current_time => $current_time,
|
||
|
lock_age => $lock_age,
|
||
|
half_reap_age => $half_reap_age,
|
||
|
}});
|
||
|
|
||
|
if ($lock_age > $half_reap_age)
|
||
|
{
|
||
|
# Renew the lock.
|
||
7 years ago
|
$anvil->Database->locking({renew => 1});
|
||
8 years ago
|
$renewed = 1;
|
||
|
|
||
|
# Update the lock age
|
||
7 years ago
|
$anvil->data->{sys}{database}{local_lock_active} = time;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::database::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active} }});
|
||
8 years ago
|
|
||
|
# Update the lock file
|
||
7 years ago
|
my $lock_file_age = $anvil->Database->lock_file({'do' => "set"});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { lock_file_age => $lock_file_age }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { renewed => $renewed }});
|
||
8 years ago
|
return($renewed);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 configure_pgsql
|
||
|
|
||
|
This configures the local database server. Specifically, it checks to make sure the daemon is running and starts it if not. It also checks the 'pg_hba.conf' configuration to make sure it is set properly to listen on this machine's IP addresses and interfaces.
|
||
|
|
||
|
If the system is already configured, this method will do nothing, so it is safe to call it at any time.
|
||
|
|
||
8 years ago
|
If there is a problem, C<< !!error!! >> is returned.
|
||
8 years ago
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 id (required)
|
||
|
|
||
|
This is the ID of the local database in the local configuration file that will be used to configure the local system.
|
||
|
|
||
|
=cut
|
||
|
sub configure_pgsql
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->configure_pgsql()" }});
|
||
8 years ago
|
|
||
|
my $id = defined $parameter->{id} ? $parameter->{id} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
8 years ago
|
|
||
|
if (not $id)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->configure_pgsql()", parameter => "id" }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
|
# If we're not running with root access, return.
|
||
|
if (($< != 0) && ($> != 0))
|
||
|
{
|
||
|
# This is a minor error as it will be hit by every unpriviledged program that connects to the
|
||
|
# database(s).
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, priority => "alert", key => "log_0113"});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
|
# First, is it running?
|
||
7 years ago
|
my $running = $anvil->System->check_daemon({daemon => "postgresql"});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { running => $running }});
|
||
8 years ago
|
|
||
|
if (not $running)
|
||
|
{
|
||
|
# Do we need to initialize the databae?
|
||
7 years ago
|
if (not -e $anvil->data->{path}{configs}{'pg_hba.conf'})
|
||
8 years ago
|
{
|
||
|
# Initialize.
|
||
7 years ago
|
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'postgresql-setup'}." initdb", source => $THIS_FILE, line => __LINE__});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
|
||
8 years ago
|
|
||
|
# Did it succeed?
|
||
7 years ago
|
if (not -e $anvil->data->{path}{configs}{'pg_hba.conf'})
|
||
8 years ago
|
{
|
||
|
# Failed...
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0050"});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
# Initialized!
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0055"});
|
||
8 years ago
|
|
||
|
# Enable it on boot.
|
||
7 years ago
|
my $return_code = $anvil->System->enable_daemon({daemon => "postgresql"});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { return_code => $return_code }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Setup postgresql.conf, if needed
|
||
7 years ago
|
my $postgresql_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'postgresql.conf'}});
|
||
8 years ago
|
my $update_postgresql_file = 1;
|
||
|
my $new_postgresql_conf = "";
|
||
|
foreach my $line (split/\n/, $postgresql_conf)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
|
||
8 years ago
|
if ($line =~ /^listen_addresses = '\*'/)
|
||
|
{
|
||
|
# No need to update.
|
||
|
$update_postgresql_file = 0;
|
||
|
last;
|
||
|
}
|
||
|
elsif ($line =~ /^#listen_addresses = 'localhost'/)
|
||
|
{
|
||
|
# Inject the new listen_addresses
|
||
7 years ago
|
$new_postgresql_conf .= "# This has been changed by Anvil::Tools::Database->configure_pgsql() to enable\n";
|
||
8 years ago
|
$new_postgresql_conf .= "# listening on all interfaces.\n";
|
||
|
$new_postgresql_conf .= "#listen_addresses = 'localhost'\n";
|
||
|
$new_postgresql_conf .= "listen_addresses = '*'\n";
|
||
|
}
|
||
|
$new_postgresql_conf .= $line."\n";
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { update_postgresql_file => $update_postgresql_file }});
|
||
8 years ago
|
if ($update_postgresql_file)
|
||
|
{
|
||
|
# Back up the existing one, if needed.
|
||
7 years ago
|
my $postgresql_backup = $anvil->data->{path}{directories}{backups}."/pgsql/postgresql.conf";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { postgresql_backup => $postgresql_backup }});
|
||
8 years ago
|
if (not -e $postgresql_backup)
|
||
|
{
|
||
7 years ago
|
$anvil->Storage->copy_file({source => $anvil->data->{path}{configs}{'postgresql.conf'}, target => $postgresql_backup});
|
||
8 years ago
|
}
|
||
|
|
||
|
# Write the updated one.
|
||
7 years ago
|
$anvil->Storage->write_file({
|
||
|
file => $anvil->data->{path}{configs}{'postgresql.conf'},
|
||
8 years ago
|
body => $new_postgresql_conf,
|
||
|
user => "postgres",
|
||
|
group => "postgres",
|
||
|
mode => "0600",
|
||
|
overwrite => 1,
|
||
|
});
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0056", variables => { file => $anvil->data->{path}{configs}{'postgresql.conf'} }});
|
||
8 years ago
|
}
|
||
|
|
||
|
# Setup pg_hba.conf now, if needed.
|
||
7 years ago
|
my $pg_hba_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'pg_hba.conf'}});
|
||
8 years ago
|
my $update_pg_hba_file = 1;
|
||
|
my $new_pg_hba_conf = "";
|
||
|
foreach my $line (split/\n/, $pg_hba_conf)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
|
||
8 years ago
|
if ($line =~ /^host\s+all\s+all\s+all\s+md5$/)
|
||
8 years ago
|
{
|
||
|
# No need to update.
|
||
|
$update_pg_hba_file = 0;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { update_pg_hba_file => $update_pg_hba_file }});
|
||
8 years ago
|
last;
|
||
|
}
|
||
|
elsif ($line =~ /^# TYPE\s+DATABASE/)
|
||
|
{
|
||
|
# Inject the new listen_addresses
|
||
|
$new_pg_hba_conf .= $line."\n";
|
||
|
$new_pg_hba_conf .= "host\tall\t\tall\t\tall\t\t\tmd5\n";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$new_pg_hba_conf .= $line."\n";
|
||
|
}
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { update_pg_hba_file => $update_pg_hba_file }});
|
||
8 years ago
|
if ($update_pg_hba_file)
|
||
|
{
|
||
|
# Back up the existing one, if needed.
|
||
7 years ago
|
my $pg_hba_backup = $anvil->data->{path}{directories}{backups}."/pgsql/pg_hba.conf";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { pg_hba_backup => $pg_hba_backup }});
|
||
8 years ago
|
if (not -e $pg_hba_backup)
|
||
|
{
|
||
7 years ago
|
$anvil->Storage->copy_file({source => $anvil->data->{path}{configs}{'pg_hba.conf'}, target => $pg_hba_backup});
|
||
8 years ago
|
}
|
||
|
|
||
|
# Write the new one.
|
||
7 years ago
|
$anvil->Storage->write_file({
|
||
|
file => $anvil->data->{path}{configs}{'pg_hba.conf'},
|
||
8 years ago
|
body => $new_pg_hba_conf,
|
||
|
user => "postgres",
|
||
|
group => "postgres",
|
||
|
mode => "0600",
|
||
|
overwrite => 1,
|
||
|
});
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0057", variables => { file => $anvil->data->{path}{configs}{'postgresql.conf'} }});
|
||
8 years ago
|
}
|
||
|
|
||
|
# Start or restart the daemon?
|
||
|
if (not $running)
|
||
|
{
|
||
|
# Start the daemon.
|
||
7 years ago
|
my $return_code = $anvil->System->start_daemon({daemon => "postgresql"});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { return_code => $return_code }});
|
||
8 years ago
|
if ($return_code eq "0")
|
||
|
{
|
||
|
# Started the daemon.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0059"});
|
||
8 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
# Failed to start
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0094"});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
}
|
||
|
elsif (($update_postgresql_file) or ($update_pg_hba_file))
|
||
|
{
|
||
|
# Reload
|
||
7 years ago
|
my $return_code = $anvil->System->start_daemon({daemon => "postgresql"});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { return_code => $return_code }});
|
||
8 years ago
|
if ($return_code eq "0")
|
||
|
{
|
||
|
# Reloaded the daemon.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0112"});
|
||
8 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
# Failed to reload
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0111"});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# Create the .pgpass file, if needed.
|
||
|
my $created_pgpass = 0;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => {
|
||
|
'path::secure::postgres_pgpass' => $anvil->data->{path}{secure}{postgres_pgpass},
|
||
|
"database::${id}::password" => $anvil->data->{database}{$id}{password},
|
||
8 years ago
|
}});
|
||
7 years ago
|
if ((not -e $anvil->data->{path}{secure}{postgres_pgpass}) && ($anvil->data->{database}{$id}{password}))
|
||
8 years ago
|
{
|
||
7 years ago
|
my $body = "*:*:*:postgres:".$anvil->data->{database}{$id}{password};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { body => $body }});
|
||
|
$anvil->Storage->write_file({
|
||
|
file => $anvil->data->{path}{secure}{postgres_pgpass},
|
||
8 years ago
|
body => $body,
|
||
|
user => "postgres",
|
||
|
group => "postgres",
|
||
|
mode => "0600",
|
||
|
overwrite => 1,
|
||
|
secure => 1,
|
||
|
});
|
||
7 years ago
|
if (-e $anvil->data->{path}{secure}{postgres_pgpass})
|
||
8 years ago
|
{
|
||
|
$created_pgpass = 1;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { created_pgpass => $created_pgpass }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# Does the database user exist?
|
||
|
my $create_user = 1;
|
||
7 years ago
|
my $database_user = $anvil->data->{database}{$id}{user};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { database_user => $database_user }});
|
||
8 years ago
|
if (not $database_user)
|
||
8 years ago
|
{
|
||
|
# No database user defined
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0099", variables => { id => $id }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
7 years ago
|
my $user_list = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\"", source => $THIS_FILE, line => __LINE__});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { user_list => $user_list }});
|
||
8 years ago
|
foreach my $line (split/\n/, $user_list)
|
||
|
{
|
||
8 years ago
|
if ($line =~ /^ $database_user\s+\|\s+(\d+)/)
|
||
8 years ago
|
{
|
||
|
# User exists already
|
||
|
my $id = $1;
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0060", variables => { user => $database_user, id => $id }});
|
||
8 years ago
|
$create_user = 0;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { create_user => $create_user }});
|
||
8 years ago
|
if ($create_user)
|
||
|
{
|
||
|
# Create the user
|
||
7 years ago
|
my $create_output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{createuser}." --no-superuser --createdb --no-createrole $database_user\"", source => $THIS_FILE, line => __LINE__});
|
||
|
my $user_list = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\"", source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $user_exists = 0;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { create_output => $create_output, user_list => $user_list }});
|
||
8 years ago
|
foreach my $line (split/\n/, $user_list)
|
||
|
{
|
||
8 years ago
|
if ($line =~ /^ $database_user\s+\|\s+(\d+)/)
|
||
8 years ago
|
{
|
||
|
# Success!
|
||
|
my $id = $1;
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0095", variables => { user => $database_user, id => $id }});
|
||
8 years ago
|
$user_exists = 1;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
if (not $user_exists)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0096", variables => { user => $database_user }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
|
# Update/set the passwords.
|
||
7 years ago
|
if ($anvil->data->{database}{$id}{password})
|
||
8 years ago
|
{
|
||
8 years ago
|
foreach my $user ("postgres", $database_user)
|
||
8 years ago
|
{
|
||
7 years ago
|
my $update_output = $anvil->System->call({secure => 1, shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c \\\"ALTER ROLE $user WITH PASSWORD '".$anvil->data->{database}{$id}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { update_output => $update_output }});
|
||
8 years ago
|
foreach my $line (split/\n/, $user_list)
|
||
|
{
|
||
|
if ($line =~ /ALTER ROLE/)
|
||
|
{
|
||
|
# Password set
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0100", variables => { user => $user }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Create the database, if needed.
|
||
8 years ago
|
my $create_database = 1;
|
||
7 years ago
|
my $database_name = $anvil->data->{database}{$id}{name};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "database::${id}::name" => $anvil->data->{database}{$id}{name} }});
|
||
8 years ago
|
|
||
7 years ago
|
my $database_list = $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__});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { database_list => $database_list }});
|
||
8 years ago
|
foreach my $line (split/\n/, $database_list)
|
||
|
{
|
||
8 years ago
|
if ($line =~ /^ $database_name$/)
|
||
8 years ago
|
{
|
||
|
# Database already exists.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0105", variables => { database => $database_name }});
|
||
8 years ago
|
$create_database = 0;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { create_database => $create_database }});
|
||
8 years ago
|
if ($create_database)
|
||
|
{
|
||
7 years ago
|
my $create_output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{createdb}." --owner $database_user $database_name\"", source => $THIS_FILE, line => __LINE__});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { create_output => $create_output }});
|
||
8 years ago
|
|
||
|
my $database_exists = 0;
|
||
7 years ago
|
my $database_list = $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__});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { database_list => $database_list }});
|
||
8 years ago
|
foreach my $line (split/\n/, $database_list)
|
||
|
{
|
||
8 years ago
|
if ($line =~ /^ $database_name$/)
|
||
8 years ago
|
{
|
||
|
# Database created
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0110", variables => { database => $database_name }});
|
||
8 years ago
|
$database_exists = 1;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
if (not $database_exists)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0109", variables => { database => $database_name }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# Remove the temporary password file.
|
||
7 years ago
|
if (($created_pgpass) && (-e $anvil->data->{path}{secure}{postgres_pgpass}))
|
||
8 years ago
|
{
|
||
7 years ago
|
unlink $anvil->data->{path}{secure}{postgres_pgpass};
|
||
|
if (-e $anvil->data->{path}{secure}{postgres_pgpass})
|
||
8 years ago
|
{
|
||
|
# Failed to unlink the file.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "log_0107"});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
# Make sure the psql TCP port is open.
|
||
7 years ago
|
$anvil->data->{database}{$id}{port} = 5432 if not $anvil->data->{database}{$id}{port};
|
||
|
my $port_status = $anvil->System->manage_firewall({
|
||
7 years ago
|
task => "open",
|
||
7 years ago
|
port_number => $anvil->data->{database}{$id}{port},
|
||
7 years ago
|
});
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { port_status => $port_status }});
|
||
7 years ago
|
|
||
8 years ago
|
return(0);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 connect_to_databases
|
||
|
|
||
|
This method tries to connect to all databases it knows of. To define databases for a machine to connect to, load a configuration file with the following parameters;
|
||
|
|
||
|
database::1::host = an-striker01.alteeve.com
|
||
|
database::1::port = 5432
|
||
|
database::1::name = scancore
|
||
|
database::1::user = admin
|
||
|
database::1::password = Initial1
|
||
|
database::1::ping_before_connect = 1
|
||
|
|
||
|
database::2::host = an-striker02.alteeve.com
|
||
|
database::2::port = 5432
|
||
|
database::2::name = scancore
|
||
|
database::2::user = admin
|
||
|
database::2::password = Initial1
|
||
|
database::2::ping_before_connect = 1
|
||
|
|
||
|
The C<< 1 >> and C<< 2 >> are the IDs of the given databases. They can be any number and do not need to be sequential, they just need to be unique.
|
||
|
|
||
|
This module will return the number of databases that were successfully connected to. This makes it convenient to check and exit if no databases are available using a check like;
|
||
|
|
||
7 years ago
|
my $database_count = $anvil->Database->connect({file => $THIS_FILE});
|
||
8 years ago
|
if($database_count)
|
||
|
{
|
||
|
# Connected to: [$database_count] database(s)!
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# No databases available, exiting.
|
||
|
}
|
||
|
|
||
|
Parameters;
|
||
|
|
||
8 years ago
|
=head3 source (optional)
|
||
|
|
||
|
The C<< source >> parameter is used to check the special C<< updated >> table one all connected databases to see when that source (program name, usually) last updated a given database. If the date stamp is the same on all connected databases, nothing further happens. If one of the databases differ, however, a resync will be requested.
|
||
|
|
||
|
If not defined, the core database will be checked.
|
||
8 years ago
|
|
||
8 years ago
|
If this is not set, no attempt to resync the database will be made.
|
||
|
|
||
|
=head3 sql_file (optional)
|
||
|
|
||
|
This is the SQL schema file that will be used to initialize the database, if the C<< test_table >> isn't found in a given database that is connected to. By default, this is C<< path::sql::Tools.sql >> (C<< /usr/share/perl/AN/Tools.sql >> by default).
|
||
8 years ago
|
|
||
|
=head3 tables (optional)
|
||
|
|
||
7 years ago
|
This is an optional array reference of of tables to specifically check when connecting to databases. Each entry is treated as a table name, and that table's most recent C<< modified_date >> time stamp will be read. If a column name in the table ends in C<< _host_uuid >>, then the check and resync will be restricted to entries in that column matching the current host's C<< sys::host_uuid >>. If the table does not have a corresponding table in the C<< history >> schema, then only the public table will be synced.
|
||
|
|
||
|
Note; The array order is used to allow you to ensure tables with primary keys are synchronyzed before tables with foreign keys. As such, please be aware of the order the table hash references are put into the array reference.
|
||
8 years ago
|
|
||
|
Example use;
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Database->connect({
|
||
7 years ago
|
tables => ["upses", "ups_batteries"],
|
||
8 years ago
|
});
|
||
8 years ago
|
|
||
8 years ago
|
If you want to specify a table that is not linked to a host, set the hash variable's value as an empty string.
|
||
|
|
||
7 years ago
|
$anvil->Database->connect({
|
||
8 years ago
|
tables => {
|
||
|
servers => "",
|
||
|
},
|
||
|
});
|
||
|
|
||
|
=head3 test_table (optional)
|
||
|
|
||
|
Once connected to the database, a query is made to see if the database needs to be initialized. Usually this is C<< defaults::sql::test_table >> (C<< hosts>> by default).
|
||
|
|
||
|
If you set this table manually, it will be checked and if the table doesn't exist on a connected database, the database will be initialized with the C<< sql_file >> parameter's file.
|
||
8 years ago
|
|
||
|
=cut
|
||
|
sub connect
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->connect()" }});
|
||
8 years ago
|
|
||
8 years ago
|
my $source = defined $parameter->{source} ? $parameter->{source} : "core";
|
||
7 years ago
|
my $sql_file = defined $parameter->{sql_file} ? $parameter->{sql_file} : $anvil->data->{path}{sql}{'anvil.sql'};
|
||
8 years ago
|
my $tables = defined $parameter->{tables} ? $parameter->{tables} : "";
|
||
7 years ago
|
my $test_table = defined $parameter->{test_table} ? $parameter->{test_table} : $anvil->data->{defaults}{sql}{test_table};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
source => $source,
|
||
|
sql_file => $sql_file,
|
||
|
tables => $tables,
|
||
|
test_table => $test_table,
|
||
|
}});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->data->{sys}{db_timestamp} = "" if not defined $anvil->data->{sys}{db_timestamp};
|
||
8 years ago
|
|
||
8 years ago
|
# We need the host_uuid before we connect.
|
||
7 years ago
|
if (not $anvil->data->{sys}{host_uuid})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{host_uuid} = $anvil->Get->host_uuid;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::host_uuid" => $anvil->data->{sys}{host_uuid} }});
|
||
8 years ago
|
}
|
||
|
|
||
|
# 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).
|
||
7 years ago
|
$anvil->data->{sys}{local_db_id} = "";
|
||
8 years ago
|
|
||
|
# This will be set to '1' if either DB needs to be initialized or if the last_updated differs on any node.
|
||
7 years ago
|
$anvil->data->{sys}{database}{resync_needed} = 0;
|
||
8 years ago
|
|
||
|
# Now setup or however-many connections
|
||
|
my $seen_connections = [];
|
||
|
my $connections = 0;
|
||
|
my $failed_connections = [];
|
||
|
my $successful_connections = [];
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{database}})
|
||
8 years ago
|
{
|
||
|
my $driver = "DBI:Pg";
|
||
7 years ago
|
my $host = $anvil->data->{database}{$id}{host} ? $anvil->data->{database}{$id}{host} : ""; # This should fail
|
||
|
my $port = $anvil->data->{database}{$id}{port} ? $anvil->data->{database}{$id}{port} : 5432;
|
||
|
my $name = $anvil->data->{database}{$id}{name} ? $anvil->data->{database}{$id}{name} : ""; # This should fail
|
||
|
my $user = $anvil->data->{database}{$id}{user} ? $anvil->data->{database}{$id}{user} : ""; # This should fail
|
||
|
my $password = $anvil->data->{database}{$id}{password} ? $anvil->data->{database}{$id}{password} : "";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
host => $host,
|
||
|
port => $port,
|
||
|
name => $name,
|
||
|
user => $user,
|
||
7 years ago
|
password => $anvil->Log->secure ? $password : "--",
|
||
7 years ago
|
}});
|
||
8 years ago
|
|
||
|
# If not set, we will always ping before connecting.
|
||
7 years ago
|
if ((not exists $anvil->data->{database}{$id}{ping_before_connect}) or (not defined $anvil->data->{database}{$id}{ping_before_connect}))
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{database}{$id}{ping_before_connect} = 1;
|
||
8 years ago
|
}
|
||
|
|
||
|
# Make sure the user didn't specify the same target twice.
|
||
|
my $target_host = "$host:$port";
|
||
|
my $duplicate = 0;
|
||
|
foreach my $existing_host (sort {$a cmp $b} @{$seen_connections})
|
||
|
{
|
||
|
if ($existing_host eq $target_host)
|
||
|
{
|
||
|
# User is connecting to the same target twice.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0053", variables => { target => $target_host }});
|
||
8 years ago
|
$duplicate = 1;
|
||
|
}
|
||
|
}
|
||
|
if (not $duplicate)
|
||
|
{
|
||
|
push @{$seen_connections}, $target_host;
|
||
|
}
|
||
|
next if $duplicate;
|
||
|
|
||
|
# Log what we're doing.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0054", variables => {
|
||
8 years ago
|
id => $id,
|
||
|
driver => $driver,
|
||
|
host => $host,
|
||
|
port => $port,
|
||
|
name => $name,
|
||
|
user => $user,
|
||
7 years ago
|
password => $anvil->Log->secure ? $password : "--",
|
||
8 years ago
|
}});
|
||
|
|
||
|
# Assemble my connection string
|
||
|
my $db_connect_string = "$driver:dbname=$name;host=$host;port=$port";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
db_connect_string => $db_connect_string,
|
||
7 years ago
|
"database::${id}::ping_before_connect" => $anvil->data->{database}{$id}{ping_before_connect},
|
||
8 years ago
|
}});
|
||
7 years ago
|
if ($anvil->data->{database}{$id}{ping_before_connect})
|
||
8 years ago
|
{
|
||
|
# Can I ping?
|
||
7 years ago
|
my ($pinged) = $anvil->System->ping({ping => $host, count => 1});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { pinged => $pinged }});
|
||
8 years ago
|
if (not $pinged)
|
||
|
{
|
||
|
# Didn't ping and 'database::<id>::ping_before_connect' not set. Record this
|
||
8 years ago
|
# in the failed connections array.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0063", variables => {
|
||
7 years ago
|
host => $port ? $host.":".$port : $host,
|
||
|
name => $name,
|
||
|
id => $id,
|
||
|
}});
|
||
8 years ago
|
push @{$failed_connections}, $id;
|
||
|
next;
|
||
|
}
|
||
|
}
|
||
|
|
||
8 years ago
|
# Before we try to connect, see if this is a local database and, if so, make sure it's setup.
|
||
7 years ago
|
my $is_local = $anvil->System->is_local({host => $host});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { is_local => $is_local }});
|
||
7 years ago
|
if ($is_local)
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{read_db_id} = $id;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::read_db_id" => $anvil->data->{sys}{read_db_id} }});
|
||
8 years ago
|
|
||
|
# Set it up (or update it) if needed. This method just returns if nothing is needed.
|
||
7 years ago
|
$anvil->Database->configure_pgsql({id => $id});
|
||
8 years ago
|
}
|
||
7 years ago
|
elsif (not $anvil->data->{sys}{read_db_id})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{read_db_id} = $id;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::read_db_id" => $anvil->data->{sys}{read_db_id} }});
|
||
7 years ago
|
}
|
||
8 years ago
|
|
||
8 years ago
|
# Connect!
|
||
|
my $dbh = "";
|
||
|
### NOTE: The Database->write() method, when passed an array, will automatically disable
|
||
|
### autocommit, do the bulk write, then commit when done.
|
||
|
# We connect with fatal errors, autocommit and UTF8 enabled.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
db_connect_string => $db_connect_string,
|
||
|
user => $user,
|
||
|
}});
|
||
8 years ago
|
eval { $dbh = DBI->connect($db_connect_string, $user, $password, {
|
||
|
RaiseError => 1,
|
||
|
AutoCommit => 1,
|
||
|
pg_enable_utf8 => 1
|
||
|
}); };
|
||
|
if ($@)
|
||
|
{
|
||
|
# Something went wrong...
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0064", variables => {
|
||
8 years ago
|
id => $id,
|
||
|
host => $host,
|
||
|
name => $name,
|
||
|
}});
|
||
|
|
||
|
push @{$failed_connections}, $id;
|
||
|
my $message_key = "log_0065";
|
||
|
my $variables = { dbi_error => $DBI::errstr };
|
||
|
if (not defined $DBI::errstr)
|
||
|
{
|
||
|
# General error
|
||
|
$variables = { dbi_error => $@ };
|
||
|
}
|
||
|
elsif ($DBI::errstr =~ /No route to host/)
|
||
|
{
|
||
|
$message_key = "log_0066";
|
||
|
$variables = { target => $host, port => $port };
|
||
|
}
|
||
|
elsif ($DBI::errstr =~ /no password supplied/)
|
||
|
{
|
||
|
$message_key = "log_0067";
|
||
|
$variables = { id => $id };
|
||
|
}
|
||
|
elsif ($DBI::errstr =~ /password authentication failed for user/)
|
||
|
{
|
||
|
$message_key = "log_0068";
|
||
|
$variables = {
|
||
|
id => $id,
|
||
|
name => $name,
|
||
|
host => $host,
|
||
|
user => $user,
|
||
|
};
|
||
|
}
|
||
|
elsif ($DBI::errstr =~ /Connection refused/)
|
||
|
{
|
||
|
$message_key = "log_0069";
|
||
|
$variables = {
|
||
|
name => $name,
|
||
|
host => $host,
|
||
|
port => $port,
|
||
|
};
|
||
|
}
|
||
|
elsif ($DBI::errstr =~ /Temporary failure in name resolution/i)
|
||
|
{
|
||
|
$message_key = "log_0070";
|
||
|
$variables = {
|
||
|
name => $name,
|
||
|
host => $host,
|
||
|
port => $port,
|
||
|
};
|
||
|
}
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => $message_key, variables => $variables });
|
||
8 years ago
|
}
|
||
|
elsif ($dbh =~ /^DBI::db=HASH/)
|
||
|
{
|
||
|
# Woot!
|
||
|
$connections++;
|
||
|
push @{$successful_connections}, $id;
|
||
7 years ago
|
$anvil->data->{cache}{db_fh}{$id} = $dbh;
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0071", variables => {
|
||
8 years ago
|
host => $host,
|
||
|
port => $port,
|
||
|
name => $name,
|
||
|
id => $id,
|
||
|
}});
|
||
|
|
||
7 years ago
|
if (not $anvil->data->{sys}{use_db_fh})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{use_db_fh} = $dbh;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'sys::use_db_fh' => $anvil->data->{sys}{use_db_fh} }});
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
# If the '$test_table' isn't the same as 'defaults::sql::test_table', see if the core schema needs loading first.
|
||
7 years ago
|
if ($test_table ne $anvil->data->{defaults}{sql}{test_table})
|
||
7 years ago
|
{
|
||
7 years ago
|
my $query = "SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE tablename=".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{defaults}{sql}{test_table})." AND schemaname='public';";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $count = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
7 years ago
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
7 years ago
|
if ($count < 1)
|
||
|
{
|
||
|
# Need to load the database.
|
||
7 years ago
|
$anvil->Database->initialize({id => $id, sql_file => $anvil->data->{path}{sql}{'Tools.sql'}});
|
||
7 years ago
|
}
|
||
|
}
|
||
|
|
||
8 years ago
|
# Now that I have connected, see if my 'hosts' table exists.
|
||
7 years ago
|
my $query = "SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE tablename=".$anvil->data->{sys}{use_db_fh}->quote($test_table)." AND schemaname='public';";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $count = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
8 years ago
|
if ($count < 1)
|
||
|
{
|
||
|
# Need to load the database.
|
||
7 years ago
|
$anvil->Database->initialize({id => $id, sql_file => $sql_file});
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::read_db_id" => $anvil->data->{sys}{read_db_id},
|
||
|
"cache::db_fh::$id" => $anvil->data->{cache}{db_fh}{$id},
|
||
8 years ago
|
}});
|
||
|
|
||
|
# Set the first ID to be the one I read from later. Alternatively, if this host is
|
||
|
# local, use it.
|
||
7 years ago
|
if (($host eq $anvil->_hostname) or
|
||
|
($host eq $anvil->_short_hostname) or
|
||
8 years ago
|
($host eq "localhost") or
|
||
|
($host eq "127.0.0.1") or
|
||
7 years ago
|
(not $anvil->data->{sys}{read_db_id}))
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{read_db_id} = $id;
|
||
|
$anvil->data->{sys}{local_db_id} = $id;
|
||
|
$anvil->data->{sys}{use_db_fh} = $anvil->data->{cache}{db_fh}{$id};
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::read_db_id" => $anvil->data->{sys}{read_db_id},
|
||
|
"sys::use_db_fh" => $anvil->data->{sys}{use_db_fh}
|
||
8 years ago
|
}});
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
# Get a time stamp for this run, if not yet gotten.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"cache::db_fh::$id" => $anvil->data->{cache}{db_fh}{$id},
|
||
|
"sys::db_timestamp" => $anvil->data->{sys}{db_timestamp},
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
8 years ago
|
# Pick a timestamp for this run, if we haven't yet.
|
||
7 years ago
|
if (not $anvil->data->{sys}{db_timestamp})
|
||
8 years ago
|
{
|
||
7 years ago
|
my $query = "SELECT cast(now() AS timestamp with time zone)::timestamptz(0);";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->data->{sys}{db_timestamp} = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::db_timestamp" => $anvil->data->{sys}{db_timestamp} }});
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::read_db_id" => $anvil->data->{sys}{read_db_id},
|
||
|
"sys::use_db_fh" => $anvil->data->{sys}{use_db_fh},
|
||
|
"sys::db_timestamp" => $anvil->data->{sys}{db_timestamp},
|
||
8 years ago
|
}});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# Do I have any connections? Don't die, if not, just return.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { connections => $connections }});
|
||
8 years ago
|
if (not $connections)
|
||
|
{
|
||
|
# Failed to connect to any database. Log this, print to the caller and return.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0091"});
|
||
8 years ago
|
return($connections);
|
||
|
}
|
||
|
|
||
|
# Report any failed DB connections
|
||
|
foreach my $id (@{$failed_connections})
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
|
"database::${id}::host" => $anvil->data->{database}{$id}{host},
|
||
|
"database::${id}::port" => $anvil->data->{database}{$id}{port},
|
||
|
"database::${id}::name" => $anvil->data->{database}{$id}{name},
|
||
|
"database::${id}::user" => $anvil->data->{database}{$id}{user},
|
||
|
"database::${id}::password" => $anvil->Log->secure ? $anvil->data->{database}{$id}{password} : "--",
|
||
7 years ago
|
}});
|
||
|
|
||
8 years ago
|
# Copy my alert hash before I delete the id.
|
||
|
my $error_array = [];
|
||
|
|
||
8 years ago
|
# Delete this DB so that we don't try to use it later. This is a quiet alert because the
|
||
|
# original connection error was likely logged.
|
||
7 years ago
|
my $say_server = $anvil->data->{database}{$id}{host}.":".$anvil->data->{database}{$id}{port}." -> ".$anvil->data->{database}{$id}{name};
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, priority => "alert", key => "log_0092", variables => { server => $say_server, id => $id }});
|
||
8 years ago
|
|
||
|
# Delete it from the list of known databases for this run.
|
||
7 years ago
|
delete $anvil->data->{database}{$id};
|
||
8 years ago
|
|
||
|
# If I've not sent an alert about this DB loss before, send one now.
|
||
7 years ago
|
my $set = $anvil->Alert->check_alert_sent({
|
||
8 years ago
|
type => "set",
|
||
|
set_by => $THIS_FILE,
|
||
|
record_locator => $id,
|
||
|
name => "connect_to_db",
|
||
7 years ago
|
modified_date => $anvil->data->{sys}{db_timestamp},
|
||
8 years ago
|
});
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { set => $set }});
|
||
8 years ago
|
|
||
|
if ($set)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error_array => $error_array }});
|
||
8 years ago
|
foreach my $hash (@{$error_array})
|
||
|
{
|
||
|
my $message_key = $hash->{message_key};
|
||
|
my $message_variables = $hash->{message_variables};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
8 years ago
|
hash => $hash,
|
||
|
message_key => $message_key,
|
||
|
message_variables => $message_variables,
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
|
# These are warning level alerts.
|
||
7 years ago
|
$anvil->Alert->register_alert({
|
||
8 years ago
|
alert_level => "warning",
|
||
8 years ago
|
alert_set_by => $THIS_FILE,
|
||
|
alert_title_key => "alert_title_0003",
|
||
8 years ago
|
alert_message_key => $message_key,
|
||
|
alert_message_variables => $message_variables,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Send an 'all clear' message if a now-connected DB previously wasn't.
|
||
|
foreach my $id (@{$successful_connections})
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"database::${id}::host" => $anvil->data->{database}{$id}{host},
|
||
|
"database::${id}::port" => $anvil->data->{database}{$id}{port},
|
||
|
"database::${id}::name" => $anvil->data->{database}{$id}{name},
|
||
|
"database::${id}::user" => $anvil->data->{database}{$id}{user},
|
||
|
"database::${id}::password" => $anvil->Log->secure ? $anvil->data->{database}{$id}{password} : "--",
|
||
7 years ago
|
}});
|
||
|
|
||
7 years ago
|
### TODO: Is this still an issue? If so, then we either need to require that the DB host
|
||
|
### matches the actual hostname (dumb) or find another way of mapping the host name.
|
||
8 years ago
|
# Query to see if the newly connected host is in the DB yet. If it isn't, don't send an
|
||
|
# alert as it'd cause a duplicate UUID error.
|
||
7 years ago
|
# my $query = "SELECT COUNT(*) FROM hosts WHERE host_name = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{database}{$id}{host}).";";
|
||
|
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
|
||
7 years ago
|
#
|
||
7 years ago
|
# my $count = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
|
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }});
|
||
7 years ago
|
#
|
||
|
# if ($count > 0)
|
||
|
# {
|
||
7 years ago
|
my $cleared = $anvil->Alert->check_alert_sent({
|
||
8 years ago
|
type => "clear",
|
||
7 years ago
|
set_by => $THIS_FILE,
|
||
8 years ago
|
record_locator => $id,
|
||
|
name => "connect_to_db",
|
||
7 years ago
|
modified_date => $anvil->data->{sys}{db_timestamp},
|
||
8 years ago
|
});
|
||
|
if ($cleared)
|
||
|
{
|
||
7 years ago
|
$anvil->Alert->register_alert({
|
||
7 years ago
|
level => "warning",
|
||
|
agent_name => "ScanCore",
|
||
|
title_key => "an_title_0006",
|
||
|
message_key => "cleared_log_0055",
|
||
|
message_variables => {
|
||
7 years ago
|
name => $anvil->data->{database}{$id}{name},
|
||
|
host => $anvil->data->{database}{$id}{host},
|
||
|
port => $anvil->data->{database}{$id}{port} ? $anvil->data->{database}{$id}{port} : 5432,
|
||
8 years ago
|
},
|
||
|
});
|
||
|
}
|
||
7 years ago
|
# }
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::host_uuid" => $anvil->data->{sys}{host_uuid} }});
|
||
|
if ($anvil->data->{sys}{host_uuid} !~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/)
|
||
8 years ago
|
{
|
||
8 years ago
|
# derp. bad UUID
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0103"});
|
||
8 years ago
|
|
||
|
# Disconnect and set the connection count to '0'.
|
||
7 years ago
|
$anvil->Database->disconnect();
|
||
8 years ago
|
$connections = 0;
|
||
|
}
|
||
|
|
||
|
# For now, we just find which DBs are behind and let each agent deal with bringing their tables up to
|
||
|
# date.
|
||
7 years ago
|
if ($connections > 1)
|
||
|
{
|
||
7 years ago
|
$anvil->Database->_find_behind_databases({
|
||
7 years ago
|
source => $source,
|
||
|
tables => $tables,
|
||
|
});
|
||
|
}
|
||
|
|
||
8 years ago
|
# Hold if a lock has been requested.
|
||
7 years ago
|
$anvil->Database->locking();
|
||
8 years ago
|
|
||
|
# Mark that we're not active.
|
||
7 years ago
|
$anvil->Database->mark_active({set => 1});
|
||
8 years ago
|
|
||
8 years ago
|
# Archive old data.
|
||
7 years ago
|
$anvil->Database->archive_database({});
|
||
8 years ago
|
|
||
|
# Sync the database, if needed.
|
||
7 years ago
|
$anvil->Database->resync_databases;
|
||
8 years ago
|
|
||
8 years ago
|
# Add ourselves to the database, if needed.
|
||
7 years ago
|
$anvil->Database->insert_or_update_hosts;
|
||
8 years ago
|
|
||
8 years ago
|
return($connections);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2
|
||
|
|
||
|
This cleanly closes any open file handles to all connected databases and clears some internal database related variables.
|
||
|
|
||
|
=cut
|
||
|
sub disconnect
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->disconnect()" }});
|
||
8 years ago
|
|
||
|
my $marked_inactive = 0;
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{database}})
|
||
8 years ago
|
{
|
||
|
# Don't do anything if there isn't an active file handle for this DB.
|
||
7 years ago
|
next if ((not $anvil->data->{cache}{db_fh}{$id}) or ($anvil->data->{cache}{db_fh}{$id} !~ /^DBI::db=HASH/));
|
||
8 years ago
|
|
||
|
# Clear locks and mark that we're done running.
|
||
|
if (not $marked_inactive)
|
||
|
{
|
||
7 years ago
|
$anvil->Database->mark_active({set => 0});
|
||
|
$anvil->Database->locking({release => 1});
|
||
8 years ago
|
$marked_inactive = 1;
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->data->{cache}{db_fh}{$id}->disconnect;
|
||
|
delete $anvil->data->{cache}{db_fh}{$id};
|
||
8 years ago
|
}
|
||
|
|
||
|
# Delete the stored DB-related values.
|
||
7 years ago
|
delete $anvil->data->{sys}{db_timestamp};
|
||
|
delete $anvil->data->{sys}{use_db_fh};
|
||
|
delete $anvil->data->{sys}{read_db_id};
|
||
8 years ago
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
8 years ago
|
|
||
|
=head2 get_hosts
|
||
|
|
||
|
Get a list of hosts from the c<< hosts >> table, returned as an array of hash references.
|
||
|
|
||
|
Each anonymous hash is structured as:
|
||
|
|
||
|
host_uuid => $host_uuid,
|
||
|
host_name => $host_name,
|
||
|
host_type => $host_type,
|
||
|
modified_date => $modified_date,
|
||
|
|
||
|
It also sets the variables C<< sys::hosts::by_uuid::<host_uuid> = <host_name> >> and C<< sys::hosts::by_name::<host_name> = <host_uuid> >> per host read, for quick reference.
|
||
|
|
||
|
=cut
|
||
|
sub get_hosts
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->get_hosts()" }});
|
||
8 years ago
|
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
host_uuid,
|
||
|
host_name,
|
||
|
host_type,
|
||
|
modified_date
|
||
|
FROM
|
||
|
hosts
|
||
|
;";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
|
||
8 years ago
|
|
||
|
my $return = [];
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
my $host_uuid = $row->[0];
|
||
|
my $host_name = $row->[1];
|
||
|
my $host_type = $row->[2];
|
||
|
my $modified_date = $row->[3];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
8 years ago
|
host_uuid => $host_uuid,
|
||
|
host_name => $host_name,
|
||
|
host_type => $host_type,
|
||
|
modified_date => $modified_date,
|
||
|
}});
|
||
|
push @{$return}, {
|
||
|
host_uuid => $host_uuid,
|
||
|
host_name => $host_name,
|
||
|
host_type => $host_type,
|
||
|
modified_date => $modified_date,
|
||
|
};
|
||
|
|
||
|
# Record the host_uuid in a hash so that the name can be easily retrieved.
|
||
7 years ago
|
$anvil->data->{sys}{hosts}{by_uuid}{$host_uuid} = $host_name;
|
||
|
$anvil->data->{sys}{hosts}{by_name}{$host_name} = $host_uuid;
|
||
8 years ago
|
}
|
||
|
|
||
|
return($return);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 get_local_id
|
||
|
|
||
|
This returns the database ID from 'C<< striker.conf >>' based on matching the 'C<< database::<id>::host >>' to the local machine's host name or one of the active IP addresses on the host.
|
||
|
|
||
|
# Get the local ID
|
||
7 years ago
|
my $local_id = $anvil->Database->get_local_id;
|
||
8 years ago
|
|
||
|
This will return a blank string if no match is found.
|
||
|
|
||
|
=cut
|
||
|
sub get_local_id
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->get_local_id()" }});
|
||
8 years ago
|
|
||
|
my $local_id = "";
|
||
7 years ago
|
my $network_details = $anvil->Get->network_details;
|
||
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{database}})
|
||
8 years ago
|
{
|
||
7 years ago
|
if ($network_details->{hostname} eq $anvil->data->{database}{$id}{host})
|
||
8 years ago
|
{
|
||
|
$local_id = $id;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
if (not $local_id)
|
||
|
{
|
||
|
foreach my $interface (sort {$a cmp $b} keys %{$network_details->{interface}})
|
||
|
{
|
||
|
my $ip_address = $network_details->{interface}{$interface}{ip};
|
||
|
my $subnet_mask = $network_details->{interface}{$interface}{netmask};
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{database}})
|
||
8 years ago
|
{
|
||
7 years ago
|
if ($ip_address eq $anvil->data->{database}{$id}{host})
|
||
8 years ago
|
{
|
||
|
$local_id = $id;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return($local_id);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 initialize
|
||
|
|
||
8 years ago
|
This will initialize a database using a given file.
|
||
8 years ago
|
|
||
|
=cut
|
||
|
sub initialize
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->initialize()" }});
|
||
8 years ago
|
|
||
7 years ago
|
my $id = $parameter->{id} ? $parameter->{id} : $anvil->data->{sys}{read_db_id};
|
||
|
my $sql_file = $parameter->{sql_file} ? $parameter->{sql_file} : $anvil->data->{path}{sql}{'Tools.sql'};
|
||
8 years ago
|
my $success = 1;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
|
||
8 years ago
|
id => $id,
|
||
|
sql_file => $sql_file,
|
||
|
}});
|
||
|
|
||
8 years ago
|
# This just makes some logging cleaner below.
|
||
7 years ago
|
my $say_server = $anvil->data->{database}{$id}{host}.":".$anvil->data->{database}{$id}{port}." -> ".$anvil->data->{database}{$id}{name};
|
||
8 years ago
|
|
||
8 years ago
|
if (not $id)
|
||
|
{
|
||
|
# No database to talk to...
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0077"});
|
||
8 years ago
|
return(0);
|
||
|
}
|
||
7 years ago
|
elsif (not defined $anvil->data->{cache}{db_fh}{$id})
|
||
8 years ago
|
{
|
||
|
# Database handle is gone.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0078", variables => { id => $id }});
|
||
8 years ago
|
return(0);
|
||
|
}
|
||
|
if (not $sql_file)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0079", variables => {
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
id => $id,
|
||
|
}});
|
||
|
return(0);
|
||
|
}
|
||
|
elsif (not -e $sql_file)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0080", variables => {
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
id => $id,
|
||
|
sql_file => $sql_file,
|
||
|
}});
|
||
|
return(0);
|
||
|
}
|
||
|
elsif (not -r $sql_file)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0081", variables => {
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
id => $id,
|
||
|
sql_file => $sql_file,
|
||
|
}});
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
# Tell the user we need to initialize
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0082", variables => {
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
id => $id,
|
||
8 years ago
|
sql_file => $sql_file,
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
|
# Read in the SQL file and replace #!variable!name!# with the database owner name.
|
||
7 years ago
|
my $user = $anvil->data->{database}{$id}{user};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { user => $user }});
|
||
8 years ago
|
|
||
7 years ago
|
my $sql = $anvil->Storage->read_file({file => $sql_file});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ">> sql" => $sql }});
|
||
8 years ago
|
|
||
|
$sql =~ s/#!variable!user!#/$user/sg;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< sql" => $sql }});
|
||
8 years ago
|
|
||
|
# Now that I am ready, disable autocommit, write and commit.
|
||
7 years ago
|
$anvil->Database->write({id => $id, query => $sql, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->data->{sys}{db_initialized}{$id} = 1;
|
||
8 years ago
|
|
||
|
# Mark that we need to update the DB.
|
||
7 years ago
|
$anvil->data->{sys}{database}{resync_needed} = 1;
|
||
8 years ago
|
|
||
|
return($success);
|
||
|
};
|
||
|
|
||
8 years ago
|
=head2 insert_or_update_hosts
|
||
|
|
||
|
This updates (or inserts) a record in the 'hosts' table.
|
||
|
|
||
|
If there is an error, an empty string is returned.
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 host_name (required)
|
||
|
|
||
|
This default value is the local hostname.
|
||
|
|
||
|
=head3 host_type (required)
|
||
|
|
||
|
This default value is the value returned by C<< System->determine_host_type >>.
|
||
|
|
||
|
=head3 host_uuid (required)
|
||
|
|
||
|
The default value is the host's UUID (as returned by C<< Get->host_uuid >>.
|
||
|
|
||
|
=head3 id (optional)
|
||
|
|
||
|
If set, only the corresponding database will be written to.
|
||
|
|
||
|
=cut
|
||
|
sub insert_or_update_hosts
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->insert_or_update_hosts()" }});
|
||
8 years ago
|
|
||
7 years ago
|
my $host_name = $parameter->{host_name} ? $parameter->{host_name} : $anvil->_hostname;
|
||
|
my $host_type = $parameter->{host_type} ? $parameter->{host_type} : $anvil->System->determine_host_type;
|
||
|
my $host_uuid = $parameter->{host_uuid} ? $parameter->{host_uuid} : $anvil->Get->host_uuid;
|
||
8 years ago
|
my $id = $parameter->{id} ? $parameter->{id} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
host_name => $host_name,
|
||
|
host_type => $host_type,
|
||
|
host_uuid => $host_uuid,
|
||
|
id => $id,
|
||
|
}});
|
||
|
|
||
|
if (not $host_name)
|
||
|
{
|
||
|
# Throw an error and exit.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_hosts()", parameter => "host_name" }});
|
||
8 years ago
|
return("");
|
||
|
}
|
||
|
if (not $host_uuid)
|
||
|
{
|
||
|
# Throw an error and exit.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_hosts()", parameter => "host_uuid" }});
|
||
8 years ago
|
return("");
|
||
|
}
|
||
|
|
||
|
# Read the old values, if they exist.
|
||
|
my $old_host_name = "";
|
||
|
my $old_host_type = "";
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
host_name,
|
||
|
host_type
|
||
|
FROM
|
||
|
hosts
|
||
|
WHERE
|
||
7 years ago
|
host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($host_uuid)."
|
||
8 years ago
|
;";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, id => $id, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
$old_host_name = $row->[0];
|
||
|
$old_host_type = $row->[1];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
old_host_name => $old_host_name,
|
||
|
old_host_type => $old_host_type,
|
||
|
}});
|
||
|
}
|
||
|
if (not $count)
|
||
|
{
|
||
|
# Add this host to the database
|
||
|
my $query = "
|
||
|
INSERT INTO
|
||
|
hosts
|
||
|
(
|
||
|
host_uuid,
|
||
|
host_name,
|
||
|
host_type,
|
||
|
modified_date
|
||
|
) VALUES (
|
||
7 years ago
|
".$anvil->data->{sys}{use_db_fh}->quote($host_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($host_name).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($host_type).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
);
|
||
|
";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
|
$anvil->Database->write({query => $query, id => $id, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
7 years ago
|
elsif (($old_host_name ne $host_name) or ($old_host_type ne $host_type))
|
||
8 years ago
|
{
|
||
|
# Clear the stop data.
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
hosts
|
||
|
SET
|
||
7 years ago
|
host_name = ".$anvil->data->{sys}{use_db_fh}->quote($host_name).",
|
||
|
host_type = ".$anvil->data->{sys}{use_db_fh}->quote($host_type).",
|
||
|
modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
WHERE
|
||
7 years ago
|
host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($host_uuid)."
|
||
8 years ago
|
;";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
|
$anvil->Database->write({query => $query, id => $id, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0126", variables => { method => "Database->insert_or_update_hosts()" }});
|
||
8 years ago
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
7 years ago
|
=head2 insert_or_update_network_interfaces
|
||
|
|
||
|
This updates (or inserts) a record in the 'interfaces' table. This table is used to store physical network interface information.
|
||
|
|
||
|
If there is an error, an empty string is returned. Otherwise, the record's UUID is returned.
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 id (optional)
|
||
|
|
||
|
If set, only the corresponding database will be written to.
|
||
|
|
||
|
=head3 network_interface_bond_uuid (optional)
|
||
|
|
||
|
If this interface is part of a bond, this UUID will be the C<< bonds >> -> C<< bond_uuid >> that this interface is slaved to.
|
||
|
|
||
|
=head3 network_interface_bridge_uuid (optional)
|
||
|
|
||
|
If this interface is connected to a bridge, this is the C<< bridges >> -> C<< bridge_uuid >> of that bridge.
|
||
|
|
||
|
=head3 network_interface_duplex (optional)
|
||
|
|
||
|
This can be set to C<< full >>, C<< half >> or C<< unknown >>, with the later being the default.
|
||
|
|
||
|
=head3 network_interface_host_uuid (optional)
|
||
|
|
||
|
This is the host's UUID, as set in C<< sys::host_uuid >>. If not passed, the host's UUID will be read from the system.
|
||
|
|
||
|
=head3 network_interface_link_state (optional)
|
||
|
|
||
|
This can be set to C<< 0 >> or C<< 1 >>, with the later being the default. This indicates if a physical link is present.
|
||
|
|
||
|
=head3 network_interface_mac_address (required)
|
||
|
|
||
|
This is the MAC address of the interface.
|
||
|
|
||
|
=head3 network_interface_medium (required)
|
||
|
|
||
|
This is the medium the interface uses. This is generally C<< copper >>, C<< fiber >>, C<< radio >>, etc.
|
||
|
|
||
|
=head3 network_interface_mtu (optional)
|
||
|
|
||
|
This is the maximum transmit unit (MTU) that this interface supports, in bytes per second. This is usally C<< 1500 >>.
|
||
|
|
||
7 years ago
|
=head3 network_interface_name (required)
|
||
7 years ago
|
|
||
7 years ago
|
This is the current device name for this interface.
|
||
7 years ago
|
|
||
7 years ago
|
=head3 network_interface_operational (optional)
|
||
7 years ago
|
|
||
7 years ago
|
This can be set to C<< up >>, C<< down >> or C<< unknown >>, with the later being the default. This indicates whether the interface is active or not.
|
||
7 years ago
|
|
||
|
=head3 network_interface_speed (optional)
|
||
|
|
||
|
This is the current speed of the network interface in Mbps (megabits per second). If it is not passed, it is set to 0.
|
||
|
|
||
|
=head3 network_interface_uuid (optional)
|
||
|
|
||
|
This is the UUID of an existing record to be updated. If this is not passed, the UUID will be searched using the interface's MAC address. If no match is found, the record will be INSERTed and a new random UUID generated.
|
||
|
|
||
|
=cut
|
||
|
sub insert_or_update_network_interfaces
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->insert_or_update_network_interfaces()" }});
|
||
7 years ago
|
|
||
7 years ago
|
my $id = defined $parameter->{id} ? $parameter->{id} : "";
|
||
|
my $network_interface_bond_uuid = defined $parameter->{network_interface_bond_uuid} ? $parameter->{network_interface_bond_uuid} : "--";
|
||
|
my $network_interface_bridge_uuid = defined $parameter->{network_interface_bridge_uuid} ? $parameter->{network_interface_bridge_uuid} : "--";
|
||
|
my $network_interface_duplex = defined $parameter->{network_interface_duplex} ? $parameter->{network_interface_duplex} : "--";
|
||
7 years ago
|
my $network_interface_host_uuid = defined $parameter->{network_interface_host_uuid} ? $parameter->{network_interface_host_uuid} : $anvil->Get->host_uuid;
|
||
7 years ago
|
my $network_interface_link_state = defined $parameter->{network_interface_link_state} ? $parameter->{network_interface_link_state} : "--";
|
||
|
my $network_interface_operational = defined $parameter->{network_interface_operational} ? $parameter->{network_interface_operational} : "--";
|
||
|
my $network_interface_mac_address = defined $parameter->{network_interface_mac_address} ? $parameter->{network_interface_mac_address} : "--";
|
||
|
my $network_interface_medium = defined $parameter->{network_interface_medium} ? $parameter->{network_interface_medium} : "--";
|
||
|
my $network_interface_mtu = defined $parameter->{network_interface_mtu} ? $parameter->{network_interface_mtu} : "--";
|
||
|
my $network_interface_name = defined $parameter->{network_interface_name} ? $parameter->{network_interface_name} : "--";
|
||
|
my $network_interface_speed = defined $parameter->{network_interface_speed} ? $parameter->{network_interface_speed} : "--";
|
||
|
my $network_interface_uuid = defined $parameter->{network_interface_uuid} ? $parameter->{interface_uuid} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
id => $id,
|
||
|
network_interface_bond_uuid => $network_interface_bond_uuid,
|
||
|
network_interface_bridge_uuid => $network_interface_bridge_uuid,
|
||
|
network_interface_duplex => $network_interface_duplex,
|
||
|
network_interface_host_uuid => $network_interface_host_uuid,
|
||
|
network_interface_link_state => $network_interface_link_state,
|
||
|
network_interface_operational => $network_interface_operational,
|
||
|
network_interface_mac_address => $network_interface_mac_address,
|
||
|
network_interface_medium => $network_interface_medium,
|
||
|
network_interface_mtu => $network_interface_mtu,
|
||
|
network_interface_name => $network_interface_name,
|
||
|
network_interface_speed => $network_interface_speed,
|
||
|
network_interface_uuid => $network_interface_uuid,
|
||
7 years ago
|
}});
|
||
|
|
||
|
# I will need a MAC address and a UUID. If I don't have one, use the other to look it up.
|
||
|
if ((not $network_interface_mac_address) && (not $network_interface_uuid))
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_network_interfaces()", parameter => "network_interface_mac_address" }});
|
||
7 years ago
|
return("");
|
||
|
}
|
||
|
elsif (not $network_interface_uuid)
|
||
|
{
|
||
|
# See if I know this NIC by referencing it's MAC.
|
||
7 years ago
|
my $query = "SELECT network_interface_uuid FROM network_interfaces WHERE network_interface_mac_address = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_mac_address).";";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
$network_interface_uuid = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
7 years ago
|
$network_interface_uuid = "" if not defined $network_interface_uuid;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_uuid => $network_interface_uuid }});
|
||
7 years ago
|
}
|
||
|
|
||
|
# Now, if we're inserting or updating, we'll need to require different bits.
|
||
|
if ($network_interface_uuid)
|
||
|
{
|
||
|
# Update
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
network_interface_host_uuid,
|
||
|
network_interface_mac_address,
|
||
7 years ago
|
network_interface_name,
|
||
7 years ago
|
network_interface_speed,
|
||
|
network_interface_mtu,
|
||
|
network_interface_link_state,
|
||
|
network_interface_operational,
|
||
|
network_interface_duplex,
|
||
|
network_interface_medium,
|
||
|
network_interface_bond_uuid,
|
||
|
network_interface_bridge_uuid
|
||
|
FROM
|
||
|
network_interfaces
|
||
|
WHERE
|
||
7 years ago
|
network_interface_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_uuid).";
|
||
7 years ago
|
";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, id => $id, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
7 years ago
|
my $old_network_interface_host_uuid = $row->[0];
|
||
|
my $old_network_interface_mac_address = $row->[1];
|
||
|
my $old_network_interface_name = $row->[2];
|
||
|
my $old_network_interface_speed = $row->[3];
|
||
|
my $old_network_interface_mtu = defined $row->[4] ? $row->[4] : "";
|
||
|
my $old_network_interface_link_state = $row->[5];
|
||
|
my $old_network_interface_operational = $row->[6];
|
||
|
my $old_network_interface_duplex = $row->[7];
|
||
|
my $old_network_interface_medium = defined $row->[8] ? $row->[8] : "";
|
||
|
my $old_network_interface_bond_uuid = defined $row->[9] ? $row->[9] : "";
|
||
|
my $old_network_interface_bridge_uuid = defined $row->[10] ? $row->[10] : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
old_network_interface_host_uuid => $old_network_interface_host_uuid,
|
||
|
old_network_interface_mac_address => $old_network_interface_mac_address,
|
||
|
old_network_interface_name => $old_network_interface_name,
|
||
|
old_network_interface_speed => $old_network_interface_speed,
|
||
|
old_network_interface_mtu => $old_network_interface_mtu,
|
||
|
old_network_interface_link_state => $old_network_interface_link_state,
|
||
|
old_network_interface_operational => $old_network_interface_operational,
|
||
|
old_network_interface_duplex => $old_network_interface_duplex,
|
||
|
old_network_interface_medium => $old_network_interface_medium,
|
||
|
old_network_interface_bond_uuid => $old_network_interface_bond_uuid,
|
||
|
old_network_interface_bridge_uuid => $old_network_interface_bridge_uuid,
|
||
7 years ago
|
}});
|
||
|
|
||
|
# If the caller didn't pass some values, we'll treat the
|
||
|
|
||
|
# Anything to update? This is a little extra complicated because if a variable was
|
||
|
# not passed in, we want to not compare it.
|
||
7 years ago
|
if ((($network_interface_bond_uuid ne "--") && ($network_interface_bond_uuid ne $old_network_interface_bond_uuid)) or
|
||
|
(($network_interface_bridge_uuid ne "--") && ($network_interface_bridge_uuid ne $old_network_interface_bridge_uuid)) or
|
||
|
(($network_interface_name ne "--") && ($network_interface_name ne $old_network_interface_name)) or
|
||
|
(($network_interface_duplex ne "--") && ($network_interface_duplex ne $old_network_interface_duplex)) or
|
||
|
(($network_interface_link_state ne "--") && ($network_interface_link_state ne $old_network_interface_link_state)) or
|
||
|
(($network_interface_operational ne "--") && ($network_interface_operational ne $old_network_interface_operational)) or
|
||
|
(($network_interface_mac_address ne "--") && ($network_interface_mac_address ne $old_network_interface_mac_address)) or
|
||
|
(($network_interface_medium ne "--") && ($network_interface_medium ne $old_network_interface_medium)) or
|
||
|
(($network_interface_mtu ne "--") && ($network_interface_mtu ne $old_network_interface_mtu)) or
|
||
|
(($network_interface_speed ne "--") && ($network_interface_speed ne $old_network_interface_speed)) or
|
||
|
($network_interface_host_uuid ne $old_network_interface_host_uuid))
|
||
7 years ago
|
{
|
||
|
# UPDATE any rows passed to us.
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
network_interfaces
|
||
|
SET
|
||
7 years ago
|
network_interface_host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_host_uuid).",
|
||
7 years ago
|
";
|
||
|
if ($network_interface_bond_uuid ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_bond_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_bond_uuid).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_bridge_uuid ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_bridge_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_bridge_uuid).", \n";
|
||
7 years ago
|
}
|
||
7 years ago
|
if ($network_interface_name ne "--")
|
||
7 years ago
|
{
|
||
7 years ago
|
$query .= " network_interface_name = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_name).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_duplex ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_duplex = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_duplex).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_link_state ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_link_state = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_link_state).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_operational ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_operational = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_operational).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_mac_address ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_mac_address = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_mac_address).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_medium ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_medium = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_medium).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_mtu ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_mtu = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_mtu).", \n";
|
||
7 years ago
|
}
|
||
|
if ($network_interface_speed ne "--")
|
||
|
{
|
||
7 years ago
|
$query .= " network_interface_speed = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_speed).", \n";
|
||
7 years ago
|
}
|
||
7 years ago
|
$query .= " modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
7 years ago
|
WHERE
|
||
7 years ago
|
network_interface_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($network_interface_uuid)."
|
||
7 years ago
|
;";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
|
$anvil->Database->write({query => $query, id => $id, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
# No change.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# INSERT, but make sure we have enough data first.
|
||
|
if (($network_interface_mac_address eq "--") or (not $network_interface_mac_address))
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_network_interfaces()", parameter => "network_interface_mac_address" }});
|
||
7 years ago
|
return("");
|
||
|
}
|
||
7 years ago
|
if (($network_interface_name eq "--") or (not $network_interface_name))
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_network_interfaces()", parameter => "network_interface_name" }});
|
||
7 years ago
|
return("");
|
||
|
}
|
||
|
|
||
|
# Convert unpassed values to their defaults.
|
||
7 years ago
|
$network_interface_bond_uuid = "NULL" if $network_interface_bond_uuid eq "--";
|
||
|
$network_interface_bridge_uuid = "NULL" if $network_interface_bridge_uuid eq "--";
|
||
|
$network_interface_duplex = "unknown" if $network_interface_duplex eq "--";
|
||
|
$network_interface_link_state = 0 if $network_interface_link_state eq "--";
|
||
|
$network_interface_operational = "unknown" if $network_interface_operational eq "--";
|
||
|
$network_interface_medium = "" if $network_interface_medium eq "--";
|
||
|
$network_interface_speed = 0 if $network_interface_speed eq "--";
|
||
|
$network_interface_mtu = 0 if $network_interface_mtu eq "--";
|
||
7 years ago
|
|
||
|
# Make sure the UUIDs are sane.
|
||
7 years ago
|
if (($network_interface_bond_uuid ne "NULL") && (not $anvil->Validate->is_uuid({uuid => $network_interface_bond_uuid})))
|
||
7 years ago
|
{
|
||
|
# Bad UUID.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0130", variables => { method => "Database->insert_or_update_network_interfaces()", parameter => "network_interface_name", uuid => $network_interface_bond_uuid }});
|
||
7 years ago
|
return("");
|
||
|
}
|
||
7 years ago
|
if (($network_interface_bridge_uuid ne "NULL") && (not $anvil->Validate->is_uuid({uuid => $network_interface_bridge_uuid})))
|
||
7 years ago
|
{
|
||
|
# Bad UUID.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0130", variables => { method => "Database->insert_or_update_network_interfaces()", parameter => "network_interface_name", uuid => $network_interface_bridge_uuid }});
|
||
7 years ago
|
return("");
|
||
|
}
|
||
|
|
||
|
# And INSERT
|
||
7 years ago
|
$network_interface_uuid = $anvil->Get->uuid;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_interface_uuid => $network_interface_uuid }});
|
||
7 years ago
|
|
||
|
my $query = "
|
||
|
INSERT INTO
|
||
|
network_interfaces
|
||
|
(
|
||
|
network_interface_uuid,
|
||
|
network_interface_bond_uuid,
|
||
|
network_interface_bridge_uuid,
|
||
7 years ago
|
network_interface_name,
|
||
7 years ago
|
network_interface_duplex,
|
||
|
network_interface_host_uuid,
|
||
|
network_interface_link_state,
|
||
|
network_interface_operational,
|
||
|
network_interface_mac_address,
|
||
|
network_interface_medium,
|
||
|
network_interface_mtu,
|
||
|
network_interface_speed,
|
||
|
modified_date
|
||
|
) VALUES (
|
||
7 years ago
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_bond_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_bridge_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_name).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_duplex).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_host_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_link_state).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_operational).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_mac_address).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_medium).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_mtu).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($network_interface_speed).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
7 years ago
|
);
|
||
|
";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
|
$anvil->Database->write({query => $query, id => $id, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0126", variables => { method => "Database->insert_or_update_network_interfaces()" }});
|
||
7 years ago
|
return($network_interface_uuid);
|
||
|
}
|
||
|
|
||
|
|
||
8 years ago
|
=head2 insert_or_update_states
|
||
|
|
||
|
This updates (or inserts) a record in the 'states' table. The C<< state_uuid >> referencing the database row will be returned.
|
||
|
|
||
|
If there is an error, an empty string is returned.
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 state_uuid (optional)
|
||
|
|
||
|
This is the C<< state_uuid >> to update. If it is not specified but the C<< state_name >> is, a check will be made to see if an entry already exists. If so, that row will be UPDATEd. If not, a random UUID will be generated and a new entry will be INSERTed.
|
||
|
|
||
|
=head3 state_name (required)
|
||
|
|
||
|
This is the C<< state_name >> to INSERT or UPDATE. If a C<< state_uuid >> is passed, then the C<< state_name >> can be changed.
|
||
|
|
||
|
=head3 state_host_uuid (optional)
|
||
|
|
||
|
This is the host's UUID that this state entry belongs to. If not passed, C<< sys::host_uuid >> will be used.
|
||
|
|
||
|
=head3 state_note (optional)
|
||
|
|
||
|
This is an optional note related to this state entry.
|
||
|
|
||
|
=cut
|
||
|
sub insert_or_update_states
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->insert_or_update_states()" }});
|
||
8 years ago
|
|
||
|
my $state_uuid = $parameter->{state_uuid} ? $parameter->{state_uuid} : "";
|
||
|
my $state_name = $parameter->{state_name} ? $parameter->{state_name} : "";
|
||
7 years ago
|
my $state_host_uuid = $parameter->{state_host_uuid} ? $parameter->{state_host_uuid} : $anvil->data->{sys}{host_uuid};
|
||
8 years ago
|
my $state_note = $parameter->{state_note} ? $parameter->{state_note} : "NULL";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
state_uuid => $state_uuid,
|
||
|
state_name => $state_name,
|
||
|
state_host_uuid => $state_host_uuid,
|
||
|
state_note => $state_note,
|
||
|
}});
|
||
|
|
||
|
if (not $state_name)
|
||
|
{
|
||
|
# Throw an error and exit.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_states()", parameter => "state_name" }});
|
||
8 years ago
|
return("");
|
||
|
}
|
||
|
if (not $state_host_uuid)
|
||
|
{
|
||
|
# Throw an error and exit.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0108"});
|
||
8 years ago
|
return("");
|
||
|
}
|
||
|
|
||
|
# If we don't have a UUID, see if we can find one for the given state server name.
|
||
|
if (not $state_uuid)
|
||
|
{
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
state_uuid
|
||
|
FROM
|
||
|
states
|
||
|
WHERE
|
||
7 years ago
|
state_name = ".$anvil->data->{sys}{use_db_fh}->quote($state_name)."
|
||
8 years ago
|
AND
|
||
7 years ago
|
state_host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($state_host_uuid)."
|
||
8 years ago
|
;";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
$state_uuid = $row->[0];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { state_uuid => $state_uuid }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# If I still don't have an state_uuid, we're INSERT'ing .
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { state_uuid => $state_uuid }});
|
||
8 years ago
|
if (not $state_uuid)
|
||
|
{
|
||
|
# It's possible that this is called before the host is recorded in the database. So to be
|
||
|
# safe, we'll return without doing anything if there is no host_uuid in the database.
|
||
7 years ago
|
my $hosts = $anvil->Database->get_hosts();
|
||
8 years ago
|
my $found = 0;
|
||
|
foreach my $hash_ref (@{$hosts})
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
"hash_ref->{host_uuid}" => $hash_ref->{host_uuid},
|
||
7 years ago
|
"sys::host_uuid" => $anvil->data->{sys}{host_uuid},
|
||
8 years ago
|
}});
|
||
7 years ago
|
if ($hash_ref->{host_uuid} eq $anvil->data->{sys}{host_uuid})
|
||
8 years ago
|
{
|
||
|
$found = 1;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { found => $found }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
if (not $found)
|
||
|
{
|
||
|
# We're out.
|
||
|
return("");
|
||
|
}
|
||
|
|
||
|
# INSERT
|
||
7 years ago
|
$state_uuid = $anvil->Get->uuid();
|
||
8 years ago
|
my $query = "
|
||
|
INSERT INTO
|
||
|
states
|
||
|
(
|
||
|
state_uuid,
|
||
|
state_name,
|
||
|
state_host_uuid,
|
||
|
state_note,
|
||
|
modified_date
|
||
|
) VALUES (
|
||
7 years ago
|
".$anvil->data->{sys}{use_db_fh}->quote($state_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($state_name).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($state_host_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($state_note).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
);
|
||
|
";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
|
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
# Query the rest of the values and see if anything changed.
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
state_name,
|
||
|
state_host_uuid,
|
||
|
state_note
|
||
|
FROM
|
||
|
states
|
||
|
WHERE
|
||
7 years ago
|
state_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($state_uuid)."
|
||
8 years ago
|
;";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
my $old_state_name = $row->[0];
|
||
|
my $old_state_host_uuid = $row->[1];
|
||
|
my $old_state_note = defined $row->[2] ? $row->[2] : "NULL";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
old_state_name => $old_state_name,
|
||
|
old_state_host_uuid => $old_state_host_uuid,
|
||
|
old_state_note => $old_state_note,
|
||
|
}});
|
||
|
|
||
|
# Anything change?
|
||
|
if (($old_state_name ne $state_name) or
|
||
|
($old_state_host_uuid ne $state_host_uuid) or
|
||
|
($old_state_note ne $state_note))
|
||
|
{
|
||
|
# Something changed, save.
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
states
|
||
|
SET
|
||
7 years ago
|
state_name = ".$anvil->data->{sys}{use_db_fh}->quote($state_name).",
|
||
|
state_host_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($state_host_uuid).",
|
||
|
state_note = ".$anvil->data->{sys}{use_db_fh}->quote($state_note).",
|
||
|
modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
WHERE
|
||
7 years ago
|
state_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($state_uuid)."
|
||
8 years ago
|
";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
|
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { state_uuid => $state_uuid }});
|
||
8 years ago
|
return($state_uuid);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 insert_or_update_variables
|
||
|
|
||
|
This updates (or inserts) a record in the 'variables' table. The C<< state_uuid >> referencing the database row will be returned.
|
||
|
|
||
|
Unlike the other methods of this type, this method can be told to update the 'variable_value' only. This is so because the section, description and default columns rarely ever change. If this is set and the variable name is new, an INSERT will be done the same as if it weren't set, with the unset columns set to NULL.
|
||
|
|
||
8 years ago
|
If there is an error, C<< !!error!! >> is returned.
|
||
8 years ago
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 variable_uuid (optional)
|
||
|
|
||
|
If this is passed, the variable will be updated using this UUID, which allows the C<< variable_name >> to be changed.
|
||
|
|
||
|
=head3 variable_name (optional)
|
||
|
|
||
|
This is the name of variable to be inserted or updated.
|
||
|
|
||
|
B<NOTE>: This paramter is only optional if C<< variable_uuid >> is used. Otherwise this parameter is required.
|
||
|
|
||
|
=head3 variable_value (optional)
|
||
|
|
||
|
This is the value to set the variable to. If it is empty, the variable's value will be set to empty.
|
||
|
|
||
|
=head3 variable_default (optional)
|
||
|
|
||
|
If this is set, it changes the default value for the given variable. This is used to tell the user what the default was or enable resetting to defaults.
|
||
|
|
||
|
=head3 variable_description (optional)
|
||
|
|
||
|
This can be set to a string key that explains what this variable does when presenting this variable to a user.
|
||
|
|
||
|
=head3 variable_section (option)
|
||
|
|
||
|
If this is set, it will group this variable with other variables in the same section when displaying this variable to the user.
|
||
|
|
||
|
=head3 variable_source_uuid (optional)
|
||
|
|
||
|
This is an optional field to mark a source UUID that this variable belongs to. By default, a variable applies to everything that reads it, but if this is set, the variable can be restricted to just a given record. This is often used to tag the variable to a particular host by setting the host UUID, but it could also be a UUID of an entry in another database table, when C<< variable_source_table >> is used. Ultimately, this can be used however you want.
|
||
|
|
||
|
=head3 variable_source_table (optional)
|
||
|
|
||
|
This is an optional database table name that the variables relates to. Generally it is used along side C<< variable_source_uuid >>, but that isn't required.
|
||
|
|
||
|
=head3 update_value_only (optional)
|
||
|
|
||
|
When set to C<< 1 >>, this method will only update the variable's C<< variable_value >> column. Any other parameters are used to help locate the variable to update only.
|
||
|
|
||
|
=cut
|
||
|
sub insert_or_update_variables
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->insert_or_update_variables()" }});
|
||
8 years ago
|
|
||
|
my $variable_uuid = defined $parameter->{variable_uuid} ? $parameter->{variable_uuid} : "";
|
||
|
my $variable_name = defined $parameter->{variable_name} ? $parameter->{variable_name} : "";
|
||
|
my $variable_value = defined $parameter->{variable_value} ? $parameter->{variable_value} : "NULL";
|
||
|
my $variable_default = defined $parameter->{variable_default} ? $parameter->{variable_default} : "NULL";
|
||
|
my $variable_description = defined $parameter->{variable_description} ? $parameter->{variable_description} : "NULL";
|
||
|
my $variable_section = defined $parameter->{variable_section} ? $parameter->{variable_section} : "NULL";
|
||
|
my $variable_source_uuid = defined $parameter->{variable_source_uuid} ? $parameter->{variable_source_uuid} : "NULL";
|
||
|
my $variable_source_table = defined $parameter->{variable_source_table} ? $parameter->{variable_source_table} : "NULL";
|
||
|
my $update_value_only = defined $parameter->{update_value_only} ? $parameter->{update_value_only} : 1;
|
||
7 years ago
|
my $log_level = defined $parameter->{log_level} ? $parameter->{log_level} : 3; # Undocumented for now.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => {
|
||
8 years ago
|
variable_uuid => $variable_uuid,
|
||
|
variable_name => $variable_name,
|
||
|
variable_value => $variable_value,
|
||
|
variable_default => $variable_default,
|
||
|
variable_description => $variable_description,
|
||
|
variable_section => $variable_section,
|
||
|
variable_source_uuid => $variable_source_uuid,
|
||
|
variable_source_table => $variable_source_table,
|
||
|
update_value_only => $update_value_only,
|
||
7 years ago
|
log_level => $log_level,
|
||
8 years ago
|
}});
|
||
|
|
||
|
# We'll need either the name or UUID.
|
||
|
if ((not $variable_name) && (not $variable_uuid))
|
||
|
{
|
||
|
# Neither given, throw an error and return.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0037"});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
|
# If we have a variable UUID but not a name, read the variable name. If we don't have a UUID, see if
|
||
|
# we can find one for the given variable name.
|
||
7 years ago
|
if (($anvil->Validate->is_uuid({uuid => $variable_uuid})) && (not $variable_name))
|
||
8 years ago
|
{
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
variable_name
|
||
|
FROM
|
||
|
variables
|
||
|
WHERE
|
||
7 years ago
|
variable_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid);
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
$variable_name = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
8 years ago
|
$variable_name = "" if not defined $variable_name;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { variable_name => $variable_name }});
|
||
8 years ago
|
}
|
||
|
|
||
|
if (($variable_name) && (not $variable_uuid))
|
||
|
{
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
variable_uuid
|
||
|
FROM
|
||
|
variables
|
||
|
WHERE
|
||
7 years ago
|
variable_name = ".$anvil->data->{sys}{use_db_fh}->quote($variable_name);
|
||
8 years ago
|
if (($variable_source_uuid ne "NULL") && ($variable_source_table ne "NULL"))
|
||
|
{
|
||
|
$query .= "
|
||
|
AND
|
||
7 years ago
|
variable_source_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_uuid)."
|
||
8 years ago
|
AND
|
||
7 years ago
|
variable_source_table = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_table)."
|
||
8 years ago
|
";
|
||
|
}
|
||
|
$query .= ";";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
$variable_uuid = $row->[0];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { variable_uuid => $variable_uuid }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# If I still don't have an variable_uuid, we're INSERT'ing .
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { variable_uuid => $variable_uuid }});
|
||
8 years ago
|
if (not $variable_uuid)
|
||
|
{
|
||
|
# INSERT
|
||
7 years ago
|
$variable_uuid = $anvil->Get->uuid();
|
||
8 years ago
|
my $query = "
|
||
|
INSERT INTO
|
||
|
variables
|
||
|
(
|
||
|
variable_uuid,
|
||
|
variable_name,
|
||
|
variable_value,
|
||
|
variable_default,
|
||
|
variable_description,
|
||
|
variable_section,
|
||
|
variable_source_uuid,
|
||
|
variable_source_table,
|
||
|
modified_date
|
||
|
) VALUES (
|
||
7 years ago
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_name).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_value).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_default).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_description).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_section).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_source_uuid).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($variable_source_table).",
|
||
|
".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
);
|
||
|
";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
# Query only the value
|
||
|
if ($update_value_only)
|
||
|
{
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
variable_value
|
||
|
FROM
|
||
|
variables
|
||
|
WHERE
|
||
7 years ago
|
variable_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid);
|
||
8 years ago
|
if (($variable_source_uuid ne "NULL") && ($variable_source_table ne "NULL"))
|
||
|
{
|
||
|
$query .= "
|
||
|
AND
|
||
7 years ago
|
variable_source_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_uuid)."
|
||
8 years ago
|
AND
|
||
7 years ago
|
variable_source_table = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_table)."
|
||
8 years ago
|
";
|
||
|
}
|
||
|
$query .= ";";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
my $old_variable_value = defined $row->[0] ? $row->[0] : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { old_variable_value => $old_variable_value }});
|
||
8 years ago
|
|
||
|
# Anything change?
|
||
|
if ($old_variable_value ne $variable_value)
|
||
|
{
|
||
|
# Variable changed, save.
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
variables
|
||
|
SET
|
||
7 years ago
|
variable_value = ".$anvil->data->{sys}{use_db_fh}->quote($variable_value).",
|
||
|
modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
WHERE
|
||
7 years ago
|
variable_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid);
|
||
8 years ago
|
if (($variable_source_uuid ne "NULL") && ($variable_source_table ne "NULL"))
|
||
|
{
|
||
|
$query .= "
|
||
|
AND
|
||
7 years ago
|
variable_source_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_uuid)."
|
||
8 years ago
|
AND
|
||
7 years ago
|
variable_source_table = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_table)."
|
||
8 years ago
|
";
|
||
|
}
|
||
|
$query .= ";";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Query the rest of the values and see if anything changed.
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
variable_name,
|
||
|
variable_value,
|
||
|
variable_default,
|
||
|
variable_description,
|
||
|
variable_section
|
||
|
FROM
|
||
|
variables
|
||
|
WHERE
|
||
7 years ago
|
variable_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid)."
|
||
8 years ago
|
;";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
my $old_variable_name = $row->[0];
|
||
|
my $old_variable_value = $row->[1] ? $row->[1] : "NULL";
|
||
|
my $old_variable_default = $row->[2] ? $row->[2] : "NULL";
|
||
|
my $old_variable_description = $row->[3] ? $row->[3] : "NULL";
|
||
|
my $old_variable_section = $row->[4] ? $row->[4] : "NULL";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => {
|
||
8 years ago
|
old_variable_name => $old_variable_name,
|
||
|
old_variable_value => $old_variable_value,
|
||
|
old_variable_default => $old_variable_default,
|
||
|
old_variable_description => $old_variable_description,
|
||
|
old_variable_section => $old_variable_section,
|
||
|
}});
|
||
|
|
||
|
# Anything change?
|
||
|
if (($old_variable_name ne $variable_name) or
|
||
|
($old_variable_value ne $variable_value) or
|
||
|
($old_variable_default ne $variable_default) or
|
||
|
($old_variable_description ne $variable_description) or
|
||
|
($old_variable_section ne $variable_section))
|
||
|
{
|
||
|
# Something changed, save.
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
variables
|
||
|
SET
|
||
7 years ago
|
variable_name = ".$anvil->data->{sys}{use_db_fh}->quote($variable_name).",
|
||
|
variable_value = ".$anvil->data->{sys}{use_db_fh}->quote($variable_value).",
|
||
|
variable_default = ".$anvil->data->{sys}{use_db_fh}->quote($variable_default).",
|
||
|
variable_description = ".$anvil->data->{sys}{use_db_fh}->quote($variable_description).",
|
||
|
variable_section = ".$anvil->data->{sys}{use_db_fh}->quote($variable_section).",
|
||
|
modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})."
|
||
8 years ago
|
WHERE
|
||
7 years ago
|
variable_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid)."
|
||
8 years ago
|
";
|
||
|
$query =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { query => $query }});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { variable_uuid => $variable_uuid }});
|
||
8 years ago
|
return($variable_uuid);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 lock_file
|
||
|
|
||
|
This reads, sets or updates the database lock file timestamp.
|
||
8 years ago
|
|
||
8 years ago
|
=cut
|
||
|
sub lock_file
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->lock_file()" }});
|
||
8 years ago
|
|
||
|
my $do = $parameter->{'do'} ? $parameter->{'do'} : "get";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'do' => $do }});
|
||
8 years ago
|
|
||
|
my $lock_time = 0;
|
||
|
if ($do eq "set")
|
||
|
{
|
||
|
$lock_time = time;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { lock_time => $lock_time }});
|
||
|
$anvil->Storage->write_file({
|
||
|
file => $anvil->data->{path}{'lock'}{database},
|
||
8 years ago
|
body => $lock_time,
|
||
|
overwrite => 1,
|
||
|
});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Read the lock file's time stamp, if the file exists.
|
||
7 years ago
|
if (-e $anvil->data->{path}{'lock'}{database})
|
||
8 years ago
|
{
|
||
7 years ago
|
$lock_time = $anvil->Storage->read_file({file => $anvil->data->{path}{'lock'}{database}});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { lock_time => $lock_time }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { lock_time => $lock_time }});
|
||
8 years ago
|
return($lock_time);
|
||
|
}
|
||
8 years ago
|
|
||
8 years ago
|
=head2 locking
|
||
|
|
||
|
This handles requesting, releasing and waiting on locks.
|
||
|
|
||
8 years ago
|
If it is called without any parameters, it will act as a pauser that halts the program until any existing locks are released.
|
||
|
|
||
8 years ago
|
Parameters;
|
||
|
|
||
8 years ago
|
=head3 request (optional)
|
||
|
|
||
|
When set to C<< 1 >>, a log request will be made. If an existing lock exists, it will wait until the existing lock clears before requesting the lock and returning.
|
||
|
|
||
|
=head3 release (optional)
|
||
|
|
||
|
When set to C<< 1 >>, an existing lock held by this machine will be release.
|
||
|
|
||
|
=head3 renew (optional)
|
||
|
|
||
|
When set to C<< 1 >>, an existing lock held by this machine will be renewed.
|
||
|
|
||
|
=head3 check (optional)
|
||
|
|
||
|
This checks to see if a lock is in place and, if it is, the lock string is returned (in the format C<< <host_name>::<source_uuid>::<unix_time_stamp> >> that requested the active lock.
|
||
8 years ago
|
|
||
|
=cut
|
||
|
sub locking
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->locking()" }});
|
||
8 years ago
|
|
||
|
my $request = defined $parameter->{request} ? $parameter->{request} : 0;
|
||
|
my $release = defined $parameter->{release} ? $parameter->{release} : 0;
|
||
|
my $renew = defined $parameter->{renew} ? $parameter->{renew} : 0;
|
||
|
my $check = defined $parameter->{check} ? $parameter->{check} : 0;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
request => $request,
|
||
|
release => $release,
|
||
|
renew => $renew,
|
||
|
check => $check,
|
||
|
}});
|
||
|
|
||
|
# These are used to ID this lock.
|
||
7 years ago
|
my $source_name = $anvil->_hostname;
|
||
|
my $source_uuid = $anvil->data->{sys}{host_uuid};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
source_name => $source_name,
|
||
|
source_uuid => $source_uuid,
|
||
|
}});
|
||
|
|
||
|
my $set = 0;
|
||
|
my $variable_name = "lock_request";
|
||
|
my $variable_value = $source_name."::".$source_uuid."::".time;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
variable_name => $variable_name,
|
||
|
variable_value => $variable_value,
|
||
|
}});
|
||
8 years ago
|
|
||
|
# Make sure we have a sane lock age
|
||
7 years ago
|
if ((not defined $anvil->data->{database}{locking}{reap_age}) or
|
||
|
(not $anvil->data->{database}{locking}{reap_age}) or
|
||
|
($anvil->data->{database}{locking}{reap_age} =~ /\D/)
|
||
8 years ago
|
)
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->data->{database}{locking}{reap_age} = $anvil->data->{defaults}{database}{locking}{reap_age};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "database::locking::reap_age" => $anvil->data->{database}{locking}{reap_age} }});
|
||
8 years ago
|
}
|
||
|
|
||
|
# If I have been asked to check, we will return the variable_uuid if a lock is set.
|
||
|
if ($check)
|
||
|
{
|
||
7 years ago
|
my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => $variable_name});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
lock_value => $lock_value,
|
||
|
variable_uuid => $variable_uuid,
|
||
|
modified_date => $modified_date,
|
||
|
}});
|
||
8 years ago
|
|
||
|
return($lock_value);
|
||
|
}
|
||
|
|
||
|
# If I've been asked to clear a lock, do so now.
|
||
|
if ($release)
|
||
|
{
|
||
|
# 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.
|
||
7 years ago
|
my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => $variable_name});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
lock_value => $lock_value,
|
||
|
variable_uuid => $variable_uuid,
|
||
|
modified_date => $modified_date,
|
||
|
}});
|
||
8 years ago
|
|
||
|
if ($lock_value)
|
||
|
{
|
||
7 years ago
|
my $variable_uuid = $anvil->ScanCore->insert_or_update_variables({
|
||
8 years ago
|
variable_name => $variable_name,
|
||
|
variable_value => "",
|
||
|
update_value_only => 1,
|
||
|
});
|
||
7 years ago
|
$anvil->data->{sys}{database}{local_lock_active} = 0;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
variable_uuid => $variable_uuid,
|
||
7 years ago
|
"sys::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active},
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
8 years ago
|
# Log that the lock has been released.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0039", variables => { host => $anvil->_hostname }});
|
||
8 years ago
|
}
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { set => $set }});
|
||
8 years ago
|
return($set);
|
||
|
}
|
||
|
|
||
|
# If I've been asked to renew, do so now.
|
||
|
if ($renew)
|
||
|
{
|
||
|
# Yup, do it.
|
||
7 years ago
|
my $variable_uuid = $anvil->ScanCore->insert_or_update_variables({
|
||
8 years ago
|
variable_name => $variable_name,
|
||
|
variable_value => $variable_value,
|
||
|
update_value_only => 1,
|
||
|
});
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { variable_uuid => $variable_uuid }});
|
||
8 years ago
|
|
||
|
if ($variable_uuid)
|
||
|
{
|
||
|
$set = 1;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { set => $set }});
|
||
8 years ago
|
}
|
||
7 years ago
|
$anvil->data->{sys}{database}{local_lock_active} = time;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
variable_uuid => $variable_uuid,
|
||
7 years ago
|
"sys::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active},
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
8 years ago
|
# Log that we've renewed the lock.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0044", variables => { host => $anvil->_hostname }});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { set => $set }});
|
||
8 years ago
|
return($set);
|
||
|
}
|
||
|
|
||
8 years ago
|
# We always check for, and then wait for, locks. Read in the locks, if any. If any are set and they are
|
||
|
# younger than database::locking::reap_age, we'll hold.
|
||
8 years ago
|
my $waiting = 1;
|
||
|
while ($waiting)
|
||
|
{
|
||
|
# Set the 'waiting' to '0'. If we find a lock, we'll set it back to '1'.
|
||
|
$waiting = 0;
|
||
|
|
||
|
# See if we had a lock.
|
||
7 years ago
|
my ($lock_value, $variable_uuid, $modified_date) = $anvil->Database->read_variable({variable_name => $variable_name});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
waiting => $waiting,
|
||
|
lock_value => $lock_value,
|
||
|
variable_uuid => $variable_uuid,
|
||
|
modified_date => $modified_date,
|
||
|
}});
|
||
8 years ago
|
if ($lock_value =~ /^(.*?)::(.*?)::(\d+)/)
|
||
|
{
|
||
|
my $lock_source_name = $1;
|
||
|
my $lock_source_uuid = $2;
|
||
|
my $lock_time = $3;
|
||
|
my $current_time = time;
|
||
7 years ago
|
my $timeout_time = $lock_time + $anvil->data->{database}{locking}{reap_age};
|
||
8 years ago
|
my $lock_age = $current_time - $lock_time;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
lock_source_name => $lock_source_name,
|
||
|
lock_source_uuid => $lock_source_uuid,
|
||
|
current_time => $current_time,
|
||
|
lock_time => $lock_time,
|
||
|
timeout_time => $timeout_time,
|
||
|
lock_age => $lock_age,
|
||
|
}});
|
||
8 years ago
|
|
||
|
# If the lock is stale, delete it.
|
||
|
if ($current_time > $timeout_time)
|
||
|
{
|
||
|
# The lock is stale.
|
||
7 years ago
|
my $variable_uuid = $anvil->ScanCore->insert_or_update_variables({
|
||
8 years ago
|
variable_name => $variable_name,
|
||
|
variable_value => "",
|
||
|
update_value_only => 1,
|
||
|
});
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { variable_uuid => $variable_uuid }});
|
||
8 years ago
|
}
|
||
|
# Only wait if this isn't our own lock.
|
||
|
elsif ($lock_source_uuid ne $source_uuid)
|
||
|
{
|
||
|
# Mark 'wait', set inactive and sleep.
|
||
7 years ago
|
$anvil->Database->mark_active({set => 0});
|
||
8 years ago
|
|
||
|
$waiting = 1;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
lock_source_uuid => $lock_source_uuid,
|
||
|
source_uuid => $source_uuid,
|
||
|
waiting => $waiting,
|
||
|
}});
|
||
8 years ago
|
sleep 5;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# If I am here, there are no pending locks. Have I been asked to set one?
|
||
|
if ($request)
|
||
|
{
|
||
|
# Yup, do it.
|
||
7 years ago
|
my $variable_uuid = $anvil->ScanCore->insert_or_update_variables({
|
||
8 years ago
|
variable_name => $variable_name,
|
||
|
variable_value => $variable_value,
|
||
|
update_value_only => 1,
|
||
|
});
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { variable_uuid => $variable_uuid }});
|
||
8 years ago
|
|
||
|
if ($variable_uuid)
|
||
|
{
|
||
|
$set = 1;
|
||
7 years ago
|
$anvil->data->{sys}{database}{local_lock_active} = time;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
set => $set,
|
||
|
variable_uuid => $variable_uuid,
|
||
7 years ago
|
"sys::local_lock_active" => $anvil->data->{sys}{database}{local_lock_active},
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
8 years ago
|
# Log that we've got the lock.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0045", variables => { host => $anvil->_hostname }});
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
# Now return.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { set => $set }});
|
||
8 years ago
|
return($set);
|
||
|
}
|
||
|
|
||
|
=head2 mark_active
|
||
|
|
||
|
This sets or clears that the caller is about to work on the database
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 set (optional, default C<< 1 >>)
|
||
|
|
||
|
If set to c<< 0 >>,
|
||
|
|
||
|
=cut
|
||
|
sub mark_active
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->mark_active()" }});
|
||
8 years ago
|
|
||
|
my $set = defined $parameter->{set} ? $parameter->{set} : 1;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { set => $set }});
|
||
8 years ago
|
|
||
|
# If I haven't connected to a database yet, why am I here?
|
||
7 years ago
|
if (not $anvil->data->{sys}{read_db_id})
|
||
8 years ago
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
my $value = "false";
|
||
|
if ($set)
|
||
|
{
|
||
|
$value = "true";
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { value => $value }});
|
||
8 years ago
|
|
||
7 years ago
|
my $state_uuid = $anvil->Database->insert_or_update_states({
|
||
8 years ago
|
state_name => "db_in_use",
|
||
7 years ago
|
state_host_uuid => $anvil->data->{sys}{host_uuid},
|
||
8 years ago
|
state_note => $value,
|
||
|
});
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { state_uuid => $state_uuid }});
|
||
8 years ago
|
|
||
|
return($state_uuid);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 query
|
||
|
|
||
|
This performs a query and returns an array reference of array references (from C<< DBO->fetchall_arrayref >>). The first array contains all the returned rows and each row is an array reference of columns in that row.
|
||
|
|
||
8 years ago
|
If an error occurs, C<< !!error!! >> will be returned.
|
||
8 years ago
|
|
||
|
For example, given the query;
|
||
|
|
||
|
scancore=# SELECT host_uuid, host_name, host_type FROM hosts ORDER BY host_name ASC;
|
||
|
host_uuid | host_name | host_type
|
||
|
--------------------------------------+--------------------------+-----------
|
||
|
e27fc9a0-2656-4aaf-80e6-fedb3c339037 | an-a01n01.alteeve.com | node
|
||
|
4bea6ddd-c3ff-43e9-8e9e-b2dea1923145 | an-a01n02.alteeve.com | node
|
||
|
ff852db7-c77a-403b-877f-91f85f3ad95c | an-striker01.alteeve.com | dashboard
|
||
|
2dd5aab1-65d6-4416-9bc1-98dc344aa08b | an-striker02.alteeve.com | dashboard
|
||
|
(4 rows)
|
||
|
|
||
|
The returned array would have four values, one for each returned row. Each row would be an array reference containing three values, one per row. So given the above example;
|
||
|
|
||
7 years ago
|
my $rows = $anvil->Database->query({query => "SELECT host_uuid, host_name, host_type FROM hosts ORDER BY host_name ASC;"});
|
||
8 years ago
|
foreach my $columns (@{$results})
|
||
|
{
|
||
|
my $host_uuid = $columns->[0];
|
||
|
my $host_name = $columns->[1];
|
||
|
my $host_type = $columns->[2];
|
||
|
print "Host: [$host_name] (UUID: [$host_uuid], type: [$host_type]).\n";
|
||
|
}
|
||
|
|
||
|
Would print;
|
||
|
|
||
|
Host: [an-a01n01.alteeve.com] (UUID: [e27fc9a0-2656-4aaf-80e6-fedb3c339037], type: [node]).
|
||
|
Host: [an-a01n02.alteeve.com] (UUID: [4bea6ddd-c3ff-43e9-8e9e-b2dea1923145], type: [node]).
|
||
|
Host: [an-striker01.alteeve.com] (UUID: [ff852db7-c77a-403b-877f-91f85f3ad95c], type: [dashboard]).
|
||
|
Host: [an-striker02.alteeve.com] (UUID: [2dd5aab1-65d6-4416-9bc1-98dc344aa08b], type: [dashboard]).
|
||
|
|
||
|
B<NOTE>: Do not sort the array references; They won't make any sense as the references are randomly created pointers. The arrays will be returned in the order of the returned data, so do your sorting in the query itself.
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 id (optional)
|
||
|
|
||
7 years ago
|
By default, the local database will be queried (if run on a machine with a database). Otherwise, the first database successfully connected to will be used for queries (as stored in C<< $anvil->data->{sys}{read_db_id} >>).
|
||
8 years ago
|
|
||
8 years ago
|
If you want to read from a specific database, though, you can set this parameter to the ID of the database (C<< database::<id>::host). If you specify a read from a database that isn't available, C<< !!error!! >> will be returned.
|
||
8 years ago
|
|
||
|
=head3 line (optional)
|
||
|
|
||
|
To help with logging the source of a query, C<< line >> can be set to the line number of the script that requested the query. It is generally used along side C<< source >>.
|
||
|
|
||
|
=head3 query (required)
|
||
|
|
||
|
This is the SQL query to perform.
|
||
|
|
||
7 years ago
|
B<NOTE>: ALWAYS use C<< $anvil->data->{sys}{use_db_fh}->quote(...)>> when preparing data coming from ANY external source! Otherwise you'll end up XKCD 327'ing your database eventually...
|
||
8 years ago
|
|
||
|
=head3 secure (optional, defaul '0')
|
||
|
|
||
7 years ago
|
If set, the query will be treated as containing sensitive data and will only be logged if C<< $anvil->Log->secure >> is enabled.
|
||
8 years ago
|
|
||
|
=head3 source (optional)
|
||
|
|
||
|
To help with logging the source of a query, C<< source >> can be set to the name of the script that requested the query. It is generally used along side C<< line >>.
|
||
|
|
||
|
|
||
|
=cut
|
||
|
sub query
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->query()" }});
|
||
8 years ago
|
|
||
7 years ago
|
my $id = $parameter->{id} ? $parameter->{id} : $anvil->data->{sys}{read_db_id};
|
||
8 years ago
|
my $line = $parameter->{line} ? $parameter->{line} : __LINE__;
|
||
|
my $query = $parameter->{query} ? $parameter->{query} : "";
|
||
|
my $secure = $parameter->{secure} ? $parameter->{secure} : 0;
|
||
|
my $source = $parameter->{source} ? $parameter->{source} : $THIS_FILE;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
id => $id,
|
||
7 years ago
|
"cache::db_fh::${id}" => $anvil->data->{cache}{db_fh}{$id},
|
||
8 years ago
|
line => $line,
|
||
7 years ago
|
query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--",
|
||
8 years ago
|
secure => $secure,
|
||
|
source => $source,
|
||
|
}});
|
||
|
|
||
8 years ago
|
# Make logging code a little cleaner
|
||
7 years ago
|
my $say_server = $anvil->data->{database}{$id}{host}.":".$anvil->data->{database}{$id}{port}." -> ".$anvil->data->{database}{$id}{name};
|
||
8 years ago
|
|
||
8 years ago
|
if (not $id)
|
||
|
{
|
||
|
# No database to talk to...
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0072"});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
7 years ago
|
elsif (not defined $anvil->data->{cache}{db_fh}{$id})
|
||
8 years ago
|
{
|
||
|
# Database handle is gone.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0073", variables => { id => $id }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
if (not $query)
|
||
|
{
|
||
|
# No query
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0084", variables => {
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
}});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
# If I am still alive check if any locks need to be renewed.
|
||
7 years ago
|
$anvil->Database->check_lock_age;
|
||
8 years ago
|
|
||
8 years ago
|
# Do I need to log the transaction?
|
||
7 years ago
|
if ($anvil->data->{sys}{database}{log_transactions})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $source, line => $line, secure => $secure, level => 0, key => "log_0074", variables => {
|
||
8 years ago
|
id => $id,
|
||
8 years ago
|
query => $query,
|
||
|
}});
|
||
|
}
|
||
|
|
||
|
# Test access to the DB before we do the actual query
|
||
7 years ago
|
$anvil->Database->_test_access({id => $id});
|
||
8 years ago
|
|
||
|
# Do the query.
|
||
7 years ago
|
my $DBreq = $anvil->data->{cache}{db_fh}{$id}->prepare($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0075", variables => {
|
||
|
query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--",
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
db_error => $DBI::errstr,
|
||
|
}});
|
||
|
|
||
|
# Execute on the query
|
||
7 years ago
|
$DBreq->execute() or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0076", variables => {
|
||
|
query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--",
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
db_error => $DBI::errstr,
|
||
|
}});
|
||
|
|
||
|
# Return the array
|
||
|
return($DBreq->fetchall_arrayref());
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 read_variable
|
||
|
|
||
|
This reads a variable from the C<< variables >> table. Be sure to only use the reply from here to override what might have been set in a config file. This method always returns the data from the database itself.
|
||
|
|
||
|
The method returns an array reference containing, in order, the variable's value, database UUID and last modified date stamp.
|
||
|
|
||
8 years ago
|
If anything goes wrong, C<< !!error!! >> is returned. If the variable didn't exist in the database, an empty string will be returned for the UUID, value and modified date.
|
||
8 years ago
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 variable_uuid (optional)
|
||
|
|
||
|
If specified, this specifies the variable UUID to read. When this parameter is specified, the C<< variable_name >> parameter is ignored.
|
||
|
|
||
|
=head3 variable_name
|
||
|
|
||
|
=cut
|
||
|
sub read_variable
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->read_variable()" }});
|
||
8 years ago
|
|
||
7 years ago
|
my $variable_uuid = $parameter->{variable_uuid} ? $parameter->{variable_uuid} : "";
|
||
8 years ago
|
my $variable_name = $parameter->{variable_name} ? $parameter->{variable_name} : "";
|
||
|
my $variable_source_uuid = $parameter->{variable_source_uuid} ? $parameter->{variable_source_uuid} : "NULL";
|
||
|
my $variable_source_table = $parameter->{variable_source_table} ? $parameter->{variable_source_table} : "NULL";
|
||
7 years ago
|
my $id = $parameter->{id} ? $parameter->{id} : $anvil->data->{sys}{read_db_id};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
variable_uuid => $variable_uuid,
|
||
|
variable_name => $variable_name,
|
||
|
variable_source_uuid => $variable_source_uuid,
|
||
|
variable_source_table => $variable_source_table,
|
||
|
}});
|
||
|
|
||
|
# Do we have either the
|
||
|
if ((not $variable_name) && (not $variable_uuid))
|
||
|
{
|
||
|
# Throw an error and exit.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0036"});
|
||
8 years ago
|
return("!!error!!", "!!error!!", "!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
|
# If we don't have a UUID, see if we can find one for the given SMTP server name.
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
variable_value,
|
||
|
variable_uuid,
|
||
7 years ago
|
round(extract(epoch from modified_date)) AS mtime
|
||
8 years ago
|
FROM
|
||
|
variables
|
||
|
WHERE ";
|
||
|
if ($variable_uuid)
|
||
|
{
|
||
|
$query .= "
|
||
7 years ago
|
variable_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_uuid);
|
||
8 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
$query .= "
|
||
7 years ago
|
variable_name = ".$anvil->data->{sys}{use_db_fh}->quote($variable_name);
|
||
8 years ago
|
if (($variable_source_uuid ne "NULL") && ($variable_source_table ne "NULL"))
|
||
|
{
|
||
|
$query .= "
|
||
|
AND
|
||
7 years ago
|
variable_source_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_uuid)."
|
||
8 years ago
|
AND
|
||
7 years ago
|
variable_source_table = ".$anvil->data->{sys}{use_db_fh}->quote($variable_source_table)."
|
||
8 years ago
|
";
|
||
|
}
|
||
|
}
|
||
|
$query .= ";";
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0124", variables => { query => $query }});
|
||
8 years ago
|
|
||
8 years ago
|
my $variable_value = "";
|
||
|
my $modified_date = "";
|
||
7 years ago
|
my $results = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__});
|
||
8 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
$variable_value = defined $row->[0] ? $row->[0] : "";
|
||
|
$variable_uuid = $row->[1];
|
||
|
$modified_date = $row->[2];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
variable_value => $variable_value,
|
||
|
variable_uuid => $variable_uuid,
|
||
|
modified_date => $modified_date,
|
||
|
}});
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
variable_value => $variable_value,
|
||
|
variable_uuid => $variable_uuid,
|
||
|
modified_date => $modified_date,
|
||
|
}});
|
||
|
return($variable_value, $variable_uuid, $modified_date);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 resync_databases
|
||
|
|
||
7 years ago
|
This will resync the database data on this and peer database(s) if needed. It takes no arguments and will immediately return unless C<< sys::database::resync_needed >> was set.
|
||
8 years ago
|
|
||
|
=cut
|
||
|
sub resync_databases
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->resync_databases()" }});
|
||
8 years ago
|
|
||
7 years ago
|
# If a resync isn't needed, just return.
|
||
7 years ago
|
if (not $anvil->data->{sys}{database}{resync_needed})
|
||
7 years ago
|
{
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
### NOTE: Don't sort this array, we need to resync in the order that the user passed the tables to us
|
||
|
### to avoid trouble with primary/foreign keys.
|
||
7 years ago
|
# We're going to use the array of tables assembles by _find_behind_databases() stored in
|
||
|
# 'sys::database::check_tables'
|
||
7 years ago
|
foreach my $table (@{$anvil->data->{sys}{database}{check_tables}})
|
||
7 years ago
|
{
|
||
|
# If the 'schema' is 'public', there is no table in the history schema. If there is a host
|
||
|
# column, the resync will be restricted to entries from this host uuid.
|
||
7 years ago
|
my $schema = $anvil->data->{sys}{database}{table}{$table}{schema};
|
||
|
my $host_column = $anvil->data->{sys}{database}{table}{$table}{host_column};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
table => $table,
|
||
|
schema => $schema,
|
||
|
host_column => $host_column,
|
||
|
}});
|
||
|
|
||
7 years ago
|
# If there is a column name that is '<table>_uuid', or the same with the table's name minus
|
||
|
# the last 's', this will be the UUID column to keep records linked in history. We'll need to
|
||
|
# know this off the bat. Tables where we don't find a UUID column won't be sync'ed.
|
||
|
my $column1 = $table."_uuid";
|
||
|
my $column2 = "";
|
||
|
if ($table =~ /^(.*)s$/)
|
||
|
{
|
||
|
$column2 = $1."_uuid";
|
||
|
}
|
||
7 years ago
|
my $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'scancore' AND table_schema = 'public' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name = ".$anvil->data->{sys}{use_db_fh}->quote($column1).";";
|
||
7 years ago
|
if ($column2)
|
||
|
{
|
||
7 years ago
|
$query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = 'scancore' AND table_schema = 'public' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND (column_name = ".$anvil->data->{sys}{use_db_fh}->quote($column1)." OR column_name = ".$anvil->data->{sys}{use_db_fh}->quote($column2).");";
|
||
7 years ago
|
}
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0124", variables => { query => $query }});
|
||
|
my $uuid_column = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
7 years ago
|
$uuid_column = "" if not defined $uuid_column;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { uuid_column => $uuid_column }});
|
||
7 years ago
|
next if not $uuid_column;
|
||
|
|
||
7 years ago
|
# Get all the columns in this table.
|
||
7 years ago
|
$query = "SELECT column_name, is_nullable, data_type FROM information_schema.columns WHERE table_schema = ".$anvil->data->{sys}{use_db_fh}->quote($schema)." AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table)." AND column_name != 'history_id';";
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0124", variables => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
|
my $column_name = $row->[0];
|
||
|
my $not_null = $row->[1] eq "NO" ? 1 : 0;
|
||
|
my $data_type = $row->[2];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
column_name => $column_name,
|
||
|
not_null => $not_null,
|
||
|
data_type => $data_type,
|
||
|
}});
|
||
|
|
||
7 years ago
|
$anvil->data->{sys}{database}{table}{$table}{column}{$column_name}{not_null} = $not_null;
|
||
|
$anvil->data->{sys}{database}{table}{$table}{column}{$column_name}{data_type} = $data_type;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::table::${table}::column::${column_name}::not_null" => $anvil->data->{sys}{database}{table}{$table}{column}{$column_name}{not_null},
|
||
|
"sys::database::table::${table}::column::${column_name}::data_type" => $anvil->data->{sys}{database}{table}{$table}{column}{$column_name}{data_type},
|
||
7 years ago
|
}});
|
||
|
}
|
||
|
|
||
|
# Now read in the data from the different databases.
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{cache}{db_fh}})
|
||
7 years ago
|
{
|
||
|
# ...
|
||
7 years ago
|
$anvil->data->{db_resync}{$id}{public}{sql} = [];
|
||
|
$anvil->data->{db_resync}{$id}{history}{sql} = [];
|
||
7 years ago
|
|
||
7 years ago
|
# Read in the data, modified_date first as we'll need that for all entries we record.
|
||
7 years ago
|
my $query = "SELECT modified_date AT time zone 'UTC', $uuid_column, ";
|
||
7 years ago
|
my $read_columns = [];
|
||
7 years ago
|
push @{$read_columns}, "modified_date";
|
||
7 years ago
|
push @{$read_columns}, $uuid_column;
|
||
7 years ago
|
foreach my $column_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{database}{table}{$table}{column}})
|
||
7 years ago
|
{
|
||
|
# We'll skip the host column as we'll use it in the conditional.
|
||
|
next if $column_name eq "modified_date";
|
||
7 years ago
|
next if $column_name eq $host_column;
|
||
|
next if $column_name eq $uuid_column;
|
||
7 years ago
|
$query .= $column_name.", ";
|
||
|
|
||
|
push @{$read_columns}, $column_name;
|
||
|
}
|
||
|
|
||
7 years ago
|
# Strip the last comma and the add the schema.table name.
|
||
|
$query =~ s/, $/ /;
|
||
|
$query .= "FROM ".$schema.".".$table;
|
||
7 years ago
|
|
||
|
# Restrict to this host if a host column was found.
|
||
|
if ($host_column)
|
||
|
{
|
||
7 years ago
|
$query .= " WHERE ".$host_column." = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{host_uuid});
|
||
7 years ago
|
}
|
||
7 years ago
|
$query .= " ORDER BY modified_date DESC;";
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0074", variables => { id => $id, query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $results = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
my $count = @{$results};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
results => $results,
|
||
|
count => $count,
|
||
|
}});
|
||
|
next if not $count;
|
||
|
|
||
|
foreach my $row (@{$results})
|
||
|
{
|
||
7 years ago
|
my $modified_date = "";
|
||
|
my $row_uuid = "";
|
||
7 years ago
|
for (my $i = 0; $i < @{$read_columns}; $i++)
|
||
|
{
|
||
|
my $column_name = $read_columns->[$i];
|
||
|
my $column_value = defined $row->[$i] ? $row->[$i] : "NULL";
|
||
7 years ago
|
my $not_null = $anvil->data->{sys}{database}{table}{$table}{column}{$column_name}{not_null};
|
||
|
my $data_type = $anvil->data->{sys}{database}{table}{$table}{column}{$column_name}{data_type};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
"s1:i" => $i,
|
||
|
"s2:column_name" => $column_name,
|
||
|
"s3:column_value" => $column_value,
|
||
|
"s4:not_null" => $not_null,
|
||
|
"s5:data_type" => $data_type,
|
||
7 years ago
|
}});
|
||
|
if ((not $not_null) && ($column_value eq "NULL"))
|
||
|
{
|
||
|
$column_value = "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { column_value => $column_value }});
|
||
7 years ago
|
}
|
||
7 years ago
|
|
||
|
# The modified_date should be the first row.
|
||
|
if ($column_name eq "modified_date")
|
||
|
{
|
||
|
$modified_date = $column_value;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { modified_date => $modified_date }});
|
||
7 years ago
|
next;
|
||
|
}
|
||
|
|
||
|
# The row's UUID should be the second row.
|
||
|
if ($column_name eq $uuid_column)
|
||
|
{
|
||
|
$row_uuid = $column_value;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { row_uuid => $row_uuid }});
|
||
7 years ago
|
|
||
|
# This is used to determine if a given entry needs to be
|
||
|
# updated or inserted into the public schema
|
||
7 years ago
|
$anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{'exists'} = 1;
|
||
|
$anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{seen} = 0;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::${id}::${table}::${uuid_column}::${row_uuid}::exists" => $anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{'exists'},
|
||
|
"db_data::${id}::${table}::${uuid_column}::${row_uuid}::seen" => $anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{seen},
|
||
7 years ago
|
}});
|
||
|
next;
|
||
|
}
|
||
|
|
||
|
die $THIS_FILE." ".__LINE__."; This row's modified_date wasn't the first column returned in query: [$query]\n" if not $modified_date;
|
||
7 years ago
|
die $THIS_FILE." ".__LINE__."; This row's UUID column: [$uuid_column] wasn't the second column returned in query: [$query]\n" if not $row_uuid;
|
||
7 years ago
|
|
||
7 years ago
|
# Record this in the unified and local hashes. # This table isn't restricted to given hosts.
|
||
7 years ago
|
$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name} = $column_value;
|
||
|
$anvil->data->{db_data}{$id}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name} = $column_value;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::unified::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}::${column_name}" => $anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name},
|
||
|
"db_data::${id}::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}::${column_name}" => $anvil->data->{db_data}{$id}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name},
|
||
7 years ago
|
}});
|
||
7 years ago
|
}
|
||
|
}
|
||
7 years ago
|
}
|
||
|
|
||
|
# Now all the data is read in, we can see what might be missing from each DB.
|
||
7 years ago
|
foreach my $modified_date (sort {$b cmp $a} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { modified_date => $modified_date }});
|
||
7 years ago
|
|
||
7 years ago
|
foreach my $row_uuid (sort {$a cmp $b} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { row_uuid => $row_uuid }});
|
||
7 years ago
|
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{cache}{db_fh}})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
7 years ago
|
|
||
7 years ago
|
# For each 'row_uuid' we see;
|
||
|
# - Check if we've *seen* it before
|
||
|
# |- If not seen; See if it *exists* in the public schema yet.
|
||
|
# | |- If so, check to see if the entry in the public schema is up to date.
|
||
|
# | | \- If not, _UPDATE_ public schema.
|
||
|
# | \- If not, do an _INSERT_ into public schema.
|
||
|
# \- If we have seen, see if it exists at the current timestamp.
|
||
|
# \- If not, _INSERT_ it into history schema.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::${id}::${table}::${uuid_column}::${row_uuid}::seen" => $anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{seen}
|
||
7 years ago
|
}});
|
||
7 years ago
|
if (not $anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{seen})
|
||
7 years ago
|
{
|
||
7 years ago
|
# Mark this record as now having been seen.
|
||
7 years ago
|
$anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{seen} = 1;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::${id}::${table}::${uuid_column}::${row_uuid}::seen" => $anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{seen}
|
||
7 years ago
|
}});
|
||
7 years ago
|
|
||
|
# Does it exist?
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::${id}::${table}::${uuid_column}::${row_uuid}::exists" => $anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{'exists'}
|
||
7 years ago
|
}});
|
||
7 years ago
|
if ($anvil->data->{db_data}{$id}{$table}{$uuid_column}{$row_uuid}{'exists'})
|
||
7 years ago
|
{
|
||
7 years ago
|
# It exists, but does it exist at this time stamp?
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::${id}::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}" => $anvil->data->{db_data}{$id}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid},
|
||
7 years ago
|
}});
|
||
7 years ago
|
if (not $anvil->data->{db_data}{$id}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid})
|
||
7 years ago
|
{
|
||
7 years ago
|
# No, so UPDATE it. We'll build the query now...
|
||
|
my $query = "UPDATE public.$table SET ";
|
||
7 years ago
|
foreach my $column_name (sort {$a cmp $b} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}})
|
||
7 years ago
|
{
|
||
7 years ago
|
my $column_value = $anvil->data->{sys}{use_db_fh}->quote($anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name});
|
||
7 years ago
|
$column_value =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
column_name => $column_name,
|
||
|
column_value => $column_value,
|
||
|
}});
|
||
7 years ago
|
|
||
7 years ago
|
$query .= "$column_name = ".$anvil->data->{sys}{use_db_fh}->quote().", ";
|
||
7 years ago
|
}
|
||
7 years ago
|
$query .= "modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($modified_date)."::timestamp AT TIME ZONE 'UTC' WHERE $uuid_column = ".$anvil->data->{sys}{use_db_fh}->quote($row_uuid).";";
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0074", variables => { id => $id, query => $query }});
|
||
7 years ago
|
|
||
|
# Now record the query in the array
|
||
7 years ago
|
push @{$anvil->data->{db_resync}{$id}{public}{sql}}, $query;
|
||
7 years ago
|
} # if not exists - timestamp
|
||
|
} # if exists
|
||
7 years ago
|
else
|
||
|
{
|
||
7 years ago
|
# It doesn't exist, so INSERT it. We need to
|
||
|
# build entries for the column names and
|
||
|
# values at the same time to make certain
|
||
|
# they're in the same order.
|
||
|
my $columns = "";
|
||
|
my $values = "";
|
||
7 years ago
|
foreach my $column_name (sort {$a cmp $b} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}})
|
||
7 years ago
|
{
|
||
7 years ago
|
my $column_value = $anvil->data->{sys}{use_db_fh}->quote($anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name});
|
||
7 years ago
|
$column_value =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
column_name => $column_name,
|
||
|
column_value => $column_value,
|
||
|
}});
|
||
|
$columns .= $column_name.", ";
|
||
|
$values .= $column_value.", ";
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
columns => $columns,
|
||
|
'values' => $values,
|
||
7 years ago
|
}});
|
||
7 years ago
|
|
||
7 years ago
|
my $query = "INSERT INTO public.$table (".$uuid_column.", ".$columns."modified_date) VALUES (".$anvil->data->{sys}{use_db_fh}->quote($row_uuid).", ".$values.$anvil->data->{sys}{use_db_fh}->quote($modified_date)."::timestamp AT TIME ZONE 'UTC');";
|
||
7 years ago
|
if ($host_column)
|
||
|
{
|
||
|
# Add the host column.
|
||
7 years ago
|
$query = "INSERT INTO public.$table ($host_column, $uuid_column, ".$columns."modified_date) VALUES (".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{host_uuid}).", ".$anvil->data->{sys}{use_db_fh}->quote($row_uuid).", ".$values.$anvil->data->{sys}{use_db_fh}->quote($modified_date)."::timestamp AT TIME ZONE 'UTC');";
|
||
7 years ago
|
}
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0074", variables => { id => $id, query => $query }});
|
||
7 years ago
|
|
||
|
# Now record the query in the array
|
||
7 years ago
|
push @{$anvil->data->{db_resync}{$id}{public}{sql}}, $query;
|
||
7 years ago
|
} # if not exists
|
||
|
} # if not seen
|
||
|
else
|
||
|
{
|
||
|
### NOTE: If the table doesn't have a history schema,
|
||
|
### we skip this.
|
||
|
next if $schema eq "public";
|
||
|
|
||
|
# We've seen this row_uuid before, so it is just a
|
||
|
# question of whether the entry for the current
|
||
|
# timestamp exists in the history schema.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"db_data::${id}::${table}::modified_date::${modified_date}::${uuid_column}::${row_uuid}" => $anvil->data->{db_data}{$id}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid},
|
||
7 years ago
|
}});
|
||
7 years ago
|
if (not $anvil->data->{db_data}{$id}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid})
|
||
7 years ago
|
{
|
||
|
# It hasn't been seen, so INSERT it. We need
|
||
|
# to build entries for the column names and
|
||
|
# values at the same time to make certain
|
||
|
# they're in the same order.
|
||
|
my $columns = "";
|
||
|
my $values = "";
|
||
7 years ago
|
foreach my $column_name (sort {$a cmp $b} keys %{$anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}})
|
||
7 years ago
|
{
|
||
7 years ago
|
my $column_value = $anvil->data->{sys}{use_db_fh}->quote($anvil->data->{db_data}{unified}{$table}{modified_date}{$modified_date}{$uuid_column}{$row_uuid}{$column_name});
|
||
7 years ago
|
$column_value =~ s/'NULL'/NULL/g;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
column_name => $column_name,
|
||
|
column_value => $column_value,
|
||
7 years ago
|
}});
|
||
7 years ago
|
$columns .= $column_name.", ";
|
||
|
$values .= $column_value.", ";
|
||
|
}
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
columns => $columns,
|
||
|
'values' => $values,
|
||
|
}});
|
||
|
|
||
7 years ago
|
my $query = "INSERT INTO history.$table (".$uuid_column.", ".$columns."modified_date) VALUES (".$anvil->data->{sys}{use_db_fh}->quote($row_uuid).", ".$values.$anvil->data->{sys}{use_db_fh}->quote($modified_date)."::timestamp AT TIME ZONE 'UTC');";
|
||
7 years ago
|
if ($host_column)
|
||
|
{
|
||
|
# Add the host column.
|
||
7 years ago
|
$query = "INSERT INTO history.$table ($host_column, $uuid_column, ".$columns."modified_date) VALUES (".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{host_uuid}).", ".$anvil->data->{sys}{use_db_fh}->quote($row_uuid).", ".$values.$anvil->data->{sys}{use_db_fh}->quote($modified_date)."::timestamp AT TIME ZONE 'UTC');";
|
||
7 years ago
|
}
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0074", variables => { id => $id, query => $query }});
|
||
7 years ago
|
|
||
|
# Now record the query in the array
|
||
7 years ago
|
push @{$anvil->data->{db_resync}{$id}{history}{sql}}, $query;
|
||
7 years ago
|
} # if not exists - timestamp
|
||
|
} # if seen
|
||
|
} # foreach $id
|
||
|
} # foreach $row_uuid
|
||
|
} # foreach $modified_date ...
|
||
|
|
||
|
# Free up memory by deleting the DB data from the main hash.
|
||
7 years ago
|
delete $anvil->data->{db_data};
|
||
7 years ago
|
|
||
|
# Do the INSERTs now and then release the memory.
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{cache}{db_fh}})
|
||
7 years ago
|
{
|
||
|
# Merge the queries for both schemas into one array, with public schema
|
||
|
# queries being first, then delete the arrays holding them to free memory
|
||
|
# before we start the resync.
|
||
|
my $merged = [];
|
||
7 years ago
|
@{$merged} = (@{$anvil->data->{db_resync}{$id}{public}{sql}}, @{$anvil->data->{db_resync}{$id}{history}{sql}});
|
||
|
undef $anvil->data->{db_resync}{$id}{public}{sql};
|
||
|
undef $anvil->data->{db_resync}{$id}{history}{sql};
|
||
7 years ago
|
|
||
7 years ago
|
# If the merged array has any entries, push them in.
|
||
7 years ago
|
if (@{$merged} > 0)
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Database->write({id => $id, query => $merged, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
undef $merged;
|
||
7 years ago
|
}
|
||
7 years ago
|
}
|
||
7 years ago
|
} # foreach my $table
|
||
|
|
||
8 years ago
|
# Show tables;
|
||
|
# SELECT table_schema, table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') ORDER BY table_name ASC, table_schema DESC;
|
||
|
|
||
|
# Show columns;
|
||
|
# SELECT table_catalog, table_schema, table_name, column_name, column_default, is_nullable, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'alerts';
|
||
|
|
||
|
# psql -E scancore <<-- LOVE <3
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
8 years ago
|
|
||
8 years ago
|
=head2 write
|
||
|
|
||
|
This records data to one or all of the databases. If an ID is passed, the query is written to one database only. Otherwise, it will be written to all DBs.
|
||
|
|
||
|
=cut
|
||
|
sub write
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->write()" }});
|
||
8 years ago
|
|
||
7 years ago
|
my $id = $parameter->{id} ? $parameter->{id} : "";
|
||
8 years ago
|
my $line = $parameter->{line} ? $parameter->{line} : __LINE__;
|
||
|
my $query = $parameter->{query} ? $parameter->{query} : "";
|
||
|
my $secure = $parameter->{secure} ? $parameter->{secure} : 0;
|
||
|
my $source = $parameter->{source} ? $parameter->{source} : $THIS_FILE;
|
||
|
my $reenter = $parameter->{reenter} ? $parameter->{reenter} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
id => $id,
|
||
7 years ago
|
"cache::db_fh::${id}" => $anvil->data->{cache}{db_fh}{$id},
|
||
8 years ago
|
line => $line,
|
||
7 years ago
|
query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--",
|
||
8 years ago
|
secure => $secure,
|
||
|
source => $source,
|
||
8 years ago
|
reenter => $reenter,
|
||
8 years ago
|
}});
|
||
|
|
||
8 years ago
|
# Make logging code a little cleaner
|
||
7 years ago
|
my $say_server = $id eq "" ? "#!string!log_0129!#" : $anvil->data->{database}{$id}{host}.":".$anvil->data->{database}{$id}{port}." -> ".$anvil->data->{database}{$id}{name};
|
||
7 years ago
|
#print "id: [$id], say_server: [$say_server]\n";
|
||
8 years ago
|
|
||
8 years ago
|
# We don't check if ID is set here because not being set simply means to write to all available DBs.
|
||
|
if (not $query)
|
||
|
{
|
||
|
# No query
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0085", variables => { server => $say_server }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
# If I am still alive check if any locks need to be renewed.
|
||
7 years ago
|
$anvil->Database->check_lock_age;
|
||
8 years ago
|
|
||
|
# This array will hold either just the passed DB ID or all of them, if no ID was specified.
|
||
|
my @db_ids;
|
||
|
if ($id)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
8 years ago
|
push @db_ids, $id;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{cache}{db_fh}})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
8 years ago
|
push @db_ids, $id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Sort out if I have one or many queries.
|
||
|
my $limit = 25000;
|
||
|
my $count = 0;
|
||
|
my $query_set = [];
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::database::maximum_batch_size" => $anvil->data->{sys}{database}{maximum_batch_size} }});
|
||
|
if ($anvil->data->{sys}{database}{maximum_batch_size})
|
||
8 years ago
|
{
|
||
7 years ago
|
if ($anvil->data->{sys}{database}{maximum_batch_size} =~ /\D/)
|
||
8 years ago
|
{
|
||
|
# Bad value.
|
||
7 years ago
|
$anvil->data->{sys}{database}{maximum_batch_size} = 25000;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::database::maximum_batch_size" => $anvil->data->{sys}{database}{maximum_batch_size} }});
|
||
8 years ago
|
}
|
||
|
|
||
|
# Use the set value now.
|
||
7 years ago
|
$limit = $anvil->data->{sys}{database}{maximum_batch_size};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { limit => $limit }});
|
||
8 years ago
|
}
|
||
|
if (ref($query) eq "ARRAY")
|
||
|
{
|
||
|
# Multiple things to enter.
|
||
|
$count = @{$query};
|
||
|
|
||
|
# If I am re-entering, then we'll proceed normally. If not, and if we have more than 10k
|
||
|
# queries, we'll split up the queries into 10k chunks and re-enter.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
count => $count,
|
||
8 years ago
|
limit => $limit,
|
||
8 years ago
|
reenter => $reenter,
|
||
|
}});
|
||
|
if (($count > $limit) && (not $reenter))
|
||
|
{
|
||
|
my $i = 0;
|
||
|
my $next = $limit;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { i => $i, 'next' => $next }});
|
||
8 years ago
|
foreach my $this_query (@{$query})
|
||
|
{
|
||
|
push @{$query_set}, $this_query;
|
||
|
$i++;
|
||
|
|
||
|
if ($i > $next)
|
||
|
{
|
||
|
# Commit this batch.
|
||
|
foreach my $id (@db_ids)
|
||
|
{
|
||
|
# Commit this chunk to this DB.
|
||
7 years ago
|
$anvil->Database->write({id => $id, query => $query_set, source => $THIS_FILE, line => $line, reenter => 1});
|
||
8 years ago
|
|
||
|
### TODO: Rework this so that we exit here (so that we can
|
||
|
### send an alert) if the RAM use is too high.
|
||
|
# This can get memory intensive, so check our RAM usage and
|
||
|
# bail if we're eating too much.
|
||
7 years ago
|
my $ram_use = $anvil->System->check_memory({program_name => $THIS_FILE});
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ram_use => $ram_use }});
|
||
8 years ago
|
|
||
|
# Wipe out the old set array, create it as a new anonymous array and reset 'i'.
|
||
|
undef $query_set;
|
||
|
$query_set = [];
|
||
|
$i = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Not enough to worry about or we're dealing with a chunk, proceed as normal.
|
||
|
foreach my $this_query (@{$query})
|
||
|
{
|
||
|
push @{$query_set}, $this_query;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
push @{$query_set}, $query;
|
||
|
}
|
||
|
foreach my $id (@db_ids)
|
||
|
{
|
||
|
# Test access to the DB before we do the actual query
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
|
$anvil->Database->_test_access({id => $id});
|
||
8 years ago
|
|
||
|
# Do the actual query(ies)
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
id => $id,
|
||
|
count => $count,
|
||
|
}});
|
||
|
if ($count)
|
||
|
{
|
||
|
# More than one query, so start a transaction block.
|
||
7 years ago
|
$anvil->data->{cache}{db_fh}{$id}->begin_work;
|
||
8 years ago
|
}
|
||
|
|
||
|
foreach my $query (@{$query_set})
|
||
|
{
|
||
7 years ago
|
if ($anvil->data->{sys}{database}{log_transactions})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $source, line => $line, secure => $secure, level => 0, key => "log_0083", variables => {
|
||
7 years ago
|
id => $id,
|
||
|
query => $query,
|
||
|
}});
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
if (not $anvil->data->{cache}{db_fh}{$id})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0089", variables => { id => $id }});
|
||
8 years ago
|
next;
|
||
|
}
|
||
|
|
||
|
# Do the do.
|
||
7 years ago
|
$anvil->data->{cache}{db_fh}{$id}->do($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0090", variables => {
|
||
|
query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--",
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
db_error => $DBI::errstr,
|
||
|
}});
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
8 years ago
|
if ($count)
|
||
|
{
|
||
|
# Commit the changes.
|
||
7 years ago
|
$anvil->data->{cache}{db_fh}{$id}->commit();
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
8 years ago
|
if ($count)
|
||
|
{
|
||
|
# Free up some memory.
|
||
|
undef $query_set;
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
8 years ago
|
|
||
|
# =head3
|
||
|
#
|
||
|
# Private Functions;
|
||
|
#
|
||
|
# =cut
|
||
|
|
||
|
#############################################################################################################
|
||
|
# Private functions #
|
||
|
#############################################################################################################
|
||
8 years ago
|
|
||
7 years ago
|
=head2 _archive_table
|
||
|
|
||
|
NOTE: Not implemented yet (will do so once enough records are in the DB.)
|
||
|
|
||
|
This takes a table name
|
||
|
|
||
|
This takes a table to check to see if old records need to be archived the data from the history schema to a plain-text dump.
|
||
|
|
||
|
B<NOTE>: If we're asked to use an offset that is too high, we'll go into a loop and may end up doing some empty loops. We don't check to see if the offset is sensible, though setting it too high won't cause the archive operation to fail, but it won't chunk as expected.
|
||
|
|
||
|
B<NOTE>: The archive process works on all records, B<NOT> restricted to records referencing this host via a C<< *_host_uuid >> column.
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 table <required>
|
||
|
|
||
|
This is the table that will be archived, if needed.
|
||
|
|
||
|
An archive will be deemed required if there are more than C<< sys::database::archive::trigger >> records (default is C<< 100000 >>) in the table's C<< history >> schema. If this is set to C<< 0 >>, archiving will be disabled.
|
||
|
|
||
|
Individual tables can have custom triggers by setting C<< sys::database::archive::tables::<table>::trigger >>.
|
||
|
|
||
|
=cut
|
||
|
sub _archive_table
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->_archive_table()" }});
|
||
7 years ago
|
|
||
|
my $table = $parameter->{table} ? $parameter->{table} : "";
|
||
|
my $offset = $parameter->{offset} ? $parameter->{offset} : 0;
|
||
|
my $loop = $parameter->{loop} ? $parameter->{loop} : 0;
|
||
7 years ago
|
my $division = $parameter->{division} ? $parameter->{division} : $anvil->data->{sys}{database}{archive}{division};
|
||
|
my $compress = $parameter->{compress} ? $parameter->{compress} : $anvil->data->{sys}{database}{archive}{compress};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
table => $table,
|
||
|
offset => $offset,
|
||
|
loop => $loop,
|
||
|
division => $division,
|
||
|
compress => $compress,
|
||
|
}});
|
||
|
|
||
|
if (not $table)
|
||
|
{
|
||
|
# ...
|
||
|
return("!!error!!");
|
||
|
}
|
||
|
|
||
|
# Has the user disabled archiving?
|
||
7 years ago
|
my $trigger = $anvil->data->{sys}{database}{archive}{trigger};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { trigger => $trigger }});
|
||
|
if ((exists $anvil->data->{sys}{database}{archive}{tables}{$table}{division}) && ($anvil->data->{sys}{database}{archive}{tables}{$table}{division} =~ /^\d+$/))
|
||
7 years ago
|
{
|
||
7 years ago
|
$trigger = $anvil->data->{sys}{database}{archive}{tables}{$table}{division};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { trigger => $trigger }});
|
||
7 years ago
|
}
|
||
|
if ($trigger)
|
||
|
{
|
||
|
# Archiving is disabled.
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
# First, if this table doesn't have a history schema, exit.
|
||
7 years ago
|
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = 'history' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table).";";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
7 years ago
|
if (not $count)
|
||
|
{
|
||
|
# History table doesn't exist, we're done.
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
# Before we do any real analysis, do we have enough entries in the history schema to trigger an archive?
|
||
|
$query = "SELECT COUNT(*) FROM history.".$table.";";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
$count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
7 years ago
|
if ($count <= $trigger)
|
||
|
{
|
||
|
# History table doesn't exist, we're done.
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
# There is enough data to trigger an archive, so lets get started with a list of columns in this
|
||
|
# table.
|
||
7 years ago
|
$query = "SELECT column_name FROM information_schema.columns WHERE table_schema = 'history' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table)." AND column_name != 'history_id' AND column_name != 'modified_date';";
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0124", variables => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $columns = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
|
||
7 years ago
|
my $column_count = @{$columns};
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
7 years ago
|
columns => $columns,
|
||
|
column_count => $column_count
|
||
|
}});
|
||
|
|
||
|
print Dumper $columns;
|
||
|
|
||
|
# See m2's DB->archive_if_needed() for old version of this.
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 _find_behind_databases
|
||
|
|
||
|
This returns the most up to date database ID, the time it was last updated and an array or DB IDs that are behind.
|
||
|
|
||
8 years ago
|
If there is a problem, C<< !!error!! >> is returned.
|
||
8 years ago
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 source (required)
|
||
|
|
||
|
This is used the same as in C<< Database->connect >>'s C<< source >> parameter. Please read that for usage information.
|
||
|
|
||
|
=head3 tables (optional)
|
||
|
|
||
|
This is used the same as in C<< Database->connect >>'s C<< tables >> parameter. Please read that for usage information.
|
||
|
|
||
|
=cut
|
||
|
sub _find_behind_databases
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->_find_behind_databases()" }});
|
||
8 years ago
|
|
||
|
my $source = $parameter->{source} ? $parameter->{source} : "";
|
||
|
my $tables = $parameter->{tables} ? $parameter->{tables} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
source => $source,
|
||
|
tables => $tables,
|
||
|
}});
|
||
|
|
||
|
# This should always be set, but just in case...
|
||
|
if (not $source)
|
||
|
{
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->_find_behind_databases()", parameter => "source" }});
|
||
8 years ago
|
return("!!error!!");
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
# Now, look through the core tables, plus any tables the user might have passed, for differing
|
||
|
# 'modified_date' entries, or no entries in one DB with entries in the other (as can happen with a
|
||
|
# newly setup db).
|
||
7 years ago
|
$anvil->data->{sys}{database}{check_tables} = [];
|
||
|
foreach my $table (@{$anvil->data->{sys}{database}{core_tables}})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { table => $table }});
|
||
|
push @{$anvil->data->{sys}{database}{check_tables}}, $table;
|
||
7 years ago
|
}
|
||
|
if (ref($tables) eq "ARRAY")
|
||
|
{
|
||
|
foreach my $table (@{$tables})
|
||
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { table => $table }});
|
||
|
push @{$anvil->data->{sys}{database}{check_tables}}, $table;
|
||
7 years ago
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
# Preset all tables to have an initial 'modified_date' of 0.
|
||
7 years ago
|
foreach my $table (sort {$a cmp $b} @{$anvil->data->{sys}{database}{check_tables}})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{database}{table}{$table}{last_updated} = 0;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::table::${table}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{last_updated},
|
||
7 years ago
|
}});
|
||
|
}
|
||
|
|
||
8 years ago
|
# Look at all the databases and find the most recent time stamp (and the ID of the DB).
|
||
7 years ago
|
my $source_updated_time = 0;
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{database}})
|
||
|
{
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"database::${id}::host" => $anvil->data->{database}{$id}{host},
|
||
|
"database::${id}::port" => $anvil->data->{database}{$id}{port},
|
||
|
"database::${id}::name" => $anvil->data->{database}{$id}{name},
|
||
|
"database::${id}::user" => $anvil->data->{database}{$id}{user},
|
||
|
"database::${id}::password" => $anvil->Log->secure ? $anvil->data->{database}{$id}{password} : "--",
|
||
7 years ago
|
}});
|
||
|
|
||
7 years ago
|
# Loop through the tables in this DB. For each table, we'll record the most recent time
|
||
|
# stamp. Later, We'll look through again and any table/DB with an older time stamp will be
|
||
|
# behind and a resync will be needed.
|
||
7 years ago
|
foreach my $table (@{$anvil->data->{sys}{database}{check_tables}})
|
||
8 years ago
|
{
|
||
7 years ago
|
# Does this table exist yet?
|
||
7 years ago
|
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = 'public' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table).";";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $count = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
7 years ago
|
|
||
|
if ($count == 1)
|
||
8 years ago
|
{
|
||
7 years ago
|
# Does this table have a '*_host_uuid' column?
|
||
7 years ago
|
my $query = "SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND column_name LIKE '\%_host_uuid' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table).";";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
# See if there is a column that ends in '_host_uuid'. If there is, we'll use
|
||
|
# it later to restrict resync activity to these columns with the local
|
||
|
# 'sys::host_uuid'.
|
||
7 years ago
|
my $host_column = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
7 years ago
|
$host_column = "" if not defined $host_column;
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { host_column => $host_column }});
|
||
7 years ago
|
|
||
|
# Does this table have a history schema version?
|
||
7 years ago
|
$query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = 'history' AND table_name = ".$anvil->data->{sys}{use_db_fh}->quote($table).";";
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
|
||
7 years ago
|
|
||
7 years ago
|
my $count = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { count => $count }});
|
||
7 years ago
|
|
||
|
my $schema = $count ? "history" : "public";
|
||
7 years ago
|
$query = "
|
||
8 years ago
|
SELECT
|
||
|
round(extract(epoch from modified_date))
|
||
|
FROM
|
||
7 years ago
|
$schema.$table ";
|
||
8 years ago
|
if ($host_column)
|
||
|
{
|
||
|
$query .= "
|
||
|
WHERE
|
||
7 years ago
|
$host_column = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{host_uuid}) ;
|
||
8 years ago
|
}
|
||
|
$query .= "
|
||
|
ORDER BY
|
||
|
modified_date DESC
|
||
|
;";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
id => $id,
|
||
|
query => $query,
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
7 years ago
|
my $last_updated = $anvil->Database->query({id => $id, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
|
||
7 years ago
|
$last_updated = 0 if not defined $last_updated;
|
||
7 years ago
|
|
||
7 years ago
|
# Record this table's last modified_date for later comparison. We'll also
|
||
|
# record the schema and host column, if found, to save looking the same thing
|
||
|
# up later if we do need a resync.
|
||
7 years ago
|
$anvil->data->{sys}{database}{table}{$table}{id}{$id}{last_updated} = $last_updated;
|
||
|
$anvil->data->{sys}{database}{table}{$table}{schema} = $schema;
|
||
|
$anvil->data->{sys}{database}{table}{$table}{host_column} = $host_column;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::table::${table}::id::${id}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{id}{$id}{last_updated},
|
||
|
"sys::database::table::${table}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{last_updated},
|
||
|
"sys::database::table::${table}::schema" => $anvil->data->{sys}{database}{table}{$table}{schema},
|
||
|
"sys::database::table::${table}::host_column" => $anvil->data->{sys}{database}{table}{$table}{host_column},
|
||
8 years ago
|
}});
|
||
7 years ago
|
|
||
7 years ago
|
if ($anvil->data->{sys}{database}{table}{$table}{id}{$id}{last_updated} > $anvil->data->{sys}{database}{table}{$table}{last_updated})
|
||
7 years ago
|
{
|
||
7 years ago
|
$anvil->data->{sys}{database}{table}{$table}{last_updated} = $anvil->data->{sys}{database}{table}{$table}{id}{$id}{last_updated};
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::table::${table}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{last_updated},
|
||
7 years ago
|
}});
|
||
|
}
|
||
8 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
# Now loop through each table we've seen and see if the moditied_date differs for any of the
|
||
|
# databases. If it has, trigger a resync.
|
||
7 years ago
|
foreach my $table (sort {$a cmp $b} keys %{$anvil->data->{sys}{database}{table}})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::table::${table}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{last_updated},
|
||
8 years ago
|
}});
|
||
7 years ago
|
foreach my $id (sort {$a cmp $b} keys %{$anvil->data->{sys}{database}{table}{$table}{id}})
|
||
8 years ago
|
{
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::table::${table}::id::${id}::last_updated" => $anvil->data->{sys}{database}{table}{$table}{id}{$id}{last_updated},
|
||
8 years ago
|
}});
|
||
7 years ago
|
if ($anvil->data->{sys}{database}{table}{$table}{last_updated} > $anvil->data->{sys}{database}{table}{$table}{id}{$id}{last_updated})
|
||
8 years ago
|
{
|
||
7 years ago
|
# Resync needed.
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "log_0106", variables => { id => $id }});
|
||
8 years ago
|
|
||
|
# Mark it as behind.
|
||
7 years ago
|
$anvil->Database->_mark_database_as_behind({id => $id});
|
||
7 years ago
|
last;
|
||
8 years ago
|
}
|
||
|
}
|
||
7 years ago
|
last if $anvil->data->{sys}{database}{resync_needed};
|
||
8 years ago
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
=head2 _mark_database_as_behind
|
||
|
|
||
|
This method marks that a resync is needed and, if needed, switches the database this machine will read from.
|
||
|
|
||
|
Parameters;
|
||
|
|
||
|
=head3 id
|
||
|
|
||
|
This is the C<< id >> of the database being marked as "behind".
|
||
|
|
||
|
=cut
|
||
|
sub _mark_database_as_behind
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->_mark_database_as_behind()" }});
|
||
8 years ago
|
|
||
|
my $id = $parameter->{id} ? $parameter->{id} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
8 years ago
|
|
||
7 years ago
|
$anvil->data->{sys}{database}{to_update}{$id}{behind} = 1;
|
||
|
$anvil->data->{sys}{database}{resync_needed} = 1;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
|
"sys::database::to_update::${id}::behind" => $anvil->data->{sys}{database}{to_update}{$id}{behind},
|
||
|
"sys::database::resync_needed" => $anvil->data->{sys}{database}{resync_needed},
|
||
8 years ago
|
}});
|
||
8 years ago
|
|
||
|
# We can't trust this database for reads, so switch to another database for reads if
|
||
|
# necessary.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
|
||
8 years ago
|
id => $id,
|
||
7 years ago
|
"sys::read_db_id" => $anvil->data->{sys}{read_db_id},
|
||
8 years ago
|
}});
|
||
7 years ago
|
if ($id eq $anvil->data->{sys}{read_db_id})
|
||
8 years ago
|
{
|
||
|
# Switch.
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { ">> sys::read_db_id" => $anvil->data->{sys}{read_db_id} }});
|
||
|
foreach my $this_id (sort {$a cmp $b} keys %{$anvil->data->{database}})
|
||
8 years ago
|
{
|
||
|
next if $this_id eq $id;
|
||
7 years ago
|
$anvil->data->{sys}{read_db_id} = $this_id;
|
||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "<< sys::read_db_id" => $anvil->data->{sys}{read_db_id} }});
|
||
8 years ago
|
last;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
8 years ago
|
=head2 _test_access
|
||
|
|
||
|
This method takes a database ID and performs a simple C<< SELECT 1 >> query, wrapped in a ten second C<< alarm >>. If the database has died, the query will hang and the C<< alarm >> will fire, killing this program. If the call returns, the C<< alarm >> is cancelled.
|
||
|
|
||
|
This exists to handle the loss of a database mid-run where a normal query, which isn't wrapped in a query, could hang indefinately.
|
||
|
|
||
|
=cut
|
||
|
sub _test_access
|
||
|
{
|
||
|
my $self = shift;
|
||
|
my $parameter = shift;
|
||
7 years ago
|
my $anvil = $self->parent;
|
||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0125", variables => { method => "Database->_test_access()" }});
|
||
8 years ago
|
|
||
|
my $id = $parameter->{id} ? $parameter->{id} : "";
|
||
7 years ago
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { id => $id }});
|
||
8 years ago
|
|
||
8 years ago
|
# Make logging code a little cleaner
|
||
7 years ago
|
my $say_server = $anvil->data->{database}{$id}{host}.":".$anvil->data->{database}{$id}{port}." -> ".$anvil->data->{database}{$id}{name};
|
||
8 years ago
|
|
||
8 years ago
|
# Log our test
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0087", variables => { server => $say_server }});
|
||
8 years ago
|
|
||
|
my $query = "SELECT 1";
|
||
7 years ago
|
my $DBreq = $anvil->data->{cache}{db_fh}{$id}->prepare($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0075", variables => {
|
||
8 years ago
|
query => $query,
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
db_error => $DBI::errstr,
|
||
|
}});
|
||
|
|
||
|
# Give the test query a few seconds to respond, just in case we have some latency to a remote DB.
|
||
|
alarm(10);
|
||
7 years ago
|
$DBreq->execute() or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0076", variables => {
|
||
8 years ago
|
query => $query,
|
||
8 years ago
|
server => $say_server,
|
||
8 years ago
|
db_error => $DBI::errstr,
|
||
|
}});
|
||
|
# If we're here, we made contact.
|
||
|
alarm(0);
|
||
|
|
||
|
# Success!
|
||
7 years ago
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0088"});
|
||
8 years ago
|
|
||
|
return(0);
|
||
|
}
|