* Changed the Storage->copy() 'target' parameter to 'target_file' to avoid confusion with the often-used 'target' parameter for connecting to remote machines.

* Changed 'database::...' so that 'x' is now the database host's UUID instead of a simple integer. This will simplify sync'ing configs. Also removed default entries, and made it so that anvil-prep-database injects the local config during first setup. Renamed Database->get_local_id to get_local_uuid and changed the 'id' parameter to 'uuid'. Changed Database->initialize's 'id' parameter to 'host_uuid'. The Database->query, Database->write, Database->_mark_database_as_behind and Database->_find_behind_databases methods had their 'id' parameter changed to 'uuid'.
* Added the 'remote_user' parameter to Get->anvil_version, System->ping and System->change_shell_user_password for conencting to remote targets.
* Added the 'remote_user' parameter to all internal Remote->call uses.
* Updated Storage->backup, Storage->copy_file, Storage->make_directory,

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 7 years ago
parent eafd4fd3f7
commit a89fb24adf
  1. 12
      Anvil/Tools.t
  2. 560
      Anvil/Tools/Database.pm
  3. 16
      Anvil/Tools/Get.pm
  4. 28
      Anvil/Tools/Remote.pm
  5. 456
      Anvil/Tools/Storage.pm
  6. 43
      Anvil/Tools/System.pm
  7. 101
      anvil.conf
  8. 3
      rpm/SPECS/anvil.spec
  9. 56
      share/words.xml
  10. 113
      tools/anvil-prep-database

@ -346,25 +346,25 @@ if (-e $copy_file)
{
unlink $copy_file or die "The test copy file: [$copy_file] exists (from a previous run?) and can't be removed. The error was: $!\n";
}
$anvil->Storage->copy_file({source => $test_file, target => $copy_file});
$anvil->Storage->copy_file({source_file => $test_file, target_file => $copy_file});
if (-e $copy_file)
{
$copied_ok = 1;
}
is($copied_ok, "1", "Verifying that 'Storage->copy_file' was able to copy the test file.");
my $copy_rc = $anvil->Storage->copy_file({target => $copy_file});
my $copy_rc = $anvil->Storage->copy_file({target_file => $copy_file});
is($copy_rc, "1", "Verifying that 'Storage->copy_file' returned '1' when no source file was passed.");
$copy_rc = "";
$copy_rc = $anvil->Storage->copy_file({source => $test_file});
$copy_rc = $anvil->Storage->copy_file({source_file => $test_file});
is($copy_rc, "2", "Verifying that 'Storage->copy_file' returned '2' when no target file was passed.");
$copy_rc = "";
$copy_rc = $anvil->Storage->copy_file({source => $test_file, target => $copy_file});
$copy_rc = $anvil->Storage->copy_file({source_file => $test_file, target_file => $copy_file});
is($copy_rc, "3", "Verifying that 'Storage->copy_file' returned '3' when the target file already exists.");
$copy_rc = "";
$copy_rc = $anvil->Storage->copy_file({source => $test_file, target => $copy_file, overwrite => 1});
$copy_rc = $anvil->Storage->copy_file({source_file => $test_file, target_file => $copy_file, overwrite => 1});
is($copy_rc, "0", "Verifying that 'Storage->copy_file' returned '0' when the target file already exists and overwrite was set.");
$copy_rc = "";
$copy_rc = $anvil->Storage->copy_file({source => "/fake/file", target => $copy_file});
$copy_rc = $anvil->Storage->copy_file({source_file => "/fake/file", target_file => $copy_file});
is($copy_rc, "4", "Verifying that 'Storage->copy_file' returned '4' when the target file is passed but doesn't exist.");
# find
my $test_path = $anvil->Storage->find({ file => "Anvil/Tools.t" });

File diff suppressed because it is too large Load Diff

@ -101,6 +101,10 @@ This is the password to use when connecting to a remote machine. If not set, but
This is the TCP port to use when connecting to a remote machine. If not set, but C<< target >> is, C<< 22 >> will be used.
=head3 remote_user (optional, default root)
If C<< target >> is set, this will be the user we connect to the remote machine as.
=head3 target (optional)
This is the IP or host name of the machine to read the version of. If this is not set, the local system's version is checked.
@ -113,13 +117,15 @@ sub anvil_version
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $password = $parameter->{password} ? $parameter->{password} : "";
my $port = $parameter->{port} ? $parameter->{port} : "";
my $target = $parameter->{target} ? $parameter->{target} : "local";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "local";
my $version = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
password => $anvil->Log->secure ? $password : "--",
port => $port,
remote_user => $remote_user,
target => $target,
}});
@ -135,12 +141,14 @@ else
echo 0;
fi;
";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($error, $output) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error => $error }});

@ -186,8 +186,8 @@ Example;
# Call 'hostname' on a node.
my ($error, $output) = $anvil->Remote->call({
target => "an-a01n01.alteeve.com",
user => "admin",
password => "super secret password",
remote_user => "admin",
shell_call => "/usr/bin/hostname",
});
@ -195,8 +195,8 @@ Example;
# connection when done.
my ($error, $output) = $anvil->Remote->call({
target => "an-a01n01.alteeve.com",
user => "root",
password => "super secret password",
remote_user => "root",
shell_call => "/usr/sbin/fence_ipmilan -a an-a01n02.ipmi -l admin -p \"super secret password\" -o status",
secure => 1,
'close' => 1,
@ -234,6 +234,10 @@ This is the TCP port to use when connecting to the C<< target >>. The default is
B<NOTE>: See C<< target >> for optional port definition.
=head3 remote_user (optional, default root)
This is the user account on the C<< target >> to connect as and to run the C<< shell_call >> as. The C<< password >> if so this user's account on the C<< target >>.
=head3 secure (optional, default C<< 0 >>)
If set, the C<< shell_call >> is treated as containing sensitive data and will not be logged unless C<< $anvil->Log->secure >> is enabled.
@ -250,10 +254,6 @@ B<NOTE>: If the target matches an entry in '/etc/ssh/ssh_config', the port defin
B<NOTE>: If the C<< target >> is presented in the format C<< target:port >>, the port will be separated from the target and used as the TCP port. If the C<< port >> parameter is set, however, the port split off the C<< target >> will be ignored.
=head3 user (optional, default 'root')
This is the user account on the C<< target >> to connect as and to run the C<< shell_call >> as. The C<< password >> if so this user's account on the C<< target >>.
=cut
sub call
{
@ -288,7 +288,7 @@ sub call
my $password = defined $parameter->{password} ? $parameter->{password} : $anvil->data->{sys}{root_password};
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
my $shell_call = defined $parameter->{shell_call} ? $parameter->{shell_call} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "root";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $start_time = time;
my $ssh_fh = $anvil->data->{cache}{ssh_fh}{$ssh_fh_key};
# NOTE: The shell call might contain sensitive data, so we show '--' if 'secure' is set and $anvil->Log->secure is not.
@ -299,7 +299,7 @@ sub call
shell_call => ((not $anvil->Log->secure) && ($secure)) ? "--" : $shell_call,
ssh_fh => $ssh_fh,
start_time => $start_time,
user => $user,
remote_user => $remote_user,
}});
if (not $shell_call)
@ -314,9 +314,9 @@ sub call
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->call()", parameter => "target" }});
return("!!error!!");
}
if (not $user)
if (not $remote_user)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->call()", parameter => "user" }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Remote->call()", parameter => "remote_user" }});
return("!!error!!");
}
@ -424,7 +424,7 @@ sub call
#print "[".$connect_time."] - Connection failed time to: [$target:$port]\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", list => {
user => $user,
remote_user => $remote_user,
target => $target,
port => $port,
shell_call => $shell_call,
@ -444,7 +444,7 @@ sub call
$variables = {
target => $target,
port => $port,
user => $user,
remote_user => $remote_user,
};
}
elsif ($@ =~ /No route to host/)
@ -466,10 +466,10 @@ sub call
if (not $error)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => {
user => $user,
remote_user => $remote_user,
password => $anvil->Log->secure ? $password : "--",
}});
if (not $ssh_fh->auth_password($user, $password))
if (not $ssh_fh->auth_password($remote_user, $password))
{
# Can we log in without a password?
my $user = getpwuid($<);

@ -12,7 +12,7 @@ our $VERSION = "3.0.0";
my $THIS_FILE = "Storage.pm";
### Methods;
# backup_file
# backup
# change_mode
# change_owner
# check_md5sums
@ -96,6 +96,10 @@ This will create a copy of the file under the C<< path::directories::backups >>
By default, a failure to backup will be fatal with return code C<< 1 >> for safety reasons. If the file is critical, you can set C<< fatal => 0 >> and an empty string will be returned on error.
This method can work on local and remote systems.
If the backup failed, an empty string is returned.
Parameters;
=head3 fatal (optional, default 1)
@ -106,6 +110,22 @@ If set to C<< 0 >>, any problem with the backup will be ignored and an empty str
This is the path and file name of the file to be backed up. Fully paths must be used.
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 target (optional)
If set, the file will be backed up on the target machine. This must be either an IP address or a resolvable host name.
=head3 remote_user (optional)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=cut
sub backup
{
@ -115,14 +135,22 @@ sub backup
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 2;
my $fatal = defined $parameter->{fatal} ? $parameter->{fatal} : 1;
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $source_file = defined $parameter->{file} ? $parameter->{file} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
source_file => $source_file,
fatal => $fatal,
port => $port,
password => $anvil->Log->secure ? $password : "--",
target => $target,
remote_user => $remote_user,
source_file => $source_file,
}});
my $proceed = 0;
my $target_file = "";
if (not $source_file)
{
# No file passed in
@ -135,7 +163,78 @@ sub backup
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0150", variables => { source_file => $source_file }});
if ($fatal) { $anvil->nice_exit({code => 1}); }
}
elsif (not -e $source_file)
if ($target)
{
# Make sure the source file exists, is a file and can be read.
my $shell_call = "
if [ -e '".$source_file."' ];
if [ -f '".$source_file."' ];
then
if [ -r '".$source_file."' ];
then
".$anvil->data->{path}{exe}{echo}." 'ok'
else
".$anvil->data->{path}{exe}{echo}." 'not readable'
fi
else
".$anvil->data->{path}{exe}{echo}." 'not a file'
fi
else
".$anvil->data->{path}{exe}{echo}." 'not found'
fi";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($error, $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $shell_call,
});
if (not $error)
{
# No error. Did the file exist?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'output->[0]' => $output->[0] }});
if ($output->[0] eq "not found")
{
# File doesn't exist.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0151", variables => { source_file => $source_file }});
if ($fatal) { $anvil->nice_exit({code => 1}); }
}
elsif ($output->[0] eq "not a file")
{
# Not a file
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0153", variables => { source_file => $source_file }});
if ($fatal) { $anvil->nice_exit({code => 1}); }
}
elsif ($output->[0] eq "not readable")
{
# Can't read the file.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0152", variables => { source_file => $source_file }});
if ($fatal) { $anvil->nice_exit({code => 1}); }
}
else
{
# We're good.
$proceed = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { proceed => $proceed }});
}
}
else
{
# Didn't connect?
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0165", variables => {
target => $target,
source_file => $source_file,
}});
if ($fatal) { $anvil->nice_exit({code => 1}); }
}
}
else
{
# Local file
if (not -e $source_file)
{
# File doesn't exist.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0151", variables => { source_file => $source_file }});
@ -154,6 +253,13 @@ sub backup
if ($fatal) { $anvil->nice_exit({code => 1}); }
}
else
{
$proceed = 1;
}
}
# Proceed?
if ($proceed)
{
# Proceed with the backup. We'll recreate the path
my ($directory, $file) = ($source_file =~ /^(\/.*)\/(.*)$/);
@ -171,15 +277,23 @@ sub backup
}});
# Backup! It will create the target directory, if needed.
$anvil->Storage->copy_file({
source => $source_file,
target => $target_file,
debug => 2,
my $failed = $anvil->Storage->copy_file({
debug => $debug,
source_file => $source_file,
target_file => $target_file,
password => $password,
target => $target,
remote_user => $remote_user,
source_file => $source_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { failed => $failed }});
if (not $failed)
{
# Log that the file was backed up.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0154", variables => { source_file => $source_file, target_file => $target_file }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target_file => $target_file }});
return($target_file);
@ -433,10 +547,12 @@ sub check_md5sums
=head2 copy_file
This copies a file, with a few additional checks like creating the target directory if it doesn't exist, aborting if the file has already been backed up before, etc.
This copies a file, with a few additional checks like creating the target directory if it doesn't exist, aborting if the file has already been backed up before, etc. It can copy files on the local or a remote machine.
# Example
$anvil->Storage->copy_file({source => "/some/file", target => "/another/directory/file"});
$anvil->Storage->copy_file({source_file => "/some/file", target_file => "/another/directory/file"});
Returns C<< 0 >> on success, otherwise C<< 1 >>.
Parameters;
@ -446,16 +562,32 @@ If this is set to 'C<< 1 >>', and if the target file exists, it will be replaced
If this is not passed and the target exists, this module will return 'C<< 3 >>'.
=head3 source (required)
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 source_file (required)
This is the source file. If it isn't specified, 'C<< 1 >>' will be returned. If it doesn't exist, this method will return 'C<< 4 >>'.
=head3 target (required)
=head3 target (optional)
If set, the file will be copied on the target machine. This must be either an IP address or a resolvable host name.
This is the target *B<file>*, not the directory to put it in. The target file name can be different from the source file name.
=head3 target_file (required)
This is the target B<< file >>, not the directory to put it in. The target file name can be different from the source file name.
if this is not specified, 'C<< 2 >>' will be returned.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=cut
sub copy_file
{
@ -465,62 +597,168 @@ sub copy_file
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0;
my $source = defined $parameter->{source} ? $parameter->{source} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $source_file = defined $parameter->{source_file} ? $parameter->{source_file} : "";
my $target_file = defined $parameter->{target_file} ? $parameter->{target_file} : "";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
overwrite => $overwrite,
source => $source,
password => $anvil->Log->secure ? $password : "--",
remote_user => $remote_user,
source_file => $source_file,
target_file => $target_file,
target => $target,
}});
if (not $source)
if (not $source_file)
{
# No source passed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->copy_file()", parameter => "source" }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->copy_file()", parameter => "source_file" }});
return(1);
}
elsif (not -e $source)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0052", variables => { source => $source }});
return(4);
}
if (not $target)
if (not $target_file)
{
# No target passed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->copy_file()", parameter => "target" }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->copy_file()", parameter => "target_file" }});
return(2);
}
my ($directory, $file) = ($target_file =~ /^(.*)\/(.*)$/);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
directory => $directory,
file => $file,
}});
if ($target)
{
# Copying on a remote system.
my $proceed = 1;
my $shell_call = "
if [ -e '".$source_file."' ];
".$anvil->data->{path}{exe}{echo}." 'source file exists'
else
".$anvil->data->{path}{exe}{echo}." 'source file not found'
fi
if [ -d '".$target_file."' ];
".$anvil->data->{path}{exe}{echo}." 'target file exists'
elif [ -d '".$directory."' ];
".$anvil->data->{path}{exe}{echo}." 'target directory exists'
else
".$anvil->data->{path}{exe}{echo}." 'target directory not found'
fi";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($error, $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $shell_call,
});
if ($error)
{
# Something went wrong.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0169", variables => {
source_file => $source_file,
target_file => $target_file,
error => $error,
output => $output,
target => $target,
remote_user => $remote_user,
}});
return(1);
}
else
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
'output->[0]' => $output->[0],
'output->[1]' => $output->[1],
}});
if ($output->[0] eq "source file not found")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0052", variables => { source_file => $source_file }});
return(1);
}
if (($output->[0] eq "source file exists") && (not $overwrite))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0046", variables => {
source_file => $source_file,
target_file => $target_file,
}});
return(1);
}
if ($output->[1] eq "target directory not found")
{
my $failed = $anvil->Storage->make_directory({
debug => $debug,
directory => $directory,
password => $password,
remote_user => $remote_user,
target => $target,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { failed => $failed }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0170", variables => {
source_file => $source_file,
target_file => $target_file,
}});
return(1);
}
# Now backup the file.
my ($error, $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $anvil->data->{path}{exe}{'cp'}." -af ".$source_file." ".$target_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});
}
}
else
{
# Copying locally
if (not -e $source_file)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0052", variables => { source_file => $source_file }});
return(1);
}
# If the target exists, abort
if ((-e $target) && (not $overwrite))
if ((-e $target_file) && (not $overwrite))
{
# This isn't an error.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0046", variables => {
source => $source,
target => $target,
source_file => $source_file,
target_file => $target_file,
}});
return(3);
return(1);
}
# Make sure the target directory exists and create it, if not.
my ($directory, $file) = ($target =~ /^(.*)\/(.*)$/);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
directory => $directory,
file => $file,
}});
if (not -e $directory)
{
$anvil->Storage->make_directory({
my $failed = $anvil->Storage->make_directory({
debug => $debug,
directory => $directory,
group => $(, # Real UID
user => $<, # Real GID
mode => "0750",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { failed => $failed }});
if ($failed)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0170", variables => {
source_file => $source_file,
target_file => $target_file,
}});
return(1);
}
}
# Now backup the file.
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'cp'}." -af $source $target"});
my $output = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{'cp'}." -af ".$source_file." ".$target_file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});
}
return(0);
}
@ -603,7 +841,7 @@ This creates a directory (and any parent directories).
$anvil->Storage->make_directory({directory => "/foo/bar/baz", owner => "me", grou[ => "me", group => 755});
If it fails to create the directory, an alert will be logged.
If it fails to create the directory, C<< 1 >> will be returned. Otherwise, C<< 0 >> will be returned.
Parameters;
@ -619,6 +857,22 @@ This is the group name or group ID to set the ownership of the directory to.
This is the numeric mode to set on the file. It expects four digits to cover the sticky bit, but will work with three digits.
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 target (optional)
If set, the directory will be created on this machine. This must be an IP address or a (resolvable) host name.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=head3 user (optional)
This is the user name or user ID to set the ownership of the directory to.
@ -634,11 +888,20 @@ sub make_directory
my $directory = defined $parameter->{directory} ? $parameter->{directory} : "";
my $group = defined $parameter->{group} ? $parameter->{group} : "";
my $mode = defined $parameter->{mode} ? $parameter->{mode} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "";
my $failed = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
directory => $directory,
group => $group,
mode => $mode,
port => $port,
password => $anvil->Log->secure ? $password : "--",
remote_user => $remote_user,
target => $target,
user => $user,
}});
@ -658,9 +921,74 @@ sub make_directory
$working_directory .= "/$this_directory";
$working_directory =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { working_directory => $working_directory }});
# Are we working locally or remotely?
if ($target)
{
# Assemble the command
my $shell_call = "
if [ -d '".$working_directory."' ];
then
".$anvil->data->{path}{exe}{echo}." 'exists'
else
".$anvil->data->{path}{exe}{'mkdir'}." $working_directory
";
if ($mode)
{
$shell_call .= " ".$anvil->data->{path}{exe}{'chmod'}." ".$mode."\n";
}
if (($user) && ($group))
{
$shell_call .= " ".$anvil->data->{path}{exe}{'chown'}." ".$user.":".$group."\n";
}
elsif ($user)
{
$shell_call .= " ".$anvil->data->{path}{exe}{'chown'}." ".$user.":\n";
}
elsif ($group)
{
$shell_call .= " ".$anvil->data->{path}{exe}{'chown'}." :".$group."\n";
}
$shell_call .= "
if [ -d '".$working_directory."' ];
then
".$anvil->data->{path}{exe}{echo}." 'created'
else
".$anvil->data->{path}{exe}{echo}." 'failed to create'
fi;
fi;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
my ($error, $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $shell_call,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
error => $error,
output => $output,
}});
if ($output->[0] eq "failed to create")
{
$failed = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0167", variables => {
directory => $working_directory,
error => $error,
output => $output,
target => $target,
remote_user => $remote_user,
}});
}
}
else
{
# Locally.
if (not -e $working_directory)
{
# Directory doesn't exist, so create it.
my $error = "";
my $shell_call = $anvil->data->{path}{exe}{'mkdir'}." ".$working_directory;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0011", variables => { shell_call => $shell_call }});
open (my $file_handle, $shell_call." 2>&1 |") or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
@ -669,6 +997,7 @@ sub make_directory
chomp;
my $line = $_;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0017", variables => { line => $line }});
$error .= $line."\n";
}
close $file_handle;
@ -680,10 +1009,21 @@ sub make_directory
{
$anvil->Storage->change_owner({target => $working_directory, user => $user, group => $group});
}
if (not -e $working_directory)
{
$failed = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0168", variables => {
directory => $working_directory,
error => $error,
}});
}
}
}
last if $failed;
}
return(0);
return($failed);
}
=head2 read_config
@ -1512,7 +1852,7 @@ The file will be written locally in C<< /tmp/<file_name> >>, C<< $anvil->Storage
This is the user name or user ID to set the ownership of the file to.
=head3 remote_user (optional)
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
@ -1534,7 +1874,7 @@ sub write_file
my $secure = defined $parameter->{secure} ? $parameter->{secure} : "";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "root";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => {
body => $body,
file => $file,
@ -1599,23 +1939,42 @@ then
else
".$anvil->data->{path}{exe}{echo}." 'not found';
fi";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
($error, my $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
port => $port,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $shell_call,
});
if (not $error)
{
# No error. Did the file exist?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'output->[0]' => $output->[0] }});
if (($output->[0] eq "exists") && (not $overwrite))
if ($output->[0] eq "exists")
{
if (not $overwrite)
{
# Abort, we're not allowed to overwrite.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0040", variables => { file => $file }});
$error = 1;
}
}
else
{
# Back it up.
my $backup_file = $anvil->Storage->backup({
file => $file,
debug => $debug,
target => $target,
port => $port,
user => $remote_user,
password => $password,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
}
# Make sure the directory exists on the remote machine. In this case, we'll use 'mkdir -p' if it isn't.
if (not $error)
@ -1627,11 +1986,13 @@ then
else
".$anvil->data->{path}{exe}{echo}." 'not found';
fi";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
($error, my $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $shell_call,
});
@ -1640,11 +2001,13 @@ fi";
{
# Create the directory
my $shell_call = $anvil->data->{path}{exe}{'mkdir'}." -p ".$directory;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
($error, my $output) = $anvil->Remote->call({
debug => $debug,
target => $target,
user => $remote_user,
password => $password,
remote_user => $remote_user,
shell_call => $shell_call,
});
}
@ -1708,6 +2071,7 @@ fi";
{
# Don't pass the mode as the file's mode is likely not executable.
$anvil->Storage->make_directory({
debug => $debug,
directory => $directory,
group => $group,
user => $user,

@ -208,6 +208,10 @@ If you are changing the password of a user on a remote machine, this is the pass
This is the TCP port number to use if connecting to a remote machine over SSH. Ignored if C<< target >> is not given.
=head3 remote_user (optional, default root)
If C<< target >> is set and we're changing the password for a remote user, this is the user we B<< log into >> the remote machine as, B<< not >> the user whose password we will change.
=head3 target (optional)
This is the IP address or (resolvable) host name of the target machine whose user account you want to change the password
@ -224,16 +228,18 @@ sub change_shell_user_password
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $new_password = $parameter->{new_password} ? $parameter->{new_password} : "";
my $password = $parameter->{password} ? $parameter->{password} : "";
my $port = $parameter->{port} ? $parameter->{port} : "";
my $target = $parameter->{target} ? $parameter->{target} : "";
my $user = $parameter->{user} ? $parameter->{user} : "";
my $new_password = defined $parameter->{new_password} ? $parameter->{new_password} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "";
my $return_code = 255;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
user => $user,
target => $target,
port => $port,
remote_user => $remote_user,
new_password => $anvil->Log->secure ? $new_password : "--",
password => $anvil->Log->secure ? $password : "--",
}});
@ -277,12 +283,14 @@ sub change_shell_user_password
if ($target)
{
# Remote call.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
$output = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});
}
@ -1169,6 +1177,10 @@ This is the port used to access a remote machine. This is used when pinging from
B<NOTE>: See C<< Remote->call >> for additional information on specifying the SSH port as part of the target.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user we will use to log into the remote machine to run the actual ping.
=head3 target (optional)
This is the host name or IP address of a remote machine that you want to run the ping on. This is used to test a remote machine's access to a given ping target.
@ -1192,14 +1204,15 @@ sub ping
# print "[".$ping_time."] - Pinged: [$host]\n";
# If we were passed a target, try pinging from it instead of locally
my $count = $parameter->{count} ? $parameter->{count} : 1; # How many times to try to ping it? Will exit as soon as one succeeds
my $fragment = $parameter->{fragment} ? $parameter->{fragment} : 1; # Allow fragmented packets? Set to '0' to check MTU.
my $password = $parameter->{password} ? $parameter->{password} : "";
my $payload = $parameter->{payload} ? $parameter->{payload} : 0; # The size of the ping payload. Use when checking MTU.
my $ping = $parameter->{ping} ? $parameter->{ping} : "";
my $port = $parameter->{port} ? $parameter->{port} : "";
my $target = $parameter->{target} ? $parameter->{target} : "";
my $timeout = $parameter->{timeout} ? $parameter->{timeout} : 1; # This sets the 'timeout' delay.
my $count = defined $parameter->{count} ? $parameter->{count} : 1; # How many times to try to ping it? Will exit as soon as one succeeds
my $fragment = defined $parameter->{fragment} ? $parameter->{fragment} : 1; # Allow fragmented packets? Set to '0' to check MTU.
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $payload = defined $parameter->{payload} ? $parameter->{payload} : 0; # The size of the ping payload. Use when checking MTU.
my $ping = defined $parameter->{ping} ? $parameter->{ping} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $timeout = defined $parameter->{timeout} ? $parameter->{timeout} : 1; # This sets the 'timeout' delay.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
count => $count,
fragment => $fragment,
@ -1207,6 +1220,7 @@ sub ping
password => $anvil->Log->secure ? $password : "--",
ping => $ping,
port => $port,
remote_user => $remote_user,
target => $target,
}});
@ -1259,11 +1273,14 @@ sub ping
if (($target) && ($target ne "local") && ($target ne $anvil->_hostname) && ($target ne $anvil->_short_hostname))
{
### Remote calls
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0166", variables => { shell_call => $shell_call, target => $target, remote_user => $remote_user }});
$output = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});
}

@ -1,10 +1,11 @@
### This is the main Anvil! configuration file.
#
### Database
# Database connections;
#
# Each Anvil! database is defined below using an incrementing counter as the second variable. The value of
# the second variable is not important, so long as it is unique. Generally, it's a simple incrementing
# intger.
# the second variable is the local 'host_uuid' (via: dmidecode --string system-uuid | perl -ne 'print lc').
# This ensures that entries can be moved and copied without causing conflicts.
#
# There are six variables for each database definition;
# host = This is the (resolvable) host name or IP address of the peer database.
@ -24,16 +25,51 @@
# might be necessary if the peer dashboard is behind a firewall/router or otherwise can't respond
# to ICMP pings.
# NOTE: The database is called 'anvil' and the database owner is 'admin'.
#database::eec27c2f-2308-4b4f-bd81-24118b53f8a3::host = localhost
#database::eec27c2f-2308-4b4f-bd81-24118b53f8a3::port = 5432
#database::eec27c2f-2308-4b4f-bd81-24118b53f8a3::password = Initial1
#database::eec27c2f-2308-4b4f-bd81-24118b53f8a3::ping = 1
# Below are the databases configured for use by this system. Please be careful manually editing them. They
# are updated by Striker and changes can be overwritten. Please do not alter or remove the 'start db list'
# and 'end db list' comments below.
### start db list ###
### end db list ###
# To keep Anvil!'s database growth in check, an auto-archive mechanism is used by some agents where, at the
# end of each scan, the number of records in the history schema for a given table are counted (restricted to
# the agent's host, when appropriate).
#
# When the number exceeds the trigger, the number of records that are archived is approximately (number of
# records above trigger + 'count'). This is not an exact number because a representative timestamp will be
# selected from the hostory schema at this count, and then any record equal to or older than the time stamp
# is removed.
#
# To protect against the potential of using too much disk space, archives are off by default. Under normal
# behaviour, old records are simple removed. To enable the archive function, set this to '1'.
#sys::database::archive::save_to_disk = 1
#
# When archiving to disk is enabled, to protect against large memory use or long archive times in the case
# where the number of records to archive are particularly large, the 'division' value is used to break up the
# archive job into "chunks". Generally speaking, the division should be greater than the count, and never be
# needed. However, if the archive process takes too long, or if the archive was triggered well above the
# trigger value, the division can help prevent using too much memory at once. If division is set to '0',
# archive jobs will never be divided.
#
# The archives are all stored in the specified directory using the name format
# '<agent>.<table>.<timestamp>.bz2' and the archives are synced between dashboards for safe keeping. Archive
# files are never removed automatically.
#
# To disable auto-archiving entirely, set 'trigger' to '0'.
#
# NOTE: If the archive directory doesn't exist, Anvil! will create it
# automatically the first time it is needed.
sys::database::archive::compress = 1
sys::database::archive::count = 50000
sys::database::archive::directory = /usr/local/anvil/archives/
sys::database::archive::division = 60000
sys::database::archive::trigger = 100000
database::1::host = 192.168.122.201
database::1::port = 5432
database::1::password = Initial1
database::1::ping = 1
database::2::host = 192.168.122.202
database::2::port = 5432
database::2::password = Initial1
database::2::ping = 1
# This is the schema for the Anvil! database.
sys::database::schema = /usr/sbin/anvil/anvil.sql
@ -93,44 +129,3 @@ sys::database::schema = /usr/sbin/anvil/anvil.sql
#defaults::template::html = alteeve
### Database
# To keep Anvil!'s database growth in check, an auto-archive mechanism is
# used by some agents where, at the end of each scan, the number of records in
# the history schema for a given table are counted (restricted to the agent's
# host, when appropriate).
#
# When the number exceeds the trigger, the number of records that are archived
# is approximately (number of records above trigger + 'count'). This is not an
# exact number because a representative timestamp will be selected from the
# hostory schema at this count, and then any record equal to or older than the
# time stamp is removed.
#
# To protect against the potential of using too much disk space, archives are
# off by default. Under normal behaviour, old records are simple removed. To
# enable the archive function, set this to '1'.
#sys::database::archive::save_to_disk = 1
#
# When archiving to disk is enabled, to protect against large memory use or
# long archive times in the case where the number of records to archive are
# particularly large, the 'division' value is used to break up the archive job
# into "chunks". Generally speaking, the division should be greater than the
# count, and never be needed. However, if the archive process takes too long,
# or if the archive was triggered well above the trigger value, the division
# can help prevent using too much memory at once. If division is set to '0',
# archive jobs will never be divided.
#
# The archives are all stored in the specified
# directory using the name format '<agent>.<table>.<timestamp>.bz2' and the
# archives are synced between dashboards for safe keeping. Archive files are
# never removed automatically.
#
# To disable auto-archiving entirely, set 'trigger' to '0'.
#
# NOTE: If the archive directory doesn't exist, Anvil! will create it
# automatically the first time it is needed.
sys::database::archive::compress = 1
sys::database::archive::count = 50000
sys::database::archive::directory = /usr/local/anvil/archives/
sys::database::archive::division = 60000
sys::database::archive::trigger = 100000

@ -165,6 +165,9 @@ restorecon -rv %{buildroot}/%{_localstatedir}/www
%post striker
systemctl enable httpd.service
systemctl start httpd.service
# Open access for Striker. The database will be opened after initial setup.
firewall-cmd --zone=public --add-service=http
firewall-cmd --zone=public --add-service=http --permanent
%files core
%doc README.md notes

@ -98,16 +98,16 @@ It also has replacement variables: [#!variable!first!#] and [#!variable!second!#
<key name="log_0043"><![CDATA[[ Error ] - The method System->call() was called but 'shell_call' was not passed or was empty.]]></key>
<key name="log_0044">The host: [#!variable!host!#] has renewed its database lock.</key>
<key name="log_0045">The host: [#!variable!host!#] is requesting a database lock.</key>
<key name="log_0046"><![CDATA[[ Note ] - The method Storage->copy_file() was asked to copy: [#!variable!source!#] to: [#!variable!target!#], but the target already exists and 'overwrite' wasn't specified, skipping.]]></key>
<key name="log_0046"><![CDATA[[ Note ] - The method Storage->copy_file() was asked to copy: [#!variable!source_file!#] to: [#!variable!target_file!#], but the target already exists and 'overwrite' wasn't specified, skipping.]]></key>
<key name="log_0047"><![CDATA[[ Error ] - The method Log->level() was passed an invalid log level: [#!variable!set!#]. Only '0', '1', '2', '3' or '4' are valid.]]></key>
<key name="log_0048"><![CDATA[[ Warning ] - Testing of Anvil::Tools is beginning. This will generate warnings and alerts and are not a concern.]]></key>
<key name="log_0049"><![CDATA[[ Warning ] - Testing of Anvil::Tools is complete.]]></key>
<key name="log_0050">[ Error ] - There is a local database defined, but it does not appear to exist and we could not initialize the database server. Is 'postgresql-server' installed?</key>
<key name="log_0051"><![CDATA[[ Error ] - The method Storage->change_owner() was asked to change the ownership of: [#!variable!target!#] which doesn't exist.]]></key>
<key name="log_0052"><![CDATA[[ Error ] - The method Storage->copy_file() was called but the source file: [#!variable!source!#] doesn't exist.]]></key>
<key name="log_0052"><![CDATA[[ Error ] - The method Storage->copy_file() was called but the source file: [#!variable!source_file!#] doesn't exist.]]></key>
<key name="log_0053"><![CDATA[[ Error ] - The 'Database->connect()' method tried to connect to the same database twice: [#!variable!target!#].]]></key>
<key name="log_0054">
Connecting to Database with configuration ID: [#!variable!id!#]
Connecting to Database with configuration ID: [#!variable!uuid!#]
- driver: . [#!variable!driver!#]
- host: ... [#!variable!host!#]
- port: ... [#!variable!port!#]
@ -120,11 +120,11 @@ Connecting to Database with configuration ID: [#!variable!id!#]
<key name="log_0057">Updated: [#!variable!file!#] to require passwords for access.</key>
<key name="log_0058"><![CDATA[[ Error ] - The method Remote->call() was called but the port: [#!variable!port!#] is invalid. It must be a digit between '1' and '65535'.]]></key>
<key name="log_0059">Started the PostgreSQL database server.</key>
<key name="log_0060">Database user: [#!variable!user!#] already exists with ID: [#!variable!id!#].</key>
<key name="log_0060">Database user: [#!variable!user!#] already exists with ID: [#!variable!uuid!#].</key>
<key name="log_0061"><![CDATA[[ Error ] - The method Get->users_home() was asked to find the home directory for the user: [#!variable!user!#], but was unable to do so.]]></key>
<key name="log_0062">SSH session opened without a password to: [#!variable!target!#].</key>
<key name="log_0063">The database: [#!variable!host!# -> #!variable!name!#] with the ID: [#!variable!id!#] did not respond to pings and 'database::#!variable!id!#::ping' is not set to '0' in '#!data!path::configs::anvil.conf!#', skipping it.</key>
<key name="log_0064">[ Warning ] - The database: [#!variable!name!#] on host: [#!variable!host!#] with ID: [#!variable!id!#] can not be used, skipping it.</key>
<key name="log_0063">The database: [#!variable!host!# -> #!variable!name!#] with the ID: [#!variable!uuid!#] did not respond to pings and 'database::#!variable!uuid!#::ping' is not set to '0' in '#!data!path::configs::anvil.conf!#', skipping it.</key>
<key name="log_0064">[ Warning ] - The database: [#!variable!name!#] on host: [#!variable!host!#] with ID: [#!variable!uuid!#] can not be used, skipping it.</key>
<key name="log_0065">
The database connection error was:
----------
@ -132,41 +132,41 @@ The database connection error was:
----------
</key>
<key name="log_0066">Is the database server running on: [#!variable!target!#] and does the target's firewall allow connections on TCP port: [#!variable!port!#]?</key>
<key name="log_0067"><![CDATA[The password was not passed, and it is required. Please set: [database::#!variable!id!#::password = <password>] in: [#!data!path::configs::anvil.conf!#].]]></key>
<key name="log_0067"><![CDATA[The password was not passed, and it is required. Please set: [database::#!variable!uuid!#::password = <password>] in: [#!data!path::configs::anvil.conf!#].]]></key>
<key name="log_0068"><![CDATA[The database user name: [#!variable!user!#] on the host: [#!variable!host!#] is either not the owner of the database: [#!variable!name!#], or the password for that user is incorrect.
- Please edit: [#!data!path::configs::anvil.conf!#]
* If the user name is not correct, please update:
database::#!variable!id!#::user = <user>
database::#!variable!uuid!#::user = <user>
* If the user name is correct, please update:
database::#!variable!id!#::password = <password>
database::#!variable!uuid!#::password = <password>
]]></key>
<key name="log_0069">The connection to the database: [#!variable!name!#] on host: [#!variable!host!#:#!variable!port!#] was refused. Is the database server running?</key>
<key name="log_0070">The connection to the database: [#!variable!name!#] on host: [#!variable!host!#:#!variable!port!#] failed because the name could not be translated to an IP address. Is this database server's host name in '/etc/hosts'?</key>
<key name="log_0071">Successfully Connected to the database: [#!variable!name!#] (id: [#!variable!id!#]) on host: [#!variable!host!#:#!variable!port!#].</key>
<key name="log_0072"><![CDATA[[ Error ] - The method Database->query() was called without a database ID to query and 'sys::read_db_id' doesn't contain a database ID, either. Are any databases available?]]></key>
<key name="log_0073"><![CDATA[[ Error ] - The method Database->query() was asked to query the database with ID: [#!variable!id!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0074">About to run: [#!variable!id!#]:[#!variable!query!#]</key>
<key name="log_0071">Successfully Connected to the database: [#!variable!name!#] (id: [#!variable!uuid!#]) on host: [#!variable!host!#:#!variable!port!#].</key>
<key name="log_0072"><![CDATA[[ Error ] - The method Database->query() was called without a database ID to query and 'sys::read_db_uuid' doesn't contain a database ID, either. Are any databases available?]]></key>
<key name="log_0073"><![CDATA[[ Error ] - The method Database->query() was asked to query the database with ID: [#!variable!uuid!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0074">About to run: [#!variable!uuid!#]:[#!variable!query!#]</key>
<key name="log_0075"><![CDATA[[ Error ] - Failed to prepare the database query: [#!variable!query!#] on: [#!variable!server!#]. The error was: [#!variable!db_error!#]. Note that if the query reports '--', the query was listed as containing sensitive data and '$anvil->Log->secure' is not set.]]></key>
<key name="log_0076"><![CDATA[[ Error ] - Failed to execute the database query: [#!variable!query!#] on: [#!variable!server!#]. The error was: [#!variable!db_error!#]. Note that if the query reports '--', the query was listed as containing sensitive data and '$anvil->Log->secure' is not set.]]></key>
<key name="log_0077"><![CDATA[[ Error ] - The method Database->initialize() was called without a database ID to query and 'sys::read_db_id' doesn't contain a database ID, either. Are any databases available?]]></key>
<key name="log_0078"><![CDATA[[ Error ] - The method Database->initialize() was asked to query the database with ID: [#!variable!id!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0079"><![CDATA[[ Error ] - The method Database->initialize() was asked to initialize the database: [#!variable!server!#] (id: [#!variable!id!#]) but a core SQL file to load wasn't passed, and the 'database::#!variable!id!#::core_sql' variable isn't set. Unable to initialize without the core SQL file.]]></key>
<key name="log_0080"><![CDATA[[ Error ] - The method Database->initialize() was asked to initialize the database: [#!variable!server!#] (id: [#!variable!id!#]) but the core SQL file: [#!variable!sql_file!#] doesn't exist.]]></key>
<key name="log_0081"><![CDATA[[ Error ] - The method Database->initialize() was asked to initialize the database: [#!variable!server!#] (id: [#!variable!id!#]) but the core SQL file: [#!variable!sql_file!#] exist, but can't be read.]]></key>
<key name="log_0077"><![CDATA[[ Error ] - The method Database->initialize() was called without a database ID to query and 'sys::read_db_uuid' doesn't contain a database ID, either. Are any databases available?]]></key>
<key name="log_0078"><![CDATA[[ Error ] - The method Database->initialize() was asked to query the database with ID: [#!variable!uuid!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0079"><![CDATA[[ Error ] - The method Database->initialize() was asked to initialize the database: [#!variable!server!#] (id: [#!variable!uuid!#]) but a core SQL file to load wasn't passed, and the 'database::#!variable!uuid!#::core_sql' variable isn't set. Unable to initialize without the core SQL file.]]></key>
<key name="log_0080"><![CDATA[[ Error ] - The method Database->initialize() was asked to initialize the database: [#!variable!server!#] (id: [#!variable!uuid!#]) but the core SQL file: [#!variable!sql_file!#] doesn't exist.]]></key>
<key name="log_0081"><![CDATA[[ Error ] - The method Database->initialize() was asked to initialize the database: [#!variable!server!#] (id: [#!variable!uuid!#]) but the core SQL file: [#!variable!sql_file!#] exist, but can't be read.]]></key>
<key name="log_0082">The database: [#!variable!server!#] needs to be initialized using: [#!variable!sql_file!#].</key>
<key name="log_0083">About to record: [#!variable!id!#]:[#!variable!query!#]</key>
<key name="log_0083">About to record: [#!variable!uuid!#]:[#!variable!query!#]</key>
<key name="log_0084"><![CDATA[[ Error ] - The method Database->query() was asked to query the database: [#!variable!server!#] but no query was given.]]></key>
<key name="log_0085"><![CDATA[[ Error ] - The method Database->write() was asked to write to the database: [#!variable!server!#] but no query was given.]]></key>
<key name="log_0086"><![CDATA[[ Error ] - The method System->check_memory() was called without a program name to check.]]></key>
<key name="log_0087">Testing access to the the database: [#!variable!server!#] prior to query or write. Program will exit if it fails.</key>
<key name="log_0088">Access confirmed.</key>
<key name="log_0089"><![CDATA[[ Error ] - The method Database->write() was asked to write to the database with ID: [#!variable!id!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0089"><![CDATA[[ Error ] - The method Database->write() was asked to write to the database with ID: [#!variable!uuid!#] but there is no file handle open to the database. Was the connection lost?]]></key>
<key name="log_0090"><![CDATA[[ Error ] - Failed to 'do' the database query: [#!variable!query!#] on: [#!variable!server!#]. The error was: [#!variable!db_error!#]. Note that if the query reports '--', the query was listed as containing sensitive data and '$anvil->Log->secure' is not set.]]></key>
<key name="log_0091">Failed to connect to any database.</key>
<key name="log_0092"><![CDATA[[ Error ] - Unable to connect to the database: [#!variable!server!#] (id: [#!variable!id!#]).]]></key>
<key name="log_0092"><![CDATA[[ Error ] - Unable to connect to the database: [#!variable!server!#] (id: [#!variable!uuid!#]).]]></key>
<key name="log_0093"><![CDATA[[ Error ] - The method Alert->check_alert_sent() was called but the 'modified_date' parameter was not passed and/or 'sys::db_timestamp' is not set. Did the program fail to connect to any databases?]]></key>
<key name="log_0094">[ Error ] - Failed to start the Postgres server. Please check the system logs for details.</key>
<key name="log_0095">The database user: [#!variable!user!#] was created with ID: [#!variable!id!#].</key>
<key name="log_0095">The database user: [#!variable!user!#] was created with ID: [#!variable!uuid!#].</key>
<key name="log_0096">[ Error ] - Failed to add the database user: [#!variable!user!#]! Unable to proceed.</key>
<key name="log_0097"><![CDATA[[ Error ] - The method Alert->check_alert_sent() was called but the 'set' parameter was not passed or it is empty. It should be 'set' or 'clear'.]]></key>
<key name="log_0098">
@ -178,14 +178,14 @@ The database connection error was:
- Name: ......... [#!variable!name!#]
- Timestamp: .... [#!variable!modified_date!#]
</key>
<key name="log_0099">[ Error ] - There is no Anvil! database user set for the local machine. Please check: [#!data!path::config::anvil.conf!#]'s DB entry: [#!variable!id!#].</key>
<key name="log_0099">[ Error ] - There is no Anvil! database user set for the local machine. Please check: [#!data!path::config::anvil.conf!#]'s DB entry: [#!variable!uuid!#].</key>
<key name="log_0100">Database user: [#!variable!user!#] password has been set/updated.</key>
<key name="log_0101"><![CDATA[[ Error ] - The method Alert->register_alert() was called but the 'title_key' parameter was not passed or it is empty and 'header' is enable (default).]]></key>
<key name="log_0102">I am not recording the alert with message_key: [#!variable!message_key!#] to the database because its log level was lower than any recipients.</key>
<key name="log_0103">The local machine's UUID was not read properly. It should be stored in: [#!data!sys::host_uuid!#] and contain hexadecimal characters in the format: '012345-6789-abcd-ef01-23456789abcd' and usually matches the output of 'dmidecode --string system-uuid'. If this file exists and if there is a string in the file, please verify that it is structured correctly.</key>
<key name="log_0104">The database with ID: [#!variable!id!#] for: [#!variable!file!#] is behind.</key>
<key name="log_0104">The database with ID: [#!variable!uuid!#] for: [#!variable!file!#] is behind.</key>
<key name="log_0105">Anvil! database: [#!variable!database!#] already exists.</key>
<key name="log_0106">The database with ID: [#!variable!id!#] is behind. A database esync will be requested.</key>
<key name="log_0106">The database with ID: [#!variable!uuid!#] is behind. A database esync will be requested.</key>
<key name="log_0107">[ Warning ] - Failed to delete the temporary postgres password.</key>
<key name="log_0108"><![CDATA[[ Error ] - The method Database->insert_or_update_states() was called but the 'state_host_uuid' parameter was not passed or it is empty. Normally this is set to 'sys::data_uuid'.]]></key>
<key name="log_0109">[ Error ] - Failed to create the Anvil! database: [#!variable!database!#]</key>
@ -244,6 +244,12 @@ The database connection error was:
<key name="log_0162"><![CDATA[[ Error ] - The method Storage->read_file() tried to rsync the remote file: [#!variable!remote_file!#] to the local temporary file: [#!variable!local_file!#], but it did not arrive. There might be more information above.]]></key>
<key name="log_0163">The file: [#!variable!file!#] does not exist.</key>
<key name="log_0164"><![CDATA[[ Warning ] - Storage->read_config()' was called without a file name to read.]]></key>
<key name="log_0165">The Storage->backup() method was asked to backup the file: [#!variable!source_file!#] on: [#!variable!target!#], but it looks like there was a problem connecting to the target.</key>
<key name="log_0166">About to run the shell command: [#!variable!shell_call!#] on: [#!variable!target!#] as: [#!variable!remote_user!#]</key>
<key name="log_0167">Failed to create the directory: [#!variable!directory!#] on: [#!variable!target!#] as: [#!variable!remote_user!#]. The error (if any) was: [#!variable!error!#] and the output (if any) was: [#!variable!output!#].</key>
<key name="log_0168">Failed to create the directory: [#!variable!directory!#]. The error (if any) was: [#!variable!error!#].</key>
<key name="log_0169">Failed to copy the file: [#!variable!source_file!#] to: [#!variable!target_file!#] on the target: [#!variable!target!#] as: [#!variable!remote_user!#]. The error (if any) was: [#!variable!error!#] and the output (if any) was: [#!variable!output!#].</key>
<key name="log_0170"><![CDATA[[ Note ] - The method Storage->copy_file() was asked to copy: [#!variable!source_file!#] to: [#!variable!target_file!#], but the target's parent directory doesn't exist and we were unable to create it.]]></key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>

@ -36,9 +36,19 @@ $anvil->Get->switches;
# Paths
$anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
my $local_id = $anvil->Database->get_local_id;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { local_id => $local_id }});
if ($local_id)
my $local_uuid = $anvil->Database->get_local_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_uuid => $local_uuid }});
# If we didn't get the $local_uuid, then there is no entry for this system in anvil.conf yet, so we'll add it.
if (not $local_uuid)
{
$local_uuid = add_to_local_config($anvil);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_uuid => $local_uuid }});
}
exit;
# Now configure!
if ($local_uuid)
{
# Start checks
my $running = $anvil->System->check_daemon({daemon => "postgresql"});
@ -87,7 +97,10 @@ if ($local_id)
# Setup postgresql.conf
my $postgresql_backup = $anvil->data->{path}{directories}{backups}."/pgsql/postgresql.conf";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { postgresql_backup => $postgresql_backup }});
$anvil->Storage->copy_file({source => $anvil->data->{path}{configs}{'postgresql.conf'}, target => $postgresql_backup});
$anvil->Storage->copy_file({
source_file => $anvil->data->{path}{configs}{'postgresql.conf'},
target_file => $postgresql_backup,
});
my $postgresql_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'postgresql.conf'}});
my $update_file = 1;
@ -126,7 +139,10 @@ if ($local_id)
my $pg_hba_backup = $anvil->data->{path}{directories}{backups}."/pgsql/pg_hba.conf";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { pg_hba_backup => $pg_hba_backup }});
$anvil->Storage->copy_file({source => $anvil->data->{path}{configs}{'pg_hba.conf'}, target => $pg_hba_backup});
$anvil->Storage->copy_file({
source_file => $anvil->data->{path}{configs}{'pg_hba.conf'},
target_file => $pg_hba_backup,
});
my $pg_hba_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'pg_hba.conf'}});
$update_file = 1;
my $new_pg_hba_conf = "";
@ -190,11 +206,11 @@ if ($local_id)
my $created_pgpass = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => {
'path::secure::postgres_pgpass' => $anvil->data->{path}{secure}{postgres_pgpass},
"database::${local_id}::password" => $anvil->data->{database}{$local_id}{password},
"database::${local_uuid}::password" => $anvil->data->{database}{$local_uuid}{password},
}});
if ((not -e $anvil->data->{path}{secure}{postgres_pgpass}) && ($anvil->data->{database}{$local_id}{password}))
if ((not -e $anvil->data->{path}{secure}{postgres_pgpass}) && ($anvil->data->{database}{$local_uuid}{password}))
{
my $body = "*:*:*:postgres:".$anvil->data->{database}{$local_id}{password}."\n";
my $body = "*:*:*:postgres:".$anvil->data->{database}{$local_uuid}{password}."\n";
$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},
@ -214,12 +230,12 @@ if ($local_id)
# Does the database user exist?
my $create_user = 1;
my $database_user = $anvil->data->{database}{$local_id}{user} ? $anvil->data->{database}{$id}{user} : $anvil->data->{sys}{database}{user};
my $database_user = $anvil->data->{database}{$local_uuid}{user} ? $anvil->data->{database}{$local_uuid}{user} : $anvil->data->{sys}{database}{user};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { database_user => $database_user }});
if (not $database_user)
{
# No database user defined
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0099", variables => { id => $local_id }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0099", variables => { uuid => $local_uuid }});
exit(3);
}
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__});
@ -261,11 +277,11 @@ if ($local_id)
}
# Update/set the passwords.
if ($anvil->data->{database}{$local_id}{password})
if ($anvil->data->{database}{$local_uuid}{password})
{
foreach my $user ("postgres", $database_user)
{
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}{$local_id}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__});
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}{$local_uuid}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { update_output => $update_output }});
foreach my $line (split/\n/, $user_list)
{
@ -281,7 +297,7 @@ if ($local_id)
# Create the database, if needed.
my $create_database = 1;
my $database_name = $anvil->data->{database}{$local_id}{name} ? $anvil->data->{database}{$local_id}{name} : $anvil->data->{sys}{database}{name};
my $database_name = $anvil->data->{database}{$local_uuid}{name} ? $anvil->data->{database}{$local_uuid}{name} : $anvil->data->{sys}{database}{name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { database_name => $database_name }});
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__});
@ -394,3 +410,74 @@ exit(0);
#############################################################################################################
# Functions #
#############################################################################################################
# This adds this machine to the local anvil.conf file.
sub add_to_local_config
{
my ($anvil) = @_;
my $host_uuid = $anvil->Get->host_uuid();
my $local_uuid = "";
my $anvil_conf_body = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
host_uuid => $host_uuid,
anvil_conf_body => $anvil_conf_body,
}});
# Setup the data we'll insert. The password will be changed after the user does the initial config.
my $insert = "database::".$host_uuid."::host = localhost\n";
$insert .= "database::".$host_uuid."::port = 5432\n";
$insert .= "database::".$host_uuid."::password = Initial1\n";
$insert .= "database::".$host_uuid."::ping = 0\n\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { insert => $insert }});
# Now inject the config.
my $new_body = "";
my $config_seen = 0;
my $test_line = "database::${host_uuid}::";
foreach my $line (split/\n/, $anvil_conf_body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /^$test_line/)
{
# It's already configured, abort.
$config_seen = 1;
$local_uuid = $host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
config_seen => $config_seen,
local_uuid => $local_uuid,
}});
}
if ($line eq "### end db list ###")
{
$new_body .= $insert;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_body => $new_body }});
}
$new_body .= $line."\n";
}
# If we're here, we're ready to write it out.
if (not $config_seen)
{
# Backup the original
my $backup_file = $anvil->Storage->backup({file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { backup_file => $backup_file }});
# Now update!
$anvil->Storage->write_file({
file => $anvil->data->{path}{configs}{'anvil.conf'},
body => $new_body,
user => "admin",
group => "admin",
mode => "0644",
overwrite => 1,
});
# Record the local UUID for returning to the caller.
$local_uuid = $host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_uuid => $local_uuid }});
}
return($local_uuid);
}

Loading…
Cancel
Save