commit
137e8f1e50
37 changed files with 3307 additions and 1391 deletions
@ -0,0 +1,411 @@ |
||||
#!/usr/bin/perl |
||||
# |
||||
# This does checks for changes that are needed because of version changes. Over time, checks here can be |
||||
# removed. |
||||
|
||||
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 => [ |
||||
]}); |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}}); |
||||
|
||||
$anvil->Database->connect({sensitive => 1}); |
||||
$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 }); |
||||
} |
||||
|
||||
my $host_type = $anvil->Get->host_type(); |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }}); |
||||
|
||||
if ($host_type eq "striker") |
||||
{ |
||||
striker_checks($anvil); |
||||
} |
||||
elsif ($host_type eq "node") |
||||
{ |
||||
node_checks($anvil); |
||||
} |
||||
elsif ($host_type eq "dr") |
||||
{ |
||||
dr_checks($anvil); |
||||
} |
||||
|
||||
|
||||
$anvil->nice_exit({exit_code => 0}); |
||||
|
||||
|
||||
############################################################################################################# |
||||
# Functions # |
||||
############################################################################################################# |
||||
|
||||
# Check for things that need to happen on Striker dashboards. |
||||
sub striker_checks |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
# This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override' |
||||
update_notifications($anvil); |
||||
|
||||
### NOTE: Disabled until review complete |
||||
# This checks to make sure that the 'audits' table exists (added late into M3.0 pre-release) |
||||
#update_audits($anvil); |
||||
|
||||
### NOTE: Disabled until review complete |
||||
# This checks to make sure that the new dr_links table exists, and that existing anvil_dr1_host_uuid |
||||
# entries are copied. |
||||
update_dr_links($anvil); |
||||
|
||||
### TODO: Remove these later. This is here to clean up how we used to handle db_in_use and lock_request flags. |
||||
if (1) |
||||
{ |
||||
# Broadly clear all states that are '0' now. |
||||
my $queries = []; |
||||
push @{$queries}, "DELETE FROM states WHERE state_name LIKE 'db_in_use::%' AND state_note != '1';"; |
||||
push @{$queries}, "DELETE FROM history.variables WHERE variable_name = 'lock_request';"; |
||||
push @{$queries}, "DELETE FROM variables WHERE variable_name = 'lock_request';"; |
||||
foreach my $query (@{$queries}) |
||||
{ |
||||
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }}); |
||||
} |
||||
$anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# Check for things that need to happen on Anvil! Subnodes. |
||||
sub node_checks |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 |
||||
handle_bz1961562($anvil); |
||||
|
||||
# Make sure DRBD compiled after a kernel upgrade. |
||||
$anvil->DRBD->_initialize_kmod({debug => 2}); |
||||
|
||||
# Make sure logind is update to handle fencing properly |
||||
# see - https://access.redhat.com/solutions/1578823 |
||||
$anvil->Cluster->configure_logind({debug => 2}); |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# Check for things that need to happen on DR hosts. |
||||
sub dr_checks |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 |
||||
handle_bz1961562($anvil); |
||||
|
||||
# Make sure DRBD compiled after a kernel upgrade. |
||||
$anvil->DRBD->_initialize_kmod({debug => 2}); |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# |
||||
sub update_dr_links |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) |
||||
{ |
||||
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'dr_links';"; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
||||
|
||||
my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0]; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
||||
|
||||
if (not $count) |
||||
{ |
||||
# Add the table. |
||||
my $query = q| |
||||
CREATE TABLE dr_links ( |
||||
dr_link_uuid uuid not null primary key, |
||||
dr_link_host_uuid uuid not null, |
||||
dr_link_anvil_uuid uuid not null, |
||||
dr_link_note text, -- Set to 'DELETE' when no longer used. |
||||
modified_date timestamp with time zone not null, |
||||
|
||||
FOREIGN KEY(dr_link_host_uuid) REFERENCES hosts(host_uuid), |
||||
FOREIGN KEY(dr_link_anvil_uuid) REFERENCES anvils(anvil_uuid) |
||||
); |
||||
ALTER TABLE dr_links OWNER TO admin; |
||||
|
||||
CREATE TABLE history.dr_links ( |
||||
history_id bigserial, |
||||
dr_link_uuid uuid, |
||||
dr_link_host_uuid uuid, |
||||
dr_link_anvil_uuid uuid, |
||||
dr_link_note text, |
||||
modified_date timestamp with time zone not null |
||||
); |
||||
ALTER TABLE history.dr_links OWNER TO admin; |
||||
|
||||
CREATE FUNCTION history_dr_links() RETURNS trigger |
||||
AS $$ |
||||
DECLARE |
||||
history_dr_links RECORD; |
||||
BEGIN |
||||
SELECT INTO history_dr_links * FROM dr_links WHERE dr_link_uuid = new.dr_link_uuid; |
||||
INSERT INTO history.dr_links |
||||
(dr_link_uuid, |
||||
dr_link_host_uuid, |
||||
dr_link_anvil_uuid, |
||||
dr_link_note, |
||||
modified_date) |
||||
VALUES |
||||
(history_dr_links.dr_link_uuid, |
||||
history_dr_links.dr_link_host_uuid, |
||||
history_dr_links.dr_link_anvil_uuid, |
||||
history_dr_links.dr_link_note, |
||||
history_dr_links.modified_date); |
||||
RETURN NULL; |
||||
END; |
||||
$$ |
||||
LANGUAGE plpgsql; |
||||
ALTER FUNCTION history_dr_links() OWNER TO admin; |
||||
|
||||
CREATE TRIGGER trigger_dr_links |
||||
AFTER INSERT OR UPDATE ON dr_links |
||||
FOR EACH ROW EXECUTE PROCEDURE history_dr_links(); |
||||
|; |
||||
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }}); |
||||
$anvil->Database->write({debug => 2, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
||||
} |
||||
} |
||||
|
||||
# Now make sure that existing DR entries are copied here. |
||||
$anvil->Database->get_hosts({deubg => 2}); |
||||
$anvil->Database->get_dr_links({debug => 2}); |
||||
|
||||
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_dr1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid}; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||
"s1:anvil_name" => $anvil_name, |
||||
"s2:anvil_uuid" => $anvil_uuid, |
||||
"s3:anvil_dr1_host_uuid" => $anvil_dr1_host_uuid, |
||||
}}); |
||||
if ($anvil_dr1_host_uuid) |
||||
{ |
||||
my $dr1_host_name = $anvil->data->{hosts}{host_uuid}{$anvil_dr1_host_uuid}{short_host_name}; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dr1_host_name => $dr1_host_name }}); |
||||
|
||||
if ((not exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}) or |
||||
(not exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}{$anvil_dr1_host_uuid}) or |
||||
(not exists $anvil->data->{dr_links}{by_anvil_uuid}{$anvil_uuid}{dr_link_host_uuid}{$anvil_dr1_host_uuid}{dr_link_uuid})) |
||||
{ |
||||
# Add it. |
||||
my $dr_link_uuid = $anvil->Database->insert_or_update_dr_links({ |
||||
debug => 2, |
||||
dr_link_anvil_uuid => $anvil_uuid, |
||||
dr_link_host_uuid => $anvil_dr1_host_uuid, |
||||
dr_link_note => "auto_generated", |
||||
}); |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dr1_host_name => $dr1_host_name }}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# This checks to make sure that the 'audits' table exists (added late into M3.0 pre-release) |
||||
sub update_audits |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) |
||||
{ |
||||
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'audits';"; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
||||
|
||||
my $count = $anvil->Database->query({query => $query, uuid => $uuid, source => $THIS_FILE, line => __LINE__})->[0]->[0]; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
||||
|
||||
if (not $count) |
||||
{ |
||||
# Add the table. |
||||
my $query = q| |
||||
CREATE TABLE audits ( |
||||
audit_uuid uuid primary key, |
||||
audit_user_uuid uuid not null, -- This is the users -> user_uuid the audit is tracking |
||||
audit_details text not null, -- This is the information explaining the action being audited. |
||||
modified_date timestamp with time zone not null, |
||||
|
||||
FOREIGN KEY(audit_user_uuid) REFERENCES users(user_uuid) |
||||
); |
||||
ALTER TABLE audits OWNER TO admin; |
||||
|
||||
CREATE TABLE history.audits ( |
||||
history_id bigserial, |
||||
audit_uuid uuid, |
||||
audit_user_uuid uuid, |
||||
audit_details text, |
||||
modified_date timestamp with time zone not null |
||||
); |
||||
ALTER TABLE history.audits OWNER TO admin; |
||||
|
||||
CREATE FUNCTION history_audits() RETURNS trigger |
||||
AS $$ |
||||
DECLARE |
||||
history_audits RECORD; |
||||
BEGIN |
||||
SELECT INTO history_audits * FROM audits WHERE audit_uuid = new.audit_uuid; |
||||
INSERT INTO history.audits |
||||
(audit_uuid, |
||||
audit_user_uuid, |
||||
audit_details, |
||||
modified_date) |
||||
VALUES |
||||
(history_audit.audit_uuid, |
||||
history_audit.audit_user_uuid, |
||||
history_audit.audit_details, |
||||
history_audit.modified_date); |
||||
RETURN NULL; |
||||
END; |
||||
$$ |
||||
LANGUAGE plpgsql; |
||||
ALTER FUNCTION history_audits() OWNER TO admin; |
||||
|
||||
CREATE TRIGGER trigger_audits |
||||
AFTER INSERT OR UPDATE ON audits |
||||
FOR EACH ROW EXECUTE PROCEDURE history_audits(); |
||||
|; |
||||
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }}); |
||||
$anvil->Database->write({debug => 2, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); |
||||
} |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# This converts the old/broken 'notifications' tables with the more appropriately named 'alert-override' |
||||
sub update_notifications |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) |
||||
{ |
||||
my $query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_catalog = 'anvil' AND table_name = 'notifications';"; |
||||
$anvil->Log->variables({source => $THIS_FILE, uuid => $uuid, line => __LINE__, level => 2, list => { query => $query }}); |
||||
|
||||
my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); |
||||
|
||||
if ($count) |
||||
{ |
||||
my $queries = []; |
||||
push @{$queries}, "DROP FUNCTION history_notifications() CASCADE;"; |
||||
push @{$queries}, "DROP TABLE history.notifications;"; |
||||
push @{$queries}, "DROP TABLE public.notifications;"; |
||||
push @{$queries}, q|CREATE TABLE alert_overrides ( |
||||
alert_override_uuid uuid not null primary key, |
||||
alert_override_recipient_uuid uuid not null, -- The recipient we're linking. |
||||
alert_override_host_uuid uuid not null, -- This host_uuid of the referenced machine |
||||
alert_override_alert_level integer not null, -- This is the alert level (at or above) that this user wants alerts from. If set to '-1', the record is deleted. |
||||
modified_date timestamp with time zone not null, |
||||
|
||||
FOREIGN KEY(alert_override_host_uuid) REFERENCES hosts(host_uuid), |
||||
FOREIGN KEY(alert_override_recipient_uuid) REFERENCES recipients(recipient_uuid) |
||||
); |
||||
ALTER TABLE alert_overrides OWNER TO admin; |
||||
|
||||
CREATE TABLE history.alert_overrides ( |
||||
history_id bigserial, |
||||
alert_override_uuid uuid, |
||||
alert_override_recipient_uuid uuid, |
||||
alert_override_host_uuid uuid, |
||||
alert_override_alert_level integer, |
||||
modified_date timestamp with time zone not null |
||||
); |
||||
ALTER TABLE history.alert_overrides OWNER TO admin; |
||||
|
||||
CREATE FUNCTION history_alert_overrides() RETURNS trigger |
||||
AS $$ |
||||
DECLARE |
||||
history_alert_overrides RECORD; |
||||
BEGIN |
||||
SELECT INTO history_alert_overrides * FROM alert_overrides WHERE alert_override_uuid = new.alert_override_uuid; |
||||
INSERT INTO history.alert_overrides |
||||
(alert_override_uuid, |
||||
alert_override_recipient_uuid, |
||||
alert_override_host_uuid, |
||||
alert_override_alert_level, |
||||
modified_date) |
||||
VALUES |
||||
(history_alert_overrides.alert_override_uuid, |
||||
history_alert_overrides.alert_override_recipient_uuid, |
||||
history_alert_overrides.alert_override_host_uuid, |
||||
history_alert_overrides.alert_override_alert_level, |
||||
history_alert_overrides.modified_date); |
||||
RETURN NULL; |
||||
END; |
||||
$$ |
||||
LANGUAGE plpgsql; |
||||
ALTER FUNCTION history_alert_overrides() OWNER TO admin; |
||||
|
||||
CREATE TRIGGER trigger_alert_overrides |
||||
AFTER INSERT OR UPDATE ON alert_overrides |
||||
FOR EACH ROW EXECUTE PROCEDURE history_alert_overrides(); |
||||
|; |
||||
foreach my $query (@{$queries}) |
||||
{ |
||||
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0124", variables => { query => $query }}); |
||||
} |
||||
$anvil->Database->write({debug => 2, uuid => $uuid, query => $queries, source => $THIS_FILE, line => __LINE__}); |
||||
} |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
sub handle_bz1961562 |
||||
{ |
||||
my ($anvil) = @_; |
||||
|
||||
### TODO: Test that this is fixed. The bug is now ERRATA |
||||
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 |
||||
# We're a node or DR host. We need to touch this file. |
||||
my $work_around_file = "/etc/qemu/firmware/50-edk2-ovmf-cc.json"; |
||||
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { work_around_file => $work_around_file }}); |
||||
if (not -e $work_around_file) |
||||
{ |
||||
$anvil->Storage->write_file({ |
||||
debug => 2, |
||||
file => $work_around_file, |
||||
body => "", |
||||
overwrite => 0, |
||||
backup => 0, |
||||
mode => "0644", |
||||
user => "root", |
||||
group => "root", |
||||
}); |
||||
} |
||||
|
||||
return(0); |
||||
} |
@ -0,0 +1,652 @@ |
||||
#!/usr/bin/perl |
||||
# |
||||
# Author: Madison Kelly (mkelly@alteeve.ca) |
||||
# Alteeve's Niche! Inc. - https://alteeve.com/w/ |
||||
# Version: 0.0.1 |
||||
# License: GPL v2+ |
||||
# |
||||
# This program ties LINBIT's DRBD fencing into pacemaker's stonith. It provides a power-fence alternative to |
||||
# the default 'crm-{un,}fence-peer.sh' {un,}fence-handler. |
||||
# |
||||
# WARNING: This unfence handler is probably not safe to use outside of an Anvil! IA platform. It makes a lot |
||||
# of operational assumptions about the system and desired goals. |
||||
# |
||||
# TODO: |
||||
# - |
||||
|
||||
### NOTE: This doesn't use Anvil::Tools on purpose. We want to be quick and depend on as few things as |
||||
### possible. |
||||
|
||||
use strict; |
||||
use warnings; |
||||
use XML::Simple; |
||||
use Data::Dumper; |
||||
|
||||
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. |
||||
$| = 1; |
||||
|
||||
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; |
||||
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; |
||||
if (($running_directory =~ /^\./) && ($ENV{PWD})) |
||||
{ |
||||
$running_directory =~ s/^\./$ENV{PWD}/; |
||||
} |
||||
|
||||
my $conf = { |
||||
'log' => { |
||||
facility => "local0", |
||||
level => 2, |
||||
line_numbers => 1, |
||||
tag => $THIS_FILE, |
||||
}, |
||||
# If a program isn't at the defined path, $ENV{PATH} will be searched. |
||||
path => { |
||||
exe => { |
||||
cibadmin => "/usr/sbin/cibadmin", |
||||
crm_attribute => "/usr/sbin/crm_attribute", |
||||
crm_error => "/usr/sbin/crm_error", |
||||
drbdadm => "/usr/sbin/drbdadm", |
||||
echo => "/usr/bin/echo", |
||||
getent => "/usr/bin/getent", |
||||
logger => "/usr/bin/logger", |
||||
pcs => "/usr/sbin/pcs", |
||||
stonith_admin => "/usr/sbin/stonith_admin", |
||||
}, |
||||
}, |
||||
# The script will set this. |
||||
cluster => { |
||||
target_node => "", |
||||
}, |
||||
# These are the environment variables set by DRBD. See 'man drbd.conf' |
||||
# -> 'handlers'. |
||||
environment => { |
||||
# The resource triggering the fence. |
||||
'DRBD_RESOURCE' => defined $ENV{DRBD_RESOURCE} ? $ENV{DRBD_RESOURCE} : "", |
||||
# The resource minor number, or, in the case of volumes, numbers. |
||||
'DRBD_MINOR' => defined $ENV{DRBD_MINOR} ? $ENV{DRBD_MINOR} : "", |
||||
# This is the address format (ipv4, ipv6, etc) |
||||
'DRBD_PEER_AF' => defined $ENV{DRBD_PEER_AF} ? $ENV{DRBD_PEER_AF} : "", |
||||
# This is the IP address of the target node. |
||||
'DRBD_PEER_ADDRESS' => defined $ENV{DRBD_PEER_ADDRESS} ? $ENV{DRBD_PEER_ADDRESS} : "", |
||||
# This isn't set |
||||
'DRBD_PEERS' => defined $ENV{DRBD_PEERS} ? $ENV{DRBD_PEERS} : "", |
||||
### NOTE: Below here are undocumented variables. Don't expect them to always be useful. |
||||
# My node ID |
||||
'DRBD_MY_NODE_ID' => defined $ENV{DRBD_MY_NODE_ID} ? $ENV{DRBD_MY_NODE_ID} : "", |
||||
# The target's ID |
||||
'DRBD_PEER_NODE_ID' => defined $ENV{DRBD_PEER_NODE_ID} ? $ENV{DRBD_PEER_NODE_ID} : "", |
||||
}, |
||||
}; |
||||
|
||||
# Find executables. |
||||
find_executables($conf); |
||||
|
||||
# Something for the logs |
||||
to_log($conf, {message => "The Anvil! DRBD unfence handler has been invoked.", 'line' => __LINE__}); |
||||
|
||||
# These are the full host names of the nodes given their IDs. |
||||
foreach my $i (0..31) |
||||
{ |
||||
my $key = "DRBD_NODE_ID_".$i; |
||||
if ((exists $ENV{$key}) && (defined $ENV{$key})) |
||||
{ |
||||
$conf->{environment}{$key} = $ENV{$key}; |
||||
my $level = $conf->{environment}{$key} eq "" ? 3 : 2; |
||||
to_log($conf, {message => "DRBD Environment variable: [$key] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level}); |
||||
} |
||||
} |
||||
|
||||
# Record the environment variables |
||||
foreach my $key (sort {$a cmp $b} keys %{$conf->{environment}}) |
||||
{ |
||||
# $conf->{environment}{DRBD_RESOURCE} -> [srv51-Workstation3] |
||||
my $level = $conf->{environment}{$key} eq "" ? 3 : 2; |
||||
to_log($conf, {message => "DRBD Environment variable: [$key] -> [".$conf->{environment}{$key}."]", 'line' => __LINE__, level => $level}); |
||||
} |
||||
foreach my $key (sort {$a cmp $b} keys %ENV) |
||||
{ |
||||
next if exists $conf->{environment}{$key}; |
||||
my $level = $ENV{$key} eq "" ? 3 : 2; |
||||
to_log($conf, {message => "System Environment variable: [$key] -> [".$ENV{$key}."]", 'line' => __LINE__, level => 3}); |
||||
} |
||||
|
||||
# Make sure we at least have the target's IP. |
||||
if (not $conf->{environment}{DRBD_PEER_ADDRESS}) |
||||
{ |
||||
to_log($conf, {message => "Called without target's IP. Nothing to do, exiting. Were we called by 'pcs stonith list'?", 'line' => __LINE__, level => 1, priority => "alert"}); |
||||
exit(1); |
||||
} |
||||
|
||||
# This also checks that we're quorate and not in maintenance mode. |
||||
identify_peer($conf); |
||||
|
||||
# If we're still alive, we now need to check the DRBD resource disk state locally. |
||||
get_drbd_status($conf); |
||||
|
||||
# Is there a specific resource? |
||||
if ($conf->{environment}{DRBD_RESOURCE}) |
||||
{ |
||||
# Prevent the resource from running on the peer. |
||||
to_log($conf, {message => "We're being asked to unfence the specific resource: [".$conf->{environment}{DRBD_RESOURCE}."]. Will remove the node attribute blocking this server from running on: [".$conf->{cluster}{target_node}."].", 'line' => __LINE__}); |
||||
remove_constraint($conf); |
||||
} |
||||
else |
||||
{ |
||||
to_log($conf, {message => "We were not given a specific resource to unfence. Nothing to do.", 'line' => __LINE__}); |
||||
exit(0); |
||||
} |
||||
|
||||
# If we hit here, something very wrong happened. |
||||
exit(1); |
||||
|
||||
|
||||
############################################################################################################# |
||||
# Functions # |
||||
############################################################################################################# |
||||
|
||||
# This removes a location constraint that prevents the resource / server from running on the peer node. |
||||
sub remove_constraint |
||||
{ |
||||
my ($conf) = @_; |
||||
|
||||
my $target_server = $conf->{environment}{DRBD_RESOURCE}; |
||||
my $target_node = $conf->{cluster}{target_node}; |
||||
to_log($conf, {message => "Will now create a location constraint against: [".$target_server."] preventing it from running on: [".$target_node."].", 'line' => __LINE__, level => 1}); |
||||
|
||||
# Check that the rule was set. |
||||
my $rule_name = "drbd-fenced_".$target_server; |
||||
my $rule_set = 1; |
||||
my $rule_found = 0; |
||||
my $rule_output = ""; |
||||
my $shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --query"; |
||||
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2}); |
||||
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2}); |
||||
|
||||
if (($line =~ /name=$rule_name/) && ($line =~ /value=0/)) |
||||
{ |
||||
$rule_set = 0; |
||||
to_log($conf, {message => "rule_set: [".$rule_set."]", 'line' => __LINE__, level => 2}); |
||||
last; |
||||
} |
||||
else |
||||
{ |
||||
$rule_output .= $line."\n"; |
||||
} |
||||
} |
||||
close $file_handle; |
||||
|
||||
if (not $rule_set) |
||||
{ |
||||
# No need to unfence. |
||||
to_log($conf, {message => "The node attribute rule: [".$rule_name."] against the node: [".$target_node."] was not found. No need to unfence.", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(0); |
||||
} |
||||
|
||||
# Clear the node attribute |
||||
$shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --update 0"; |
||||
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2}); |
||||
open ($file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3}); |
||||
} |
||||
close $file_handle; |
||||
|
||||
# Check that the rule was set. |
||||
$rule_output = ""; |
||||
$shell_call = $conf->{path}{exe}{crm_attribute}." --type nodes --node ".$target_node." --name ".$rule_name." --query"; |
||||
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2}); |
||||
open ($file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2}); |
||||
|
||||
if (($line =~ /name=$rule_name/) && ($line =~ /value=0/)) |
||||
{ |
||||
$rule_set = 0; |
||||
to_log($conf, {message => "rule_set: [".$rule_set."]", 'line' => __LINE__, level => 2}); |
||||
last; |
||||
} |
||||
else |
||||
{ |
||||
$rule_output .= $line."\n"; |
||||
} |
||||
} |
||||
close $file_handle; |
||||
|
||||
if (not $rule_set) |
||||
{ |
||||
# Success! |
||||
to_log($conf, {message => "The node attribute rule: [".$rule_name."] against the node: [".$target_node."] has been cleared successfully.", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(0); |
||||
} |
||||
else |
||||
{ |
||||
# Failed. |
||||
$rule_output =~ s/\n$//gs; |
||||
to_log($conf, {message => "The node attribute triggering the fence rule: [".$rule_name."] against the node: [".$target_node."] appears to have not been cleared. Expected a string with 'name=".$rule_name." value=0' but got: [".$rule_output."].", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# This reads the status of all resources. If we're not all UpToDate, check if the peer is. If the peer is, |
||||
# abort. If not, proceed (someone is gouig to have a bad day, but maybe some servers will live) |
||||
sub get_drbd_status |
||||
{ |
||||
my ($conf) = @_; |
||||
|
||||
my $resource = ""; |
||||
my $peer = ""; |
||||
my $local_all_uptodate = 1; |
||||
my $peer_all_uptodate = 1; |
||||
my $shell_call = $conf->{path}{exe}{drbdadm}." status all"; |
||||
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2}); |
||||
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3}); |
||||
|
||||
if (not $line) |
||||
{ |
||||
$resource = ""; |
||||
$peer = ""; |
||||
to_log($conf, {message => "resource: [".$resource."], peer: [".$peer."]", 'line' => __LINE__, level => 3}); |
||||
next; |
||||
} |
||||
if ($line =~ /^(\S+)\s+role/) |
||||
{ |
||||
$resource = $1; |
||||
to_log($conf, {message => "resource: [".$resource."]", 'line' => __LINE__, level => 3}); |
||||
next; |
||||
} |
||||
if ($line =~ /^\s+(.*?) role:/) |
||||
{ |
||||
$peer = $1; |
||||
to_log($conf, {message => "peer: [".$peer."]", 'line' => __LINE__, level => 3}); |
||||
next; |
||||
} |
||||
if ($resource) |
||||
{ |
||||
if ($line =~ /disk:(.*)$/) |
||||
{ |
||||
my $local_dstate = $1; |
||||
$local_dstate =~ s/\s.*$//; |
||||
to_log($conf, {message => "local_dstate: [".$local_dstate."]", 'line' => __LINE__, level => 2}); |
||||
if (lc($local_dstate) ne "uptodate") |
||||
{ |
||||
$local_all_uptodate = 0; |
||||
to_log($conf, {message => "local_all_uptodate: [".$local_all_uptodate."]", 'line' => __LINE__, level => 2}); |
||||
} |
||||
next; |
||||
} |
||||
if ($line =~ /peer-disk:(.*)$/) |
||||
{ |
||||
my $peer_dstate = $1; |
||||
$peer_dstate =~ s/\s.*$//; |
||||
to_log($conf, {message => "peer: [".$peer."], peer_dstate: [".$peer_dstate."]", 'line' => __LINE__, level => 2}); |
||||
if (lc($peer_dstate) ne "uptodate") |
||||
{ |
||||
$peer_all_uptodate = 0; |
||||
to_log($conf, {message => "peer_all_uptodate: [".$peer_all_uptodate."]", 'line' => __LINE__, level => 2}); |
||||
} |
||||
next; |
||||
} |
||||
} |
||||
|
||||
} |
||||
close $file_handle; |
||||
|
||||
# If we're not all UpToDate, but the peer is, abort |
||||
to_log($conf, {message => "local_all_uptodate: [".$local_all_uptodate."], peer_all_uptodate: [".$peer_all_uptodate."]", 'line' => __LINE__, level => 2}); |
||||
if ((not $local_all_uptodate) && ($peer_all_uptodate)) |
||||
{ |
||||
# We're not good |
||||
to_log($conf, {message => "This node has DRBD resources that are not UpToDate, but the peer is fully UpToDate. Aborting.", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# This identifies the pacemaker name of the target node. If it can't find the peer, it exits with '1'. |
||||
sub identify_peer |
||||
{ |
||||
my ($conf) = @_; |
||||
|
||||
# I know the target's (SN) IP, map it to a node. |
||||
my $target_host = ""; |
||||
my $target_ip = $conf->{environment}{DRBD_PEER_ADDRESS}; |
||||
|
||||
# First, can we translate the IP to a host name? |
||||
my $shell_call = $conf->{path}{exe}{getent}." hosts ".$target_ip; |
||||
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 2}); |
||||
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 2}); |
||||
if ($line =~ /^$target_ip\s+(.*)$/) |
||||
{ |
||||
# This could be multiple names. |
||||
$target_host = $1; |
||||
to_log($conf, {message => "target_host: [".$target_host."]", 'line' => __LINE__, level => 2}); |
||||
#to_log($conf, {message => ">> target_host: [".$target_host."]", 'line' => __LINE__, level => 2}); |
||||
|
||||
# Strip off any suffix, we only want the short name. |
||||
#$target_host =~ s/\..*//; |
||||
#to_log($conf, {message => "<< target_host: [".$target_host."]", 'line' => __LINE__, level => 2}); |
||||
#last; |
||||
} |
||||
} |
||||
close $file_handle; |
||||
|
||||
# If I got the host name, try to match it to a pacemaker node name. |
||||
if ($target_host) |
||||
{ |
||||
# Get the current CIB (in an XML::Simple hash). This will exit if it fails to read the XML |
||||
# and convert it to an XML::Simple hash. |
||||
my $body = read_cib($conf); |
||||
|
||||
# Parse the XML. |
||||
my $host_name = $ENV{HOSTNAME}; |
||||
my $short_host_name = $ENV{HOSTNAME}; |
||||
$short_host_name =~ s/\..*$//; |
||||
to_log($conf, {message => "host_name: [".$host_name."], short_host_name: [".$short_host_name."]", 'line' => __LINE__, level => 2}); |
||||
|
||||
foreach my $hash_ref (sort {$a cmp $b} @{$body->{configuration}{nodes}{node}}) |
||||
{ |
||||
my $node = $hash_ref->{uname}; |
||||
my $id = $hash_ref->{id}; |
||||
to_log($conf, {message => "node: [".$node."], id: [".$id."]", 'line' => __LINE__, level => 2}); |
||||
foreach my $target_name (split/ /, $target_host) |
||||
{ |
||||
to_log($conf, {message => ">> target_name: [".$target_name."]", 'line' => __LINE__, level => 2}); |
||||
$target_name =~ s/\..*//; |
||||
to_log($conf, {message => "<< target_name: [".$target_name."]", 'line' => __LINE__, level => 2}); |
||||
if ($node =~ /^$target_name/) |
||||
{ |
||||
$conf->{cluster}{target_node} = $node; |
||||
to_log($conf, {message => "Found the pacemaker name of the target node: [".$conf->{cluster}{target_node}."]", 'line' => __LINE__, level => 1}); |
||||
} |
||||
elsif ($node =~ /^$short_host_name/) |
||||
{ |
||||
# This is me. Am I in maintenance mode? |
||||
if (exists $hash_ref->{instance_attributes}) |
||||
{ |
||||
# We've got some data.. |
||||
my $name = defined $hash_ref->{instance_attributes}{nvpair}{name} ? $hash_ref->{instance_attributes}{nvpair}{name} : ""; |
||||
my $value = defined $hash_ref->{instance_attributes}{nvpair}{value} ? $hash_ref->{instance_attributes}{nvpair}{value} : ""; |
||||
to_log($conf, {message => "node: [".$node."] instance attribyte name: [".$name."], value: [".$value."]", 'line' => __LINE__, level => 1}); |
||||
if (($name eq "maintenance") and ($value eq "on")) |
||||
{ |
||||
# We're in maintenance mode, abort. |
||||
to_log($conf, {message => "This node is in maintenance mode. Not able to fence!", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
my $quorate = $body->{'have-quorum'}; |
||||
to_log($conf, {message => "quorate: [".$quorate."]", 'line' => __LINE__, level => 1}); |
||||
if (not $quorate) |
||||
{ |
||||
to_log($conf, {message => "This not is not quorate. Refusing to fence the peer!", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
|
||||
# See if the target node is already out of the cluster. |
||||
foreach my $hash_ref (@{$body->{status}{node_state}}) |
||||
{ |
||||
my $node = $hash_ref->{uname}; |
||||
my $join = $hash_ref->{'join'}; |
||||
my $expected = $hash_ref->{expected}; |
||||
to_log($conf, {message => "node: [".$node."] join: [".$join."], expected: [".$expected."]", 'line' => __LINE__, level => 3}); |
||||
if ($node eq $conf->{cluster}{target_node}) |
||||
{ |
||||
to_log($conf, {message => "Checking the status of target node: [".$node."].", 'line' => __LINE__, level => 1}); |
||||
if (($join eq "down") && ($expected eq "down")) |
||||
{ |
||||
# The node is out. |
||||
to_log($conf, {message => "The node: [".$node."] is already down. No actual fence needed.", 'line' => __LINE__, level => 1}); |
||||
exit(7); |
||||
} |
||||
else |
||||
{ |
||||
to_log($conf, {message => "The node: [".$node."] is: [".$join."/".$expected."] (join/expected). Proceeding with the fence action.", 'line' => __LINE__, level => 1}); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
# Did I find the target? |
||||
if (not $conf->{cluster}{target_node}) |
||||
{ |
||||
to_log($conf, {message => "Failed to find the pacemaker name of the target node. Unable to proceed!", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# This reads in the CIB XML and returns it as a single multi-line string. |
||||
sub read_cib |
||||
{ |
||||
my ($conf) = @_; |
||||
|
||||
my $xml_opened = 0; |
||||
my $xml_closed = 0; |
||||
my $body = ""; |
||||
my $cib = '<?xml version="1.0" encoding="UTF-8"?>'; |
||||
my $shell_call = $conf->{path}{exe}{cibadmin}." --local --query"; |
||||
to_log($conf, {message => "Calling: [".$shell_call."]", 'line' => __LINE__, level => 3}); |
||||
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
to_log($conf, {message => "Output: [".$line."]", 'line' => __LINE__, level => 3}); |
||||
|
||||
$cib .= "\n".$line; |
||||
if ($line =~ /Signon to CIB failed/i) |
||||
{ |
||||
# Failed to connect, we're probably not in the cluster. |
||||
to_log($conf, {message => "This node does not appear to be in the cluster. Unable to get the CIB status.", 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
if ($line =~ /^<cib .*?>$/) |
||||
{ |
||||
$xml_opened = 1; |
||||
to_log($conf, {message => "xml_opened: [".$xml_opened."].", 'line' => __LINE__, level => 3}); |
||||
} |
||||
if ($line =~ /^<\/cib>$/) |
||||
{ |
||||
$xml_closed = 1; |
||||
to_log($conf, {message => "xml_closed: [".$xml_closed."].", 'line' => __LINE__, level => 3}); |
||||
} |
||||
} |
||||
close $file_handle; |
||||
to_log($conf, {message => "cib: ==========\n".$cib."\n==========", 'line' => __LINE__, level => 3}); |
||||
|
||||
# Now parse the CIB XML if I read it OK. |
||||
to_log($conf, {message => "xml_opened: [".$xml_opened."], xml_closed: [".$xml_closed."].", 'line' => __LINE__, level => 3}); |
||||
if (($xml_opened) && ($xml_closed)) |
||||
{ |
||||
# We're good |
||||
local $@; |
||||
my $xml = XML::Simple->new(); |
||||
my $test = eval { $body = $xml->XMLin($cib, KeyAttr => { language => 'name', key => 'name' }, ForceArray => [ 'id' ]) }; |
||||
if (not $test) |
||||
{ |
||||
chomp $@; |
||||
my $error = "[ Error ] - The was a problem parsing: [".$cib."]. The error was:\n"; |
||||
$error .= "===========================================================\n"; |
||||
$error .= $@."\n"; |
||||
$error .= "===========================================================\n"; |
||||
to_log($conf, {message => $error, 'line' => __LINE__, level => 0, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
# Failed to read the CIB XML. |
||||
to_log($conf, {message => "This node does not appear to be in the cluster. Unable to read the CIB XML properly.", 'line' => __LINE__, level => 2, priority => "err"}); |
||||
exit(1); |
||||
} |
||||
|
||||
return($body); |
||||
} |
||||
|
||||
# This checks the given paths and, if something isn't found, it searches PATH trying to find it. |
||||
sub find_executables |
||||
{ |
||||
my ($conf) = @_; |
||||
|
||||
# Variables. |
||||
my $check = ""; |
||||
my $bad = 0; |
||||
|
||||
# If PATH isn't set, set it (could have been scrubbed by a caller). |
||||
if (not $ENV{PATH}) |
||||
{ |
||||
$ENV{PATH} = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin"; |
||||
} |
||||
|
||||
# Log entries can only happen if I've found 'logger', so an extra check will be made on 'to_log' |
||||
# calls. |
||||
my @dirs = split/:/, $ENV{PATH}; |
||||
foreach my $exe (sort {$b cmp $a} keys %{$conf->{path}{exe}}) |
||||
{ |
||||
if ( not -e $conf->{path}{exe}{$exe} ) |
||||
{ |
||||
to_log($conf, {message => "The program: [".$exe."] is not at: [".$conf->{path}{exe}{$exe}."]. Looking for it now...", 'line' => __LINE__, level => 1}); |
||||
foreach my $path (@dirs) |
||||
{ |
||||
$check = "$path/$exe"; |
||||
$check =~ s/\/\//\//g; |
||||
to_log($conf, {message => "Checking: [".$check."]", 'line' => __LINE__, level => 2}); |
||||
if ( -e $check ) |
||||
{ |
||||
if (-e $conf->{path}{exe}{logger}) |
||||
{ |
||||
to_log($conf, {message => "Found it! Changed path for: [".$exe."] from: [".$conf->{path}{exe}{$exe}."] to: [".$check."]", 'line' => __LINE__, level => 1}); |
||||
} |
||||
else |
||||
{ |
||||
warn "DEBUG: Found it! Changed path for: [".$exe."] from: [".$conf->{path}{exe}{$exe}."] to: [".$check."]\n"; |
||||
} |
||||
$conf->{path}{exe}{$exe} = $check; |
||||
} |
||||
else |
||||
{ |
||||
to_log($conf, {message => "Not found.", 'line' => __LINE__, level => 2}); |
||||
} |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
to_log($conf, {message => "Found!", 'line' => __LINE__, level => 3}); |
||||
next; |
||||
} |
||||
|
||||
# Make sure it exists now. |
||||
to_log($conf, {message => "Checking again if: [".$exe."] is at: [".$conf->{path}{exe}{$exe}."].", 'line' => __LINE__, level => 3}); |
||||
if (not -e $conf->{path}{exe}{$exe}) |
||||
{ |
||||
$bad = 1; |
||||
if (-e $conf->{path}{exe}{logger}) |
||||
{ |
||||
to_log($conf, {message => "Failed to find executable: [".$exe."]. Unable to proceed.", 'line' => __LINE__, level => 0}); |
||||
} |
||||
else |
||||
{ |
||||
warn "Failed to find executable: [".$exe."]. Unable to proceed.\n"; |
||||
} |
||||
} |
||||
} |
||||
if ($bad) |
||||
{ |
||||
exit(1); |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
# Log file entries |
||||
sub to_log |
||||
{ |
||||
my ($conf, $parameters) = @_; |
||||
|
||||
my $facility = defined $parameters->{facility} ? $parameters->{facility} : $conf->{'log'}{facility}; |
||||
my $level = defined $parameters->{level} ? $parameters->{level} : 1; |
||||
my $line = defined $parameters->{'line'} ? $parameters->{'line'} : 0; |
||||
my $message = defined $parameters->{message} ? $parameters->{message} : ""; |
||||
my $priority = defined $parameters->{priority} ? $parameters->{priority} : ""; |
||||
|
||||
# Leave if we don't care about this message |
||||
return if $level > $conf->{'log'}{level}; |
||||
return if not $message; |
||||
|
||||
# Build the message. We log the line |
||||
if (($conf->{'log'}{line_numbers}) && ($line)) |
||||
{ |
||||
$message = $line."; ".$message; |
||||
} |
||||
|
||||
my $priority_string = $facility; |
||||
if ($priority) |
||||
{ |
||||
$priority_string .= ".".$priority; |
||||
} |
||||
elsif ($level eq "0") |
||||
{ |
||||
$priority_string .= ".notice"; |
||||
} |
||||
elsif (($level eq "1") or ($level eq "2")) |
||||
{ |
||||
$priority_string .= ".info"; |
||||
} |
||||
else |
||||
{ |
||||
$priority_string .= ".debug"; |
||||
} |
||||
|
||||
# Clean up the string for bash |
||||
$message =~ s/"/\\\"/gs; |
||||
#$message =~ s/\(/\\\(/gs; |
||||
|
||||
my $shell_call = $conf->{path}{exe}{logger}." --priority ".$priority_string." --tag ".$conf->{'log'}{tag}." -- \"".$message."\""; |
||||
open (my $file_handle, $shell_call." 2>&1 |") or die "Failed to call: [".$shell_call."]. The error was: $!\n"; |
||||
while(<$file_handle>) |
||||
{ |
||||
# This should not generate output. |
||||
chomp; |
||||
my $line = $_; |
||||
print "Unexpected logging output: [".$line."]\n"; |
||||
} |
||||
close $file_handle; |
||||
|
||||
return(0); |
||||
} |
||||
|
Loading…
Reference in new issue