From 911523dfce9b35ee8b048b2ed3573873fca2a827 Mon Sep 17 00:00:00 2001 From: Digimer Date: Sat, 5 Sep 2020 01:18:42 -0400 Subject: [PATCH] * Got a lot of work done in generating emails. Doesn't work yet, but the code to generate emails for recipients using their preferred language and alert level is done (though limited testing so far). * Dropped support for supporting imperial measurements in generated emails. * Created Database->get_alerts() to read in alert data and ->get_recipients() to get the list of alert recipients. * SQL Schema changes; ** Added 'alert_processed' to 'alerts' to track what alerts have been processed. ** Changed 'recipient_new_level' to 'recipient_level' now that we're only using 'notifications' as a per-host override for user/hosts alert levels. ** Removed 'recipient_units' as we're no longer supporting non-metric values. * Updated Alert->register() to take strings for the alert level (which gets translated to integers). * Created Email->get_current_server() to returned the mail_server_uuid of the active mail server (if any). Created ->send_alerts() to process unprocessed alerts and send emails to recipients. * Updated Words->parse_banged_string() to take the 'language' parameter. Signed-off-by: Digimer --- Anvil/Tools/Alert.pm | 137 +++++----- Anvil/Tools/Database.pm | 477 +++++++++++++++++++++++----------- Anvil/Tools/Email.pm | 279 +++++++++++++++++++- Anvil/Tools/Words.pm | 29 ++- cgi-bin/striker | 150 +++++------ html/skins/alteeve/email.html | 13 +- notes | 79 ++++++ rpm/SPECS/anvil.spec | 5 +- share/anvil.sql | 49 ++-- share/words.xml | 25 +- tools/test.pl | 16 +- 11 files changed, 895 insertions(+), 364 deletions(-) diff --git a/Anvil/Tools/Alert.pm b/Anvil/Tools/Alert.pm index 3d7185af..c8ef1d8f 100644 --- a/Anvil/Tools/Alert.pm +++ b/Anvil/Tools/Alert.pm @@ -77,7 +77,7 @@ sub parent =head2 check_alert_sent -This is used by scan agents that need to track whether an alert was sent when a sensor dropped below/rose above a set alert threshold. For example, if a sensor alerts at 20°C and clears at 25°C, this will be called when either value is passed. When passing the warning threshold, the alert is registered and sent to the user. Once set, no further warning alerts are sent. When the value passes over the clear threshold, this is checked and if an alert was previously registered, it is removed and an "all clear" message is sent. In this way, multiple alerts will not go out if a sensor floats around the warning threshold and a "cleared" message won't be sent unless a "warning" message was previously sent. +This is used by programs, usually scancore scan agents, that need to track whether an alert was sent when a sensor dropped below/rose above a set alert threshold. For example, if a sensor alerts at 20°C and clears at 25°C, this will be called when either value is passed. When passing the warning threshold, the alert is registered and sent to the user. Once set, no further warning alerts are sent. When the value passes over the clear threshold, this is checked and if an alert was previously registered, it is removed and an "all clear" message is sent. In this way, multiple alerts will not go out if a sensor floats around the warning threshold and a "cleared" message won't be sent unless a "warning" message was previously sent. If there is a problem, C<< !!error!! >> is returned. @@ -284,17 +284,17 @@ WHERE =head2 register -This registers an alert to be sent later. +This registers an alert to be sent later by C<< Email->send_alerts >>. -The C<< alert_uuid >> is returned on success. If anything goes wrong, C<< !!error!! >> will be returned. +The C<< alert_uuid >> is returned on success. If anything goes wrong, C<< !!error!! >> is returned. If there are no recipients who would receive the alert, it will not be recorded and an empty string is 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. +This assigns an severity level to the alert. Any recipient listening to this level or higher will receive this alert. This value can be set as a numeric value or as a string. -=head4 1 (critical) +=head4 1 / critical Alerts at this level will go to all recipients, except for those ignoring the source system entirely. @@ -302,19 +302,19 @@ This is reserved for alerts that could lead to imminent service interruption or 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) +=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) +=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) +=head4 4 / info This is used for alerts that are almost always safe to ignore, but may be useful in testing and debugging. @@ -353,7 +353,7 @@ NOTE: The timestamp is generally set for a given program or agent run (set when =head3 title (optional) -NOTE: This is required if C<< show_header >> is set! +NOTE: If not set and C<< show_header >> is set to C<< 1 >>, a generic title will be added based on the C<< alert_level >> and if C<< clear_alert >> is set or not. This is the title of the alert. It is expected to be in the format C<< >>. 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. @@ -385,6 +385,7 @@ sub register title => $title, }}); + # Missing parameters? if (not $alert_level) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register()", parameter => "alert_level" }}); @@ -400,13 +401,44 @@ sub register $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Alert->register()", parameter => "message" }}); return("!!error!!"); } + + # If the alert level was a string, convert it to the numerical version. Also check that we've got a + # sane alert level at all. + if (lc($alert_level) eq "critical") + { + $alert_level = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_level => $alert_level }}); + } + elsif (lc($alert_level) eq "warning") + { + $alert_level = 2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_level => $alert_level }}); + } + elsif (lc($alert_level) eq "notice") + { + $alert_level = 3; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_level => $alert_level }}); + } + elsif (lc($alert_level) eq "info") + { + $alert_level = 4; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_level => $alert_level }}); + } + elsif (($alert_level =~ /\D/) or ($alert_level < 1) or ($alert_level > 4)) + { + # Invalid + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0142", variables => { alert_level => $alert_level }}); + return("!!error!!"); + } + + # Do we need to generate a header? if (($show_header) && (not $title)) { # Set it based on the alert_level. - if ($alert_level eq "1") { $title = $clear_alert ? "alert_title_0005" : "alert_title_0001"; } # Critical (or Critical Cleared) - elsif ($alert_level eq "2") { $title = $clear_alert ? "alert_title_0006" : "alert_title_0002"; } # Warning (or Warning Cleared) - elsif ($alert_level eq "3") { $title = $clear_alert ? "alert_title_0007" : "alert_title_0003"; } # Notice (or Notice Cleared) - elsif ($alert_level eq "4") { $title = $clear_alert ? "alert_title_0008" : "alert_title_0004"; } # Info (or Info Cleared) + if ($alert_level == 1) { $title = $clear_alert ? "alert_title_0005" : "alert_title_0001"; } # Critical (or Critical Cleared) + elsif ($alert_level == 2) { $title = $clear_alert ? "alert_title_0006" : "alert_title_0002"; } # Warning (or Warning Cleared) + elsif ($alert_level == 3) { $title = $clear_alert ? "alert_title_0007" : "alert_title_0003"; } # Notice (or Notice Cleared) + elsif ($alert_level == 4) { $title = $clear_alert ? "alert_title_0008" : "alert_title_0004"; } # Info (or Info Cleared) $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { title => $title }}); } @@ -417,65 +449,35 @@ sub register # Before we actually record the alert, see if there are any recipients listening. For example, very # rarely is anyone listening to alert level 4 (info), so skipping recording it saves unnecessary # growth of the history.alerts table. - - - -=cut - # 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 - # the lowest level log level actually being listened to and simply skip anything lower than that. - # 5 == debug - # 1 == critical - my $lowest_log_level = 5; - foreach my $integer (sort {$a cmp $b} keys %{$anvil->data->{alerts}{recipient}}) + my $proceed = 0; + $anvil->Database->get_recipients({debug => $debug}); + foreach my $recipient_uuid (keys %{$anvil->data->{recipients}{recipient_uuid}}) { - # We want to know the alert level, regardless of whether the recipient is an email of file - # target. - my $this_level; - if ($anvil->data->{alerts}{recipient}{$integer}{email}) - { - # Email recipient - $this_level = ($anvil->data->{alerts}{recipient}{$integer}{email} =~ /level="(.*?)"/)[0]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_level => $this_level }}); - } - elsif ($anvil->data->{alerts}{recipient}{$integer}{file}) - { - # File target - $this_level = ($anvil->data->{alerts}{recipient}{$integer}{file} =~ /level="(.*?)"/)[0]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_level => $this_level }}); - } + my $recipient_email = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}; + my $recipient_level = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:recipient_uuid' => $recipient_uuid, + 's2:recipient_level' => $recipient_level, + 's3:recipient_email' => $recipient_email, + }}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_level => $this_level }}); - if ($this_level) + if ($recipient_level >= $alert_level) { - $this_level = $anvil->Alert->convert_level_name_to_number({level => $this_level}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - this_level => $this_level, - lowest_log_level => $lowest_log_level, - }}); - if ($this_level < $lowest_log_level) - { - $lowest_log_level = $this_level; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { lowest_log_level => $lowest_log_level }}); - } + # Someone wants to hear about this. + $proceed = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { proceed => $proceed }}); + last; } } - # Now get the numeric value of this alert and return if it is higher. - my $this_level = $anvil->Alert->convert_level_name_to_number({level => $level}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - alert_level => $level, - this_level => $this_level, - lowest_log_level => $lowest_log_level, - }}); - if ($this_level > $lowest_log_level) + if (not $proceed) { - # Return. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0102", variables => { message => $message }}); - return(0); + # No one is listening, ignore. + return(""); } # Always INSERT. ScanCore removes them as they're acted on (copy is left in history.alerts). + my $alert_uuid = $anvil->Get->uuid(); my $query = " INSERT INTO alerts @@ -490,10 +492,10 @@ INSERT INTO alert_show_header, modified_date ) VALUES ( - ".$anvil->Database->quote($anvil->Get->uuid()).", - ".$anvil->Database->quote($anvil->data->{sys}{host_uuid}).", + ".$anvil->Database->quote($alert_uuid).", + ".$anvil->Database->quote($anvil->Get->host_uuid()).", ".$anvil->Database->quote($set_by).", - ".$anvil->Database->quote($level).", + ".$anvil->Database->quote($alert_level).", ".$anvil->Database->quote($title).", ".$anvil->Database->quote($message).", ".$anvil->Database->quote($sort_position).", @@ -503,9 +505,10 @@ INSERT INTO "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); -=cut - return(0); + ### TODO: Add an optional 'send_now' parameter to causes us to call 'Email->send_alerts' + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_uuid => $alert_uuid }}); + return($alert_uuid); } ### TODO: Write this, maybe? Or remove it and ->warning()? diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 292eb382..b6f34567 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -20,6 +20,7 @@ my $THIS_FILE = "Database.pm"; # configure_pgsql # connect # disconnect +# get_alerts # get_anvils # get_fences # get_host_from_uuid @@ -1252,7 +1253,7 @@ sub connect }}); # Copy my alert hash before I delete the uuid. - my $error_array = []; +# my $error_array = []; # Delete this DB so that we don't try to use it later. This is a quiet alert because the # original connection error was likely logged. @@ -1263,40 +1264,40 @@ sub connect delete $anvil->data->{database}{$uuid}; # If I've not sent an alert about this DB loss before, send one now. - my $set = $anvil->Alert->check_alert_sent({ - debug => $debug, - type => "set", - set_by => $THIS_FILE, - record_locator => $uuid, - name => "connect_to_db", - modified_date => $anvil->data->{sys}{database}{timestamp}, - }); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { set => $set }}); - - if ($set) - { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error_array => $error_array }}); - foreach my $hash (@{$error_array}) - { - my $message_key = $hash->{message_key}; - my $message_variables = $hash->{message_variables}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - hash => $hash, - message_key => $message_key, - message_variables => $message_variables, - }}); - - # These are warning level alerts. - $anvil->Alert->register({ - debug => $debug, - alert_level => "warning", - alert_set_by => $THIS_FILE, - alert_title_key => "alert_title_0003", - alert_message_key => $message_key, - alert_message_variables => $message_variables, - }); - } - } +# my $set = $anvil->Alert->check_alert_sent({ +# debug => $debug, +# type => "set", +# set_by => $THIS_FILE, +# record_locator => $uuid, +# name => "connect_to_db", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { set => $set }}); +# +# if ($set) +# { +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { error_array => $error_array }}); +# foreach my $hash (@{$error_array}) +# { +# my $message = $hash->{message_key}; +# my $variable_count = keys +# my $message_key = $hash->{message_key}; +# my $message_variables = $hash->{message_variables}; +# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { +# hash => $hash, +# message_key => $message_key, +# message_variables => $message_variables, +# }}); +# +# # These are warning level alerts. +# $anvil->Alert->register({ +# debug => $debug, +# alert_level => "warning", +# set_by => $THIS_FILE, +# message => $message, +# }); +# } +# } } # Send an 'all clear' message if a now-connected DB previously wasn't. @@ -1324,29 +1325,29 @@ sub connect # # if ($count > 0) # { - my $cleared = $anvil->Alert->check_alert_sent({ - debug => $debug, - type => "clear", - set_by => $THIS_FILE, - record_locator => $uuid, - name => "connect_to_db", - modified_date => $anvil->data->{sys}{database}{timestamp}, - }); - if ($cleared) - { - $anvil->Alert->register({ - debug => $debug, - level => "warning", - agent_name => "Anvil!", - title_key => "an_title_0006", - message_key => "cleared_log_0055", - message_variables => { - name => $database_name, - host => $anvil->data->{database}{$uuid}{host}, - port => defined $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432, - }, - }); - } +# my $cleared = $anvil->Alert->check_alert_sent({ +# debug => $debug, +# type => "clear", +# set_by => $THIS_FILE, +# record_locator => $uuid, +# name => "connect_to_db", +# modified_date => $anvil->data->{sys}{database}{timestamp}, +# }); +# if ($cleared) +# { +# $anvil->Alert->register({ +# debug => $debug, +# level => "warning", +# agent_name => "Anvil!", +# title_key => "an_title_0006", +# message_key => "cleared_log_0055", +# message_variables => { +# name => $database_name, +# host => $anvil->data->{database}{$uuid}{host}, +# port => defined $anvil->data->{database}{$uuid}{port} ? $anvil->data->{database}{$uuid}{port} : 5432, +# }, +# }); +# } # } } @@ -1439,35 +1440,94 @@ sub disconnect } -=head2 get_recipients +=head2 get_alerts -This returns a list of users listening to alerts for a given host, along with their alert level. +This reads in alerts from the C<< alerts >> table. -This method takes no parameters. +Data is stored as: + + alerts::alert_uuid::::alert_host_uuid + alerts::alert_uuid::::alert_set_by + alerts::alert_uuid::::alert_level + alerts::alert_uuid::::alert_title + alerts::alert_uuid::::alert_message + alerts::alert_uuid::::alert_sort_position + alerts::alert_uuid::::alert_show_header + alerts::alert_uuid::::alert_processed + alerts::alert_uuid::::unix_modified_date + alerts::alert_uuid::::modified_date + +The C<< unix_modified_date >> is the unix timestamp to facilitate sorting by alert age. + +Parameters; + +=head3 include_processed (Optional, default 0) + +By default, only unprocessed alerts are loaded. If this is set to C<< 1 >>, alerts that have already been processed will also be loaded. + +=head3 all_hosts (Optional, default 0) + +By default, only alerts registered on the load host are loaded. If this is set to C<< 1 >>, alerts from all hosts are loaded. =cut -sub get_recipients +sub get_alerts { my $self = shift; my $parameter = shift; my $anvil = $self->parent; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->get_recipients()" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->get_manifests()" }}); + my $all_hosts = defined $parameter->{all_hosts} ? $parameter->{all_hosts} : 0; + my $include_processed = defined $parameter->{include_processed} ? $parameter->{include_processed} : 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + all_hosts => $all_hosts, + include_processed => $include_processed, + }}); + + if (exists $anvil->data->{alerts}) + { + delete $anvil->data->{alerts}; + } my $query = " SELECT - recipient_uuid, - recipient_name, - recipient_email, - recipient_language, - recipient_units, - recipient_new_level + alert_uuid, + alert_host_uuid, + alert_set_by, + alert_level, + alert_title, + alert_message, + alert_sort_position, + alert_show_header, + alert_processed, + round(extract(epoch from modified_date)) AS unix_modified_date, + modified_date FROM - recipients + alerts "; + if ((not $include_processed) && (not $all_hosts)) + { + $query .= " +WHERE + alert_processed = '0' +AND + alert_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." "; + } + elsif (not $include_processed) + { + $query .= " +WHERE + alert_processed = '0' "; + } + elsif (not $all_hosts) + { + $query .= " +WHERE + alert_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." " + } + $query .= " ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); - my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $count = @{$results}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { @@ -1476,42 +1536,56 @@ FROM }}); foreach my $row (@{$results}) { - my $recipient_uuid = $row->[0]; - my $recipient_name = $row->[1]; - my $recipient_email = $row->[2]; - my $recipient_language = $row->[3]; - my $recipient_units = $row->[4]; - my $recipient_new_level = $row->[5]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - recipient_uuid => $recipient_uuid, - recipient_name => $recipient_name, - recipient_email => $recipient_email, - recipient_language => $recipient_language, - recipient_units => $recipient_units, - recipient_new_level => $recipient_new_level, - }}); - - # Store the data - $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name} = $recipient_name; - $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email} = $recipient_email; - $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language} = $recipient_language; - $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_units} = $recipient_units; - $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_new_level} = $recipient_new_level; + my $alert_uuid = $row->[0]; + my $alert_host_uuid = $row->[1]; + my $alert_set_by = $row->[2]; + my $alert_level = $row->[3]; + my $alert_title = $row->[4]; + my $alert_message = $row->[5]; + my $alert_sort_position = $row->[6]; + my $alert_show_header = $row->[7]; + my $alert_processed = $row->[8]; + my $unix_modified_date = $row->[9]; + my $modified_date = $row->[10]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "recipients::recipient_uuid::${recipient_uuid}}::recipient_name" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_email" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_language" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_units" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_units}, - "recipients::recipient_uuid::${recipient_uuid}}::recipient_new_level" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_new_level}, + alert_uuid => $alert_uuid, + alert_host_uuid => $alert_host_uuid, + alert_set_by => $alert_set_by, + alert_level => $anvil->Log->is_secure($alert_level), + alert_title => $alert_title, + alert_message => $alert_message, + alert_sort_position => $alert_sort_position, + alert_show_header => $alert_show_header, + alert_processed => $alert_processed, + unix_modified_date => $unix_modified_date, + modified_date => $modified_date, }}); - # Make it easy to look up the mail server's UUID from the server address. - $anvil->data->{recipients}{email_to_uuid}{$recipient_email} = $recipient_uuid; + # Record the data in the hash, too. + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_host_uuid} = $alert_host_uuid; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_set_by} = $alert_set_by; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_level} = $alert_level; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_title} = $alert_title; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_message} = $alert_message; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_sort_position} = $alert_sort_position; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_show_header} = $alert_show_header; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_processed} = $alert_processed; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{unix_modified_date} = $unix_modified_date; + $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{modified_date} = $modified_date; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "recipients::email_to_uuid::${recipient_email}" => $anvil->data->{recipients}{email_to_uuid}{$recipient_email}, + "alerts::alert_uuid::${alert_uuid}::alert_host_uuid" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_host_uuid}, + "alerts::alert_uuid::${alert_uuid}::alert_set_by" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_set_by}, + "alerts::alert_uuid::${alert_uuid}::alert_level" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_level}, + "alerts::alert_uuid::${alert_uuid}::alert_title" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_title}, + "alerts::alert_uuid::${alert_uuid}::alert_message" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_message}, + "alerts::alert_uuid::${alert_uuid}::alert_sort_position" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_sort_position}, + "alerts::alert_uuid::${alert_uuid}::alert_show_header" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_show_header}, + "alerts::alert_uuid::${alert_uuid}::alert_processed" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_processed}, + "alerts::alert_uuid::${alert_uuid}::unix_modified_date" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{unix_modified_date}, + "alerts::alert_uuid::${alert_uuid}::modified_date" => $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{modified_date}, }}); } - + return(0); } @@ -2978,6 +3052,108 @@ FROM } +=head2 get_recipients + +This returns a list of users listening to alerts for a given host, along with their alert level. + +This method takes no parameters. + +=cut +sub get_recipients +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->get_recipients()" }}); + + # We're going to include the alert levels for this host based on overrides that might exist in the + # 'notifications' table. If the data hasn't already been loaded, we'll load it now. + if (not $anvil->data->{notifications}{notification_uuid}) + { + $anvil->Database->get_notifications({debug => $debug}); + } + + my $host_uuid = $anvil->Get->host_uuid(); + my $query = " +SELECT + recipient_uuid, + recipient_name, + recipient_email, + recipient_language, + recipient_level +FROM + recipients +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $recipient_uuid = $row->[0]; + my $recipient_name = $row->[1]; + my $recipient_email = $row->[2]; + my $recipient_language = $row->[3]; + my $recipient_level = $row->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + recipient_uuid => $recipient_uuid, + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + recipient_level => $recipient_level, + }}); + + # Store the data + $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name} = $recipient_name; + $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email} = $recipient_email; + $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language} = $recipient_language; + $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level} = $recipient_level; + $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host} = $recipient_level; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "recipients::recipient_uuid::${recipient_uuid}}::recipient_name" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name}, + "recipients::recipient_uuid::${recipient_uuid}}::recipient_email" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}, + "recipients::recipient_uuid::${recipient_uuid}}::recipient_language" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language}, + "recipients::recipient_uuid::${recipient_uuid}}::recipient_level" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level}, + "recipients::recipient_uuid::${recipient_uuid}}::level_on_host" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}, + }}); + + # Make it easy to look up the mail server's UUID from the server address. + $anvil->data->{recipients}{email_to_uuid}{$recipient_email} = $recipient_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "recipients::email_to_uuid::${recipient_email}" => $anvil->data->{recipients}{email_to_uuid}{$recipient_email}, + }}); + + # If there is an override for a given recipient on this host, mark it as such. + foreach my $notification_uuid (keys %{$anvil->data->{notifications}{notification_uuid}}) + { + my $notification_recipient_uuid = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_recipient_uuid}; + my $notification_host_uuid = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_host_uuid}; + my $notification_alert_level = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_alert_level}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + notification_recipient_uuid => $notification_recipient_uuid, + notification_host_uuid => $notification_host_uuid, + notification_alert_level => $notification_alert_level, + }}); + if (($notification_host_uuid eq $host_uuid) && ($notification_recipient_uuid eq $recipient_uuid)) + { + $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host} = $notification_alert_level; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "recipients::recipient_uuid::${recipient_uuid}}::level_on_host" => $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}, + }}); + last; + } + } + } + + return(0); +} + + =head2 get_ssh_keys This loads all known user's SSH public keys and all known machine's public keys into the data hash. On success, this method returns C<< 0 >>. If any problems occur, C<< 1 >> is returned. @@ -7960,7 +8136,7 @@ This is the language that alert emails are crafted using for this recipient. Thi This is the name of the recipient, and is used when crafting the email body and reply-to lists. -=head3 recipient_new_level (optional, default '2') +=head3 recipient_level (optional, default '2') When adding a new Anvil! to the system, the recipient will automatically start monitoring the new Anvil! using this alert level. This can be set to C<< 0 >> to prevent auto-monitoring of new systems. @@ -7982,10 +8158,6 @@ Warning alerts. These are alerts that likely require the attention of an adminis Notice alerts. These are generally low priority alerts that do not need attention, but might be indicators of developing problems. (ie: UPSes transfering to batteries, server migration/shut down/boot up, etc) -=head3 recipient_units (optional, default 'metric') - -This can be set to 'imperial' if the user wants to receive values in imperial units. Currently, this causes temperatures to be returned in C<< °F >> instead of C<< °C >>. - =head3 recipient_uuid (optional) If set, this is the UUID that will be used to update a record in the database. If not set, it will be searched for by looking for a matching C<< recipient_email >>. @@ -7999,27 +8171,25 @@ sub insert_or_update_recipients my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->insert_or_update_recipients()" }}); - my $delete = defined $parameter->{'delete'} ? $parameter->{'delete'} : 0; - my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; - my $file = defined $parameter->{file} ? $parameter->{file} : ""; - my $line = defined $parameter->{line} ? $parameter->{line} : ""; - my $recipient_email = defined $parameter->{recipient_email} ? $parameter->{recipient_email} : ""; - my $recipient_language = defined $parameter->{recipient_language} ? $parameter->{recipient_language} : "en_CA"; - my $recipient_name = defined $parameter->{recipient_name} ? $parameter->{recipient_name} : ""; - my $recipient_new_level = defined $parameter->{recipient_new_level} ? $parameter->{recipient_new_level} : "2"; - my $recipient_units = defined $parameter->{recipient_units} ? $parameter->{recipient_units} : "metric"; - my $recipient_uuid = defined $parameter->{recipient_uuid} ? $parameter->{recipient_uuid} : ""; + my $delete = defined $parameter->{'delete'} ? $parameter->{'delete'} : 0; + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + my $file = defined $parameter->{file} ? $parameter->{file} : ""; + my $line = defined $parameter->{line} ? $parameter->{line} : ""; + my $recipient_email = defined $parameter->{recipient_email} ? $parameter->{recipient_email} : ""; + my $recipient_language = defined $parameter->{recipient_language} ? $parameter->{recipient_language} : "en_CA"; + my $recipient_name = defined $parameter->{recipient_name} ? $parameter->{recipient_name} : ""; + my $recipient_level = defined $parameter->{recipient_level} ? $parameter->{recipient_level} : "2"; + my $recipient_uuid = defined $parameter->{recipient_uuid} ? $parameter->{recipient_uuid} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 'delete' => $delete, - uuid => $uuid, - file => $file, - line => $line, - recipient_email => $recipient_email, - recipient_language => $recipient_language, - recipient_name => $recipient_name, - recipient_new_level => $recipient_new_level, - recipient_units => $recipient_units, - recipient_uuid => $recipient_uuid, + 'delete' => $delete, + uuid => $uuid, + file => $file, + line => $line, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + recipient_name => $recipient_name, + recipient_level => $recipient_level, + recipient_uuid => $recipient_uuid, }}); # Did we get a mail server name? @@ -8029,10 +8199,10 @@ sub insert_or_update_recipients return(""); } - # Make sure the recipient_new_level is 0, 1, 2 or 3 - if (($recipient_new_level ne "0") && ($recipient_new_level ne "1") && ($recipient_new_level ne "2") && ($recipient_new_level ne "3")) + # Make sure the recipient_level is 0, 1, 2 or 3 + if (($recipient_level ne "0") && ($recipient_level ne "1") && ($recipient_level ne "2") && ($recipient_level ne "3")) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0108", variables => { recipient_new_level => $recipient_new_level }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0108", variables => { recipient_level => $recipient_level }}); return(""); } @@ -8132,16 +8302,14 @@ INSERT INTO recipient_email, recipient_language, recipient_name, - recipient_new_level, - recipient_units, + recipient_level, modified_date ) VALUES ( ".$anvil->Database->quote($recipient_uuid).", ".$anvil->Database->quote($recipient_email).", ".$anvil->Database->quote($recipient_language).", ".$anvil->Database->quote($recipient_name).", - ".$anvil->Database->quote($recipient_new_level).", - ".$anvil->Database->quote($recipient_units).", + ".$anvil->Database->quote($recipient_level).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." ); "; @@ -8156,8 +8324,7 @@ SELECT recipient_email, recipient_language, recipient_name, - recipient_new_level, - recipient_units + recipient_level FROM recipients WHERE @@ -8179,39 +8346,35 @@ WHERE } foreach my $row (@{$results}) { - my $old_recipient_email = $row->[0]; - my $old_recipient_language = $row->[1]; - my $old_recipient_name = $row->[2]; - my $old_recipient_new_level = $row->[3]; - my $old_recipient_units = $row->[4]; + my $old_recipient_email = $row->[0]; + my $old_recipient_language = $row->[1]; + my $old_recipient_name = $row->[2]; + my $old_recipient_level = $row->[3]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - old_recipient_email => $old_recipient_email, - old_recipient_language => $old_recipient_language, - old_recipient_name => $old_recipient_name, - old_recipient_new_level => $old_recipient_new_level, - old_recipient_units => $old_recipient_units, + old_recipient_email => $old_recipient_email, + old_recipient_language => $old_recipient_language, + old_recipient_name => $old_recipient_name, + old_recipient_level => $old_recipient_level, }}); # Anything change? - if (($old_recipient_email ne $recipient_email) or - ($old_recipient_language ne $recipient_language) or - ($old_recipient_name ne $recipient_name) or - ($old_recipient_new_level ne $recipient_new_level) or - ($old_recipient_units ne $recipient_units)) + if (($old_recipient_email ne $recipient_email) or + ($old_recipient_language ne $recipient_language) or + ($old_recipient_name ne $recipient_name) or + ($old_recipient_level ne $recipient_level)) { # Something changed, save. my $query = " UPDATE recipients SET - recipient_email = ".$anvil->Database->quote($recipient_email).", - recipient_language = ".$anvil->Database->quote($recipient_language).", - recipient_name = ".$anvil->Database->quote($recipient_name).", - recipient_new_level = ".$anvil->Database->quote($recipient_new_level).", - recipient_units = ".$anvil->Database->quote($recipient_units).", - modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." + recipient_email = ".$anvil->Database->quote($recipient_email).", + recipient_language = ".$anvil->Database->quote($recipient_language).", + recipient_name = ".$anvil->Database->quote($recipient_name).", + recipient_level = ".$anvil->Database->quote($recipient_level).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE - recipient_uuid = ".$anvil->Database->quote($recipient_uuid)." + recipient_uuid = ".$anvil->Database->quote($recipient_uuid)." "; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Database->write({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); diff --git a/Anvil/Tools/Email.pm b/Anvil/Tools/Email.pm index 5f3aa931..07fa8712 100755 --- a/Anvil/Tools/Email.pm +++ b/Anvil/Tools/Email.pm @@ -19,8 +19,11 @@ our $VERSION = "3.0.0"; my $THIS_FILE = "Email.pm"; ### Methods; -# check_queue # check_config +# check_queue +# get_current_server +# get_next_server +# send_alerts # =pod @@ -249,6 +252,53 @@ sub check_queue return($oldest_message); } +=head2 get_current_server + +This method returns of the C<< mail_server_uuid >> of the currently configured mail server. If no mail server is currently configured, an empty string is returned. + +This method takes no parameters. + +=cut +sub get_current_server +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Email->get_current_server()" }}); + + if (not exists $anvil->data->{mail_servers}{mail_server}) + { + # Try loading the mail server data. + $anvil->Database->get_mail_servers({debug => $debug}); + } + + my $newest_mail_server_time = 0; + my $newest_mail_server_uuid = ""; + foreach my $mail_server_uuid (keys %{$anvil->data->{mail_servers}{mail_server}}) + { + my $last_used = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{last_used}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + mail_server_uuid => $mail_server_uuid, + last_used => $last_used, + }}); + + if ($last_used > $newest_mail_server_time) + { + $newest_mail_server_time = $last_used; + $newest_mail_server_uuid = $mail_server_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + newest_mail_server_time => $newest_mail_server_time, + newest_mail_server_uuid => $newest_mail_server_uuid, + }}); + } + } + + # TODO: Verify that this mail server is actually configured. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { newest_mail_server_uuid => $newest_mail_server_uuid }}); + return($newest_mail_server_uuid); +} + =head2 get_next_server When two or more mail servers are configured, this will return the C<< mail_server_uuid >> of the mail server used in the most distant past. If two or more mail servers have never been used before, a random unused server is returned. @@ -264,8 +314,7 @@ sub get_next_server my $parameter = shift; my $anvil = $self->parent; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Email->check_queue()" }}); - + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Email->get_next_server()" }}); # If configured/running, the number of messages in queue is checked. If '0', # 'mail_server::queue_empty' is updated with the current time. If 1 or more, the time since the queue @@ -296,6 +345,230 @@ sub get_next_server return($oldest_mail_server_uuid); } +=head2 send_alerts + +This method looks for registered alerts, creates an email for recipients, and sends the resulting emails into the mail server queue for dispatch. + +This method takes no parameters. + +=cut +sub send_alerts +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Email->send_alerts()" }}); + + # Load the alerts + $anvil->Database->get_recipients({debug => 2}); + $anvil->Database->get_alerts({debug => 2}); + foreach my $alert_uuid (keys %{$anvil->data->{alerts}{alert_uuid}}) + { + my $alert_host_uuid = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_host_uuid}; + my $alert_set_by = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_set_by}; + my $alert_level = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_level}; + my $alert_title = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_title}; + my $alert_message = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_message}; + my $alert_sort_position = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_sort_position}; + my $alert_show_header = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_show_header}; + my $alert_processed = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{alert_processed}; + my $unix_modified_date = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{unix_modified_date}; + my $modified_date = $anvil->data->{alerts}{alert_uuid}{$alert_uuid}{modified_date}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + alert_host_uuid => $alert_host_uuid, + alert_set_by => $alert_set_by, + alert_level => $alert_level, + alert_title => $alert_title, + alert_message => $alert_message, + alert_sort_position => $alert_sort_position, + alert_show_header => $alert_show_header, + alert_processed => $alert_processed, + unix_modified_date => $unix_modified_date, + modified_date => $modified_date, + }}); + + # Walk through the recipients to see who wants to hear about this. + foreach my $recipient_uuid (keys %{$anvil->data->{recipients}{recipient_uuid}}) + { + my $recipient_name = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name}; + my $recipient_email = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}; + my $recipient_language = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language}; + my $recipient_level = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{level_on_host}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + recipient_level => $recipient_level, + }}); + + ### NOTE: Levels; + # 1 - critical + # 2 - warning + # 3 - notice + # 4 - info + if ($recipient_level <= $alert_level) + { + # The user wants it. + my $message = $anvil->Words->parse_banged_string({ + language => $recipient_language, + key_string => $alert_message, + + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { message => $message }}); + if ($alert_title) + { + my $title = $anvil->Words->parse_banged_string({ + language => $recipient_language, + key_string => $alert_title, + + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { title => $title }}); + + $message = $title."\n".$message."\n"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { message => $message }}); + } + + # Store it in a sortable hash. + $anvil->data->{alerts}{queue}{$recipient_uuid}{$alert_sort_position}{$alert_level}{$unix_modified_date}{$alert_uuid} = $message; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "alerts::queue::${recipient_uuid}::${alert_sort_position}::${alert_level}::${unix_modified_date}::${alert_uuid}" => $anvil->data->{alerts}{queue}{$recipient_uuid}{$alert_sort_position}{$alert_level}{$unix_modified_date}{$alert_uuid}, + }}); + + # This stores all recipients used in the 'Reply To' section. It also stores + # the highest alert level for the email subject line. + if (not exists $anvil->data->{alerts}{reply_to}{$recipient_uuid}) + { + $anvil->data->{alerts}{reply_to}{$recipient_uuid}{highest_alert_level} = $alert_level; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "alerts::reply_to::${recipient_uuid}::highest_alert_level" => $anvil->data->{alerts}{reply_to}{$recipient_uuid}{highest_alert_level}, + }}); + } + elsif ($alert_level < $anvil->data->{alerts}{reply_to}{$recipient_uuid}) + { + $anvil->data->{alerts}{reply_to}{$recipient_uuid}{highest_alert_level} = $alert_level; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "alerts::reply_to::${recipient_uuid}::highest_alert_level" => $anvil->data->{alerts}{reply_to}{$recipient_uuid}{highest_alert_level}, + }}); + } + } + } + } + + # Build the emails now. + my $host_name = $anvil->_host_name; + foreach my $recipient_uuid (keys %{$anvil->data->{alerts}{queue}}) + { + my $recipient_name = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_name}; + my $recipient_email = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_email}; + my $recipient_language = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_language}; + my $highest_alert_level = $anvil->data->{alerts}{reply_to}{$recipient_uuid}{highest_alert_level}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + highest_alert_level => $highest_alert_level, + }}); + + # Build the message subject (I know I could be clever and append the level to 'email_000' but + # that makes it harder to search the code to uses of keys). + my $subject_key = "email_0004"; + if ($highest_alert_level == 3) { $subject_key = "email_0003"; } + elsif ($highest_alert_level == 2) { $subject_key = "email_0002"; } + elsif ($highest_alert_level == 1) { $subject_key = "email_0001"; } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { subject_key => $subject_key }}); + + my $subject = $anvil->Words->string({ + language => $recipient_language, + key => $subject_key, + variables => { + host_name => $host_name, + }, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { subject => $subject }}); + + my $footer = $anvil->Words->string({ + language => $recipient_language, + key => "email_0005", + variables => { + host_name => $host_name, + }, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { footer => $footer }}); + + ### TODO: Determine if any of the sorts need to be reversed + # Build the message body now. + my $body = ""; + foreach my $alert_sort_position (sort {$a cmp $b} keys %{$anvil->data->{alerts}{queue}{$recipient_uuid}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_sort_position => $alert_sort_position }}); + foreach my $alert_level (sort {$a cmp $b} keys %{$anvil->data->{alerts}{queue}{$recipient_uuid}{$alert_sort_position}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { alert_level => $alert_level }}); + foreach my $unix_modified_date (sort {$a cmp $b} keys %{$anvil->data->{alerts}{queue}{$recipient_uuid}{$alert_sort_position}{$alert_level}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { unix_modified_date => $unix_modified_date }}); + foreach my $alert_uuid (keys %{$anvil->data->{alerts}{queue}{$recipient_uuid}{$alert_sort_position}{$alert_level}{$unix_modified_date}}) + { + my $message = $anvil->data->{alerts}{queue}{$recipient_uuid}{$alert_sort_position}{$alert_level}{$unix_modified_date}{$alert_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:alert_uuid' => $alert_uuid, + 's2:message' => $message, + }}); + + $body .= $message."\n"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { body => $body }}); + } + } + } + } + + # Not build the "Reply To" line. + my $reply_to = ""; + foreach my $other_recipient_uuid (keys %{$anvil->data->{alerts}{queue}}) + { + next if $recipient_uuid eq $other_recipient_uuid; + my $other_recipient_email = $anvil->data->{recipients}{recipient_uuid}{$other_recipient_uuid}{recipient_email}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + other_recipient_uuid => $other_recipient_uuid, + other_recipient_email => $other_recipient_email, + }}); + + $reply_to .= $other_recipient_email.", "; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reply_to => $reply_to }}); + } + $reply_to =~ s/, $//; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { reply_to => $reply_to }}); + + # Who are we sending as? + my $mail_server_uuid = $anvil->Email->get_current_server({debug => 2}); + my $from = $mail_server_uuid ? $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_username} : "root\@".$host_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + mail_server_uuid => $mail_server_uuid, + from => $from, + }}); + + # Ready! + my $email_body = " +From: ".$from." +To: ".$recipient_name." <".$recipient_email."> +Subject: ".$subject." +Reply-To: ".$reply_to." + +".$body." +".$footer." +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { email_body => $email_body }}); + + # Write it to a file. + + # Call mailx to read it in + } + + return(0); +} + + # =head3 # # Private Functions; diff --git a/Anvil/Tools/Words.pm b/Anvil/Tools/Words.pm index 84707839..d81703b2 100644 --- a/Anvil/Tools/Words.pm +++ b/Anvil/Tools/Words.pm @@ -337,7 +337,17 @@ sub parse_banged_string # Setup default values my $out_string = ""; my $key_string = defined $parameter->{key_string} ? $parameter->{key_string} : 0; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { key_string => $key_string }}); + my $language = defined $parameter->{language} ? $parameter->{language} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + key_string => $key_string, + language => $language, + }}); + + if (not $language) + { + $language = $anvil->Words->language(); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { language => $language }}); + } # Some variable values will be multi-line strings. We need to replace the new-lines in those # multi-line values into '##br##' so that we can do a proper variable insertion. We can't simply @@ -462,13 +472,22 @@ sub parse_banged_string } # Parse the line now. - $out_string .= $anvil->Words->string({debug => $debug, key => $key, variables => $variables}); + $out_string .= $anvil->Words->string({ + test => 0, + key => $key, + variables => $variables, + language => $language, + }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { out_string => $out_string }}); } else { # This key is just a key, no variables. - $out_string .= $anvil->Words->string({test => 0, key => $message}); + $out_string .= $anvil->Words->string({ + test => 0, + key => $message, + language => $language, + }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { out_string => $out_string }}); } } @@ -728,8 +747,8 @@ sub string # Setup default values my $key = defined $parameter->{key} ? $parameter->{key} : ""; - my $language = defined $parameter->{language} ? $parameter->{language} : $anvil->Words->language; - my $file = defined $parameter->{file} ? $parameter->{file} : $anvil->data->{path}{words}{'words.xml'}; + my $language = $parameter->{language} ? $parameter->{language} : $anvil->Words->language; + my $file = $parameter->{file} ? $parameter->{file} : $anvil->data->{path}{words}{'words.xml'}; my $string = defined $parameter->{string} ? $parameter->{string} : ""; my $variables = defined $parameter->{variables} ? $parameter->{variables} : ""; ### NOTE: Don't call Log->entry here, or we'll get a recursive loop! Use 'test' to debug. diff --git a/cgi-bin/striker b/cgi-bin/striker index 8135ce00..ba0cadc9 100755 --- a/cgi-bin/striker +++ b/cgi-bin/striker @@ -412,27 +412,25 @@ sub process_email_recipient_page { my ($anvil) = @_; - my $recipient_uuid = defined $anvil->data->{cgi}{recipient_uuid}{value} ? $anvil->data->{cgi}{recipient_uuid}{value} : ""; - my $recipient_name = defined $anvil->data->{cgi}{recipient_name}{value} ? $anvil->data->{cgi}{recipient_name}{value} : ""; - my $recipient_email = defined $anvil->data->{cgi}{recipient_email}{value} ? $anvil->data->{cgi}{recipient_email}{value} : ""; - my $recipient_language = defined $anvil->data->{cgi}{recipient_language}{value} ? $anvil->data->{cgi}{recipient_language}{value} : "en_CA"; - my $recipient_units = defined $anvil->data->{cgi}{recipient_units}{value} ? $anvil->data->{cgi}{recipient_units}{value} : "metric"; - my $recipient_new_level = defined $anvil->data->{cgi}{recipient_new_level}{value} ? $anvil->data->{cgi}{recipient_new_level}{value} : "2"; - my $delete = defined $anvil->data->{cgi}{'delete'}{value} ? $anvil->data->{cgi}{'delete'}{value} : ""; - my $back = defined $anvil->data->{cgi}{back}{value} ? $anvil->data->{cgi}{back}{value} : ""; - my $save = defined $anvil->data->{cgi}{save}{value} ? $anvil->data->{cgi}{save}{value} : ""; - my $confirm = defined $anvil->data->{cgi}{confirm}{value} ? $anvil->data->{cgi}{confirm}{value} : ""; + my $recipient_uuid = defined $anvil->data->{cgi}{recipient_uuid}{value} ? $anvil->data->{cgi}{recipient_uuid}{value} : ""; + my $recipient_name = defined $anvil->data->{cgi}{recipient_name}{value} ? $anvil->data->{cgi}{recipient_name}{value} : ""; + my $recipient_email = defined $anvil->data->{cgi}{recipient_email}{value} ? $anvil->data->{cgi}{recipient_email}{value} : ""; + my $recipient_language = defined $anvil->data->{cgi}{recipient_language}{value} ? $anvil->data->{cgi}{recipient_language}{value} : "en_CA"; + my $recipient_level = defined $anvil->data->{cgi}{recipient_level}{value} ? $anvil->data->{cgi}{recipient_level}{value} : "2"; + my $delete = defined $anvil->data->{cgi}{'delete'}{value} ? $anvil->data->{cgi}{'delete'}{value} : ""; + my $back = defined $anvil->data->{cgi}{back}{value} ? $anvil->data->{cgi}{back}{value} : ""; + my $save = defined $anvil->data->{cgi}{save}{value} ? $anvil->data->{cgi}{save}{value} : ""; + my $confirm = defined $anvil->data->{cgi}{confirm}{value} ? $anvil->data->{cgi}{confirm}{value} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - recipient_uuid => $recipient_uuid, - recipient_name => $recipient_name, - recipient_email => $recipient_email, - recipient_language => $recipient_language, - recipient_units => $recipient_units, - recipient_new_level => $recipient_new_level, - back => $back, - save => $save, - 'delete' => $delete, - confirm => $confirm, + recipient_uuid => $recipient_uuid, + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + recipient_level => $recipient_level, + back => $back, + save => $save, + 'delete' => $delete, + confirm => $confirm, }}); if ($back) @@ -456,8 +454,7 @@ SELECT recipient_name, recipient_email, recipient_language, - recipient_units, - recipient_new_level + recipient_level FROM recipients WHERE @@ -473,17 +470,15 @@ WHERE }}); foreach my $row (@{$results}) { - $recipient_name = $row->[0]; - $recipient_email = $row->[1]; - $recipient_language = $row->[2]; - $recipient_units = $row->[3]; - $recipient_new_level = $row->[4]; + $recipient_name = $row->[0]; + $recipient_email = $row->[1]; + $recipient_language = $row->[2]; + $recipient_level = $row->[3]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - recipient_name => $recipient_name, - recipient_email => $recipient_email, - recipient_language => $recipient_language, - recipient_units => $recipient_units, - recipient_new_level => $recipient_new_level, + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + recipient_level => $recipient_level, }}); if ($delete) @@ -503,11 +498,10 @@ WHERE $anvil->data->{form}{ok_message} = $anvil->Template->get({file => "main.html", name => "ok_message", variables => { ok_message => $ok_message }}); # Clear the form - $recipient_name = ""; - $recipient_email = ""; - $recipient_language = "en_CA"; - $recipient_units = "metric"; - $recipient_new_level = "2"; + $recipient_name = ""; + $recipient_email = ""; + $recipient_language = "en_CA"; + $recipient_level = "2"; } else { @@ -576,13 +570,12 @@ WHERE if ($confirm) { ($recipient_uuid) = $anvil->Database->insert_or_update_recipients({ - debug => 2, - recipient_uuid => $recipient_uuid, - recipient_name => $recipient_name, - recipient_email => $recipient_email, - recipient_language => $recipient_language, - recipient_units => $recipient_units, - recipient_new_level => $recipient_new_level, + debug => 2, + recipient_uuid => $recipient_uuid, + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + recipient_level => $recipient_level, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { recipient_uuid => $recipient_uuid }}); if ($recipient_uuid) @@ -592,12 +585,11 @@ WHERE $anvil->data->{form}{ok_message} = $anvil->Template->get({file => "main.html", name => "ok_message", variables => { ok_message => $ok_message }}); # Clear the form - $recipient_uuid = ""; - $recipient_name = ""; - $recipient_email = ""; - $recipient_language = "en_CA"; - $recipient_units = "metric"; - $recipient_new_level = "2"; + $recipient_uuid = ""; + $recipient_name = ""; + $recipient_email = ""; + $recipient_language = "en_CA"; + $recipient_level = "2"; } else { @@ -608,27 +600,22 @@ WHERE } else { - my $say_recipient_units = "#!string!unit_0027!#"; - if ($recipient_language eq "imperial") - { - $say_recipient_units = "#!string!unit_0028!#"; - } # Ignore - my $say_recipient_new_level = "#!string!unit_0023!#"; - if ($recipient_new_level eq "1") + my $say_recipient_level = "#!string!unit_0023!#"; + if ($recipient_level eq "1") { # Critical - $say_recipient_new_level = "#!string!unit_0024!#"; + $say_recipient_level = "#!string!unit_0024!#"; } - elsif ($recipient_new_level eq "2") + elsif ($recipient_level eq "2") { # Warning - $say_recipient_new_level = "#!string!unit_0025!#"; + $say_recipient_level = "#!string!unit_0025!#"; } - elsif ($recipient_new_level eq "3") + elsif ($recipient_level eq "3") { # Notice - $say_recipient_new_level = "#!string!unit_0026!#"; + $say_recipient_level = "#!string!unit_0026!#"; } my $say_recipient_language = $anvil->Words->language({iso => $recipient_language, long => 1}); $say_recipient_language = "???" if not $say_recipient_language; @@ -639,15 +626,13 @@ WHERE $anvil->data->{form}{back_link} =~ s/save=.*?$//; $anvil->data->{form}{refresh_link} = ""; $anvil->data->{form}{body} = $anvil->Template->get({file => "email.html", name => "recipient-confirm", variables => { - recipient_uuid => $recipient_uuid, - recipient_name => $recipient_name, - recipient_email => $recipient_email, - recipient_language => $recipient_language, - say_recipient_language => $say_recipient_language, - recipient_units => $recipient_units, - say_recipient_units => $say_recipient_units, - recipient_new_level => $recipient_new_level, - say_recipient_new_level => $say_recipient_new_level, + recipient_uuid => $recipient_uuid, + recipient_name => $recipient_name, + recipient_email => $recipient_email, + recipient_language => $recipient_language, + say_recipient_language => $say_recipient_language, + recipient_level => $recipient_level, + say_recipient_level => $say_recipient_level, }}); return(0); } @@ -728,21 +713,9 @@ WHERE class => $anvil->data->{cgi}{recipient_language}{alert} ? "input_alert" : "input_clear", }); - # Units (select) - my $recipient_units_select = $anvil->Template->select_form({ - name => "recipient_units", - options => [ - "metric#!#".$anvil->Words->string({key => "unit_0027"}), - "imperial#!#".$anvil->Words->string({key => "unit_0028"}), - ], - blank => 0, - selected => $recipient_units, - class => $anvil->data->{cgi}{recipient_units}{alert} ? "input_alert" : "input_clear", - }); - # Log Level (select) - my $recipient_new_level_select = $anvil->Template->select_form({ - name => "recipient_new_level", + my $recipient_level_select = $anvil->Template->select_form({ + name => "recipient_level", options => [ "0#!#".$anvil->Words->string({key => "unit_0023"}), "1#!#".$anvil->Words->string({key => "unit_0024"}), @@ -750,8 +723,8 @@ WHERE "3#!#".$anvil->Words->string({key => "unit_0026"}), ], blank => 0, - selected => $recipient_new_level, - class => $anvil->data->{cgi}{recipient_new_level}{alert} ? "input_alert" : "input_clear", + selected => $recipient_level, + class => $anvil->data->{cgi}{recipient_level}{alert} ? "input_alert" : "input_clear", }); # Show the menu. @@ -762,8 +735,7 @@ WHERE recipient_name => $recipient_name_form, recipient_email => $recipient_email_form, language => $recipient_language_select, - units => $recipient_units_select, - new_level => $recipient_new_level_select, + new_level => $recipient_level_select, recipient_uuid => $recipient_uuid, }}); diff --git a/html/skins/alteeve/email.html b/html/skins/alteeve/email.html index 65c4cbd0..ed5600cd 100644 --- a/html/skins/alteeve/email.html +++ b/html/skins/alteeve/email.html @@ -352,8 +352,8 @@ #!string!striker_0191!# -   #!variable!say_recipient_new_level!# - +   #!variable!say_recipient_level!# + @@ -365,15 +365,6 @@ - - - #!string!striker_0193!# - - -   #!variable!say_recipient_units!# - - -   diff --git a/notes b/notes index 73b6b635..81c83b21 100644 --- a/notes +++ b/notes @@ -1,3 +1,82 @@ +BEGIN TRANSACTION; +ALTER TABLE history.recipients RENAME recipient_new_level TO recipient_level; +ALTER TABLE history.recipients DROP COLUMN recipient_units; +ALTER TABLE recipients RENAME recipient_new_level TO recipient_level; +ALTER TABLE recipients DROP COLUMN recipient_units; + +DROP FUNCTION history_recipients() CASCADE; +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, + recipient_name, + recipient_email, + recipient_language, + recipient_level, + modified_date) + VALUES + (history_recipients.recipient_uuid, + history_recipients.recipient_name, + history_recipients.recipient_email, + history_recipients.recipient_language, + history_recipients.recipient_level, + history_recipients.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_recipients() OWNER TO admin; + +CREATE TRIGGER trigger_recipients + AFTER INSERT OR UPDATE ON recipients + FOR EACH ROW EXECUTE PROCEDURE history_recipients(); + +ALTER TABLE alerts ADD COLUMN alert_processed integer not null default 0; +ALTER TABLE history.alerts ADD COLUMN alert_processed integer; +DROP FUNCTION history_alerts() CASCADE; +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_message, + alert_sort_position, + alert_show_header, + alert_processed, + 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.alert_processed, + 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; + ============ From: test-alert@alert.alteeve.com To: Madison Kelly diff --git a/rpm/SPECS/anvil.spec b/rpm/SPECS/anvil.spec index 4437a68b..43fca5f7 100644 --- a/rpm/SPECS/anvil.spec +++ b/rpm/SPECS/anvil.spec @@ -3,7 +3,7 @@ %define anvilgroup admin Name: anvil Version: 3.0 -Release: 35%{?dist} +Release: 36%{?dist} Summary: Alteeve Anvil! complete package. License: GPLv2+ @@ -374,6 +374,9 @@ fi %changelog +* tbd Madison Kelly 3.0-36 +- Updated source. + * Thu Sep 03 2020 Madison Kelly 3.0-35 - Added screen as a core module dependency - Updated source. diff --git a/share/anvil.sql b/share/anvil.sql index 23e578dc..5023135a 100644 --- a/share/anvil.sql +++ b/share/anvil.sql @@ -403,6 +403,7 @@ CREATE TABLE alerts ( alert_message text not null, -- ScanCore will read in the agents .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. + alert_processed integer not null default 0, -- This is set to '1' when an alert has been processed (sent to recipients) modified_date timestamp with time zone not null, FOREIGN KEY(alert_host_uuid) REFERENCES hosts(host_uuid) @@ -419,6 +420,7 @@ CREATE TABLE history.alerts ( alert_message text, alert_sort_position integer, alert_show_header integer, + alert_processed integer, modified_date timestamp with time zone not null ); ALTER TABLE history.alerts OWNER TO admin; @@ -438,6 +440,7 @@ BEGIN alert_message, alert_sort_position, alert_show_header, + alert_processed, modified_date) VALUES (history_alerts.alert_uuid, @@ -448,6 +451,7 @@ BEGIN history_alerts.alert_message, history_alerts.alert_sort_position, history_alerts.alert_show_header, + history_alerts.alert_processed, history_alerts.modified_date); RETURN NULL; END; @@ -460,28 +464,24 @@ CREATE TRIGGER trigger_alerts FOR EACH ROW EXECUTE PROCEDURE history_alerts(); --- NOTE: This doesn't store the user's level, as it might be unique per Anvil!. --- 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 not null, -- If set, this is the language the user wants to receive alerts in. If not set, the default language is used. - recipient_units text not null, -- This can be set to 'imperial' if the user prefers temperatures in °F - 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, 1 is critical, 2 is warning, and 3 is notice - modified_date timestamp with time zone not null + 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 not null, -- If set, this is the language the user wants to receive alerts in. If not set, the default language is used. + recipient_level integer not null, -- This is the default alert level this recipient is interested in. It can be adjusted on a per-host basis via the 'notifications' table. + modified_date timestamp with time zone not null ); ALTER TABLE recipients OWNER TO admin; CREATE TABLE history.recipients ( - history_id bigserial, - recipient_uuid uuid, - recipient_name text, - recipient_email text, - recipient_language text, - recipient_units text, - recipient_new_level integer, - modified_date timestamp with time zone not null + history_id bigserial, + recipient_uuid uuid, + recipient_name text, + recipient_email text, + recipient_language text, + recipient_level integer, + modified_date timestamp with time zone not null ); ALTER TABLE history.recipients OWNER TO admin; @@ -496,16 +496,14 @@ BEGIN recipient_name, recipient_email, recipient_language, - recipient_units, - recipient_new_level, + recipient_level, modified_date) VALUES (history_recipients.recipient_uuid, history_recipients.recipient_name, history_recipients.recipient_email, history_recipients.recipient_language, - history_recipients.recipient_units, - history_recipients.recipient_new_level, + history_recipients.recipient_level, history_recipients.modified_date); RETURN NULL; END; @@ -518,8 +516,8 @@ CREATE TRIGGER trigger_recipients FOR EACH ROW EXECUTE PROCEDURE history_recipients(); --- This creates links between recipients and Anvil! systems, with a request alert level, so that we can --- decide who gets what alerts for a given Anvil! system +-- This table is used when a user wants to set a custom alert level for a given machine. Typically this is +-- used to ignore test Anvil! systems. CREATE TABLE notifications ( notification_uuid uuid not null primary key, notification_recipient_uuid uuid not null, -- The recipient we're linking. @@ -536,7 +534,7 @@ CREATE TABLE history.notifications ( history_id bigserial, notification_uuid uuid, notification_recipient_uuid uuid, - notification_host_uuid uuid, + notification_host_uuid uuid, notification_alert_level integer, modified_date timestamp with time zone not null ); @@ -1647,7 +1645,8 @@ ALTER TABLE updated OWNER TO admin; -- To avoid "waffling" when a sensor is close to an alert (or cleared) threshold, a gap between the alarm -- value and the clear value is used. If the sensor climbs above (or below) the "clear" value, but didn't -- previously pass the "alert" threshold, we DON'T want to send an "all clear" message. So do solve that, --- this table is used by agents to record when a warning message was sent. +-- this table is used by agents to record when a warning message was sent (and as such, which need to be +-- cleared). CREATE TABLE alert_sent ( alert_sent_uuid uuid not null primary key, alert_sent_host_uuid uuid not null, -- The node associated with this alert diff --git a/share/words.xml b/share/words.xml index f1c2a4f0..2f4e1d6c 100644 --- a/share/words.xml +++ b/share/words.xml @@ -35,6 +35,23 @@ Author: Madison Kelly DR Host Unknown Type + + [ #!string!brand_0004!# ] - Critical level alert from #!variable!host_name!# + [ #!string!brand_0004!# ] - Warning level alert from #!variable!host_name!# + [ #!string!brand_0004!# ] - Notice level alert from #!variable!host_name!# + [ #!string!brand_0004!# ] - Informational level alert from #!variable!host_name!# + +-- +This alert email was sent from the machine: +- #!variable!host_name!# + +It was generated by #!string!brand_0004!#, which is part of the #!string!brand_0002!# Intelligent Availability platform running on the host above. + +This email was *not* sent by #!string!scancore_brand_0001!#. If you do not know why you are receiving this email, please speak to your system's administrator. + +If you need any assistance, please feel free to contact #!string!brand_0001!# (https://alteeve.com) and we will do our best to assist. + + There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network). The local system UUID can't be read yet. This might be because the system is brand new and/or ScanCore hasn't run yet. Please try again in a minute. @@ -158,7 +175,7 @@ Failed to generate an RSA public key for the user: [#!variable!user!#]. The outp The file: [#!variable!file!#] was not found. find_matches() was given the hash key: [#!variable!key!#], but it does not reference a hash. Are any IPs associated with this target?]]> Failed to reconnect after reconfiguring the network, exiting. - The 'recipient_new_level': [#!variable!recipient_new_level!#] is invalid. It should be '0', '1', '2', or '3'. + The 'recipient_level': [#!variable!recipient_level!#] is invalid. It should be '0', '1', '2', or '3'. The 'notification_alert_level': [#!variable!notification_alert_level!#] is invalid. It should be '0', '1', '2', or '3'. The 'notification_uuid': [#!variable!notification_uuid!#] was not found in the database. @@ -210,6 +227,7 @@ The error was: =========================================================== There appears to be no mail server in the database with the UUID: [#!variable!uuid!#]. + There alert level: [#!variable!alert_level!#] is invalid. Valid values are '1' / 'critical', '2' / 'warning, '3' / 'notice', and '4' / 'info'. Current Network Interfaces and States @@ -1289,6 +1307,7 @@ About to try to download aproximately: [#!variable!packages!#] packages needed t ############################################################################################################# Hosts added or updated by the #!string!brand_0002!# on: [#!variable!date!#]: + ScanCore has started. Saved the mail server information successfully! @@ -1699,8 +1718,8 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st Critical Warning Notice - Metric - Imperial + Info + Up Down Mbps diff --git a/tools/test.pl b/tools/test.pl index 688be86b..f1c585c9 100755 --- a/tools/test.pl +++ b/tools/test.pl @@ -28,7 +28,17 @@ $anvil->Get->switches; print "Connecting to the database(s);\n"; $anvil->Database->connect({debug => 2}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0132"}); -# -# my ($oldest_message) = $anvil->Email->check_queue({debug => 2}); -# print "Oldest message: [".$oldest_message."]\n"; +if (0) +{ + $anvil->Alert->register({ + debug => 2, + alert_level => "warning", + message => "message_0179", + set_by => $THIS_FILE, + }); +} +if (1) +{ + $anvil->Email->send_alerts({debug => 2}); +}