From 26d529e6faaafa442e2b06093db1714f8f0b75bc Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 3 Mar 2020 16:21:11 +0000 Subject: Rename App/Ping903 to App::Mangemanche --- lib/App/Mangemanche.pm | 173 +++++++++++++++++++++++ lib/App/Mangemanche/Command.pm | 133 ++++++++++++++++++ lib/App/Mangemanche/Command/dbload.pm | 243 +++++++++++++++++++++++++++++++++ lib/App/Mangemanche/Command/ident.pm | 74 ++++++++++ lib/App/Mangemanche/Command/inspect.pm | 85 ++++++++++++ lib/App/Mangemanche/Command/ipadd.pm | 57 ++++++++ lib/App/Mangemanche/Command/ipdel.pm | 55 ++++++++ lib/App/Mangemanche/Command/nagios.pm | 107 +++++++++++++++ lib/App/Ping903.pm | 118 ---------------- lib/App/Ping903/Command.pm | 133 ------------------ lib/App/Ping903/Command/dbload.pm | 243 --------------------------------- lib/App/Ping903/Command/ident.pm | 74 ---------- lib/App/Ping903/Command/inspect.pm | 85 ------------ lib/App/Ping903/Command/ipadd.pm | 57 -------- lib/App/Ping903/Command/ipdel.pm | 55 -------- lib/App/Ping903/Command/nagios.pm | 62 --------- 16 files changed, 927 insertions(+), 827 deletions(-) create mode 100644 lib/App/Mangemanche.pm create mode 100644 lib/App/Mangemanche/Command.pm create mode 100644 lib/App/Mangemanche/Command/dbload.pm create mode 100644 lib/App/Mangemanche/Command/ident.pm create mode 100644 lib/App/Mangemanche/Command/inspect.pm create mode 100644 lib/App/Mangemanche/Command/ipadd.pm create mode 100644 lib/App/Mangemanche/Command/ipdel.pm create mode 100644 lib/App/Mangemanche/Command/nagios.pm delete mode 100644 lib/App/Ping903.pm delete mode 100644 lib/App/Ping903/Command.pm delete mode 100644 lib/App/Ping903/Command/dbload.pm delete mode 100644 lib/App/Ping903/Command/ident.pm delete mode 100644 lib/App/Ping903/Command/inspect.pm delete mode 100644 lib/App/Ping903/Command/ipadd.pm delete mode 100644 lib/App/Ping903/Command/ipdel.pm delete mode 100644 lib/App/Ping903/Command/nagios.pm (limited to 'lib') diff --git a/lib/App/Mangemanche.pm b/lib/App/Mangemanche.pm new file mode 100644 index 0000000..d719ecb --- /dev/null +++ b/lib/App/Mangemanche.pm @@ -0,0 +1,173 @@ +package App::Mangemanche; +use strict; +use warnings; +use Getopt::Long qw(:config gnu_getopt no_ignore_case require_order); +use Pod::Man; +use Pod::Usage; +use Pod::Find qw(pod_where); +use Net::Ping903; +use App::Mangemanche::Command ':exit_codes'; +use File::Basename; + +our $VERSION = '0.4.90'; + +my $DEFAULT_URL = 'http://localhost:8080'; + +sub new { + my $class = shift; + my $config_file; + my $url; + my $config = {}; + + GetOptions( + 'shorthelp|?' => sub { + pod2usage(-input => pod_where({-inc => 1}, $class), + -verbose => 99, + -sections => [qw(NAME SYNOPSIS COMMANDS)], + -exitstatus => 'NOEXIT'); + $class->list_commands; + exit(EX_OK); + }, + 'help' => sub { + App::Mangemanche::Command::mandoc($class, basename($0)), + }, + 'usage' => sub { + pod2usage(-exitstatus => EX_OK, + -input => pod_where({-inc => 1}, $class), + -verbose => 0) + }, + 'config|c=s' => \$config_file, + 'url|u=s' => \$url + ) or exit(EX_USAGE); + + if ($config_file) { + if (-f $config_file) { + $config = $class->readconfig($config_file); + } else { + die "configuration file $config_file does not exists\n"; + } + } else { + $config_file = '/etc/ping903.conf'; + if (-f $config_file) { + $config = $class->readconfig($config_file); + } + } + + if ($url) { + $config->{baseurl} = $url; + } elsif (!$config->{baseurl}) { + $config->{baseurl} = $DEFAULT_URL; + } + + unless ($config->{baseurl} =~ m{^https?://}) { + $config->{baseurl} = "http://$url"; + } + + my $agent = new Net::Ping903($config->{baseurl}); + + my $com = shift @ARGV; + die "no command name\n" unless $com; + + my $modname = __PACKAGE__ . '::Command::' . $com; + my $modpath = $modname; + $modpath =~ s{::}{/}g; + $modpath .= '.pm'; + my $cmd; + eval { + require $modpath; + $cmd = $modname->new($com, $agent); + }; + if ($@) { + if ($@ =~ /Can't locate $modpath/) { + die "unknown command: $com\n" + } + die $@; + } + return $cmd; +} + +sub readconfig { + my ($class, $file) = @_; + my $config = {}; + if (open(my $fh, '<', $file)) { + while (<$fh>) { + chomp; + s/^\s+//; + s/\s+$//; + next if /^(#.*)?$/; + if (m{^listen\s+(.+)$}) { + $config->{baseurl} = $1; + last; + } + } + close $fh; + } else { + die "$file: file doesn't exist\n"; + } + return $config; +} + +sub list_commands { + my $class = shift; + my @classpath = (split(/::/, $class), 'Command'); + + print "\nAvailable commands are:\n"; + foreach my $mod (sort + map { + my $name = basename($_); + if (exists($INC{File::Spec->catfile(@classpath,$name)})) { + () + } else { + eval { + require $_; + }; + $name =~ s/\.pm$//; + $@ ? () : [$name, $_]; + } + } + map { + glob File::Spec->catfile($_, @classpath, '*.pm') + } @INC) { + + my $s; + open(my $fh, '>', \$s); + pod2usage(-input => $mod->[1], + -output => $fh, + -verbose => 99, + -sections => ['NAME'], + -exitstatus => 'NOEXIT'); + close $fh; + my (undef, $descr) = split("\n", $s||''); + unless ($descr) { + $descr = ' ' . $mod->[0] + } + print "$descr\n"; + } +} + +1; + +=head1 NAME + +mangemanche - interface to B daemon. + +=head1 SYNOPSIS + +B +[B<-c> I] +[B<-u> I] +[B<--config=>I] +[B<--url=>I] +I +I... + +B +[B<-?>] +[B<--help>] +[B<--usage>] + +=head1 DESCRIPTION + +=head1 OPTIONS + + diff --git a/lib/App/Mangemanche/Command.pm b/lib/App/Mangemanche/Command.pm new file mode 100644 index 0000000..9857652 --- /dev/null +++ b/lib/App/Mangemanche/Command.pm @@ -0,0 +1,133 @@ +package App::Mangemanche::Command; +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use Pod::Find qw(pod_where); +use Carp; +use parent 'Exporter'; +use Pod::Man; +use File::Temp qw(tempfile); + +use constant { + EX_OK => 0, + EX_FAIL => 1, + EX_USAGE => 2 +}; + +our @EXPORT_OK = qw(EX_OK EX_FAIL EX_USAGE mandoc); +our %EXPORT_TAGS = ( 'exit_codes' => [ + qw(EX_OK EX_FAIL EX_USAGE) + ] ); + +sub mandoc { + my ($class, $name) = @_; + + my $input = pod_where({-inc => 1}, $class); + unless ($name) { + ($name = $class) =~ s/.*:://; + } + my $parser = Pod::Man->new(name => $name, + center => 'Ping903 control command', + release => 'Mangemanche'); + my ($fh,$filename) = tempfile(); + $parser->parse_from_file($input, $filename); + close($fh); + system("man $filename"); + unlink($filename); + exit(0); +} + +sub new { + my ($class, $com, $agent, %opts) = @_; + my $self = bless { progname => $com, agent => $agent }, $class; + + $self->{options} = {}; + + $self->{optdef}{'shorthelp|?'} = sub { + pod2usage(-message => $class->pod_usage_msg, + -input => pod_where({-inc => 1}, $class), + -exitstatus => EX_OK) + }; + $self->{optdef}{help} = sub { + mandoc($class); + }; + $self->{optdef}{usage} = sub { + pod2usage(-exitstatus => EX_OK, + -verbose => 0, + -input => pod_where({-inc => 1}, $class)) + }; + + foreach my $k (keys %opts) { + if (ref($opts{$k}) eq 'CODE') { + $self->{optdef}{$k} = sub { &{$opts{$k}}($self, @_ ) } + } elsif (ref($opts{$k})) { + $self->{optdef}{$k} = $opts{$k}; + } else { + $self->{optdef}{$k} = \$self->{options}{$opts{$k}} + } + } + $self; +} + +sub run { + my $self = shift; + GetOptions(%{$self->{optdef}}) or exit(EX_USAGE); +} + +sub agent { shift->{agent} } + +sub option { + my $self = shift; + my $name = shift; + if (defined(my $v = shift)) { + croak "too many arguments" if @_; + $self->{options}{$name} = $v; + } + return $self->{options}{$name}; +} + +sub pod_usage_msg { + my ($class) = @_; + + my $msg = ""; + + open my $fd, '>', \$msg; + + my $input = pod_where({-inc => 1}, $class); + + pod2usage(-verbose => 99, + -sections => 'NAME', + -output => $fd, + -exitval => 'NOEXIT', + -input => $input); + + my @a = split /\n/, $msg; + if ($#a < 1) { + croak "missing or malformed NAME section in " . ($input // $0); + } + $msg = $a[1]; + $msg =~ s/^\s+//; + $msg =~ s/ - /: /; + return $msg; +} + +sub error { + my ($self, @msg) = @_; + print STDERR "$self->{progname}: "; + print STDERR "@msg\n"; +} + +sub abend { + my ($self, $code, @msg) = @_; + $self->error(@msg); + exit $code; +} + +sub usage_error { + my $self = shift; + $self->abend(EX_USAGE, @_); +} + +1; + diff --git a/lib/App/Mangemanche/Command/dbload.pm b/lib/App/Mangemanche/Command/dbload.pm new file mode 100644 index 0000000..c7ddf38 --- /dev/null +++ b/lib/App/Mangemanche/Command/dbload.pm @@ -0,0 +1,243 @@ +package App::Mangemanche::Command::dbload; +use strict; +use warnings; +use parent 'App::Mangemanche::Command'; +use App::Mangemanche::Command ':exit_codes'; +use JSON; +use DBI; +use File::Spec; + +sub new { + my ($class, $com, $agent) = @_; + + my $self = bless $class->SUPER::new($com, $agent, + 'D|driver=s' => 'driver', + 'd|database=s' => sub { + my $self = shift; + push @{$self->{options}{connarg}}, "database=$_[1]"; + }, + 'h|host=s' => sub { + my $self = shift; + push @{$self->{options}{connarg}}, "host=$_[1]"; + }, + 'P|port=s' => sub { + my $self = shift; + push @{$self->{options}{connarg}}, "port=$_[1]"; + }, + 'u|user=s' => 'dbuser', + 'p|password=s' => 'dbpass', + 'params=s' => 'dbparams', + 'defaults-file=s' => 'defaults-file', + 't|table=s' => 'table', + 'c|column=s' => 'column', + 'q|query=s' => 'query'); + + $self->{options}{driver} = 'mysql'; + + $self; +} + +sub run { + my $self = shift; + $self->SUPER::run; + $self->usage_error("extra parameters") if @ARGV; + + my $query = $self->option('query'); + unless ($query) { + $self->usage_error("--table option must be specified") + unless $self->option('table'); + $self->usage_error("--column option must be specified") + unless $self->option('column'); + $query = qq{SELECT $self->{options}{column} FROM $self->{options}{table}}; + } + + if (my $p = $self->option('dbparams')) { + push @{$self->{options}{connarg}}, $p; + } + + unless ($self->{options}{connarg}) { + $self->usage_error('Database parameters not initialized. Please use the --database (optionally - --host and --port) option.'); + } + + if ($self->option('driver') eq 'mysql') { + unless ($self->option('defaults-file')) { + my $f = File::Spec->catfile($ENV{HOME}, '.my.cnf'); + if (-f $f) { + $self->option('defaults-file', $f); + } + } + } + if (my $p = $self->option('defaults-file')) { + push @{$self->{options}{connarg}}, ";mysql_read_default_file=$p"; + } + + my $arg = join(':', ('DBI',$self->{options}{driver}, + @{$self->{options}{connarg}})); + + my $dbh = DBI->connect($arg, $self->{options}{dbuser}, + $self->{options}{dbpass}, + { RaiseError => 0, PrintError => 1, AutoCommit => 1}) + or $self->abend(EX_FAIL, "can't connect to the database server"); + + my $res = $dbh->selectall_arrayref($query, + { RaiseError => 0, PrintError => 1 }) + or $self->abend(EX_FAIL, "query failed"); + + unless ($self->agent->set_ip_list([ map { $_->[0] } @$res ])) { + $self->abend(EX_FAIL, $self->agent->error_message); + } +} + +1; + +=head1 NAME + +dbload - Load IP addresses from database into Ping903 + +=head1 SYNOPSIS + +B +[B<-D> I] +[B<-P> I] +[B<-c> I] +[B<-d> I] +[B<-h> I] +[B<-p> I] +[B<-q> I] +[B<-t> I] +[B<-u> I] +[B<--column>=I] +[B<--database>=I] +[B<--defaults-file>=I] +[B<--driver>=I] +[B<--host>=I] +[B<--params>=I] +[B<--password>=I] +[B<--port>=I] +[B<--query>=I] +[B<--table>=I
] +[B<--user>=I] + +=head1 DESCRIPTION + +Reads IP addresses from the database and adds them to the list of monitored +hosts of L. + +Most parameters are configurable. You need to supply at least the database +connection information and the query to use. For the latter, use the +B<--table> and B<--column> options together. For really complex queries, +use the B<--query> option instead. See below for details. + +On success, prints on standard output the number of IP addresses loaded and +exits with status 0. On error, displays on stabdard error the detailed +diagnostic information as obtained from the server and exits with status 1. + +=head1 OPTIONS + +=head2 Database connection options + +=over 4 + +=item B<-D>, B<--driver>=I + +Sets L database driver name. Default is C. + +=item B<-h>, B<--host>=I + +Name or IP address of the database server. + +=item B<-P>, B<--port>=I + +Port number the database server listens on. + +=item B<-d>, B<--database>=I + +Name of the database to use. + +=item B<-u>, B<--user>=I + +Database user name. + +=item B<-p>, B<--password>=I + +Database password. + +=item B<--defaults-file>=I + +Name of the MySQL defaults file to use. This option is used only if DBI +driver B is used. By default, the file F<.my.cnf> in the home +directory is used, if it exists. + +=item B<--params>=I + +Additional parameters for the DBI driver. For detailed information, refer +to the documentation of the driver in use. + +=back + +=head2 Query to extract IP addresses from the database + +Three options are provided: + +=over 4 + +=item B<-t>, B<--table>=I
+ +Name of the table which holds IP addresses. + +=item B<-c>, B<--column>=I + +Name of the column in I
where IP address is stored. If need +be, you can use SQL expression as well. + +=item B<-q>, B<--query>=I + +SQL query to use in order to obtain IP addresses. This overrides the two +options above. + +=back + +Normally you would use a combination of B<--table> and B<--column> options. +For example, if you have a database table C, which has a column C, +that holds IP address in dotted-quad form, you would use the following options: + + --table=hosts --column=ip + +If the table holds IP addresses in numeric form, use the following instead +(MySQL is assumed): + + --table=hosts --column='INET_NTOA(ip)' + +Finally, if a complex query is needed to extract IP addresses (e.g. joining +several tables, etc.), supply it in full, via the B<--query> option. + +=head2 Informative options + +=over 4 + +=item B<-?> + +Display short help summary. + +=item B<--usage> + +Display command line usage summary. + +=item B<--help> + +Display a detailed program manual. + +=back + +=head1 SEE ALSO + +L, +L, +L. + +=cut + + + + + diff --git a/lib/App/Mangemanche/Command/ident.pm b/lib/App/Mangemanche/Command/ident.pm new file mode 100644 index 0000000..a871b95 --- /dev/null +++ b/lib/App/Mangemanche/Command/ident.pm @@ -0,0 +1,74 @@ +package App::Mangemanche::Command::ident; +use strict; +use warnings; +use parent 'App::Mangemanche::Command'; +use App::Mangemanche::Command ':exit_codes'; + +sub run { + my $self = shift; + $self->SUPER::run; + $self->usage_error("extra parameters") if @ARGV > 1; + + my $id = $self->agent->get_id(@ARGV); + unless ($id) { + $self->abend(EX_FAIL, $self->agent->error_message); + } + + foreach my $kw (sort keys %$id) { + my $val = $id->{$kw} or next; + print "$kw "; + if (ref($val) eq 'ARRAY') { + print join(' ', @$val); + } elsif (JSON::is_bool($val)) { + print $val ? "on" : "off"; + } else { + print $val + } + print "\n"; + } +} + +1; + +=head1 NAME + +ident - display ping903 server identification + +=head1 SYNOPSIS + +B +[I] + +=head1 DESCRIPTION + +Queries the running instance of B about identification parameters +and prints them on screen. The identification parameters are: package name, +package version an running instance PID. + +If given a single argument I, only the requested parameter is +displayed. + +=head1 OPTIONS + +=head2 Informative options + +=over 4 + +=item B<-?> + +Display short help summary. + +=item B<--usage> + +Display command line usage summary. + +=item B<--help> + +Display a detailed program manual. + +=back + +=head1 SEE ALSO + +L(1), L(5). + diff --git a/lib/App/Mangemanche/Command/inspect.pm b/lib/App/Mangemanche/Command/inspect.pm new file mode 100644 index 0000000..81f1049 --- /dev/null +++ b/lib/App/Mangemanche/Command/inspect.pm @@ -0,0 +1,85 @@ +package App::Mangemanche::Command::inspect; +use strict; +use warnings; +use parent 'App::Mangemanche::Command'; +use App::Mangemanche::Command ':exit_codes'; + +sub run { + my $self = shift; + $self->SUPER::run; + $self->usage_error("extra parameters") if @ARGV > 1; + + my $cfg = $self->agent->get_config(@ARGV); + unless ($cfg) { + $self->abend(EX_FAIL, $self->agent->error_message); + } + + foreach my $kw (sort keys %$cfg) { + my $val = $cfg->{$kw} or next; + print "$kw "; + if (ref($val) eq 'ARRAY') { + print "< +[I] + +=head1 DESCRIPTION + +Queries the running instance of B about its configuration and +prints it on screen in its configuration file format. + +If given a single argument I, only the requested configuration +parameter is displayed. + +=head1 OPTIONS + +=head2 Informative options + +=over 4 + +=item B<-?> + +Display short help summary. + +=item B<--usage> + +Display command line usage summary. + +=item B<--help> + +Display a detailed program manual. + +=back + +=head1 NOTES + +Depending on the B server and the user settings, the configuration +returned can be censored. + +The IP list is always formatted as a here-document, containing both immutable +and mutable IPs from the server configuration. + +=head1 SEE ALSO + +L(1), L(5). + diff --git a/lib/App/Mangemanche/Command/ipadd.pm b/lib/App/Mangemanche/Command/ipadd.pm new file mode 100644 index 0000000..53c48b6 --- /dev/null +++ b/lib/App/Mangemanche/Command/ipadd.pm @@ -0,0 +1,57 @@ +package App::Mangemanche::Command::ipadd; +use strict; +use warnings; +use parent 'App::Mangemanche::Command'; +use App::Mangemanche::Command ':exit_codes'; + +sub run { + my $self = shift; + $self->SUPER::run; + $self->usage_error("required parameters missing") unless @ARGV == 1; + my $ip = shift @ARGV; + unless ($self->agent->ipadd($ip)) { + $self->abend(EX_FAIL, $self->agent->error_message); + } +} +1; + +=head1 NAME + +ipadd - adds a single IP to the ping903 ip list + +=head1 SYNOPSIS + +B I + +=head1 DESCRIPTION + +Adds I to the mutable IP list of the running B daemon. + +=head1 OPTIONS + +=head2 Informative options + +=over 4 + +=item B<-?> + +Display short help summary. + +=item B<--usage> + +Display command line usage summary. + +=item B<--help> + +Display a detailed program manual. + +=back + +=head1 SEE ALSO + +L, +L, +L. + +=cut + diff --git a/lib/App/Mangemanche/Command/ipdel.pm b/lib/App/Mangemanche/Command/ipdel.pm new file mode 100644 index 0000000..ef4005b --- /dev/null +++ b/lib/App/Mangemanche/Command/ipdel.pm @@ -0,0 +1,55 @@ +package App::Mangemanche::Command::ipdel; +use strict; +use warnings; +use parent 'App::Mangemanche::Command'; +use App::Mangemanche::Command ':exit_codes'; + +sub run { + my $self = shift; + $self->SUPER::run; + $self->usage_error("required parameters missing") unless @ARGV == 1; + my $ip = shift @ARGV; + unless ($self->agent->ipdel($ip)) { + $self->abend(EX_FAIL, $self->agent->error_message); + } +} +1; +=head1 NAME + +ipdel - deletes a single IP from the ping903 mutable ip list + +=head1 SYNOPSIS + +B I + +=head1 DESCRIPTION + +Deletes I from the mutable IP list of the running B daemon. + +=head1 OPTIONS + +=head2 Informative options + +=over 4 + +=item B<-?> + +Display short help summary. + +=item B<--usage> + +Display command line usage summary. + +=item B<--help> + +Display a detailed program manual. + +=back + +=head1 SEE ALSO + +L, +L, +L. + +=cut diff --git a/lib/App/Mangemanche/Command/nagios.pm b/lib/App/Mangemanche/Command/nagios.pm new file mode 100644 index 0000000..7e42d01 --- /dev/null +++ b/lib/App/Mangemanche/Command/nagios.pm @@ -0,0 +1,107 @@ +package App::Mangemanche::Command::nagios; +use strict; +use warnings; +use parent 'App::Mangemanche::Command'; +use App::Mangemanche::Command ':exit_codes'; +use File::Spec; +use File::Basename; +use Nagios::Config; + +sub new { + my ($class, $com, $agent) = @_; + + bless $class->SUPER::new($com, $agent, + 'progname|p=s@' => 'prognames', + 'config|c=s' => 'nagiosconf'); +} + +sub run { + my $self = shift; + $self->SUPER::run; + $self->usage_error("extra parameters") if @ARGV; + my $cf = $self->{options}{nagiosconf} // '/etc/nagios/nagios.cfg'; + push @{$self->{options}{prognames}}, 'ping903q'; + $self->{cfg} = Nagios::Config->new(filename => $cf); + + my %iph; + foreach my $host ($self->{cfg}->list_hosts) { + if ($host->register && $self->check_command($host)) { + $iph{$host->address} = 1; + } + } + foreach my $service ($self->{cfg}->list_services) { + if ($service->register && $self->check_command($service)) { + my @addr = map { $_->address } (@{$service->host_name}); + @iph{@addr} = (1) x @addr + } + } + + my @iplist = keys %iph; + my $n = @iplist; + $self->error("info: collected $n addresses"); + unless ($self->agent->set_ip_list([@iplist])) { + $self->abend(EX_FAIL, $self->agent->error_message); + } +} + +sub check_command { + my ($self, $host) = @_; + my $chk = $host->check_command or return; + $chk =~ s/!.*//; + my $def = $self->{cfg}->find_object($chk) or return; + return unless $def->can('command_line'); + my ($cmd) = split(/\s+/, $def->command_line); + return grep { + File::Spec->file_name_is_absolute($_) + ? $cmd eq $_ + : basename($cmd) eq $_ + } @{$self->{options}{prognames}}; +} + +1; +=head1 NAME + +nagios - extract monitored IP addresses from the Nagios configuration + +=head1 SYNOPSIS + +B +[B<-c> I] +[B<-p> I] +[B<--config=>I] +[B<--progname=>I] + +=head1 DESCRIPTION + +Scans Nagios configuration for services that use B and extracts +IP addresses of associated hosts. At the end of the run, the extracted +addresses are posted to the mutable list of IP addresses monitored by +the running B server. + +Default nagios configuration file is F. + +=head1 OPTIONS + +=over 4 + +=item B<-c>, B<--config=>I + +Read Nagios configuration from I. + +=item B<-p>, B<--progname=>I + +Look for I in addition to B. + +=back + +=head1 SEE ALSO + +L, +L, +L, +L. + +=cut + + + diff --git a/lib/App/Ping903.pm b/lib/App/Ping903.pm deleted file mode 100644 index 2a39391..0000000 --- a/lib/App/Ping903.pm +++ /dev/null @@ -1,118 +0,0 @@ -package App::Ping903; -use strict; -use warnings; -use Getopt::Long qw(:config gnu_getopt no_ignore_case require_order); -use Pod::Man; -use Pod::Usage; -use Pod::Find qw(pod_where); -use Net::Ping903; -use App::Ping903::Command ':exit_codes'; -use File::Basename; - -our $VERSION = '0.4.90'; - -my $DEFAULT_URL = 'http://localhost:8080'; - -sub new { - my $class = shift; - my $config_file; - my $url; - my $config = {}; - - GetOptions( - 'shorthelp|?' => sub { - pod2usage(-input => pod_where({-inc => 1}, $class), - -verbose => 99, - -sections => [qw(NAME SYNOPSIS COMMANDS)], - -exitstatus => EX_OK) - }, - 'help' => sub { - App::Ping903::Command::mandoc($class, basename($0)), - }, - 'usage' => sub { - pod2usage(-exitstatus => EX_OK, - -input => pod_where({-inc => 1}, $class), - -verbose => 0) - }, - 'config|c=s' => \$config_file, - 'url|u=s' => \$url - ) or exit(EX_USAGE); - - if ($config_file) { - if (-f $config_file) { - $config = $class->readconfig($config_file); - } else { - die "configuration file $config_file does not exists\n"; - } - } else { - $config_file = '/etc/ping903.conf'; - if (-f $config_file) { - $config = $class->readconfig($config_file); - } - } - - if ($url) { - $config->{baseurl} = $url; - } elsif (!$config->{baseurl}) { - $config->{baseurl} = $DEFAULT_URL; - } - - unless ($config->{baseurl} =~ m{^https?://}) { - $config->{baseurl} = "http://$url"; - } - - my $agent = new Net::Ping903($config->{baseurl}); - - my $com = shift @ARGV; - die "no command name\n" unless $com; - - my $modname = __PACKAGE__ . '::Command::' . $com; - my $modpath = $modname; - $modpath =~ s{::}{/}g; - $modpath .= '.pm'; - my $cmd; - eval { - require $modpath; - $cmd = $modname->new($com, $agent); - }; - if ($@) { - if ($@ =~ /Can't locate $modpath/) { - die "unknown command: $com\n" - } - die $@; - } - return $cmd; -} - -sub readconfig { - my ($class, $file) = @_; - my $config = {}; - if (open(my $fh, '<', $file)) { - while (<$fh>) { - chomp; - s/^\s+//; - s/\s+$//; - next if /^(#.*)?$/; - if (m{^listen\s+(.+)$}) { - $config->{baseurl} = $1; - last; - } - } - close $fh; - } else { - die "$file: file doesn't exist\n"; - } - return $config; -} - -1; - -=head1 NAME - -=head1 SYNOPSIS - -=head1 DESCRIPTION - -=head1 OPTIONS - - diff --git a/lib/App/Ping903/Command.pm b/lib/App/Ping903/Command.pm deleted file mode 100644 index bd1a85f..0000000 --- a/lib/App/Ping903/Command.pm +++ /dev/null @@ -1,133 +0,0 @@ -package App::Ping903::Command; -use strict; -use warnings; -use Getopt::Long; -use Pod::Usage; -use Pod::Find qw(pod_where); -use Carp; -use parent 'Exporter'; -use Pod::Man; -use File::Temp qw(tempfile); - -use constant { - EX_OK => 0, - EX_FAIL => 1, - EX_USAGE => 2 -}; - -our @EXPORT_OK = qw(EX_OK EX_FAIL EX_USAGE mandoc); -our %EXPORT_TAGS = ( 'exit_codes' => [ - qw(EX_OK EX_FAIL EX_USAGE) - ] ); - -sub mandoc { - my ($class, $name) = @_; - - my $input = pod_where({-inc => 1}, $class); - unless ($name) { - ($name = $class) =~ s/.*:://; - } - my $parser = Pod::Man->new(name => $name, - center => 'Ping903 control command', - release => 'Mangemanche'); - my ($fh,$filename) = tempfile(); - $parser->parse_from_file($input, $filename); - close($fh); - system("man $filename"); - unlink($filename); - exit(0); -} - -sub new { - my ($class, $com, $agent, %opts) = @_; - my $self = bless { progname => $com, agent => $agent }, $class; - - $self->{options} = {}; - - $self->{optdef}{'shorthelp|?'} = sub { - pod2usage(-message => $class->pod_usage_msg, - -input => pod_where({-inc => 1}, $class), - -exitstatus => EX_OK) - }; - $self->{optdef}{help} = sub { - mandoc($class); - }; - $self->{optdef}{usage} = sub { - pod2usage(-exitstatus => EX_OK, - -verbose => 0, - -input => pod_where({-inc => 1}, $class)) - }; - - foreach my $k (keys %opts) { - if (ref($opts{$k}) eq 'CODE') { - $self->{optdef}{$k} = sub { &{$opts{$k}}($self, @_ ) } - } elsif (ref($opts{$k})) { - $self->{optdef}{$k} = $opts{$k}; - } else { - $self->{optdef}{$k} = \$self->{options}{$opts{$k}} - } - } - $self; -} - -sub run { - my $self = shift; - GetOptions(%{$self->{optdef}}) or exit(EX_USAGE); -} - -sub agent { shift->{agent} } - -sub option { - my $self = shift; - my $name = shift; - if (defined(my $v = shift)) { - croak "too many arguments" if @_; - $self->{options}{$name} = $v; - } - return $self->{options}{$name}; -} - -sub pod_usage_msg { - my ($class) = @_; - - my $msg = ""; - - open my $fd, '>', \$msg; - - my $input = pod_where({-inc => 1}, $class); - - pod2usage(-verbose => 99, - -sections => 'NAME', - -output => $fd, - -exitval => 'NOEXIT', - -input => $input); - - my @a = split /\n/, $msg; - if ($#a < 1) { - croak "missing or malformed NAME section in " . ($input // $0); - } - $msg = $a[1]; - $msg =~ s/^\s+//; - $msg =~ s/ - /: /; - return $msg; -} - -sub error { - my ($self, @msg) = @_; - print STDERR "$self->{progname}: "; - print STDERR "@msg\n"; -} - -sub abend { - my ($self, $code, @msg) = @_; - $self->error(@msg); - exit $code; -} - -sub usage_error { - my $self = shift; - $self->abend(EX_USAGE, @_); -} - -1; - diff --git a/lib/App/Ping903/Command/dbload.pm b/lib/App/Ping903/Command/dbload.pm deleted file mode 100644 index 20c8ad1..0000000 --- a/lib/App/Ping903/Command/dbload.pm +++ /dev/null @@ -1,243 +0,0 @@ -package App::Ping903::Command::dbload; -use strict; -use warnings; -use parent 'App::Ping903::Command'; -use App::Ping903::Command ':exit_codes'; -use JSON; -use DBI; -use File::Spec; - -sub new { - my ($class, $com, $agent) = @_; - - my $self = bless $class->SUPER::new($com, $agent, - 'D|driver=s' => 'driver', - 'd|database=s' => sub { - my $self = shift; - push @{$self->{options}{connarg}}, "database=$_[1]"; - }, - 'h|host=s' => sub { - my $self = shift; - push @{$self->{options}{connarg}}, "host=$_[1]"; - }, - 'P|port=s' => sub { - my $self = shift; - push @{$self->{options}{connarg}}, "port=$_[1]"; - }, - 'u|user=s' => 'dbuser', - 'p|password=s' => 'dbpass', - 'params=s' => 'dbparams', - 'defaults-file=s' => 'defaults-file', - 't|table=s' => 'table', - 'c|column=s' => 'column', - 'q|query=s' => 'query'); - - $self->{options}{driver} = 'mysql'; - - $self; -} - -sub run { - my $self = shift; - $self->SUPER::run; - $self->usage_error("extra parameters") if @ARGV; - - my $query = $self->option('query'); - unless ($query) { - $self->usage_error("--table option must be specified") - unless $self->option('table'); - $self->usage_error("--column option must be specified") - unless $self->option('column'); - $query = qq{SELECT $self->{options}{column} FROM $self->{options}{table}}; - } - - if (my $p = $self->option('dbparams')) { - push @{$self->{options}{connarg}}, $p; - } - - unless ($self->{options}{connarg}) { - $self->usage_error('Database parameters not initialized. Please use the --database (optionally - --host and --port) option.'); - } - - if ($self->option('driver') eq 'mysql') { - unless ($self->option('defaults-file')) { - my $f = File::Spec->catfile($ENV{HOME}, '.my.cnf'); - if (-f $f) { - $self->option('defaults-file', $f); - } - } - } - if (my $p = $self->option('defaults-file')) { - push @{$self->{options}{connarg}}, ";mysql_read_default_file=$p"; - } - - my $arg = join(':', ('DBI',$self->{options}{driver}, - @{$self->{options}{connarg}})); - - my $dbh = DBI->connect($arg, $self->{options}{dbuser}, - $self->{options}{dbpass}, - { RaiseError => 0, PrintError => 1, AutoCommit => 1}) - or $self->abend(EX_FAIL, "can't connect to the database server"); - - my $res = $dbh->selectall_arrayref($query, - { RaiseError => 0, PrintError => 1 }) - or $self->abend(EX_FAIL, "query failed"); - - unless ($self->agent->set_ip_list([ map { $_->[0] } @$res ])) { - $self->abend(EX_FAIL, $self->agent->error_message); - } -} - -1; - -=head1 NAME - -dbload - Load IP addresses from database into Ping903 - -=head1 SYNOPSIS - -B -[B<-D> I] -[B<-P> I] -[B<-c> I] -[B<-d> I] -[B<-h> I] -[B<-p> I] -[B<-q> I] -[B<-t> I
] -[B<-u> I] -[B<--column>=I] -[B<--database>=I] -[B<--defaults-file>=I] -[B<--driver>=I] -[B<--host>=I] -[B<--params>=I] -[B<--password>=I] -[B<--port>=I] -[B<--query>=I] -[B<--table>=I
] -[B<--user>=I] - -=head1 DESCRIPTION - -Reads IP addresses from the database and adds them to the list of monitored -hosts of L. - -Most parameters are configurable. You need to supply at least the database -connection information and the query to use. For the latter, use the -B<--table> and B<--column> options together. For really complex queries, -use the B<--query> option instead. See below for details. - -On success, prints on standard output the number of IP addresses loaded and -exits with status 0. On error, displays on stabdard error the detailed -diagnostic information as obtained from the server and exits with status 1. - -=head1 OPTIONS - -=head2 Database connection options - -=over 4 - -=item B<-D>, B<--driver>=I - -Sets L database driver name. Default is C. - -=item B<-h>, B<--host>=I - -Name or IP address of the database server. - -=item B<-P>, B<--port>=I - -Port number the database server listens on. - -=item B<-d>, B<--database>=I - -Name of the database to use. - -=item B<-u>, B<--user>=I - -Database user name. - -=item B<-p>, B<--password>=I - -Database password. - -=item B<--defaults-file>=I - -Name of the MySQL defaults file to use. This option is used only if DBI -driver B is used. By default, the file F<.my.cnf> in the home -directory is used, if it exists. - -=item B<--params>=I - -Additional parameters for the DBI driver. For detailed information, refer -to the documentation of the driver in use. - -=back - -=head2 Query to extract IP addresses from the database - -Three options are provided: - -=over 4 - -=item B<-t>, B<--table>=I
- -Name of the table which holds IP addresses. - -=item B<-c>, B<--column>=I - -Name of the column in I
where IP address is stored. If need -be, you can use SQL expression as well. - -=item B<-q>, B<--query>=I - -SQL query to use in order to obtain IP addresses. This overrides the two -options above. - -=back - -Normally you would use a combination of B<--table> and B<--column> options. -For example, if you have a database table C, which has a column C, -that holds IP address in dotted-quad form, you would use the following options: - - --table=hosts --column=ip - -If the table holds IP addresses in numeric form, use the following instead -(MySQL is assumed): - - --table=hosts --column='INET_NTOA(ip)' - -Finally, if a complex query is needed to extract IP addresses (e.g. joining -several tables, etc.), supply it in full, via the B<--query> option. - -=head2 Informative options - -=over 4 - -=item B<-?> - -Display short help summary. - -=item B<--usage> - -Display command line usage summary. - -=item B<--help> - -Display a detailed program manual. - -=back - -=head1 SEE ALSO - -L, -L, -L. - -=cut - - - - - diff --git a/lib/App/Ping903/Command/ident.pm b/lib/App/Ping903/Command/ident.pm deleted file mode 100644 index 67cebd9..0000000 --- a/lib/App/Ping903/Command/ident.pm +++ /dev/null @@ -1,74 +0,0 @@ -package App::Ping903::Command::ident; -use strict; -use warnings; -use parent 'App::Ping903::Command'; -use App::Ping903::Command ':exit_codes'; - -sub run { - my $self = shift; - $self->SUPER::run; - $self->usage_error("extra parameters") if @ARGV > 1; - - my $id = $self->agent->get_id(@ARGV); - unless ($id) { - $self->abend(EX_FAIL, $self->agent->error_message); - } - - foreach my $kw (sort keys %$id) { - my $val = $id->{$kw} or next; - print "$kw "; - if (ref($val) eq 'ARRAY') { - print join(' ', @$val); - } elsif (JSON::is_bool($val)) { - print $val ? "on" : "off"; - } else { - print $val - } - print "\n"; - } -} - -1; - -=head1 NAME - -ident - display ping903 server identification - -=head1 SYNOPSIS - -B -[I] - -=head1 DESCRIPTION - -Queries the running instance of B about identification parameters -and prints them on screen. The identification parameters are: package name, -package version an running instance PID. - -If given a single argument I, only the requested parameter is -displayed. - -=head1 OPTIONS - -=head2 Informative options - -=over 4 - -=item B<-?> - -Display short help summary. - -=item B<--usage> - -Display command line usage summary. - -=item B<--help> - -Display a detailed program manual. - -=back - -=head1 SEE ALSO - -L(1), L(5). - diff --git a/lib/App/Ping903/Command/inspect.pm b/lib/App/Ping903/Command/inspect.pm deleted file mode 100644 index 6c4a11b..0000000 --- a/lib/App/Ping903/Command/inspect.pm +++ /dev/null @@ -1,85 +0,0 @@ -package App::Ping903::Command::inspect; -use strict; -use warnings; -use parent 'App::Ping903::Command'; -use App::Ping903::Command ':exit_codes'; - -sub run { - my $self = shift; - $self->SUPER::run; - $self->usage_error("extra parameters") if @ARGV > 1; - - my $cfg = $self->agent->get_config(@ARGV); - unless ($cfg) { - $self->abend(EX_FAIL, $self->agent->error_message); - } - - foreach my $kw (sort keys %$cfg) { - my $val = $cfg->{$kw} or next; - print "$kw "; - if (ref($val) eq 'ARRAY') { - print "< -[I] - -=head1 DESCRIPTION - -Queries the running instance of B about its configuration and -prints it on screen in its configuration file format. - -If given a single argument I, only the requested configuration -parameter is displayed. - -=head1 OPTIONS - -=head2 Informative options - -=over 4 - -=item B<-?> - -Display short help summary. - -=item B<--usage> - -Display command line usage summary. - -=item B<--help> - -Display a detailed program manual. - -=back - -=head1 NOTES - -Depending on the B server and the user settings, the configuration -returned can be censored. - -The IP list is always formatted as a here-document, containing both immutable -and mutable IPs from the server configuration. - -=head1 SEE ALSO - -L(1), L(5). - diff --git a/lib/App/Ping903/Command/ipadd.pm b/lib/App/Ping903/Command/ipadd.pm deleted file mode 100644 index 443c052..0000000 --- a/lib/App/Ping903/Command/ipadd.pm +++ /dev/null @@ -1,57 +0,0 @@ -package App::Ping903::Command::ipadd; -use strict; -use warnings; -use parent 'App::Ping903::Command'; -use App::Ping903::Command ':exit_codes'; - -sub run { - my $self = shift; - $self->SUPER::run; - $self->usage_error("required parameters missing") unless @ARGV == 1; - my $ip = shift @ARGV; - unless ($self->agent->ipadd($ip)) { - $self->abend(EX_FAIL, $self->agent->error_message); - } -} -1; - -=head1 NAME - -ipadd - adds a single IP to the ping903 ip list - -=head1 SYNOPSIS - -B I - -=head1 DESCRIPTION - -Adds I to the mutable IP list of the running B daemon. - -=head1 OPTIONS - -=head2 Informative options - -=over 4 - -=item B<-?> - -Display short help summary. - -=item B<--usage> - -Display command line usage summary. - -=item B<--help> - -Display a detailed program manual. - -=back - -=head1 SEE ALSO - -L, -L, -L. - -=cut - diff --git a/lib/App/Ping903/Command/ipdel.pm b/lib/App/Ping903/Command/ipdel.pm deleted file mode 100644 index 45ca0d7..0000000 --- a/lib/App/Ping903/Command/ipdel.pm +++ /dev/null @@ -1,55 +0,0 @@ -package App::Ping903::Command::ipdel; -use strict; -use warnings; -use parent 'App::Ping903::Command'; -use App::Ping903::Command ':exit_codes'; - -sub run { - my $self = shift; - $self->SUPER::run; - $self->usage_error("required parameters missing") unless @ARGV == 1; - my $ip = shift @ARGV; - unless ($self->agent->ipdel($ip)) { - $self->abend(EX_FAIL, $self->agent->error_message); - } -} -1; -=head1 NAME - -ipdel - deletes a single IP from the ping903 mutable ip list - -=head1 SYNOPSIS - -B I - -=head1 DESCRIPTION - -Deletes I from the mutable IP list of the running B daemon. - -=head1 OPTIONS - -=head2 Informative options - -=over 4 - -=item B<-?> - -Display short help summary. - -=item B<--usage> - -Display command line usage summary. - -=item B<--help> - -Display a detailed program manual. - -=back - -=head1 SEE ALSO - -L, -L, -L. - -=cut diff --git a/lib/App/Ping903/Command/nagios.pm b/lib/App/Ping903/Command/nagios.pm deleted file mode 100644 index b6c8204..0000000 --- a/lib/App/Ping903/Command/nagios.pm +++ /dev/null @@ -1,62 +0,0 @@ -package App::Ping903::Command::nagios; -use strict; -use warnings; -use parent 'App::Ping903::Command'; -use App::Ping903::Command ':exit_codes'; -use File::Spec; -use File::Basename; -use Nagios::Config; - -sub new { - my ($class, $com, $agent) = @_; - - bless $class->SUPER::new($com, $agent, - 'progname|p=s@' => 'prognames', - 'config|c=s' => 'nagiosconf'); -} - -sub run { - my $self = shift; - $self->SUPER::run; - $self->usage_error("extra parameters") if @ARGV; - my $cf = $self->{options}{nagiosconf} // '/etc/nagios/nagios.cfg'; - push @{$self->{options}{prognames}}, 'ping903q'; - $self->{cfg} = Nagios::Config->new(filename => $cf); - - my %iph; - foreach my $host ($self->{cfg}->list_hosts) { - if ($host->register && $self->check_command($host)) { - $iph{$host->address} = 1; - } - } - foreach my $service ($self->{cfg}->list_services) { - if ($service->register && $self->check_command($service)) { - my @addr = map { $_->address } (@{$service->host_name}); - @iph{@addr} = (1) x @addr - } - } - - my @iplist = keys %iph; - my $n = @iplist; - $self->error("info: collected $n addresses"); - unless ($self->agent->set_ip_list([@iplist])) { - $self->abend(EX_FAIL, $self->agent->error_message); - } -} - -sub check_command { - my ($self, $host) = @_; - my $chk = $host->check_command or return; - $chk =~ s/!.*//; - my $def = $self->{cfg}->find_object($chk) or return; - return unless $def->can('command_line'); - my ($cmd) = split(/\s+/, $def->command_line); - return grep { - File::Spec->file_name_is_absolute($_) - ? $cmd eq $_ - : basename($cmd) eq $_ - } @{$self->{options}{prognames}}; -} - -1; - -- cgit v1.2.1