Merge remote-tracking branch 'upstream/master'
[torrus-plus.git] / src / lib / Torrus / DataAccess.pm
blob28113eebb66e852124e80f1cff3b522b4774e5ac
1 # Copyright (C) 2002 Stanislav Sinyagin
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
17 # $Id$
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
20 package Torrus::DataAccess;
21 use strict;
22 use warnings;
24 use Torrus::ConfigTree;
25 use Torrus::Log;
26 use Torrus::RPN;
28 use RRDs;
30 our $VERSION = 1.0;
32 # The Torrus::DataAccess object contains cached values, and it does not
33 # check the cache validity. We assume that a Torrus::DataAccess object
34 # lifetime is within a short period of time, such as one monitor cycle.
36 sub new
38 my $self = {};
39 my $class = shift;
40 bless $self, $class;
41 return $self;
44 # Read the data from datasource file, depending on its type.
45 # If time is not specified, reads the latest available data.
46 # In case of rrd-cdef leaf type, the returned timestamp is the
47 # earliest timestamp of the data sources involved.
49 # ($value, $timestamp) = $da->read( $config_tree, $leaf_token )
51 # ($value, $timestamp) = $da->read( $config_tree, $leaf_token, $end_time )
53 # ($value, $timestamp) = $da->read( $config_tree, $leaf_token,
54 # $end_time, $start_time )
57 sub read
59 my $self = shift;
60 my $config_tree = shift;
61 my $token = shift;
62 my $t_end = shift;
63 my $t_start = shift;
65 my $cachekey = $token .
66 ':' . (defined($t_end)?$t_end:'') .
67 ':' . (defined($t_start)?$t_start:'');
69 if( exists( $self->{'cache_read'}{$cachekey} ) )
71 return @{$self->{'cache_read'}{$cachekey}};
74 if( not $config_tree->isLeaf( $token ) )
76 my $path = $config_tree->path( $token );
77 Error("Torrus::DataAccess::readLast: $path is not a leaf");
78 return
81 my $ret_val;
82 my $ret_time;
84 my $ds_type = $config_tree->getNodeParam( $token, 'ds-type' );
85 if( $ds_type eq 'rrd-file' or
86 $ds_type eq 'collector' )
88 my $leaf_type = $config_tree->getNodeParam( $token, 'leaf-type' );
90 if( $leaf_type eq 'rrd-def' )
92 my $file = $config_tree->getNodeParam( $token, 'data-file' );
93 my $dir = $config_tree->getNodeParam( $token, 'data-dir' );
94 my $ds = $config_tree->getNodeParam( $token, 'rrd-ds' );
95 my $cf = $config_tree->getNodeParam( $token, 'rrd-cf' );
96 ( $ret_val, $ret_time ) =
97 $self->read_RRD_DS( $dir.'/'.$file,
98 $cf, $ds, $t_end, $t_start );
100 elsif( $leaf_type eq 'rrd-cdef' )
102 my $expr = $config_tree->getNodeParam( $token, 'rpn-expr' );
103 ( $ret_val, $ret_time ) =
104 $self->read_RPN( $config_tree, $token, $expr,
105 $t_end, $t_start );
108 else
110 my $path = $config_tree->path( $token );
111 Error("$path: leaf-type $leaf_type is not supported ".
112 "for data access");
115 else
117 my $path = $config_tree->path( $token );
118 Error("$path: ds-type $ds_type is not supported ".
119 "for data access");
122 $self->{'cache_read'}{$cachekey} = [ $ret_val, $ret_time ];
123 return ( $ret_val, $ret_time );
127 sub read_RRD_DS
129 my $self = shift;
130 my $filename = shift;
131 my $cf = shift;
132 my $ds = shift;
133 my $t_end = shift;
134 my $t_start = shift;
136 my $cachekey = $filename . ':' . $cf .
137 ':' . (defined($t_end)?$t_end:'') .
138 ':' . (defined($t_start)?$t_start:'');
140 if( exists( $self->{'cache_RRD'}{$cachekey}{$ds} ) )
142 return @{$self->{'cache_RRD'}{$cachekey}{$ds}};
145 my $rrdinfo = RRDs::info( $filename );
146 my $ERR = RRDs::error;
147 if( $ERR )
149 Error("Error during RRD info for $filename: $ERR");
150 return
153 my $step = $rrdinfo->{'step'};
154 my $last_available = $rrdinfo->{'last_update'};
155 $last_available -= $last_available % $step;
157 if( not defined $t_end )
159 $t_end = $last_available;
161 elsif( index( $t_end, 'LAST' ) >= 0 )
163 $t_end =~ s/LAST/$last_available/g;
166 if( not defined $t_start )
168 $t_start = $t_end . '-' . int($step * 3);
170 elsif( index( $t_start, 'LAST' ) >= 0 )
172 $t_start =~ s/LAST/$last_available/g;
175 # From here on, f_ prefix means fetch results
176 my( $f_start, $f_step, $f_names, $f_data ) =
177 RRDs::fetch( $filename, $cf, '--start', $t_start, '--end', $t_end );
178 $ERR = RRDs::error;
179 if( $ERR )
181 Error("Error during RRD fetch for $filename: $ERR");
182 return
186 # Memorize the DS names in cache
188 for( my $i = 0; $i < @{$f_names}; $i++ )
190 $self->{'cache_RRD'}{$cachekey}{$f_names->[$i]} = [];
193 # Get the last available data and store in cache
195 for my $f_line ( @{$f_data} )
197 for( my $i = 0; $i < @{$f_names}; $i++ )
199 if( defined $f_line->[$i] )
201 $self->{'cache_RRD'}{$cachekey}{$f_names->[$i]} =
202 [ $f_line->[$i], $f_start ];
205 $f_start += $f_step;
208 if( not exists( $self->{'cache_RRD'}{$cachekey}{$ds} ) )
210 Error("DS name $ds is not found in $filename");
211 return
213 else
215 if( scalar( @{$self->{'cache_RRD'}{$cachekey}{$ds}} ) == 0 )
217 Warn("Value undefined for ",
218 "DS=$ds, CF=$cf, start=$t_start, end=$t_end in $filename");
219 return
221 else
223 return @{$self->{'cache_RRD'}{$cachekey}{$ds}};
230 # Data access for other CF than defined for the leaf doesn't make much
231 # sense. So we ignore the CF in DataAccess and leave it for the
232 # sake of Renderer compatibility
233 my %cfNames =
234 ( 'AVERAGE' => 1,
235 'MIN' => 1,
236 'MAX' => 1,
237 'LAST' => 1 );
240 sub read_RPN
242 my $self = shift;
243 my $config_tree = shift;
244 my $token = shift;
245 my $expr = shift;
246 my $t_end = shift;
247 my $t_start = shift;
249 my @expr_list = split(',', $expr);
250 my @eval_expr;
251 my $timestamp = $t_end > 0 ? $t_end : time();
253 my $rpn = Torrus::RPN->new();
255 my $callback = sub
257 my ($noderef, $timeoffset) = @_;
259 my $function;
260 if( $noderef =~ s/^(.)\@// )
262 $function = $1;
265 my $leaf = length($noderef) > 0 ?
266 $config_tree->getRelative($token, $noderef) : $token;
268 if( not defined $leaf )
270 my $path = $config_tree->path($token);
271 Error("Cannot find relative reference $noderef at $path");
272 return
275 my ($rval, $var_tstamp) = $self->read($config_tree,
276 $leaf,
277 $timeoffset,
278 $t_start);
279 if( defined $rval )
281 if( $var_tstamp == 0 )
283 Warn("Torrus::DataAccess::read retirned zero timestamp ".
284 "for $leaf");
287 if( $var_tstamp < $timestamp )
289 $timestamp = $var_tstamp;
293 if( defined( $function ) )
295 if( $function eq 'T' )
297 return $var_tstamp;
299 elsif( not $cfNames{$function} )
301 Error("Function not supported in RPN: $function");
302 return
305 return $rval;
308 my $result = $rpn->run( $expr, $callback );
310 return ( $result, $timestamp );
316 # Local Variables:
317 # mode: perl
318 # indent-tabs-mode: nil
319 # perl-indent-level: 4
320 # End: