2 # Copyright (C) 2008 Stanislav Sinyagin
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18 # $Id: srvderive.in,v 1.3 2011-06-11 00:44:48 ivan Exp $
19 # Stanislav Sinyagin <ssinyagin@yahoo.com>
21 # Combine SUM or MAX from several service IDs and create a new one
23 BEGIN { require '@torrus_config_pl@'; }
33 use Torrus::ServiceID;
34 use Torrus::SQL::SrvExport;
50 my %known_functions = ('MAX' => \&function_max, 'SUM' => \&function_sum);
54 'start=s' => \$startdate,
56 'month' => \$onemonth,
57 'func=s' => \$function,
61 'verbose' => \$verbose,
63 'help' => \$help_needed,
66 if( $help_needed or not $ok or (not $startdate) or
67 (defined($enddate) + defined($onemonth) != 1) or
69 (scalar(@input) < 2 and scalar( @ARGV ) < 2) or
72 print STDERR "Usage: $0 TIMESPAN OUTPUT FUNCTION SOURCES...\n\n",
74 " --start=YYYY-MM-DD [mandatory] Start date\n",
75 " --month [optional] Calendar month timespan\n",
76 " --end=YYYY-MM-DD [optional] Next day after the timespan end\n",
77 " Either --month or --end option must be defined\n\n",
79 " --out=SERVICEID [mandatory] Output Service ID\n\n",
81 " --func=MAX|SUM [mandatory] Aggregation function\n\n",
83 " Either --in=SERVICEID, or Service ID names as command line ",
85 " Minimum 2 sources required\n\n",
87 " --step=N [300] service data interval\n",
88 " --verbose print extra information\n",
89 " --debug print debugging information\n",
90 " --help print usage information\n",
98 if( not defined($known_functions{$function}) )
100 printf STDERR ("Unknown function: %s. Must be onne of: %s\n",
101 $function, join(', ', sort keys %known_functions));
109 Torrus::Log::setLevel('debug');
113 Torrus::Log::setLevel('verbose');
117 my $starttime = str2time( $startdate );
118 if( not defined($starttime) )
120 Error('Cannot parse start date: ' . $startdate);
125 $startdate = time2str('%Y-%m-%d', $starttime);
129 if( defined($enddate) )
131 $endtime = str2time( $enddate );
132 if( not defined($endtime) )
134 Error('Cannot parse end date: ' . $enddate);
138 if( $endtime < $starttime )
140 Error('End date is earlier than start date');
146 # Calculate +1 calendar month
148 my ($ss,$mm,$hh,$day,$month,$year,$zone) =
149 strptime( $startdate );
155 my $endmonth = $month + 1;
163 $endtime = str2time( sprintf('%.4d-%.2d-%.2d',
164 $endyear, $endmonth, $day) );
165 if( not defined($endtime) )
167 # oops, it was past the end of the month
176 $endtime = str2time( sprintf('%.4d-%.2d-%.2d',
177 $endyear, $endmonth, $day) );
179 if( not defined($endtime) )
181 Error('Cannot determine the end date');
188 $enddate = time2str('%Y-%m-%d', $endtime);
191 Verbose('Start time: ', scalar(localtime($starttime)));
192 Verbose('End time: ', scalar(localtime($endtime)));
194 my $srvExp = Torrus::SQL::SrvExport->new();
195 if( not defined( $srvExp ) )
197 Error('Cannot connect to the SQL database');
201 my $srvIDs = $srvExp->getServiceIDs();
203 foreach my $serviceid ( @input )
205 if( not grep {$serviceid eq $_} @{$srvIDs} )
207 Error('Input service ID not found in the database: ' . $serviceid);
212 &Torrus::DB::setSafeSignalHandlers();
214 # Check if the output ServiceID exists in the local database
215 # The database contains only IDs generated from datasource trees.
217 my $srvIDParams = new Torrus::ServiceID();
218 if( $srvIDParams->idExists( $output ) )
220 Error('Output service ID was previously created from Torrus ' .
221 'datasource tree. Cannot override it: ' . $output);
224 &Torrus::DB::cleanupEnvironment();
225 &Torrus::DB::setUnsafeSignalHandlers();
230 foreach my $serviceid ( @input )
232 $in_data{$serviceid} =
233 $srvExp->getIntervalData( $startdate, $enddate, $serviceid );
235 Verbose(sprintf('Loaded %d rows of data for %s',
236 scalar( @{$in_data{$serviceid}} ),
240 my $n_points = floor( ($endtime - $starttime) / $step );
244 foreach my $serviceid ( @input )
247 $#aligned = $n_points;
249 # Fill in the aligned array. For each interval by modulo(step),
250 # we take the sum of values that get into that interval
252 foreach my $row ( @{$in_data{$serviceid}} )
254 my $rowtime = str2time( $row->{'srv_date'} . 'T' .
255 $row->{'srv_time'} );
257 my $pos = floor( ($rowtime - $starttime) / $step );
258 my $value = Math::BigFloat->new( $row->{'value'} );
259 if( $value->is_nan() )
264 if( not defined($aligned[$pos]))
266 $aligned[$pos] = $value;
270 $aligned[$pos]->badd($value);
274 $aligned_data{$serviceid} = \@aligned;
277 Verbose(sprintf('Aligned data into %d intervals', $n_points));
279 # Store the derived data
280 my $dbh = Torrus::SQL::SrvExport->dbh();
281 if( not defined( $dbh ) )
283 Error('Lost SQL connection');
287 my $sth = $dbh->prepare( Torrus::SQL::SrvExport->sqlInsertStatement() );
288 if( not defined( $sth ) )
290 Error('Error preparing the SQL statement: ' . $dbh->errstr);
295 for( my $pos = 0; $pos < $n_points; $pos++ )
298 foreach my $serviceid ( @input )
300 my $val = $aligned_data{$serviceid}->[$pos];
301 if( defined( $val ) )
307 if( scalar( @args ) > 0 )
309 my $value = &{$known_functions{$function}}(@args);
311 my $timestamp = $starttime + $pos * $step;
312 my $datestr = time2str('%Y-%m-%d', $timestamp);
313 my $timestr = time2str('%H:%M:%S', $timestamp);
317 Debug('Updating SQL database: ' .
318 join(', ', $datestr, $timestr,
319 $output, $value, $step ));
322 if( not $sth->execute( $datestr, $timestr,
323 $output, $value, $step ) )
325 Error('Error executing SQL: ' . $dbh->errstr);
331 $dbh->commit or Error($dbh->errstr);
333 Verbose('Database update finished');
340 my $value = Math::BigFloat->new();
345 if( $value->bcmp($a) < 0 )
347 $value = Math::BigFloat->new($a);
357 my $value = Math::BigFloat->new(0);
371 # indent-tabs-mode: nil
372 # perl-indent-level: 4