plugins/check_amanda-client
changeset 21 2247be0e2a13
parent 20 92818898bb2c
child 23 6c2728f0c6f7
equal deleted inserted replaced
20:92818898bb2c 21:2247be0e2a13
     1 #! /usr/bin/perl
     1 #! /usr/bin/perl
     2 # source: https://ssl.schlittermann.de/hg/ius/nagios/nagios-plugin-amanda-client
     2 # source: https://ssl.schlittermann.de/hg/ius/nagios/nagios-plugin-amanda-client
     3 
     3 
     4 use 5.010;
     4 use 5.014;
     5 use strict;
     5 use strict;
     6 use warnings;
     6 use warnings;
     7 use Getopt::Long;
     7 use Getopt::Long;
     8 use POSIX;
     8 use POSIX;
     9 use File::Spec::Functions;
     9 use File::Spec::Functions;
    11 use File::Find;
    11 use File::Find;
    12 use Carp;
    12 use Carp;
    13 use Pod::Usage;
    13 use Pod::Usage;
    14 use Const::Fast;
    14 use Const::Fast;
    15 
    15 
    16 our $VERSION = '0.0.2';
       
    17 
       
    18 const my $NAME  => 'AMANDA-CLIENT';
    16 const my $NAME  => 'AMANDA-CLIENT';
    19 const my $USER  => 'backup';
    17 const my $USER  => 'backup';
    20 const my $CFDIR => '/etc/amanda';
    18 const my $CFDIR => '/etc/amanda';
    21 
    19 
    22 sub su;
    20 sub su;
    28 sub amchecks;
    26 sub amchecks;
    29 sub compare_lists;
    27 sub compare_lists;
    30 
    28 
    31 sub main;
    29 sub main;
    32 
    30 
    33 sub ok;
    31 sub OK;
    34 sub warning;
    32 sub WARNING;
    35 sub critical;
    33 sub CRITICAL;
    36 sub unknown;
    34 sub UNKNOWN;
    37 sub verbose;
    35 sub verbose;
    38 sub unique { my %h; @h{@_} = (); keys %h }
    36 sub unique { my %h; @h{@_} = (); keys %h }
    39 
    37 
    40 $SIG{__DIE__} = sub { unknown @_ unless $^S };
    38 $SIG{__DIE__} = sub { UNKNOWN @_ unless $^S };
    41 
    39 
    42 exit main @ARGV;
    40 exit main @ARGV if not caller;
    43 
    41 
    44 #----
    42 #----
    45 
    43 
    46 sub main {
    44 sub main {
    47     my @opt_ignore;
    45     my @opt_exclude;
       
    46     my @opt_include;
    48     my $opt_verbose = 0;
    47     my $opt_verbose = 0;
    49 
    48 
    50     GetOptions(
    49     GetOptions(
    51         'i|ignore=s@' => \@opt_ignore,
    50         'e|x|exclude=s@' => \@opt_exclude,
    52         'h|help'      => sub { pod2usage(-verbose => 1, -exit => 0) },
    51         'i|include=s@'   => \@opt_include,
    53         'm|man'       => sub { pod2usage(-verbose => 2, -exit => 0) },
    52         'h|help'         => sub { pod2usage(-verbose => 1, -exit => 0) },
    54         'v|verbose'   => \$opt_verbose,
    53         'm|man'          => sub { pod2usage(-verbose => 2, -exit => 0) },
       
    54         'v|verbose'      => \$opt_verbose,
    55     ) or pod2usage;
    55     ) or pod2usage;
    56 
    56 
    57     *::verbose = $opt_verbose ? sub { say '# ', @_ } : sub { };
    57     *::verbose = $opt_verbose ? sub { say '# ', @_ } : sub { };
    58 
    58 
    59     # test needs to be run as root:* or as backup:backup
    59     # test needs to be run as root:* or as backup:backup
    62 
    62 
    63     # amservice needs to be suid root, but executable
    63     # amservice needs to be suid root, but executable
    64     # by the backup user/group
    64     # by the backup user/group
    65     verbose q{checking permissions for `amservice'};
    65     verbose q{checking permissions for `amservice'};
    66     eval { check_perms find_tool('amservice'), 04750, 'root', $) }
    66     eval { check_perms find_tool('amservice'), 04750, 'root', $) }
    67       or unknown $@;
    67       or UNKNOWN $@;
    68 
    68 
    69     # find the backup sets we know about
    69     # find the backup sets we know about
    70     # here we suppose that it's possible to find strings like
    70     # here we suppose that it's possible to find strings like
    71     # 'conf "foo"' in files named 'amanda-client.conf' below /etc/amanda
    71     # 'conf "foo"' in files named 'amanda-client.conf' below /etc/amanda
    72 
    72 
    73     verbose qq{find config names from $CFDIR};
    73     verbose qq{find config names from $CFDIR};
    74     my @confs = sort +unique eval { config_names $CFDIR }
    74     my @confs = sort +unique eval { config_names $CFDIR }
    75       or unknown $@;
    75       or UNKNOWN $@;
    76 
    76 
    77     eval { amchecks @confs } or critical $@;
    77     eval { amchecks @confs } or CRITICAL $@;
    78 
    78 
    79     my @dles = eval { compare_lists confs => \@confs, ignore => \@opt_ignore }
    79     my @dles = eval {
    80       or critical $@;
    80         compare_lists
    81     ok 'config: ' . join(', ', @confs), @dles;
    81           confs   => \@confs,
       
    82           exclude => \@opt_exclude,
       
    83           include => \@opt_include;
       
    84     }
       
    85       or CRITICAL $@;
       
    86     OK 'config: ' . join(', ', @confs), @dles;
    82 
    87 
    83     # never reached
    88     # never reached
    84     return 0;
    89     return 0;
    85 }
    90 }
    86 
    91 
   152     my $dir     = shift;
   157     my $dir     = shift;
   153     my @configs = ();
   158     my @configs = ();
   154     find(
   159     find(
   155         sub {
   160         sub {
   156             -f and /^amanda-client\.conf$/ or return;
   161             -f and /^amanda-client\.conf$/ or return;
   157             open(my $fh, '<', $_) or die "Can't open  $File::Find::name: $!\n";
   162             open(my $fh, '<', $_)
       
   163               or die "Can't open  $File::Find::name: $!\n";
   158             push @configs, map { /^conf\s+"(.+?)"/ ? $1 : () } <$fh>;
   164             push @configs, map { /^conf\s+"(.+?)"/ ? $1 : () } <$fh>;
   159         },
   165         },
   160         $dir
   166         $dir
   161     );
   167     );
   162 
   168 
   210     chomp((undef, my @dles) = qx(amdump_client --config '$conf' list));
   216     chomp((undef, my @dles) = qx(amdump_client --config '$conf' list));
   211     return map { [$_, (stat $_)[1] == 2 ? (stat $_)[0] : -1] } @dles;
   217     return map { [$_, (stat $_)[1] == 2 ? (stat $_)[0] : -1] } @dles;
   212 }
   218 }
   213 
   219 
   214 sub compare_lists {
   220 sub compare_lists {
   215     my %arg    = @_;
   221     my %arg     = @_;
   216     my @confs  = @{ $arg{confs} } or croak 'missing list of confs';
   222     my @confs   = @{ $arg{confs} } or croak 'missing list of confs';
   217     my @ignore = @{ $arg{ignore} };
   223     my @exclude = @{ $arg{exclude} };
   218 
   224     my @include = @{ $arg{include} };
   219     warning
   225 
   220       "ignored filesystem(s) @$_ does not exist, update the config please!\n"
   226     WARNING
   221       if @ignore and @$_ = grep { not -e } unique map { /^.*?:(.*)/ } @ignore;
   227       "excluded filesystem(s) @$_ does not exist, update the config please!\n"
   222 
   228       if @exclude
   223     my @candidates = get_devices;
   229           and @$_ = grep { not -e } unique map { /^.*?:(.*)/ } @exclude;
       
   230 
       
   231     my @candidates = (get_devices, @include);
   224     my %missing;
   232     my %missing;
   225 
   233 
   226     foreach my $conf (@confs) {
   234     foreach my $conf (@confs) {
   227         my @dles = _amlist $conf;
   235         my @dles = _amlist $conf;
   228         foreach my $candidate (@candidates) {
   236         foreach my $candidate (@candidates) {
       
   237             next if not $candidate =~ m{^/|\Q$conf\E:};
   229 
   238 
   230             # we're satisfied if either the name of the device is in
   239             # we're satisfied if either the name of the device is in
   231             # the disklist, or the device id is found
   240             # the disklist, or the device id is found
   232             $candidate->[0] ~~ [map { $_->[0] } @dles] and next;
   241             $candidate->[0] ~~ [map { $_->[0] } @dles] and next;
   233             $candidate->[1] ~~ [map { $_->[1] } @dles] and next;
   242             $candidate->[1] ~~ [map { $_->[1] } @dles] and next;
   234             push @{ $missing{$conf} }, $candidate->[0]
   243             push @{ $missing{$conf} }, $candidate->[0]
   235               if not "$conf:$candidate->[0]" ~~ @ignore;
   244               if not "$conf:$candidate->[0]" ~~ @exclude;
   236         }
   245         }
   237     }
   246     }
   238     die map { "$_ missing: " . join(', ' => @{ $missing{$_} }) . "\n" }
   247     die map { "$_ missing: " . join(', ' => @{ $missing{$_} }) . "\n" }
   239       keys %missing
   248       keys %missing
   240       if %missing;
   249       if %missing;
   241 
   250 
   242     return map { $_->[0] } @candidates;
   251     return map { $_->[0] } @candidates;
   243 }
   252 }
   244 
   253 
   245 sub ok { say "$NAME OK\n", join "\n" => @_; exit 0 }
   254 sub OK { say "$NAME OK\n", join "\n" => @_; exit 0 }
   246 sub warning  { print "$NAME WARNING\n",  join "\n" => @_; exit 1 }
   255 sub WARNING  { print "$NAME WARNING\n",  join "\n" => @_; exit 1 }
   247 sub critical { print "$NAME CRITICAL\n", join "\n" => @_; exit 2 }
   256 sub CRITICAL { print "$NAME CRITICAL\n", join "\n" => @_; exit 2 }
   248 sub unknown  { print "$NAME UNKNOWN\n",  join "\n" => @_; exit 3 }
   257 sub UNKNOWN  { print "$NAME UNKNOWN\n",  join "\n" => @_; exit 3 }
       
   258 
       
   259 1;
   249 
   260 
   250 __END__
   261 __END__
   251 
   262 
   252 =pod
   263 =pod
   253 
   264 
   266 
   277 
   267 =head1 OPTIONS
   278 =head1 OPTIONS
   268 
   279 
   269 =over
   280 =over
   270 
   281 
   271 =item B<-i>|B<--ignore> I<config:filesystem>
   282 =item B<-x>|B<--exclude> [I<config:>]I<filesystem>
   272 
   283 
   273 The name of a filesystem to be ignored. Example:
   284 The name of a filesystem to be excluded. 
   274 
   285 No config means all configs.
   275     check_amanda-client --ignore weekly:/var/spool/squid --ignore daily:/boot
   286 
       
   287     check_amanda-client --exclude weekly:/var/spool/squid --exclude /boot
       
   288 
       
   289 
       
   290 =item B<-i>|B<--include> [I<config>:]I<filesystem>
       
   291 
       
   292 Which filesystems/directories we additionally want to check.
       
   293 No config means all configs.
       
   294 
       
   295     check_amanda-client --include weekly:/etc
       
   296 
       
   297 If the directory is contained already in another directory/filesystem,
       
   298 the test is satisfied.
   276 
   299 
   277 =item B<-h>|B<--help>
   300 =item B<-h>|B<--help>
   278 
   301 
   279 Show a short description and help text.
   302 Show a short description and help text.
   280 
   303