source: people/joerg.steffens/technical/ldap-stats/ldap-stats.pl.orig@ 1236

Last change on this file since 1236 was 1145, checked in by joergs, on Apr 17, 2013 at 1:03:03 PM

initial, multi date formats

  • Property svn:executable set to *
File size: 42.2 KB
Line 
1#!/usr/bin/env perl
2#
3# Program: Generate LDAP Statistics Reports <ldap-stats.pl>
4#
5# Source code home: http://prefetch.net/code/ldap-stats.pl
6#
7# Author: Matty < matty91 @ gmail dot com >
8#
9# Current Version: 5.2
10#
11# Revision History:
12#
13# Version 5.2
14# Perl::Tidy and Perl::Critic -- Gavin Henry, Suretec Systems Ltd.
15#
16# Version 5.1
17# - Changed the location of the uc() statement -- Quanah Gibson-Mount
18#
19# Version 5.0
20# - Changed reporting structure to be dynamic -- Quanah Gibson-Mount
21# - Fixed a bug with name resolution -- Quanah Gibson-Mount
22# - Added the URL to the script -- Quanah Gibson-Mount
23#
24# Version 4.2
25# - Utilize strict mode -- Peter Schober
26#
27# Version 4.1
28# - Fixed a typo in the length() function -- Peter Schober
29#
30# Version 4.0
31# - Added "-d" option to print all days
32# - Fixed day sort order
33# - Added "-m" option to print all months
34# - Fixed month sort order
35# - Correct spelling. -- Dave Horsfall
36# - Align headings. -- Dave Horsfall
37# - Support ldapi:// connections ("LOCAL-SOCKET"). -- Dave Horsfall
38# - Only do lookup if numeric IP. -- Dave Horsfall
39#
40# Version 3.0 - 3.4
41# - Added ability to resolve IP addresses to hostnames with "-n" option
42# - Adjusted print() routines to limit lines to 80-characters -- Dave Horsfall
43# - Clean up unnecessary (..) in regexes -- Peter Marschall
44# - Split attributes found in searches (controlled by new option -s) -- Peter Marschall
45# - Added report to print which filters are used
46# - Added report to print explicit attributes requested -- Francis Swasey
47# - Fix usage: correct line break, all lines < 80 chars -- Peter Marschall
48# - Replace unnecessary printf() by print -- Peter Marschall
49# - Concatenate arguments into one call to print instead of multiple calls -- Peter Marschall
50# - Adapt underlining of some headers to length of logfile / date -- Peter Marschall
51# - Added additional checks to address missing entries during logfile rotation
52# - Fixed "uninitialized value in hash element" -- Todd Lyons
53# - Added additional comments to code
54# - Added report for operations by time of day
55# - Added report for operations per day
56# - Added report for operations per month
57# - Removed debug statements to speedup logfile processing
58# - Changed printf() format specifiers to match column definitions
59#
60# Version 2.0 - 2.2
61# - Adjusted the Search base comparison to catch ""
62# - Translate "" to RootDSE in the search base results
63# - Only print "Unindexed attribute" if unindexed attributes exist
64# - Normalize the bind DN and search base to avoid duplicates
65# - Fix typo with binddn array
66# - Improved filter for anonymous and authenticated binds -- Peter Marschall
67# - Logfiles are now passed as arguments to ldap-stats.pl
68# (e.g, ldap-stats.pl openldap1 openldap2 openldap3 old* ) -- Peter Marschall
69# - Cleaned up and combined filters for ADDs, MODs, DELs -- Peter Marschall
70# - Added support for CMPs & MODRDNs -- Peter Marschall
71# - Reduced number of regular expressions to one per filter -- Peter Marschall
72# - Removed head and tail program requirements, as dates are read on the fly from the
73# decoded logfile -- Peter Marschall
74# - Support for gzip and bzip2 compressed files -- Peter Marschall
75# - Optimized some expressions -- Peter Marschall
76# - Removed several Perl warnings, and added "-w" to default runtime options -- Peter Marschall
77# - Support for regular expressions in logfile names (e.g., ldap-stats.pl /var/log/openldap* ) -- Peter Marschall
78# - Changed default Perl interpreter to /usr/bin/perl
79# - Changed to OpenLDAP license
80#
81# Version 1.1 - 1.9
82# - Updated the bind, binddn, search, search base, and unindexed search regexs to
83# match a wider array of characters -- added by Peter Marschall
84# - Shortened several regular expressions by replacing "[0-9]" with "\d" -- added by Peter Marschall
85# - Fixed a divide by zero bug when logfiles contain 0 connections -- added by Dave Horsfall
86# - Removed unnecessary file open(s)
87# - Removed end of line ($) character from anonymous BIND regular expressions
88# - Added "-l" option to print lines as they are processed from a logfile
89# - Updated documentation
90# - Updated formatting of search dn report
91# - Updated formatting of search base report
92# - Added an additional report with the number of binds per DN
93# - Updated examples
94# - Added additional debug messages to connection setup
95# - Fixed documentation issues
96# - Added debugging flag (-d) to give detailed information on logfile processing
97# - Added "usage" subroutine to ease option maintenance
98# - Fixed a bug in the BIND calculations -- found and fixed by Quanah Gibson-Mount
99# - Fixed a bug in the MOD calculations -- found and fixed by Quanah Gibson-Mount
100# - Fixed a bug in the SRCH calculations -- found and fixed by Quanah Gibson-Mount
101# - Added a connection associative array to coorelate conn identifiers w/hosts -- Quanah Gibson-Mount
102# - Updated the usage message with information on "-c" option
103# - The "-f" option now accepts multiple logfiles
104# - Changed the headers to include information on all logfiles processed
105# - Added the day the report was run to the report headers
106#
107# Version 1.0
108# Original release
109#
110# Last Updated: 13-11-2006
111#
112# Purpose:
113# Produces numerous reports from OpenLDAP 2.1, 2.2 and 2.3 logfiles.
114#
115# License:
116#
117# Redistribution and use in source and binary forms, with or without
118# modification, are permitted only as authorized by the OpenLDAP
119# Public License.
120#
121# A copy of this license is available in the file LICENSE in the
122# top-level directory of the distribution or, alternatively, at
123# <http://www.OpenLDAP.org/license.html>.
124#
125# Installation:
126# 1. Enable a minimum of 'loglevel 256' in the slapd.conf configuration file.
127# 2. Copy the shell script to a suitable location.
128# 3. Refer to the usage section for options and examples.
129#
130# Usage:
131# Refer to the usage subroutine,
132#
133# Example:
134# Refer to http://prefetch.net/code/ldap-stats.pl.txt to see sample output
135
136use strict;
137use warnings;
138use Getopt::Long;
139use Socket;
140use Carp;
141use 5.006; # As returned by Perl::MinimumVersion
142
143#######################
144### usage subroutine
145### Parameters: None
146#######################
147sub usage {
148 print
149"Usage: ldap-stats.pl [ -s ] [ -c <count> ] [ -l <count> ] [ -h ] <logfile> ...\n"
150 . " -c <count> Number of lines to display for each report [25]\n"
151 . " -d Display all available days in the day of month report\n"
152 . " -h Display a usage help screen\n"
153 . " -l <count> Print status message after processing <count> lines [0]\n"
154 . " -m Display all available months in the month of year report\n"
155 . " -n Resolve IP addresses to hostnames\n"
156 . " -o <ops> -o <ops> ... Operations to print in the reports [ALL]\n"
157 . " Valid operations are: CONNECT, FAILURES, BIND, UNBIND,\n"
158 . " SRCH, CMP, ADD, MOD, MODRDN, DEL\n"
159 . " Predefined reports are: ALL, READ, WRITE\n"
160 . " -s Split attributes found used in searches\n";
161 return;
162}
163
164### Declare lexical variables
165my ( $logfile, $i, $counter, $help );
166my ( %unindexed, %search, @operations );
167
168### Allow the number of entries displayed to be variable
169my $count = 25;
170
171### Figure out if we need to print "Processing X lines"
172my $increment = 0;
173
174## tell whether to split attributes in searches
175my $splitattrs = 0;
176
177# Tell whether to lookup names
178my $resolvename = 0;
179
180# Print all months
181my $printmonths = 0;
182
183# Print all days
184my $printdays = 0;
185
186###################################
187#### Get some options from the user
188###################################
189#getopts("o:l:c:nhsmd", \%options);
190
191GetOptions(
192 'count|c=i' => \$count,
193 'days|d' => \$printdays,
194 'help|h' => \$help,
195 'length|l=i' => \$increment,
196 'months|m' => \$printmonths,
197 'network|n' => \$resolvename,
198 'operations|o=s' => \@operations,
199 'split|s' => \$splitattrs
200);
201
202### print a nice usage message
203if ($help) {
204 usage;
205 exit 1;
206}
207
208### Make sure there is at least one logfile
209if ( !@ARGV ) {
210 usage;
211 exit 1;
212}
213
214############################
215### Define various variables
216############################
217my $date = localtime time;
218
219if ( !@operations ) {
220 @operations = ('ALL');
221}
222
223my %stats = (
224 TOTAL_CONNECT => 0,
225 TOTAL_BIND => 0,
226 TOTAL_UNBIND => 0,
227 TOTAL_SRCH => 0,
228 TOTAL_DEL => 0,
229 TOTAL_ADD => 0,
230 TOTAL_CMP => 0,
231 TOTAL_MOD => 0,
232 TOTAL_MODRDN => 0,
233 TOTAL_UNINDEXED => 0,
234 TOTAL_AUTHFAILURES => 0,
235);
236
237my %hours; # Hash to store the time of day (e.g., 21st of August)
238my %days; # Hash to store the days of each month (e.g., 21st)
239my %months; # Hash to store the day of the month (e.g., Dec)
240my %hosts; # Hash to store client IP addresses
241my %conns; # Hash to store connection identifiers
242my %binddns; # Hash to store bind DNs
243my %logarray; # Hash to store logfiles
244my %filters; # Hash to store search filters
245my %searchattributes; # Hash to store specific attributes that are requested
246my %operations; # Hash to store operations information
247
248$operations{CONNECT} = {
249 DATA => 0,
250 STRING => ' Connect',
251 SPACING => ' --------',
252 FIELD => '%8s',
253};
254
255$operations{FAILURES} = {
256 DATA => 0,
257 STRING => ' Failed',
258 SPACING => ' ------',
259 FIELD => '%6s',
260};
261
262$operations{BIND} = {
263 DATA => 0,
264 STRING => ' Bind',
265 SPACING => ' -------',
266 FIELD => '%7s',
267};
268
269$operations{UNBIND} = {
270 DATA => 0,
271 STRING => ' Unbind',
272 SPACING => ' -------',
273 FIELD => '%7s',
274};
275
276$operations{SRCH} = {
277 DATA => 0,
278 STRING => ' Search',
279 SPACING => ' --------',
280 FIELD => '%8s',
281};
282
283$operations{ADD} = {
284 DATA => 0,
285 STRING => ' Add',
286 SPACING => ' -----',
287 FIELD => '%5s',
288};
289
290$operations{CMP} = {
291 DATA => 0,
292 STRING => ' Cmp',
293 SPACING => ' -----',
294 FIELD => '%5s',
295};
296
297$operations{MOD} = {
298 DATA => 0,
299 STRING => ' Mod',
300 SPACING => ' -----',
301 FIELD => '%5s',
302};
303
304$operations{MODRDN} = {
305 DATA => 0,
306 STRING => ' ModRDN',
307 SPACING => ' ------',
308 FIELD => '%6s',
309};
310
311$operations{DEL} = {
312 DATA => 0,
313 STRING => ' Del',
314 SPACING => ' ----',
315 FIELD => '%4s',
316};
317
318###################################################
319### Open the logfile and process all of the entries
320###################################################
321for my $file (@ARGV) {
322 $logfile = $file;
323 my $lines = 0;
324
325 ### find open filter to use
326 my $openfilter = '<' . $logfile . q{};
327
328 ### decode gzipped / bzip2-compressed files
329 if ( $logfile =~ /\.bz2$/mx ) {
330 $openfilter = q{bzip2 -dc "} . $logfile . q{"|}
331 or carp "Problem decompressing!: $!\n";
332 }
333
334 if ( $logfile =~ /\.(gz|Z)$/mx ) {
335 $openfilter = q{gzip -dc "} . $logfile . q{"|}
336 or carp "Problem decompressing!: $!\n";
337 }
338
339 ### If the logfile isn't valid, move on to the next one
340 if ( !open LOGFILE, $openfilter ) {
341 print "ERROR: unable to open '$logfile': $!\n";
342 next;
343 }
344
345 ### setup the arrray to hold the start/stop times
346 $logarray{$logfile} = {
347 SDATE => q{},
348 EDATE => q{},
349 };
350
351 ### Only print banner if requested
352 if ( $increment > 0 ) {
353 ### Print a banner and initialize the $counter variable
354 print "\nProcessing file \"$logfile\"\n"
355 . q{-} x ( 18 + length ${$logfile} ) . "\n";
356 $counter = 0;
357 $lines = $increment;
358 }
359
360 while ( my $line = <LOGFILE> ) {
361
362 ### check start and end dates
363 if ( $line =~ /^(\w+\s+\d+\s+\d+:\d+:\d+)/mx ) {
364 if ( !$logarray{$logfile}{SDATE} ) {
365 $logarray{$logfile}{SDATE} = $1;
366 }
367 $logarray{$logfile}{EDATE} = $1;
368 }
369
370 ### Check to see if we have processed $lines lines
371 if ( ( $lines > 0 ) && ( $counter == $lines ) ) {
372 print " Processed $lines lines in \"$logfile\"\n";
373 $lines += $increment;
374 }
375
376 ### Check for a new connection
377 if ( $line =~
378/^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+).*conn=(\d+) [ ] fd=\d+ [ ] (?:ACCEPT|connection) [ ] from/mx
379 )
380 {
381 my $month = $1;
382 my $day = $2;
383 my $hour = $3;
384 my $conn = $6;
385 my $host;
386
387 if ( $line =~ /IP=(\d+\.\d+\.\d+\.\d+):/mx ) {
388 $host = $1;
389 }
390 elsif ( $line =~ /PATH=(\S+)/mx ) {
391 $host = 'LOCAL-SOCKET';
392 }
393 else {
394 $host = 'UNKNOWN';
395 }
396
397 ### Create an array to store the list of hosts
398 if ( !( defined $hosts{$host} ) ) {
399 $hosts{$host} = {
400 CONNECT => 1,
401 AUTHFAILURES => 0,
402 BIND => 0,
403 UNBIND => 0,
404 SRCH => 0,
405 ADD => 0,
406 CMP => 0,
407 MOD => 0,
408 MODRDN => 0,
409 DEL => 0,
410 };
411 }
412 else {
413 ### Entry exists, increment the CONNECT value
414 $hosts{$host}{CONNECT}++;
415 }
416
417 ### Create an array to store the hours
418 if ( !( defined $hours{$hour} ) ) {
419 $hours{$hour} = {
420 CONNECT => 1,
421 AUTHFAILURES => 0,
422 BIND => 0,
423 UNBIND => 0,
424 SRCH => 0,
425 ADD => 0,
426 CMP => 0,
427 MOD => 0,
428 MODRDN => 0,
429 DEL => 0,
430 };
431 }
432 else {
433 ### Entry exists, increment the CONNECT value
434 $hours{$hour}{CONNECT}++;
435 }
436
437 ### Create an array to store the months
438 if ( !( defined $months{$month} ) ) {
439 $months{$month} = {
440 CONNECT => 1,
441 AUTHFAILURES => 0,
442 BIND => 0,
443 UNBIND => 0,
444 SRCH => 0,
445 ADD => 0,
446 CMP => 0,
447 MOD => 0,
448 MODRDN => 0,
449 DEL => 0,
450 };
451 }
452 else {
453 ### Entry exists, increment the CONNECT value
454 $months{$month}{CONNECT}++;
455 }
456
457 ### Create an array to store the days
458 if ( !( defined $days{$day} ) ) {
459 $days{$day} = {
460 CONNECT => 1,
461 AUTHFAILURES => 0,
462 BIND => 0,
463 UNBIND => 0,
464 SRCH => 0,
465 ADD => 0,
466 CMP => 0,
467 MOD => 0,
468 MODRDN => 0,
469 DEL => 0,
470 };
471 }
472 else {
473 ### Entry exists, increment the CONNECT value
474 $days{$day}{CONNECT}++;
475 }
476
477 ### Add the host to the connection table
478 $conns{$conn} = $host;
479
480 ### Increment the total number of connections
481 $stats{TOTAL_CONNECT}++;
482
483 ### Check for anonymous binds
484 }
485 elsif ( $line =~
486/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] BIND [ ] dn="" [ ] method=128/mx
487 )
488 {
489 my $month = $1;
490 my $day = $2;
491 my $hour = $3;
492 my $conn = $4;
493
494 ### Increment the counters
495 if ( defined $conns{$conn}
496 && defined $hosts{ $conns{$conn} } )
497 {
498 $hosts{ $conns{$conn} }{BIND}++;
499 $hours{$hour}{BIND}++;
500 $days{$day}{BIND}++;
501 $months{$month}{BIND}++;
502 $stats{TOTAL_BIND}++;
503 }
504
505 ### Add the binddn to the binddns array
506 $binddns{anonymous}++;
507
508 ### Check for non-anonymous binds
509 }
510 elsif ( $line =~
511/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] BIND [ ] dn="([^"]+)" [ ] mech=/mx
512 )
513 {
514 my $month = $1;
515 my $day = $2;
516 my $hour = $3;
517 my $conn = $4;
518 my $binddn = lc $5;
519
520 ### Increment the counters
521 if ( defined $conns{$conn}
522 && defined $hosts{ $conns{$conn} } )
523 {
524 $hosts{ $conns{$conn} }{BIND}++;
525 $hours{$hour}{BIND}++;
526 $days{$day}{BIND}++;
527 $months{$month}{BIND}++;
528 $stats{TOTAL_BIND}++;
529 }
530
531 ### Add the binddn to the binddns array
532 $binddns{$binddn}++;
533
534 ### Check the search base
535 }
536 elsif ( $line =~
537/\bconn=\d+ [ ] op=\d+ [ ] SRCH [ ] base="([^"]*?)" [ ] .*filter="([^"]*?)"/mx
538 )
539 {
540 my $base = lc $1;
541 my $filter = $2;
542
543 ### Stuff the search base into an array
544 if ( defined $base ) {
545 $search{$base}++;
546 }
547
548 if ( defined $filter ) {
549 $filters{$filter}++;
550 }
551
552 ### Check for search attributes
553 }
554 elsif ( $line =~ /\bconn=\d+ [ ] op=\d+ [ ] SRCH [ ] attr=(.+)/mx ) {
555 my $attrs = lc $1;
556
557 if ($splitattrs) {
558 for my $attr ( split q{ }, $attrs ) {
559 $searchattributes{$attr}++;
560 }
561 }
562 else {
563 $searchattributes{$attrs}++;
564 }
565
566 ### Check for SEARCHES
567 }
568 elsif ( $line =~
569/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] SEARCH [ ] RESULT/mx
570 )
571 {
572 my $month = $1;
573 my $day = $2;
574 my $hour = $3;
575 my $conn = $4;
576
577 ### Increment the counters
578 if ( defined $conns{$conn}
579 && defined $hosts{ $conns{$conn} } )
580 {
581 $hosts{ $conns{$conn} }{SRCH}++;
582 $hours{$hour}{SRCH}++;
583 $days{$day}{SRCH}++;
584 $months{$month}{SRCH}++;
585 $stats{TOTAL_SRCH}++;
586 }
587
588 ### Check for unbinds
589 }
590 elsif ( $line =~
591 /^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] UNBIND/mx
592 )
593 {
594 my $month = $1;
595 my $day = $2;
596 my $hour = $3;
597 my $conn = $4;
598
599 ### Increment the counters
600 if ( defined $conns{$conn}
601 && defined $hosts{ $conns{$conn} } )
602 {
603 $hosts{ $conns{$conn} }{UNBIND}++;
604 $hours{$hour}{UNBIND}++;
605 $days{$day}{UNBIND}++;
606 $months{$month}{UNBIND}++;
607 $stats{TOTAL_UNBIND}++;
608 }
609
610 ### Check the result of the last operation
611 ### TODO: Add other err=X values from contrib/ldapc++/src/LDAPResult.h
612 }
613 elsif ( $line =~
614/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+(?: SEARCH)? [ ] RESULT [ ]/mx
615 )
616 {
617 my $month = $1;
618 my $day = $2;
619 my $hour = $3;
620 my $conn = $4;
621
622 if ( $line =~ /\berr=49\b/mx ) {
623 ### Increment the counters
624 if ( defined $conns{$conn}
625 && defined $hosts{ $conns{$conn} } )
626 {
627 $hosts{ $conns{$conn} }{AUTHFAILURES}++;
628 $hours{$hour}{AUTHFAILURES}++;
629 $days{$day}{AUTHFAILURES}++;
630 $months{$month}{AUTHFAILURES}++;
631 $stats{TOTAL_AUTHFAILURES}++;
632 }
633 }
634
635 ### Check for entry changes: add, modify modrdn, delete
636 }
637 elsif ( $line =~
638/^(\w+)\s+(\d+)\s+(\d+):\d+:\d+.*conn=(\d+) [ ] op=\d+ [ ] (ADD|CMP|MOD|MODRDN|DEL) [ ] dn=/mx
639 )
640 {
641 my $month = $1;
642 my $day = $2;
643 my $hour = $3;
644 my $conn = $4;
645 my $type = $5;
646
647 ### Increment the counters
648 if ( defined $conns{$conn}
649 && defined $hosts{ $conns{$conn} } )
650 {
651 $hosts{ $conns{$conn} }{$type}++;
652 $hours{$hour}{$type}++;
653 $days{$day}{$type}++;
654 $months{$month}{$type}++;
655 $stats{ 'TOTAL_' . $type }++;
656 }
657
658 ### Check for unindexed searches
659 }
660 elsif ( $line =~
661 /: [ ] \(([a-zA-Z0-9\;\-]+)\) [ ] index_param [ ] failed/mx )
662 {
663 my $attr = $1;
664
665 $unindexed{$attr}++;
666 $stats{TOTAL_UNINDEXED}++;
667 }
668 $counter++;
669 }
670 close LOGFILE;
671}
672
673###################################################################
674### Print a nice header with the logfiles and date ranges processed
675###################################################################
676## Please see file perltidy.ERR
677print "\n\n"
678 . "Report Generated on $date\n"
679 . q{-} x ( 20 + length $date ) . "\n";
680
681for my $logfile ( sort keys %logarray ) {
682 if ( !-z $logfile ) {
683 printf "Processed \"$logfile\": %s - %s\n", $logarray{$logfile}{SDATE},
684 $logarray{$logfile}{EDATE};
685 }
686 else {
687 printf "Processed \"$logfile\": no data\n";
688 }
689}
690
691#######################################
692### Print an overall report with totals
693#######################################
694
695my $total_operations =
696 $stats{TOTAL_BIND} + $stats{TOTAL_UNBIND} + $stats{TOTAL_SRCH} +
697 $stats{TOTAL_MOD} + $stats{TOTAL_ADD} + $stats{TOTAL_MODRDN} +
698 $stats{TOTAL_DEL};
699
700print "\n\n" . "Operation totals\n" . "----------------\n";
701printf "Total operations : %d\n", $total_operations;
702printf "Total connections : %d\n", $stats{TOTAL_CONNECT};
703printf "Total authentication failures : %d\n", $stats{TOTAL_AUTHFAILURES};
704printf "Total binds : %d\n", $stats{TOTAL_BIND};
705printf "Total unbinds : %d\n", $stats{TOTAL_UNBIND};
706printf "Total searches : %d\n", $stats{TOTAL_SRCH};
707printf "Total compares : %d\n", $stats{TOTAL_CMP};
708printf "Total modifications : %d\n", $stats{TOTAL_MOD};
709printf "Total modrdns : %d\n", $stats{TOTAL_MODRDN};
710printf "Total additions : %d\n", $stats{TOTAL_ADD};
711printf "Total deletions : %d\n", $stats{TOTAL_DEL};
712printf "Unindexed attribute requests : %d\n", $stats{TOTAL_UNINDEXED};
713printf "Operations per connection : %.2f\n",
714 $stats{TOTAL_CONNECT} ? $total_operations / $stats{TOTAL_CONNECT} : 0;
715
716###################################################
717### Process the host information and print a report
718###################################################
719for my $selected (@operations) {
720 $selected = uc $selected;
721
722 my $ops_ref = {
723 CONNECT => sub { $operations{CONNECT}{DATA} = 1 },
724 FAILURES => sub { $operations{FAILURES}{DATA} = 1 },
725 BIND => sub { $operations{BIND}{DATA} = 1 },
726 UNBIND => sub { $operations{UNBIND}{DATA} = 1 },
727 SRCH => sub { $operations{SRCH}{DATA} = 1 },
728 CMP => sub { $operations{CMP}{DATA} = 1 },
729 ADD => sub { $operations{ADD}{DATA} = 1 },
730 MOD => sub { $operations{MOD}{DATA} = 1 },
731 MODRDN => sub { $operations{MODRDN}{DATA} = 1 },
732 DEL => sub { $operations{DEL}{DATA} = 1 },
733 ALL => sub {
734 $operations{CONNECT}{DATA} = 1;
735 $operations{FAILURES}{DATA} = 1;
736 $operations{BIND}{DATA} = 1;
737 $operations{UNBIND}{DATA} = 1;
738 $operations{SRCH}{DATA} = 1;
739 $operations{CMP}{DATA} = 1;
740 $operations{ADD}{DATA} = 1;
741 $operations{MOD}{DATA} = 1;
742 $operations{MODRDN}{DATA} = 1;
743 $operations{DEL}{DATA} = 1;
744 },
745 READ => sub {
746 $operations{CONNECT}{DATA} = 1;
747 $operations{BIND}{DATA} = 1;
748 $operations{UNBIND}{DATA} = 1;
749 $operations{SRCH}{DATA} = 1;
750 $operations{CMP}{DATA} = 1;
751 },
752 WRITE => sub {
753 $operations{CONNECT}{DATA} = 1;
754 $operations{BIND}{DATA} = 1;
755 $operations{UNBIND}{DATA} = 1;
756 $operations{ADD}{DATA} = 1;
757 $operations{MOD}{DATA} = 1;
758 $operations{MODRDN}{DATA} = 1;
759 $operations{DEL}{DATA} = 1;
760 },
761 };
762 if ( $ops_ref->{$selected} ) { $ops_ref->{$selected}->() }
763 else { croak "Unknown operation: '$selected';\n" }
764}
765
766print "\n\n";
767my $printstr = 'Hostname ';
768$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{};
769$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{};
770$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{};
771$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{};
772$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{};
773$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{};
774$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{};
775$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{};
776$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{};
777$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{};
778$printstr .= "\n";
779print $printstr;
780$printstr = '---------------';
781$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{};
782$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{};
783$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{};
784$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{};
785$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{};
786$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{};
787$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{};
788$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{};
789$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{};
790$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{};
791print "$printstr\n";
792
793for my $index ( sort keys %hosts ) {
794
795 ### Resolve IP addresses to names if requested
796 my $host = $index;
797
798 ### Convert the IP address to an Internet address, and resolve with gethostbyaddr()
799 if ( $resolvename && ( $index =~ /\d+\.\d+\.\d+\.\d+/mx ) ) {
800 my $ipaddr = inet_aton($index);
801 $host = gethostbyaddr $ipaddr, AF_INET;
802 if ( !defined $host ) {
803 $host = $index;
804 }
805 }
806 printf '%-15.15s', $host;
807 if ( $operations{CONNECT}{DATA} ) {
808 printf " $operations{CONNECT}{FIELD}",
809 $hosts{$index}{CONNECT} ? $hosts{$index}{CONNECT} : 0;
810 }
811 if ( $operations{FAILURES}{DATA} ) {
812 printf " $operations{FAILURES}{FIELD}",
813 $hosts{$index}{AUTHFAILURES} ? $hosts{$index}{AUTHFAILURES} : 0;
814 }
815 if ( $operations{BIND}{DATA} ) {
816 printf " $operations{BIND}{FIELD}",
817 $hosts{$index}{BIND} ? $hosts{$index}{BIND} : 0;
818 }
819 if ( $operations{UNBIND}{DATA} ) {
820 printf " $operations{UNBIND}{FIELD}",
821 $hosts{$index}{UNBIND} ? $hosts{$index}{UNBIND} : 0;
822 }
823 if ( $operations{SRCH}{DATA} ) {
824 printf " $operations{SRCH}{FIELD}",
825 $hosts{$index}{SRCH} ? $hosts{$index}{SRCH} : 0;
826 }
827 if ( $operations{CMP}{DATA} ) {
828 printf " $operations{CMP}{FIELD}",
829 $hosts{$index}{CMP} ? $hosts{$index}{CMP} : 0;
830 }
831 if ( $operations{ADD}{DATA} ) {
832 printf " $operations{ADD}{FIELD}",
833 $hosts{$index}{ADD} ? $hosts{$index}{ADD} : 0;
834 }
835 if ( $operations{MOD}{DATA} ) {
836 printf " $operations{MOD}{FIELD}",
837 $hosts{$index}{MOD} ? $hosts{$index}{MOD} : 0;
838 }
839 if ( $operations{MODRDN}{DATA} ) {
840 printf " $operations{MODRDN}{FIELD}",
841 $hosts{$index}{MODRDN} ? $hosts{$index}{MODRDN} : 0;
842 }
843 if ( $operations{DEL}{DATA} ) {
844 printf " $operations{DEL}{FIELD}",
845 $hosts{$index}{DEL} ? $hosts{$index}{DEL} : 0;
846 }
847 print "\n";
848}
849
850#######################################################
851### Process the hours information and print a report
852########################################################
853print "\n\n";
854$printstr = 'Hour of Day ';
855$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{};
856$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{};
857$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{};
858$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{};
859$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{};
860$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{};
861$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{};
862$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{};
863$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{};
864$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{};
865$printstr .= "\n";
866print $printstr;
867$printstr = '-------------';
868$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{};
869$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{};
870$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{};
871$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{};
872$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{};
873$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{};
874$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{};
875$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{};
876$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{};
877$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{};
878print "$printstr\n";
879
880for my $index ( sort keys %hours ) {
881 printf '%-2s:00 - %2s:59', $index, $index;
882 if ( $operations{CONNECT}{DATA} ) {
883 printf " $operations{CONNECT}{FIELD}",
884 $hours{$index}{CONNECT} ? $hours{$index}{CONNECT} : 0;
885 }
886 if ( $operations{FAILURES}{DATA} ) {
887 printf " $operations{FAILURES}{FIELD}",
888 $hours{$index}{AUTHFAILURES} ? $hours{$index}{AUTHFAILURES} : 0;
889 }
890 if ( $operations{BIND}{DATA} ) {
891 printf " $operations{BIND}{FIELD}",
892 $hours{$index}{BIND} ? $hours{$index}{BIND} : 0;
893 }
894 if ( $operations{UNBIND}{DATA} ) {
895 printf " $operations{UNBIND}{FIELD}",
896 $hours{$index}{UNBIND} ? $hours{$index}{UNBIND} : 0;
897 }
898 if ( $operations{SRCH}{DATA} ) {
899 printf " $operations{SRCH}{FIELD}",
900 $hours{$index}{SRCH} ? $hours{$index}{SRCH} : 0;
901 }
902 if ( $operations{CMP}{DATA} ) {
903 printf " $operations{CMP}{FIELD}",
904 $hours{$index}{CMP} ? $hours{$index}{CMP} : 0;
905 }
906 if ( $operations{ADD}{DATA} ) {
907 printf " $operations{ADD}{FIELD}",
908 $hours{$index}{ADD} ? $hours{$index}{ADD} : 0;
909 }
910 if ( $operations{MOD}{DATA} ) {
911 printf " $operations{MOD}{FIELD}",
912 $hours{$index}{MOD} ? $hours{$index}{MOD} : 0;
913 }
914 if ( $operations{MODRDN}{DATA} ) {
915 printf " $operations{MODRDN}{FIELD}",
916 $hours{$index}{MODRDN} ? $hours{$index}{MODRDN} : 0;
917 }
918 if ( $operations{DEL}{DATA} ) {
919 printf " $operations{DEL}{FIELD}",
920 $hours{$index}{DEL} ? $hours{$index}{DEL} : 0;
921 }
922 print "\n";
923}
924
925#######################################################
926### Process the month information and print a report
927########################################################
928print "\n\n";
929$printstr = 'Day of Month ';
930$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{};
931$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{};
932$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{};
933$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{};
934$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{};
935$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{};
936$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{};
937$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{};
938$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{};
939$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{};
940$printstr .= "\n";
941print $printstr;
942$printstr = '-------------';
943$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{};
944$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{};
945$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{};
946$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{};
947$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{};
948$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{};
949$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{};
950$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{};
951$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{};
952$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{};
953print "$printstr\n";
954
955for ( 1 .. 31 ) {
956 if ( defined $days{$_} || $printdays ) {
957 printf ' %-11s', $_;
958 if ( $operations{CONNECT}{DATA} ) {
959 printf " $operations{CONNECT}{FIELD}",
960 $days{$_}{CONNECT} ? $days{$_}{CONNECT} : 0;
961 }
962 if ( $operations{FAILURES}{DATA} ) {
963 printf " $operations{FAILURES}{FIELD}",
964 $days{$_}{AUTHFAILURES} ? $days{$_}{AUTHFAILURES} : 0;
965 }
966 if ( $operations{BIND}{DATA} ) {
967 printf " $operations{BIND}{FIELD}",
968 $days{$_}{BIND} ? $days{$_}{BIND} : 0;
969 }
970 if ( $operations{UNBIND}{DATA} ) {
971 printf " $operations{UNBIND}{FIELD}",
972 $days{$_}{UNBIND} ? $days{$_}{UNBIND} : 0;
973 }
974 if ( $operations{SRCH}{DATA} ) {
975 printf " $operations{SRCH}{FIELD}",
976 $days{$_}{SRCH} ? $days{$_}{SRCH} : 0;
977 }
978 if ( $operations{CMP}{DATA} ) {
979 printf " $operations{CMP}{FIELD}",
980 $days{$_}{CMP} ? $days{$_}{CMP} : 0;
981 }
982 if ( $operations{ADD}{DATA} ) {
983 printf " $operations{ADD}{FIELD}",
984 $days{$_}{ADD} ? $days{$_}{ADD} : 0;
985 }
986 if ( $operations{MOD}{DATA} ) {
987 printf " $operations{MOD}{FIELD}",
988 $days{$_}{MOD} ? $days{$_}{MOD} : 0;
989 }
990 if ( $operations{MODRDN}{DATA} ) {
991 printf " $operations{MODRDN}{FIELD}",
992 $days{$_}{MODRDN} ? $days{$_}{MODRDN} : 0;
993 }
994 if ( $operations{DEL}{DATA} ) {
995 printf " $operations{DEL}{FIELD}",
996 $days{$_}{DEL} ? $days{$_}{DEL} : 0;
997 }
998 print "\n";
999 }
1000}
1001#######################################################
1002### Process the month information and print a report
1003########################################################
1004print "\n\n";
1005$printstr = ' Month ';
1006$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{STRING} : q{};
1007$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{STRING} : q{};
1008$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{STRING} : q{};
1009$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{STRING} : q{};
1010$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{STRING} : q{};
1011$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{STRING} : q{};
1012$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{STRING} : q{};
1013$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{STRING} : q{};
1014$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{STRING} : q{};
1015$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{STRING} : q{};
1016$printstr .= "\n";
1017print $printstr;
1018$printstr = '-------------';
1019$printstr .= $operations{CONNECT}{DATA} ? $operations{CONNECT}{SPACING} : q{};
1020$printstr .= $operations{FAILURES}{DATA} ? $operations{FAILURES}{SPACING} : q{};
1021$printstr .= $operations{BIND}{DATA} ? $operations{BIND}{SPACING} : q{};
1022$printstr .= $operations{UNBIND}{DATA} ? $operations{UNBIND}{SPACING} : q{};
1023$printstr .= $operations{SRCH}{DATA} ? $operations{SRCH}{SPACING} : q{};
1024$printstr .= $operations{CMP}{DATA} ? $operations{CMP}{SPACING} : q{};
1025$printstr .= $operations{ADD}{DATA} ? $operations{ADD}{SPACING} : q{};
1026$printstr .= $operations{MOD}{DATA} ? $operations{MOD}{SPACING} : q{};
1027$printstr .= $operations{MODRDN}{DATA} ? $operations{MODRDN}{SPACING} : q{};
1028$printstr .= $operations{DEL}{DATA} ? $operations{DEL}{SPACING} : q{};
1029print "$printstr\n";
1030
1031for my $index qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) {
1032 if ( defined $months{$index} || $printmonths ) {
1033 printf ' %-11s', $index;
1034 if ( $operations{CONNECT}{DATA} ) {
1035 printf " $operations{CONNECT}{FIELD}",
1036 $months{$index}{CONNECT} ? $months{$index}{CONNECT} : 0;
1037 }
1038 if ( $operations{FAILURES}{DATA} ) {
1039 printf " $operations{FAILURES}{FIELD}",
1040 $months{$index}{AUTHFAILURES}
1041 ? $months{$index}{AUTHFAILURES}
1042 : 0;
1043 }
1044 if ( $operations{BIND}{DATA} ) {
1045 printf " $operations{BIND}{FIELD}",
1046 $months{$index}{BIND} ? $months{$index}{BIND} : 0;
1047 }
1048 if ( $operations{UNBIND}{DATA} ) {
1049 printf " $operations{UNBIND}{FIELD}",
1050 $months{$index}{UNBIND} ? $months{$index}{UNBIND} : 0;
1051 }
1052 if ( $operations{SRCH}{DATA} ) {
1053 printf " $operations{SRCH}{FIELD}",
1054 $months{$index}{SRCH} ? $months{$index}{SRCH} : 0;
1055 }
1056 if ( $operations{CMP}{DATA} ) {
1057 printf " $operations{CMP}{FIELD}",
1058 $months{$index}{CMP} ? $months{$index}{CMP} : 0;
1059 }
1060 if ( $operations{ADD}{DATA} ) {
1061 printf " $operations{ADD}{FIELD}",
1062 $months{$index}{ADD} ? $months{$index}{ADD} : 0;
1063 }
1064 if ( $operations{MOD}{DATA} ) {
1065 printf " $operations{MOD}{FIELD}",
1066 $months{$index}{MOD} ? $months{$index}{MOD} : 0;
1067 }
1068 if ( $operations{MODRDN}{DATA} ) {
1069 printf " $operations{MODRDN}{FIELD}",
1070 $months{$index}{MODRDN} ? $months{$index}{MODRDN} : 0;
1071 }
1072 if ( $operations{DEL}{DATA} ) {
1073 printf " $operations{DEL}{FIELD}",
1074 $months{$index}{DEL} ? $months{$index}{DEL} : 0;
1075 }
1076 print "\n";
1077 }
1078}
1079
1080####################################################
1081### Process the unindexed searches and print a report
1082####################################################
1083my @sarray; # sort array
1084if ( $stats{TOTAL_UNINDEXED} > 0 ) {
1085
1086 print "\n\n"
1087 . "# Uses Unindexed attribute\n"
1088 . "---------- -----------------------------------------------------------\n";
1089
1090 @sarray =
1091 reverse sort { $unindexed{$a} <=> $unindexed{$b} } keys %unindexed;
1092 UNINDEXED:
1093 for my $num ( 0 .. $#sarray ) {
1094 if ( $num > $count ) {
1095 last UNINDEXED;
1096 }
1097 printf " %-8d %-60s\n", $unindexed{ $sarray[$num] }, $sarray[$num];
1098 }
1099}
1100
1101######################################################
1102### Process the stored search bases and print a report
1103######################################################
1104print "\n\n"
1105 . "# Searches Search base\n"
1106 . "---------- -----------------------------------------------------------\n";
1107
1108@sarray = reverse sort { $search{$a} <=> $search{$b} } keys %search;
1109SEARCH:
1110for my $num ( 0 .. $#sarray ) {
1111 if ( $num > $count ) {
1112 last SEARCH;
1113 }
1114 printf " %-8d %-60s\n", $search{ $sarray[$num] },
1115 $sarray[$num] || 'RootDSE';
1116}
1117
1118######################################################
1119### Process the stored search filters
1120######################################################
1121print "\n\n"
1122 . "# Uses Filter\n"
1123 . "---------- -----------------------------------------------------------\n";
1124
1125@sarray = reverse sort { $filters{$a} <=> $filters{$b} } keys %filters;
1126FILTER:
1127for my $num ( 0 .. $#sarray ) {
1128 if ( $num > $count ) {
1129 last FILTER;
1130 }
1131 printf " %-8d %-60s\n", $filters{ $sarray[$num] }, $sarray[$num];
1132}
1133
1134######################################################
1135### Process the stored attribute array
1136######################################################
1137print "\n\n"
1138 . "# Uses Attributes explicitly requested in search string\n"
1139 . "---------- -------------------------------------------------\n";
1140
1141@sarray =
1142 reverse sort { $searchattributes{$a} <=> $searchattributes{$b} }
1143 keys %searchattributes;
1144SEARCHATTR:
1145for my $num ( 0 .. $#sarray ) {
1146 if ( $num > $count ) {
1147 last SEARCHATTR;
1148 }
1149 printf " %-8d %-60s\n", $searchattributes{ $sarray[$num] },
1150 $sarray[$num];
1151}
1152
1153######################################################
1154### Process the stored binddns and print a report
1155######################################################
1156print "\n\n"
1157 . "# Binds Bind DN\n"
1158 . "---------- --------------------------------------------------------------\n";
1159
1160@sarray = reverse sort { $binddns{$a} <=> $binddns{$b} } keys %binddns;
1161BINDDN:
1162for my $num ( 0 .. $#sarray ) {
1163 if ( $num > $count ) {
1164 last BINDDN;
1165 }
1166 printf " %-8d %-60s\n", $binddns{ $sarray[$num] }, $sarray[$num];
1167}
1168
1169print "\n\n";
1170
1171# EOF
Note: See TracBrowser for help on using the repository browser.