File Coverage

blib/lib/Cisco/Accounting/Data.pm
Criterion Covered Total %
statement 9 117 7.6
branch 0 28 0.0
condition 0 8 0.0
subroutine 3 19 15.7
pod 0 12 0.0
total 12 184 6.5


line stmt bran cond sub pod time code
1             package Cisco::Accounting::Data;
2              
3             ## ----------------------------------------------------------------------------------------------
4             ## Cisco::Accounting::Data
5             ##
6             ## $Id: Data.pm 119 2007-08-18 21:36:42Z mwallraf $
7             ## $Author: mwallraf $
8             ## $Date: 2007-08-18 23:36:42 +0200 (Sat, 18 Aug 2007) $
9             ##
10             ## This program is free software; you can redistribute it and/or
11             ## modify it under the same terms as Perl itself.
12             ## ----------------------------------------------------------------------------------------------
13              
14              
15             $VERSION = "1.00";
16              
17 1     1   5 use strict;
  1         1  
  1         27  
18 1     1   4 use warnings;
  1         2  
  1         20  
19 1     1   4 use Carp;
  1         1  
  1         1384  
20             #use Data::Dumper;
21              
22             my $DEBUG = 0;
23              
24              
25             # $data = reference to @output of the telnet command "show ip accounting"
26             sub new {
27 0     0 0   my ($this) = shift;
28 0           my (%parms) = @_;
29            
30 0   0       my $class = ref($this) || $this;
31 0           my $self = {};
32 0           bless($self, $class);
33              
34 0           $self->{'headers'} = [ 'source', 'destination', 'lastPollBytes', 'lastPollPackets', 'totalBytes', 'totalPakcets', 'polls' ];
35              
36 0           $self->{'data'} = {}; # "src-dest" -> [ source, destination, lastPollBytes, lastPollPackets, totalBytes, totalPakcets, polls ]
37              
38 0           $self->{'stats'}->{'totalpolls'} = 0;
39 0           $self->{'stats'}->{'totalbytes'} = 0;
40 0           $self->{'stats'}->{'totalpackets'} = 0;
41 0           $self->{'stats'}->{'totalpolledlines'} = 0;
42 0           $self->{'stats'}->{'totalskippedlines'} = 0;
43 0           $self->{'stats'}->{'uniquehostpairs'} = 0;
44 0           $self->{'stats'}->{'starttime'} = ''; # starttime of the first poll
45 0           $self->{'stats'}->{'lastpolltime'} = ''; # time last poll has run
46              
47 0 0         $self->{'keep_history'} = (defined($parms{'keep_history'}))?($parms{'keep_history'}):(1); # keep summarized historical data for each poll
48              
49 0           $self->{'historical'} = {}; # {'timestamp'} -> { 'totalBytes' -> '', 'totalPackets' -> '', 'hostPairs' -> ''}
50              
51 0           &_init($class);
52 0           return($self);
53             } # end sub new
54              
55              
56             # initialization : set up logging
57             sub _init {
58 0     0     my $class=shift;
59            
60             ## enable debugging if needed
61 0 0   0     $SIG{'__WARN__'} = sub { carp($_[0]) } if ($DEBUG > 0);
  0            
62             }
63              
64              
65              
66             #
67             # parse telnet data output into $self->{'data'} output
68             # data should always be in format : / *source *destination *packets *bytes */
69             #
70             sub parse {
71 0     0 0   my ($self) = shift;
72 0           my ($acct_data) = shift; # reference to array of output lines
73              
74 0           my ($row);
75 0           my ($src, $dst, $packets, $bytes);
76            
77             ## update last poll time
78 0           $self->{'stats'}->{'lastpolltime'} = time();
79 0 0         $self->{'stats'}->{'starttime'} = time() unless ($self->{'stats'}->{'starttime'});
80              
81              
82 0           foreach $row (@{$acct_data}) {
  0            
83 0           eval {
84 0           ($src, $dst, $packets, $bytes) = &_parse_row($row);
85             };
86              
87             ## update historical data if needed
88             ## in case there's no data, still update poll time to historical data
89 0 0 0       if (!defined($self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}) && $self->{'keep_history'}) {
90 0           $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'hostPairs'} = 0;
91 0           $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'totalBytes'} = 0;
92 0           $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'totalPackets'} = 0;
93             }
94              
95             ## skip row if we didn't find what we expected (src, dst, packets, bytes)
96 0 0         if ($@) {
97 0           $self->{'stats'}->{'totalskippedlines'}++;
98 0 0         if ($DEBUG > 0) {
99 0           carp("skipping row : " . $@);
100             }
101 0           next;
102             }
103              
104             ## update the statistics, if the source-destination combination already exists
105             ## then add the new data to existing
106 0 0         if (exists $self->{'data'}->{"$src-$dst"}) {
107 0           $self->{'data'}->{"$src-$dst"}->{'lastPollBytes'} = $bytes;
108 0           $self->{'data'}->{"$src-$dst"}->{'lastPollPackets'} = $packets;
109 0           $self->{'data'}->{"$src-$dst"}->{'totalBytes'} += $bytes;
110 0           $self->{'data'}->{"$src-$dst"}->{'totalPackets'} += $packets;
111 0           $self->{'data'}->{"$src-$dst"}->{'polls'} ++;
112             }
113             ## if this is the first time we see this host pair then initialize stats
114             else {
115 0           $self->{'data'}->{"$src-$dst"}->{'source'} = $src;
116 0           $self->{'data'}->{"$src-$dst"}->{'destination'} = $dst;
117 0           $self->{'data'}->{"$src-$dst"}->{'lastPollBytes'} = $bytes;
118 0           $self->{'data'}->{"$src-$dst"}->{'lastPollPackets'} = $packets;
119 0           $self->{'data'}->{"$src-$dst"}->{'totalBytes'} = $bytes;
120 0           $self->{'data'}->{"$src-$dst"}->{'totalPackets'} = $packets;
121 0           $self->{'data'}->{"$src-$dst"}->{'polls'} = 1;
122 0           $self->{'stats'}->{'uniquehostpairs'} ++;
123             }
124            
125             # ## update historical data if needed
126 0 0         if ($self->{'keep_history'}) {
127             # if (defined($self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}})) {
128 0           $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'hostPairs'}++;
129 0           $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'totalBytes'} += $bytes;
130 0           $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'totalPackets'} += $packets;
131             # }
132             # else {
133             # $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'hostPairs'} = 1;
134             # $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'totalBytes'} = $bytes;
135             # $self->{'historical'}->{$self->{'stats'}->{'lastpolltime'}}->{'totalPackets'} = $packets;
136             # }
137             }
138            
139             ## update global statistics
140 0           $self->{'stats'}->{'totalbytes'} += $bytes;
141 0           $self->{'stats'}->{'totalpackets'} += $packets;
142 0           $self->{'stats'}->{'totalpolledlines'} ++;
143             }
144              
145             ## update stats
146 0           $self->{'stats'}->{'totalpolls'} ++;
147              
148 0           return $self->{'data'};
149             }
150              
151             ##
152             ## parses a single row of data, returns array of (source, destination, packets, bytes)
153             ## dies otherwise
154             ##
155             sub _parse_row() {
156 0     0     my ($row) = shift;
157            
158 0           my (@cols);
159            
160             ## remove leading and trailing spaces, eol characters
161             ## split row in columns delimited by spaces
162 0           $row =~ s/^ *(.*)[ \n]*$/$1/;
163 0           @cols = split(/ +/,$row);
164            
165             ## die unless we've got 4 columns
166 0 0         if ((scalar @cols) < 4) {
167 0           croak("skip row, we need 4 columns (\"$row\")");
168             }
169            
170             ## validate each column, make sure it contains the correct data
171 0           eval {
172 0           map { &_validate_column('column' => $_, 'ip' => 1); } ($cols[0], $cols[1]); # these columns should contain ip addresses
  0            
173 0           map { &_validate_column('column' => $_, 'ip' => 0); } ($cols[2], $cols[3]); # these columns should contain positive number
  0            
174             };
175 0 0         if ($@) {
176 0           croak("skip row, invalid column format : " . $@);
177             }
178            
179             ## everything is ok, we got (source, destination, packets, bytes)
180 0           return @cols;
181             }
182              
183              
184             ##
185             ## validate a single column
186             ## parameters = column => $col, ip => 0|1
187             ##
188             sub _validate_column() {
189 0     0     my (%parms) = @_;
190            
191 0           my $column = $parms{'column'};
192 0   0       my $is_ip = $parms{'ip'} || 0;
193            
194 0 0         if (!$column) {
195 0           croak("column does not contain a value ($column)");
196             }
197            
198             ## check if it's an ip address
199 0 0         if ($is_ip > 0) {
200 0 0         unless ($column =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
201 0           croak("expecting ip address but not found in column \"$column\"");
202             }
203             }
204            
205             ## check if it's a positive number
206             else {
207 0 0         unless ($column =~ /^[0-9]+$/) {
208 0           croak("expecting positive number but not found in column \"$column\"");
209             }
210             }
211             }
212              
213              
214             ##
215             ## returns reference to array with the default headers for the columns
216             ##
217             sub get_headers() {
218 0     0 0   my ($self) = shift;
219 0           return $self->{'headers'};
220             }
221              
222              
223             ##
224             ## return reference to output hash, hash contains reference to array of columns
225             ##
226             sub get_data() {
227 0     0 0   my ($self) = shift;
228 0           return $self->{'data'};
229             }
230              
231             ##
232             ## return reference to hash with statistics
233             ##
234             sub get_stats() {
235 0     0 0   my ($self) = shift;
236            
237 0           return $self->{'stats'};
238             }
239              
240             ##
241             ## return reference to hash with statistics
242             ##
243             sub get_history() {
244 0     0 0   my ($self) = shift;
245            
246 0           return $self->{'historical'};
247             }
248              
249             sub get_total_polls() {
250 0     0 0   my ($self) = shift;
251 0           return $self->{'stats'}->{'totalpolls'};
252             }
253              
254             sub get_total_polled_lines() {
255 0     0 0   my ($self) = shift;
256 0           return $self->{'stats'}->{'totalpolledlines'};
257             }
258              
259             sub get_total_bytes() {
260 0     0 0   my ($self) = shift;
261 0           return $self->{'stats'}->{'totalbytes'};
262             }
263              
264             sub get_total_packets() {
265 0     0 0   my ($self) = shift;
266 0           return $self->{'stats'}->{'totalpackets'};
267             }
268              
269             sub get_last_poll_time() {
270 0     0 0   my ($self) = shift;
271 0           return $self->{'stats'}->{'lastpolltime'};
272             }
273              
274             sub get_first_poll_time() {
275 0     0 0   my ($self) = shift;
276 0           return $self->{'stats'}->{'starttime'};
277             }
278              
279             1;
280              
281              
282             __END__