File Coverage

blib/lib/perfSONAR_PS/DB/RRD.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package perfSONAR_PS::DB::RRD;
2              
3 1     1   38768 use fields 'LOGGER', 'PATH', 'NAME', 'DATASOURCES', 'COMMIT';
  1         1595  
  1         5  
4              
5 1     1   70 use strict;
  1         3  
  1         22  
6 1     1   4 use warnings;
  1         6  
  1         45  
7              
8             our $VERSION = 0.09;
9              
10             =head1 NAME
11              
12             perfSONAR_PS::DB::RRD - A module that provides a simple API for dealing with
13             data stored in rrd files through the RRDTool's RRDp perl module.
14              
15             =head1 DESCRIPTION
16              
17             This module builds on the simple offerings of RRDp (simple a series of pipes to
18             communicate with rrd files) to offer some common functionality that is present
19             in the other DB modules of perfSONAR_PS.
20              
21             =cut
22              
23 1     1   1477 use RRDp;
  0            
  0            
24             use Log::Log4perl qw(get_logger);
25             use Params::Validate qw(:all);
26              
27             use perfSONAR_PS::Common;
28             use perfSONAR_PS::ParameterValidation;
29              
30             =head2 new($package, { path, name, dss, error })
31              
32             Create a new RRD object. All arguments are optional:
33              
34             * path - path to RRD executable on the host system
35             * name - name of the RRD file this object will be reading
36             * dss - hash reference of datasource values
37             * error - Flag to allow RRD to pass back error values
38              
39             The arguments can be set (and re-set) via the appropriate function calls.
40              
41             =cut
42              
43             sub new {
44             my ( $package, @args ) = @_;
45             my $parameters = validateParams( @args, { path => 0, name => 0, dss => 0, error => 0 } );
46              
47             my $self = fields::new($package);
48             $self->{LOGGER} = get_logger("perfSONAR_PS::DB::RRD");
49             if ( exists $parameters->{path} and $parameters->{path} ) {
50             $self->{PATH} = $parameters->{path};
51             }
52             if ( exists $parameters->{name} and $parameters->{name} ) {
53             $self->{NAME} = $parameters->{name};
54             }
55             if ( exists $parameters->{dss} and $parameters->{dss} ) {
56             $self->{DATASOURCES} = \%{ $parameters->{dss} };
57             }
58             if ( exists $parameters->{error} and $parameters->{error} ) {
59             if ( $parameters->{error} == 1 ) {
60             $RRDp::error_mode = 'catch';
61             $self->{LOGGER}->debug("Setting error mode.");
62             }
63             else {
64             undef $RRDp::error_mode;
65             $self->{LOGGER}->debug("Unsetting error mode.");
66             }
67             }
68             return $self;
69             }
70              
71             =head2 setFile($self, { file })
72              
73             Sets the RRD filename for the RRD object.
74              
75             =cut
76              
77             sub setFile {
78             my ( $self, @args ) = @_;
79             my $parameters = validateParams( @args, { file => 1 } );
80              
81             if ( $parameters->{file} =~ m/\.rrd$/mx ) {
82             $self->{NAME} = $parameters->{file};
83             return 0;
84             }
85             else {
86             $self->{LOGGER}->error("Cannot set filename.");
87             return -1;
88             }
89             }
90              
91             =head2 setPath($self, { path })
92              
93             Sets the 'path' to the RRD binary for the RRD object.
94              
95             =cut
96              
97             sub setPath {
98             my ( $self, @args ) = @_;
99             my $parameters = validateParams( @args, { path => 1 } );
100              
101             if ( $parameters->{path} =~ m/rrdtool$/mx ) {
102             $self->{PATH} = $parameters->{path};
103             return 0;
104             }
105             else {
106             $self->{LOGGER}->error("Cannot set path.");
107             return -1;
108             }
109             }
110              
111             =head2 setVariables($self, { dss })
112              
113             Sets several variables (as a hash reference) in the RRD object.
114              
115             =cut
116              
117             sub setVariables {
118             my ( $self, @args ) = @_;
119             my $parameters = validateParams( @args, { dss => 1 } );
120              
121             $self->{DATASOURCES} = \%{ $parameters->{dss} };
122             return 0;
123             }
124              
125             =head2 setVariable($self, { dss })
126              
127             Sets a variable value in the RRD object.
128              
129             =cut
130              
131             sub setVariable {
132             my ( $self, @args ) = @_;
133             my $parameters = validateParams( @args, { ds => 1 } );
134              
135             $self->{DATASOURCES}->{ $parameters->{ds} } = q{};
136             return 0;
137             }
138              
139             =head2 setError($self, { error })
140              
141             Sets the error variable for the RRD object.
142              
143             =cut
144              
145             sub setError {
146             my ( $self, @args ) = @_;
147             my $parameters = validateParams( @args, { error => 1 } );
148              
149             if ( $parameters->{error} == 1 ) {
150             $RRDp::error_mode = 'catch';
151             $self->{LOGGER}->debug("Setting error mode.");
152             }
153             else {
154             undef $RRDp::error_mode;
155             $self->{LOGGER}->debug("Unsetting error mode.");
156             }
157             return 0;
158             }
159              
160             =head2 getErrorMessage($self, { })
161              
162             Gets any error returned from the underlying RRDp module for this RRD object.
163              
164             =cut
165              
166             sub getErrorMessage {
167             my ( $self, @args ) = @_;
168             my $parameters = validateParams( @args, {} );
169              
170             if ($RRDp::error) {
171             return $RRDp::error;
172             }
173             return;
174             }
175              
176             =head2 openDB($self, { })
177              
178             'Opens' (creates a pipe) to the defined RRD file.
179              
180             =cut
181              
182             sub openDB {
183             my ( $self, @args ) = @_;
184             my $parameters = validateParams( @args, {} );
185              
186             if ( exists $self->{PATH} and exists $self->{NAME} ) {
187             RRDp::start $self->{PATH};
188             return 0;
189             }
190             else {
191             $self->{LOGGER}->error("Missing path or name in object.");
192             return -1;
193             }
194             }
195              
196             =head2 closeDB($self, { })
197              
198             'Closes' (terminates the pipe) of an open RRD.
199              
200             =cut
201              
202             sub closeDB {
203             my ( $self, @args ) = @_;
204             my $parameters = validateParams( @args, {} );
205              
206             if ( exists $self->{PATH} and exists $self->{NAME} ) {
207             my $status = RRDp::end;
208             if ($status) {
209             $self->{LOGGER}->error( $self->{PATH} . " has returned status \"" . $status . "\" on closing." );
210             return -1;
211             }
212             return 0;
213             }
214             else {
215             $self->{LOGGER}->error("RRD file not open.");
216             return -1;
217             }
218             }
219              
220             =head2 query($self, { cf, resolution, start, end })
221              
222             Query a RRD with specific times/resolutions.
223              
224             =cut
225              
226             sub query {
227             my ( $self, @args ) = @_;
228             my $parameters = validateParams( @args, { cf => 1, resolution => 0, start => 0, end => 0 } );
229              
230             my %rrd_result = ();
231             my @rrd_headings = ();
232             unless ( $parameters->{cf} ) {
233             $self->{LOGGER}->error("Consolidation function invalid.");
234             }
235              
236             my $cmd = "fetch " . $self->{NAME} . " " . $parameters->{cf};
237             if ( $parameters->{resolution} ) {
238             $cmd = $cmd . " -r " . $parameters->{resolution};
239             }
240             if ( $parameters->{start} ) {
241             $cmd = $cmd . " -s " . $parameters->{start};
242             }
243             if ( $parameters->{end} ) {
244             $cmd = $cmd . " -e " . $parameters->{end};
245             }
246              
247             $self->{LOGGER}->debug( "Calling rrdtool with command: " . $cmd );
248             RRDp::cmd $cmd;
249             my $answer = RRDp::read;
250             if ($RRDp::error) {
251             $self->{LOGGER}->error( "Database error \"" . $RRDp::error . "\"." );
252             %rrd_result = ();
253             $rrd_result{ANSWER} = $RRDp::error;
254             return %rrd_result;
255             }
256              
257             if ( $$answer ) {
258             my @array = split( /\n/mx, $$answer );
259             my $len = $#{@array};
260             for my $x ( 0 .. $len ) {
261             if ( $x == 0 ) {
262             @rrd_headings = split( /\s+/mx, $array[$x] );
263             }
264             elsif ( $x > 1 ) {
265             unless ( defined $array[$x] and $array[$x] ) {
266             next;
267             }
268             my @line = split( /\s+/mx, $array[$x] );
269             $line[0] =~ s/://mx;
270             my $len2 = $#{@rrd_headings};
271             for my $z ( 1 .. $len2 ) {
272             $rrd_result{ $line[0] }{ $rrd_headings[$z] } = $line[$z] if $line[$z];
273             }
274             }
275             }
276             }
277             return %rrd_result;
278             }
279              
280             =head2 insert($self, { time, ds, value })
281              
282             'Inserts' a time/value pair for a given variable. These are not inserted
283             into the RRD, but will wait until we enter into the commit phase (i.e. by
284             calling the commit function). This allows us to stack up a bunch of values
285             first, and reuse time figures.
286              
287             =cut
288              
289             sub insert {
290             my ( $self, @args ) = @_;
291             my $parameters = validateParams( @args, { time => 1, ds => 1, value => 1 } );
292              
293             $self->{COMMIT}->{ $parameters->{time} }->{ $parameters->{ds} } = $parameters->{value};
294             return 0;
295             }
296              
297             =head2 insertCommit($self, { })
298              
299             'Commits' all outstanding variables time/data pairs for a given RRD.
300              
301             =cut
302              
303             sub insertCommit {
304             my ( $self, @args ) = @_;
305             my $parameters = validateParams( @args, {} );
306              
307             my $answer = q{};
308             my @result = ();
309             foreach my $time ( keys %{ $self->{COMMIT} } ) {
310             my $cmd = "updatev " . $self->{NAME} . " -t ";
311             my $template = q{};
312             my $values = q{};
313             my $counter = 0;
314             foreach my $ds ( keys %{ $self->{COMMIT}->{$time} } ) {
315             if ( $counter == 0 ) {
316             $template = $template . $ds;
317             $values = $values . $time . ":" . $self->{COMMIT}->{$time}->{$ds};
318             }
319             else {
320             $template = $template . ":" . $ds;
321             $values = $values . ":" . $self->{COMMIT}->{$time}->{$ds};
322             }
323             $counter++;
324             }
325              
326             unless ( $template and $values ) {
327             $self->{LOGGER}->error("RRDTool cannot update when datasource values are not specified.");
328             next;
329             }
330              
331             delete $self->{COMMIT}->{$time};
332             $cmd = $cmd . $template . " " . $values;
333             RRDp::cmd $cmd;
334             $answer = RRDp::read;
335             unless ($RRDp::error) {
336             push @result, $$answer;
337             }
338             }
339             return @result;
340             }
341              
342             =head2 firstValue($self, { })
343              
344             Returns the first value of an RRD.
345              
346             =cut
347              
348             sub firstValue {
349             my ( $self, @args ) = @_;
350             my $parameters = validateParams( @args, {} );
351              
352             RRDp::cmd "first " . $self->{NAME};
353             my $answer = RRDp::read;
354             unless ($RRDp::error) {
355             chomp($$answer);
356             return $$answer;
357             }
358             return;
359             }
360              
361             =head2 lastValue($self, { })
362              
363             Returns the last value of an RRD.
364              
365             =cut
366              
367             sub lastValue {
368             my ( $self, @args ) = @_;
369             my $parameters = validateParams( @args, {} );
370              
371             RRDp::cmd "last " . $self->{NAME};
372             my $answer = RRDp::read;
373             unless ($RRDp::error) {
374             chomp($$answer);
375             return $$answer;
376             }
377             return;
378             }
379              
380             =head2 lastTime($self, { })
381              
382             Returns the last time the RRD was updated.
383              
384             =cut
385              
386             sub lastTime {
387             my ( $self, @args ) = @_;
388             my $parameters = validateParams( @args, {} );
389              
390             RRDp::cmd "lastupdate " . $self->{NAME};
391             my $answer = RRDp::read;
392             my @result = split( /\n/mx, $$answer );
393             my @time = split( /:/mx, $result[-1] );
394             unless ($RRDp::error) {
395             return $time[0];
396             }
397             return;
398             }
399              
400             1;
401              
402             __END__