* Starting work in the new anvil-manage-alerts, which will (when done), allow for management of mail servers, alert recipients, notification over-rides and to trigger test alerts.
* Updated Database->get_recipients() to record recipients by name for better sorting. Signed-off-by: Digimer <digimer@alteeve.ca>main
parent
76f95d8e53
commit
a6cd5c6604
4 changed files with 790 additions and 0 deletions
@ -0,0 +1,142 @@ |
|||||||
|
.\" Manpage for the Anvil! server removal tool |
||||||
|
.\" Contact mkelly@alteeve.com to report issues, concerns or suggestions. |
||||||
|
.TH anvil-manage-alerts "8" "October 26 2022" "Anvil! Intelligent Availability™ Platform" |
||||||
|
.SH NAME |
||||||
|
anvil-manage-alerts \- This program manages alerts; Email servers, recipients, notification overrides, and generating test alerts. |
||||||
|
.SH SYNOPSIS |
||||||
|
.B anvil-manage-alerts |
||||||
|
\fI\,<command> \/\fR |
||||||
|
.SH DESCRIPTION |
||||||
|
The program allows you to add, edit and delete email servers, alert recipients, and notification overrides. You can also use it to generate a test alert. |
||||||
|
If run without any switches, the list of mail servers and recipients are returned. |
||||||
|
|
||||||
|
When called without any switches, the list of currect mail servers, alert recipients and notification overrides are shown, along with all known hosts. |
||||||
|
.TP |
||||||
|
.SH OPTIONS |
||||||
|
.TP |
||||||
|
\-?, \-h, \fB\-\-help\fR |
||||||
|
Show this man page. |
||||||
|
.TP |
||||||
|
\fB\-\-log-secure\fR |
||||||
|
When logging, record sensitive data, like passwords. |
||||||
|
.TP |
||||||
|
\-v, \-vv, \-vvv |
||||||
|
Set the log level to 1, 2 or 3 respectively. Be aware that level 3 generates a significant amount of log data. |
||||||
|
.SS "Commands:" |
||||||
|
.TP |
||||||
|
\fB\-\-add\fR |
||||||
|
This is used to add a new mail server or alert recipient. |
||||||
|
.TP |
||||||
|
\fB\-\-edit\fR |
||||||
|
This is used to edit and existing mail server or alert recipient. |
||||||
|
|
||||||
|
NOTE: All fields are required when editing an existing mail server or recipient! |
||||||
|
.TP |
||||||
|
\fB\-\-delete\fR |
||||||
|
This deletes an existing mail server or alert recipient. |
||||||
|
.TP |
||||||
|
\fB\-\-mail-servers\fR |
||||||
|
This is used to manage mail servers. Specifically, this control the mail server that we send alert emails to. The options used with this are; |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-uuid\fR <uuid> |
||||||
|
This is required for \fB\-\-edit\fR and \fB\-\-delete\fR. It is the existing mail server being worked on. |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-address\fR <URL or IP> |
||||||
|
This is the URL or IP address of the mail server we're logging into to send email. |
||||||
|
|
||||||
|
Example: mail.example.com |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-port\fR |
||||||
|
This is the TCP port used when connecting to the target mail server. |
||||||
|
|
||||||
|
Example: 587 |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-username\fR |
||||||
|
This is the mail server user name (usually an email address) used when authenticating against the mail server. |
||||||
|
|
||||||
|
Example: admin@example.com |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-password\fR |
||||||
|
This is the password used along with \fB\-\-mail-server-username\fR when authenticating against the mail server. Not all mail servers require a password, so this is optional. |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-security\fR <none, starttls or tls-ssl> |
||||||
|
This is the security type used when authenticating against the mail server. |
||||||
|
|
||||||
|
Valid values are: 'none', 'starttls' or 'tls-ssl'. |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-authentication\fR <none, plain-text, or encrypted> |
||||||
|
This is how passwords are passed to the mail server. |
||||||
|
|
||||||
|
Valid values are: 'none', 'plain-text', or 'encrypted' |
||||||
|
.TP |
||||||
|
\fB\-\-mail-server-helo-domain\fR |
||||||
|
This is the 'HELO' domain name used when communicating with the mail server. This is the domain we're telling the mail server that the email is coming from. You can use your domain, or the domain of the host. |
||||||
|
|
||||||
|
Example: example.com |
||||||
|
|
||||||
|
See: https://www.ibm.com/docs/en/zos/2.2.0?topic=sc-helo-command-identify-domain-name-sending-host-smtp |
||||||
|
.TP |
||||||
|
\fB\-\-notifications\fR |
||||||
|
This is where an alert recipient can have notification overrides. Typically this is used so that a given user can ignore alerts from a specific Anvil! node pair. |
||||||
|
.TP |
||||||
|
\fB\-\-notification-uuid\fR <uuid> |
||||||
|
This is required for \fB\-\-edit\fR and \fB\-\-delete\fR. It is the existing notification override being worked on. |
||||||
|
.TP |
||||||
|
\fB\-\-notification-recipient-uuid\fR <uuid> |
||||||
|
This is the recipients -> recipient_uuid who we are creating the override for. |
||||||
|
.TP |
||||||
|
\fB\-\-notification-host-uuid\fR |
||||||
|
This is the hosts -> host_uuid of the machine that you are creating the alert |
||||||
|
.TP |
||||||
|
\fB\-\-notification-alert-level\fR <1, 2, 3 or 4> |
||||||
|
This is the desired override alert level. |
||||||
|
|
||||||
|
Valid values are: |
||||||
|
|
||||||
|
1 = "critical" alerts only |
||||||
|
|
||||||
|
2 = "warning" and critical alerts |
||||||
|
|
||||||
|
3 = "notice", warning and critical alerts |
||||||
|
|
||||||
|
4 = "info"; All alerts. This generates almost constant alerts! |
||||||
|
.TP |
||||||
|
\fB\-\-recipients\fR |
||||||
|
This is used to manage alert recipients. Specifically, this control the mail server that we send alert emails to. The options used with this are; |
||||||
|
.TP |
||||||
|
\fB\-\-recipient-uuid\fR |
||||||
|
This is required for \fB\-\-edit\fR and \fB\-\-delete\fR. It is the existing alert recipient is being worked on. |
||||||
|
.TP |
||||||
|
\fB\-\-recipient-name\fR |
||||||
|
This is the name of the person receiving the alerts. This is used in the email header. |
||||||
|
|
||||||
|
Example: Austin Powers |
||||||
|
.TP |
||||||
|
\fB\-\-recipient-email\fR |
||||||
|
This is the email address for the alert recipient. |
||||||
|
|
||||||
|
Example: notaspy@example.com |
||||||
|
.TP |
||||||
|
\fB\-\-recipient-language\fR <en_CA> |
||||||
|
In the future, languages will be added and this can be used to indicate what language the user will receive their alerts in. At the time of writing this man page, only 'en_CA' is supported. |
||||||
|
.TP |
||||||
|
\fB\-\-recipient-level\fR <1, 2, 3 or 4> |
||||||
|
This is the default alert level this recipient is interested in. It can be adjusted on a per-host basis via the 'notifications' over-rides. |
||||||
|
|
||||||
|
Valid values are: |
||||||
|
|
||||||
|
1 = "critical" alerts only |
||||||
|
|
||||||
|
2 = "warning" and critical alerts |
||||||
|
|
||||||
|
3 = "notice", warning and critical alerts |
||||||
|
|
||||||
|
4 = "info"; All alerts. This generates almost constant alerts! |
||||||
|
.TP |
||||||
|
.SH AUTHOR |
||||||
|
Written by Madison Kelly, Alteeve staff and the Anvil! project contributors. |
||||||
|
.SH "REPORTING BUGS" |
||||||
|
Report bugs to users@clusterlabs.org |
||||||
|
|
||||||
|
|
||||||
|
", "download", "everywhere", "file", "is-script", "job-uuid", "rename", "to |
@ -0,0 +1,634 @@ |
|||||||
|
#!/usr/bin/perl |
||||||
|
|
||||||
|
use strict; |
||||||
|
use warnings; |
||||||
|
use Anvil::Tools; |
||||||
|
use Data::Dumper; |
||||||
|
use Text::Diff; |
||||||
|
|
||||||
|
$| = 1; |
||||||
|
|
||||||
|
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; |
||||||
|
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; |
||||||
|
if (($running_directory =~ /^\./) && ($ENV{PWD})) |
||||||
|
{ |
||||||
|
$running_directory =~ s/^\./$ENV{PWD}/; |
||||||
|
} |
||||||
|
|
||||||
|
my $anvil = Anvil::Tools->new(); |
||||||
|
|
||||||
|
# Get a list of all interfaces with IP addresses. |
||||||
|
$anvil->Get->switches({list => [ |
||||||
|
"add", |
||||||
|
"edit", |
||||||
|
"delete", |
||||||
|
"mail-servers", |
||||||
|
"mail-server-uuid", |
||||||
|
"mail-server-address", |
||||||
|
"mail-server-port", |
||||||
|
"mail-server-username", |
||||||
|
"mail-server-password", |
||||||
|
"mail-server-security", |
||||||
|
"mail-server-authentication", |
||||||
|
"mail-server-helo-domain", |
||||||
|
"notifications", |
||||||
|
"notification-uuid", |
||||||
|
"notification-recipient-uuid", |
||||||
|
"notification-host-uuid", |
||||||
|
"notification-alert-level", |
||||||
|
"recipients", |
||||||
|
"recipient-uuid", |
||||||
|
"recipient-name", |
||||||
|
"recipient-email", |
||||||
|
"recipient-language", |
||||||
|
"recipient-level", |
||||||
|
]}); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}}); |
||||||
|
|
||||||
|
$anvil->Database->connect(); |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" }); |
||||||
|
if (not $anvil->data->{sys}{database}{connections}) |
||||||
|
{ |
||||||
|
# No databases, exit. |
||||||
|
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003" }); |
||||||
|
$anvil->nice_exit({ exit_code => 1 }); |
||||||
|
} |
||||||
|
|
||||||
|
check_switches($anvil); |
||||||
|
|
||||||
|
if ($anvil->data->{switches}{"mail-servers"}) |
||||||
|
{ |
||||||
|
handle_mail_servers($anvil); |
||||||
|
} |
||||||
|
elsif ($anvil->data->{switches}{"notifications"}) |
||||||
|
{ |
||||||
|
handle_notifications($anvil); |
||||||
|
} |
||||||
|
elsif ($anvil->data->{switches}{"recipients"}) |
||||||
|
{ |
||||||
|
handle_recipients($anvil); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
show_existing($anvil); |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->nice_exit({exit_code => 0}); |
||||||
|
|
||||||
|
|
||||||
|
############################################################################################################# |
||||||
|
# Functions # |
||||||
|
############################################################################################################# |
||||||
|
|
||||||
|
sub recipients |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
sub handle_mail_servers |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
sub handle_notifications |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
# Show existing mail servers. |
||||||
|
sub show_existing |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
# Show mail servers |
||||||
|
$anvil->data->{longest}{mail_server_address} = 0; |
||||||
|
$anvil->data->{longest}{mail_server_port} = 0; |
||||||
|
$anvil->data->{longest}{mail_server_username} = 0; |
||||||
|
$anvil->data->{longest}{mail_server_password} = 0; |
||||||
|
$anvil->data->{longest}{mail_server_security} = 0; |
||||||
|
$anvil->data->{longest}{mail_server_authentication} = 0; |
||||||
|
$anvil->data->{longest}{mail_server_helo_domain} = 0; |
||||||
|
$anvil->data->{longest}{notification_recipient_name} = 0; |
||||||
|
$anvil->data->{longest}{notification_host_name} = 0; |
||||||
|
$anvil->data->{longest}{notification_anvil_name} = 0; |
||||||
|
$anvil->data->{longest}{notification_alert_level} = 0; |
||||||
|
$anvil->data->{longest}{recipient_name} = 0; |
||||||
|
$anvil->data->{longest}{recipient_email} = 0; |
||||||
|
$anvil->data->{longest}{recipient_language} = 0; |
||||||
|
$anvil->data->{longest}{recipient_alert_level} = 0; |
||||||
|
|
||||||
|
$anvil->data->{say_alert}{1} = "1 (".$anvil->Words->string({key => "unit_0024"}).")"; |
||||||
|
$anvil->data->{say_alert}{2} = "2 (".$anvil->Words->string({key => "unit_0025"}).")"; |
||||||
|
$anvil->data->{say_alert}{3} = "3 (".$anvil->Words->string({key => "unit_0026"}).")"; |
||||||
|
$anvil->data->{say_alert}{4} = "4 (".$anvil->Words->string({key => "unit_0027"}).")"; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'say_alert::1' => $anvil->data->{say_alert}{1}, |
||||||
|
'say_alert::2' => $anvil->data->{say_alert}{2}, |
||||||
|
'say_alert::3' => $anvil->data->{say_alert}{3}, |
||||||
|
'say_alert::4' => $anvil->data->{say_alert}{4}, |
||||||
|
}}); |
||||||
|
|
||||||
|
# Get longest counts. |
||||||
|
foreach my $mail_server_address (sort {$a cmp $b} keys %{$anvil->data->{mail_servers}{address_to_uuid}}) |
||||||
|
{ |
||||||
|
my $mail_server_uuid = $anvil->data->{mail_servers}{address_to_uuid}{$mail_server_address}; |
||||||
|
my $mail_server_port = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_port}; |
||||||
|
my $mail_server_username = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_username}; |
||||||
|
my $mail_server_password = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_password}; |
||||||
|
my $mail_server_security = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_security}; |
||||||
|
my $mail_server_authentication = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_authentication}; |
||||||
|
my $mail_server_helo_domain = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_helo_domain}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
's1:mail_server_address' => $mail_server_address, |
||||||
|
's2:mail_server_uuid' => $mail_server_uuid, |
||||||
|
's3:mail_server_port' => $mail_server_port, |
||||||
|
's4:mail_server_username' => $mail_server_username, |
||||||
|
's5:mail_server_password' => $anvil->Log->is_secure($mail_server_password), |
||||||
|
's6:mail_server_security' => $mail_server_security, |
||||||
|
's7:mail_server_authentication' => $mail_server_authentication, |
||||||
|
's8:mail_server_helo_domain' => $mail_server_helo_domain, |
||||||
|
}}); |
||||||
|
|
||||||
|
if (length($mail_server_address) > $anvil->data->{longest}{mail_server_address}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_address} = length($mail_server_address); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_address' => $anvil->data->{longest}{mail_server_address}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($mail_server_port) > $anvil->data->{longest}{mail_server_port}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_port} = length($mail_server_port); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_port' => $anvil->data->{longest}{mail_server_port}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($mail_server_username) > $anvil->data->{longest}{mail_server_username}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_username} = length($mail_server_username); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_username' => $anvil->data->{longest}{mail_server_username}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($mail_server_password) > $anvil->data->{longest}{mail_server_password}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_password} = length($mail_server_password); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_password' => $anvil->data->{longest}{mail_server_password}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($mail_server_security) > $anvil->data->{longest}{mail_server_security}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_security} = length($mail_server_security); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_security' => $anvil->data->{longest}{mail_server_security}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($mail_server_authentication) > $anvil->data->{longest}{mail_server_authentication}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_authentication} = length($mail_server_authentication); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_authentication' => $anvil->data->{longest}{mail_server_authentication}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($mail_server_helo_domain) > $anvil->data->{longest}{mail_server_helo_domain}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{mail_server_helo_domain} = length($mail_server_helo_domain); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::mail_server_helo_domain' => $anvil->data->{longest}{mail_server_helo_domain}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach my $notification_uuid (sort {$a cmp $b} keys %{$anvil->data->{notifications}{notification_uuid}}) |
||||||
|
{ |
||||||
|
my $notification_recipient_uuid = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_recipient_uuid}; |
||||||
|
my $notification_recipient_name = $anvil->data->{recipients}{recipient_uuid}{$notification_recipient_uuid}{recipient_name}; |
||||||
|
my $notification_recipient_email = $anvil->data->{recipients}{recipient_uuid}{$notification_recipient_uuid}{recipient_email}; |
||||||
|
my $say_recipient = $notification_recipient_name." <".$notification_recipient_email.">"; |
||||||
|
my $notification_host_uuid = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_host_uuid}; |
||||||
|
my $notification_short_host_name = $anvil->data->{hosts}{host_uuid}{$notification_host_uuid}{short_host_name}; |
||||||
|
my $say_anvil_name = $anvil->data->{hosts}{host_uuid}{$notification_host_uuid}{anvil_name} ? $anvil->data->{hosts}{host_uuid}{$notification_host_uuid}{anvil_name} : "--"; |
||||||
|
my $notification_alert_level = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_alert_level}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
's1:notification_uuid' => $notification_uuid, |
||||||
|
's2:notification_recipient_uuid' => $notification_recipient_uuid, |
||||||
|
's3:notification_recipient_name' => $notification_recipient_name, |
||||||
|
's4:notification_recipient_email' => $notification_recipient_email, |
||||||
|
's5:say_recipient' => $say_recipient, |
||||||
|
's6:notification_host_uuid' => $notification_host_uuid, |
||||||
|
's7:notification_short_host_name' => $notification_short_host_name, |
||||||
|
's8:say_anvil_name' => $say_anvil_name, |
||||||
|
's9:notification_alert_level' => $notification_alert_level, |
||||||
|
}}); |
||||||
|
|
||||||
|
if (length($say_recipient) > $anvil->data->{longest}{notification_recipient_name}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{notification_recipient_name} = length($say_recipient); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::notification_recipient_name' => $anvil->data->{longest}{notification_recipient_name}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($notification_short_host_name) > $anvil->data->{longest}{notification_host_name}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{notification_host_name} = length($notification_short_host_name); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::notification_host_name' => $anvil->data->{longest}{notification_host_name}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($say_anvil_name) > $anvil->data->{longest}{notification_anvil_name}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{notification_anvil_name} = length($say_anvil_name); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::notification_anvil_name' => $anvil->data->{longest}{notification_anvil_name}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($anvil->data->{say_alert}{$notification_alert_level}) > $anvil->data->{longest}{notification_alert_level}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{notification_alert_level} = length($anvil->data->{say_alert}{$notification_alert_level}); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::notification_alert_level' => $anvil->data->{longest}{notification_alert_level}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
# This will let us display over-rides by user in order. |
||||||
|
$anvil->data->{notifications}{name_to_uuid}{$notification_recipient_name} = $notification_uuid; |
||||||
|
$anvil->data->{notifications}{notification_uuid}{$notification_uuid}{recipient_name} = $say_recipient; |
||||||
|
$anvil->data->{notifications}{notification_uuid}{$notification_uuid}{anvil_name} = $say_anvil_name; |
||||||
|
$anvil->data->{notifications}{notification_uuid}{$notification_uuid}{host_uuid} = $notification_host_uuid; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
"s1:notifications::name_to_uuid::${notification_recipient_name}" => $anvil->data->{notifications}{name_to_uuid}{$notification_recipient_name}, |
||||||
|
"s2:notifications::notification_uuid::${notification_uuid}::recipient_name" => $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{recipient_name}, |
||||||
|
"s3:notifications::notification_uuid::${notification_uuid}::anvil_name" => $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{anvil_name}, |
||||||
|
"s4:notifications::notification_uuid::${notification_uuid}::host_uuid" => $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{host_uuid}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
foreach my $recipient_name (sort {$a cmp $b} keys %{$anvil->data->{recipients}{name_to_uuid}}) |
||||||
|
{ |
||||||
|
my $recipient_uuid = $anvil->data->{recipients}{name_to_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 $say_language = $anvil->data->{sys}{languages}{$recipient_language}; |
||||||
|
my $recipient_level = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
"s1:recipient_name" => $recipient_name, |
||||||
|
"s2:recipient_uuid" => $recipient_uuid, |
||||||
|
"s3:recipient_email" => $recipient_email, |
||||||
|
"s4:recipient_language" => $recipient_language, |
||||||
|
"s5:say_language" => $say_language, |
||||||
|
"s6:recipient_level" => $recipient_level, |
||||||
|
}}); |
||||||
|
|
||||||
|
if (length($recipient_name) > $anvil->data->{longest}{recipient_name}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{recipient_name} = length($recipient_name); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::recipient_name' => $anvil->data->{longest}{recipient_name}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($recipient_email) > $anvil->data->{longest}{recipient_email}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{recipient_email} = length($recipient_email); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::recipient_email' => $anvil->data->{longest}{recipient_email}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($say_language) > $anvil->data->{longest}{recipient_language}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{recipient_language} = length($say_language); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::recipient_language' => $anvil->data->{longest}{recipient_language}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
if (length($anvil->data->{say_alert}{$recipient_level}) > $anvil->data->{longest}{recipient_alert_level}) |
||||||
|
{ |
||||||
|
$anvil->data->{longest}{recipient_alert_level} = length($anvil->data->{say_alert}{$recipient_level}); |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
'longest::recipient_alert_level' => $anvil->data->{longest}{recipient_alert_level}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Now show the data. |
||||||
|
# my $break_line = "+-".sprintf("%0${longest_anvil_name}d", 0); |
||||||
|
# my $header_line = "| ".sprintf("%-${longest_anvil_name}s", $anvil_header)." "; |
||||||
|
# my $blank_lead = "| ".sprintf("%-${longest_anvil_name}s", $anvil_header)." "; |
||||||
|
print "-=] Mail Servers;\n"; |
||||||
|
print "Address, Port, Login User, Password, Security, Authentication, HELO Domaon, Mail Server UUID\n"; |
||||||
|
my $mail_servers = 0; |
||||||
|
foreach my $mail_server_address (sort {$a cmp $b} keys %{$anvil->data->{mail_servers}{address_to_uuid}}) |
||||||
|
{ |
||||||
|
my $mail_server_uuid = $anvil->data->{mail_servers}{address_to_uuid}{$mail_server_address}; |
||||||
|
my $mail_server_port = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_port}; |
||||||
|
my $mail_server_username = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_username}; |
||||||
|
my $mail_server_password = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_password}; |
||||||
|
my $mail_server_security = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_security}; |
||||||
|
my $mail_server_authentication = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_authentication}; |
||||||
|
my $mail_server_helo_domain = $anvil->data->{mail_servers}{mail_server}{$mail_server_uuid}{mail_server_helo_domain}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
's1:mail_server_address' => $mail_server_address, |
||||||
|
's2:mail_server_uuid' => $mail_server_uuid, |
||||||
|
's3:mail_server_port' => $mail_server_port, |
||||||
|
's4:mail_server_username' => $mail_server_username, |
||||||
|
's5:mail_server_password' => $anvil->Log->is_secure($mail_server_password), |
||||||
|
's6:mail_server_security' => $mail_server_security, |
||||||
|
's7:mail_server_authentication' => $mail_server_authentication, |
||||||
|
's8:mail_server_helo_domain' => $mail_server_helo_domain, |
||||||
|
}}); |
||||||
|
|
||||||
|
print $mail_server_address.", ".$mail_server_port.", ".$mail_server_username.", ".$mail_server_password.", ".$mail_server_security.", ".$mail_server_authentication.", ".$mail_server_helo_domain.", ".$mail_server_uuid."\n"; |
||||||
|
$mail_servers++; |
||||||
|
} |
||||||
|
if (not $mail_servers) |
||||||
|
{ |
||||||
|
print "No mail servers configured yet!\n"; |
||||||
|
} |
||||||
|
print "\n"; |
||||||
|
print "-=] Recipients;\n"; |
||||||
|
print "Name, Email, Alert Level, Language, Recipient UUID\n"; |
||||||
|
my $recipients = 0; |
||||||
|
foreach my $recipient_name (sort {$a cmp $b} keys %{$anvil->data->{recipients}{name_to_uuid}}) |
||||||
|
{ |
||||||
|
my $recipient_uuid = $anvil->data->{recipients}{name_to_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 $say_language = $anvil->data->{sys}{languages}{$recipient_language}; |
||||||
|
my $recipient_level = $anvil->data->{recipients}{recipient_uuid}{$recipient_uuid}{recipient_level}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
"s1:recipient_name" => $recipient_name, |
||||||
|
"s2:recipient_uuid" => $recipient_uuid, |
||||||
|
"s3:recipient_email" => $recipient_email, |
||||||
|
"s4:recipient_language" => $recipient_language, |
||||||
|
"s5:say_language" => $say_language, |
||||||
|
"s6:recipient_level" => $recipient_level, |
||||||
|
}}); |
||||||
|
|
||||||
|
print $recipient_name.", ".$recipient_email.", ".$anvil->data->{say_alert}{$recipient_level}.", ".$say_language.", ".$recipient_uuid."\n"; |
||||||
|
$recipients++; |
||||||
|
} |
||||||
|
if (not $recipients) |
||||||
|
{ |
||||||
|
print "No alert recipients added yet!\n"; |
||||||
|
} |
||||||
|
|
||||||
|
print "\n"; |
||||||
|
print "-=] Notification Over-rides;\n"; |
||||||
|
print "Recipient, Host, Anvil!, Alert Level, Notification UUID\n"; |
||||||
|
my $notifications = 0; |
||||||
|
foreach my $recipient_name (sort {$a cmp $b} keys %{$anvil->data->{notifications}{name_to_uuid}}) |
||||||
|
{ |
||||||
|
my $notification_uuid = $anvil->data->{notifications}{name_to_uuid}{$recipient_name}; |
||||||
|
my $say_recipient = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{recipient_name}; |
||||||
|
my $say_anvil_name = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{anvil_name}; |
||||||
|
my $host_uuid = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_host_uuid}; |
||||||
|
my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name}; |
||||||
|
my $alert_level = $anvil->data->{notifications}{notification_uuid}{$notification_uuid}{notification_alert_level}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
's1:notification_uuid' => $notification_uuid, |
||||||
|
's2:say_recipient' => $say_recipient, |
||||||
|
's3:say_anvil_name' => $say_anvil_name, |
||||||
|
's4:alert_level' => $alert_level, |
||||||
|
}}); |
||||||
|
|
||||||
|
print $say_recipient.", ".$short_host_name.", ".$say_anvil_name.", ".$anvil->data->{say_alert}{$alert_level}.", ".$notification_uuid."\n"; |
||||||
|
$notifications++; |
||||||
|
} |
||||||
|
if (not $notifications) |
||||||
|
{ |
||||||
|
print "No notification over-rides found.\n"; |
||||||
|
} |
||||||
|
|
||||||
|
# Lastly, show machines. |
||||||
|
print "\n"; |
||||||
|
print "-=] Striker Dashboards;\n"; |
||||||
|
foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) |
||||||
|
{ |
||||||
|
my $host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name}; |
||||||
|
my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; |
||||||
|
next if $host_type ne "striker"; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
's1:host_name' => $host_name, |
||||||
|
's2:host_uuid' => $host_uuid, |
||||||
|
}}); |
||||||
|
|
||||||
|
print "- ".$host_name.", UUID: [".$host_uuid."]\n"; |
||||||
|
} |
||||||
|
print "\n"; |
||||||
|
print "-=] Anvil! Nodes;\n"; |
||||||
|
foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}}) |
||||||
|
{ |
||||||
|
my $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid}; |
||||||
|
my $anvil_description = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description}; |
||||||
|
my $anvil_node1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid}; |
||||||
|
my $anvil_node2_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid}; |
||||||
|
my $anvil_dr1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid}; |
||||||
|
my $say_dr1_name = $anvil_dr1_host_uuid ? $anvil->data->{hosts}{host_uuid}{$anvil_dr1_host_uuid}{short_host_name} : "--"; |
||||||
|
my $say_dr1_uuid = $anvil_dr1_host_uuid ? $anvil_dr1_host_uuid : "--"; |
||||||
|
print "- Name: [".$anvil_name."], UUID: [".$anvil_uuid."], Description: [".$anvil_uuid."]\n"; |
||||||
|
print " - Node 1: .. [".$anvil->data->{hosts}{host_uuid}{$anvil_node1_host_uuid}{short_host_name}."], UUID: [".$anvil_node1_host_uuid."]\n"; |
||||||
|
print " - Node 2: .. [".$anvil->data->{hosts}{host_uuid}{$anvil_node2_host_uuid}{short_host_name}."], UUID: [".$anvil_node2_host_uuid."]\n"; |
||||||
|
print " - DR Host 1: [".$say_dr1_name."], UUID: [".$say_dr1_uuid."]\n"; |
||||||
|
} |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
# This sanity checks input switches. By necessity, this also loads data from the database. |
||||||
|
sub check_switches |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
# Load data |
||||||
|
$anvil->Database->get_hosts(); |
||||||
|
$anvil->Database->get_anvils(); |
||||||
|
$anvil->Database->get_mail_servers(); |
||||||
|
$anvil->Database->get_recipients(); |
||||||
|
$anvil->Database->get_notifications(); |
||||||
|
$anvil->Words->language_list(); |
||||||
|
|
||||||
|
### Now sanity check |
||||||
|
my $problem = 0; |
||||||
|
|
||||||
|
# Validate UUIDs. |
||||||
|
foreach my $switch ("mail-server-uuid", "notification-uuid", "recipient-uuid", "notification-recipient-uuid", "notification-host-uuid") |
||||||
|
{ |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { switch => $switch }}); |
||||||
|
if (($anvil->data->{switches}{$switch}) && (not $anvil->Validate->uuid({uuid => $anvil->data->{switches}{$switch}}))) |
||||||
|
{ |
||||||
|
# Invalid UUID. |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0380", variables => { |
||||||
|
uuid => $anvil->data->{switches}{$switch}, |
||||||
|
switch => "--".$switch, |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Validate domain or IP data. |
||||||
|
foreach my $switch ("mail-server-address", "mail-server-helo-domain") |
||||||
|
{ |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { switch => $switch }}); |
||||||
|
if ($anvil->data->{switches}{$switch}) |
||||||
|
{ |
||||||
|
# Make sure it's a domain or IP address. |
||||||
|
if ((not $anvil->Validate->domain_name({name => $anvil->data->{switches}{$switch}})) && (not $anvil->Validate->ip({ip => $anvil->data->{switches}{$switch}}))) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0381", variables => { |
||||||
|
name => $anvil->data->{switches}{$switch}, |
||||||
|
switch => "--".$switch, |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Check log levels. |
||||||
|
foreach my $switch ("notification-alert-level", "recipient-level") |
||||||
|
{ |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { switch => $switch }}); |
||||||
|
if ($anvil->data->{switches}{$switch}) |
||||||
|
{ |
||||||
|
# If they used a level name, convert it to a number. |
||||||
|
# 1 = "critical" alerts only |
||||||
|
# 2 = "warning" and critical alerts |
||||||
|
# 3 = "notice", warning and critical alerts |
||||||
|
# 4 = "info"; All alerts. This generates almost constant alerts! |
||||||
|
if ($anvil->data->{switches}{$switch} eq "critical") |
||||||
|
{ |
||||||
|
$anvil->data->{switches}{$switch} = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::".$switch => $anvil->data->{switches}{$switch} }}); |
||||||
|
} |
||||||
|
elsif ($anvil->data->{switches}{$switch} eq "warning") |
||||||
|
{ |
||||||
|
$anvil->data->{switches}{$switch} = 2; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::".$switch => $anvil->data->{switches}{$switch} }}); |
||||||
|
} |
||||||
|
elsif ($anvil->data->{switches}{$switch} eq "notice") |
||||||
|
{ |
||||||
|
$anvil->data->{switches}{$switch} = 3; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::".$switch => $anvil->data->{switches}{$switch} }}); |
||||||
|
} |
||||||
|
elsif ($anvil->data->{switches}{$switch} eq "info") |
||||||
|
{ |
||||||
|
$anvil->data->{switches}{$switch} = 4; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::".$switch => $anvil->data->{switches}{$switch} }}); |
||||||
|
} |
||||||
|
|
||||||
|
if (($anvil->data->{switches}{$switch} =~ /\D/) or |
||||||
|
($anvil->data->{switches}{$switch} < 1) or |
||||||
|
($anvil->data->{switches}{$switch} > 4)) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0382", variables => { |
||||||
|
level => $anvil->data->{switches}{$switch}, |
||||||
|
switch => "--".$switch, |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Ports (there's only one) |
||||||
|
if ($anvil->data->{switches}{"mail-server-port"}) |
||||||
|
{ |
||||||
|
# Make sure it's a valid port. |
||||||
|
if (($anvil->data->{switches}{"mail-server-port"} =~ /\D/) or |
||||||
|
($anvil->data->{switches}{"mail-server-port"} < 1) or |
||||||
|
($anvil->data->{switches}{"mail-server-port"} > 65535)) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0383", variables => { |
||||||
|
port => $anvil->data->{switches}{"mail-server-port"}, |
||||||
|
switch => "--mail-server-port", |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Make sure mail server security is correct. |
||||||
|
if ($anvil->data->{switches}{"mail-server-security"}) |
||||||
|
{ |
||||||
|
$anvil->data->{switches}{"mail-server-security"} = lc($anvil->data->{switches}{"mail-server-security"}); |
||||||
|
if (($anvil->data->{switches}{"mail-server-security"} ne "none") && |
||||||
|
($anvil->data->{switches}{"mail-server-security"} ne "starttls") && |
||||||
|
($anvil->data->{switches}{"mail-server-security"} ne "tls-ssl")) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0384", variables => { |
||||||
|
security => $anvil->data->{switches}{"mail-server-security"}, |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ($anvil->data->{switches}{"mail-server-authentication"}) |
||||||
|
{ |
||||||
|
$anvil->data->{switches}{"mail-server-authentication"} = lc($anvil->data->{switches}{"mail-server-authentication"}); |
||||||
|
if (($anvil->data->{switches}{"mail-server-authentication"} ne "none") && |
||||||
|
($anvil->data->{switches}{"mail-server-authentication"} ne "plain-text") && |
||||||
|
($anvil->data->{switches}{"mail-server-authentication"} ne "encrypted")) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0385", variables => { |
||||||
|
auth => $anvil->data->{switches}{"mail-server-authentication"}, |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
if ($anvil->data->{switches}{"recipient-email"}) |
||||||
|
{ |
||||||
|
if (not $anvil->Validate->email({email => $anvil->data->{switches}{"recipient-email"}})) |
||||||
|
{ |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0386", variables => { |
||||||
|
email => $anvil->data->{switches}{"recipient-email"}, |
||||||
|
}}); |
||||||
|
|
||||||
|
$problem = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Later, we'll need to parse the language file, for now, we always force 'en_CA'. |
||||||
|
$anvil->data->{switches}{"recipient-language"} = "en_CA"; |
||||||
|
|
||||||
|
# If there's a problem, exit now. |
||||||
|
if ($problem) |
||||||
|
{ |
||||||
|
$anvil->nice_exit({exit_code => 1}); |
||||||
|
} |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
Loading…
Reference in new issue