* Started adding in front-end support for managing email servers and alert recipients. Added the new 'Email' module to (later) habdle all email-related tasks.

* Fixed a bug in Accounts->read_cookies where, when a user's hash had expired, the logged error message didn't show the user's name.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent 2f81275551
commit c3869a2ff6
  1. 15
      Anvil/Tools.pm
  2. 22
      Anvil/Tools/Account.pm
  3. 121
      Anvil/Tools/Email.pm
  4. 51
      cgi-bin/striker
  5. 37
      html/skins/alteeve/email.html
  6. 6
      html/skins/alteeve/email.js
  7. BIN
      html/skins/alteeve/images/email_off.png
  8. BIN
      html/skins/alteeve/images/email_on.png
  9. BIN
      html/skins/alteeve/images/email_recipient.png
  10. BIN
      html/skins/alteeve/images/email_server.png
  11. 9
      html/skins/alteeve/images/sources.txt
  12. 11
      html/skins/alteeve/main.html
  13. 32
      notes
  14. 1
      rpm/SPECS/anvil.spec
  15. 4
      share/words.xml
  16. 2
      tools/scancore
  17. 2
      tools/test.pl

@ -44,6 +44,7 @@ use Anvil::Tools::Alert;
use Anvil::Tools::Convert; use Anvil::Tools::Convert;
use Anvil::Tools::Database; use Anvil::Tools::Database;
use Anvil::Tools::DRBD; use Anvil::Tools::DRBD;
use Anvil::Tools::Email;
use Anvil::Tools::Get; use Anvil::Tools::Get;
use Anvil::Tools::Job; use Anvil::Tools::Job;
use Anvil::Tools::Log; use Anvil::Tools::Log;
@ -125,6 +126,7 @@ sub new
CONVERT => Anvil::Tools::Convert->new(), CONVERT => Anvil::Tools::Convert->new(),
DATABASE => Anvil::Tools::Database->new(), DATABASE => Anvil::Tools::Database->new(),
DRBD => Anvil::Tools::DRBD->new(), DRBD => Anvil::Tools::DRBD->new(),
EMAIL => Anvil::Tools::Email->new(),
GET => Anvil::Tools::Get->new(), GET => Anvil::Tools::Get->new(),
LOG => Anvil::Tools::Log->new(), LOG => Anvil::Tools::Log->new(),
JOB => Anvil::Tools::Job->new(), JOB => Anvil::Tools::Job->new(),
@ -166,6 +168,7 @@ sub new
$anvil->Convert->parent($anvil); $anvil->Convert->parent($anvil);
$anvil->Database->parent($anvil); $anvil->Database->parent($anvil);
$anvil->DRBD->parent($anvil); $anvil->DRBD->parent($anvil);
$anvil->Email->parent($anvil);
$anvil->Get->parent($anvil); $anvil->Get->parent($anvil);
$anvil->Log->parent($anvil); $anvil->Log->parent($anvil);
$anvil->Job->parent($anvil); $anvil->Job->parent($anvil);
@ -502,6 +505,18 @@ sub DRBD
return ($self->{HANDLE}{DRBD}); return ($self->{HANDLE}{DRBD});
} }
=head2 Email
Access the C<Email.pm> methods via 'C<< $anvil->Email->method >>'.
=cut
sub Email
{
my $self = shift;
return ($self->{HANDLE}{EMAIL});
}
=head2 Get =head2 Get
Access the C<Get.pm> methods via 'C<< $anvil->Get->method >>'. Access the C<Get.pm> methods via 'C<< $anvil->Get->method >>'.

@ -531,15 +531,19 @@ sub read_cookies
# Validate the cookie if there is a User UUID. Pick the random number up from the database. # Validate the cookie if there is a User UUID. Pick the random number up from the database.
my $query = " my $query = "
SELECT SELECT
session_salt a.user_name,
b.session_salt
FROM FROM
sessions users a,
sessions b
WHERE WHERE
session_user_uuid = ".$anvil->Database->quote($anvil->data->{cookie}{anvil_user_uuid})." a.user_uuid = b.session_user_uuid
AND
b.session_user_uuid = ".$anvil->Database->quote($anvil->data->{cookie}{anvil_user_uuid})."
AND AND
session_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." b.session_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)."
;"; ;";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results}; my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
@ -562,9 +566,13 @@ AND
} }
# Read in their "rand" value # Read in their "rand" value
$anvil->data->{sessions}{session_salt} = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; $anvil->data->{sys}{users}{user_name} = $results->[0]->[0];
$anvil->data->{sessions}{session_salt} = $results->[0]->[1];
$anvil->data->{sessions}{session_salt} = "" if not defined $anvil->data->{sessions}{session_salt}; $anvil->data->{sessions}{session_salt} = "" if not defined $anvil->data->{sessions}{session_salt};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sessions::session_salt" => $anvil->data->{sessions}{session_salt} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"sys::users::user_name" => $anvil->data->{sys}{users}{user_name},
"sessions::session_salt" => $anvil->data->{sessions}{session_salt},
}});
# Generate a hash using today and yesterday's date. # Generate a hash using today and yesterday's date.
my ($today_hash) = $anvil->Account->_build_cookie_hash({ my ($today_hash) = $anvil->Account->_build_cookie_hash({

@ -0,0 +1,121 @@
package Anvil::Tools::Email;
#
# This module contains methods used to manage the local postfix server and handle and dispatch email via
# mailx.
#
use strict;
use warnings;
use Scalar::Util qw(weaken isweak);
use Data::Dumper;
our $VERSION = "3.0.0";
my $THIS_FILE = "Email.pm";
### Methods;
=pod
=encoding utf8
=head1 NAME
Anvil::Tools::Email
Provides all methods used to manage the local C<< postfix >> server and handle and dispatch email via C<< mailx >>
=head1 SYNOPSIS
use Anvil::Tools;
# Get a common object handle on all Anvil::Tools modules.
my $anvil = Anvil::Tools->new();
# Access to methods using '$anvil->Email->X'.
#
#
=head1 METHODS
Methods in this module;
=cut
sub new
{
my $class = shift;
my $self = {
};
bless $self, $class;
return ($self);
}
# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more
# sense in this case to think of it as a parent.
sub parent
{
my $self = shift;
my $parent = shift;
$self->{HANDLE}{TOOLS} = $parent if $parent;
# Defend against memory leads. See Scalar::Util'.
if (not isweak($self->{HANDLE}{TOOLS}))
{
weaken($self->{HANDLE}{TOOLS});
}
return ($self->{HANDLE}{TOOLS});
}
#############################################################################################################
# Public methods #
#############################################################################################################
=head2 check_postfix
This method checks the current postfix server configuration to see if it needs to be updated, then checks to see if the local C<< postfix >> daemin is enabled and started.
If any problem is encountered, C<< 1 >> is returned. Otherwise, if all is well, C<< 0 >> is returned.
Parameters;
=head3 config (optional, default '1')
If set to C<< 0 >>, the configuration is not checked or updated.
=head3 daemon (optional, default '1')
If set to C<< 0 >>, the C<< postfix >> daemon is not checked or started.
=cut
sub check_postfix
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $problem = 0;
my $config = defined $parameter->{config} ? $parameter->{config} : 1;
my $daemon = defined $parameter->{daemon} ? $parameter->{daemon} : 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
config => $config,
daemon => $daemon,
}});
return($problem);
}
# =head3
#
# Private Functions;
#
# =cut
#############################################################################################################
# Private functions #
#############################################################################################################

@ -210,6 +210,7 @@ sub print_and_exit
files_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "files_button_on"}) : $anvil->Template->get({file => "main.html", name => "files_button_off"}), files_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "files_button_on"}) : $anvil->Template->get({file => "main.html", name => "files_button_off"}),
jobs_button => $say_jobs_button, jobs_button => $say_jobs_button,
striker_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "striker_button_on"}) : $anvil->Template->get({file => "main.html", name => "striker_button_off"}), striker_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "striker_button_on"}) : $anvil->Template->get({file => "main.html", name => "striker_button_off"}),
email_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "email_button_on"}) : $anvil->Template->get({file => "main.html", name => "email_button_off"}),
user_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "user_button_on"}) : $anvil->Template->get({file => "main.html", name => "user_button_off"}), user_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "user_button_on"}) : $anvil->Template->get({file => "main.html", name => "user_button_off"}),
}}); }});
my $footer = $anvil->Template->get({file => "main.html", name => "footer", variables => { my $footer = $anvil->Template->get({file => "main.html", name => "footer", variables => {
@ -329,6 +330,10 @@ sub process_task
{ {
process_jobs_menu($anvil); process_jobs_menu($anvil);
} }
elsif ($anvil->data->{cgi}{email}{value})
{
process_email_menu($anvil);
}
else else
{ {
# Load the main page. # Load the main page.
@ -349,6 +354,52 @@ sub process_task
return(0); return(0);
} }
# This handles tasks related to email, mail servers and alert recipients.
sub process_email_menu
{
my ($anvil) = @_;
$anvil->data->{form}{back_link} = "?email=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};
$anvil->data->{cgi}{save}{value} = "" if not defined $anvil->data->{cgi}{save}{value};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"form::back_link" => $anvil->data->{form}{back_link},
"cgi::task::value" => $anvil->data->{cgi}{task}{value},
"cgi::action::value" => $anvil->data->{cgi}{action}{value},
"cgi::save::value" => $anvil->data->{cgi}{save}{value},
}});
if ($anvil->data->{cgi}{task}{value} eq "email_server")
{
#process_email_server_page($anvil);
}
elsif ($anvil->data->{cgi}{task}{value} eq "email_recipient")
{
#process_email_recipient_page($anvil);
}
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,
}});
# The 'back' goes home
$anvil->data->{form}{back_link} = "?";
$anvil->data->{form}{refresh_link} = "?email=true";
$anvil->data->{form}{body} = $anvil->Template->get({file => "email.html", name => "main-menu", variables => {
}});
}
return(0);
}
# This handles the "Striker" menu items. # This handles the "Striker" menu items.
sub process_striker_menu sub process_striker_menu
{ {

@ -0,0 +1,37 @@
<!-- start main-menu -->
<table align="center" class="anvil_main_menu">
<script type="text/javascript" src="/skins/alteeve/email.js"></script>
<tr>
<td colspan="2">
&nbsp;
</td>
</tr>
<tr>
<td colspan="2" class="title">
#!string!striker_0165!#
</td>
</tr>
<tr>
<td colspan="2">
&nbsp;
</td>
</tr>
<tr>
<td class="main_option_icon">
<a href="?email=true&task=email_server"><img src="#!data!skin::url!#/images/email_server.png" class="top_icon" ></a>
</td>
<td class="main_option">
<a href="?email=true&task=email_server">#!string!striker_0166!#</a>
</td>
</tr>
<tr>
<td class="main_option_icon">
<a href="?email=true&task=email_recipient"><img src="#!data!skin::url!#/images/email_recipient.png" class="top_icon" ></a>
</td>
<td class="main_option">
<a href="?email=true&task=email_recipient">#!string!striker_0167!#</a>
</td>
</tr>
</table>
<!-- end main-menu -->

@ -0,0 +1,6 @@
$.ajaxSetup({
cache: false
});
$(function() {
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -58,3 +58,12 @@ Folder by Mint Shirt from the Noun Project (https://thenounproject.com/term/fold
Broken Key by I Putu Kharismayadi from the Noun Project (https://thenounproject.com/term/folder/1481931/) Broken Key by I Putu Kharismayadi from the Noun Project (https://thenounproject.com/term/folder/1481931/)
- broken_key.png - broken_key.png
Mail By unlimicon, ID from the Noun Project (https://thenounproject.com/term/mail/867234/)
- email.png
Mail server Mail Server by SBTS from the Noun Project (https://thenounproject.com/term/mail-server/1236869/)
- email_server.png
Reader by Richa from the Noun Project
- email_recipient.png

@ -48,6 +48,9 @@
<td> <td>
#!variable!anvil_button!# #!variable!anvil_button!#
</td> </td>
<td>
#!variable!email_button!#
</td>
<td> <td>
#!variable!user_button!# #!variable!user_button!#
</td> </td>
@ -58,6 +61,14 @@
</table> </table>
<!-- end button_bar_right --> <!-- end button_bar_right -->
<!-- start email_button_off -->
<img src="#!data!skin::url!#/images/email_off.png" class="top_icon">
<!-- end email_button_off -->
<!-- start email_button_on -->
<a href="?email=true"><img src="#!data!skin::url!#/images/email_on.png" alt="#!string!striker_0164!#" class="top_icon"></a>
<!-- end email_button_on -->
<!-- start files_button_off --> <!-- start files_button_off -->
<img src="#!data!skin::url!#/images/files_off.png" class="top_icon"> <img src="#!data!skin::url!#/images/files_off.png" class="top_icon">
<!-- end files_button_off --> <!-- end files_button_off -->

32
notes

@ -48,21 +48,29 @@ dnf download --source awscli booth booth-arbitrator booth-core booth-site booth-
pcs pcs-snmp python3-azure-sdk python3-boto3 python3-botocore python3-fasteners python3-gflags python3-google-api-client python3-httplib2 python3-oauth2client python3-s3transfer python3-uritemplate \ pcs pcs-snmp python3-azure-sdk python3-boto3 python3-botocore python3-fasteners python3-gflags python3-google-api-client python3-httplib2 python3-oauth2client python3-s3transfer python3-uritemplate \
resource-agents resource-agents-aliyun resource-agents-gcp resource-agents resource-agents-aliyun resource-agents-gcp
rpm -Uvh python-s3transfer-0.1.13-1.el8.src.rpm python-oauth2client-4.1.2-6.el8.src.rpm booth-1.0-5.f2d38ce.git.el8.src.rpm google-api-python-client-1.6.5-3.el8.src.rpm python-boto3-1.6.1-2.el8.src.rpm python-httplib2-0.10.3-4.el8.src.rpm python-botocore-1.9.1-2.el8.src.rpm corosync-qdevice-3.0.0-2.el8.src.rpm python-uritemplate-3.0.0-3.el8.src.rpm python3-azure-sdk-4.0.0-9.el8.src.rpm awscli-1.14.50-5.el8.src.rpm python-gflags-2.0-15.el8ost.src.rpm resource-agents-4.1.1-33.el8.src.rpm python-fasteners-0.14.1-15.el8ost.src.rpm pcs-0.10.2-4.el8.src.rpm fence-agents-4.2.1-30.el8_1.1.src.rpm rpm -Uvh python-s3transfer-0.1.13-1.el8.src.rpm python-oauth2client-4.1.2-6.el8.src.rpm booth-1.0-5.f2d38ce.git.el8.src.rpm google-api-python-client-1.6.5-3.el8.src.rpm python-boto3-1.6.1-2.el8.src.rpm python-httplib2-0.10.3-4.el8.src.rpm python-botocore-1.9.1-2.el8.src.rpm corosync-qdevice-3.0.0-2.el8.src.rpm python-uritemplate-3.0.0-3.el8.src.rpm python3-azure-sdk-4.0.0-9.el8.src.rpm awscli-1.14.50-5.el8.src.rpm python-gflags-2.0-15.el8ost.src.rpm resource-agents-4.1.1-33.el8.src.rpm pcs-0.10.2-4.el8.src.rpm fence-agents-4.2.1-30.el8_1.1.src.rpm
rpm -Uvh python-fasteners-0.14.1-15.el8ost.src.rpm
rpmbuild -ba python-s3transfer.spec python-oauth2client.spec booth.spec google-api-python-client.spec python-boto3.spec python-httplib2.spec python-botocore.spec corosync-qdevice.spec python-uritemplate.spec python3-azure-sdk.spec \ rpm -Uvh awscli-1.14.50-5.el8.src.rpm booth-1.0-5.f2d38ce.git.el8.src.rpm corosync-qdevice-3.0.0-2.el8.src.rpm fence-agents-4.2.1-30.el8.1.src.rpm google-api-python-client-1.6.5-3.el8.src.rpm pacemaker-2.0.2-3.el8.2.src.rpm \
awscli.spec python-gflags.spec resource-agents.spec python-fasteners.spec pcs.spec fence-agents.spec pcs-0.10.2-4.el8.src.rpm python-boto3-1.6.1-2.el8.src.rpm python-botocore-1.9.1-2.el8.src.rpm python-gflags-2.0-15.el8.src.rpm python-httplib2-0.10.3-4.el8.src.rpm python-oauth2client-4.1.2-6.el8.src.rpm \
python-s3transfer-0.1.13-1.el8.src.rpm python-uritemplate-3.0.0-3.el8.src.rpm python3-azure-sdk-4.0.0-9.el8.src.rpm resource-agents-4.1.1-33.el8.src.rpm
The key packages which are needed are:
corosync rpmbuild -ba --sign pcs.spec
corosynclib-devel rpmbuild -ba --sign python3-azure-sdk.spec
pacemaker rpmbuild -ba --sign python-s3transfer.spec python-oauth2client.spec booth.spec google-api-python-client.spec python-boto3.spec python-httplib2.spec python-botocore.spec corosync-qdevice.spec python-uritemplate.spec awscli.spec python-gflags.spec resource-agents.spec fence-agents.spec
pacemaker-cli
pacemaker-doc # rpmbuild -ba python-fasteners.spec
pacemaker-libs-devel
pcs
resource-agents ### Need to find/build
python2-futures
python2-monotonic
dnf install booth-site corosync corosynclib-devel pacemaker pacemaker-cli pacemaker-libs-devel pcs
Network planning; Network planning;

@ -37,6 +37,7 @@ Requires: hdparm
Requires: htop Requires: htop
Requires: iproute Requires: iproute
Requires: lsscsi Requires: lsscsi
Requires: mailx
Requires: mlocate Requires: mlocate
Requires: perl-Capture-Tiny Requires: perl-Capture-Tiny
Requires: perl-Data-Dumper Requires: perl-Data-Dumper

@ -1019,6 +1019,10 @@ If you are comfortable that the target has changed for a known reason, you can s
<key name="striker_0161">The network will soon be reconfigured and then the target will reboot. In a couple minutes, it should be ready.</key> <key name="striker_0161">The network will soon be reconfigured and then the target will reboot. In a couple minutes, it should be ready.</key>
<key name="striker_0162">Return</key> <key name="striker_0162">Return</key>
<key name="striker_0163">How many network connections will exist for each network type.</key> <key name="striker_0163">How many network connections will exist for each network type.</key>
<key name="striker_0164">Email and alert configuration</key>
<key name="striker_0165">Alert email server and recipient configuration.</key>
<key name="striker_0166">Configure which server(s) can be used for forwarding email alerts to.</key>
<key name="striker_0167">Configure who will receive email alerts.</key>
<!-- These are generally units and appended to numbers --> <!-- These are generally units and appended to numbers -->
<key name="suffix_0001">#!variable!number!#/sec</key> <key name="suffix_0001">#!variable!number!#/sec</key>

@ -233,6 +233,8 @@ sub prepare_for_run
$anvil->Database->connect(); $anvil->Database->connect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0132"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0132"});
# TODO: Check/configure the mail server.
return(0); return(0);
} }

@ -30,4 +30,4 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure =
print "DB Connections: [".$anvil->data->{sys}{database}{connections}."]\n"; print "DB Connections: [".$anvil->data->{sys}{database}{connections}."]\n";
#$anvil->Network->load_interfces({debug => 2}); #$anvil->Network->load_interfces({debug => 2});
$anvil->System->generate_state_json({debug => 2}); #$anvil->System->generate_state_json({debug => 2});

Loading…
Cancel
Save