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 |
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 |