App-Icli
view release on metacpan or search on metacpan
close($fh)
or warn("Failed to close $description ($file): $!\n");
}
sub enhance_status {
for my $c ( @{ $data->{servicecomments} } ) {
my $service
= firstval { $_->{service_description} eq $c->{service_description} }
@{ $data->{services}->{ $c->{host_name} } };
push( @{ $service->{comments} }, $c );
}
for my $c ( @{ $data->{hostcomments} } ) {
push( @{ $data->{hosts}->{ $c->{host_name} }->{comments} }, $c );
}
HOST: for my $h ( keys %{ $data->{services} } ) {
for my $s ( @{ $data->{services}->{$h} } ) {
if ( $s->{current_state} != 0 ) {
$extra->{$h}->{service_problem} = 1;
next HOST;
}
}
}
HOST: for my $h ( keys %{ $config->{services} } ) {
for my $s ( @{ $config->{services}->{$h} } ) {
if ( $s->{contacts} ) {
$s->{contacts} =~ s{^ *}{}o;
$s->{contacts} = [ split( m{, *}, $s->{contacts} ) ];
}
for my $group ( split( m{, *}, $s->{contact_groups} ) ) {
push(
@{ $s->{contacts} },
@{ $config->{contactgroups}{$group} }
);
}
}
}
}
sub parse_action {
if ( not $action ) {
return;
}
my @raw_args;
my %actionmap = (
a => 'acknowledge',
d => 'downtime',
r => 'recheck',
R => 'force_recheck',
);
( $action, @raw_args ) = split( /:/, $action );
@action_args = split( /,/, join( ':', @raw_args ) );
$list_type = q{};
if ( exists $actionmap{$action} ) {
$action = $actionmap{$action};
}
elsif ( length($action) <= 2 ) {
say STDERR "Note: Action shortcut '${action}' is unknown";
}
}
sub compute_hostlist {
for my $arg (@ARGV) {
my ( $host, $service ) = split( qr{/}, $arg );
if ( not any { $host } @for_hosts ) {
push( @for_hosts, $host );
}
if ($service) {
push( @for_services, $service );
}
}
foreach my $host (@for_hosts) {
if ( not exists $data->{services}->{$host} ) {
die("Unknown host: ${host}\n");
}
}
@list_hosts = @for_hosts;
@list_services = @for_services;
foreach my $group (@for_groups) {
if ( not exists $config->{'hostgroups'}->{$group} ) {
die("Unknown hostgroup: ${group}\n");
}
foreach
my $host ( split /,/, $config->{'hostgroups'}->{$group}->{'members'} )
{
if ( not any { $_ eq $host } @list_hosts ) {
push( @list_hosts, $host );
}
}
}
if ( @list_hosts == 0 ) {
@list_hosts = sort keys %{ $data->{hosts} };
}
if (@list_services) {
@list_hosts
= grep { have_service_multi( $_, @list_services ) } @list_hosts;
}
if ( $list_type eq 'h' ) {
@list_hosts
= grep { filter_host( $data->{'hosts'}->{$_} ) } @list_hosts;
}
}
sub service_state {
my ($s) = @_;
my $checked = $s->{has_been_checked};
my $digit = $s->{current_state};
if ( not $checked ) {
return 'PENDING ';
}
}
}
printf( "%-16.16s %4s\n", 'total hosts', $h_ok + $h_d + $h_u );
printf( "%-16.16s %s\n", 'up', pretty_state( $h_ok, 'ok' ) );
printf( "%-16.16s %s\n", 'down', pretty_state( $h_d, 'critical' ) );
printf( "%-16.16s %s\n", 'unreachable', pretty_state( $h_u, 'unknown' ) );
printf( "%-16.16s %s\n", 'pending', pretty_state( $h_p, 'pending' ) );
print "\n";
printf( "%-16.16s %4s\n", 'total services', $s_ok + $s_w + $s_c + $s_u );
printf( "%-16.16s %s\n", 'ok', pretty_state( $s_ok, 'ok' ) );
printf( "%-16.16s %s\n", 'warning', pretty_state( $s_w, 'warning' ) );
printf( "%-16.16s %s\n", 'critical', pretty_state( $s_c, 'critical' ) );
printf( "%-16.16s %s\n", 'unknown', pretty_state( $s_u, 'unknown' ) );
printf( "%-16.16s %s\n", 'pending', pretty_state( $s_p, 'pending' ) );
}
sub dispatch_command {
my $str = join( ';', @_ );
open( my $cmd_fh, '>', $rw_file )
or die( "Failed to open icinga command file ($rw_file): $!\n"
. "Set --rw-file to change it\n" );
printf $cmd_fh ( '[%d] %s', time(), $str, );
close($cmd_fh)
or warn("Failed to close $rw_file: $!\n");
}
sub action_on_host {
my ($host) = @_;
my $tz = DateTime::TimeZone->new( name => 'local' );
given ($action) {
when ('downtime') {
my ( $start, $end, $duration, $comment, @opts ) = @action_args;
my $strp = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S',
time_zone => $tz->name,
);
my $dt_start = $strp->parse_datetime($start);
my $dt_end = $strp->parse_datetime($end);
my $fixed = $duration ? 0 : 1;
my $command = 'SCHEDULE_HOST_DOWNTIME';
my $addendum = q{};
$duration = parse_duration($duration);
if ( 'children' ~~ \@opts ) {
$command = 'SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME';
$addendum = ' and its children';
}
if ( 'trigger_children' ~~ \@opts ) {
$command = 'SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME';
$addendum = ' and its children (triggered)';
}
dispatch_command( $command, $host, $dt_start->epoch, $dt_end->epoch,
$fixed, 0, $duration, 'cli', $comment );
say "Scheduled host downtime for '$host'$addendum";
}
when ('recheck') {
dispatch_command( 'SCHEDULE_HOST_SVC_CHECKS', $host, time() );
say "Scheduled check of * on '$host'";
}
when ('force_recheck') {
dispatch_command( 'SCHEDULE_FORCED_HOST_SVC_CHECKS', $host,
time() );
say "Scheduled forced check of * on '$host'";
}
default {
say STDERR "Cannot run action '${action}' on a host"
}
}
}
sub action_on_service {
my ( $host, $service ) = @_;
if ( not have_service( $host, $service ) ) {
return;
}
my $tz = DateTime::TimeZone->new( name => 'local' );
given ($action) {
when ('downtime') {
my ( $start, $end, $duration, $comment, @opts ) = @action_args;
my $strp = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S',
time_zone => $tz->name,
);
my $dt_start = $strp->parse_datetime($start);
my $dt_end = $strp->parse_datetime($end);
my $fixed = $duration ? 0 : 1;
$duration = parse_duration($duration);
dispatch_command( 'SCHEDULE_SVC_DOWNTIME', $host, $service,
$dt_start->epoch, $dt_end->epoch,
$fixed, 0, $duration, 'cli', $comment );
say "Scheduled service downtime for '$service' on '$host'";
}
when ('recheck') {
dispatch_command( 'SCHEDULE_SVC_CHECK', $host, $service, time() );
say "Scheduled check of '$service' on '$host'";
}
when ('force_recheck') {
dispatch_command( 'SCHEDULE_FORCED_SVC_CHECK', $host, $service,
time() );
say "Scheduled forced check of '$service' on '$host'";
}
when ('Acknowledge') {
dispatch_command( 'ACKNOWLEDGE_SVC_PROBLEM', $host, $service, 2, 1,
1, 'cli', $action_args[0] );
say "Acknowledged $host/$service: $action_args[0]";
}
default {
say STDERR "Cannot run action '${action}' on a service"
}
}
}
GetOptions(
'a|action=s' => \$action,
'c|config=s' => \$config_file,
'C|no-colours' => sub { $colours = 0 },
'f|status-file=s' => \$status_file,
'F|rw-file=s' => \$rw_file,
'g|hostgroup=s' => sub { push( @for_groups, split( /,/, $_[1] ) ) },
'h|host=s' => sub { push( @for_hosts, split( /,/, $_[1] ) ) },
'l|list=s' => sub { $list_type = substr( $_[1], 0, 1 ) },
'm|match=s' => sub { $match_output = qr{$_[1]}i },
'o|overview' => \$overview,
's|service=s' => sub { push( @for_services, split( /,/, $_[1] ) ) },
'U|as-contact=s' => \$as_contact,
'v|verbose+' => \$verbosity,
'V|version' => sub { say "icli version $VERSION"; exit 0 },
'x|cut-mode=s' => sub { $cut_mode = substr( $_[1], 0, 1 ) },
'z|filter=s' => sub { push( @filters, split( /,/, $_[1] ) ) },
) or die("Please see perldoc -F $0 for help\n");
read_objects( $status_file, \$data, 'icinga status_file', '--status-file' );
read_objects( $config_file, \$config, 'icinga object_cache_file', '--config' );
enhance_status();
parse_action();
compute_hostlist();
if ($overview) {
if ( $list_type eq 'h' ) {
for my $host (@list_hosts) {
display_host_overview($host);
}
}
else {
display_overview();
}
}
elsif ( $list_type ~~ [qw[s h]] ) {
for my $host (@list_hosts) {
display_host( $host, ( @list_hosts > 1 ) );
}
}
elsif ( $list_type eq 'q' ) {
display_queue();
}
elsif ( $list_type eq 'd' ) {
if ( exists $data->{hostdowntimes} ) {
say "Host downtimes:";
if ( $verbosity == 1 ) {
printf( "%-25.25s %-20.20s %-20.20s\n", 'Host', 'start', 'stop' );
}
elsif ( $verbosity == 2 ) {
printf( "%-25.25s %-20.20s %-20.20s %-17.17s %s\n",
'Host', 'start', 'stop', 'duration', 'comment' );
}
foreach my $downtime ( @{ $data->{hostdowntimes} } ) {
display_downtime($downtime);
}
}
else {
say "No host downtimes";
}
if ( exists $data->{servicedowntimes} ) {
say "\nService downtimes:";
if ( $verbosity == 1 ) {
printf( "%-25.25s %-25.25s %-20.20s %-20.20s\n",
'Host', 'Service', 'start', 'stop' );
}
elsif ( $verbosity == 2 ) {
printf( "%-25.25s %-25.25s %-20.20s %-20.20s %-17.17s %s\n",
'Host', 'Service', 'start', 'stop', 'duration', 'comment' );
}
foreach my $downtime ( @{ $data->{servicedowntimes} } ) {
display_downtime($downtime);
}
}
else {
say "\nNo service downtimes";
}
}
elsif ($action) {
foreach my $host (@list_hosts) {
if ( not @list_services and not @filters ) {
action_on_host($host);
}
elsif ( not @list_services and @filters ) {
foreach my $service ( grep { filter_service($_) }
@{ $data->{'services'}->{$host} } )
{
action_on_service( $host, $service->{'service_description'} );
}
}
else {
foreach my $service (@list_services) {
action_on_service( $host, $service );
}
}
}
}
else {
die("See perldoc -F $0\n");
}
__END__
=head1 NAME
B<icli> - Icinga Command Line Interface
=head1 SYNOPSIS
B<icli> [B<-v>|B<-vv>|B<-vvv>] [B<-z> I<filter>] [B<-h> I<hosts>] [B<-g> I<hostgroups>]
[B<-s> I<services>] [B<-c> I<config>] [B<-C>] [B<-f> I<status-file>]
[B<-F> I<rw-file>] [B<-lh>|B<-ls>|B<-lq>|B<-ld>] [B<-a> I<action>[B<:>I<args>]]
[I<host>/I<service> I<...>]
=head1 VERSION
version 0.48
=head1 DESCRIPTION
B<icli> is a command line interface to B<Icinga>. By default it lists all
services and their states.
Note that when supplying custom config and status file paths, B<icli> also
works with B<Nagios>. 100% compatibility is not guaranteed, however.
B<icli> only works when executed on the host running the B<Icinga> daemon. To
use it on another host, shell aliases (like C<< alias icli='ssh $icingahost
icli' >>) or similar are recommended.
You can narrow down the list of services you want displayed either using
B<filters> (like C<< icli -z!o >>), the B<-h>/B<-s> arguments (C<< icli -h
aneurysm -s Libraries,Websites >>) or commandline args (C<< icli
aneurysm/{Libraries,Websites} >> with shell expansion).
( run in 0.565 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )