package Anvil::Tools ;
#
# This is the "root" package that manages the sub modules and controls access to their methods.
#
BEGIN
{
our $ VERSION = "3.0.0" ;
# This suppresses the 'could not find ParserDetails.ini in /PerlApp/XML/SAX' warning message in
# XML::Simple calls.
#$ENV{HARNESS_ACTIVE} = 1;
}
use strict ;
use warnings ;
use Scalar::Util qw( weaken isweak ) ;
use Time::HiRes ;
use Data::Dumper ;
use CGI ;
my $ THIS_FILE = "Tools.pm" ;
### Methods;
# data
# environment
# nice_exit
# refresh
# _add_hash_reference
# _anvil_version
# _make_hash_reference
# _set_defaults
# _set_paths
use utf8 ;
binmode ( STDERR , ':encoding(utf-8)' ) ;
binmode ( STDOUT , ':encoding(utf-8)' ) ;
# I intentionally don't use EXPORT, @ISA and the like because I want my "subclass"es to be accessed in a
# somewhat more OO style. I know some may wish to strike me down for this, but I like the idea of accessing
# methods via their containing module's name. (A La: C<< $anvil->Module->method >> rather than C<< $anvil->method >>).
use Anvil::Tools::Account ;
use Anvil::Tools::Alert ;
use Anvil::Tools::Cluster ;
use Anvil::Tools::Convert ;
use Anvil::Tools::Database ;
use Anvil::Tools::DRBD ;
use Anvil::Tools::Email ;
use Anvil::Tools::Get ;
use Anvil::Tools::Job ;
use Anvil::Tools::Log ;
use Anvil::Tools::Network ;
use Anvil::Tools::Remote ;
use Anvil::Tools::ScanCore ;
use Anvil::Tools::Server ;
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
use Anvil::Tools::Striker ;
use Anvil::Tools::Storage ;
use Anvil::Tools::System ;
use Anvil::Tools::Template ;
use Anvil::Tools::Words ;
use Anvil::Tools::Validate ;
= pod
= encoding utf8
= head1 NAME
Anvil:: Tools
Provides a common oject handle to all Anvil::Tools:: * module methods and handles invocation configuration .
= head1 SYNOPSIS
use Anvil::Tools ;
# Get a common object handle on all Anvil::Tools::* modules.
my $ anvil = Anvil::Tools - > new ( ) ;
# Again, but this time sets some initial values in the '$anvil->data' hash.
my $ anvil = Anvil::Tools - > new (
{
data = > {
foo = > "" ,
bar = > [] ,
baz = > { } ,
} ,
} ) ;
# This example gets the handle and also sets the default user and log
# languages as Japanese, sets a custom log file and sets the log level to
# '2'.
my $ anvil = Anvil::Tools - > new (
{
'Log' = > {
user_language = > "jp" ,
log_language = > "jp"
level = > 2 ,
} ,
} ) ;
= head1 DESCRIPTION
The Anvil:: Tools module and all sub - modules are designed for use by Alteeve - based applications . It can be used as a general framework by anyone interested .
Core features are ;
* Supports per user , per logging language selection where translations from from XML - formatted "String" files that support UTF8 and variable substitutions .
* Support for command - line and HTML output . Skinning support for HTML - based user interfaces .
* Redundant database access , resynchronization and archiving .
* Highly - native with minimal use of external perl modules and compiled code .
= head1 METHODS
Methods in the core module ;
= cut
# The constructor through which all other module's methods will be accessed.
sub new
{
my $ class = shift ;
my $ parameter = shift ;
my $ self = {
HANDLE = > {
ACCOUNT = > Anvil::Tools::Account - > new ( ) ,
ALERT = > Anvil::Tools::Alert - > new ( ) ,
CLUSTER = > Anvil::Tools::Cluster - > new ( ) ,
CONVERT = > Anvil::Tools::Convert - > new ( ) ,
DATABASE = > Anvil::Tools::Database - > new ( ) ,
DRBD = > Anvil::Tools::DRBD - > new ( ) ,
EMAIL = > Anvil::Tools::Email - > new ( ) ,
GET = > Anvil::Tools::Get - > new ( ) ,
LOG = > Anvil::Tools::Log - > new ( ) ,
JOB = > Anvil::Tools::Job - > new ( ) ,
NETWORK = > Anvil::Tools::Network - > new ( ) ,
REMOTE = > Anvil::Tools::Remote - > new ( ) ,
SCANCORE = > Anvil::Tools::ScanCore - > new ( ) ,
SERVER = > Anvil::Tools::Server - > new ( ) ,
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
STRIKER = > Anvil::Tools::Striker - > new ( ) ,
STORAGE = > Anvil::Tools::Storage - > new ( ) ,
SYSTEM = > Anvil::Tools::System - > new ( ) ,
TEMPLATE = > Anvil::Tools::Template - > new ( ) ,
WORDS = > Anvil::Tools::Words - > new ( ) ,
VALIDATE = > Anvil::Tools::Validate - > new ( ) ,
# This is to be removed before development ends.
'log' = > {
main = > "" ,
} ,
} ,
DATA = > { } ,
ENV_VALUES = > {
ENVIRONMENT = > 'cli' ,
} ,
HOST = > {
# This is the host's UUID. It should never be manually set.
UUID = > "" ,
ANVIL_VERSION = > "" ,
} ,
} ;
# Bless you!
bless $ self , $ class ;
# This isn't needed, but it makes the code below more consistent with and portable to other modules.
my $ anvil = $ self ;
weaken ( $ anvil ) ; # Helps avoid memory leaks. See Scalar::Utils
# Get a handle on the various submodules
$ anvil - > Account - > parent ( $ anvil ) ;
$ anvil - > Alert - > parent ( $ anvil ) ;
$ anvil - > Cluster - > parent ( $ anvil ) ;
$ anvil - > Convert - > parent ( $ anvil ) ;
$ anvil - > Database - > parent ( $ anvil ) ;
$ anvil - > DRBD - > parent ( $ anvil ) ;
$ anvil - > Email - > parent ( $ anvil ) ;
$ anvil - > Get - > parent ( $ anvil ) ;
$ anvil - > Log - > parent ( $ anvil ) ;
$ anvil - > Job - > parent ( $ anvil ) ;
$ anvil - > Network - > parent ( $ anvil ) ;
$ anvil - > Remote - > parent ( $ anvil ) ;
$ anvil - > ScanCore - > parent ( $ anvil ) ;
$ anvil - > Server - > parent ( $ anvil ) ;
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
$ anvil - > Striker - > parent ( $ anvil ) ;
$ anvil - > Storage - > parent ( $ anvil ) ;
$ anvil - > System - > parent ( $ anvil ) ;
$ anvil - > Template - > parent ( $ anvil ) ;
$ anvil - > Words - > parent ( $ anvil ) ;
$ anvil - > Validate - > parent ( $ anvil ) ;
# Set some system paths and system default variables
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
$ anvil - > _set_defaults ( ) ;
$ anvil - > _set_paths ( ) ;
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
# Record the start time.
$ anvil - > data - > { ENV_VALUES } { START_TIME } = Time::HiRes:: time ;
# Set passed parameters if needed.
my $ debug = 3 ;
if ( ref ( $ parameter ) eq "HASH" )
{
# Local parameters...
if ( $ parameter - > { log_level } )
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
{
$ anvil - > Log - > level ( { set = > $ parameter - > { log_level } } ) ;
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
}
if ( $ parameter - > { log_secure } )
{
$ anvil - > Log - > secure ( { set = > $ parameter - > { log_secure } } ) ;
}
if ( $ parameter - > { debug } )
{
$ debug = $ parameter - > { debug } ;
}
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
}
elsif ( $ parameter )
{
# Um...
print $ THIS_FILE . " " . __LINE__ . "; Anvil::Tools->new() invoked with an invalid parameter. Expected a hash reference, but got: [$parameter]\n" ;
exit ( 1 ) ;
}
# This will help clean up if we catch a signal.
$ SIG { INT } = sub { $ anvil - > catch_sig ( { signal = > "INT" } ) ; } ;
$ SIG { TERM } = sub { $ anvil - > catch_sig ( { signal = > "TERM" } ) ; } ;
# This sets the environment this program is running in.
if ( $ ENV { SERVER_NAME } )
{
$ anvil - > environment ( "html" ) ;
# There is no PWD environment variable, so we'll use 'DOCUMENT_ROOT' as 'PWD'
$ ENV { PWD } = $ ENV { DOCUMENT_ROOT } ;
}
else
{
$ anvil - > environment ( "cli" ) ;
}
# Setup my '$anvil->data' hash right away so that I have a place to store the strings hash.
$ anvil - > data ( $ parameter - > { data } ) if $ parameter - > { data } ;
# Initialize the list of directories to seach.
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
$ anvil - > Storage - > search_directories ( { debug = > $ debug , initialize = > 1 } ) ;
# I need to read the initial words early.
$ anvil - > Words - > read ( { debug = > $ debug } ) ;
# If the local './anvil.conf' file exists, read it in.
if ( - r $ anvil - > data - > { path } { configs } { 'anvil.conf' } )
{
$ anvil - > Storage - > read_config ( { debug = > 3 , file = > $ anvil - > data - > { path } { configs } { 'anvil.conf' } } ) ;
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
### TODO: Should anvil.conf override parameters?
# Let parameters override config file values.
if ( $ parameter - > { log_level } )
{
$ anvil - > Log - > level ( { set = > $ parameter - > { log_level } } ) ;
}
if ( $ parameter - > { log_secure } )
{
$ anvil - > Log - > secure ( { set = > $ parameter - > { log_secure } } ) ;
}
}
# Get the local host UUID.
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
$ anvil - > Get - > host_uuid ( { debug = > $ debug } ) ;
# Read in any command line switches.
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
$ anvil - > Get - > switches ( { debug = > $ debug } ) ;
# Read in the local Anvil! version.
#...
return ( $ self ) ;
}
#############################################################################################################
# Public methods #
#############################################################################################################
= head2 data
This is the method used to access the main hash reference that all user - accessible values are stored in . This includes words , configuration file variables and so forth .
When called without an argument , it returns the existing '$anvil->data' hash reference .
my $ anvil = $ anvil - > data ( ) ;
When called with a hash reference as the argument , it sets '$anvil->data' to the new hash .
my $ some_hash = { } ;
my $ anvil = $ anvil - > data ( $ some_hash ) ;
Data can be entered into or access by treating '$anvil->data' as a normal hash reference .
my $ anvil = Anvil::Tools - > new (
{
data = > {
foo = > "" ,
bar = > [ 6 , 4 , 12 ] ,
baz = > {
animal = > "Cat" ,
thing = > "Boat" ,
} ,
} ,
} ) ;
# Copy the 'Cat' value into the $animal variable.
my $ animal = $ anvil - > data - > { baz } { animal } ;
# Set 'A thing' in 'foo'.
$ anvil - > data - > { foo } = "A thing" ;
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
The C << $ anvil >> variable is set inside all modules and acts as shared storage for variables , values and references in all modules . It acts as the core storage for most applications using Anvil:: Tools .
= cut
sub data
{
my ( $ anvil ) = shift ;
# Pick up the passed in hash, if any.
$ anvil - > { DATA } = shift if $ _ [ 0 ] ;
return ( $ anvil - > { DATA } ) ;
}
= head2 environment
This is the method used to check or set whether the program is outputting to command line or a browser .
When called without an argument , it returns the current environment .
if ( $ anvil - > environment ( ) eq "cli" )
{
# format for STDOUT
}
elsif ( $ anvil - > environment ( ) eq "html" )
{
# Use the template system to output HTML
}
When called with a string as the argument , that string will be set as the environment string .
$ anvil - > environment ( "cli" ) ;
Technically , any string can be used , however only 'cli' or 'html' are used by convention .
= cut
sub environment
{
my ( $ anvil ) = shift ;
weaken ( $ anvil ) ;
# Pick up the passed in delimiter, if any.
if ( $ _ [ 0 ] )
{
$ anvil - > data - > { ENV_VALUES } { ENVIRONMENT } = shift ;
# Load the CGI stuff if we're in a browser
if ( $ anvil - > data - > { ENV_VALUES } { ENVIRONMENT } eq "html" )
{
CGI::Carp - > import ( qw( fatalsToBrowser ) ) ;
}
}
return ( $ anvil - > data - > { ENV_VALUES } { ENVIRONMENT } ) ;
}
= head2 nice_exit
This is a simple method to exit cleanly , closing database connections and exiting with the set exit code .
Parameters ;
= head3 exit_code ( optional )
If set , this will be the exit code . The default is to exit with code C << 0 >> .
= cut
sub nice_exit
{
my $ self = shift ;
my $ parameter = shift ;
my $ anvil = $ self ;
my $ debug = defined $ parameter - > { debug } ? $ parameter - > { debug } : 3 ;
my $ exit_code = defined $ parameter - > { exit_code } ? $ parameter - > { exit_code } : 0 ;
# Close database connections (if any).
$ anvil - > Database - > disconnect ( { debug = > $ debug } ) ;
# Report the runtime.
my $ end_time = Time::HiRes:: time ;
my $ run_time = $ end_time - $ anvil - > data - > { ENV_VALUES } { START_TIME } ;
my $ caller = ( $ 0 =~ /^.*\/(.*)$/ ) [ 0 ] ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:ENV_VALUES::START_TIME' = > $ anvil - > data - > { ENV_VALUES } { START_TIME } ,
's2:end_time' = > $ end_time ,
's3:run_time' = > $ run_time ,
's4:caller' = > $ caller ,
} } ) ;
$ anvil - > Log - > entry ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , key = > "log_0135" , variables = > { 'caller' = > $ caller , runtime = > $ run_time } } ) ;
my ( $ package , $ filename , $ line ) = caller ;
$ anvil - > Log - > variables ( { source = > $ THIS_FILE , line = > __LINE__ , level = > $ debug , list = > {
's1:package' = > $ package ,
's2:filename' = > $ filename ,
's3:line' = > $ line ,
} } ) ;
# Close the log file.
if ( $ anvil - > data - > { HANDLE } { 'log' } { main } )
{
close $ anvil - > data - > { HANDLE } { 'log' } { main } ;
$ anvil - > data - > { HANDLE } { 'log' } { main } = "" ;
}
#print "Exiting with RC: [".$exit_code."]\n";
exit ( $ exit_code ) ;
}
= head2 refresh
This method re - reads the configuration file and resets paths , defaults and re - reads the words file ( s ) .
= cut
sub refresh
{
my $ self = shift ;
my $ parameter = shift ;
my $ anvil = $ self ;
my $ debug = defined $ parameter - > { debug } ? $ parameter - > { debug } : 3 ;
$ anvil - > _set_paths ( ) ;
$ anvil - > _set_defaults ( ) ; # This reset the log level
$ anvil - > Storage - > read_config ( ) ; # This reset the log level also
$ anvil - > Get - > switches ; # Re-read to let switches override again.
$ anvil - > Words - > read ( ) ;
return ( 0 ) ;
}
#############################################################################################################
# Public methods used to access sub modules. #
#############################################################################################################
= head1 Submodule Access Methods
The methods below are used to access methods of submodules using 'C<< $anvil->Module->method() >>' .
= cut
= head2 Account
Access the C <Acount.pm> methods via 'C<< $anvil->Alert->method >>' .
= cut
sub Account
{
my $ self = shift ;
return ( $ self - > { HANDLE } { ACCOUNT } ) ;
}
= head2 Alert
Access the C <Alert.pm> methods via 'C<< $anvil->Alert->method >>' .
= cut
sub Alert
{
my $ self = shift ;
return ( $ self - > { HANDLE } { ALERT } ) ;
}
= head2 Cluster
Access the C <Cluster.pm> methods via 'C<< $anvil->Cluster->method >>' .
= cut
sub Cluster
{
my $ self = shift ;
return ( $ self - > { HANDLE } { CLUSTER } ) ;
}
= head2 Convert
Access the C <Convert.pm> methods via 'C<< $anvil->Convert->method >>' .
= cut
sub Convert
{
my $ self = shift ;
return ( $ self - > { HANDLE } { CONVERT } ) ;
}
= head2 Database
Access the C <Database.pm> methods via 'C<< $anvil->Database->method >>' .
= cut
sub Database
{
my $ self = shift ;
return ( $ self - > { HANDLE } { DATABASE } ) ;
}
= head2 DRBD
Access the C <DRBD.pm> methods via 'C<< $anvil->DRBD->method >>' .
= cut
sub DRBD
{
my $ self = shift ;
return ( $ self - > { HANDLE } { DRBD } ) ;
}
= head2 Email
Access the C <Email.pm> methods via 'C<< $anvil->Email->method >>' .
= cut
sub Email
{
my $ self = shift ;
return ( $ self - > { HANDLE } { EMAIL } ) ;
}
= head2 Get
Access the C <Get.pm> methods via 'C<< $anvil->Get->method >>' .
= cut
sub Get
{
my $ self = shift ;
return ( $ self - > { HANDLE } { GET } ) ;
}
= head2 Job
Access the C <Job.pm> methods via 'C<< $anvil->Log->method >>' .
= cut
sub Job
{
my $ self = shift ;
return ( $ self - > { HANDLE } { JOB } ) ;
}
= head2 Log
Access the C <Log.pm> methods via 'C<< $anvil->Log->method >>' .
= cut
sub Log
{
my $ self = shift ;
return ( $ self - > { HANDLE } { LOG } ) ;
}
= head2 Network
Access the C <Network.pm> methods via 'C<< $anvil->Network->method >>' .
= cut
sub Network
{
my $ self = shift ;
return ( $ self - > { HANDLE } { NETWORK } ) ;
}
= head2 Remote
Access the C <Remote.pm> methods via 'C<< $anvil->Remote->method >>' .
= cut
sub Remote
{
my $ self = shift ;
return ( $ self - > { HANDLE } { REMOTE } ) ;
}
= head2 ScanCore
Access the C <ScanCore.pm> methods via 'C<< $anvil->ScanCore->method >>' .
= cut
sub ScanCore
{
my $ self = shift ;
return ( $ self - > { HANDLE } { SCANCORE } ) ;
}
= head2 Server
Access the C <Server.pm> methods via 'C<< $anvil->Server->method >>' .
= cut
sub Server
{
my $ self = shift ;
return ( $ self - > { HANDLE } { SERVER } ) ;
}
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
= head2 Striker
Access the C <Striker.pm> methods via 'C<< $anvil->Striker->method >>' .
= cut
sub Striker
{
my $ self = shift ;
return ( $ self - > { HANDLE } { STRIKER } ) ;
}
= head2 Storage
Access the C <Storage.pm> methods via 'C<< $anvil->Storage->method >>' .
= cut
sub Storage
{
my $ self = shift ;
return ( $ self - > { HANDLE } { STORAGE } ) ;
}
= head2 System
Access the C <System.pm> methods via 'C<< $anvil->System->method >>' .
= cut
sub System
{
my $ self = shift ;
return ( $ self - > { HANDLE } { SYSTEM } ) ;
}
= head2 Template
Access the C <Template.pm> methods via 'C<< $anvil->Template->method >>' .
= cut
sub Template
{
my $ self = shift ;
return ( $ self - > { HANDLE } { TEMPLATE } ) ;
}
= head2 Words
Access the C <Words.pm> methods via 'C<< $anvil->Words->method >>' .
= cut
sub Words
{
my $ self = shift ;
return ( $ self - > { HANDLE } { WORDS } ) ;
}
= head2 Validate
Access the C <Validate.pm> methods via 'C<< $anvil->Validate->method >>' .
= cut
sub Validate
{
my $ self = shift ;
return ( $ self - > { HANDLE } { VALIDATE } ) ;
}
= head1 Private Functions ;
These methods generally should never be called from a program using Anvil:: Tools . However , we are not your boss .
= cut
#############################################################################################################
# Private methods #
#############################################################################################################
= head2 _add_hash_reference
This is a helper to the '$anvil->_make_hash_reference' method . It is called each time a new string is to be created as a new hash key in the passed hash reference .
NOTE: Contributed by Shaun Fryer and Viktor Pavlenko by way of Toronto Perl Mongers .
= cut
sub _add_hash_reference
{
my $ self = shift ;
my $ href1 = shift ;
my $ href2 = shift ;
for my $ key ( keys %$ href2 )
{
if ( ref $ href1 - > { $ key } eq 'HASH' )
{
$ self - > _add_hash_reference ( $ href1 - > { $ key } , $ href2 - > { $ key } ) ;
}
else
{
$ href1 - > { $ key } = $ href2 - > { $ key } ;
}
}
}
= head2 _anvil_version
= cut
sub _anvil_version
{
my $ self = shift ;
my $ anvil = $ self ;
$ anvil - > { HOST } { ANVIL_VERSION } = "" if not defined $ anvil - > { HOST } { ANVIL_VERSION } ;
$ anvil - > { HOST } { SCHEMA_VERSION } = "" if not defined $ anvil - > { HOST } { SCHEMA_VERSION } ;
if ( $ anvil - > { HOST } { ANVIL_VERSION } eq "" )
{
# Try to read the local Anvil! version.
( $ anvil - > { HOST } { ANVIL_VERSION } , $ anvil - > { HOST } { SCHEMA_VERSION } ) = $ anvil - > Get - > anvil_version ( ) ;
}
return ( $ anvil - > { HOST } { ANVIL_VERSION } , $ anvil - > { HOST } { SCHEMA_VERSION } ) ;
}
= head2 _get_hash_reference
This is called when we need to parse a double - colon separated string into two or more elements which represent keys in the 'C<< $anvil->data >>' hash . Once suitably split up , the value is read and returned .
For example ;
$ anvil - > data - > { foo } { bar } = "baz" ;
my $ value = $ anvil - > _get_hash_reference ( { key = > "foo::bar" } ) ;
The 'C<< $value >>' now contains "C<< baz >>" .
NOTE: If the key is not found , 'C<< undef >>' is returned .
Parameters ;
= head3 key ( required )
This is the key to return the value for . If it is not passed , or if it does not have 'C<< :: >>' in it , 'C<< undef >>' will be returned .
= cut
sub _get_hash_reference
{
# 'href' is the hash reference I am working on.
my $ self = shift ;
my $ parameter = shift ;
my $ anvil = $ self ;
if ( $ parameter - > { key } !~ /::/ )
{
print "$THIS_FILE " . __LINE__ . "; The hash key string: [" . $ parameter - > { key } . "] doesn't seem to be valid. It should be a string in the format 'foo::bar::baz'.\n" ;
$ anvil - > nice_exit ( { exit_code = > 1 } ) ;
}
# Split up the keys.
my $ key = $ parameter - > { key } ? $ parameter - > { key } : "" ;
my $ value = undef ; # We return 'undef' so that the caller can tell the difference between an empty string versus nothing found.
if ( $ key =~ /::/ )
{
my @ keys = split /::/ , $ key ;
my $ last_key = pop @ keys ;
# Re-order the array.
my $ current_hash_ref = $ anvil - > data ;
foreach my $ key ( @ keys )
{
$ current_hash_ref = $ current_hash_ref - > { $ key } ;
}
$ value = $ current_hash_ref - > { $ last_key } ;
}
return ( $ value ) ;
}
= head2 _make_hash_reference
This takes a string with double - colon seperators and divides on those double - colons to create a hash reference where each element is a hash key .
NOTE: Contributed by Shaun Fryer and Viktor Pavlenko by way of Toronto Perl Mongers .
= cut
sub _make_hash_reference
{
my $ self = shift ;
my $ href = shift ;
my $ key_string = shift ;
my $ value = shift ;
my @ keys = split /::/ , $ key_string ;
my $ last_key = pop @ keys ;
my $ _href = { } ;
$ _href - > { $ last_key } = $ value ;
while ( my $ key = pop @ keys )
{
my $ elem = { } ;
$ elem - > { $ key } = $ _href ;
$ _href = $ elem ;
}
$ self - > _add_hash_reference ( $ href , $ _href ) ;
}
= head2 _set_defaults
This sets default variable values for the program .
= cut
sub _set_defaults
{
my ( $ anvil ) = shift ;
$ anvil - > data - > { scancore } = {
timing = > {
# Delay between DB connection attempts when no databases are available?
agent_runtime = > 30 ,
db_retry_interval = > 2 ,
# Delay between scans?
run_interval = > 60 ,
} ,
database = > {
# This is the number of hours, after which, transient data (like temperature and
# power data) is considered "old" and gets deleted from the database.
age_out = > 24 ,
} ,
} ;
$ anvil - > data - > { sys } = {
apache = > {
user = > "admin" ,
} ,
daemon = > {
dhcpd = > "dhcpd.service" ,
firewalld = > "firewalld.service" ,
httpd = > "httpd.service" ,
postgresql = > "postgresql.service" ,
tftp = > "tftp.socket" ,
} ,
daemons = > {
restart_firewalld = > 1 ,
} ,
database = > {
archive = > {
compress = > 1 ,
count = > 50000 ,
directory = > "/usr/local/anvil/archives/" ,
division = > 6000 ,
trigger = > 100000 ,
} ,
connections = > 0 ,
failed_connection_log_level = > 1 ,
local_lock_active = > 0 ,
local_uuid = > "" ,
locking_reap_age = > 300 ,
log_transactions = > 0 ,
maximum_batch_size = > 25000 ,
read_uuid = > "" ,
* Fixed a bug where setting the debug level to 3 caused a deep recursion and a system hang.
* Update Anvil::Tools->new() to access the parameters 'log_level', 'log_secure' and 'debug', streamlining the frequent calls to $anvil->Log->level and ->secure in program startup, and allowing the values to take effect during the ->new constructor.
* Passed 'debug' to child method calls in more places (still more to do though).
* Fixed a bug where 'test_table' wasn't set in the right place, causing the database to try to initialize repeatedly.
* Made Database->archive_database only run if called with root access.
* Now the number of database connections are stored in 'sys::db_connections' instead of checking the returned number, and that is cleared on disconnect.
* Started working more on 'anvil-daemon', including adding support for System->call being taking 'background', 'stderr_file' and 'stdout_file' paramters which, when set, used Proc::Simple to background the process.
* Did some more work on database archiving, though still far from done.
Signed-off-by: Digimer <digimer@alteeve.ca>
7 years ago
test_table = > "hosts" ,
timestamp = > "" ,
use_handle = > "" ,
} ,
host_type = > "" ,
host_uuid = > "" ,
language = > "en_CA" ,
'log' = > {
date = > 1 ,
# Stores the '-v|...|-vvv' so that shell calls can be run at the same level as the
# avtive program when set by the user at the command line.
level = > "" ,
# This is set to '1' when a log call might become recursive.
disable = > 0 ,
} ,
manage = > {
firewall = > 1 ,
} ,
password = > {
algorithm = > "sha512" ,
hash_count = > 500000 ,
salt_length = > 16 ,
} ,
privacy = > {
strong = > 0 ,
} ,
ram_limits = > {
striker = > 3221225472 , # 3 GiB
node = > 1073741824 , # 1 GiB
dr = > 2147483648 , # 2 GiB
} ,
# On actual RHEL systems, this will be used to ensure that given repos are enabled on given
# machines types. Obviously, this requires that the host has been subscribed.
rhel = > {
repos = > {
common = > [ "codeready-builder-for-rhel-8-x86_64-rpms" ] ,
dashboard = > [] ,
dr = > [] ,
node = > [ "rhel-8-for-x86_64-highavailability-rpms" ] ,
} ,
} ,
servers = > {
# This is the list of OSes short in the user's short list of OS types to
# optimize for. The full the list is available by running:
# /usr/bin/osinfo-query os
os_short_list = > "rhel8.7,rhel9.1,win10,win2k19,win2k22" ,
} ,
terminal = > {
columns = > 80 ,
stty = > "" ,
} ,
use_base2 = > 1 ,
user = > {
name = > "admin" ,
cookie_valid = > 0 ,
language = > "en_CA" ,
skin = > "alteeve" ,
} ,
# This is data filled from the active user's database table.
users = > {
user_name = > "" ,
user_password_hash = > "" ,
user_salt = > "" ,
user_algorithm = > "" ,
user_hash_count = > "" ,
user_language = > "" ,
user_is_admin = > "" ,
user_is_experienced = > "" ,
user_is_trusted = > "" ,
} ,
} ;
$ anvil - > data - > { defaults } = {
database = > {
locking = > {
reap_age = > 300 ,
}
} ,
language = > {
# Default language for all output shown to a user.
output = > 'en_CA' ,
} ,
limits = > {
# This is the maximum number of times we're allow to loop when injecting variables
# into a string being processed in Anvil::Tools::Words->string();
string_loops = > 1000 ,
} ,
'log' = > {
db_transactions = > 0 ,
facility = > "local0" ,
language = > "en_CA" ,
level = > 1 ,
secure = > 0 ,
server = > "" ,
tag = > "anvil" ,
} ,
# NOTE: These are here to allow foreign users to override western defaults in anvil.conf.
kickstart = > {
keyboard = > "--vckeymap=us --xlayouts='us'" ,
password = > "Initial1" ,
timezone = > "Etc/GMT --isUtc" ,
} ,
# See 'striker' -> 'sub generate_ip()' function comments for details on how m3 IPs are handled.
network = > {
# BCN starts at 10.200(+n)/16
bcn = > {
network = > "10.200.0.0" ,
subnet_mask = > "255.255.0.0" ,
switch_octet3 = > "1" ,
pdu_octet3 = > "2" ,
ups_octet3 = > "3" ,
striker_octet3 = > "4" ,
striker_ipmi_octet3 = > "5" ,
} ,
dns = > "8.8.8.8, 8.8.4.4" ,
# The IFN will not be under our control. So for suggestion to the user purpose only,
# IFN starts at 10.255/16
ifn = > {
network = > "10.255.0.0" ,
subnet_mask = > "255.255.0.0" ,
striker_octet3 = > "4" ,
} ,
# SN starts at 10.100(+n)/16
sn = > {
network = > "10.100.0.0" ,
subnet_mask = > "255.255.0.0" ,
} ,
test = > {
domains = > [ "alteeve.com" , "redhat.com" , "google.com" ] ,
} ,
} ,
template = > {
html = > "alteeve" ,
} ,
} ;
$ anvil - > data - > { feature } = {
scancore = > {
disable = > {
'preventative-live-migration' = > 0 ,
} ,
threshold = > {
'preventative-live-migration' = > 2 ,
} ,
} ,
} ;
return ( 0 ) ;
}
= head2 _set_paths
This sets default paths to many system commands , checking to make sure the binary exists at the path and , if not , try to find it .
= cut
sub _set_paths
{
my ( $ anvil ) = shift ;
# Executables
$ anvil - > data - > { path } = {
configs = > {
'alteeve-release.repo' = > "/etc/yum.repos.d/alteeve-release.repo" ,
'anvil.conf' = > "/etc/anvil/anvil.conf" ,
'anvil.version' = > "/etc/anvil/anvil.version" ,
'autoindex.conf' = > "/etc/httpd/conf.d/autoindex.conf" ,
'cib.xml' = > "/var/lib/pacemaker/cib/cib.xml" ,
'corosync.conf' = > "/etc/corosync/corosync.conf" ,
'dhcpd.conf' = > "/etc/dhcp/dhcpd.conf" ,
'dnf.conf' = > "/etc/dnf/dnf.conf" ,
'drbd-proxy.license' = > "/etc/drbd-proxy.license" ,
'firewalld.conf' = > "/etc/firewalld/firewalld.conf" ,
'global-common.conf' = > "/etc/drbd.d/global_common.conf" ,
hostname = > "/etc/hostname" ,
hosts = > "/etc/hosts" ,
'httpd.conf' = > "/etc/httpd/conf/httpd.conf" ,
'journald_anvil' = > "/etc/systemd/journald.conf.d/anvil.conf" ,
'journald.conf' = > "/etc/systemd/journald.conf" ,
'logind.conf' = > "/etc/systemd/logind.conf" ,
'lvm.conf' = > "/etc/lvm/lvm.conf" ,
'pg_hba.conf' = > "/var/lib/pgsql/data/pg_hba.conf" ,
'postgresql.conf' = > "/var/lib/pgsql/data/postgresql.conf" ,
pxe_default = > "/var/lib/tftpboot/pxelinux.cfg/default" ,
pxe_uefi = > "/var/lib/tftpboot/pxelinux.cfg/uefi" ,
pxe_grub = > "/var/lib/tftpboot/grub.cfg" ,
postfix_main = > "/etc/postfix/main.cf" ,
postfix_relay_password = > "/etc/postfix/relay_password" ,
'qemu.conf' = > "/etc/libvirt/qemu.conf" ,
ssh_config = > "/etc/ssh/ssh_config" ,
'type.striker' = > "/etc/anvil/type.striker" ,
'type.dr' = > "/etc/anvil/type.dr" ,
'type.node' = > "/etc/anvil/type.node" ,
} ,
data = > {
'.htpasswd' = > "/etc/httpd/.htpasswd" ,
'chrony.conf' = > "/etc/chrony.conf" ,
group = > "/etc/group" ,
httpd_conf = > "/etc/httpd/conf/httpd.conf" ,
host_configured = > "/etc/anvil/host.configured" ,
host_ssh_key = > "/etc/ssh/ssh_host_ecdsa_key.pub" ,
host_uuid = > "/etc/anvil/host.uuid" ,
issue = > "/etc/issue" ,
network_cache = > "/tmp/network_cache.anvil" ,
passwd = > "/etc/passwd" ,
'redhat-release' = > "/etc/redhat-release" ,
* Moved the fences_unified_metadata file from /tmp, which apache can not read, to /var/www/html/.
* Fixed a bug (well, made a work-around for an issue without a known reproducer) where, on some occassion, a record will end up in the public table without being copied into the history schema. When this happens, the next resync would crash out because the resynd reads in the history table only. Now, when about to INSERT a record into the public schema during a resync, an explicit check is made to see if the record alread
y exists. If it does, the INSERT is instead redirected to the history schema.
* Cleaned up the fence agent metadata when displaying to a user, converting the shell codes to underline a string with square brackets instead. We also now replace newlines with <br /> tags. Lastly, to help fence_azure_arm's metadata description to display cleanly, a check is made to format the table correctly.
* Began work on the Striker menu for handling fence device management
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
fences_unified_metadata = > "/var/www/html/fences_unified_metadata.xml" ,
} ,
devices = > {
stdout = > "/dev/stdout" ,
} ,
directories = > {
alert_emails = > "/var/spool/anvil" ,
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
anvil = > "/etc/anvil" ,
backups = > "/root/anvil-backups" ,
bonds = > "/proc/net/bonding" ,
'cgi-bin' = > "/var/www/cgi-bin" ,
drbd_resources = > "/etc/drbd.d/" ,
fence_agents = > "/usr/sbin" ,
firewalld_services = > "/usr/lib/firewalld/services" ,
firewalld_zones_etc = > "/etc/firewalld/zones" , # Changes when firewall-cmd ... --permanent is used.
firewalld_zones = > "/usr/lib/firewalld/zones" ,
html = > "/var/www/html" ,
ifcfg = > "/etc/sysconfig/network-scripts" ,
journald = > "/var/log/journal" ,
libvirtd_definitions = > "/etc/libvirt/qemu/" ,
* WIP: Working on a new method of failing over between which Striker is the active database, instead of running N-number of databases all the time.
* Created Database->backup_database() that creates a pg_dump of the active database.
* Created Database->load_database() that loads the database from a flat file, optionally creating a backup before doing so, and using iptables to block access during the process.
* Updated Database->configure_pgsql() to not start the postgresql daemon unless it just initialized the DB.
* Much work, not yet complete, to Database->connect() to stop after the first successful connection. Added logic that, if not connection was established and the host is a Striker, to load a peer's backup, if it exists, and then start the local daemon.
* Updated anvil-daemon to now have a section to run tasks on a ten minute cycle, which will later be used for the primary Striker to dump / copy its database to peer(s).
Signed-off-by: Madison Kelly <mkelly@alteeve.ca>
3 years ago
pgsql = > "/var/lib/pgsql/" ,
resource_status = > "/sys/kernel/debug/drbd/resources" ,
scan_agents = > "/usr/sbin/scancore-agents" ,
shared = > {
archives = > "/mnt/shared/archives" ,
base = > "/mnt/shared" ,
definitions = > "/mnt/shared/definitions" ,
files = > "/mnt/shared/files" ,
incoming = > "/mnt/shared/incoming" ,
provision = > "/mnt/shared/provision" ,
temp = > "/mnt/shared/temp" ,
} ,
skins = > "/var/www/html/skins" ,
status = > "/var/www/html/status" ,
syslinux = > "/usr/share/syslinux" ,
tftpboot = > "/var/lib/tftpboot" ,
temp = > "/tmp/anvil" ,
tmp = > "/tmp" ,
tools = > "/usr/sbin" ,
units = > "/usr/lib/systemd/system" ,
} ,
exe = > {
akmods = > "/usr/sbin/akmods" ,
'alteeve-repo-setup' = > "/usr/sbin/alteeve-repo-setup" ,
'anvil-boot-server' = > "/usr/sbin/anvil-boot-server" ,
'anvil-change-password' = > "/usr/sbin/anvil-change-password" ,
'anvil-check-memory' = > "/usr/sbin/anvil-check-memory" ,
'anvil-configure-host' = > "/usr/sbin/anvil-configure-host" ,
'anvil-daemon' = > "/usr/sbin/anvil-daemon" ,
'anvil-delete-server' = > "/usr/sbin/anvil-delete-server" ,
'anvil-download-file' = > "/usr/sbin/anvil-download-file" ,
'anvil-file-details' = > "/usr/sbin/anvil-file-details" ,
'anvil-get-server-screenshot' = > "/usr/sbin/anvil-get-server-screenshot" ,
'anvil-join-anvil' = > "/usr/sbin/anvil-join-anvil" ,
'anvil-maintenance-mode' = > "/usr/sbin/anvil-maintenance-mode" ,
'anvil-manage-dr' = > "/usr/sbin/anvil-manage-dr" ,
'anvil-manage-files' = > "/usr/sbin/anvil-manage-files" ,
'anvil-manage-firewall' = > "/usr/sbin/anvil-manage-firewall" ,
'anvil-manage-keys' = > "/usr/sbin/anvil-manage-keys" ,
'anvil-manage-power' = > "/usr/sbin/anvil-manage-power" ,
'anvil-migrate-server' = > "/usr/sbin/anvil-migrate-server" ,
'anvil-parse-fence-agents' = > "/usr/sbin/anvil-parse-fence-agents" ,
'anvil-provision-server' = > "/usr/sbin/anvil-provision-server" ,
'anvil-report-memory' = > "/usr/sbin/anvil-report-memory" ,
The core logic is done!!!! Still need to finish end-points for the WebUI to hook into, but the core of M3 is complete! Many, many bugs are expected, of course. :)
* Created DRBD->check_if_syncsource() and ->check_if_synctarget() that return '1' if the target host is currently SyncSource or SyncTarget for any resource, respectively.
* Updated DRBD->update_global_common() to return the unified-format diff if any changes were made to global-common.conf.
* Created ScanCore->check_health() that returns the health score for a host. Created ->count_servers() that returns the number of servers on a host, how much RAM is used by those servers and, if available, the estimated migration time of the servers. Updated ->check_temperature() to set/clear/return the time that a host has been in a warning or critical temperature state.
* Finished ScanCore->post_scan_analysis_node()!!! It certainly has bugs, and much testing is needed, but the logic is all in place! Oh what a slog that was... It should be far more intelligent than M2 though, once flushed out and tested.
* Created Server->active_migrations() that returns '1' if any servers are in a migration on an Anvil! system. Updated ->migrate_virsh() to record how long a migration took in the "server::migration_duration" variable, which is averaged by ScanCore->count_servers() to estimate migration times.
* Updated scan-drbd to check/update the global-common.conf file's config at the end of a scan.
* Updated ScanCore itself to not scan when in maintenance mode. Also updated it to call 'anvil-safe-start' when ScanCore starts, so long as it is within ten minutes of the host booting.
Signed-off-by: Digimer <digimer@alteeve.ca>
4 years ago
'anvil-safe-start' = > "/usr/sbin/anvil-safe-start" ,
'anvil-safe-stop' = > "/usr/sbin/anvil-safe-stop" ,
'anvil-shutdown-server' = > "/usr/sbin/anvil-shutdown-server" ,
'anvil-sync-shared' = > "/usr/sbin/anvil-sync-shared" ,
'anvil-update-files' = > "/usr/sbin/anvil-update-files" ,
'anvil-update-states' = > "/usr/sbin/anvil-update-states" ,
'anvil-update-system' = > "/usr/sbin/anvil-update-system" ,
'anvil-version-changes' = > "/usr/sbin/anvil-version-changes" ,
augtool = > "/usr/bin/augtool" ,
base64 = > "/usr/bin/base64" ,
blockdev = > "/usr/sbin/blockdev" ,
bridge = > "/usr/sbin/bridge" ,
bzip2 = > "/usr/bin/bzip2" ,
'call_striker-get-peer-data' = > "/usr/sbin/call_striker-get-peer-data" ,
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
cat = > "/usr/bin/cat" ,
'chmod' = > "/usr/bin/chmod" ,
'chown' = > "/usr/bin/chown" ,
chronyc = > "/usr/bin/chronyc" ,
cibadmin = > "/usr/sbin/cibadmin" ,
'corosync-quorumtool' = > "/usr/sbin/corosync-quorumtool" ,
cp = > "/usr/bin/cp" ,
createdb = > "/usr/bin/createdb" ,
createrepo_c = > "/usr/bin/createrepo_c" ,
createuser = > "/usr/bin/createuser" ,
crm_attribute = > "/usr/sbin/crm_attribute" ,
crm_error = > "/usr/sbin/crm_error" ,
crm_resource = > "/usr/sbin/crm_resource" ,
crm_mon = > "/usr/sbin/crm_mon" ,
df = > "/usr/bin/df" ,
dmidecode = > "/usr/sbin/dmidecode" ,
dnf = > "/usr/bin/dnf" ,
drbdadm = > "/usr/sbin/drbdadm" ,
drbdsetup = > "/usr/sbin/drbdsetup" ,
* WIP: Working on a new method of failing over between which Striker is the active database, instead of running N-number of databases all the time.
* Created Database->backup_database() that creates a pg_dump of the active database.
* Created Database->load_database() that loads the database from a flat file, optionally creating a backup before doing so, and using iptables to block access during the process.
* Updated Database->configure_pgsql() to not start the postgresql daemon unless it just initialized the DB.
* Much work, not yet complete, to Database->connect() to stop after the first successful connection. Added logic that, if not connection was established and the host is a Striker, to load a peer's backup, if it exists, and then start the local daemon.
* Updated anvil-daemon to now have a section to run tasks on a ten minute cycle, which will later be used for the primary Striker to dump / copy its database to peer(s).
Signed-off-by: Madison Kelly <mkelly@alteeve.ca>
3 years ago
dropdb = > "/usr/bin/dropdb" ,
echo = > "/usr/bin/echo" ,
ethtool = > "/usr/sbin/ethtool" ,
expect = > "/usr/bin/expect" ,
fence_pacemaker = > "/usr/sbin/fence_pacemaker" ,
'firewall-cmd' = > "/usr/bin/firewall-cmd" ,
free = > "/usr/bin/free" ,
gcc = > "/usr/bin/gcc" ,
getent = > "/usr/bin/getent" ,
gethostip = > "/usr/bin/gethostip" ,
'grep' = > "/usr/bin/grep" ,
groupadd = > "/usr/sbin/groupadd" ,
head = > "/usr/bin/head" ,
hostnamectl = > "/usr/bin/hostnamectl" ,
hpacucli = > "/usr/sbin/hpacucli" ,
htpasswd = > "/usr/bin/htpasswd" ,
httpd = > "/usr/sbin/httpd" ,
ifdown = > "/sbin/ifdown" ,
ifup = > "/sbin/ifup" ,
ip = > "/usr/sbin/ip" ,
iperf3 = > "/usr/bin/iperf3" ,
'ipmi-oem' = > "/usr/sbin/ipmi-oem" ,
ipmitool = > "/usr/bin/ipmitool" ,
### NOTE: When Network->manage_firewall() is done, search for and replace all
* WIP: Working on a new method of failing over between which Striker is the active database, instead of running N-number of databases all the time.
* Created Database->backup_database() that creates a pg_dump of the active database.
* Created Database->load_database() that loads the database from a flat file, optionally creating a backup before doing so, and using iptables to block access during the process.
* Updated Database->configure_pgsql() to not start the postgresql daemon unless it just initialized the DB.
* Much work, not yet complete, to Database->connect() to stop after the first successful connection. Added logic that, if not connection was established and the host is a Striker, to load a peer's backup, if it exists, and then start the local daemon.
* Updated anvil-daemon to now have a section to run tasks on a ten minute cycle, which will later be used for the primary Striker to dump / copy its database to peer(s).
Signed-off-by: Madison Kelly <mkelly@alteeve.ca>
3 years ago
### instances where iptables is called and replace with firewall-cmd
### calls
iptables = > "/usr/sbin/iptables" ,
'iptables-save' = > "/usr/sbin/iptables-save" ,
journalctl = > "/usr/bin/journalctl" ,
'kill' = > "/usr/bin/kill" ,
logger = > "/usr/bin/logger" ,
ls = > "/usr/bin/ls" ,
lsblk = > "/usr/bin/lsblk" ,
lshw = > "/usr/sbin/lshw" ,
lspci = > "/usr/sbin/lspci" ,
lvchange = > "/usr/sbin/lvchange" ,
lvcreate = > "/usr/sbin/lvcreate" ,
lvdisplay = > "/usr/sbin/lvdisplay" ,
lvremove = > "/usr/sbin/lvremove" ,
lvrename = > "/usr/sbin/lvrename" ,
lvs = > "/usr/sbin/lvs" ,
lvscan = > "/usr/sbin/lvscan" ,
mailx = > "/usr/bin/mailx" ,
man = > "/usr/bin/man" ,
md5sum = > "/usr/bin/md5sum" ,
'mkdir' = > "/usr/bin/mkdir" ,
modifyrepo_c = > "/usr/bin/modifyrepo_c" ,
modprobe = > "/usr/sbin/modprobe" ,
mv = > "/usr/bin/mv" ,
nmap = > "/usr/bin/nmap" ,
nmcli = > "/bin/nmcli" ,
ocf_alteeve = > "/usr/lib/ocf/resource.d/alteeve/server" ,
openssl = > "/usr/bin/openssl" ,
'osinfo-query' = > "/usr/bin/osinfo-query" ,
pamscale = > "/usr/bin/pamscale" ,
pamtopng = > "/usr/bin/pamtopng" ,
passwd = > "/usr/bin/passwd" ,
pcs = > "/usr/sbin/pcs" ,
perccli64 = > "/opt/MegaRAID/perccli/perccli64" ,
pidof = > "/usr/sbin/pidof" ,
ping = > "/usr/bin/ping" ,
* WIP: Working on a new method of failing over between which Striker is the active database, instead of running N-number of databases all the time.
* Created Database->backup_database() that creates a pg_dump of the active database.
* Created Database->load_database() that loads the database from a flat file, optionally creating a backup before doing so, and using iptables to block access during the process.
* Updated Database->configure_pgsql() to not start the postgresql daemon unless it just initialized the DB.
* Much work, not yet complete, to Database->connect() to stop after the first successful connection. Added logic that, if not connection was established and the host is a Striker, to load a peer's backup, if it exists, and then start the local daemon.
* Updated anvil-daemon to now have a section to run tasks on a ten minute cycle, which will later be used for the primary Striker to dump / copy its database to peer(s).
Signed-off-by: Madison Kelly <mkelly@alteeve.ca>
3 years ago
pg_dump = > "/usr/bin/pg_dump" ,
pgrep = > "/usr/bin/pgrep" ,
ps = > "/usr/bin/ps" ,
psql = > "/usr/bin/psql" ,
'postgresql-setup' = > "/usr/bin/postgresql-setup" ,
postmap = > "/usr/sbin/postmap" ,
postqueue = > "/usr/sbin/postqueue" ,
pwd = > "/usr/bin/pwd" ,
pvs = > "/usr/sbin/pvs" ,
pvscan = > "/usr/sbin/pvscan" ,
rm = > "/usr/bin/rm" ,
rpm = > "/usr/bin/rpm" ,
rsync = > "/usr/bin/rsync" ,
sed = > "/usr/bin/sed" ,
setsid = > "/usr/bin/setsid" , # See: https://serverfault.com/questions/1105733/virsh-command-hangs-when-script-runs-in-the-background
'shutdown' = > "/usr/sbin/shutdown" ,
snmpget = > "/usr/bin/snmpget" ,
snmpset = > "/usr/bin/snmpset" ,
'ssh-keygen' = > "/usr/bin/ssh-keygen" ,
'ssh-keyscan' = > "/usr/bin/ssh-keyscan" ,
'stat' = > "/usr/bin/stat" ,
storcli64 = > "/opt/MegaRAID/storcli/storcli64" ,
strings = > "/usr/bin/strings" ,
'striker-auto-initialize-all' = > "/usr/sbin/striker-auto-initialize-all" ,
'striker-get-peer-data' = > "/usr/sbin/striker-get-peer-data" ,
'striker-initialize-host' = > "/usr/sbin/striker-initialize-host" ,
'striker-manage-install-target' = > "/usr/sbin/striker-manage-install-target" ,
'striker-manage-peers' = > "/usr/sbin/striker-manage-peers" ,
'striker-manage-vnc-pipes' = > "/usr/sbin/striker-manage-vnc-pipes" ,
'striker-open-ssh-tunnel' = > "/usr/sbin/striker-open-ssh-tunnel" ,
'striker-parse-oui' = > "/usr/sbin/striker-parse-oui" ,
'striker-prep-database' = > "/usr/sbin/striker-prep-database" ,
'striker-scan-network' = > "/usr/sbin/striker-scan-network" ,
stty = > "/usr/bin/stty" ,
su = > "/usr/bin/su" ,
* Got the node/dr host initialization form to the point where it can test access and decide if it should show the Red Hat account form. Decided that for M3, node/dr host setup will now be a four-stage process; initial install (over PXE), initialization (install the proper anvil-{node,dr} RPM and connect to the database), setup/map the network, and then add to an Anvil! pair.
* Updated striker to no longer try to SSH to a remote machine. To enable this, we'd have to give apache a shell and an SSH key, which is dumb and dangerous when considered.
* Created tools/striker-get-peer-data which is meant to be invoked as the 'admin' user (via a setuid c-wrapper). It collects basic data about a target machine and reports what it finds on STDOUT. It gets the password for the target via the database.
* Updated anvil-daemon to check/create/update setuid c-wrapper(s), which for now is limited to call_striker-initialize-host.
* Created Anvil/Tools/Striker.pm to store Striker web-specific methods, including get_peer_data() which calls tools/striker-initialize-host via the setuid admin call_striker-initialize-host c-wrapper.
* In order to allow striker via apache to read a peer's anvil.version, which it can no longer do over SSH, any connection to a peer where the anvil.version is read is cached as /etc/anvil/anvil.<peer>.version. When Get->anvil_version is called as 'apache', this file is read instead.
* Updated Database->resync_databases() and ->_find_behind_databases() to ignore the 'states' table.
* Created tools/striker-initialize-host which will be called as a job to initialize a node/dr host.
Signed-off-by: Digimer <digimer@alteeve.ca>
5 years ago
'subscription-manager' = > "/usr/sbin/subscription-manager" ,
swapon = > "/usr/sbin/swapon" ,
sysctl = > "/usr/sbin/sysctl" ,
systemctl = > "/usr/bin/systemctl" ,
timeout = > "/usr/bin/timeout" ,
touch = > "/usr/bin/touch" ,
tput = > "/usr/bin/tput" ,
'tr' = > "/usr/bin/tr" ,
uname = > "/usr/bin/uname" ,
unfence_pacemaker = > "/usr/sbin/unfence_pacemaker" ,
unzip = > "/usr/bin/unzip" ,
useradd = > "/usr/sbin/useradd" ,
usermod = > "/usr/sbin/usermod" ,
uuidgen = > "/usr/bin/uuidgen" ,
virsh = > "/usr/bin/virsh" ,
'virt-install' = > "/usr/bin/virt-install" ,
wipefs = > "/usr/sbin/wipefs" ,
vgs = > "/usr/sbin/vgs" ,
vgscan = > "/usr/sbin/vgscan" ,
wc = > "/usr/bin/wc" ,
wget = > "/usr/bin/wget" ,
yum = > "/usr/bin/yum" ,
} ,
json = > {
all_status = > "all_status.json" ,
files = > "files.json" ,
} ,
'lock' = > {
database = > "/tmp/anvil-tools.database.lock" ,
} ,
'log' = > {
main = > "/var/log/anvil.log" ,
alert = > "/var/log/anvil.alert.log" ,
} ,
proc = > {
cpuinfo = > "/proc/cpuinfo" ,
meminfo = > "/proc/meminfo" ,
uptime = > "/proc/uptime" ,
} ,
secure = > {
postgres_pgpass = > "/var/lib/pgsql/.pgpass" ,
} ,
sql = > {
'anvil.sql' = > "/usr/share/anvil/anvil.sql" ,
} ,
systemd = > {
httpd_enabled_symlink = > "/etc/systemd/system/multi-user.target.wants/httpd.service" ,
tftp_enabled_symlink = > "/etc/systemd/system/sockets.target.wants/tftp.socket" ,
} ,
urls = > {
skins = > "/skins" ,
oui_file = > "http://standards.ieee.org/develop/regauth/oui/oui.txt" ,
alteeve_repo = > "https://www.alteeve.com/an-repo/m3/anvil-release-latest.noarch.rpm" ,
} ,
words = > {
'words.xml' = > "/usr/share/anvil/words.xml" ,
} ,
} ;
# Make sure we actually have the requested files.
foreach my $ type ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { path } } )
{
# We don't look for urls because they're relative to the domain. We also don't look for
# configs as we might find backups.
next if $ type eq "urls" ;
next if $ type eq "configs" ;
foreach my $ file ( sort { $ a cmp $ b } keys % { $ anvil - > data - > { path } { $ type } } )
{
if ( not - e $ anvil - > data - > { path } { $ type } { $ file } )
{
my $ full_path = $ anvil - > Storage - > find ( { file = > $ file } ) ;
if ( ( $ full_path ) && ( $ full_path ne "#!not_found!#" ) )
{
$ anvil - > data - > { path } { $ type } { $ file } = $ full_path ;
}
}
}
} ;
return ( 0 ) ;
}
= head1 Exit Codes
= head2 C <1>
Anvil::Tools - > new ( ) passed something other than a hash reference .
= head2 C <2>
Failed to find the requested file in C << Anvil::Tools::Storage - > find >> and 'fatal' was set .
= head1 Requirements
The following packages are required on EL7 .
* C <expect>
* C <httpd>
* C <mailx>
* C <perl-Test-Simple>
* C <policycoreutils-python>
* C <postgresql>
* C <syslinux>
* C <perl-XML-Simple>
= head1 Recommended Packages
The following packages provide non - critical functionality .
* C <subscription-manager>
= cut
# This catches SIGINT and SIGTERM and fires out an email before shutting down.
sub catch_sig
{
my $ self = shift ;
my $ parameter = shift ;
my $ anvil = $ self ;
my $ signal = $ parameter - > { signal } ? $ parameter - > { signal } : "" ;
if ( $ signal )
{
print "\n\nProcess with PID: [$$] exiting on SIG" . $ signal . ".\n" ;
if ( $ anvil - > data - > { sys } { terminal } { stty } )
{
# Restore the terminal.
print "Restoring the terminal\n" ;
$ anvil - > System - > call ( { shell_call = > $ anvil - > data - > { path } { exe } { stty } . " " . $ anvil - > data - > { sys } { terminal } { stty } } ) ;
}
}
# Exit with '0' so shutdowns from systemd doesn't think we failed.
$ anvil - > nice_exit ( { exit_code = > 0 } ) ;
}
1 ;