* Started work on M3 ScanCore!

* Started expanding Alert->register_alert() to actually implement it.
* Improved handling errors in Words->key().
* Started work on Striker's "Anvil!" menu section. Also cleaned up the power handling.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 6 years ago
parent 5eb1a41994
commit f5ae90c941
  1. 11
      Anvil/Tools.pm
  2. 274
      Anvil/Tools/Alert.pm
  3. 7
      Anvil/Tools/Template.pm
  4. 23
      Anvil/Tools/Words.pm
  5. 120
      ScanCore/ScanCore
  6. 230
      cgi-bin/striker
  7. 27
      html/skins/alteeve/anvil.html
  8. 117
      notes
  9. 14
      rpm/SPECS/anvil.spec
  10. 164
      share/anvil.sql
  11. 10
      share/words.xml
  12. 5
      tools/anvil-daemon
  13. 1
      tools/striker-manage-install-target
  14. 12
      units/ScanCore.service

@ -732,7 +732,15 @@ sub _set_defaults
{ {
my ($anvil) = shift; my ($anvil) = shift;
$anvil->data->{sys} = { $anvil->data->{ScanCore} = {
timing => {
# Delay between DB connection attempts when no databases are available?
db_retry_interval => 2,
# Delay between scans?
run_interval => 30,
},
};
$anvil->data->{sys} = {
apache => { apache => {
user => "admin", user => "admin",
}, },
@ -928,6 +936,7 @@ sub _set_paths
firewalld_zones => "/etc/firewalld/zones", firewalld_zones => "/etc/firewalld/zones",
html => "/var/www/html", html => "/var/www/html",
ifcfg => "/etc/sysconfig/network-scripts", ifcfg => "/etc/sysconfig/network-scripts",
scan_agents => "/usr/sbin/ScanCore/agents",
skins => "/var/www/html/skins", skins => "/var/www/html/skins",
syslinux => "/usr/share/syslinux", syslinux => "/usr/share/syslinux",
tftpboot => "/var/lib/tftpboot", tftpboot => "/var/lib/tftpboot",

@ -286,7 +286,73 @@ WHERE
This registers an alert to be sent later. This registers an alert to be sent later.
If anything goes wrong, C<< !!error!! >> will be returned. The C<< alert_uuid >> is returned on success. If anything goes wrong, C<< !!error!! >> will be returned.
Parameters;
=head3 alert_level (required)
This assigns an severity level to the alert. Any recipient listening to this level or higher will receive this alert.
=head4 1 (critical)
Alerts at this level will go to all recipients, except for those ignoring the source system entirely.
This is reserved for alerts that could lead to imminent service interruption or unexpected loss of redundancy.
Alerts at this level should trigger alarm systems for all administrators as well as management who may be impacted by service interruptions.
=head4 2 (warning)
This is used for alerts that require attention from administrators. Examples include intentional loss of redundancy caused by load shedding, hardware in pre-failure, loss of input power, temperature anomalies, etc.
Alerts at this level should trigger alarm systems for administrative staff.
=head4 3 (notice)
This is used for alerts that are generally safe to ignore, but might provide early warnings of developing issues or insight into system behaviour.
Alerts at this level should not trigger alarm systems. Periodic review is sufficient.
=head4 4 (info)
This is used for alerts that are almost always safe to ignore, but may be useful in testing and debugging.
=head3 message (required)
This is the message body of the alert. It is expected to be in the format C<< <string_key> >>. If variables are to be injected into the C<< string_key >>, a comma-separated list in the format C<< !!variable_name1!value1!![,!!variable_nameN!valueN!!] >> is used.
Example with a message alone; C<< foo_0001 >>.
Example with two variables; C<< foo_0002,!!bar!abc!!,!!baz!123!! >>.
=head3 set_by (required)
This is the name of the program that registered this alert. Usually this is simply the caller's C<< $THIS_FILE >> or C<< $0 >> variable.
=head3 show_header (optional, default '1')
When set to C<< 0 >>, only the alert message body is shown, and the title is omitted. This can be useful when a set of alerts are sorted under a common title.
=head3 sort_position (optional, default '9999')
This is used to keep a set of alerts in a certain order when converted to an message body. By default, all alerts have a default value of '9999', so they will be sorted using their severity level, and then the time they were entered into the system. If this is set to a number lower than this, then the value here will sort/prioritize messages over the severity/time values. If two or more alerts have the same sort position, then severity and then time stamps will be used.
In brief; alert messages are sorted in this order;
1. C<< sort_position >>
2. c<< alert_level >>
3. C<< timestamp >>
NOTE: The timestamp is generally set for a given program or agent run (set when connecting to the database), NOT by the real time of the database insert. For this reason, relying on the timestamp alone will not generally give the desired results, and why C<< sort_position >> exists.
=head3 title (optional)
NOTE: This is required if C<< show_header >> is set!
This is the title of the alert. It is expected to be in the format C<< <string_key> >>. If variables are to be injected into the C<< string_key >>, a comma-separated list in the format C<< !!variable_name1!value1!![,!!variable_nameN!valueN!!] >> is used.
Example with a message alone; C<< foo_0001 >>.
Example with two variables; C<< foo_0002,!!bar!abc!!,!!baz!123!! >>.
=cut =cut
sub register_alert sub register_alert
@ -294,66 +360,53 @@ sub register_alert
my $self = shift; my $self = shift;
my $parameter = shift; my $parameter = shift;
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 2; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $header = defined $parameter->{header} ? $parameter->{header} : 1; my $alert_level = defined $parameter->{alert_level} ? $parameter->{alert_level} : 0;
my $level = defined $parameter->{level} ? $parameter->{level} : "warning"; my $message = defined $parameter->{message} ? $parameter->{message} : "";
my $message_key = defined $parameter->{message_key} ? $parameter->{message_key} : ""; my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : "";
my $message_variables = defined $parameter->{message_variables} ? $parameter->{message_variables} : ""; my $show_header = defined $parameter->{show_header} ? $parameter->{show_header} : 1;
my $set_by = defined $parameter->{set_by} ? $parameter->{set_by} : ""; my $sort_position = defined $parameter->{sort_position} ? $parameter->{sort_position} : 9999;
my $sort = defined $parameter->{'sort'} ? $parameter->{'sort'} : 9999; my $title = defined $parameter->{title} ? $parameter->{title} : "title_0003";
my $title_key = defined $parameter->{title_key} ? $parameter->{title_key} : "title_0003";
my $title_variables = defined $parameter->{title_variables} ? $parameter->{title_variables} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
header => $header, show_header => $show_header,
level => $level, alert_level => $alert_level,
message_key => $message_key, message => $message,
message_variables => $message_variables, set_by => $set_by,
set_by => $set_by, sort_position => $sort_position,
'sort' => $sort, title => $title,
title_key => $title_key,
title_variables => $title_variables,
}}); }});
if (not $alert_level)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "alert_level" }});
return("!!error!!");
}
if (not $set_by) if (not $set_by)
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "set_by" }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "set_by" }});
return("!!error!!"); return("!!error!!");
} }
if (not $message_key) if (not $message)
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "message_key" }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register_alert()", parameter => "message" }});
return("!!error!!"); return("!!error!!");
} }
if (($header) && (not $title_key)) if (($show_header) && (not $title))
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0101"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0101"});
return("!!error!!"); return("!!error!!");
} }
# zero-pad sort numbers so that they sort properly. # zero-pad sort numbers so that they sort properly.
$sort = sprintf("%04d", $sort); $sort_position = sprintf("%04d", $sort_position);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_sort => $sort }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { sort_position => $sort_position }});
# Convert the hash of title variables and message variables into '!!x!y!!,!!a!b!!,...' strings.
if (ref($title_variables) eq "HASH")
{
foreach my $key (sort {$a cmp $b} keys %{$title_variables})
{
$title_variables->{$key} = "--" if not defined $title_variables->{$key};
$title_variables .= "!!$key!".$title_variables->{$key}."!!,";
}
}
if (ref($message_variables) eq "HASH")
{
foreach my $key (sort {$a cmp $b} keys %{$message_variables})
{
$message_variables->{$key} = "--" if not defined $message_variables->{$key};
$message_variables .= "!!$key!".$message_variables->{$key}."!!,";
}
}
=cut
# In most cases, no one is listening to 'debug' or 'info' level alerts. If that is the case here, # In most cases, no one is listening to 'debug' or 'info' level alerts. If that is the case here,
# don't record the alert because it can cause the history.alerts table to grow needlessly. So find # don't record the alert because it can cause the history.alerts table to grow needlessly. So find
# the lowest level log level actually being listened to and simply skip anything lower than that. # the lowest level log level actually being listened to and simply skip anything lower than that.
@ -404,7 +457,7 @@ sub register_alert
if ($this_level > $lowest_log_level) if ($this_level > $lowest_log_level)
{ {
# Return. # Return.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0102", variables => { message_key => $message_key }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0102", variables => { message => $message }});
return(0); return(0);
} }
@ -417,29 +470,26 @@ INSERT INTO
alert_host_uuid, alert_host_uuid,
alert_set_by, alert_set_by,
alert_level, alert_level,
alert_title_key, alert_title,
alert_title_variables, alert_message,
alert_message_key, alert_sort_position,
alert_message_variables, alert_show_header,
alert_sort,
alert_header,
modified_date modified_date
) VALUES ( ) VALUES (
".$anvil->data->{sys}{database}{use_handle}->quote($anvil->Get->uuid()).", ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->Get->uuid()).",
".$anvil->data->{sys}{database}{use_handle}->quote($anvil->data->{sys}{host_uuid}).", ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->data->{sys}{host_uuid}).",
".$anvil->data->{sys}{database}{use_handle}->quote($set_by).", ".$anvil->data->{sys}{database}{use_handle}->quote($set_by).",
".$anvil->data->{sys}{database}{use_handle}->quote($level).", ".$anvil->data->{sys}{database}{use_handle}->quote($level).",
".$anvil->data->{sys}{database}{use_handle}->quote($title_key).", ".$anvil->data->{sys}{database}{use_handle}->quote($title).",
".$anvil->data->{sys}{database}{use_handle}->quote($title_variables).", ".$anvil->data->{sys}{database}{use_handle}->quote($message).",
".$anvil->data->{sys}{database}{use_handle}->quote($message_key).", ".$anvil->data->{sys}{database}{use_handle}->quote($sort_position).",
".$anvil->data->{sys}{database}{use_handle}->quote($message_variables).", ".$anvil->data->{sys}{database}{use_handle}->quote($show_header).",
".$anvil->data->{sys}{database}{use_handle}->quote($sort).",
".$anvil->data->{sys}{database}{use_handle}->quote($header).",
".$anvil->data->{sys}{database}{use_handle}->quote($anvil->data->{sys}{database}{timestamp})." ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->data->{sys}{database}{timestamp})."
); );
"; ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
=cut
return(0); return(0);
} }
@ -455,122 +505,6 @@ sub error
my $parameter = shift; my $parameter = shift;
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
# $anvil->Log->entry({log_level => $debug, title_key => "tools_log_0001", title_variables => { function => "error" }, message_key => "tools_log_0002", file => $THIS_FILE, line => __LINE__});
#
# # Setup default values
# my $title_key = $parameter->{title_key} ? $parameter->{title_key} : $anvil->String->get({key => "an_0004"});
# my $title_variables = $parameter->{title_variables} ? $parameter->{title_variables} : "";
# my $message_key = $parameter->{message_key} ? $parameter->{message_key} : $anvil->String->get({key => "an_0005"});
# my $message_variables = $parameter->{message_variables} ? $parameter->{message_variables} : "";
# my $code = $parameter->{code} ? $parameter->{code} : 1;
# my $file = $parameter->{file} ? $parameter->{file} : $anvil->String->get({key => "an_0006"});
# my $line = $parameter->{line} ? $parameter->{line} : "";
# #print "$THIS_FILE ".__LINE__."; title_key: [$title_key], title_variables: [$title_variables], message_key: [$message_key], message_variables: [$message_variables], code: [$code], file: [$file], line: [$line]\n";
#
# # It is possible for this to become a run-away call, so this helps
# # catch when that happens.
# $anvil->_error_count($anvil->_error_count + 1);
# if ($anvil->_error_count > $anvil->_error_limit)
# {
# print "Infinite loop detected while trying to print an error:\n";
# print "- title_key: [$title_key]\n";
# print "- title_variables: [$title_variables]\n";
# print "- message_key: [$message_key]\n";
# print "- message_variables: [$title_variables]\n";
# print "- code: [$code]\n";
# print "- file: [$file]\n";
# print "- line: [$line]\n";
# die "Infinite loop detected while trying to print an error, exiting.\n";
# }
#
# # If the 'code' is empty and 'message' is "error_\d+", strip that code
# # off and use it as the error code.
# #print "$THIS_FILE ".__LINE__."; code: [$code], message_key: [$message_key]\n";
# if ((not $code) && ($message_key =~ /error_(\d+)/))
# {
# $code = $1;
# #print "$THIS_FILE ".__LINE__."; code: [$code], message_key: [$message_key]\n";
# }
#
# # If the title is a key, translate it.
# #print "$THIS_FILE ".__LINE__."; title_key: [$title_key]\n";
# if ($title_key =~ /\w+_\d+$/)
# {
# $title_key = $anvil->String->get({
# key => $title_key,
# variables => $title_variables,
# });
# #print "$THIS_FILE ".__LINE__."; title_key: [$title_key]\n";
# }
#
# # If the message is a key, translate it.
# #print "$THIS_FILE ".__LINE__."; message_key: [$message_key]\n";
# if ($message_key =~ /\w+_\d+$/)
# {
# $message_key = $anvil->String->get({
# key => $message_key,
# variables => $message_variables,
# });
# #print "$THIS_FILE ".__LINE__."; message_key: [$message_key]\n";
# }
#
# # Set my error string
# my $fatal_heading = $anvil->String->get({key => "an_0002"});
# #print "$THIS_FILE ".__LINE__."; fatal_heading: [$fatal_heading]\n";
#
# my $readable_line = $anvil->Readable->comma($line);
# #print "$THIS_FILE ".__LINE__."; readable_line: [$readable_line]\n";
#
# ### TODO: Copy this to 'warning'.
# # At this point, the title and message keys are the actual messages.
# my $error = "\n".$anvil->String->get({
# key => "an_0007",
# variables => {
# code => $code,
# heading => $fatal_heading,
# file => $file,
# line => $readable_line,
# title => $title_key,
# message => $message_key,
# },
# })."\n\n";
# #print "$THIS_FILE ".__LINE__."; error: [$error]\n";
#
# # Set the internal error flags
# $anvil->Alert->_set_error($error);
# $anvil->Alert->_set_error_code($code);
#
# # Append "exiting" to the error string if it is fatal.
# $error .= $anvil->String->get({key => "an_0008"})."\n";
#
# # Write a copy of the error to the log.
# $anvil->Log->entry({file => $THIS_FILE, level => 0, raw => $error});
#
# # If this is a browser calling us, print the footer so that the loading pinwheel goes away.
# if ($ENV{'HTTP_REFERER'})
# {
# $anvil->Striker->_footer();
# }
#
# # Don't actually die, but do print the error, if fatal errors have been globally disabled (as is done
# # in the tests).
# if (not $anvil->Alert->no_fatal_errors)
# {
# if ($ENV{'HTTP_REFERER'})
# {
# print "<pre>\n";
# print "$error\n" if not $anvil->Alert->no_fatal_errors;
# print "</pre>\n";
# }
# else
# {
# print "$error\n" if not $anvil->Alert->no_fatal_errors;
# }
# $anvil->data->{sys}{footer_printed} = 1;
# $anvil->nice_exit({exit_code => $code});
# }
#
# return ($code);
} }
1; 1;

@ -123,10 +123,10 @@ sub get
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $file = defined $parameter->{file} ? $parameter->{file} : ""; my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $language = defined $parameter->{language} ? $parameter->{language} : $anvil->Words->language; my $language = defined $parameter->{language} ? $parameter->{language} : $anvil->Words->language({debug => $debug});
my $name = defined $parameter->{name} ? $parameter->{name} : ""; my $name = defined $parameter->{name} ? $parameter->{name} : "";
my $show_name = defined $parameter->{show_name} ? $parameter->{show_name} : 1; my $show_name = defined $parameter->{show_name} ? $parameter->{show_name} : 1;
my $skin = defined $parameter->{skin} ? $parameter->{skin} : $anvil->Template->skin; my $skin = defined $parameter->{skin} ? $parameter->{skin} : $anvil->Template->skin({debug => $debug});
my $variables = defined $parameter->{variables} ? $parameter->{variables} : ""; my $variables = defined $parameter->{variables} ? $parameter->{variables} : "";
$skin = $anvil->data->{path}{directories}{skins}."/".$skin; $skin = $anvil->data->{path}{directories}{skins}."/".$skin;
my $template = ""; my $template = "";
@ -229,6 +229,7 @@ sub get
# Now that I have the skin, inject my variables. We'll use Words->string() to do this for us. # Now that I have the skin, inject my variables. We'll use Words->string() to do this for us.
$template = $anvil->Words->string({ $template = $anvil->Words->string({
debug => $debug,
string => $template, string => $template,
variables => $variables, variables => $variables,
}); });
@ -242,6 +243,7 @@ sub get
{ {
# Woops! # Woops!
$template = $anvil->Words->string({key => "error_0029", variables => { $template = $anvil->Words->string({key => "error_0029", variables => {
debug => $debug,
template => $name, template => $name,
file => $source, file => $source,
}}); }});
@ -252,6 +254,7 @@ sub get
if ($template eq "#!error!#") if ($template eq "#!error!#")
{ {
$template = $anvil->Words->string({key => "error_0030", variables => { $template = $anvil->Words->string({key => "error_0030", variables => {
debug => $debug,
template => $name, template => $name,
file => $source, file => $source,
}}); }});

@ -178,16 +178,16 @@ sub key
my $file = defined $parameter->{file} ? $parameter->{file} : ""; my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $string = "#!not_found!#"; my $string = "#!not_found!#";
my $error = 0; my $error = 0;
#print $THIS_FILE." ".__LINE__."; [ Debug ] - key: [$key], language: [$language], file: [$file]\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, raw => "[ Debug ] - key: [$key], language: [$language], file: [$file]"});
if (not $key) if (not $key)
{ {
#print $THIS_FILE." ".__LINE__."; Anvil::Tools::Words->key()' called without a key name to read.\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => "[ Error ] - Anvil::Tools::Words->key()' called without a key name to read."});
$error = 1; $error = 1;
} }
if (not $language) if (not $language)
{ {
#print $THIS_FILE." ".__LINE__."; Anvil::Tools::Words->key()' called without a language, and 'defaults::languages::output' is not set.\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => "[ Error ] - Anvil::Tools::Words->key()' called without a language, and 'defaults::languages::output' is not set."});
$error = 2; $error = 2;
} }
@ -195,13 +195,13 @@ sub key
{ {
foreach my $this_file (sort {$a cmp $b} keys %{$anvil->data->{words}}) foreach my $this_file (sort {$a cmp $b} keys %{$anvil->data->{words}})
{ {
#print $THIS_FILE." ".__LINE__."; [ Debug ] - this_file: [$this_file], file: [$file]\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, raw => "[ Debug ] - this_file: [$this_file], file: [$file]"});
# If they've specified a file and this doesn't match, skip it. # If they've specified a file and this doesn't match, skip it.
next if (($file) && ($this_file !~ /$file$/)); next if (($file) && ($this_file !~ /$file$/));
if (exists $anvil->data->{words}{$this_file}{language}{$language}{key}{$key}{content}) if (exists $anvil->data->{words}{$this_file}{language}{$language}{key}{$key}{content})
{ {
$string = $anvil->data->{words}{$this_file}{language}{$language}{key}{$key}{content}; $string = $anvil->data->{words}{$this_file}{language}{$language}{key}{$key}{content};
#print $THIS_FILE." ".__LINE__."; [ Debug ] - string: [$string]\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, raw => "[ Debug ] - string: [$string]"});
last; last;
} }
} }
@ -209,17 +209,10 @@ sub key
if ($string eq "#!not_found!#") if ($string eq "#!not_found!#")
{ {
my $message = "[ Error ] - Failed to find the string key: [".$key."]!!"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => "[ Error ] - Failed to find the string key: [".$key."]!!"});
print $THIS_FILE." ".__LINE__."; ".$message."\n";
$anvil->Log->entry({
level => 0,
line => __LINE__,
raw => $message,
source => $THIS_FILE,
})
} }
#print $THIS_FILE." ".__LINE__."; [ Debug ] - string: [$string]\n"; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, raw => "[ Debug ] - string: [$string]"});
return($string); return($string);
} }
@ -521,6 +514,7 @@ sub string
my $file = defined $parameter->{file} ? $parameter->{file} : $anvil->data->{path}{words}{'words.xml'}; my $file = defined $parameter->{file} ? $parameter->{file} : $anvil->data->{path}{words}{'words.xml'};
my $string = defined $parameter->{string} ? $parameter->{string} : ""; my $string = defined $parameter->{string} ? $parameter->{string} : "";
my $variables = defined $parameter->{variables} ? $parameter->{variables} : ""; my $variables = defined $parameter->{variables} ? $parameter->{variables} : "";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, raw => "[ Debug ] - key: [$key], language: [$language], file: [$file], string: [$string], variables: [$variables]"});
# If we weren't passed a raw string, we'll get the string from our ->key() method, then inject any # If we weren't passed a raw string, we'll get the string from our ->key() method, then inject any
# variables, if needed. This also handles the initial sanity checks. If we get back '#!not_found!#', # variables, if needed. This also handles the initial sanity checks. If we get back '#!not_found!#',
@ -532,6 +526,7 @@ sub string
language => $language, language => $language,
file => $file, file => $file,
}); });
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, raw => "[ Debug ] - string: [$string]"});
} }
if (($string ne "#!not_found!#") && ($string =~ /#!([^\s]+?)!#/)) if (($string ne "#!not_found!#") && ($string =~ /#!([^\s]+?)!#/))

@ -0,0 +1,120 @@
#!/usr/bin/perl
#
# This is the main ScanCore program. It is started/killed/recovered by anvil-daemon.
#
# Examples;
#
# Exit codes;
# 0 = Normal exit.
# 1 = No database connections available.
#
# TODO:
#
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
# Disable buffering
$| = 1;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1});
$anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0115", variables => { program => $THIS_FILE }});
# Read switches
$anvil->data->{switches}{'run-once'} = "";
$anvil->Get->switches;
# Connect to DBs.
$anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0003"});
# We sleep before exiting so that we don't get into a high-speed loop of systemd re-invoking us.
sleep 2;
$anvil->nice_exit({exit_code => 1});
}
# Calculate my sum so that we can exit if it changes later.
$anvil->Storage->record_md5sums;
# Disconnect. We'll reconnect inside the loop
$anvil->Database->disconnect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "log_0203"});
# The main loop
while(1)
{
# Reload defaults, re-read the config and then connect to the database(s)
$anvil->_set_paths();
$anvil->_set_defaults();
$anvil->Storage->read_config({force_read => 1, file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Database->connect({check_if_configured => $check_if_database_is_configured});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0132"});
# Mark that we don't want to check the database now.
$check_if_database_is_configured = 0;
if ($anvil->data->{sys}{database}{connections})
{
# Run the normal tasks
call_agents($anvil);
}
else
{
# No databases available, we can't do anything this run. Sleep for a couple of seconds and
# then try again.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0202"});
my $db_retry_interval = 2;
if ((exists $anvil->data->{ScanCore}{timing}{db_retry_interval}) && ($anvil->data->{ScanCore}{timing}{db_retry_interval} =~ /^\d+$/))
{
$db_retry_interval = $anvil->data->{ScanCore}{timing}{db_retry_interval};
}
sleep($db_retry_interval);
next;
}
# Exit if 'run-once' selected.
if ($anvil->data->{switches}{'run-once'})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0055"});
$anvil->nice_exit({code => 0});
}
# Disconnect from the database(s) and sleep now.
$anvil->Database->disconnect();
my $run_interval = 30;
if ((exists $anvil->data->{ScanCore}{timing}{run_interval}) && ($anvil->data->{ScanCore}{timing}{run_interval} =~ /^\d+$/))
{
$run_interval = $anvil->data->{ScanCore}{timing}{run_interval};
}
sleep($run_interval);
}
$anvil->nice_exit({code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
# This invokes all scan agents found in 'path::directories::scan_agents'
sub call_agents
{
}

@ -238,91 +238,11 @@ sub process_task
# If we're here, the user is logged in! # If we're here, the user is logged in!
if ($anvil->data->{cgi}{striker}{value}) if ($anvil->data->{cgi}{striker}{value})
{ {
$anvil->data->{form}{back_link} = "?striker=true"; process_striker_menu($anvil);
$anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value};
$anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value};
if ($anvil->data->{cgi}{task}{value} eq "sync")
{
process_sync_page($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "reconfig")
{
process_reconfig_page($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "update")
{
process_update($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "install-target")
{
process_install_target($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "reboot")
{
process_power($anvil, "reboot");
}
elsif ($anvil->data->{cgi}{task}{value} eq "poweroff")
{
process_power($anvil, "poweroff");
}
else
{
# What we show for the reboot icon and text depends on if a reboot is pending.
my $reboot_needed = $anvil->System->reboot_needed();
my $reboot_icon = $reboot_needed ? "reboot_needed_icon.png" : "reboot_icon.png";
my $reboot_message = $reboot_needed ? "#!string!striker_0093!#" : "#!string!striker_0092!#";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
reboot_needed => $reboot_needed,
reboot_icon => $reboot_icon,
reboot_message => $reboot_message,
}});
# What we show for the install target icon and text depends on if it is enabled or not.
my $install_target_title = "#!string!striker_0109!#";
my $install_target_icon = "install_target_disabled.png";
my $install_target_subtask = "unavailable";
my ($install_manifest_status, $variable_uuid, $modified_date) = $anvil->Database->read_variable({
variable_name => "install-target::enabled",
variable_source_uuid => $anvil->Get->host_uuid,
variable_source_table => "hosts",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
install_manifest_status => $install_manifest_status,
variable_uuid => $variable_uuid,
modified_date => $modified_date,
}});
if ($install_manifest_status eq "enabled")
{
# Offer the button to disable it.
$install_target_title = "#!string!striker_0108!#";
$install_target_icon = "install_target_enabled.png";
$install_target_subtask = "disable";
}
elsif ($install_manifest_status eq "disabled")
{
# Offer the button to enable it.
$install_target_title = "#!string!striker_0107!#";
$install_target_subtask = "enable";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
install_target_title => $install_target_title,
install_target_icon => $install_target_icon,
install_target_subtask => $install_target_subtask,
}});
# The 'back' goes home
$anvil->data->{form}{back_link} = "?";
$anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-setup", variables => {
reboot_icon => $reboot_icon,
reboot_message => $reboot_message,
install_target_icon => $install_target_icon,
install_target_title => $install_target_title,
install_target_subtask => $install_target_subtask,
}});
}
} }
elsif (0) elsif ($anvil->data->{cgi}{anvil}{value})
{ {
process_anvil_menu($anvil);
} }
else else
{ {
@ -344,6 +264,121 @@ sub process_task
return(0); return(0);
} }
# This handles the "Striker" menu items.
sub process_striker_menu
{
my ($anvil) = @_;
$anvil->data->{form}{back_link} = "?striker=true";
$anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value};
$anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value};
if ($anvil->data->{cgi}{task}{value} eq "sync")
{
process_sync_page($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "reconfig")
{
process_reconfig_page($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "update")
{
process_update($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "install-target")
{
process_install_target($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "reboot")
{
process_power($anvil, "reboot");
}
elsif ($anvil->data->{cgi}{task}{value} eq "poweroff")
{
process_power($anvil, "poweroff");
}
else
{
# What we show for the reboot icon and text depends on if a reboot is pending.
my $reboot_needed = $anvil->System->reboot_needed();
my $reboot_icon = $reboot_needed ? "reboot_needed_icon.png" : "reboot_icon.png";
my $reboot_message = $reboot_needed ? "#!string!striker_0093!#" : "#!string!striker_0092!#";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
reboot_needed => $reboot_needed,
reboot_icon => $reboot_icon,
reboot_message => $reboot_message,
}});
# What we show for the install target icon and text depends on if it is enabled or not.
my $install_target_title = "#!string!striker_0109!#";
my $install_target_icon = "install_target_disabled.png";
my $install_target_subtask = "unavailable";
my ($install_manifest_status, $variable_uuid, $modified_date) = $anvil->Database->read_variable({
variable_name => "install-target::enabled",
variable_source_uuid => $anvil->Get->host_uuid,
variable_source_table => "hosts",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
install_manifest_status => $install_manifest_status,
variable_uuid => $variable_uuid,
modified_date => $modified_date,
}});
if ($install_manifest_status eq "enabled")
{
# Offer the button to disable it.
$install_target_title = "#!string!striker_0108!#";
$install_target_icon = "install_target_enabled.png";
$install_target_subtask = "disable";
}
elsif ($install_manifest_status eq "disabled")
{
# Offer the button to enable it.
$install_target_title = "#!string!striker_0107!#";
$install_target_subtask = "enable";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
install_target_title => $install_target_title,
install_target_icon => $install_target_icon,
install_target_subtask => $install_target_subtask,
}});
# The 'back' goes home
$anvil->data->{form}{back_link} = "?";
$anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-setup", variables => {
reboot_icon => $reboot_icon,
reboot_message => $reboot_message,
install_target_icon => $install_target_icon,
install_target_title => $install_target_title,
install_target_subtask => $install_target_subtask,
}});
}
return(0);
}
# This handles the "Anvil" menu items.
sub process_anvil_menu
{
my ($anvil) = @_;
$anvil->data->{form}{back_link} = "?striker=true";
$anvil->data->{cgi}{task}{value} = "" if not defined $anvil->data->{cgi}{task}{value};
$anvil->data->{cgi}{action}{value} = "" if not defined $anvil->data->{cgi}{action}{value};
if ($anvil->data->{cgi}{task}{value} eq "manifest")
{
#process_manifest_page($anvil);
}
else
{
# The 'back' goes home
$anvil->data->{form}{back_link} = "?";
$anvil->data->{form}{body} = $anvil->Template->get({file => "anvil.html", name => "main-menu", variables => {
}});
}
return(0);
}
# This enables or disables the Install Target feature # This enables or disables the Install Target feature
sub process_install_target sub process_install_target
{ {
@ -401,15 +436,27 @@ sub process_power
if ($anvil->data->{cgi}{confirm}{value}) if ($anvil->data->{cgi}{confirm}{value})
{ {
# Record the job! # Record the job!
my $job_command = $anvil->data->{path}{exe}{'anvil-manage-power'}." --reboot -y";
my $job_title = "job_0009";
my $job_description = "job_0006";
my $say_title = "#!string!job_0005!#";
my $say_description = "#!string!job_0006!#";
if ($task eq "poweroff")
{
$job_command = $anvil->data->{path}{exe}{'anvil-manage-power'}." --poweroff -y";
$job_title = "job_0010";
$job_description = "job_0008";
$say_title = "#!string!job_0007!#";
$say_description = "#!string!job_0008!#";
}
my ($job_uuid) = $anvil->Database->insert_or_update_jobs({ my ($job_uuid) = $anvil->Database->insert_or_update_jobs({
debug => 3,
file => $THIS_FILE, file => $THIS_FILE,
line => __LINE__, line => __LINE__,
job_command => $task eq "poweroff" ? $anvil->data->{path}{exe}{'anvil-manage-power'}." --poweroff -y" : $anvil->data->{path}{exe}{'anvil-manage-power'}." --reboot -y", job_command => $job_command,
job_data => "", job_data => "",
job_name => "reboot::system", job_name => "reboot::system",
job_title => $task eq "poweroff" ? "job_0010" : "job_0009", job_title => $job_title,
job_description => $task eq "poweroff" ? "job_0008" : "job_0006", job_description => $job_description,
job_progress => 0, job_progress => 0,
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }});
@ -420,8 +467,8 @@ sub process_power
title_id => "", title_id => "",
message_id => "", message_id => "",
reload_url => "/cgi-bin/".$THIS_FILE, reload_url => "/cgi-bin/".$THIS_FILE,
title => $task eq "poweroff" ? "#!string!job_0007!#" : "#!string!job_0005!#", title => $say_title,
description => $task eq "poweroff" ? "#!string!job_0008!#" : "#!string!job_0006!#", description => $say_description,
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "form::body" => $anvil->data->{form}{body} }});
@ -1653,6 +1700,7 @@ ORDER BY
class => $dns_class, class => $dns_class,
extra => "", extra => "",
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { say_dns => $say_dns }});
# Which interface gets the route? # Which interface gets the route?
my $default_dg_iface = defined $anvil->data->{cgi}{dg_iface}{value} ? $anvil->data->{cgi}{dg_iface}{value} : "ifn_link1"; my $default_dg_iface = defined $anvil->data->{cgi}{dg_iface}{value} ? $anvil->data->{cgi}{dg_iface}{value} : "ifn_link1";
@ -1663,8 +1711,10 @@ ORDER BY
selected => $default_dg_iface, selected => $default_dg_iface,
class => $anvil->data->{cgi}{dg_iface}{alert} ? "input_alert" : "input_clear", class => $anvil->data->{cgi}{dg_iface}{alert} ? "input_alert" : "input_clear",
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { dg_iface_select => $dg_iface_select }});
my $dg_iface_class = $anvil->data->{cgi}{dg_iface}{alert} ? "input_alert" : "input_clear"; my $dg_iface_class = $anvil->data->{cgi}{dg_iface}{alert} ? "input_alert" : "input_clear";
my $say_dg_iface = $anvil->Template->get({file => "main.html", name => "input_select_form", variables => { my $say_dg_iface = $anvil->Template->get({debug => 1, file => "main.html", name => "input_select_form", variables => {
field => "#!string!striker_0039!#", field => "#!string!striker_0039!#",
description => "#!string!striker_0040!#", description => "#!string!striker_0040!#",
'select' => "", 'select' => "",

@ -0,0 +1,27 @@
<!-- start main-menu -->
<table align="center" class="anvil_main_menu">
<tr>
<td colspan="2">
&nbsp;
</td>
</tr>
<tr>
<td colspan="2" class="title">
#!string!striker_0113!#
</td>
</tr>
<tr>
<td colspan="2">
&nbsp;
</td>
</tr>
<tr>
<td rowspan="2" class="icon_button">
<a href="?anvil=true&task=create"><img src="#!data!skin::url!#/images/create-anvil.png" class="top_icon" ></a>
</td>
<td class="menu_title">
<a href="?anvil=true&task=sync">#!string!striker_0114!#</a>
</td>
</tr>
</table>
<!-- end main-menu -->

117
notes

@ -14,7 +14,6 @@ logvol swap --fstype="swap" --size=4096 --name=lv_swap --vgname=striker_vg0
logvol / --fstype="xfs" --size=15852 --label="lv_root" --name=lv_root --vgname=striker_vg0 logvol / --fstype="xfs" --size=15852 --label="lv_root" --name=lv_root --vgname=striker_vg0
====================================================================================== ======================================================================================
DOCS; - DOCS; -
- Explanation of 'comps.xml' (package grouping) - https://pagure.io/fedora-comps - Explanation of 'comps.xml' (package grouping) - https://pagure.io/fedora-comps
- Firewalld - Firewalld
@ -39,10 +38,8 @@ UDP 5405 corosync bcn Required on all corosync nodes (needed by corosync)
TCP 7788+ drbd sn 1 port per resource TCP 7788+ drbd sn 1 port per resource
TCP 49152-49215 virsh bcn live migration - migration_port_min and migration_port_max attributes in the /etc/libvirt/qemu.conf TCP 49152-49215 virsh bcn live migration - migration_port_min and migration_port_max attributes in the /etc/libvirt/qemu.conf
NOTE: DHCP listens to raw sockets and ignores firewalld rules. We need to stop dhcpd directly - https://kb.isc.org/docs/aa-00378 NOTE: DHCP listens to raw sockets and ignores firewalld rules. We need to stop dhcpd directly - https://kb.isc.org/docs/aa-00378
* After all changes; * After all changes;
firewall-cmd --zone=public --add-port=49152-49215/tcp --permanent firewall-cmd --zone=public --add-port=49152-49215/tcp --permanent
firewall-cmd --reload firewall-cmd --reload
@ -51,27 +48,14 @@ firewall-cmd --reload
If we want to create services or helpers later, look under - /usr/lib/firewalld/ If we want to create services or helpers later, look under - /usr/lib/firewalld/
Core firewalld configs, including defaults zones, etc - /etc/firewalld/ Core firewalld configs, including defaults zones, etc - /etc/firewalld/
* Zones are meant to deal with dynamic environments and aren't that useful in mostly static server environments * Zones are meant to deal with dynamic environments and aren't that useful in mostly static server environments
** Seem to be pre-configured sets of what is/isn't allowed. 'public' for IFN, 'work' for SN/BCN? 'external/internal' are for routing
** Configured in /etc/firewalld/zones/<zone>.xml - Create 'BCN', 'SN' and 'IFN'?
* Use 'firewall-cmd' WITHOUT '--permanent' for things like enabling the VNC port for a server. Use '--permanent' for everything else. * Use 'firewall-cmd' WITHOUT '--permanent' for things like enabling the VNC port for a server. Use '--permanent' for everything else.
==== ====
Striker as PXE server Striker as PXE server
==== ====
# NOTE: We DON'T enable DHCP. We'll turn it on as needed.
# NOTE: Apache needs to show dot-files! (anaconda looks for .treeinfo)
systemctl start tftp.socket
systemctl enable tftp.socket
# Bootloader for BIOS # Bootloader for BIOS
OS="fedora28" OS="fedora28"
mkdir /var/lib/tftpboot/ mkdir /var/lib/tftpboot/
@ -137,9 +121,6 @@ su - postgres -c "dropdb anvil" && su - postgres -c "createdb --owner admin anvi
su - postgres -c "psql anvil" su - postgres -c "psql anvil"
All systems have a UUID, even VMs. Use that for system UUID in the future.
Changes made using tools such as nmcli do not require a reload but do require the associated interface to be put down and then up again. That can be done by using commands in the following format: Changes made using tools such as nmcli do not require a reload but do require the associated interface to be put down and then up again. That can be done by using commands in the following format:
* nmcli dev disconnect interface-name * nmcli dev disconnect interface-name
@ -716,3 +697,101 @@ cat: /sys/class/block/sdb/device/transport: No such file or directory
Disk size: Disk size:
/sys/class/block/sda/size * <block size> /sys/class/block/sda/size * <block size>
=====
man pages
1 Executable programs or shell commands
5 File formats and conventions eg /etc/passwd
7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
8 System administration commands (usually only for root)
A manual page consists of several sections.
Conventional section names include NAME, SYNOPSIS, CONFIGURATION, DESCRIPTION, OPTIONS, EXIT STATUS, RETURN VALUE, ERRORS, ENVIRONMENT, FILES, VERSIONS, CONFORMING TO, NOTES, BUGS, EXAMPLE, AUTHORS, and SEE ALSO.
The following conventions apply to the SYNOPSIS section and can be used as a guide in other sections.
bold text type exactly as shown.
italic text replace with appropriate argument.
[-abc] any or all arguments within [ ] are optional.
-a|-b options delimited by | cannot be used together.
argument ... argument is repeatable.
[expression] ... entire expression within [ ] is repeatable.
====
BEGIN TRANSACTION;
DROP FUNCTION history_alerts() CASCADE;
DROP TABLE history.alerts;
DROP TABLE alerts;
CREATE TABLE alerts (
alert_uuid uuid not null primary key,
alert_host_uuid uuid not null, -- The name of the node or dashboard that this alert came from.
alert_set_by text not null,
alert_level integer not null, -- 1 (critical), 2 (warning), 3 (notice) or 4 (info)
alert_title text not null, -- ScanCore will read in the agents <name>.xml words file and look for this message key
alert_message text not null, -- ScanCore will read in the agents <name>.xml words file and look for this message key
alert_sort_position integer not null default 9999, -- The alerts will sort on this column. It allows for an optional sorting of the messages in the alert.
alert_show_header integer not null default 1, -- This can be set to have the alert be printed with only the contents of the string, no headers.
modified_date timestamp with time zone not null,
FOREIGN KEY(alert_host_uuid) REFERENCES hosts(host_uuid)
);
ALTER TABLE alerts OWNER TO admin;
CREATE TABLE history.alerts (
history_id bigserial,
alert_uuid uuid,
alert_host_uuid uuid,
alert_set_by text,
alert_level integer,
alert_title text,
alert_message text,
alert_sort_position integer,
alert_show_header integer,
modified_date timestamp with time zone not null
);
ALTER TABLE history.alerts OWNER TO admin;
CREATE FUNCTION history_alerts() RETURNS trigger
AS $$
DECLARE
history_alerts RECORD;
BEGIN
SELECT INTO history_alerts * FROM alerts WHERE alert_uuid = new.alert_uuid;
INSERT INTO history.alerts
(alert_uuid,
alert_host_uuid,
alert_set_by,
alert_level,
alert_title,
alert_title_variables,
alert_message,
alert_message_variables,
alert_sort_position,
alert_show_header,
modified_date)
VALUES
(history_alerts.alert_uuid,
history_alerts.alert_host_uuid,
history_alerts.alert_set_by,
history_alerts.alert_level,
history_alerts.alert_title,
history_alerts.alert_message,
history_alerts.alert_sort_position,
history_alerts.alert_show_header,
history_alerts.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_alerts() OWNER TO admin;
CREATE TRIGGER trigger_alerts
AFTER INSERT OR UPDATE ON alerts
FOR EACH ROW EXECUTE PROCEDURE history_alerts();
COMMIT;

@ -3,7 +3,7 @@
%define anvilgroup admin %define anvilgroup admin
Name: anvil Name: anvil
Version: 3.0 Version: 3.0
Release: 20%{?dist} Release: 21%{?dist}
Summary: Alteeve Anvil! complete package. Summary: Alteeve Anvil! complete package.
License: GPLv2+ License: GPLv2+
@ -52,6 +52,7 @@ Requires: perl-Text-Diff
Requires: perl-Time-HiRes Requires: perl-Time-HiRes
Requires: perl-UUID-Tiny Requires: perl-UUID-Tiny
Requires: perl-XML-Simple Requires: perl-XML-Simple
Requires: postfix
Requires: postgresql-contrib Requires: postgresql-contrib
Requires: postgresql-plperl Requires: postgresql-plperl
Requires: rsync Requires: rsync
@ -289,7 +290,7 @@ Provides support for asynchronous disaster recovery hosts in an Anvil! cluster.
%install %install
rm -rf $RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT
mkdir -p %{buildroot}/%{_sbindir}/anvil/ mkdir -p %{buildroot}/%{_sbindir}/ScanCore/agents/
mkdir -p %{buildroot}/%{_sysconfdir}/anvil/ mkdir -p %{buildroot}/%{_sysconfdir}/anvil/
mkdir -p %{buildroot}/%{_localstatedir}/www/ mkdir -p %{buildroot}/%{_localstatedir}/www/
mkdir -p %{buildroot}/%{_usr}/share/anvil/ mkdir -p %{buildroot}/%{_usr}/share/anvil/
@ -302,7 +303,8 @@ cp -R -p Anvil %{buildroot}/%{_datadir}/perl5/
cp -R -p html %{buildroot}/%{_localstatedir}/www/ cp -R -p html %{buildroot}/%{_localstatedir}/www/
cp -R -p cgi-bin %{buildroot}/%{_localstatedir}/www/ cp -R -p cgi-bin %{buildroot}/%{_localstatedir}/www/
cp -R -p units/* %{buildroot}/%{_usr}/lib/systemd/system/ cp -R -p units/* %{buildroot}/%{_usr}/lib/systemd/system/
cp -R -p tools/* %{buildroot}/%{_sbindir} cp -R -p tools/* %{buildroot}/%{_sbindir}/
cp -R -p ScanCore/agents/* %{buildroot}/%{_sbindir}/ScanCore/agents/
cp -R -p anvil.conf %{buildroot}/%{_sysconfdir}/anvil/ cp -R -p anvil.conf %{buildroot}/%{_sysconfdir}/anvil/
cp -R -p anvil.version %{buildroot}/%{_sysconfdir}/anvil/ cp -R -p anvil.version %{buildroot}/%{_sysconfdir}/anvil/
cp -R -p share/* %{buildroot}/%{_usr}/share/anvil/ cp -R -p share/* %{buildroot}/%{_usr}/share/anvil/
@ -346,6 +348,7 @@ echo "Preparing the database"
anvil-prep-database anvil-prep-database
anvil-update-states anvil-update-states
### TODO: I don't think we need this anymore
# Open access for Striker. The database will be opened after initial setup. # Open access for Striker. The database will be opened after initial setup.
echo "Opening the web and postgresql ports." echo "Opening the web and postgresql ports."
firewall-cmd --add-service=http firewall-cmd --add-service=http
@ -389,6 +392,7 @@ firewall-cmd --add-service=postgresql --permanent
%{_usr}/lib/* %{_usr}/lib/*
%{_usr}/share/anvil/* %{_usr}/share/anvil/*
%{_sbindir}/* %{_sbindir}/*
%{_sbindir}/ScanCore/agents/*
%{_sysconfdir}/anvil/anvil.version %{_sysconfdir}/anvil/anvil.version
%{_datadir}/perl5/* %{_datadir}/perl5/*
@ -404,6 +408,10 @@ firewall-cmd --add-service=postgresql --permanent
%changelog %changelog
* Madison Kelly <mkelly@alteeve.ca> 3.0-21
- Started adding support for ScanCore
- Updated source.
* Wed Dec 12 2018 Madison Kelly <mkelly@alteeve.ca> 3.0-20 * Wed Dec 12 2018 Madison Kelly <mkelly@alteeve.ca> 3.0-20
- Updated source. - Updated source.

@ -88,33 +88,33 @@ CREATE TRIGGER trigger_hosts
-- This stores information about users. -- This stores information about users.
-- Note that is all permissions are left false, the user can still interact with the Anvil! doing safe things, like changing optical media, perform migrations, start servers (but not stop them), etc. -- Note that is all permissions are left false, the user can still interact with the Anvil! doing safe things, like changing optical media, perform migrations, start servers (but not stop them), etc.
CREATE TABLE users ( CREATE TABLE users (
user_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here. user_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here.
user_name text not null, user_name text not null,
user_password_hash text not null, -- A user without a password is disabled. user_password_hash text not null, -- A user without a password is disabled.
user_salt text not null, -- This is used to enhance the security of the user's password. user_salt text not null, -- This is used to enhance the security of the user's password.
user_algorithm text not null, -- This is the algorithm used to encrypt the password and salt. user_algorithm text not null, -- This is the algorithm used to encrypt the password and salt.
user_hash_count text not null, -- This is the number of times that the password+salt was re-hashed through the algorithm. user_hash_count text not null, -- This is the number of times that the password+salt was re-hashed through the algorithm.
user_language text not null, -- If set, this will choose a different language over the default. user_language text not null, -- If set, this will choose a different language over the default.
user_is_admin integer not null default 0, -- If 1, all aspects of the program are available to the user. user_is_admin integer not null default 0, -- If 1, all aspects of the program are available to the user.
user_is_experienced integer not null default 0, -- If 1, user is allowed to delete a server, alter disk size, alter hardware and do other potentially risky things. They will also get fewer confirmation dialogues. user_is_experienced integer not null default 0, -- If 1, user is allowed to delete a server, alter disk size, alter hardware and do other potentially risky things. They will also get fewer confirmation dialogues.
user_is_trusted integer not null default 0, -- If 1, user is allowed to do things that would cause interruptions, like force-reset and gracefully stop servers, withdraw nodes, and stop the Anvil! entirely. user_is_trusted integer not null default 0, -- If 1, user is allowed to do things that would cause interruptions, like force-reset and gracefully stop servers, withdraw nodes, and stop the Anvil! entirely.
modified_date timestamp with time zone not null modified_date timestamp with time zone not null
); );
ALTER TABLE users OWNER TO #!variable!user!#; ALTER TABLE users OWNER TO #!variable!user!#;
CREATE TABLE history.users ( CREATE TABLE history.users (
history_id bigserial, history_id bigserial,
user_uuid uuid, user_uuid uuid,
user_name text, user_name text,
user_password_hash text, user_password_hash text,
user_salt text, user_salt text,
user_algorithm text, user_algorithm text,
user_hash_count text, user_hash_count text,
user_language text, user_language text,
user_is_admin integer, user_is_admin integer,
user_is_experienced integer, user_is_experienced integer,
user_is_trusted integer, user_is_trusted integer,
modified_date timestamp with time zone not null modified_date timestamp with time zone not null
); );
ALTER TABLE history.users OWNER TO #!variable!user!#; ALTER TABLE history.users OWNER TO #!variable!user!#;
@ -213,7 +213,7 @@ CREATE TABLE sessions (
session_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here. session_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here.
session_host_uuid uuid not null, -- This is the host uuid for this session. session_host_uuid uuid not null, -- This is the host uuid for this session.
session_user_uuid uuid not null, -- This is the user uuid for the user logging in. session_user_uuid uuid not null, -- This is the user uuid for the user logging in.
session_salt text not null, -- This is used when generating a session hash for a session when they log in. session_salt text not null, -- This is used when generating a session hash for a session when they log in.
session_user_agent text, session_user_agent text,
modified_date timestamp with time zone not null, modified_date timestamp with time zone not null,
@ -227,7 +227,7 @@ CREATE TABLE history.sessions (
session_uuid uuid, session_uuid uuid,
session_host_uuid uuid, session_host_uuid uuid,
session_user_uuid uuid, session_user_uuid uuid,
session_salt text, session_salt text,
session_user_agent text, session_user_agent text,
modified_date timestamp with time zone not null modified_date timestamp with time zone not null
); );
@ -266,35 +266,31 @@ CREATE TRIGGER trigger_sessions
-- This stores alerts coming in from various sources -- This stores alerts coming in from various sources
CREATE TABLE alerts ( CREATE TABLE alerts (
alert_uuid uuid not null primary key, alert_uuid uuid not null primary key,
alert_host_uuid uuid not null, -- The name of the node or dashboard that this alert came from. alert_host_uuid uuid not null, -- The name of the node or dashboard that this alert came from.
alert_set_by text not null, alert_set_by text not null,
alert_level text not null, -- debug (log only), info (+ admin email), notice (+ curious users), warning (+ client technical staff), critical (+ all) alert_level integer not null, -- 1 (critical), 2 (warning), 3 (notice) or 4 (info)
alert_title_key text not null, -- ScanCore will read in the agents <name>.xml words file and look for this message key alert_title text not null, -- ScanCore will read in the agents <name>.xml words file and look for this message key
alert_title_variables text not null, -- List of variables to substitute into the message key. Format is 'var1=val1 #!# var2 #!# val2 #!# ... #!# varN=valN'. alert_message text not null, -- ScanCore will read in the agents <name>.xml words file and look for this message key
alert_message_key text not null, -- ScanCore will read in the agents <name>.xml words file and look for this message key alert_sort_position integer not null default 9999, -- The alerts will sort on this column. It allows for an optional sorting of the messages in the alert.
alert_message_variables text not null, -- List of variables to substitute into the message key. Format is 'var1=val1 #!# var2 #!# val2 #!# ... #!# varN=valN'. alert_show_header integer not null default 1, -- This can be set to have the alert be printed with only the contents of the string, no headers.
alert_sort text not null, -- The alerts will sort on this column. It allows for an optional sorting of the messages in the alert. modified_date timestamp with time zone not null,
alert_header integer not null default 1, -- This can be set to have the alert be printed with only the contents of the string, no headers.
modified_date timestamp with time zone not null,
FOREIGN KEY(alert_host_uuid) REFERENCES hosts(host_uuid) FOREIGN KEY(alert_host_uuid) REFERENCES hosts(host_uuid)
); );
ALTER TABLE alerts OWNER TO #!variable!user!#; ALTER TABLE alerts OWNER TO #!variable!user!#;
CREATE TABLE history.alerts ( CREATE TABLE history.alerts (
history_id bigserial, history_id bigserial,
alert_uuid uuid, alert_uuid uuid,
alert_host_uuid uuid, alert_host_uuid uuid,
alert_set_by text, alert_set_by text,
alert_level text, alert_level integer,
alert_title_key text, alert_title text,
alert_title_variables text, alert_message text,
alert_message_key text, alert_sort_position integer,
alert_message_variables text, alert_show_header integer,
alert_sort text, modified_date timestamp with time zone not null
alert_header integer,
modified_date timestamp with time zone not null
); );
ALTER TABLE history.alerts OWNER TO #!variable!user!#; ALTER TABLE history.alerts OWNER TO #!variable!user!#;
@ -309,24 +305,22 @@ BEGIN
alert_host_uuid, alert_host_uuid,
alert_set_by, alert_set_by,
alert_level, alert_level,
alert_title_key, alert_title,
alert_title_variables, alert_title_variables,
alert_message_key, alert_message,
alert_message_variables, alert_message_variables,
alert_sort, alert_sort_position,
alert_header, alert_show_header,
modified_date) modified_date)
VALUES VALUES
(history_alerts.alert_uuid, (history_alerts.alert_uuid,
history_alerts.alert_host_uuid, history_alerts.alert_host_uuid,
history_alerts.alert_set_by, history_alerts.alert_set_by,
history_alerts.alert_level, history_alerts.alert_level,
history_alerts.alert_title_key, history_alerts.alert_title,
history_alerts.alert_title_variables, history_alerts.alert_message,
history_alerts.alert_message_key, history_alerts.alert_sort_position,
history_alerts.alert_message_variables, history_alerts.alert_show_header,
history_alerts.alert_sort,
history_alerts.alert_header,
history_alerts.modified_date); history_alerts.modified_date);
RETURN NULL; RETURN NULL;
END; END;
@ -339,6 +333,60 @@ CREATE TRIGGER trigger_alerts
FOR EACH ROW EXECUTE PROCEDURE history_alerts(); FOR EACH ROW EXECUTE PROCEDURE history_alerts();
-- This is the list of alert recipients.
CREATE TABLE recipients (
recipient_uuid uuid not null primary key,
recipient_name text not null, -- This is the recipient's name
recipient_email text not null, -- This is the recipient's email address or the file name, depending.
recipient_language text, -- If set, this is the language the user wants to receive alerts in. If not set, the default language is used.
recipient_new_level integer not null, -- This is the alert level to use when automatically adding watch links to new systems. '0' tells us to ignore new systems.
modified_date timestamp with time zone not null,
FOREIGN KEY(recipient_host_uuid) REFERENCES hosts(host_uuid)
);
ALTER TABLE recipients OWNER TO #!variable!user!#;
CREATE TABLE history.recipients (
history_id bigserial,
recipient_uuid uuid,
modified_date timestamp with time zone not null
);
ALTER TABLE history.recipients OWNER TO #!variable!user!#;
CREATE FUNCTION history_recipients() RETURNS trigger
AS $$
DECLARE
history_recipients RECORD;
BEGIN
SELECT INTO history_recipients * FROM recipients WHERE recipient_uuid = new.recipient_uuid;
INSERT INTO history.recipients
(recipient_uuid,
modified_date)
VALUES
(history_recipients.recipient_uuid,
history_recipients.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_recipients() OWNER TO #!variable!user!#;
CREATE TRIGGER trigger_recipients
AFTER INSERT OR UPDATE ON recipients
FOR EACH ROW EXECUTE PROCEDURE history_recipients();
-- TODO: We need to create;
-- Recipients (email, we're not supporting files anymore); Name, Address, Units, Language, default watch level
-- - Display as a list; Strikers, then Anvil!s; each anvil being node 1, node 2 and DR if available)
-- Watching (recipient, host, level) -> Link Recipient to hosts
-- Mail Server (server details)
-- Host Mail (Host uses which mail server, in what order)
-- This holds user-configurable variable. These values override defaults but NOT configuration files. -- This holds user-configurable variable. These values override defaults but NOT configuration files.
CREATE TABLE variables ( CREATE TABLE variables (
variable_uuid uuid not null primary key, variable_uuid uuid not null primary key,

@ -620,9 +620,9 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="striker_0053">Reload</key> <key name="striker_0053">Reload</key>
<key name="striker_0054">Configure Striker Peers</key> <key name="striker_0054">Configure Striker Peers</key>
<key name="striker_0055">When you sync with a peer, this machine's data will be copied to and recorded on the peer's database. Data gathered by ScanCore will also be kept in sync on both dashboards, and any general purpose data collected by other dashboards while this one is offline will be copied back when this machine comes online. Should this machine ever be rebuilt, data recorded from before the rebuild will be automatically restored as well.</key> <key name="striker_0055">When you sync with a peer, this machine's data will be copied to and recorded on the peer's database. Data gathered by ScanCore will also be kept in sync on both dashboards, and any general purpose data collected by other dashboards while this one is offline will be copied back when this machine comes online. Should this machine ever be rebuilt, data recorded from before the rebuild will be automatically restored as well.</key>
<key name="striker_0056">Update this Striker</key> <key name="striker_0056">Update System</key>
<key name="striker_0057">This will update this system using any available software repositories. You can also use this to create or load update packs to allow for the update of offline or air-gapped Anvil! systems.</key> <key name="striker_0057">This will update this system using any available software repositories. You can also use this to create or load update packs to allow for the update of offline or air-gapped Anvil! systems.</key>
<key name="striker_0058">Configure this Striker</key> <key name="striker_0058">Configure Striker</key>
<key name="striker_0059">Update the network configuration for this Striker.</key> <key name="striker_0059">Update the network configuration for this Striker.</key>
<key name="striker_0060">Welcome!</key> <key name="striker_0060">Welcome!</key>
<key name="striker_0061">Create or manage Anvil! systems</key> <key name="striker_0061">Create or manage Anvil! systems</key>
@ -655,10 +655,10 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="striker_0088">The system will be updated momentarily. This system will now be in maintenance mode until the update is complete.</key> <key name="striker_0088">The system will be updated momentarily. This system will now be in maintenance mode until the update is complete.</key>
<key name="striker_0089">This indicates whether this system needs to be rebooted or not.</key> <key name="striker_0089">This indicates whether this system needs to be rebooted or not.</key>
<key name="striker_0090">This system is in maintenance mode and is not currently available.</key> <key name="striker_0090">This system is in maintenance mode and is not currently available.</key>
<key name="striker_0091">Reboot this Striker</key> <key name="striker_0091">Reboot This System</key>
<key name="striker_0092">This option will restart the host operating system. This is not currently needed.</key> <key name="striker_0092">This option will restart the host operating system. This is not currently needed.</key>
<key name="striker_0093">This machine needs to be rebooted. This option will restart the host operating system.</key> <key name="striker_0093">This machine needs to be rebooted. This option will restart the host operating system.</key>
<key name="striker_0094">Power off this Striker</key> <key name="striker_0094">Power Off This System</key>
<key name="striker_0095">This will power off the Striker machine and leave it off. To power it back on, you will need physical access or cycle the power of the PDU feeding this Striker.</key> <key name="striker_0095">This will power off the Striker machine and leave it off. To power it back on, you will need physical access or cycle the power of the PDU feeding this Striker.</key>
<key name="striker_0096">Recent and Running Jobs</key> <key name="striker_0096">Recent and Running Jobs</key>
<key name="striker_0097">There are no jobs currently running or recently completed.</key> <key name="striker_0097">There are no jobs currently running or recently completed.</key>
@ -677,6 +677,8 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="striker_0110">The 'Install Target' feature is used to do base (stage 1) installs on new or rebuilt Striker dashboards, Anvil! nodes or Disaster Recivery hosts. Specifically, it allows machines to boot off their BCN network interface and install the base operating system.</key> <key name="striker_0110">The 'Install Target' feature is used to do base (stage 1) installs on new or rebuilt Striker dashboards, Anvil! nodes or Disaster Recivery hosts. Specifically, it allows machines to boot off their BCN network interface and install the base operating system.</key>
<key name="striker_0111">The 'Install Target' disable job has been requested. It should be completed in a few moments. You may need to reload the next page in a minute to see that it has been enabled.</key> <key name="striker_0111">The 'Install Target' disable job has been requested. It should be completed in a few moments. You may need to reload the next page in a minute to see that it has been enabled.</key>
<key name="striker_0112">The 'Install Target' enabled job has been requested. It should be completed in a few moments. You may need to reload the next page in a minute to see that it has been disabled.</key> <key name="striker_0112">The 'Install Target' enabled job has been requested. It should be completed in a few moments. You may need to reload the next page in a minute to see that it has been disabled.</key>
<key name="striker_0113">Anvil! Configuration and Management.</key>
<key name="striker_0114">Create a new Anvil! system.</key>
<!-- Strings used by jobs --> <!-- Strings used by jobs -->
<key name="job_0001">Configure Network</key> <key name="job_0001">Configure Network</key>

@ -1,7 +1,7 @@
#!/usr/bin/perl #!/usr/bin/perl
# #
# This is the master daemon that manages all periodically run processes on Striker dashboards and Anvil! # This is the master daemon that manages all periodically run processes on Striker dashboards, Anvil! cluster
# nodes. # nodes and DR hosts.
# #
# Exit codes; # Exit codes;
# 0 = Normal exit or md5sum of this program changed and it exited to reload. # 0 = Normal exit or md5sum of this program changed and it exited to reload.
@ -26,7 +26,6 @@ use Proc::Simple;
use JSON; use JSON;
use HTML::Strip; use HTML::Strip;
use HTML::FromText; use HTML::FromText;
use Data::Dumper; use Data::Dumper;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];

@ -2244,6 +2244,7 @@ sub load_packages
"poppler-glib.x86_64", "poppler-glib.x86_64",
"poppler.x86_64", "poppler.x86_64",
"popt.x86_64", "popt.x86_64",
"postfix.x86_64",
"postgresql-contrib.x86_64", "postgresql-contrib.x86_64",
"postgresql-libs.x86_64", "postgresql-libs.x86_64",
"postgresql-plperl.x86_64", "postgresql-plperl.x86_64",

@ -0,0 +1,12 @@
[Unit]
Description=Anvil! Intelligent Availability Platform - ScanCore Decision Engine
Wants=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/ScanCore
ExecStop=/bin/kill -WINCH ${MAINPID}
Restart=always
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save