line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################ |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# RRD::Editor - Portable, pure perl tool to create and edit RRD files. |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
############################################################ |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package RRD::Editor; |
8
|
|
|
|
|
|
|
|
9
|
4
|
|
|
4
|
|
85132
|
use 5.8.8; # nan doesn't seem to be supported properly by perl before this |
|
4
|
|
|
|
|
14
|
|
|
4
|
|
|
|
|
190
|
|
10
|
4
|
|
|
4
|
|
50
|
use strict; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
155
|
|
11
|
4
|
|
|
4
|
|
17
|
use warnings; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
316
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Exporter; |
14
|
4
|
|
|
4
|
|
2165
|
use POSIX qw/strftime/; |
|
4
|
|
|
|
|
26010
|
|
|
4
|
|
|
|
|
54
|
|
15
|
4
|
|
|
4
|
|
4583
|
use Carp qw(croak carp cluck); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
292
|
|
16
|
|
|
|
|
|
|
#use Getopt::Long qw(GetOptionsFromString :config pass_through); |
17
|
4
|
|
|
4
|
|
3779
|
use Getopt::Long qw(:config pass_through); |
|
4
|
|
|
|
|
52920
|
|
|
4
|
|
|
|
|
28
|
|
18
|
4
|
|
|
4
|
|
3040
|
use Time::HiRes qw(time); |
|
4
|
|
|
|
|
5614
|
|
|
4
|
|
|
|
|
17
|
|
19
|
4
|
|
|
4
|
|
638
|
use Config; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
170
|
|
20
|
|
|
|
|
|
|
|
21
|
4
|
|
|
4
|
|
16
|
use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS @ISA); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
505
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
$VERSION = '0.18'; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
26
|
|
|
|
|
|
|
@EXPORT = qw(); |
27
|
|
|
|
|
|
|
@EXPORT_OK = qw( |
28
|
|
|
|
|
|
|
open close create update info dump fetch |
29
|
|
|
|
|
|
|
last set_last lastupdate minstep |
30
|
|
|
|
|
|
|
DS_names DS_heartbeat set_DS_heartbeat DS_min set_DS_min DS_max set_DS_max DS_type set_DS_type rename_DS add_DS delete_DS |
31
|
|
|
|
|
|
|
num_RRAs RRA_numrows RRA_type RRA_step RRA_xff set_RRA_xff RRA_el set_RRA_el add_RRA delete_RRA resize_RRA |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
%EXPORT_TAGS = (all => \@EXPORT_OK); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# define sizes of long ints and floats for the various file encodings |
37
|
4
|
|
|
4
|
|
46
|
use constant NATIVE_LONG_EL_SIZE => $Config{longsize}; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
3039
|
|
38
|
4
|
|
|
4
|
|
9967
|
use constant NATIVE_DOUBLE_EL_SIZE => $Config{doublesize}; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
120
|
|
39
|
4
|
|
|
4
|
|
254
|
use constant PORTABLE_LONG_EL_SIZE => 4; # 32 bits |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
166
|
|
40
|
4
|
|
|
4
|
|
19
|
use constant PORTABLE_SINGLE_EL_SIZE => 4; # IEEE 754 single is 32 bits |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
158
|
|
41
|
4
|
|
|
4
|
|
25
|
use constant PORTABLE_DOUBLE_EL_SIZE => 8; # IEEE 754 double is 64 bits |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
434
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# try to figure out the endian-ness of this machine; assumes IEEE 754 doubles which should be ok on all modern machines |
45
|
|
|
|
|
|
|
sub _endian { |
46
|
4
|
|
|
4
|
|
13
|
my $endiantest=unpack("h*", pack("d","1000.1234")); |
47
|
4
|
50
|
|
|
|
12
|
if ($endiantest eq "c92a329bcf04f804") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
48
|
4
|
|
|
|
|
978
|
return "little"; |
49
|
|
|
|
|
|
|
} elsif ($endiantest eq "04f804cf9b322ac9") { |
50
|
0
|
|
|
|
|
0
|
return "big"; # big endian |
51
|
|
|
|
|
|
|
} elsif ($endiantest eq "cf04f804c92a329b") { |
52
|
0
|
|
|
|
|
0
|
return "mixed"; # mixed endian (used by some ARM processors) |
53
|
|
|
|
|
|
|
} else { |
54
|
0
|
|
|
|
|
0
|
return "unknown"; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
} |
57
|
4
|
|
|
4
|
|
20
|
use constant ENDIAN => _endian(); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
8
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# sort out the float cookie used by RRD files |
60
|
|
|
|
|
|
|
sub _cookie { |
61
|
|
|
|
|
|
|
# Getting RRD file float cookie right is a little tricky because Perl rounds 8.642135E130 up to 8.6421500000000028e+130 on |
62
|
|
|
|
|
|
|
# Intel 32 bit machines, and rounds to something else on 64 bit machines, and neither of these give the same bit sequence as |
63
|
|
|
|
|
|
|
# C when Perl stores 8.642135E130. Sigh ... |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# See if we can make a call to C to get the float cookie. Reliable, but needs Inline module to be available. |
66
|
4
|
|
|
4
|
|
2
|
eval { |
67
|
4
|
|
|
|
|
69
|
load Inline C => <<'END'; |
68
|
|
|
|
|
|
|
double _cookie_C() { |
69
|
|
|
|
|
|
|
return 8.642135E130; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
END |
72
|
0
|
|
|
|
|
0
|
return pack("d",_cookie_C()); |
73
|
|
|
|
|
|
|
}; |
74
|
|
|
|
|
|
|
# Inline not available. |
75
|
|
|
|
|
|
|
# Try approach that avoids need for Inline module. Ok so long as machine uses IEEE doubles (i.e. seems like all modern machines) |
76
|
|
|
|
|
|
|
# and little-endian, big-endian and mixed-endian byte order (all modern machines that I know of, museum pieces excepted): |
77
|
4
|
|
|
|
|
6
|
if (ENDIAN eq "little") { |
78
|
4
|
|
|
|
|
262
|
return chr(47). chr(37). chr(192). chr(199). chr(67). chr(43). chr(31). chr(91); # little endian |
79
|
|
|
|
|
|
|
} elsif (ENDIAN eq "big") { |
80
|
|
|
|
|
|
|
return chr(91). chr(31). chr(43). chr(67). chr(199). chr(192). chr(37). chr(47); # big endian |
81
|
|
|
|
|
|
|
} elsif (ENDIAN eq "mixed") { |
82
|
|
|
|
|
|
|
return chr(67). chr(43). chr(31). chr(91). chr(47). chr(37). chr(192). chr(199); # mixed endian (used by some ARM processors) |
83
|
|
|
|
|
|
|
} else { |
84
|
|
|
|
|
|
|
warn("Warning: To work with native (non-portable) RRD files, you need to install the perl Inline C module (e.g. by typing 'cpan -i Inline')\n"); |
85
|
|
|
|
|
|
|
return chr(67). chr(43). chr(31). chr(91). chr(47). chr(37). chr(192). chr(199); |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
} |
88
|
4
|
|
|
4
|
|
20
|
use constant DOUBLE_FLOATCOOKIE => 8.642135E130; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
182
|
|
89
|
4
|
|
|
4
|
|
18
|
use constant NATIVE_BINARY_FLOATCOOKIE => _cookie(); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
8
|
|
90
|
4
|
|
|
4
|
|
17
|
use constant PORTABLE_BINARY_FLOATCOOKIE => chr(47). chr(37). chr(192). chr(199). chr(67). chr(43). chr(31). chr(91); # portable format is always little-endian |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
178
|
|
91
|
4
|
|
|
4
|
|
16
|
use constant SINGLE_FLOATCOOKIE => 8.6421343830016e+13; # cookie to use when storing floats in single precision as +130 exponent on old cookie is too large |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
3187
|
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub _native_double { |
94
|
|
|
|
|
|
|
# try to figure out the long/double alignment needed for the RRDTOOL file format |
95
|
0
|
0
|
0
|
0
|
|
0
|
if ($Config{myarchname} =~ m/(sun|sparc|mips|irix|ppc|powerpc|arm)/i && NATIVE_LONG_EL_SIZE==4) { |
96
|
|
|
|
|
|
|
# Only affects behaviour when writing new files from scratch, otherwise can figure out the right alignment to use when |
97
|
|
|
|
|
|
|
# reading an existing file |
98
|
|
|
|
|
|
|
# Align longs on 32 bit boundaries and doubles on 64 bit boundaries. |
99
|
0
|
|
|
|
|
0
|
return "native-double-mixed"; |
100
|
|
|
|
|
|
|
} else { |
101
|
|
|
|
|
|
|
# Intel/AMD processors, DEC alpha processors - should probably swap these checks around since most machines are double-mixed ? |
102
|
|
|
|
|
|
|
# Otherwise, align longs/doubles on 32 bit machines on 32 bit boundaries, and 64 bit machines on 64 bit boundaries. |
103
|
0
|
|
|
|
|
0
|
return "native-double-simple"; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# check whether perl pack function supports little-endian usage: |
108
|
|
|
|
|
|
|
eval { |
109
|
|
|
|
|
|
|
my $test=pack("d<",\(DOUBLE_FLOATCOOKIE)); |
110
|
|
|
|
|
|
|
}; |
111
|
|
|
|
|
|
|
our $PACK_LITTLE_ENDIAN_SUPPORT = (length($@)>0 ? 0 : 1); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Define NaN, Inf, -Inf. Not as easy as it sounds - usually "nan", "inf", "-inf" works, but not always e.g. on older versions of perl, on SH4 etc |
114
|
|
|
|
|
|
|
sub _isNan { |
115
|
2025
|
|
66
|
2025
|
|
10846
|
return $_[0] eq "nan" || $_[0] != $_[0]; # NaN is the only quantity that does not equal itself |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
sub _isInf { |
118
|
1135
|
|
100
|
1135
|
|
3825
|
return $_[0] > 0 && ($_[0]*10 == $_[0]); # Inf remains equal to itself after arithmetic |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
sub _NaN { |
121
|
|
|
|
|
|
|
# try using the IEEE 754 NaN bit pattern |
122
|
4
|
|
|
4
|
|
6
|
my $nan; |
123
|
4
|
|
|
|
|
5
|
if (ENDIAN eq "little") { |
124
|
4
|
|
|
|
|
20
|
$nan= unpack("d", scalar reverse pack "H*", "7FF8000000000000");# little endian |
125
|
4
|
50
|
|
|
|
9
|
if (_isNan($nan)) { return $nan;} |
|
4
|
|
|
|
|
221
|
|
126
|
|
|
|
|
|
|
} elsif (ENDIAN eq "big") { |
127
|
|
|
|
|
|
|
$nan= unpack("d", scalar pack "H*", "7FF8000000000000"); # big endian |
128
|
|
|
|
|
|
|
if (_isNan($nan)) { return $nan;} |
129
|
|
|
|
|
|
|
} elsif (ENDIAN eq "mixed") { |
130
|
|
|
|
|
|
|
$nan= unpack("d", scalar pack "H*", "000000007FF80000"); # mixed endian (used by some ARM processors) |
131
|
|
|
|
|
|
|
if (_isNan($nan)) { return $nan;} |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
# last ditch attempt. try perl "nan" string. Doesn't work on all OS's since its relies on the interpretation made by a native C library call |
134
|
0
|
|
|
|
|
0
|
$nan = 0+"nan"; |
135
|
0
|
0
|
|
|
|
0
|
if (_isNan($nan)) {return $nan;} |
|
0
|
|
|
|
|
0
|
|
136
|
0
|
|
|
|
|
0
|
warn("Warning: Looks like you have no NaN support. Might have problems reading rrd files."); |
137
|
0
|
|
|
|
|
0
|
return 0; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
sub _Inf { |
140
|
|
|
|
|
|
|
# try using the IEEE 754 Inf bit pattern |
141
|
4
|
|
|
4
|
|
11
|
my $inf; |
142
|
4
|
|
|
|
|
4
|
if (ENDIAN eq "little") { |
143
|
4
|
|
|
|
|
12
|
$inf= unpack("d", scalar reverse pack "H*", "7FF0000000000000");# little endian |
144
|
4
|
50
|
|
|
|
10
|
if (_isInf($inf)) {return $inf;} |
|
4
|
|
|
|
|
215
|
|
145
|
|
|
|
|
|
|
} elsif (ENDIAN eq "big") { |
146
|
|
|
|
|
|
|
$inf= unpack("d", scalar pack "H*", "7FF0000000000000"); # big endian |
147
|
|
|
|
|
|
|
if (_isInf($inf)) {return $inf;} |
148
|
|
|
|
|
|
|
} elsif (ENDIAN eq "mixed") { |
149
|
|
|
|
|
|
|
$inf= unpack("d", scalar pack "H*", "000000007FF00000"); # mixed endian (used by some ARM processors) |
150
|
|
|
|
|
|
|
if (_isInf($inf)) {return $inf;} |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
# didn't work. |
153
|
0
|
|
|
|
|
0
|
$inf = 0+"inf"; |
154
|
0
|
0
|
|
|
|
0
|
if (_isInf($inf)) {return $inf;} |
|
0
|
|
|
|
|
0
|
|
155
|
0
|
|
|
|
|
0
|
warn("Warning: Looks like you have no Inf support. Might have problems reading rrd files."); |
156
|
0
|
|
|
|
|
0
|
return 1; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
sub _strfloat { |
159
|
|
|
|
|
|
|
# convert a float to a string in a standard way i.e. removing cross-platform variation in strings displayed for nan and inf |
160
|
805
|
100
|
|
805
|
|
1105
|
if (_isNan($_[0])) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
161
|
232
|
|
|
|
|
859
|
return "nan"; |
162
|
|
|
|
|
|
|
} elsif (_isInf($_[0])) { |
163
|
19
|
|
|
|
|
40
|
return "inf" ; |
164
|
|
|
|
|
|
|
} elsif (_isInf(-$_[0])) { |
165
|
19
|
|
|
|
|
44
|
return "-inf" ; |
166
|
|
|
|
|
|
|
} else { |
167
|
535
|
|
|
|
|
482
|
my $digits=10; |
168
|
535
|
50
|
|
|
|
804
|
if ($_[1]) {$digits=$_[1];} |
|
535
|
|
|
|
|
501
|
|
169
|
535
|
|
|
|
|
2158
|
my $str=sprintf "%0.".$digits."e",$_[0]; |
170
|
535
|
50
|
|
|
|
2281
|
if ($str =~ m/^([+|-]?\d*[.]?\d*e[+|-]?)0(\d\d)$/) {$str=$1.$2; } # for windows - convert 3 digit exponent to 2 digits |
|
0
|
|
|
|
|
0
|
|
171
|
535
|
|
|
|
|
1886
|
return $str; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
sub _strint { |
175
|
|
|
|
|
|
|
# convert an integer to a string in a standard way i.e. removing cross-platform variation in strings displayed for nan and inf |
176
|
40
|
100
|
|
40
|
|
67
|
if (_isNan($_[0])) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
177
|
38
|
|
|
|
|
135
|
return "nan"; |
178
|
|
|
|
|
|
|
} elsif (_isInf($_[0])) { |
179
|
0
|
|
|
|
|
0
|
return "inf" ; |
180
|
|
|
|
|
|
|
} elsif (_isInf(-$_[0])) { |
181
|
0
|
|
|
|
|
0
|
return "-inf" ; |
182
|
|
|
|
|
|
|
} else { |
183
|
2
|
|
|
|
|
9
|
return sprintf "%d",$_[0]; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
4
|
|
|
4
|
|
27
|
use constant NAN => _NaN(); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
6
|
|
188
|
4
|
|
|
4
|
|
20
|
use constant INF => _Inf(); |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
26
|
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# define index into elements in CDP_PREP array |
191
|
4
|
|
|
4
|
|
20
|
use constant VAL => 0; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
188
|
|
192
|
4
|
|
|
4
|
|
19
|
use constant UNKN_PDP_CNT => 1; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
157
|
|
193
|
4
|
|
|
4
|
|
14
|
use constant HW_INTERCEPT => 2; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
250
|
|
194
|
4
|
|
|
4
|
|
51
|
use constant HW_LAST_INTERCEPT => 3; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
150
|
|
195
|
4
|
|
|
4
|
|
16
|
use constant HW_SLOPE => 4; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
163
|
|
196
|
4
|
|
|
4
|
|
17
|
use constant HW_LAST_SLOPE => 5; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
187
|
|
197
|
4
|
|
|
4
|
|
18
|
use constant NULL_COUNT => 6; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
177
|
|
198
|
4
|
|
|
4
|
|
30
|
use constant LAST_NULL_COUNT=> 7; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
169
|
|
199
|
4
|
|
|
4
|
|
19
|
use constant PRIMARY_VAL => 8; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
146
|
|
200
|
4
|
|
|
4
|
|
16
|
use constant SECONDARY_VAL => 9; |
|
4
|
|
|
|
|
2
|
|
|
4
|
|
|
|
|
58659
|
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
###### private functions |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# older versions of Getopt::Long (e.g. used on Mac OS X) don't have this function, so lets add it explicitly |
205
|
|
|
|
|
|
|
sub _GetOptionsFromString(@) { |
206
|
17
|
|
|
17
|
|
31
|
my ($string) = shift; |
207
|
17
|
|
|
|
|
2116
|
require Text::ParseWords; |
208
|
17
|
|
|
|
|
4235
|
my @temp=@ARGV; |
209
|
17
|
|
|
|
|
55
|
@ARGV = Text::ParseWords::shellwords($string); |
210
|
17
|
|
|
|
|
1744
|
my $ret = GetOptions(@_); |
211
|
17
|
|
|
|
|
5056
|
my @args=@ARGV; |
212
|
17
|
|
|
|
|
33
|
@ARGV=@temp; |
213
|
17
|
|
|
|
|
54
|
return ( $ret, \@args ); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
### used to extract information from raw RRD file and build corresponding structured arrays |
217
|
|
|
|
|
|
|
sub _get_header_size { |
218
|
|
|
|
|
|
|
# size of file header, in bytes |
219
|
31
|
|
|
31
|
|
41
|
my $self = $_[0]; my $rrd=$self->{rrd}; |
|
31
|
|
|
|
|
54
|
|
220
|
|
|
|
|
|
|
|
221
|
31
|
|
|
|
|
272
|
return $self->{DS_DEF_IDX} + |
222
|
|
|
|
|
|
|
$self->{DS_EL_SIZE} * $rrd->{ds_cnt} + |
223
|
|
|
|
|
|
|
$self->{RRA_DEF_EL_SIZE} * $rrd->{rra_cnt} + |
224
|
|
|
|
|
|
|
$self->{LIVE_HEAD_SIZE} + |
225
|
|
|
|
|
|
|
$self->{PDP_PREP_EL_SIZE} * $rrd->{ds_cnt} + |
226
|
|
|
|
|
|
|
$self->{CDP_PREP_EL_SIZE} * $rrd->{ds_cnt} * $rrd->{rra_cnt} + |
227
|
|
|
|
|
|
|
$self->{RRA_PTR_EL_SIZE} * $rrd->{rra_cnt} |
228
|
|
|
|
|
|
|
+$self->{HEADER_PAD}; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
#### |
232
|
|
|
|
|
|
|
sub _packd { |
233
|
|
|
|
|
|
|
# pack an array of doubles into a binary string, format determined by $self->{encoding} |
234
|
|
|
|
|
|
|
# - will do packing manually if necessary, to guarantee portability |
235
|
|
|
|
|
|
|
#my ($self,$list_ptr,$encoding) = @_; |
236
|
352
|
|
|
352
|
|
470
|
my $encoding=$_[0]->{encoding}; |
237
|
352
|
100
|
|
|
|
555
|
if (defined($_[2])) {$encoding=$_[2];} |
|
2
|
|
|
|
|
2
|
|
238
|
|
|
|
|
|
|
|
239
|
352
|
50
|
33
|
|
|
2956
|
if ($encoding eq "native-double-simple" || $encoding eq "native-double-mixed") { |
|
|
50
|
33
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# backwards-compatible (with RRDTOOL) RRD format |
241
|
0
|
|
|
|
|
0
|
return pack("d*", @{$_[1]}); |
|
0
|
|
|
|
|
0
|
|
242
|
|
|
|
|
|
|
} elsif ($encoding eq "native-single") { |
243
|
|
|
|
|
|
|
# save some work - we can pack a portable-single using native float |
244
|
0
|
|
|
|
|
0
|
return pack("f*", @{$_[1]}); |
|
0
|
|
|
|
|
0
|
|
245
|
|
|
|
|
|
|
} elsif ($PACK_LITTLE_ENDIAN_SUPPORT && $encoding eq "litteendian-single") { |
246
|
|
|
|
|
|
|
# save some work - we can pack a portable-single using native float |
247
|
0
|
|
|
|
|
0
|
return pack("f<*", @{$_[1]}); |
|
0
|
|
|
|
|
0
|
|
248
|
|
|
|
|
|
|
} elsif ($PACK_LITTLE_ENDIAN_SUPPORT && $encoding eq "littleendian-double") { |
249
|
|
|
|
|
|
|
# shortcut - only difference from portable format is that native format is big-endian |
250
|
150
|
|
|
|
|
159
|
return pack("d<*", @{$_[1]}); |
|
150
|
|
|
|
|
798
|
|
251
|
|
|
|
|
|
|
} |
252
|
202
|
|
|
|
|
139
|
my $f; my $sign; my $shift; my $exp; my $mant; my $string=''; my $significand; my $significandlo; my $significandhi; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
202
|
|
|
|
|
165
|
|
|
202
|
|
|
|
|
125
|
|
|
0
|
|
|
|
|
0
|
|
253
|
202
|
100
|
66
|
|
|
604
|
if ($encoding eq "portable-single" || $encoding eq "ieee-32") { |
|
|
50
|
33
|
|
|
|
|
254
|
|
|
|
|
|
|
# manually pack an IEEE 754 32bit single precision number in little-endian order |
255
|
75
|
|
|
|
|
58
|
for (my $i=0; $i<@{$_[1]}; $i++) { |
|
234
|
|
|
|
|
324
|
|
256
|
159
|
|
|
|
|
93
|
$f=@{$_[1]}[$i]; |
|
159
|
|
|
|
|
157
|
|
257
|
159
|
100
|
|
|
|
164
|
if (_isNan($f)) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
258
|
16
|
|
|
|
|
10
|
$sign=0; $exp=255; $significand=1; |
|
16
|
|
|
|
|
12
|
|
|
16
|
|
|
|
|
12
|
|
259
|
|
|
|
|
|
|
} elsif ($f == -1 * INF) { |
260
|
4
|
|
|
|
|
4
|
$sign=1; $exp=255; $significand=0; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
3
|
|
261
|
|
|
|
|
|
|
} elsif ($f == INF) { |
262
|
4
|
|
|
|
|
4
|
$sign=0; $exp=255; $significand=0; |
|
4
|
|
|
|
|
2
|
|
|
4
|
|
|
|
|
4
|
|
263
|
|
|
|
|
|
|
} elsif ($f == 0) { |
264
|
92
|
|
|
|
|
99
|
$sign=0; $exp=0; $significand=0; |
|
92
|
|
|
|
|
63
|
|
|
92
|
|
|
|
|
56
|
|
265
|
|
|
|
|
|
|
} else { |
266
|
43
|
100
|
|
|
|
51
|
$sign = ($f<0) ? 1 : 0; |
267
|
43
|
100
|
|
|
|
40
|
$f = ($f<0) ? -$f : $f; |
268
|
|
|
|
|
|
|
# get the normalized form of f and track the exponent |
269
|
43
|
|
|
|
|
30
|
$shift = 0; |
270
|
43
|
|
|
|
|
64
|
while($f >= 2) { $f /= 2; $shift++; } |
|
284
|
|
|
|
|
218
|
|
|
284
|
|
|
|
|
336
|
|
271
|
43
|
|
66
|
|
|
88
|
while($f < 1 && $f>0) { $f *= 2; $shift--; } |
|
71
|
|
|
|
|
43
|
|
|
71
|
|
|
|
|
168
|
|
272
|
43
|
|
|
|
|
33
|
$f -= 1; |
273
|
|
|
|
|
|
|
# calculate the binary form (non-float) of the significand data |
274
|
43
|
|
|
|
|
34
|
$significand = int($f*(2**23)); |
275
|
|
|
|
|
|
|
# get the biased exponent |
276
|
43
|
|
|
|
|
30
|
$exp = int($shift + ((1<<7) - 1)); # shift + bias |
277
|
|
|
|
|
|
|
} |
278
|
159
|
|
|
|
|
279
|
$string.=pack("V",($sign<<31) | ($exp<<23) | $significand); |
279
|
|
|
|
|
|
|
} |
280
|
75
|
|
|
|
|
112
|
return $string; |
281
|
|
|
|
|
|
|
} elsif ($encoding eq "portable-double" || $encoding eq "ieee-64") { |
282
|
|
|
|
|
|
|
# manuallly pack IEEE 754 64 bit double precision in little-endian order |
283
|
127
|
|
|
|
|
104
|
for (my $i=0; $i<@{$_[1]}; $i++) { |
|
500
|
|
|
|
|
869
|
|
284
|
373
|
|
|
|
|
255
|
$f=@{$_[1]}[$i]; |
|
373
|
|
|
|
|
411
|
|
285
|
373
|
100
|
|
|
|
470
|
if (_isNan($f)) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
286
|
136
|
|
|
|
|
105
|
$sign=0; $exp=2047; $significandhi=1;$significandlo=1; |
|
136
|
|
|
|
|
103
|
|
|
136
|
|
|
|
|
85
|
|
|
136
|
|
|
|
|
101
|
|
287
|
|
|
|
|
|
|
} elsif ($f == -1 * INF) { |
288
|
4
|
|
|
|
|
4
|
$sign=1; $exp=2047; $significandhi=0;$significandlo=0; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
4
|
|
289
|
|
|
|
|
|
|
} elsif ($f == INF) { |
290
|
4
|
|
|
|
|
4
|
$sign=0; $exp=2047; $significandhi=0;$significandlo=0; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
2
|
|
|
4
|
|
|
|
|
4
|
|
291
|
|
|
|
|
|
|
} elsif ($f ==0) { |
292
|
93
|
|
|
|
|
69
|
$sign=0; $exp=0; $significandhi=0;$significandlo=0; |
|
93
|
|
|
|
|
69
|
|
|
93
|
|
|
|
|
52
|
|
|
93
|
|
|
|
|
71
|
|
293
|
|
|
|
|
|
|
} else { |
294
|
136
|
100
|
|
|
|
166
|
$sign = ($f<0) ? 1 : 0; |
295
|
136
|
100
|
|
|
|
191
|
$f = ($f<0) ? -$f : $f; |
296
|
|
|
|
|
|
|
# get the normalized form of f and track the exponent |
297
|
136
|
|
|
|
|
107
|
$shift = 0; |
298
|
136
|
|
|
|
|
204
|
while($f >= 2) { $f /= 2; $shift++; } |
|
607
|
|
|
|
|
420
|
|
|
607
|
|
|
|
|
756
|
|
299
|
136
|
|
66
|
|
|
321
|
while($f < 1 && $f>0 ) { $f *= 2; $shift--; } |
|
210
|
|
|
|
|
143
|
|
|
210
|
|
|
|
|
486
|
|
300
|
136
|
|
|
|
|
101
|
$f -= 1; |
301
|
|
|
|
|
|
|
# calculate the binary form (non-float) of the significand data |
302
|
136
|
|
|
|
|
148
|
$significandhi = int($f*(2**20)); |
303
|
136
|
|
|
|
|
135
|
$significandlo = int( ($f-$significandhi/(2**20))*(2**52)); |
304
|
|
|
|
|
|
|
# get the biased exponent |
305
|
136
|
|
|
|
|
120
|
$exp = int($shift + ((1<<10) - 1)); # shift + bias |
306
|
|
|
|
|
|
|
} |
307
|
373
|
|
|
|
|
833
|
$string.=pack("V V",$significandlo, ($sign<<31) | ($exp<<20) | $significandhi); |
308
|
|
|
|
|
|
|
} |
309
|
127
|
|
|
|
|
390
|
return $string; |
310
|
|
|
|
|
|
|
} else { |
311
|
0
|
|
|
|
|
0
|
croak("packd:unknown encoding: ".$encoding."\n"); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
##### |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub _unpackd { |
318
|
|
|
|
|
|
|
# unpack binary string into array of doubles, format determined by $self->{encoding} |
319
|
|
|
|
|
|
|
# - will do unpacking manually if necessary, to guarantee portability |
320
|
|
|
|
|
|
|
#my ($self, $string, $encoding) = @_; |
321
|
587
|
|
|
587
|
|
874
|
my $encoding=$_[0]->{encoding}; |
322
|
587
|
100
|
|
|
|
1004
|
if (defined($_[2])) {$encoding=$_[2];} |
|
30
|
|
|
|
|
40
|
|
323
|
|
|
|
|
|
|
|
324
|
587
|
50
|
33
|
|
|
5059
|
if ($encoding eq "native-double-simple" || $encoding eq "native-double-mixed") { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# backwards-compatible (with RRDTOOL) RRD format |
326
|
0
|
|
|
|
|
0
|
return unpack("d*", $_[1]); |
327
|
|
|
|
|
|
|
} elsif ($encoding eq "native-single" ) { |
328
|
|
|
|
|
|
|
# save some work - we can unpack portable-single using native float |
329
|
19
|
|
|
|
|
59
|
return unpack("f*", $_[1]); |
330
|
|
|
|
|
|
|
} elsif ($PACK_LITTLE_ENDIAN_SUPPORT && $encoding eq "littleendian-single" ) { |
331
|
|
|
|
|
|
|
# save some work - we can unpack portable-single using native float |
332
|
6
|
|
|
|
|
28
|
return unpack("f<*", $_[1]); |
333
|
|
|
|
|
|
|
} elsif ($PACK_LITTLE_ENDIAN_SUPPORT && $encoding eq "littleendian-double") { |
334
|
|
|
|
|
|
|
# shortcut - only difference from portable format is that native format is big-endian |
335
|
525
|
|
|
|
|
1802
|
return unpack("d<*", $_[1]); |
336
|
|
|
|
|
|
|
} |
337
|
37
|
|
|
|
|
39
|
my $word; my $sign; my $expo; my $mant; my $manthi; my $mantlo; my @list; my $num; my $i; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
338
|
37
|
100
|
66
|
|
|
164
|
if ($encoding eq "portable-single" || $encoding eq "ieee-32") { |
|
|
50
|
33
|
|
|
|
|
339
|
|
|
|
|
|
|
# manually unpack a little-endian IEEE 754 32bit single-precision number |
340
|
6
|
|
|
|
|
35
|
for ($i=0; $i
|
341
|
6
|
|
|
|
|
49
|
$word = (unpack("C",substr($_[1],$i+3,1)) << 24) + (unpack("C",substr($_[1],$i+2,1)) << 16) + (unpack("C",substr($_[1],$i+1,1)) << 8) + unpack("C",substr($_[1],$i,1)); |
342
|
6
|
|
|
|
|
17
|
$expo = (($word & 0x7F800000) >> 23) - 127; |
343
|
6
|
|
|
|
|
15
|
$mant = (($word & 0x007FFFFF) | 0x00800000); |
344
|
6
|
100
|
|
|
|
21
|
$sign = ($word & 0x80000000) ? -1 : 1; |
345
|
6
|
50
|
33
|
|
|
50
|
if ($expo == 128 && $mant == 0 ) { |
|
|
50
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
346
|
0
|
0
|
|
|
|
0
|
$num=$sign>0 ? INF : -1 * INF; |
347
|
|
|
|
|
|
|
} elsif ($expo == 128) { |
348
|
0
|
|
|
|
|
0
|
$num=NAN; |
349
|
|
|
|
|
|
|
} elsif ($expo == -127 && $mant ==0) { |
350
|
0
|
|
|
|
|
0
|
$num=0; |
351
|
|
|
|
|
|
|
} else { |
352
|
6
|
|
|
|
|
58
|
$num = $sign * (2**($expo-23))*$mant; |
353
|
|
|
|
|
|
|
} |
354
|
6
|
|
|
|
|
25
|
push (@list, $num); |
355
|
|
|
|
|
|
|
} |
356
|
6
|
|
|
|
|
20
|
return @list; |
357
|
|
|
|
|
|
|
} elsif ($encoding eq "portable-double" || $encoding eq "ieee-64") { |
358
|
|
|
|
|
|
|
# manually unpack IEEE 754 64 bit double-precision number. |
359
|
31
|
|
|
|
|
84
|
for ($i=0; $i
|
360
|
106
|
|
|
|
|
271
|
$word = (unpack("C",substr($_[1],$i+7,1)) << 24) + (unpack("C",substr($_[1],$i+6,1)) << 16) + (unpack("C",substr($_[1],$i+5,1)) << 8) + unpack("C",substr($_[1],$i+4,1)); |
361
|
106
|
|
|
|
|
231
|
$mantlo = (unpack("C",substr($_[1],$i+3,1)) << 24) + (unpack("C",substr($_[1],$i+2,1)) << 16) + (unpack("C",substr($_[1],$i+1,1)) << 8) + unpack("C",substr($_[1],$i,1)); |
362
|
106
|
|
|
|
|
100
|
$expo = (($word & 0x7FF00000) >> 20) - 1023; |
363
|
106
|
|
|
|
|
70
|
$manthi = ($word & 0x000FFFFF) ; |
364
|
106
|
100
|
|
|
|
130
|
$sign = ($word & 0x80000000) ? -1 : 1; |
365
|
106
|
50
|
66
|
|
|
393
|
if ($expo == 1024 && $mantlo == 0 && $manthi==0 ) { |
|
|
100
|
33
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
366
|
0
|
|
|
|
|
0
|
$num=$sign * INF; |
367
|
|
|
|
|
|
|
} elsif ($expo == 1024) { |
368
|
16
|
|
|
|
|
15
|
$num=NAN; |
369
|
|
|
|
|
|
|
} elsif ($expo==-1023 && $manthi==0 && $mantlo==0) { |
370
|
1
|
|
|
|
|
1
|
$num=0; |
371
|
|
|
|
|
|
|
} else { |
372
|
89
|
|
|
|
|
205
|
$num = $sign * ( (2**$expo) + (2**($expo-20))*$manthi + (2**($expo-52))*$mantlo ); |
373
|
|
|
|
|
|
|
} |
374
|
106
|
|
|
|
|
174
|
push (@list, $num); |
375
|
|
|
|
|
|
|
} |
376
|
31
|
|
|
|
|
85
|
return @list; |
377
|
|
|
|
|
|
|
} else { |
378
|
0
|
|
|
|
|
0
|
croak("unpackd:unknown encoding: ".$encoding."\n"); |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
##### |
383
|
|
|
|
|
|
|
sub _packlongchar { |
384
|
|
|
|
|
|
|
# pack encoding specification for integers. no need for manual packing/unpacking of integers as agreed portable formats already available |
385
|
39
|
|
|
39
|
|
48
|
my $self=$_[0]; |
386
|
39
|
50
|
33
|
|
|
181
|
if ($self->{encoding} eq "native-double-simple" || $self->{encoding} eq "native-double-mixed") { |
387
|
|
|
|
|
|
|
# backwards-compatible (with RRDTOOL) RRD format |
388
|
0
|
|
|
|
|
0
|
return "L!"; # native long int |
389
|
|
|
|
|
|
|
} else { |
390
|
|
|
|
|
|
|
# portable format, little-endian 32bit long int |
391
|
39
|
|
|
|
|
85
|
return "V"; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
#### |
396
|
|
|
|
|
|
|
sub _sizes { |
397
|
|
|
|
|
|
|
# define the sizes of the various elements in RRD binary file |
398
|
10
|
|
|
10
|
|
18
|
my ($self)=@_; |
399
|
|
|
|
|
|
|
|
400
|
10
|
|
|
|
|
23
|
$self->{OFFSET} = 12; # byte position of start of float cookie. |
401
|
10
|
|
|
|
|
14
|
$self->{RRA_DEL_PAD} = 0; # for byte alignment in RRA_DEF after char(20) string |
402
|
10
|
|
|
|
|
18
|
$self->{STAT_PAD} = 0; # for byte alignment at end of static header. |
403
|
10
|
|
|
|
|
22
|
$self->{RRA_PAD} = 0; # for byte alignment at end of RRAD_DEF float array |
404
|
10
|
50
|
33
|
|
|
215
|
if ($self->{encoding} eq "native-double-simple" || $self->{encoding} eq "native-double-mixed") { |
|
|
100
|
66
|
|
|
|
|
|
|
50
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
405
|
0
|
|
|
|
|
0
|
$self->{LONG_EL_SIZE} = NATIVE_LONG_EL_SIZE; |
406
|
0
|
|
|
|
|
0
|
$self->{FLOAT_EL_SIZE}= NATIVE_DOUBLE_EL_SIZE; |
407
|
0
|
|
|
|
|
0
|
$self->{COOKIE} = NATIVE_BINARY_FLOATCOOKIE; |
408
|
0
|
|
|
|
|
0
|
if ( NATIVE_LONG_EL_SIZE == 8) { |
409
|
|
|
|
|
|
|
# long ints and doubles are both 64 bits, alignment is at 64 bit boundaries for both native-double-simple and native-double-mixed |
410
|
0
|
|
|
|
|
0
|
$self->{OFFSET} = 16; # 64 bit alignment of the float cookie |
411
|
0
|
|
|
|
|
0
|
$self->{RRA_DEL_PAD} = 4; # 64 bit alignment for first long in RRA_DEF after char(20) string |
412
|
|
|
|
|
|
|
} elsif ( NATIVE_LONG_EL_SIZE == 4 && $self->{encoding} eq "native-double-mixed") { |
413
|
|
|
|
|
|
|
# 32 bit native-double-mixed: align long ints at 32 bit boundaries and doubles at 64 bit boundaries. |
414
|
|
|
|
|
|
|
$self->{OFFSET} = 16; # 64 bit alignment of the float cookie |
415
|
|
|
|
|
|
|
$self->{RRA_DEL_PAD} = 0; # 32 bit alignment first long in RRA_DEF after char(20) string |
416
|
|
|
|
|
|
|
$self->{STAT_PAD} = 4; # 64 bit alignment for start of DS defs |
417
|
|
|
|
|
|
|
$self->{RRA_PAD} = 4; # 64 bit alignment for first double in RRA_DEF |
418
|
|
|
|
|
|
|
} else { |
419
|
|
|
|
|
|
|
# default is 32 bit native-double-simple: align both long ints and doubles at 32 bit boundaries. |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} elsif ($self->{encoding} eq "littleendian-single" || $self->{encoding} eq "native-single" || $self->{encoding} eq "portable-single" || $self->{encoding} eq "ieee-32") { |
422
|
2
|
|
|
|
|
4
|
$self->{LONG_EL_SIZE} = PORTABLE_LONG_EL_SIZE; |
423
|
2
|
|
|
|
|
1
|
$self->{FLOAT_EL_SIZE}= PORTABLE_SINGLE_EL_SIZE; # 32 bits |
424
|
2
|
|
|
|
|
4
|
my @cookie=(SINGLE_FLOATCOOKIE); |
425
|
2
|
|
|
|
|
5
|
$self->{COOKIE} = $self->_packd(\@cookie,"portable-single"); |
426
|
|
|
|
|
|
|
} elsif ($self->{encoding} eq "littleendian-double" || $self->{encoding} eq "portable-double" || $self->{encoding} eq "ieee-64") { |
427
|
8
|
|
|
|
|
19
|
$self->{LONG_EL_SIZE} = PORTABLE_LONG_EL_SIZE; |
428
|
8
|
|
|
|
|
16
|
$self->{FLOAT_EL_SIZE}= PORTABLE_DOUBLE_EL_SIZE; # 64 bits |
429
|
8
|
|
|
|
|
23
|
$self->{COOKIE} = PORTABLE_BINARY_FLOATCOOKIE; |
430
|
|
|
|
|
|
|
} |
431
|
10
|
|
|
|
|
33
|
$self->{DIFF_SIZE} = $self->{FLOAT_EL_SIZE} - $self->{LONG_EL_SIZE}; |
432
|
10
|
|
|
|
|
37
|
$self->{STAT_HEADER_SIZE} = $self->{OFFSET} + $self->{FLOAT_EL_SIZE} + 3 * $self->{LONG_EL_SIZE}; |
433
|
10
|
|
|
|
|
30
|
$self->{STAT_HEADER_SIZE0} = $self->{STAT_HEADER_SIZE} + 10 * $self->{FLOAT_EL_SIZE} + $self->{STAT_PAD}; |
434
|
10
|
|
|
|
|
20
|
$self->{RRA_PTR_EL_SIZE} = $self->{LONG_EL_SIZE}; |
435
|
10
|
|
|
|
|
27
|
$self->{CDP_PREP_EL_SIZE} = 10 * $self->{FLOAT_EL_SIZE}; |
436
|
10
|
|
|
|
|
18
|
$self->{PDP_PREP_PAD} = 2; # for byte alignment of char(30) string in PDP_PREP |
437
|
10
|
|
|
|
|
34
|
$self->{PDP_PREP_EL_SIZE} = 30 + $self->{PDP_PREP_PAD} + 10 * $self->{FLOAT_EL_SIZE}; |
438
|
10
|
|
|
|
|
37
|
$self->{RRA_DEF_EL_SIZE} = 20 + $self->{RRA_DEL_PAD} + 2 * $self->{LONG_EL_SIZE} + 10 * $self->{FLOAT_EL_SIZE} +$self->{RRA_PAD}; |
439
|
10
|
|
|
|
|
18
|
$self->{DS_DEF_IDX} = $self->{STAT_HEADER_SIZE0}; |
440
|
10
|
|
|
|
|
21
|
$self->{DS_EL_SIZE} = 40 + 10 * $self->{FLOAT_EL_SIZE} ; |
441
|
10
|
|
|
|
|
23
|
$self->{LIVE_HEAD_SIZE} = 2 * $self->{LONG_EL_SIZE}; |
442
|
10
|
|
|
|
|
20
|
$self->{HEADER_PAD} = 0; # accounting for pad bytes at end of header (e.g. 8 pad bytes are added on Linux/Intel 64 bit platforms) |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
#### |
446
|
|
|
|
|
|
|
sub _extractDSdefs { |
447
|
|
|
|
|
|
|
# extract DS definitions from raw header (which must have been already read using rrd_open) |
448
|
6
|
|
|
6
|
|
11
|
my ($self, $header, $idx) = @_; my $rrd=$self->{rrd}; |
|
6
|
|
|
|
|
11
|
|
449
|
|
|
|
|
|
|
|
450
|
6
|
|
|
|
|
7
|
my $i; |
451
|
6
|
|
|
|
|
17
|
my $L=$self->_packlongchar(); |
452
|
6
|
|
|
|
|
15
|
@{$rrd->{ds}}=[]; |
|
6
|
|
|
|
|
63
|
|
453
|
6
|
|
|
|
|
28
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
454
|
24
|
|
|
|
|
30
|
my $ds={}; |
455
|
|
|
|
|
|
|
#($ds->{name}, $ds->{type}, $ds->{hb}, $ds->{min}, $ds->{max})= unpack("Z20 Z20 $L x".DIFF_SIZE." d d",substr(${$header},$idx,DS_EL_SIZE)); |
456
|
24
|
|
|
|
|
35
|
($ds->{name}, $ds->{type}, $ds->{hb})= unpack("Z20 Z20 $L",substr(${$header},$idx,40+$self->{LONG_EL_SIZE})); |
|
24
|
|
|
|
|
135
|
|
457
|
24
|
|
|
|
|
36
|
($ds->{min}, $ds->{max})= $self->_unpackd(substr(${$header},$idx+40+$self->{FLOAT_EL_SIZE},2*$self->{FLOAT_EL_SIZE})); |
|
24
|
|
|
|
|
78
|
|
458
|
24
|
|
|
|
|
47
|
$rrd->{ds}[$i] = $ds; |
459
|
24
|
|
|
|
|
71
|
$idx+=$self->{DS_EL_SIZE}; |
460
|
|
|
|
|
|
|
#print $ds->{name}," ",$ds->{type}," ",$ds->{hb}," ",$ds->{min}," ",$ds->{max},"\n"; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
### |
465
|
|
|
|
|
|
|
sub _extractRRAdefs { |
466
|
|
|
|
|
|
|
# extract RRA definitions from raw header (which must have been already read using rrd_open) |
467
|
6
|
|
|
6
|
|
11
|
my ($self, $header, $idx) = @_; my $rrd=$self->{rrd}; |
|
6
|
|
|
|
|
20
|
|
468
|
|
|
|
|
|
|
|
469
|
6
|
|
|
|
|
8
|
my $i; |
470
|
6
|
|
|
|
|
14
|
my $L=$self->_packlongchar(); |
471
|
6
|
|
|
|
|
15
|
@{$rrd->{rra}}=[]; |
|
6
|
|
|
|
|
140
|
|
472
|
6
|
|
|
|
|
28
|
for ($i=0; $i<$rrd->{rra_cnt}; $i++) { |
473
|
30
|
|
|
|
|
37
|
my $rra={}; |
474
|
30
|
|
|
|
|
74
|
($rra->{name}, $rra->{row_cnt}, $rra->{pdp_cnt})= unpack("Z".(20+$self->{RRA_DEL_PAD})." $L $L",substr(${$header},$idx,20+$self->{RRA_DEL_PAD}+2*$self->{LONG_EL_SIZE})); |
|
30
|
|
|
|
|
147
|
|
475
|
30
|
|
|
|
|
41
|
($rra->{xff})= $self->_unpackd(substr(${$header},$idx+20+$self->{RRA_DEL_PAD} + 2*$self->{LONG_EL_SIZE}+$self->{RRA_PAD}, $self->{FLOAT_EL_SIZE})); |
|
30
|
|
|
|
|
95
|
|
476
|
30
|
|
|
|
|
58
|
$rrd->{rra}[$i] = $rra; |
477
|
30
|
|
|
|
|
79
|
$idx+=$self->{RRA_DEF_EL_SIZE}; |
478
|
|
|
|
|
|
|
#print $rra->{name}," ",$rra->{row_cnt}," ",$rra->{pdp_cnt}," ",$rra->{xff},"\n"; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
#### |
483
|
|
|
|
|
|
|
sub _extractPDPprep { |
484
|
|
|
|
|
|
|
# extract PDP prep from raw header (which must have been already read using rrd_open) |
485
|
6
|
|
|
6
|
|
11
|
my ($self, $header, $idx) = @_; my $rrd=$self->{rrd}; |
|
6
|
|
|
|
|
13
|
|
486
|
|
|
|
|
|
|
|
487
|
6
|
|
|
|
|
9
|
my $i; |
488
|
6
|
|
|
|
|
17
|
my $L=$self->_packlongchar(); |
489
|
6
|
|
|
|
|
13
|
@{$rrd->{pdp_prep}}=[]; |
|
6
|
|
|
|
|
19
|
|
490
|
6
|
|
|
|
|
28
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
491
|
24
|
|
|
|
|
29
|
my $pdp={}; |
492
|
24
|
|
|
|
|
52
|
($pdp->{last_ds}, $pdp->{unkn_sec_cnt})= unpack("Z".(30+$self->{PDP_PREP_PAD})." $L",substr(${$header},$idx,30+$self->{PDP_PREP_PAD}+$self->{LONG_EL_SIZE})); # NB Z32 instead of Z30 due to byte alignment |
|
24
|
|
|
|
|
104
|
|
493
|
24
|
|
|
|
|
30
|
($pdp->{val})= $self->_unpackd(substr(${$header},$idx+30+$self->{PDP_PREP_PAD}+$self->{FLOAT_EL_SIZE},$self->{FLOAT_EL_SIZE})); |
|
24
|
|
|
|
|
86
|
|
494
|
24
|
|
|
|
|
59
|
$rrd->{ds}[$i]->{pdp_prep} = $pdp; |
495
|
24
|
|
|
|
|
69
|
$idx+=$self->{PDP_PREP_EL_SIZE}; |
496
|
|
|
|
|
|
|
#print $pdp->{last_ds}," ",$pdp->{unkn_sec_cnt}," ",$pdp->{val},"\n"; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
### |
501
|
|
|
|
|
|
|
sub _extractCDPprep { |
502
|
|
|
|
|
|
|
# extract CDP prep from raw header (which must have been already read using rrd_open) |
503
|
6
|
|
|
6
|
|
10
|
my ($self, $header, $idx) = @_; my $rrd=$self->{rrd}; |
|
6
|
|
|
|
|
13
|
|
504
|
|
|
|
|
|
|
|
505
|
6
|
|
|
|
|
7
|
my $i; my $ii; |
506
|
6
|
|
|
|
|
16
|
my $L=$self->_packlongchar(); |
507
|
6
|
|
|
|
|
24
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
508
|
|
|
|
|
|
|
#@{$rrd->{cdp_prep}[$ii]}=[]; |
509
|
30
|
|
|
|
|
77
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
510
|
|
|
|
|
|
|
# do a bit of code optimisation to aggregate function calls and array allocation here, since run inside inner loop. |
511
|
120
|
50
|
33
|
|
|
499
|
if ($self->{encoding} eq "native-double-simple" || $self->{encoding} eq "native-double-mixed") { |
|
|
100
|
|
|
|
|
|
512
|
0
|
|
|
|
|
0
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]} = unpack("d $L x".$self->{DIFF_SIZE}." d d d d $L x".$self->{DIFF_SIZE}." $L x".$self->{DIFF_SIZE}." d d",substr(${$header},$idx,$self->{CDP_PREP_EL_SIZE})); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
513
|
0
|
|
|
|
|
0
|
$idx+=$self->{CDP_PREP_EL_SIZE}; |
514
|
|
|
|
|
|
|
} elsif ($self->{encoding} eq "native-single") { |
515
|
20
|
|
|
|
|
36
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]} = unpack("f $L x".$self->{DIFF_SIZE}." f f f f $L x".$self->{DIFF_SIZE}." $L x".$self->{DIFF_SIZE}." f f",substr(${$header},$idx,$self->{CDP_PREP_EL_SIZE})); |
|
20
|
|
|
|
|
46
|
|
|
20
|
|
|
|
|
41
|
|
516
|
20
|
|
|
|
|
42
|
$idx+=$self->{CDP_PREP_EL_SIZE}; |
517
|
|
|
|
|
|
|
} else { |
518
|
100
|
|
|
|
|
106
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}=(0,0,0,0,0,0,0,0,0,0); # pre-allocate array |
|
100
|
|
|
|
|
384
|
|
519
|
100
|
|
|
|
|
110
|
(@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[0])=$self->_unpackd(substr(${$header},$idx,$self->{FLOAT_EL_SIZE})); $idx+=$self->{FLOAT_EL_SIZE}; |
|
100
|
|
|
|
|
250
|
|
|
100
|
|
|
|
|
262
|
|
|
100
|
|
|
|
|
217
|
|
520
|
100
|
|
|
|
|
152
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[1]=unpack("$L x".$self->{DIFF_SIZE},substr(${$header},$idx,$self->{FLOAT_EL_SIZE})); $idx+=$self->{FLOAT_EL_SIZE}; |
|
100
|
|
|
|
|
228
|
|
|
100
|
|
|
|
|
227
|
|
|
100
|
|
|
|
|
128
|
|
521
|
100
|
|
|
|
|
99
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[2..5]=$self->_unpackd(substr(${$header},$idx,4*$self->{FLOAT_EL_SIZE})); $idx+=4*$self->{FLOAT_EL_SIZE}; |
|
100
|
|
|
|
|
302
|
|
|
100
|
|
|
|
|
252
|
|
|
100
|
|
|
|
|
203
|
|
522
|
100
|
|
|
|
|
217
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[6..7]=unpack("$L x".$self->{DIFF_SIZE}." $L x".$self->{DIFF_SIZE},substr(${$header},$idx,2*$self->{FLOAT_EL_SIZE})); $idx+=2*$self->{FLOAT_EL_SIZE}; |
|
100
|
|
|
|
|
204
|
|
|
100
|
|
|
|
|
246
|
|
|
100
|
|
|
|
|
157
|
|
523
|
100
|
|
|
|
|
103
|
@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[8..9]=$self->_unpackd(substr(${$header},$idx,2*$self->{FLOAT_EL_SIZE})); $idx+=2*$self->{FLOAT_EL_SIZE}; |
|
100
|
|
|
|
|
289
|
|
|
100
|
|
|
|
|
241
|
|
|
100
|
|
|
|
|
375
|
|
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
### |
530
|
|
|
|
|
|
|
sub _extractRRAptr { |
531
|
|
|
|
|
|
|
# array of { cur_row } pointers into current row in rra from raw header (which must have been already read using rrd_open) |
532
|
6
|
|
|
6
|
|
11
|
my ($self, $header, $idx) = @_; my $rrd=$self->{rrd}; |
|
6
|
|
|
|
|
13
|
|
533
|
|
|
|
|
|
|
|
534
|
6
|
|
|
|
|
17
|
my $L=$self->_packlongchar(); |
535
|
6
|
|
|
|
|
17
|
my @ptr=unpack("$L*",substr(${$header},$idx,$self->{RRA_PTR_EL_SIZE}*$rrd->{rra_cnt})); |
|
6
|
|
|
|
|
33
|
|
536
|
6
|
|
|
|
|
7
|
my $i; |
537
|
6
|
|
|
|
|
23
|
for ($i=0; $i<$rrd->{rra_cnt}; $i++) { |
538
|
30
|
|
|
|
|
85
|
$rrd->{rra}[$i]->{ptr}=$ptr[$i]; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
#print @ptr; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
### |
544
|
|
|
|
|
|
|
sub _loadRRAdata { |
545
|
|
|
|
|
|
|
# read in and extract the RRA data. assumes rrd_open has been called to read in file header and |
546
|
|
|
|
|
|
|
# populate the RRD data structure |
547
|
4
|
|
|
4
|
|
8
|
my $self = $_[0]; my $rrd=$self->{rrd}; |
|
4
|
|
|
|
|
10
|
|
548
|
4
|
50
|
|
|
|
16
|
if (!defined($self->{fd})) {croak("loadRRDdata: must call open() first\n");} |
|
0
|
|
|
|
|
0
|
|
549
|
|
|
|
|
|
|
|
550
|
4
|
|
|
|
|
7
|
my $data; my $ds_cnt=$self->{FLOAT_EL_SIZE} * $rrd->{ds_cnt}; |
|
4
|
|
|
|
|
14
|
|
551
|
4
|
|
|
|
|
19
|
seek $self->{fd},$self->_get_header_size,0; # move to start of RRA data within file |
552
|
4
|
|
|
|
|
26
|
for (my $ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
553
|
20
|
|
|
|
|
32
|
my $idx=0; |
554
|
20
|
|
|
|
|
101
|
read($self->{fd}, $data, $self->{FLOAT_EL_SIZE} * $rrd->{ds_cnt}* $rrd->{rra}[$ii]->{row_cnt} ); |
555
|
20
|
|
|
|
|
33
|
my $row_cnt=$rrd->{rra}[$ii]->{row_cnt}; |
556
|
20
|
|
|
|
|
48
|
for (my $i=0; $i<$row_cnt; $i++) { |
557
|
|
|
|
|
|
|
# rather than unpack here, do "lazy" unpacks i.e. only when needed - much faster |
558
|
|
|
|
|
|
|
#@{$rrd->{rra}[$ii]->{data}[$i]}=unpack("d*",substr($data,$idx,$ds_cnt}) ); |
559
|
100
|
|
|
|
|
218
|
$rrd->{rra}[$ii]->{data}[$i]=substr($data,$idx,$ds_cnt); |
560
|
100
|
|
|
|
|
216
|
$idx+=$ds_cnt; |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
#print "rra $ii:", join(", ",@{$rrd->{rra_data}[$ii][$rrd->{rra_ptr}[$ii]+1]}),"\n"; |
563
|
|
|
|
|
|
|
} |
564
|
4
|
|
|
|
|
13
|
$rrd->{dataloaded}=1; # record the fact that the data is now loaded in memory |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
#### |
568
|
|
|
|
|
|
|
sub _findDSidx { |
569
|
|
|
|
|
|
|
# find the index of a DS given its name |
570
|
23
|
|
|
23
|
|
35
|
my ($self, $name) = @_; my $rrd=$self->{rrd}; |
|
23
|
|
|
|
|
33
|
|
571
|
23
|
|
|
|
|
22
|
my $i; |
572
|
23
|
|
|
|
|
69
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
573
|
39
|
100
|
|
|
|
115
|
if ($rrd->{ds}[$i]->{name} eq $name) { |
574
|
23
|
|
|
|
|
55
|
return $i; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
} |
577
|
0
|
|
|
|
|
0
|
return -1; # unknown source |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
################ public functions |
581
|
|
|
|
|
|
|
sub new { |
582
|
|
|
|
|
|
|
# create new object |
583
|
3
|
|
|
3
|
1
|
1480
|
my $self; |
584
|
3
|
|
|
|
|
12
|
$self->{file_name}=undef; # name of RRD file |
585
|
3
|
|
|
|
|
9
|
$self->{fd}=undef; # file handle |
586
|
3
|
|
|
|
|
9
|
$self->{encoding}=undef; # binary encoding within file. |
587
|
3
|
|
|
|
|
11
|
$self->{rrd}->{version}=undef; |
588
|
3
|
|
|
|
|
8
|
$self->{rrd}->{rra_cnt}= undef; # number of RRAs |
589
|
3
|
|
|
|
|
10
|
$self->{rrd}->{ds_cnt}=undef; # number of DSs |
590
|
3
|
|
|
|
|
8
|
$self->{rrd}->{pdp_step}=undef; # min time step size |
591
|
3
|
|
|
|
|
7
|
$self->{rrd}->{last_up} = undef; # time when last updated |
592
|
3
|
|
|
|
|
9
|
$self->{rrd}->{ds}=undef; # array of DS definitions |
593
|
3
|
|
|
|
|
9
|
$self->{rrd}->{rra}=undef; # array of RRA info |
594
|
3
|
|
|
|
|
10
|
$self->{rrd}->{dataloaded}=undef; # has body of RRD file been loaded into memory ? |
595
|
3
|
|
|
|
|
6
|
bless $self; |
596
|
3
|
|
|
|
|
13
|
return $self; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
sub DS_names { |
600
|
|
|
|
|
|
|
# return a list containing the names of the DS's in the RRD database. |
601
|
2
|
|
|
2
|
1
|
4
|
my $rrd=$_[0]->{rrd}; |
602
|
2
|
|
|
|
|
3
|
my @names=(); my $i; |
|
2
|
|
|
|
|
3
|
|
603
|
2
|
|
|
|
|
8
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
604
|
8
|
|
|
|
|
18
|
push(@names, $rrd->{ds}[$i]->{name}); |
605
|
|
|
|
|
|
|
} |
606
|
2
|
|
|
|
|
10
|
return @names; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
sub num_RRAs { |
610
|
|
|
|
|
|
|
# returns the number of RRA's in the database. RRAs are indexed from 0 .. num_RRAs-1. |
611
|
0
|
|
|
0
|
1
|
0
|
return $_[0]->{rrd}->{rra_cnt}; |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
sub RRA_numrows { |
615
|
|
|
|
|
|
|
# return number of rows in a RRA |
616
|
2
|
|
|
2
|
1
|
3
|
my ($self, $rraidx) = @_; my $rrd=$self->{rrd}; |
|
2
|
|
|
|
|
4
|
|
617
|
2
|
50
|
33
|
|
|
9
|
if ($rraidx > $rrd->{rra_cnt} || $rraidx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
618
|
2
|
|
|
|
|
9
|
return $rrd->{rra}[$rraidx]->{row_cnt}; |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
sub RRA_type { |
622
|
|
|
|
|
|
|
# return the type of an RRA (AVERAGE, MAX etc) |
623
|
0
|
|
|
0
|
1
|
0
|
my ($self, $rraidx) = @_; my $rrd=$self->{rrd}; |
|
0
|
|
|
|
|
0
|
|
624
|
0
|
0
|
0
|
|
|
0
|
if ($rraidx > $rrd->{rra_cnt} || $rraidx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
625
|
0
|
|
|
|
|
0
|
return $rrd->{rra}[$rraidx]->{name}; |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
sub RRA_step { |
629
|
|
|
|
|
|
|
# return the step size (in seconds) used in an RRA |
630
|
2
|
|
|
2
|
1
|
4
|
my ($self, $rraidx) = @_; my $rrd=$self->{rrd}; |
|
2
|
|
|
|
|
3
|
|
631
|
2
|
50
|
33
|
|
|
10
|
if ($rraidx > $rrd->{rra_cnt} || $rraidx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
632
|
2
|
|
|
|
|
10
|
return $rrd->{rra}[$rraidx]->{pdp_cnt}*$rrd->{pdp_step}; |
633
|
|
|
|
|
|
|
} |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
sub RRA_xff { |
636
|
|
|
|
|
|
|
# return the xff value for an RRA |
637
|
2
|
|
|
2
|
1
|
4
|
my ($self, $idx) = @_; my $rrd=$self->{rrd}; |
|
2
|
|
|
|
|
3
|
|
638
|
2
|
50
|
33
|
|
|
11
|
if ($idx > $rrd->{rra_cnt} || $idx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
639
|
2
|
|
|
|
|
13
|
return $rrd->{rra}[$idx]->{xff}; |
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
sub RRA_el { |
643
|
|
|
|
|
|
|
# fetch a specified element from a specified RRA. |
644
|
|
|
|
|
|
|
# given the index number of the RRA, the index of the DS and the row within the RRA (oldest row is 0), |
645
|
|
|
|
|
|
|
# returns a pair (t,d) where t is the unix timestamp of the data point and d is the data value |
646
|
3
|
|
|
3
|
1
|
8
|
my ($self, $rraidx, $ds_name, $tidx) = @_; my $rrd=$self->{rrd}; |
|
3
|
|
|
|
|
5
|
|
647
|
|
|
|
|
|
|
|
648
|
3
|
50
|
33
|
|
|
15
|
if ($rraidx > $rrd->{rra_cnt} || $rraidx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
649
|
3
|
|
|
|
|
6
|
my $dsidx=$self->_findDSidx($ds_name); |
650
|
3
|
50
|
33
|
|
|
13
|
if ($tidx >= $rrd->{rra}[$rraidx]->{row_cnt} || $tidx<0) {croak("Row index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
653
|
3
|
50
|
|
|
|
7
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
0
|
|
|
|
|
0
|
|
654
|
|
|
|
|
|
|
|
655
|
3
|
|
|
|
|
11
|
my $t = $rrd->{last_up} - $rrd->{last_up}%($rrd->{rra}[$rraidx]->{pdp_cnt}*$rrd->{pdp_step}) -($rrd->{rra}[$rraidx]->{row_cnt}-1-$tidx)*$rrd->{rra}[$rraidx]->{pdp_cnt}*$rrd->{pdp_step}; |
656
|
3
|
|
|
|
|
6
|
my $jj= ($rrd->{rra}[$rraidx]->{ptr}+1+ $tidx)%$rrd->{rra}[$rraidx]->{row_cnt}; |
657
|
3
|
|
|
|
|
7
|
my @line=$self->_unpackd($rrd->{rra}[$rraidx]->{data}[$jj]); |
658
|
3
|
|
|
|
|
26
|
return ($t, $line[$dsidx]); |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
sub set_RRA_el { |
662
|
|
|
|
|
|
|
# change value of a specified element from a specified RRA |
663
|
|
|
|
|
|
|
# given the index number of the RRA, the index of the DS and the row within the RRA (oldest row is 0), |
664
|
|
|
|
|
|
|
# updates the data value to be $val |
665
|
1
|
|
|
1
|
1
|
3
|
my ($self, $rraidx, $ds_name, $tidx, $val) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
666
|
1
|
|
|
|
|
5
|
my $dsidx=$self->_findDSidx($ds_name); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
669
|
1
|
50
|
|
|
|
6
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
1
|
|
|
|
|
4
|
|
670
|
|
|
|
|
|
|
|
671
|
1
|
|
|
|
|
7
|
my $jj= ($rrd->{rra}[$rraidx]->{ptr}+1 + $tidx)%$rrd->{rra}[$rraidx]->{row_cnt}; |
672
|
1
|
|
|
|
|
16
|
my @line=$self->_unpackd($rrd->{rra}[$rraidx]->{data}[$jj]); |
673
|
1
|
|
|
|
|
4
|
$line[$dsidx] = $val; |
674
|
1
|
|
|
|
|
6
|
$rrd->{rra}[$rraidx]->{data}[$jj]=$self->_packd(\@line); |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
sub last { |
678
|
|
|
|
|
|
|
# return time of last update |
679
|
3
|
|
|
3
|
1
|
20
|
return $_[0]->{rrd}->{last_up}; |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
sub set_last { |
683
|
|
|
|
|
|
|
# change time of last update; use with caution ! |
684
|
0
|
|
|
0
|
1
|
0
|
$_[0]->{rrd}->{last_up} = $_[1]; |
685
|
0
|
|
|
|
|
0
|
return 1; |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
sub lastupdate { |
689
|
|
|
|
|
|
|
# return the most recent update values |
690
|
3
|
|
|
3
|
1
|
7
|
my $self=$_[0]; my $rrd=$self->{rrd}; |
|
3
|
|
|
|
|
6
|
|
691
|
|
|
|
|
|
|
|
692
|
3
|
|
|
|
|
5
|
my @vals; |
693
|
3
|
|
|
|
|
13
|
for (my $i=0; $i<$rrd->{ds_cnt}; $i++) { |
694
|
12
|
|
|
|
|
37
|
push(@vals,$rrd->{ds}[$i]->{pdp_prep}->{last_ds}); |
695
|
|
|
|
|
|
|
} |
696
|
3
|
|
|
|
|
17
|
return @vals; |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub minstep { |
700
|
|
|
|
|
|
|
# return the min step size, in seconds |
701
|
2
|
|
|
2
|
1
|
4
|
my $self=$_[0]; my $rrd=$self->{rrd}; |
|
2
|
|
|
|
|
2
|
|
702
|
2
|
|
|
|
|
7
|
return $rrd->{pdp_step}; |
703
|
|
|
|
|
|
|
} |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub DS_heartbeat { |
706
|
|
|
|
|
|
|
# return heartbeat for DS |
707
|
2
|
|
|
2
|
1
|
4
|
my ($self, $name) = @_; my $rrd=$self->{rrd}; |
|
2
|
|
|
|
|
4
|
|
708
|
|
|
|
|
|
|
|
709
|
2
|
50
|
|
|
|
5
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
2
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
710
|
2
|
|
|
|
|
17
|
return $rrd->{ds}[$idx]->{hb}; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
sub set_DS_heartbeat { |
714
|
|
|
|
|
|
|
# change heartbeat for DS |
715
|
1
|
|
|
1
|
1
|
4
|
my ($self, $name, $hb) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
4
|
|
716
|
|
|
|
|
|
|
|
717
|
1
|
50
|
|
|
|
6
|
if ($hb < $rrd->{pdp_step}) {croak("Heartbeat value must be at least the minimum step size ".$rrd->{pdp_step}." secs\n");} |
|
0
|
|
|
|
|
0
|
|
718
|
|
|
|
|
|
|
|
719
|
1
|
50
|
|
|
|
5
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
10
|
|
|
0
|
|
|
|
|
0
|
|
720
|
|
|
|
|
|
|
# update to new value |
721
|
1
|
|
|
|
|
4
|
$rrd->{ds}[$idx]->{hb}=$hb; |
722
|
1
|
|
|
|
|
5
|
return 1; |
723
|
|
|
|
|
|
|
} |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
sub DS_min { |
726
|
|
|
|
|
|
|
# return min value for DS |
727
|
1
|
|
|
1
|
1
|
2
|
my ($self, $name) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
728
|
|
|
|
|
|
|
|
729
|
1
|
50
|
|
|
|
3
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
3
|
|
|
0
|
|
|
|
|
0
|
|
730
|
1
|
|
|
|
|
4
|
return $rrd->{ds}[$idx]->{min}; |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
sub set_DS_min { |
734
|
|
|
|
|
|
|
# change min value for DS |
735
|
1
|
|
|
1
|
1
|
13
|
my ($self, $name, $min) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
4
|
|
736
|
|
|
|
|
|
|
|
737
|
1
|
50
|
|
|
|
5
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
15
|
|
|
0
|
|
|
|
|
0
|
|
738
|
|
|
|
|
|
|
# update to new value |
739
|
1
|
|
|
|
|
5
|
$rrd->{ds}[$idx]->{min}=$min; |
740
|
1
|
|
|
|
|
7
|
return 1; |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
sub DS_max { |
744
|
|
|
|
|
|
|
# return max value for DS |
745
|
1
|
|
|
1
|
1
|
2
|
my ($self, $name) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
2
|
|
746
|
|
|
|
|
|
|
|
747
|
1
|
50
|
|
|
|
2
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
748
|
1
|
|
|
|
|
3
|
return $rrd->{ds}[$idx]->{max}; |
749
|
|
|
|
|
|
|
} |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
sub set_DS_max { |
752
|
|
|
|
|
|
|
# change max value for DS |
753
|
1
|
|
|
1
|
1
|
3
|
my ($self, $name, $max) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
754
|
|
|
|
|
|
|
|
755
|
1
|
50
|
|
|
|
4
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
756
|
|
|
|
|
|
|
# update to new value |
757
|
1
|
|
|
|
|
5
|
$rrd->{ds}[$idx]->{max}=$max; |
758
|
1
|
|
|
|
|
5
|
return 1; |
759
|
|
|
|
|
|
|
} |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
sub DS_type { |
762
|
|
|
|
|
|
|
# return type of DS |
763
|
2
|
|
|
2
|
1
|
4
|
my ($self, $name) = @_; my $rrd=$self->{rrd}; |
|
2
|
|
|
|
|
3
|
|
764
|
|
|
|
|
|
|
|
765
|
2
|
50
|
|
|
|
4
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
2
|
|
|
|
|
6
|
|
|
0
|
|
|
|
|
0
|
|
766
|
2
|
|
|
|
|
8
|
return $rrd->{ds}[$idx]->{type}; |
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
sub set_DS_type { |
770
|
|
|
|
|
|
|
# change type of DS |
771
|
1
|
|
|
1
|
1
|
4
|
my ($self, $name, $type) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
4
|
|
772
|
|
|
|
|
|
|
|
773
|
1
|
50
|
|
|
|
4
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
7
|
|
|
0
|
|
|
|
|
0
|
|
774
|
1
|
50
|
|
|
|
16
|
if ($type !~ m/(GAUGE|COUNTER|DERIVE|ABSOLUTE)/) { croak("Invalid DS type\n");} |
|
0
|
|
|
|
|
0
|
|
775
|
|
|
|
|
|
|
# update to new value |
776
|
1
|
|
|
|
|
5
|
$rrd->{ds}[$idx]->{type}=$type; |
777
|
1
|
|
|
|
|
6
|
return 1; |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
sub rename_DS { |
781
|
1
|
|
|
1
|
1
|
4
|
my ($self, $old, $new) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
782
|
|
|
|
|
|
|
|
783
|
1
|
50
|
|
|
|
5
|
my $idx=$self->_findDSidx($old); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
784
|
1
|
|
|
|
|
3
|
$rrd->{ds}[$idx]->{name}=$new; |
785
|
1
|
|
|
|
|
6
|
return 1; |
786
|
|
|
|
|
|
|
} |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
sub add_DS { |
789
|
|
|
|
|
|
|
# add a new DS. argument is is same format as used by create |
790
|
1
|
|
|
1
|
1
|
3
|
my ($self, $arg) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
4
|
|
791
|
|
|
|
|
|
|
|
792
|
1
|
50
|
|
|
|
48
|
if ($arg !~ m/^DS:([a-zA-Z0-9_\-]+):(GAUGE|COUNTER|DERIVE|ABSOLUTE):([0-9]+):(U|[-\+]?[0-9\.]+):(U|[-\+]?[0-9\.]+)$/) { croak("Invalid DS spec\n");} |
|
0
|
|
|
|
|
0
|
|
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
795
|
1
|
50
|
|
|
|
6
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
1
|
|
|
|
|
5
|
|
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
# update DS definitions |
798
|
1
|
|
|
|
|
2
|
my $ds; |
799
|
1
|
50
|
|
|
|
5
|
my $min=$4; if ($min eq "U") {$min=NAN;} # set to NaN |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
3
|
|
800
|
1
|
50
|
|
|
|
4
|
my $max=$5; if ($max eq "U") {$max=NAN;} # set to NaN |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
3
|
|
801
|
1
|
|
|
|
|
22
|
($ds->{name}, $ds->{type}, $ds->{hb}, $ds->{min}, $ds->{max}, |
802
|
|
|
|
|
|
|
$ds->{pdp_prep}->{last_ds}, $ds->{pdp_prep}->{unkn_sec_cnt}, $ds->{pdp_prep}->{val}, |
803
|
|
|
|
|
|
|
)= ($1,$2,$3,$min,$max,"U", $rrd->{last_up}%$rrd->{pdp_step}, 0.0); |
804
|
1
|
|
|
|
|
4
|
$rrd->{ds}[@{$rrd->{ds}}]=$ds; |
|
1
|
|
|
|
|
3
|
|
805
|
1
|
|
|
|
|
3
|
$rrd->{ds_cnt}++; |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
# update RRAs |
808
|
1
|
|
|
|
|
1
|
my $ii; |
809
|
1
|
|
|
|
|
5
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
810
|
5
|
|
|
|
|
23
|
@{$rrd->{rra}[$ii]->{cdp_prep}[$rrd->{ds_cnt}-1]} = (NAN,(($rrd->{last_up}-$rrd->{last_up}%$rrd->{pdp_step})%($rrd->{pdp_step}*$rrd->{rra}[$ii]->{pdp_cnt}))/$rrd->{pdp_step},0,0,0,0,0,0,0,0); |
|
5
|
|
|
|
|
37
|
|
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
# update data |
813
|
1
|
|
|
|
|
3
|
my @line; my $i; |
814
|
1
|
|
|
|
|
5
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
815
|
5
|
|
|
|
|
21
|
for ($i=0; $i<$rrd->{rra}[$ii]->{row_cnt}; $i++) { |
816
|
25
|
|
|
|
|
64
|
@line=$self->_unpackd($rrd->{rra}[$ii]->{data}[$i]); |
817
|
25
|
|
|
|
|
56
|
$line[$rrd->{ds_cnt}-1]=NAN; |
818
|
25
|
|
|
|
|
55
|
$rrd->{rra}[$ii]->{data}[$i]=$self->_packd(\@line); |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
} |
821
|
1
|
|
|
|
|
9
|
return 1; |
822
|
|
|
|
|
|
|
} |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
sub delete_DS { |
825
|
|
|
|
|
|
|
# delete a DS |
826
|
1
|
|
|
1
|
1
|
4
|
my ($self, $name) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
827
|
1
|
50
|
|
|
|
5
|
my $idx=$self->_findDSidx($name); if ($idx<0) {croak("Unknown source\n");} |
|
1
|
|
|
|
|
14
|
|
|
0
|
|
|
|
|
0
|
|
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
830
|
1
|
50
|
|
|
|
6
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
0
|
|
|
|
|
0
|
|
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
# update DS definitions |
833
|
1
|
|
|
|
|
2
|
my $i; |
834
|
1
|
|
|
|
|
3
|
$rrd->{ds_cnt}--; |
835
|
1
|
|
|
|
|
6
|
for ($i=$idx; $i<$rrd->{ds_cnt}; $i++) { |
836
|
2
|
|
|
|
|
13
|
$rrd->{ds}[$i]=$rrd->{ds}[$i+1]; |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# update RRAs |
840
|
1
|
|
|
|
|
2
|
my $ii; |
841
|
1
|
|
|
|
|
6
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
842
|
5
|
|
|
|
|
12
|
for ($i=$idx; $i<$rrd->{ds_cnt}; $i++) { |
843
|
10
|
|
|
|
|
50
|
$rrd->{rra}[$ii]->{cdp_prep}[$i]=$rrd->{rra}[$ii]->{cdp_prep}[$i+1]; |
844
|
|
|
|
|
|
|
} |
845
|
|
|
|
|
|
|
} |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
# update data |
848
|
1
|
|
|
|
|
2
|
my $j; my @line; |
849
|
1
|
|
|
|
|
6
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
850
|
5
|
|
|
|
|
13
|
for ($i=0; $i<$rrd->{rra}[$ii]->{row_cnt}; $i++) { |
851
|
25
|
|
|
|
|
133
|
@line=$self->_unpackd($rrd->{rra}[$ii]->{data}[$i]); |
852
|
25
|
|
|
|
|
77
|
for ($j=$idx; $j<$rrd->{ds_cnt}; $j++) { |
853
|
50
|
|
|
|
|
122
|
$line[$j]=$line[$j+1]; |
854
|
|
|
|
|
|
|
} |
855
|
25
|
|
|
|
|
110
|
$rrd->{rra}[$ii]->{data}[$i]=$self->_packd([@line[0..$rrd->{ds_cnt}-1]]); |
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
} |
858
|
1
|
|
|
|
|
8
|
return 1; |
859
|
|
|
|
|
|
|
} |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
sub add_RRA { |
862
|
|
|
|
|
|
|
# add a new RRA |
863
|
1
|
|
|
1
|
1
|
3
|
my ($self, $args) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
864
|
1
|
50
|
|
|
|
12
|
if ($args !~ m/^RRA:(AVERAGE|MAX|MIN|LAST):([0-9\.]+):([0-9]+):([0-9]+)$/) {croak("Invalid RRA spec\n");} |
|
0
|
|
|
|
|
0
|
|
865
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
866
|
1
|
50
|
|
|
|
6
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
0
|
|
|
|
|
0
|
|
867
|
|
|
|
|
|
|
# update RRA definitions |
868
|
1
|
|
|
|
|
2
|
my $rra; |
869
|
1
|
50
|
|
|
|
8
|
if ($4<1) { croak("Invalid row count $4\n");} |
|
0
|
|
|
|
|
0
|
|
870
|
1
|
50
|
33
|
|
|
12
|
if ($2<0.0 || $2>1.0) { croak("Invalid xff $2: must be between 0 and 1\n");} |
|
0
|
|
|
|
|
0
|
|
871
|
1
|
50
|
|
|
|
4
|
if ($3<1) { croak("Invalid step $3: must be >= 1\n");} |
|
0
|
|
|
|
|
0
|
|
872
|
1
|
|
|
|
|
57
|
($rra->{name}, $rra->{xff}, $rra->{pdp_cnt}, $rra->{row_cnt}, $rra->{ptr}, $rra->{data})=($1,$2,$3,$4,int(rand($4)),undef); |
873
|
1
|
|
|
|
|
4
|
my $idx=@{$rrd->{rra}}; |
|
1
|
|
|
|
|
3
|
|
874
|
1
|
|
|
|
|
4
|
$rrd->{rra}[$idx]=$rra; |
875
|
1
|
|
|
|
|
3
|
$rrd->{rra_cnt}++; |
876
|
|
|
|
|
|
|
|
877
|
1
|
|
|
|
|
1
|
my $i; |
878
|
1
|
|
|
|
|
7
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
879
|
4
|
|
|
|
|
17
|
@{$rrd->{rra}[$idx]->{cdp_prep}[$i]} = (NAN,(($rrd->{last_up}-$rrd->{last_up}%$rrd->{pdp_step})%($rrd->{pdp_step}*$rrd->{rra}[$idx]->{pdp_cnt}))/$rrd->{pdp_step},0,0,0,0,0,0,0,0); |
|
4
|
|
|
|
|
32
|
|
880
|
|
|
|
|
|
|
} |
881
|
|
|
|
|
|
|
# update data |
882
|
1
|
|
|
|
|
4
|
my @empty=((NAN)x$rrd->{ds_cnt}); |
883
|
1
|
|
|
|
|
12
|
for ($i=0; $i<$rrd->{rra}[$idx]->{row_cnt}; $i++) { |
884
|
10
|
|
|
|
|
23
|
$rrd->{rra}[$idx]->{data}[$i] = $self->_packd(\@empty); |
885
|
|
|
|
|
|
|
} |
886
|
1
|
|
|
|
|
7
|
return 1; |
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
sub delete_RRA { |
890
|
|
|
|
|
|
|
# delete an RRA |
891
|
1
|
|
|
1
|
1
|
2
|
my ($self, $idx) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
892
|
1
|
50
|
33
|
|
|
12
|
if ($idx > $rrd->{rra_cnt} || $idx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
893
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
894
|
1
|
50
|
|
|
|
5
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
0
|
|
|
|
|
0
|
|
895
|
|
|
|
|
|
|
# update RRA |
896
|
1
|
|
|
|
|
4
|
$rrd->{rra_cnt}--; |
897
|
1
|
|
|
|
|
5
|
for (my $i=$idx; $i<$rrd->{rra_cnt}; $i++) { |
898
|
5
|
|
|
|
|
27
|
$rrd->{rra}[$i]=$rrd->{rra}[$i+1]; |
899
|
|
|
|
|
|
|
} |
900
|
1
|
|
|
|
|
5
|
return 1; |
901
|
|
|
|
|
|
|
} |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
sub resize_RRA { |
904
|
1
|
|
|
1
|
1
|
3
|
my ($self, $idx, $size) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
905
|
|
|
|
|
|
|
|
906
|
1
|
50
|
33
|
|
|
11
|
if ($idx > $rrd->{rra_cnt} || $idx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
907
|
1
|
50
|
|
|
|
4
|
if ($size < 0) {$size=0;} |
|
0
|
|
|
|
|
0
|
|
908
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
909
|
1
|
50
|
|
|
|
5
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
0
|
|
|
|
|
0
|
|
910
|
|
|
|
|
|
|
# update data |
911
|
1
|
|
|
|
|
6
|
my @empty=((NAN)x$rrd->{ds_cnt}); |
912
|
1
|
|
|
|
|
10
|
for (my $i=$rrd->{rra}[$idx]->{row_cnt}; $i<$size; $i++) { |
913
|
15
|
|
|
|
|
26
|
$rrd->{rra}[$idx]->{data}[$i] = $self->_packd(\@empty); |
914
|
|
|
|
|
|
|
} |
915
|
1
|
|
|
|
|
4
|
$rrd->{rra}[$idx]->{row_cnt} = $size; |
916
|
1
|
|
|
|
|
6
|
return 1; |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
sub set_RRA_xff { |
920
|
|
|
|
|
|
|
# schange xff value for an RRA |
921
|
1
|
|
|
1
|
1
|
8
|
my ($self, $idx, $xff) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
4
|
|
922
|
1
|
50
|
33
|
|
|
10
|
if ($idx > $rrd->{rra_cnt} || $idx<0) {croak("RRA index out of range\n");} |
|
0
|
|
|
|
|
0
|
|
923
|
1
|
|
|
|
|
4
|
$rrd->{rra}[$idx]->{xff}=$xff; |
924
|
1
|
|
|
|
|
6
|
return 1; |
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
#sub set_RRA_step { |
928
|
|
|
|
|
|
|
# TODO: change RRA step size - will require resampling |
929
|
|
|
|
|
|
|
#} |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
sub update { |
932
|
|
|
|
|
|
|
# a re-implementation of rrdupdate. updates file in place on disk, if possible - much faster. |
933
|
|
|
|
|
|
|
|
934
|
10
|
|
|
10
|
1
|
27
|
my ($self, $args_str) = @_; my $rrd=$self->{rrd}; |
|
10
|
|
|
|
|
20
|
|
935
|
|
|
|
|
|
|
|
936
|
10
|
|
|
|
|
12
|
my $ret; my $args; my $template=''; |
|
10
|
|
|
|
|
13
|
|
937
|
10
|
|
|
|
|
24
|
($ret, $args) = _GetOptionsFromString($args_str, |
938
|
|
|
|
|
|
|
"template|t:s" => \$template, |
939
|
|
|
|
|
|
|
); |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
# update file in place ? |
942
|
10
|
|
|
|
|
14
|
my $inplace; my $fd; |
943
|
10
|
50
|
|
|
|
27
|
if (defined($rrd->{dataloaded})) { |
944
|
10
|
|
|
|
|
17
|
$inplace="memory"; # data is already loaded into memory so do update there. will need to subsequently call save() to write data to disk |
945
|
|
|
|
|
|
|
} else { |
946
|
0
|
0
|
|
|
|
0
|
if (defined($self->{fd})) { |
947
|
0
|
|
|
|
|
0
|
$inplace="file"; # data is not loaded yet, so carry out update in place in file. more efficient - no need to call save() to write data to disk. |
948
|
0
|
0
|
|
|
|
0
|
open $self->{fd}, "+<", $self->{file_name} or croak "Couldn't open file ".$self->{file_name}.": $!\n"; # reopen file in update mode ($self->open() opens in read-only mode) |
949
|
0
|
|
|
|
|
0
|
binmode($self->{fd}); |
950
|
0
|
|
|
|
|
0
|
$fd=$self->{fd}; |
951
|
|
|
|
|
|
|
} else { |
952
|
0
|
|
|
|
|
0
|
croak("update: must call open() or create() first\n"); |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
} |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
# Parse template, if provided |
957
|
10
|
|
|
|
|
10
|
my $i; my $j; |
958
|
10
|
|
|
|
|
23
|
my @tmp=split(/:/,$template); |
959
|
10
|
|
|
|
|
14
|
my @idx; |
960
|
10
|
100
|
|
|
|
18
|
if (@tmp == 0) { |
961
|
|
|
|
|
|
|
# no template, default to complete DS list |
962
|
8
|
|
|
|
|
21
|
@idx=(0 .. $rrd->{ds_cnt}-1); |
963
|
|
|
|
|
|
|
} else { |
964
|
|
|
|
|
|
|
# read DS list from template |
965
|
2
|
|
|
|
|
13
|
for ($i=0; $i<@tmp; $i++) { |
966
|
7
|
50
|
|
|
|
19
|
$idx[$i]=$self->_findDSidx($tmp[$i]); if($idx[$i]<0) {croak("Unknown DS name ".$tmp[$i]."\n");} |
|
7
|
|
|
|
|
27
|
|
|
0
|
|
|
|
|
0
|
|
967
|
|
|
|
|
|
|
} |
968
|
|
|
|
|
|
|
} |
969
|
|
|
|
|
|
|
# Parse update strings - updates the primary data points (PDPs) |
970
|
|
|
|
|
|
|
# and consolidated data points (CDPs), and writes changes to the RRAs. |
971
|
10
|
|
|
|
|
13
|
my @updvals; my @bits; my $rate; my $current_time; my $interval; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
972
|
10
|
|
|
|
|
16
|
for ($i=0; $i<@{$args}; $i++) { |
|
20
|
|
|
|
|
54
|
|
973
|
|
|
|
|
|
|
#parse colon-separated DS string |
974
|
10
|
50
|
|
|
|
33
|
if ($args->[$i] =~ m/(-t|--template)/) {next;} # ignore option here |
|
0
|
|
|
|
|
0
|
|
975
|
10
|
50
|
|
|
|
24
|
if ($args->[$i] =~ m/\@/) {croak("\@ time format not supported - use either N or a unix timestamp\n");} |
|
0
|
|
|
|
|
0
|
|
976
|
10
|
|
|
|
|
59
|
@bits=split(/:/,$args->[$i]); |
977
|
10
|
50
|
|
|
|
26
|
if (@bits-1 < @idx) {croak("expected ".@idx." data source readings (got ".(@bits-1).") from ".$args->[$i],"\n");} |
|
0
|
|
|
|
|
0
|
|
978
|
|
|
|
|
|
|
#get_time_from_reading |
979
|
10
|
50
|
|
|
|
22
|
if ($bits[0] eq "N") { |
980
|
0
|
|
|
|
|
0
|
$current_time=time(); |
981
|
|
|
|
|
|
|
#normalize_time |
982
|
|
|
|
|
|
|
} else { |
983
|
10
|
|
|
|
|
13
|
$current_time=$bits[0]; |
984
|
|
|
|
|
|
|
} |
985
|
10
|
50
|
|
|
|
28
|
if ($current_time < $rrd->{last_up}) {croak("attempt to update using time $current_time when last update time is ". $rrd->{last_up}."\n");} |
|
0
|
|
|
|
|
0
|
|
986
|
10
|
|
|
|
|
16
|
$interval=$current_time - $rrd->{last_up}; |
987
|
|
|
|
|
|
|
# initialise values to NaN |
988
|
10
|
|
|
|
|
26
|
for ($j=0; $j<$rrd->{ds_cnt}; $j++) { |
989
|
40
|
|
|
|
|
69
|
$updvals[$j]="U"; |
990
|
|
|
|
|
|
|
} |
991
|
10
|
|
|
|
|
21
|
for ($j=0; $j<@idx; $j++) { |
992
|
39
|
|
|
|
|
76
|
$updvals[$idx[$j]] = $bits[$j+1]; |
993
|
|
|
|
|
|
|
} |
994
|
|
|
|
|
|
|
# process the data sources and update the pdp_prep area accordingly |
995
|
10
|
|
|
|
|
13
|
my @pdp_new=(); |
996
|
10
|
|
|
|
|
20
|
for ($j=0;$j<@updvals; $j++) { |
997
|
40
|
50
|
|
|
|
114
|
if ($rrd->{ds}[$j]->{hb} < $interval) { |
998
|
|
|
|
|
|
|
# make sure we do not build diffs with old last_ds values |
999
|
0
|
|
|
|
|
0
|
$rrd->{ds}[$j]->{pdp_prep}->{last_ds}="U"; |
1000
|
|
|
|
|
|
|
} |
1001
|
40
|
100
|
66
|
|
|
154
|
if ($updvals[$j] ne "U" && $rrd->{ds}[$j]->{hb} >= $interval) { |
1002
|
39
|
|
|
|
|
41
|
$rate=NAN; |
1003
|
39
|
100
|
|
|
|
112
|
if ( $rrd->{ds}[$j]->{type} eq "COUNTER" ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1004
|
10
|
50
|
|
|
|
48
|
if ($updvals[$j] !~ m/^\d+$/) {croak("not a simple unsigned integer ".$updvals[$j]);} |
|
0
|
|
|
|
|
0
|
|
1005
|
10
|
100
|
|
|
|
31
|
if ($rrd->{ds}[$j]->{pdp_prep}->{last_ds} ne "U") { |
1006
|
|
|
|
|
|
|
#use bignum; # need this for next line as might be large integers |
1007
|
9
|
|
|
|
|
28
|
$pdp_new[$j] = $updvals[$j] - $rrd->{ds}[$j]->{pdp_prep}->{last_ds}; |
1008
|
|
|
|
|
|
|
# simple overflow catcher |
1009
|
9
|
100
|
|
|
|
17
|
if ($pdp_new[$j] < 0) {$pdp_new[$j]+=4294967296; } #2^32 |
|
3
|
|
|
|
|
4
|
|
1010
|
9
|
50
|
|
|
|
23
|
if ($pdp_new[$j] < 0) {$pdp_new[$j]+=18446744069414584320; } #2^64-2^32 |
|
0
|
|
|
|
|
0
|
|
1011
|
9
|
|
|
|
|
18
|
$rate=$pdp_new[$j]/$interval; |
1012
|
|
|
|
|
|
|
} else { |
1013
|
1
|
|
|
|
|
2
|
$pdp_new[$j]=NAN; |
1014
|
|
|
|
|
|
|
} |
1015
|
|
|
|
|
|
|
} elsif ( $rrd->{ds}[$j]->{type} eq "DERIVE" ) { |
1016
|
9
|
50
|
|
|
|
39
|
if ($updvals[$j] !~ m/^[+|-]?\d+$/) {croak("not a simple signed integer ".$updvals[$j]);} |
|
0
|
|
|
|
|
0
|
|
1017
|
9
|
100
|
|
|
|
24
|
if ($rrd->{ds}[$j]->{pdp_prep}->{last_ds} ne "U") { |
1018
|
|
|
|
|
|
|
#use bignum; # need this for next line as might be large integers |
1019
|
8
|
|
|
|
|
21
|
$pdp_new[$j] = $updvals[$j] - $rrd->{ds}[$j]->{pdp_prep}->{last_ds}; |
1020
|
8
|
|
|
|
|
10
|
$rate=$pdp_new[$j]/$interval; |
1021
|
|
|
|
|
|
|
} else { |
1022
|
1
|
|
|
|
|
2
|
$pdp_new[$j]=NAN; |
1023
|
|
|
|
|
|
|
} |
1024
|
|
|
|
|
|
|
} elsif ( $rrd->{ds}[$j]->{type} eq "GAUGE" ) { |
1025
|
10
|
50
|
|
|
|
54
|
if ($updvals[$j] !~ m/^(-)?[\d]+(\.[\d]+)?$/) {croak("not a number ".$updvals[$j]);} |
|
0
|
|
|
|
|
0
|
|
1026
|
10
|
|
|
|
|
35
|
$pdp_new[$j] = $updvals[$j]*$interval; |
1027
|
10
|
|
|
|
|
15
|
$rate=$pdp_new[$j]/$interval; |
1028
|
|
|
|
|
|
|
} else { # ABSOLUTE |
1029
|
10
|
|
|
|
|
15
|
$pdp_new[$j] = $updvals[$j]; |
1030
|
10
|
|
|
|
|
17
|
$rate=$pdp_new[$j]/$interval; |
1031
|
|
|
|
|
|
|
} |
1032
|
39
|
50
|
33
|
|
|
65
|
if (!_isNan($rate) |
|
|
|
66
|
|
|
|
|
1033
|
|
|
|
|
|
|
&& ( |
1034
|
|
|
|
|
|
|
(!_isNan($rrd->{ds}[$j]->{max}) && $rate >$rrd->{ds}[$j]->{max}) |
1035
|
|
|
|
|
|
|
|| (!_isNan($rrd->{ds}[$j]->{min}) && $rate <$rrd->{ds}[$j]->{min}) |
1036
|
|
|
|
|
|
|
)) { |
1037
|
0
|
|
|
|
|
0
|
$pdp_new[$j]=NAN; |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
} else { |
1040
|
1
|
|
|
|
|
4
|
$pdp_new[$j]=NAN; |
1041
|
|
|
|
|
|
|
} |
1042
|
40
|
|
|
|
|
135
|
$rrd->{ds}[$j]->{pdp_prep}->{last_ds} = $updvals[$j]; |
1043
|
|
|
|
|
|
|
} |
1044
|
|
|
|
|
|
|
# how many PDP steps have elapsed since the last update? |
1045
|
10
|
|
|
|
|
24
|
my $proc_pdp_st = $rrd->{last_up} - $rrd->{last_up} % $rrd->{pdp_step}; |
1046
|
10
|
|
|
|
|
12
|
my $occu_pdp_age = $current_time % $rrd->{pdp_step}; |
1047
|
10
|
|
|
|
|
13
|
my $occu_pdp_st = $current_time - $occu_pdp_age; |
1048
|
10
|
|
|
|
|
10
|
my $pre_int; my $post_int; |
1049
|
10
|
50
|
|
|
|
48
|
if ($occu_pdp_st > $proc_pdp_st) { |
1050
|
|
|
|
|
|
|
# OK we passed the pdp_st moment |
1051
|
10
|
|
|
|
|
12
|
$pre_int = $occu_pdp_st - $rrd->{last_up}; |
1052
|
10
|
|
|
|
|
10
|
$post_int = $occu_pdp_age; |
1053
|
|
|
|
|
|
|
} else { |
1054
|
0
|
|
|
|
|
0
|
$pre_int = $interval; |
1055
|
0
|
|
|
|
|
0
|
$post_int=0; |
1056
|
|
|
|
|
|
|
} |
1057
|
10
|
|
|
|
|
25
|
my $proc_pdp_cnt = int( $proc_pdp_st / $rrd->{pdp_step} ); |
1058
|
10
|
|
|
|
|
16
|
my $elapsed_pdp_st = int( ($occu_pdp_st - $proc_pdp_st)/$rrd->{pdp_step} ); |
1059
|
|
|
|
|
|
|
# have we moved past a pdp step size since last run ? |
1060
|
10
|
50
|
|
|
|
17
|
if ($elapsed_pdp_st == 0) { |
1061
|
|
|
|
|
|
|
# nope, simple_update |
1062
|
0
|
|
|
|
|
0
|
for ($j=0; $j<$rrd->{ds_cnt}; $j++) { |
1063
|
0
|
0
|
|
|
|
0
|
if (_isNan($pdp_new[$j])) { |
|
|
0
|
|
|
|
|
|
1064
|
0
|
|
|
|
|
0
|
$rrd->{ds}[$j]->{pdp_prep}->{unkn_sec_cnt} += int($interval); |
1065
|
|
|
|
|
|
|
} elsif (_isNan($rrd->{ds}[$j]->{pdp_prep}->{val}) ) { |
1066
|
0
|
|
|
|
|
0
|
$rrd->{ds}[$j]->{pdp_prep}->{val} = $pdp_new[$j]; |
1067
|
|
|
|
|
|
|
} else { |
1068
|
0
|
|
|
|
|
0
|
$rrd->{ds}[$j]->{pdp_prep}->{val} += $pdp_new[$j]; |
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
} else { |
1072
|
|
|
|
|
|
|
# yep |
1073
|
|
|
|
|
|
|
# process_all_pdp_st |
1074
|
10
|
|
|
|
|
12
|
my $pre_unknown; my @pdp_temp; my $diff_pdp_st; |
|
0
|
|
|
|
|
0
|
|
1075
|
10
|
|
|
|
|
25
|
for ($j=0; $j<$rrd->{ds_cnt}; $j++) { |
1076
|
|
|
|
|
|
|
# Process an update that occurs after one of the PDP moments. |
1077
|
|
|
|
|
|
|
# Increments the PDP value, sets NAN if time greater than the heartbeats have elapsed |
1078
|
40
|
|
|
|
|
38
|
$pre_unknown=0; |
1079
|
40
|
100
|
|
|
|
64
|
if (_isNan($pdp_new[$j])) { |
1080
|
3
|
|
|
|
|
4
|
$pre_unknown=$pre_int; |
1081
|
|
|
|
|
|
|
} else { |
1082
|
|
|
|
|
|
|
#print $rrd->{ds}[$j]->{pdp_prep}->{val}," "; |
1083
|
37
|
100
|
|
|
|
66
|
if (_isNan($rrd->{ds}[$j]->{pdp_prep}->{val})) { |
1084
|
2
|
|
|
|
|
4
|
$rrd->{ds}[$j]->{pdp_prep}->{val} = 0; |
1085
|
|
|
|
|
|
|
} |
1086
|
37
|
|
|
|
|
98
|
$rrd->{ds}[$j]->{pdp_prep}->{val} += $pdp_new[$j]/$interval * $pre_int; |
1087
|
|
|
|
|
|
|
} |
1088
|
|
|
|
|
|
|
#print $pdp_new[$j]," ",$interval," ",$pre_int," ",$rrd->{ds}[$j]->{pdp_prep}->{val},"\n"; |
1089
|
40
|
100
|
66
|
|
|
182
|
if ($interval > $rrd->{ds}[$j]->{hb} || $rrd->{pdp_step}/2.0 < $rrd->{ds}[$j]->{pdp_prep}->{unkn_sec_cnt}+$pre_unknown) { |
1090
|
5
|
|
|
|
|
8
|
$pdp_temp[$j]=NAN; |
1091
|
|
|
|
|
|
|
} else { |
1092
|
35
|
|
|
|
|
77
|
$pdp_temp[$j]=$rrd->{ds}[$j]->{pdp_prep}->{val}/($elapsed_pdp_st*$rrd->{pdp_step}-$rrd->{ds}[$j]->{pdp_prep}->{unkn_sec_cnt}-$pre_unknown); |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
#print $pdp_new[$j]," ",$pdp_temp[$j]," ",$rrd->{ds}[$j]->{pdp_prep}->{val}," ", $elapsed_pdp_st-$rrd->{ds}[$j]->{pdp_prep}->{unkn_sec_cnt}-$pre_unknown,"\n"; |
1095
|
40
|
100
|
|
|
|
54
|
if (_isNan($pdp_new[$j])) { |
1096
|
3
|
|
|
|
|
9
|
$rrd->{ds}[$j]->{pdp_prep}->{unkn_sec_cnt} = int($post_int); |
1097
|
3
|
|
|
|
|
12
|
$rrd->{ds}[$j]->{pdp_prep}->{val}=NAN; |
1098
|
|
|
|
|
|
|
} else { |
1099
|
37
|
|
|
|
|
51
|
$rrd->{ds}[$j]->{pdp_prep}->{unkn_sec_cnt} = 0; |
1100
|
37
|
|
|
|
|
106
|
$rrd->{ds}[$j]->{pdp_prep}->{val}=$pdp_new[$j]/$interval*$post_int; |
1101
|
|
|
|
|
|
|
#print $pdp_new[$j]," ", $interval, " ", $post_int, " ",$rrd->{ds}[$j]->{pdp_prep}->{val},"\n"; |
1102
|
|
|
|
|
|
|
} |
1103
|
|
|
|
|
|
|
} |
1104
|
|
|
|
|
|
|
# update_all_cdp_prep. Iterate over all the RRAs for a given DS and update the CDP |
1105
|
10
|
|
|
|
|
11
|
my $current_cf; my $start_pdp_offset; my @rra_step_cnt; |
|
0
|
|
|
|
|
0
|
|
1106
|
0
|
|
|
|
|
0
|
my $cum_val; my $cur_val; my $pdp_into_cdp_cnt; my $ii; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1107
|
10
|
|
|
|
|
27
|
my $idx=$self->_get_header_size; # file position (used by in place updates) |
1108
|
10
|
|
|
|
|
29
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
1109
|
50
|
|
|
|
|
96
|
$start_pdp_offset = $rrd->{rra}[$ii]->{pdp_cnt} - $proc_pdp_cnt % $rrd->{rra}[$ii]->{pdp_cnt}; |
1110
|
50
|
100
|
|
|
|
69
|
if ($start_pdp_offset <= $elapsed_pdp_st) { |
1111
|
30
|
|
|
|
|
62
|
$rra_step_cnt[$ii] = int(($elapsed_pdp_st - $start_pdp_offset)/$rrd->{rra}[$ii]->{pdp_cnt}) + 1; |
1112
|
|
|
|
|
|
|
} else { |
1113
|
20
|
|
|
|
|
27
|
$rra_step_cnt[$ii] = 0; |
1114
|
|
|
|
|
|
|
} |
1115
|
|
|
|
|
|
|
# update_cdp_prep. update CDP_PREP areas, loop over data sources within each RRA |
1116
|
50
|
|
|
|
|
89
|
for ($j=0; $j<$rrd->{ds_cnt}; $j++) { |
1117
|
200
|
100
|
|
|
|
284
|
if ($rrd->{rra}[$ii]->{pdp_cnt} > 1) { |
1118
|
|
|
|
|
|
|
# update_cdp. Given the new reading (pdp_temp_val), update or initialize the CDP value, primary value, secondary value, and # of unknowns. |
1119
|
160
|
100
|
|
|
|
193
|
if ($rra_step_cnt[$ii]>0) { |
1120
|
80
|
100
|
|
|
|
98
|
if (_isNan($pdp_temp[$j])) { |
1121
|
16
|
|
|
|
|
20
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[UNKN_PDP_CNT] +=$start_pdp_offset; |
1122
|
16
|
|
|
|
|
20
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[SECONDARY_VAL] = NAN; |
1123
|
|
|
|
|
|
|
} else { |
1124
|
64
|
|
|
|
|
97
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[SECONDARY_VAL] = $pdp_temp[$j]; |
1125
|
|
|
|
|
|
|
} |
1126
|
80
|
100
|
|
|
|
270
|
if ($rrd->{rra}[$ii]->{cdp_prep}[$j]->[UNKN_PDP_CNT] > $rrd->{rra}[$ii]->{pdp_cnt}*$rrd->{rra}[$ii]->{xff}) { |
1127
|
16
|
|
|
|
|
21
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = NAN; |
1128
|
|
|
|
|
|
|
} else { |
1129
|
|
|
|
|
|
|
#initialize_cdp_val |
1130
|
64
|
100
|
|
|
|
154
|
if ($rrd->{rra}[$ii]->{name} eq "AVERAGE") { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1131
|
16
|
50
|
|
|
|
34
|
if (_isNan($rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL])) { |
1132
|
0
|
|
|
|
|
0
|
$cum_val=0.0; |
1133
|
|
|
|
|
|
|
} else { |
1134
|
16
|
|
|
|
|
29
|
$cum_val = $rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]; |
1135
|
|
|
|
|
|
|
} |
1136
|
16
|
50
|
|
|
|
28
|
if (_isNan($pdp_temp[$j])) { |
1137
|
0
|
|
|
|
|
0
|
$cur_val=0.0; |
1138
|
|
|
|
|
|
|
} else { |
1139
|
16
|
|
|
|
|
21
|
$cur_val = $pdp_temp[$j]; |
1140
|
|
|
|
|
|
|
} |
1141
|
16
|
|
|
|
|
56
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = ($cum_val+$cur_val*$start_pdp_offset)/($rrd->{rra}[$ii]->{pdp_cnt}-$rrd->{rra}[$ii]->{cdp_prep}[$j]->[UNKN_PDP_CNT]); |
1142
|
|
|
|
|
|
|
} elsif ($rrd->{rra}[$ii]->{name} eq "MAX") { |
1143
|
16
|
50
|
|
|
|
56
|
if (_isNan($rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL])) { |
1144
|
0
|
|
|
|
|
0
|
$cum_val=-1 * INF; |
1145
|
|
|
|
|
|
|
} else { |
1146
|
16
|
|
|
|
|
24
|
$cum_val = $rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]; |
1147
|
|
|
|
|
|
|
} |
1148
|
16
|
50
|
|
|
|
26
|
if (_isNan($pdp_temp[$j])) { |
1149
|
0
|
|
|
|
|
0
|
$cur_val=-1 * INF; |
1150
|
|
|
|
|
|
|
} else { |
1151
|
16
|
|
|
|
|
14
|
$cur_val = $pdp_temp[$j]; |
1152
|
|
|
|
|
|
|
} |
1153
|
16
|
100
|
|
|
|
23
|
if ($cur_val > $cum_val) { |
1154
|
8
|
|
|
|
|
14
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = $cur_val; |
1155
|
|
|
|
|
|
|
} else { |
1156
|
8
|
|
|
|
|
17
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = $cum_val; |
1157
|
|
|
|
|
|
|
} |
1158
|
|
|
|
|
|
|
} elsif ($rrd->{rra}[$ii]->{name} eq "MIN") { |
1159
|
16
|
50
|
|
|
|
30
|
if (_isNan($rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL])) { |
1160
|
0
|
|
|
|
|
0
|
$cum_val=INF; |
1161
|
|
|
|
|
|
|
} else { |
1162
|
16
|
|
|
|
|
28
|
$cum_val = $rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]; |
1163
|
|
|
|
|
|
|
} |
1164
|
16
|
50
|
|
|
|
28
|
if (_isNan($pdp_temp[$j])) { |
1165
|
0
|
|
|
|
|
0
|
$cur_val=INF; |
1166
|
|
|
|
|
|
|
} else { |
1167
|
16
|
|
|
|
|
19
|
$cur_val = $pdp_temp[$j]; |
1168
|
|
|
|
|
|
|
} |
1169
|
16
|
100
|
|
|
|
30
|
if ($cur_val < $cum_val) { |
1170
|
8
|
|
|
|
|
18
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = $cur_val; |
1171
|
|
|
|
|
|
|
} else { |
1172
|
8
|
|
|
|
|
16
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = $cum_val; |
1173
|
|
|
|
|
|
|
} |
1174
|
|
|
|
|
|
|
} else { |
1175
|
16
|
|
|
|
|
28
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = $pdp_temp[$j]; |
1176
|
|
|
|
|
|
|
} |
1177
|
|
|
|
|
|
|
} |
1178
|
|
|
|
|
|
|
#*cdp_val = initialize_carry_over |
1179
|
80
|
|
|
|
|
103
|
$pdp_into_cdp_cnt=($elapsed_pdp_st - $start_pdp_offset) % $rrd->{rra}[$ii]->{pdp_cnt}; |
1180
|
80
|
50
|
33
|
|
|
146
|
if ($pdp_into_cdp_cnt == 0 || _isNan($pdp_temp[$j])) { |
1181
|
80
|
100
|
|
|
|
188
|
if ($rrd->{rra}[$ii]->{name} eq "MAX") { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1182
|
20
|
|
|
|
|
29
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=-1 * INF; |
1183
|
|
|
|
|
|
|
} elsif ($rrd->{rra}[$ii]->{name} eq "MIN") { |
1184
|
20
|
|
|
|
|
32
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=INF; |
1185
|
|
|
|
|
|
|
} elsif ($rrd->{rra}[$ii]->{name} eq "AVERAGE") { |
1186
|
20
|
|
|
|
|
30
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=0; |
1187
|
|
|
|
|
|
|
} else { |
1188
|
20
|
|
|
|
|
30
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=NAN; |
1189
|
|
|
|
|
|
|
} |
1190
|
|
|
|
|
|
|
} else { |
1191
|
0
|
0
|
|
|
|
0
|
if ($rrd->{rra}[$ii]->{name} eq "AVERAGE") { |
1192
|
0
|
|
|
|
|
0
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=$pdp_temp[$j]*$pdp_into_cdp_cnt; |
1193
|
|
|
|
|
|
|
} else { |
1194
|
0
|
|
|
|
|
0
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=$pdp_temp[$j]; |
1195
|
|
|
|
|
|
|
} |
1196
|
|
|
|
|
|
|
} |
1197
|
80
|
100
|
|
|
|
100
|
if (_isNan($pdp_temp[$j])) { |
1198
|
16
|
|
|
|
|
48
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[UNKN_PDP_CNT] = ($elapsed_pdp_st - $start_pdp_offset) % $rrd->{rra}[$ii]->{pdp_cnt}; |
1199
|
|
|
|
|
|
|
} else { |
1200
|
64
|
|
|
|
|
179
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[UNKN_PDP_CNT] = 0; |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
} else { |
1203
|
80
|
100
|
|
|
|
109
|
if (_isNan($pdp_temp[$j])) { |
1204
|
4
|
|
|
|
|
18
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[UNKN_PDP_CNT] += $elapsed_pdp_st; |
1205
|
|
|
|
|
|
|
} else { |
1206
|
|
|
|
|
|
|
#*cdp_val =calculate_cdp_val |
1207
|
76
|
100
|
|
|
|
150
|
if (_isNan($rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL])) { |
1208
|
19
|
50
|
|
|
|
40
|
if ($rrd->{rra}[$ii]->{name} eq "AVERAGE") { |
1209
|
0
|
|
|
|
|
0
|
$pdp_temp[$j] *= $elapsed_pdp_st; |
1210
|
|
|
|
|
|
|
} |
1211
|
19
|
|
|
|
|
49
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]=$pdp_temp[$j]; |
1212
|
|
|
|
|
|
|
} else { |
1213
|
57
|
100
|
|
|
|
160
|
if ($rrd->{rra}[$ii]->{name} eq "AVERAGE") { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1214
|
19
|
|
|
|
|
64
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]+=$pdp_temp[$j]*$elapsed_pdp_st; |
1215
|
|
|
|
|
|
|
} elsif ($rrd->{rra}[$ii]->{name} eq "MIN") { |
1216
|
19
|
50
|
|
|
|
49
|
if ($pdp_temp[$j] < $rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]) { |
1217
|
19
|
|
|
|
|
95
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL] = $pdp_temp[$j]; |
1218
|
|
|
|
|
|
|
} |
1219
|
|
|
|
|
|
|
} elsif ($rrd->{rra}[$ii]->{name} eq "MAX") { |
1220
|
19
|
50
|
|
|
|
48
|
if ($pdp_temp[$j] > $rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL]) { |
1221
|
19
|
|
|
|
|
53
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL] = $pdp_temp[$j]; |
1222
|
|
|
|
|
|
|
} |
1223
|
|
|
|
|
|
|
} else { |
1224
|
0
|
|
|
|
|
0
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[VAL] = $pdp_temp[$j]; |
1225
|
|
|
|
|
|
|
} |
1226
|
|
|
|
|
|
|
} |
1227
|
|
|
|
|
|
|
} |
1228
|
|
|
|
|
|
|
} |
1229
|
|
|
|
|
|
|
} else { |
1230
|
|
|
|
|
|
|
# Nothing to consolidate if there's one PDP per CDP |
1231
|
40
|
|
|
|
|
59
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[PRIMARY_VAL] = $pdp_temp[$j]; |
1232
|
40
|
50
|
|
|
|
95
|
if ($elapsed_pdp_st > 1) { |
1233
|
0
|
|
|
|
|
0
|
$rrd->{rra}[$ii]->{cdp_prep}[$j]->[SECONDARY_VAL] = $pdp_temp[$j]; |
1234
|
|
|
|
|
|
|
} |
1235
|
|
|
|
|
|
|
# consolidated with update_aberrant_cdps |
1236
|
|
|
|
|
|
|
} |
1237
|
|
|
|
|
|
|
} # $j ds_cnt |
1238
|
|
|
|
|
|
|
# write to RRA |
1239
|
50
|
|
|
|
|
98
|
for (my $scratch_idx=PRIMARY_VAL; $rra_step_cnt[$ii] >0; $rra_step_cnt[$ii]--, $scratch_idx=SECONDARY_VAL) { |
1240
|
30
|
|
|
|
|
73
|
$rrd->{rra}[$ii]->{ptr} = ($rrd->{rra}[$ii]->{ptr}+1) % $rrd->{rra}[$ii]->{row_cnt}; |
1241
|
|
|
|
|
|
|
#write_RRA_row |
1242
|
30
|
|
|
|
|
29
|
my @line; |
1243
|
30
|
|
|
|
|
52
|
for ($j=0; $j<$rrd->{ds_cnt}; $j++) { |
1244
|
120
|
|
|
|
|
261
|
push(@line, $rrd->{rra}[$ii]->{cdp_prep}[$j]->[$scratch_idx]); |
1245
|
|
|
|
|
|
|
} |
1246
|
30
|
50
|
|
|
|
44
|
if ($inplace eq "memory") { |
1247
|
30
|
|
|
|
|
79
|
$rrd->{rra}[$ii]->{data}[$rrd->{rra}[$ii]->{ptr}] = $self->_packd(\@line); |
1248
|
|
|
|
|
|
|
} else { |
1249
|
|
|
|
|
|
|
# update in place |
1250
|
0
|
|
|
|
|
0
|
seek $fd,$idx+$rrd->{rra}[$ii]->{ptr}*$rrd->{ds_cnt}*$self->{FLOAT_EL_SIZE},0; |
1251
|
0
|
|
|
|
|
0
|
print $fd $self->_packd(\@line); |
1252
|
|
|
|
|
|
|
} |
1253
|
|
|
|
|
|
|
# rrd_notify_row |
1254
|
|
|
|
|
|
|
} |
1255
|
50
|
|
|
|
|
164
|
$idx+=$rrd->{rra}[$ii]->{row_cnt}*$rrd->{ds_cnt}*$self->{FLOAT_EL_SIZE}; # step file pointer to start of next RRA |
1256
|
|
|
|
|
|
|
} # $ii rra_cnt |
1257
|
|
|
|
|
|
|
} # complex update |
1258
|
10
|
|
|
|
|
29
|
$rrd->{last_up}=$current_time; |
1259
|
|
|
|
|
|
|
} # args |
1260
|
10
|
50
|
|
|
|
22
|
if ($inplace eq "file") { |
1261
|
|
|
|
|
|
|
# update header |
1262
|
0
|
|
|
|
|
0
|
seek $fd,0,0; |
1263
|
|
|
|
|
|
|
#print $fd $self->getheader(); |
1264
|
0
|
|
|
|
|
0
|
$self->_saveheader($fd); |
1265
|
|
|
|
|
|
|
#close($fd); |
1266
|
|
|
|
|
|
|
} |
1267
|
10
|
|
|
|
|
68
|
return 1; |
1268
|
|
|
|
|
|
|
} |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
sub fetch { |
1271
|
|
|
|
|
|
|
# dump out measurement data |
1272
|
1
|
|
|
1
|
1
|
542
|
my ($self, $args_str) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
3
|
|
1273
|
1
|
|
|
|
|
2
|
my $out=''; |
1274
|
|
|
|
|
|
|
|
1275
|
1
|
|
|
|
|
2
|
my $step=$rrd->{pdp_step}; my $start=time()-24*60*60; my $end=time(); my $digits=10; # number of digits printed for floats |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1
|
|
1276
|
1
|
|
|
|
|
2
|
my $ret; my $args; |
1277
|
1
|
|
|
|
|
4
|
($ret, $args) = _GetOptionsFromString($args_str, |
1278
|
|
|
|
|
|
|
"resolution|r:i" => \$step, |
1279
|
|
|
|
|
|
|
"start|s:i" => \$start, |
1280
|
|
|
|
|
|
|
"end|e:i" => \$end, |
1281
|
|
|
|
|
|
|
"digits|d:i" => \$digits |
1282
|
|
|
|
|
|
|
); |
1283
|
|
|
|
|
|
|
# at the moment, start/end times are unix timestamps. |
1284
|
1
|
50
|
|
|
|
5
|
if ($start < 3600 * 24 * 365 * 10) {croak("the first entry to fetch should be after 1980");} |
|
0
|
|
|
|
|
0
|
|
1285
|
1
|
50
|
|
|
|
4
|
if ($end < $start) {croak("start ($start) should be less than end ($end)");} |
|
0
|
|
|
|
|
0
|
|
1286
|
1
|
50
|
|
|
|
4
|
if ($step<1) {croak("step must be >= 1 second");} |
|
0
|
|
|
|
|
0
|
|
1287
|
1
|
|
|
|
|
3
|
my $cf=uc($args->[0]); my $i; # so CF must be first word in argument line |
|
1
|
|
|
|
|
1
|
|
1288
|
1
|
50
|
|
|
|
8
|
if ($cf !~ m/AVERAGE|MIN|MAX|LAST/) {croak("unknown CF\n");} |
|
0
|
|
|
|
|
0
|
|
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
# find the RRA which best matches the requirements |
1291
|
1
|
|
|
|
|
1
|
my $cal_end; my $cal_start; my $step_diff; my $firstfull=1; my $firstpart=1; |
|
0
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1
|
|
1292
|
1
|
|
|
|
|
2
|
my $full_match=$end-$start; |
1293
|
1
|
|
|
|
|
2
|
my $best_full_step_diff=0; my $best_full_rra; my $best_match=0; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1
|
|
1294
|
1
|
|
|
|
|
1
|
my $best_part_step_diff=0; my $best_part_rra; |
|
1
|
|
|
|
|
1
|
|
1295
|
|
|
|
|
|
|
my $tmp_match; |
1296
|
1
|
|
|
|
|
4
|
for ($i = 0; $i < $rrd->{rra_cnt}; $i++) { |
1297
|
5
|
100
|
|
|
|
13
|
if ($rrd->{rra}[$i]->{name} eq $cf) { |
1298
|
2
|
|
|
|
|
6
|
$cal_end=$rrd->{last_up} - $rrd->{last_up}%($rrd->{rra}[$i]->{pdp_cnt}*$rrd->{pdp_step}); |
1299
|
2
|
|
|
|
|
5
|
$cal_start=$cal_end - $rrd->{rra}[$i]->{pdp_cnt}*$rrd->{rra}[$i]->{row_cnt}*$rrd->{pdp_step}; |
1300
|
2
|
|
|
|
|
5
|
$step_diff = $step-$rrd->{pdp_step}*$rrd->{rra}[$i]->{pdp_cnt}; |
1301
|
2
|
100
|
|
|
|
5
|
if ($step_diff<0) {$step_diff=-$step_diff;} # take absolute value |
|
1
|
|
|
|
|
2
|
|
1302
|
2
|
100
|
|
|
|
6
|
if ($cal_start <= $start) { |
1303
|
1
|
50
|
33
|
|
|
4
|
if ($firstfull || $step_diff < $best_full_step_diff) { |
1304
|
1
|
|
|
|
|
1
|
$firstfull=0; $best_full_step_diff = $step_diff; $best_full_rra=$i; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2
|
|
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
} else { |
1307
|
1
|
|
|
|
|
1
|
$tmp_match = $full_match; |
1308
|
1
|
50
|
|
|
|
3
|
if ($cal_start>$start) {$tmp_match-=($cal_start-$start);} |
|
1
|
|
|
|
|
2
|
|
1309
|
1
|
50
|
0
|
|
|
4
|
if ($firstpart || ($best_match<$tmp_match && $step_diff < $best_part_step_diff)) { |
|
|
|
33
|
|
|
|
|
1310
|
1
|
|
|
|
|
1
|
$firstpart=0; $best_match=$tmp_match; $best_part_step_diff=$step_diff; $best_part_rra=$i; |
|
1
|
|
|
|
|
13
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
1311
|
|
|
|
|
|
|
} |
1312
|
|
|
|
|
|
|
} |
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
} |
1315
|
1
|
|
|
|
|
2
|
my $chosen_rra; my @line; |
1316
|
1
|
50
|
|
|
|
6
|
if ($firstfull == 0) {$chosen_rra=$best_full_rra;} |
|
1
|
0
|
|
|
|
1
|
|
|
0
|
|
|
|
|
0
|
|
1317
|
0
|
|
|
|
|
0
|
elsif ($firstpart==0) {$chosen_rra=$best_part_rra;} |
1318
|
|
|
|
|
|
|
else {croak("the RRD does not contain an RRA matching the chosen CF");} |
1319
|
1
|
|
|
|
|
3
|
$step = $rrd->{rra}[$chosen_rra]->{pdp_cnt}*$rrd->{pdp_step}; |
1320
|
1
|
|
|
|
|
2
|
$start -= $start % $step; |
1321
|
1
|
|
|
|
|
2
|
$end += ($step - $end % $step); |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
1324
|
1
|
50
|
|
|
|
3
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
0
|
|
|
|
|
0
|
|
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
# output column headings |
1327
|
1
|
|
|
|
|
3
|
$out.=sprintf "%12s"," "; |
1328
|
1
|
|
|
|
|
4
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
1329
|
4
|
|
|
|
|
13
|
$out.=sprintf "%-17s", $rrd->{ds}[$i]->{name}; |
1330
|
|
|
|
|
|
|
} |
1331
|
1
|
|
|
|
|
2
|
$out.=sprintf "%s", "\n"; |
1332
|
1
|
|
|
|
|
5
|
my $t = $rrd->{last_up} - $rrd->{last_up}%($rrd->{rra}[$chosen_rra]->{pdp_cnt}*$rrd->{pdp_step}) -($rrd->{rra}[$chosen_rra]->{row_cnt}-1)*$rrd->{rra}[$chosen_rra]->{pdp_cnt}*$rrd->{pdp_step}; |
1333
|
1
|
|
|
|
|
1
|
my $jj; my $j; |
1334
|
1
|
|
|
|
|
5
|
for ($j=0; $j<$rrd->{rra}[$chosen_rra]->{row_cnt}; $j++) { |
1335
|
5
|
50
|
33
|
|
|
19
|
if ($t > $start && $t <= $end+$step) { |
1336
|
5
|
|
|
|
|
11
|
$out.=sprintf "%10u: ",$t; |
1337
|
5
|
|
|
|
|
9
|
$jj= ($rrd->{rra}[$chosen_rra]->{ptr}+1 + $j)%$rrd->{rra}[$chosen_rra]->{row_cnt}; |
1338
|
5
|
|
|
|
|
12
|
@line=$self->_unpackd($rrd->{rra}[$chosen_rra]->{data}[$jj]); |
1339
|
5
|
|
|
|
|
10
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
1340
|
20
|
|
|
|
|
26
|
$out.=sprintf "%-17s",_strfloat($line[$i],$digits); |
1341
|
|
|
|
|
|
|
} |
1342
|
5
|
|
|
|
|
6
|
$out.=sprintf "%s", "\n"; |
1343
|
|
|
|
|
|
|
} |
1344
|
5
|
|
|
|
|
11
|
$t+=$step; |
1345
|
|
|
|
|
|
|
} |
1346
|
1
|
|
|
|
|
37
|
return $out; |
1347
|
|
|
|
|
|
|
} |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
sub info { |
1350
|
|
|
|
|
|
|
# dump out header info |
1351
|
1
|
|
|
1
|
1
|
2
|
my $self=$_[0]; my $rrd = $self->{rrd}; |
|
1
|
|
|
|
|
2
|
|
1352
|
1
|
|
|
|
|
2
|
my $out=''; |
1353
|
|
|
|
|
|
|
|
1354
|
1
|
|
|
|
|
2
|
my $digits=10; my $noencoding=0; |
|
1
|
|
|
|
|
1
|
|
1355
|
1
|
50
|
|
|
|
4
|
if (defined($_[1])) { |
1356
|
1
|
|
|
|
|
1
|
my $ret; my $args; |
1357
|
1
|
|
|
|
|
5
|
($ret, $args) = _GetOptionsFromString($_[1], |
1358
|
|
|
|
|
|
|
"digits|d:i" => \$digits, |
1359
|
|
|
|
|
|
|
"noformat|n" => \$noencoding |
1360
|
|
|
|
|
|
|
); |
1361
|
|
|
|
|
|
|
} |
1362
|
|
|
|
|
|
|
|
1363
|
1
|
|
|
|
|
7
|
$out.=sprintf "%s", "rrd_version = ".$rrd->{version}."\n"; |
1364
|
1
|
50
|
|
|
|
5
|
if ($noencoding<0.5) { |
1365
|
0
|
|
|
|
|
0
|
$out.=sprintf "%s", "encoding = "; |
1366
|
0
|
0
|
0
|
|
|
0
|
if ($self->{encoding} eq "native-double-simple" || $self->{encoding} eq "native-double-mixed") { |
|
|
0
|
|
|
|
|
|
1367
|
0
|
|
|
|
|
0
|
$out.="native-double"; |
1368
|
|
|
|
|
|
|
} elsif ($self->{encoding} =~ /double/) { |
1369
|
0
|
|
|
|
|
0
|
$out.="portable-double"; |
1370
|
|
|
|
|
|
|
} else { |
1371
|
0
|
|
|
|
|
0
|
$out.="portable-single"; |
1372
|
|
|
|
|
|
|
} |
1373
|
0
|
|
|
|
|
0
|
$out.=" (".$self->{encoding}.")\n"; |
1374
|
|
|
|
|
|
|
} |
1375
|
1
|
|
|
|
|
3
|
$out.=sprintf "%s", "step = ".$rrd->{pdp_step}."\n"; |
1376
|
1
|
|
|
|
|
5
|
$out.=sprintf "%s", "last_update = ".int($rrd->{last_up})."\n"; |
1377
|
1
|
|
|
|
|
1
|
my $i; my $str; my $ii; |
|
0
|
|
|
|
|
0
|
|
1378
|
1
|
|
|
|
|
3
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
1379
|
4
|
|
|
|
|
10
|
$str="ds[".$rrd->{ds}[$i]->{name}."]"; |
1380
|
4
|
|
|
|
|
21
|
$out.=sprintf "%s", "$str.index = ".$i."\n"; |
1381
|
4
|
|
|
|
|
10
|
$out.=sprintf "%s", "$str.type = \"".$rrd->{ds}[$i]->{type}."\"\n"; |
1382
|
4
|
|
|
|
|
10
|
$out.=sprintf "%s", "$str.minimal_heartbeat = ".$rrd->{ds}[$i]->{hb}."\n"; |
1383
|
4
|
|
|
|
|
10
|
$out.=sprintf "%s.min = %s\n",$str,_strint($rrd->{ds}[$i]->{min}); |
1384
|
4
|
|
|
|
|
10
|
$out.=sprintf "%s.max = %s\n",$str,_strint($rrd->{ds}[$i]->{max}); |
1385
|
4
|
|
|
|
|
12
|
$out.=sprintf "%s", "$str.last_ds = \"".$rrd->{ds}[$i]->{pdp_prep}->{last_ds}."\"\n"; |
1386
|
4
|
|
|
|
|
8
|
$out.=sprintf "%s.value = %s\n",$str,_strfloat($rrd->{ds}[$i]->{pdp_prep}->{val}, $digits); |
1387
|
4
|
|
|
|
|
20
|
$out.=sprintf "%s", "$str.unknown_sec = ".$rrd->{ds}[$i]->{pdp_prep}->{unkn_sec_cnt}."\n"; |
1388
|
|
|
|
|
|
|
} |
1389
|
1
|
|
|
|
|
5
|
for ($i=0; $i<$rrd->{rra_cnt}; $i++) { |
1390
|
5
|
|
|
|
|
6
|
$str="rra[$i]"; |
1391
|
5
|
|
|
|
|
10
|
$out.=sprintf "%s", "$str.cf = \"".$rrd->{rra}[$i]->{name}."\"\n"; |
1392
|
5
|
|
|
|
|
13
|
$out.=sprintf "%s", "$str.rows = ".$rrd->{rra}[$i]->{row_cnt}."\n"; |
1393
|
5
|
|
|
|
|
7
|
$out.=sprintf "%s", "$str.cur_row = ".$rrd->{rra}[$i]->{ptr}."\n"; |
1394
|
5
|
|
|
|
|
12
|
$out.=sprintf "%s", "$str.pdp_per_row = ".$rrd->{rra}[$i]->{pdp_cnt}."\n"; |
1395
|
5
|
|
|
|
|
7
|
$out.=sprintf "%s.xff = %s\n",$str,_strfloat($rrd->{rra}[$i]->{xff},$digits); |
1396
|
5
|
|
|
|
|
16
|
for ($ii=0; $ii<$rrd->{ds_cnt}; $ii++) { |
1397
|
20
|
|
|
|
|
39
|
$out.=sprintf "%s.cdp_prep[$ii].value = %s\n",$str,_strfloat($rrd->{rra}[$i]->{cdp_prep}[$ii]->[VAL],$digits); |
1398
|
20
|
|
|
|
|
86
|
$out.=sprintf "%s", "$str.cdp_prep[$ii].unknown_datapoints = ".$rrd->{rra}[$i]->{cdp_prep}[$ii]->[UNKN_PDP_CNT]."\n"; |
1399
|
|
|
|
|
|
|
} |
1400
|
|
|
|
|
|
|
} |
1401
|
1
|
|
|
|
|
51
|
return $out; |
1402
|
|
|
|
|
|
|
} |
1403
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
#sub xport { |
1405
|
|
|
|
|
|
|
# # TO DO, incl JSON format |
1406
|
|
|
|
|
|
|
# my ($self, $args_str) = @_; my $rrd=$self->{rrd}; |
1407
|
|
|
|
|
|
|
#} |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
sub dump { |
1410
|
|
|
|
|
|
|
# XML dump of RRD file |
1411
|
4
|
|
|
4
|
1
|
11
|
my ($self, $args_str) = @_; my $rrd=$self->{rrd}; |
|
4
|
|
|
|
|
11
|
|
1412
|
|
|
|
|
|
|
|
1413
|
4
|
|
|
|
|
8
|
my $noheader=0; my $notimecomments=0; my $digits=10; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
6
|
|
1414
|
4
|
50
|
|
|
|
20
|
if (defined($args_str)) { |
1415
|
4
|
|
|
|
|
8
|
my $ret; my $args; |
1416
|
4
|
|
|
|
|
19
|
($ret, $args) = _GetOptionsFromString($args_str, |
1417
|
|
|
|
|
|
|
"no-header|n" => \$noheader, |
1418
|
|
|
|
|
|
|
"notimecomments|t" => \$notimecomments, |
1419
|
|
|
|
|
|
|
"digits|d:i" => \$digits |
1420
|
|
|
|
|
|
|
); |
1421
|
|
|
|
|
|
|
} |
1422
|
4
|
50
|
|
|
|
18
|
my $timecomments = $notimecomments>0 ? 0 : 1; |
1423
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
1425
|
4
|
100
|
|
|
|
21
|
if (!defined($rrd->{dataloaded})) {$self->_loadRRAdata;} |
|
1
|
|
|
|
|
5
|
|
1426
|
|
|
|
|
|
|
|
1427
|
4
|
|
|
|
|
6
|
my $out=''; my @line; |
|
4
|
|
|
|
|
7
|
|
1428
|
|
|
|
|
|
|
|
1429
|
4
|
50
|
|
|
|
13
|
if ($noheader<1) { |
1430
|
4
|
|
|
|
|
9
|
$out.=sprintf "%s", ''."\n"; |
1431
|
4
|
|
|
|
|
10
|
$out.=sprintf "%s", ''."\n"; |
1432
|
|
|
|
|
|
|
} |
1433
|
4
|
|
|
|
|
20
|
$out.=sprintf "%s", "\n\n\t".$rrd->{version}."\n"; |
1434
|
4
|
|
|
|
|
31
|
$out.=sprintf "%s", "\t".$rrd->{pdp_step}." \n\t".$rrd->{last_up}.""; |
1435
|
4
|
50
|
|
|
|
16
|
if ($timecomments) {$out.=" ";} |
|
0
|
|
|
|
|
0
|
|
1436
|
4
|
|
|
|
|
8
|
$out.="\n\t"; |
1437
|
4
|
|
|
|
|
6
|
my $i; my $ii; my $j; my $jj; my $t; my $val; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1438
|
4
|
|
|
|
|
19
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
1439
|
16
|
|
|
|
|
49
|
$out.=sprintf "%s", "\n\t\n\t\t".$rrd->{ds}[$i]->{name}."\n\t\t"; |
1440
|
16
|
|
|
|
|
38
|
$out.=sprintf "%s", "".$rrd->{ds}[$i]->{type}."\n\t\t"; |
1441
|
16
|
|
|
|
|
50
|
$out.=sprintf "%s", "".$rrd->{ds}[$i]->{hb}."\n\t\t"; |
1442
|
16
|
|
|
|
|
41
|
$out.=sprintf "%s\n\t\t%s\n\t\t",_strint($rrd->{ds}[$i]->{min}),_strint($rrd->{ds}[$i]->{max}); |
1443
|
16
|
|
|
|
|
57
|
$out.=sprintf "%s", "\n\t\t\n\t\t".$rrd->{ds}[$i]->{pdp_prep}->{last_ds}."\n\t\t"; |
1444
|
16
|
|
|
|
|
41
|
$out.=sprintf "%s\n\t\t",_strfloat($rrd->{ds}[$i]->{pdp_prep}->{val},$digits); |
1445
|
16
|
|
|
|
|
62
|
$out.=sprintf "%s", "".$rrd->{ds}[$i]->{pdp_prep}->{unkn_sec_cnt}."\n\t"; |
1446
|
16
|
|
|
|
|
45
|
$out.=sprintf "%s", "\n"; |
1447
|
|
|
|
|
|
|
} |
1448
|
4
|
|
|
|
|
8
|
$out.=sprintf "%s", "\n\t\n"; |
1449
|
4
|
|
|
|
|
19
|
for ($i=0; $i<$rrd->{rra_cnt}; $i++) { |
1450
|
20
|
|
|
|
|
28
|
$out.=sprintf "%s", "\t\n\t\t"; |
1451
|
20
|
|
|
|
|
67
|
$out.=sprintf "%s", "".$rrd->{rra}[$i]->{name}."\n\t\t"; |
1452
|
20
|
|
|
|
|
85
|
$out.=sprintf "%s", "".$rrd->{rra}[$i]->{pdp_cnt}." \n\n\t\t"; |
1453
|
20
|
|
|
|
|
124
|
$out.=sprintf "\n\t\t%s\n\t\t\n\t\t",_strfloat($rrd->{rra}[$i]->{xff},$digits); |
1454
|
20
|
|
|
|
|
42
|
$out.=sprintf "%s", "\n\t\t"; |
1455
|
20
|
|
|
|
|
57
|
for ($ii=0; $ii<$rrd->{ds_cnt}; $ii++) { |
1456
|
80
|
|
|
|
|
188
|
$out.=sprintf "\t\n\t\t\t%s\n\t\t\t", _strfloat($rrd->{rra}[$i]->{cdp_prep}[$ii]->[PRIMARY_VAL],$digits); |
1457
|
80
|
|
|
|
|
237
|
$out.=sprintf "%s\n\t\t\t", _strfloat($rrd->{rra}[$i]->{cdp_prep}[$ii]->[SECONDARY_VAL],$digits); |
1458
|
80
|
|
|
|
|
231
|
$out.=sprintf "%s\n\t\t\t",_strfloat($rrd->{rra}[$i]->{cdp_prep}[$ii]->[VAL],$digits); |
1459
|
80
|
|
|
|
|
266
|
$out.=sprintf "%s", "". $rrd->{rra}[$i]->{cdp_prep}[$ii]->[UNKN_PDP_CNT]."\n\t\t\t"; |
1460
|
80
|
|
|
|
|
194
|
$out.=sprintf "%s", "\n\t\t"; |
1461
|
|
|
|
|
|
|
} |
1462
|
20
|
|
|
|
|
22
|
$out.=sprintf "%s", "\n\t\t"; |
1463
|
20
|
|
|
|
|
24
|
$out.=sprintf "%s", "\n\t\t"; |
1464
|
20
|
|
|
|
|
80
|
$t = $rrd->{last_up} - $rrd->{last_up}%($rrd->{rra}[$i]->{pdp_cnt}*$rrd->{pdp_step}) -($rrd->{rra}[$i]->{row_cnt}-1)*$rrd->{rra}[$i]->{pdp_cnt}*$rrd->{pdp_step}; |
1465
|
20
|
|
|
|
|
58
|
for ($j=0; $j<$rrd->{rra}[$i]->{row_cnt}; $j++) { |
1466
|
120
|
|
|
|
|
257
|
$jj= ($rrd->{rra}[$i]->{ptr}+1 + $j)%$rrd->{rra}[$i]->{row_cnt}; |
1467
|
120
|
50
|
|
|
|
197
|
if ($timecomments) {$out.=sprintf "\t%s", " ";} |
|
0
|
|
|
|
|
0
|
|
1468
|
120
|
|
|
|
|
112
|
$out.=""; |
1469
|
120
|
|
|
|
|
324
|
@line=$self->_unpackd($rrd->{rra}[$i]->{data}[$jj]); |
1470
|
120
|
|
|
|
|
309
|
for ($ii=0; $ii<$rrd->{ds_cnt}; $ii++) { |
1471
|
480
|
|
|
|
|
786
|
$out.=sprintf "%s",_strfloat($line[$ii],$digits); |
1472
|
|
|
|
|
|
|
} |
1473
|
120
|
|
|
|
|
122
|
$out.=sprintf "%s", "\n\t\t"; |
1474
|
120
|
|
|
|
|
441
|
$t+=$rrd->{rra}[$i]->{pdp_cnt}*$rrd->{pdp_step}; |
1475
|
|
|
|
|
|
|
} |
1476
|
20
|
|
|
|
|
24
|
$out.=sprintf "%s", "\n\t"; |
1477
|
20
|
|
|
|
|
52
|
$out.=sprintf "%s", "\n"; |
1478
|
|
|
|
|
|
|
} |
1479
|
4
|
|
|
|
|
9
|
$out.=sprintf "%s", "\n"; |
1480
|
4
|
|
|
|
|
84
|
return $out; |
1481
|
|
|
|
|
|
|
} |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
#### |
1484
|
|
|
|
|
|
|
sub _saveheader { |
1485
|
|
|
|
|
|
|
# construct binary header for RRD file |
1486
|
3
|
|
|
3
|
|
6
|
my $self=$_[0]; |
1487
|
3
|
|
|
|
|
4
|
my $fd=$_[1]; |
1488
|
|
|
|
|
|
|
|
1489
|
3
|
|
|
|
|
10
|
my $L=$self->_packlongchar(); |
1490
|
3
|
|
|
|
|
10
|
my $header="\0"x $self->_get_header_size; # preallocate header |
1491
|
3
|
|
|
|
|
11
|
substr($header,0,9,"RRD\0".$self->{rrd}->{version}); |
1492
|
3
|
|
|
|
|
26
|
substr($header,$self->{OFFSET},$self->{FLOAT_EL_SIZE}+3*$self->{LONG_EL_SIZE}, $self->{COOKIE}.pack("$L $L $L",$self->{rrd}->{ds_cnt}, $self->{rrd}->{rra_cnt}, $self->{rrd}->{pdp_step})); |
1493
|
|
|
|
|
|
|
# DS defs |
1494
|
3
|
|
|
|
|
28
|
my $idx=$self->{STAT_HEADER_SIZE0}; |
1495
|
3
|
|
|
|
|
30
|
for (my $i=0; $i<$self->{rrd}->{ds_cnt}; $i++) { |
1496
|
12
|
|
|
|
|
67
|
substr($header,$idx,40+$self->{FLOAT_EL_SIZE},pack("Z20 Z20 $L x".$self->{DIFF_SIZE}, |
1497
|
|
|
|
|
|
|
$self->{rrd}->{ds}[$i]->{name}, $self->{rrd}->{ds}[$i]->{type}, $self->{rrd}->{ds}[$i]->{hb})); |
1498
|
12
|
|
|
|
|
15
|
$idx+=40+$self->{FLOAT_EL_SIZE}; |
1499
|
12
|
|
|
|
|
38
|
my @minmax=($self->{rrd}->{ds}[$i]->{min}, $self->{rrd}->{ds}[$i]->{max}); |
1500
|
12
|
|
|
|
|
25
|
substr($header,$idx,2*$self->{FLOAT_EL_SIZE},$self->_packd(\@minmax)); |
1501
|
12
|
|
|
|
|
39
|
$idx+=9*$self->{FLOAT_EL_SIZE}; |
1502
|
|
|
|
|
|
|
} |
1503
|
|
|
|
|
|
|
# RRA defs |
1504
|
3
|
|
|
|
|
5
|
my $i; |
1505
|
3
|
|
|
|
|
13
|
for ($i=0; $i<$self->{rrd}->{rra_cnt}; $i++) { |
1506
|
15
|
|
|
|
|
70
|
substr($header,$idx,20+$self->{RRA_DEL_PAD}+2*$self->{LONG_EL_SIZE},pack("Z".(20+$self->{RRA_DEL_PAD})." $L $L",$self->{rrd}->{rra}[$i]->{name}, $self->{rrd}->{rra}[$i]->{row_cnt}, $self->{rrd}->{rra}[$i]->{pdp_cnt})); |
1507
|
15
|
|
|
|
|
27
|
$idx+=20+$self->{RRA_DEL_PAD}+2*$self->{LONG_EL_SIZE}; |
1508
|
15
|
|
|
|
|
28
|
my @xff=($self->{rrd}->{rra}[$i]->{xff}); |
1509
|
15
|
|
|
|
|
30
|
substr($header,$idx+$self->{RRA_PAD},$self->{FLOAT_EL_SIZE},$self->_packd(\@xff)); |
1510
|
15
|
|
|
|
|
47
|
$idx += $self->{FLOAT_EL_SIZE}*10+$self->{RRA_PAD}; |
1511
|
|
|
|
|
|
|
} |
1512
|
|
|
|
|
|
|
# live header |
1513
|
3
|
|
|
|
|
12
|
substr($header,$idx,2*$self->{LONG_EL_SIZE},pack("$L $L", $self->{rrd}->{last_up},0)); |
1514
|
3
|
|
|
|
|
7
|
$idx+= 2*$self->{LONG_EL_SIZE}; |
1515
|
|
|
|
|
|
|
# PDP_PREP |
1516
|
3
|
|
|
|
|
15
|
for ($i=0; $i<$self->{rrd}->{ds_cnt}; $i++) { |
1517
|
12
|
|
|
|
|
63
|
substr($header,$idx,30+$self->{PDP_PREP_PAD}+$self->{FLOAT_EL_SIZE}, |
1518
|
|
|
|
|
|
|
pack("Z".(30+$self->{PDP_PREP_PAD})." $L x".$self->{DIFF_SIZE},$self->{rrd}->{ds}[$i]->{pdp_prep}->{last_ds}, $self->{rrd}->{ds}[$i]->{pdp_prep}->{unkn_sec_cnt})); |
1519
|
12
|
|
|
|
|
17
|
$idx+=30+$self->{PDP_PREP_PAD}+$self->{FLOAT_EL_SIZE}; |
1520
|
12
|
|
|
|
|
26
|
my @val=($self->{rrd}->{ds}[$i]->{pdp_prep}->{val}); |
1521
|
12
|
|
|
|
|
22
|
substr($header,$idx,$self->{FLOAT_EL_SIZE},$self->_packd(\@val)); |
1522
|
12
|
|
|
|
|
36
|
$idx+= $self->{FLOAT_EL_SIZE}*9; |
1523
|
|
|
|
|
|
|
} |
1524
|
|
|
|
|
|
|
# CDP_PREP |
1525
|
3
|
|
|
|
|
4
|
my @val; my $ii; |
1526
|
3
|
|
|
|
|
12
|
for (my $ii=0; $ii<$self->{rrd}->{rra_cnt}; $ii++) { |
1527
|
15
|
|
|
|
|
34
|
for ($i=0; $i<$self->{rrd}->{ds_cnt}; $i++) { |
1528
|
|
|
|
|
|
|
# do a bit of code optimisation here |
1529
|
60
|
50
|
33
|
|
|
237
|
if ($self->{encoding} eq "native-double-simple" || $self->{encoding} eq "native-double-mixed") { |
|
|
50
|
|
|
|
|
|
1530
|
0
|
|
|
|
|
0
|
substr($header,$idx,$self->{CDP_PREP_EL_SIZE}, pack("d $L x".$self->{DIFF_SIZE}." d d d d $L x".$self->{DIFF_SIZE}." $L x".$self->{DIFF_SIZE}." d d",@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]})); |
|
0
|
|
|
|
|
0
|
|
1531
|
0
|
|
|
|
|
0
|
$idx+=$self->{CDP_PREP_EL_SIZE}; |
1532
|
|
|
|
|
|
|
} elsif ($self->{encoding} eq "native-single") { |
1533
|
0
|
|
|
|
|
0
|
substr($header,$idx,$self->{CDP_PREP_EL_SIZE}, pack("f $L x".$self->{DIFF_SIZE}." f f f f $L x".$self->{DIFF_SIZE}." $L x".$self->{DIFF_SIZE}." f f",@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]})); |
|
0
|
|
|
|
|
0
|
|
1534
|
0
|
|
|
|
|
0
|
$idx+=$self->{CDP_PREP_EL_SIZE}; |
1535
|
|
|
|
|
|
|
} else { |
1536
|
60
|
|
|
|
|
67
|
substr($header,$idx,$self->{FLOAT_EL_SIZE},$self->_packd([@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[0]])); |
|
60
|
|
|
|
|
191
|
|
1537
|
60
|
|
|
|
|
96
|
$idx+=$self->{FLOAT_EL_SIZE}; |
1538
|
60
|
|
|
|
|
96
|
substr($header,$idx,$self->{FLOAT_EL_SIZE},pack("$L x".$self->{DIFF_SIZE},@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[1])); |
|
60
|
|
|
|
|
136
|
|
1539
|
60
|
|
|
|
|
58
|
$idx+=$self->{FLOAT_EL_SIZE}; |
1540
|
60
|
|
|
|
|
82
|
substr($header,$idx,4*$self->{FLOAT_EL_SIZE},$self->_packd([@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[2..5]])); |
|
60
|
|
|
|
|
182
|
|
1541
|
60
|
|
|
|
|
106
|
$idx+=4*$self->{FLOAT_EL_SIZE}; |
1542
|
60
|
|
|
|
|
127
|
substr($header,$idx,2*$self->{FLOAT_EL_SIZE},pack("$L x".$self->{DIFF_SIZE}." $L x".$self->{DIFF_SIZE},@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[6..7])); |
|
60
|
|
|
|
|
150
|
|
1543
|
60
|
|
|
|
|
70
|
$idx+=2*$self->{FLOAT_EL_SIZE}; |
1544
|
60
|
|
|
|
|
73
|
substr($header,$idx,2*$self->{FLOAT_EL_SIZE},$self->_packd([@val=@{$self->{rrd}->{rra}[$ii]->{cdp_prep}[$i]}[8..9]])); |
|
60
|
|
|
|
|
232
|
|
1545
|
60
|
|
|
|
|
996
|
$idx+=2*$self->{FLOAT_EL_SIZE}; |
1546
|
|
|
|
|
|
|
} |
1547
|
|
|
|
|
|
|
} |
1548
|
|
|
|
|
|
|
} |
1549
|
|
|
|
|
|
|
# RRA PTR |
1550
|
3
|
|
|
|
|
25
|
for ($i=0; $i<$self->{rrd}->{rra_cnt}; $i++) { |
1551
|
15
|
|
|
|
|
35
|
substr($header,$idx,$self->{LONG_EL_SIZE},pack("$L",$self->{rrd}->{rra}[$i]->{ptr})); |
1552
|
15
|
|
|
|
|
30
|
$idx+=$self->{LONG_EL_SIZE}; |
1553
|
|
|
|
|
|
|
} |
1554
|
|
|
|
|
|
|
#return $header; |
1555
|
3
|
|
|
|
|
73
|
print $fd $header; |
1556
|
|
|
|
|
|
|
} |
1557
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
sub save { |
1559
|
|
|
|
|
|
|
# save RRD data to a file |
1560
|
3
|
|
|
3
|
1
|
868
|
my $self=$_[0]; |
1561
|
|
|
|
|
|
|
|
1562
|
|
|
|
|
|
|
# load RRA data, if not already loaded |
1563
|
3
|
100
|
|
|
|
14
|
if (!defined($self->{rrd}->{dataloaded})) {$self->_loadRRAdata;} |
|
1
|
|
|
|
|
5
|
|
1564
|
|
|
|
|
|
|
|
1565
|
3
|
50
|
|
|
|
21
|
if (@_>1) { |
|
|
50
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
# open file |
1567
|
0
|
|
|
|
|
0
|
$self->{file_name}=$_[1]; |
1568
|
|
|
|
|
|
|
} elsif (!defined($self->{file_name})) { |
1569
|
0
|
|
|
|
|
0
|
croak("Must either supply a filename to use or have a file already opened e.g. via calling open()\n"); |
1570
|
|
|
|
|
|
|
} |
1571
|
3
|
50
|
33
|
2
|
|
122
|
open $self->{fd}, "+<", $self->{file_name} or open $self->{fd}, ">", $self->{file_name} or croak "Couldn't open file ".$self->{file_name}.": $!\n"; |
|
2
|
|
|
|
|
21
|
|
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
18
|
|
1572
|
3
|
|
|
|
|
2301
|
binmode($self->{fd}); |
1573
|
3
|
|
|
|
|
7
|
my $fd=$self->{fd}; |
1574
|
|
|
|
|
|
|
|
1575
|
3
|
50
|
|
|
|
10
|
if (!defined($self->{encoding})) { croak("Current encoding must be defined\n.");} |
|
0
|
|
|
|
|
0
|
|
1576
|
3
|
|
|
|
|
7
|
my $current_encoding=$self->{encoding}; |
1577
|
3
|
50
|
|
|
|
11
|
if (@_>2) { |
1578
|
0
|
|
|
|
|
0
|
my $encoding=$_[2]; |
1579
|
0
|
0
|
|
|
|
0
|
if ($encoding !~ m/^(native-double|native-double-simple|native-double-mixed|portable-double|portable-single)$/) {croak("unknown format ".$encoding."\n");} |
|
0
|
|
|
|
|
0
|
|
1580
|
0
|
0
|
|
|
|
0
|
if ($encoding =~ m/^native-double$/) {$encoding=_native_double();} |
|
0
|
|
|
|
|
0
|
|
1581
|
0
|
|
|
|
|
0
|
$self->{encoding}=$encoding; |
1582
|
|
|
|
|
|
|
} |
1583
|
3
|
|
|
|
|
16
|
$self->_sizes; |
1584
|
|
|
|
|
|
|
|
1585
|
|
|
|
|
|
|
# output headers |
1586
|
|
|
|
|
|
|
#print $fd $self->getheader(); |
1587
|
3
|
|
|
|
|
13
|
$self->_saveheader($fd); |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
# output data |
1590
|
3
|
|
|
|
|
9
|
my @line; my $i; my $ii; |
|
0
|
|
|
|
|
0
|
|
1591
|
3
|
|
|
|
|
15
|
for ($ii=0; $ii<$self->{rrd}->{rra_cnt}; $ii++) { |
1592
|
15
|
|
|
|
|
43
|
for ($i=0; $i<$self->{rrd}->{rra}[$ii]->{row_cnt}; $i++) { |
1593
|
75
|
50
|
|
|
|
108
|
if ($self->{encoding} ne $current_encoding) { |
1594
|
|
|
|
|
|
|
# need to convert binary data encoding |
1595
|
0
|
|
|
|
|
0
|
@line=$self->_unpackd($self->{rrd}->{rra}[$ii]->{data}[$i],$current_encoding); |
1596
|
0
|
|
|
|
|
0
|
$self->{rrd}->{rra}[$ii]->{data}[$i] = $self->_packd(\@line); |
1597
|
|
|
|
|
|
|
} |
1598
|
75
|
|
|
|
|
228
|
print $fd $self->{rrd}->{rra}[$ii]->{data}[$i]; |
1599
|
|
|
|
|
|
|
} |
1600
|
|
|
|
|
|
|
} |
1601
|
|
|
|
|
|
|
# done |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
# and exit |
1604
|
3
|
|
|
|
|
30
|
return 1; |
1605
|
|
|
|
|
|
|
} |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
#### |
1608
|
|
|
|
|
|
|
sub close { |
1609
|
|
|
|
|
|
|
# close an open RRD file |
1610
|
6
|
|
|
6
|
1
|
1621
|
my ($self) = @_; |
1611
|
6
|
50
|
|
|
|
26
|
if (defined($self->{fd})) { close($self->{fd}); } |
|
6
|
|
|
|
|
84
|
|
1612
|
|
|
|
|
|
|
} |
1613
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
#### |
1615
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
sub create { |
1617
|
|
|
|
|
|
|
# create a new RRD |
1618
|
1
|
|
|
1
|
1
|
621
|
my ($self, $args_str) = @_; my $rrd=$self->{rrd}; |
|
1
|
|
|
|
|
8
|
|
1619
|
|
|
|
|
|
|
|
1620
|
1
|
|
|
|
|
5
|
my $last_up=time(); my $pdp_step=300; |
|
1
|
|
|
|
|
1
|
|
1621
|
1
|
|
|
|
|
3
|
my $encoding="native-double"; # default to RRDTOOL compatible encoding. |
1622
|
1
|
|
|
|
|
2
|
my $ret; my $args; |
1623
|
1
|
|
|
|
|
5
|
($ret, $args) = _GetOptionsFromString($args_str, |
1624
|
|
|
|
|
|
|
"start|b:i" => \$last_up, |
1625
|
|
|
|
|
|
|
"step|s:i" => \$pdp_step, |
1626
|
|
|
|
|
|
|
"format|f:s" => \$encoding |
1627
|
|
|
|
|
|
|
); |
1628
|
1
|
50
|
|
|
|
4
|
if ($last_up < 3600 * 24 * 365 * 10) { croak("the first entry to the RRD should be after 1980\n"); } |
|
0
|
|
|
|
|
0
|
|
1629
|
1
|
50
|
|
|
|
2
|
if ($pdp_step <1) {croak("step size should be no less than one second\n");} |
|
0
|
|
|
|
|
0
|
|
1630
|
1
|
50
|
|
|
|
6
|
if ($encoding !~ m/^(native-double|native-double-simple|native-double-mixed|portable-double|portable-single)$/) {croak("unknown format ".$encoding."\n");} |
|
0
|
|
|
|
|
0
|
|
1631
|
1
|
50
|
|
|
|
3
|
if ($encoding =~ m/^native-double$/) {$encoding=_native_double();} |
|
0
|
|
|
|
|
0
|
|
1632
|
1
|
|
|
|
|
3
|
$self->{encoding}=$encoding; |
1633
|
1
|
|
|
|
|
5
|
$self->_sizes; |
1634
|
|
|
|
|
|
|
|
1635
|
1
|
|
|
|
|
39
|
$rrd->{version}="0003"; |
1636
|
1
|
|
|
|
|
4
|
$rrd->{ds_cnt}=0; $rrd->{rra_cnt}=0; $rrd->{pdp_step}=$pdp_step; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2
|
|
1637
|
1
|
|
|
|
|
3
|
$rrd->{last_up}=$last_up; |
1638
|
|
|
|
|
|
|
|
1639
|
|
|
|
|
|
|
# now parse the DS and RRA info |
1640
|
1
|
|
|
|
|
3
|
my $i; |
1641
|
0
|
|
|
|
|
0
|
my $min; my $max; |
1642
|
1
|
|
|
|
|
2
|
for ($i=0; $i<@{$args}; $i++) { |
|
10
|
|
|
|
|
22
|
|
1643
|
9
|
100
|
|
|
|
34
|
if (${$args}[$i] =~ m/^DS:([a-zA-Z0-9]+):(GAUGE|COUNTER|DERIVE|ABSOLUTE):([0-9]+):(U|[+|-]?[0-9\.]+):(U|[+|-]?[0-9\.]+)$/) { |
|
9
|
50
|
|
|
|
27
|
|
|
5
|
|
|
|
|
19
|
|
1644
|
4
|
|
|
|
|
2
|
my $ds; |
1645
|
4
|
50
|
|
|
|
6
|
$min=$4; if ($min eq "U") {$min=NAN;} # set to NaN |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
3
|
|
1646
|
4
|
50
|
|
|
|
4
|
$max=$5; if ($max eq "U") {$max=NAN;} # set to NaN |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
3
|
|
1647
|
4
|
|
|
|
|
24
|
($ds->{name}, $ds->{type}, $ds->{hb}, $ds->{min}, $ds->{max}, |
1648
|
|
|
|
|
|
|
$ds->{pdp_prep}->{last_ds}, $ds->{pdp_prep}->{unkn_sec_cnt}, $ds->{pdp_prep}->{val} |
1649
|
|
|
|
|
|
|
)= ($1,$2,$3,$min,$max,"U", $last_up%$pdp_step, 0.0); |
1650
|
4
|
|
|
|
|
6
|
$rrd->{ds}[@{$rrd->{ds}}]=$ds; |
|
4
|
|
|
|
|
6
|
|
1651
|
4
|
|
|
|
|
5
|
$rrd->{ds_cnt}++; |
1652
|
|
|
|
|
|
|
} elsif (${$args}[$i] =~ m/^RRA:(AVERAGE|MAX|MIN|LAST):([0-9\.]+):([0-9]+):([0-9]+)$/) { |
1653
|
5
|
|
|
|
|
4
|
my $rra; |
1654
|
5
|
50
|
|
|
|
10
|
if ($4<1) { croak("Invalid row count $4\n");} |
|
0
|
|
|
|
|
0
|
|
1655
|
5
|
50
|
33
|
|
|
19
|
if ($2<0.0 || $2>1.0) { croak("Invalid xff $2: must be between 0 and 1\n");} |
|
0
|
|
|
|
|
0
|
|
1656
|
5
|
50
|
|
|
|
11
|
if ($3<1) { croak("Invalid step $3: must be >= 1\n");} |
|
0
|
|
|
|
|
0
|
|
1657
|
5
|
|
|
|
|
61
|
($rra->{name}, $rra->{xff}, $rra->{pdp_cnt}, $rra->{row_cnt}, $rra->{ptr}, $rra->{data})=($1,$2,$3,$4,int(rand($4)),undef); |
1658
|
5
|
|
|
|
|
8
|
$rrd->{rra}[@{$rrd->{rra}}]=$rra; |
|
5
|
|
|
|
|
6
|
|
1659
|
5
|
|
|
|
|
7
|
$rrd->{rra_cnt}++; |
1660
|
|
|
|
|
|
|
} |
1661
|
|
|
|
|
|
|
} |
1662
|
1
|
50
|
|
|
|
2
|
if ($rrd->{ds_cnt}<1) {croak("You must define at least one Data Source\n");} |
|
0
|
|
|
|
|
0
|
|
1663
|
1
|
50
|
|
|
|
3
|
if ($rrd->{rra_cnt}<1) {croak("You must define at least one Round Robin Archive\n");} |
|
0
|
|
|
|
|
0
|
|
1664
|
|
|
|
|
|
|
|
1665
|
1
|
|
|
|
|
1
|
my $ii; |
1666
|
1
|
|
|
|
|
4
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
1667
|
5
|
|
|
|
|
7
|
for ($i=0; $i<$rrd->{ds_cnt}; $i++) { |
1668
|
20
|
|
|
|
|
27
|
@{$rrd->{rra}[$ii]->{cdp_prep}[$i]} = (NAN,(($last_up-$last_up%$pdp_step)%($pdp_step*$rrd->{rra}[$ii]->{pdp_cnt}))/$pdp_step,0,0,0,0,0,0,0,0); |
|
20
|
|
|
|
|
65
|
|
1669
|
|
|
|
|
|
|
} |
1670
|
|
|
|
|
|
|
} |
1671
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
# initialise the data |
1673
|
1
|
|
|
|
|
1
|
my $j; |
1674
|
1
|
|
|
|
|
4
|
my @empty=((NAN)x$rrd->{ds_cnt}); |
1675
|
1
|
|
|
|
|
5
|
for ($ii=0; $ii<$rrd->{rra_cnt}; $ii++) { |
1676
|
5
|
|
|
|
|
14
|
for ($i=0; $i<$rrd->{rra}[$ii]->{row_cnt}; $i++) { |
1677
|
25
|
|
|
|
|
45
|
$rrd->{rra}[$ii]->{data}[$i]=$self->_packd(\@empty); |
1678
|
|
|
|
|
|
|
} |
1679
|
|
|
|
|
|
|
} |
1680
|
1
|
|
|
|
|
10
|
$rrd->{dataloaded}=1; # record the fact that the data is now loaded in memory |
1681
|
|
|
|
|
|
|
} |
1682
|
|
|
|
|
|
|
|
1683
|
|
|
|
|
|
|
#### |
1684
|
|
|
|
|
|
|
sub open { |
1685
|
|
|
|
|
|
|
# open an RRD file and read the header; reading of the body of the RRD file (containing the RRA data) is left until actually needed |
1686
|
6
|
|
|
6
|
1
|
2235
|
my $self = $_[0]; my $rrd=$self->{rrd}; |
|
6
|
|
|
|
|
30
|
|
1687
|
6
|
|
|
|
|
17
|
$self->{file_name}=$_[1]; |
1688
|
|
|
|
|
|
|
|
1689
|
6
|
50
|
|
|
|
308
|
open($self->{fd}, "<", $self->{file_name}) or croak "Couldn't open file ".$self->{file_name}.": $!\n"; |
1690
|
6
|
|
|
|
|
25
|
binmode($self->{fd}); |
1691
|
6
|
|
|
|
|
77
|
my $file_len = -s $self->{file_name}; |
1692
|
|
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
# check static part of the header (with fixed size) |
1694
|
|
|
|
|
|
|
# header format: {cookie[4], version[5], double float_cookie, ds_cnt, rra_cnt, pdp_step, par[10] (unused array) } |
1695
|
6
|
|
|
|
|
110
|
read($self->{fd},my $staticheader,16+8*NATIVE_DOUBLE_EL_SIZE); |
1696
|
6
|
|
|
|
|
41
|
my $file_cookie = unpack("Z4",substr($staticheader,0,4)); |
1697
|
6
|
50
|
|
|
|
27
|
if ($file_cookie ne "RRD") { croak("Wrong magic id $file_cookie\n"); } |
|
0
|
|
|
|
|
0
|
|
1698
|
6
|
|
|
|
|
25
|
$rrd->{version}=unpack("Z5",substr($staticheader,4,5)); |
1699
|
6
|
50
|
33
|
|
|
27
|
if ($rrd->{version} ne "0003" && $rrd->{version} ne "0004") { croak("Unsupported RRD version ".$rrd->{version}."\n");} |
|
0
|
|
|
|
|
0
|
|
1700
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
# use float cookie to try to figure out the encoding used, taking account of variable byte alignment (e.g. float cookie starts at byte 12 on 32 bits Intel/Linux machines and at byte 16 on 64 bit Intel/Linux machines) |
1702
|
|
|
|
|
|
|
#my ($x, $y, $byte1, $byte2, $byte3, $byte4, $byte5, $byte6, $byte7, $byte8) =unpack("Z4 Z5 x![L!] C C C C C C C C",substr($staticheader,0,length($staticheader))); |
1703
|
|
|
|
|
|
|
#print $byte1, " ", $byte2, " ",$byte3," ", $byte4," ", $byte5," ", $byte6," ", $byte7," ", $byte8,"\n"; |
1704
|
6
|
|
|
|
|
13
|
$self->{encoding}=undef; |
1705
|
6
|
|
|
|
|
39
|
(my $x, my $y, my $t) =unpack("Z4 Z5 x![L!] d",substr($staticheader,0,length($staticheader))); |
1706
|
6
|
|
|
|
|
83
|
my $file_floatcookie_native_double_simple = sprintf("%0.6e", $t); |
1707
|
6
|
|
|
|
|
33
|
($x, $y, $t) =unpack("Z4 Z5 x![d] d",substr($staticheader,0,length($staticheader))); |
1708
|
6
|
|
|
|
|
40
|
my $file_floatcookie_native_double_mixed = sprintf("%0.6e", $t); |
1709
|
6
|
|
|
|
|
38
|
($t)=$self->_unpackd(substr($staticheader,12,PORTABLE_SINGLE_EL_SIZE),"native-single"); |
1710
|
6
|
|
|
|
|
50
|
my $file_floatcookie_native_single=sprintf("%0.6e",$t); |
1711
|
6
|
|
|
|
|
22
|
($t)=$self->_unpackd(substr($staticheader,12,PORTABLE_SINGLE_EL_SIZE),"portable-single"); |
1712
|
6
|
|
|
|
|
45
|
my $file_floatcookie_portable_single=sprintf("%0.6e",$t); |
1713
|
6
|
|
|
|
|
27
|
($t)=$self->_unpackd(substr($staticheader,12,PORTABLE_DOUBLE_EL_SIZE),"portable-double"); |
1714
|
6
|
|
|
|
|
55
|
my $file_floatcookie_portable_double=sprintf("%0.6e",$t); |
1715
|
6
|
|
|
|
|
9
|
my $file_floatcookie_littleendian_single; |
1716
|
|
|
|
|
|
|
my $file_floatcookie_littleendian_double; |
1717
|
6
|
50
|
|
|
|
21
|
if ($PACK_LITTLE_ENDIAN_SUPPORT>0) { |
1718
|
6
|
|
|
|
|
101
|
($t)=$self->_unpackd(substr($staticheader,12,PORTABLE_SINGLE_EL_SIZE),"littleendian-single"); |
1719
|
6
|
|
|
|
|
43
|
$file_floatcookie_littleendian_single=sprintf("%0.6e",$t); |
1720
|
6
|
|
|
|
|
20
|
($t)=$self->_unpackd(substr($staticheader,12,PORTABLE_DOUBLE_EL_SIZE),"littleendian-double"); |
1721
|
6
|
|
|
|
|
38
|
$file_floatcookie_littleendian_double=sprintf("%0.6e",$t); |
1722
|
|
|
|
|
|
|
} |
1723
|
6
|
|
|
|
|
12
|
my $cookie=sprintf("%0.6e",DOUBLE_FLOATCOOKIE); |
1724
|
6
|
|
|
|
|
12
|
my $singlecookie=sprintf("%0.6e",SINGLE_FLOATCOOKIE); |
1725
|
6
|
50
|
33
|
|
|
66
|
if ($file_floatcookie_native_double_simple eq $cookie) { |
|
|
50
|
0
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1726
|
0
|
|
|
|
|
0
|
$self->{encoding} = "native-double-simple"; |
1727
|
|
|
|
|
|
|
} elsif ($file_floatcookie_native_double_mixed eq $cookie ) { |
1728
|
0
|
|
|
|
|
0
|
$self->{encoding} = "native-double-mixed"; |
1729
|
|
|
|
|
|
|
} elsif ($file_floatcookie_native_single eq $singlecookie ) { |
1730
|
1
|
|
|
|
|
3
|
$self->{encoding} = "native-single"; |
1731
|
|
|
|
|
|
|
} elsif ($PACK_LITTLE_ENDIAN_SUPPORT>0 && $file_floatcookie_littleendian_double eq $cookie) { |
1732
|
5
|
|
|
|
|
15
|
$self->{encoding} = "littleendian-double"; |
1733
|
|
|
|
|
|
|
} elsif ($PACK_LITTLE_ENDIAN_SUPPORT>0 && $file_floatcookie_littleendian_single eq $singlecookie) { |
1734
|
0
|
|
|
|
|
0
|
$self->{encoding} = "littleendian-single"; |
1735
|
|
|
|
|
|
|
} elsif ($file_floatcookie_portable_single eq $singlecookie) { |
1736
|
0
|
|
|
|
|
0
|
$self->{encoding} = "portable-single"; |
1737
|
|
|
|
|
|
|
} elsif ($file_floatcookie_portable_double eq $cookie) { |
1738
|
0
|
|
|
|
|
0
|
$self->{encoding} = "portable-double"; |
1739
|
|
|
|
|
|
|
} else { |
1740
|
0
|
|
|
|
|
0
|
croak("This RRD was created on incompatible architecture\n"); |
1741
|
|
|
|
|
|
|
} |
1742
|
|
|
|
|
|
|
#print $self->{encoding},"\n"; |
1743
|
|
|
|
|
|
|
#$self->{encoding} = "portable-double"; |
1744
|
6
|
|
|
|
|
22
|
$self->_sizes; # now that we know the encoding, calc the sizes of the various elements in the file |
1745
|
6
|
|
|
|
|
20
|
my $L=$self->_packlongchar; |
1746
|
|
|
|
|
|
|
|
1747
|
|
|
|
|
|
|
# extract info on number of DS's and RRS's, plus the pdp step size |
1748
|
6
|
|
|
|
|
60
|
($rrd->{ds_cnt}, $rrd->{rra_cnt}, $rrd->{pdp_step}) =unpack("$L $L $L",substr($staticheader,$self->{OFFSET} +$self->{FLOAT_EL_SIZE},3*$self->{LONG_EL_SIZE})); |
1749
|
|
|
|
|
|
|
#print $self->{encoding}," ",$offset," ",$L," ",$self->{FLOAT_EL_SIZE}," ", $self->{LONG_EL_SIZE}," ",$rrd->{ds_cnt}," ",$rrd->{rra_cnt}," ",$rrd->{pdp_step},"\n"; |
1750
|
|
|
|
|
|
|
|
1751
|
|
|
|
|
|
|
# read in the full header now; |
1752
|
6
|
|
|
|
|
33
|
seek $self->{fd},0,0; # go back to start of the file |
1753
|
6
|
|
|
|
|
38
|
read($self->{fd},my $header,$self->_get_header_size); |
1754
|
|
|
|
|
|
|
# extract header info into structured arrays |
1755
|
6
|
|
|
|
|
16
|
my $pos=$self->{DS_DEF_IDX}; |
1756
|
6
|
|
|
|
|
25
|
$self->_extractDSdefs(\$header,$pos); |
1757
|
|
|
|
|
|
|
|
1758
|
6
|
|
|
|
|
16
|
$pos+=$self->{DS_EL_SIZE}*$rrd->{ds_cnt}; |
1759
|
6
|
|
|
|
|
24
|
$self->_extractRRAdefs(\$header,$pos); |
1760
|
|
|
|
|
|
|
|
1761
|
6
|
|
|
|
|
12
|
$pos+=$self->{RRA_DEF_EL_SIZE}*$rrd->{rra_cnt}; |
1762
|
6
|
|
|
|
|
26
|
$rrd->{last_up} = unpack("$L",substr($header,$pos,$self->{LONG_EL_SIZE})); |
1763
|
|
|
|
|
|
|
|
1764
|
6
|
|
|
|
|
11
|
$pos+=$self->{LIVE_HEAD_SIZE}; |
1765
|
6
|
|
|
|
|
35
|
$self->_extractPDPprep(\$header,$pos); |
1766
|
|
|
|
|
|
|
|
1767
|
6
|
|
|
|
|
14
|
$pos+=$self->{PDP_PREP_EL_SIZE}*$rrd->{ds_cnt}; |
1768
|
6
|
|
|
|
|
22
|
$self->_extractCDPprep(\$header,$pos); |
1769
|
|
|
|
|
|
|
|
1770
|
6
|
|
|
|
|
22
|
$pos+=$self->{CDP_PREP_EL_SIZE}*$rrd->{ds_cnt}*$rrd->{rra_cnt}; |
1771
|
6
|
|
|
|
|
28
|
$self->_extractRRAptr(\$header,$pos); |
1772
|
|
|
|
|
|
|
|
1773
|
6
|
|
|
|
|
14
|
$pos+=$self->{RRA_PTR_EL_SIZE}*$rrd->{rra_cnt}; |
1774
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
# validate file size |
1776
|
6
|
|
|
|
|
8
|
my $i; my $row_cnt=0; |
|
6
|
|
|
|
|
10
|
|
1777
|
6
|
|
|
|
|
30
|
for ($i=0; $i<$rrd->{rra_cnt}; $i++) { |
1778
|
30
|
|
|
|
|
76
|
$row_cnt+=$rrd->{rra}[$i]->{row_cnt}; |
1779
|
|
|
|
|
|
|
} |
1780
|
6
|
|
|
|
|
17
|
my $correct_len=$self->_get_header_size +$self->{FLOAT_EL_SIZE} * $row_cnt*$rrd->{ds_cnt}; |
1781
|
6
|
50
|
33
|
|
|
41
|
if ($file_len < $correct_len || $file_len > $correct_len+8) { # extra 8 bytes here is to allow for padding on Linux/Intel 64 bit platforms |
1782
|
0
|
|
|
|
|
0
|
croak($self->{file_name}." size is incorrect (is $file_len bytes but should be $correct_len bytes)"); |
1783
|
|
|
|
|
|
|
} |
1784
|
6
|
|
|
|
|
13
|
$rrd->{dataloaded}=undef; # keep note that data is not loaded yet |
1785
|
6
|
|
|
|
|
38
|
return $self->{encoding}; |
1786
|
|
|
|
|
|
|
} |
1787
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
1; |
1789
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
|
1791
|
|
|
|
|
|
|
=pod |
1792
|
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
=head1 NAME |
1794
|
|
|
|
|
|
|
|
1795
|
|
|
|
|
|
|
RRD::Editor - Portable, standalone (no need for RRDs.pm) tool to create and edit RRD files. |
1796
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1798
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
use strict; |
1800
|
|
|
|
|
|
|
use RRD::Editor (); |
1801
|
|
|
|
|
|
|
|
1802
|
|
|
|
|
|
|
# Create a new object |
1803
|
|
|
|
|
|
|
my $rrd = RRD::Editor->new(); |
1804
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
# Create a new RRD with 3 data sources called bytesIn, bytesOut and |
1806
|
|
|
|
|
|
|
# faultsPerSec and one RRA which stores 1 day worth of data at 5 minute |
1807
|
|
|
|
|
|
|
# intervals (288 data points). The argument format is the same as that used |
1808
|
|
|
|
|
|
|
# by 'rrdtool create', see L |
1809
|
|
|
|
|
|
|
$rrd->create("DS:bytesIn:GAUGE:600:U:U DS:bytesOut:GAUGE:600:U:U DS:faultsPerSec:COUNTER:600:U:U RRA:AVERAGE:0.5:1:288") |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
# Save RRD to a file |
1812
|
|
|
|
|
|
|
$rrd->save("myfile.rrd"); |
1813
|
|
|
|
|
|
|
# The file format to use can also be optionally specified: |
1814
|
|
|
|
|
|
|
# $rrd->save("myfile.rrd","native-double"); # default; non-portable format used by RRDTOOL |
1815
|
|
|
|
|
|
|
# $rrd->save("myfile.rrd","portable-double"); # portable, data stored in double-precision |
1816
|
|
|
|
|
|
|
# $rrd->save("myfile.rrd","portable-single"); # portable, data stored in single-precision |
1817
|
|
|
|
|
|
|
|
1818
|
|
|
|
|
|
|
# Load RRD from a file. Automagically figures out the file format |
1819
|
|
|
|
|
|
|
# (native-double, portable-double etc) |
1820
|
|
|
|
|
|
|
$rrd->open("myfile.rrd"); |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
# Add new data to the RRD for the same 3 data sources bytesIn, |
1823
|
|
|
|
|
|
|
# bytesOut and faultsPerSec. The argument format is the same as that used by |
1824
|
|
|
|
|
|
|
# 'rrdtool update', see L |
1825
|
|
|
|
|
|
|
$rrd->update("N:10039:389:0.4"); |
1826
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
# Show information about an RRD. Output generated is similar to |
1828
|
|
|
|
|
|
|
# 'rrdtool info'. |
1829
|
|
|
|
|
|
|
print $rrd->info(); |
1830
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
# XML dump of RRD contents. Output generated is similar to 'rrdtool dump'. |
1832
|
|
|
|
|
|
|
print $rrd->dump(); |
1833
|
|
|
|
|
|
|
|
1834
|
|
|
|
|
|
|
# Extract data measurements stored in RRAs of type AVERAGE |
1835
|
|
|
|
|
|
|
# The argument format is the same as that used by 'rrdtool fetch' and |
1836
|
|
|
|
|
|
|
# the output generated is also similar, see |
1837
|
|
|
|
|
|
|
# L |
1838
|
|
|
|
|
|
|
print $rrd->fetch("AVERAGE"); |
1839
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
# Get the time when the RRD was last updated (as a unix timestamp) |
1841
|
|
|
|
|
|
|
printf "RRD last updated at %d\n", $rrd->last(); |
1842
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
# Get the measurements added when the RRD was last updated |
1844
|
|
|
|
|
|
|
print $rrd->lastupdate(); |
1845
|
|
|
|
|
|
|
|
1846
|
|
|
|
|
|
|
# Get the min step size (or resolution) of the RRD. This defaults to 300s unless specified |
1847
|
|
|
|
|
|
|
otherwise when creating an RRD. |
1848
|
|
|
|
|
|
|
print $rrd->minstep() |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
=head2 Edit Data Sources |
1851
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
# Add a new data-source called bytes. Argument format is the same as $rrd->create(). |
1853
|
|
|
|
|
|
|
$rrd->add_DS("DS:bytes:GAUGE:600:U:U"); |
1854
|
|
|
|
|
|
|
|
1855
|
|
|
|
|
|
|
# Delete the data-source bytesIn |
1856
|
|
|
|
|
|
|
$rrd->delete_DS("bytesIn"); |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
# Get a list of the data-sources names |
1859
|
|
|
|
|
|
|
print $rrd->DS_names(); |
1860
|
|
|
|
|
|
|
|
1861
|
|
|
|
|
|
|
# Change the name of data-source bytes to be bytes_new |
1862
|
|
|
|
|
|
|
$rrd->rename_DS("bytes", "bytes_new") |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
# Get the heartbeat value for data-source bytesOut (the max number of seconds that |
1865
|
|
|
|
|
|
|
# may elapse between data measurements) |
1866
|
|
|
|
|
|
|
printf "Heartbeat for DS bytesOut = %d\n", $rrd->DS_heartbeat("bytesOut"); |
1867
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
# Set the heartbeat value for data-source bytesOut to be 1200 secs |
1869
|
|
|
|
|
|
|
$rrd->set_DS_heartbeat("bytesOut",1200); |
1870
|
|
|
|
|
|
|
|
1871
|
|
|
|
|
|
|
# Get the type (COUNTER, GAUGE etc) of data-source bytesOut |
1872
|
|
|
|
|
|
|
printf "Type of DS bytesOut = %s\n", $rrd->DS_type("bytesOut"); |
1873
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
# Set the type of data-source bytesOut to be COUNTER |
1875
|
|
|
|
|
|
|
$rrd->set_DS_type("bytesOut", "COUNTER"); |
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
# Get the minimum value allowed for measurements from data-source bytesOut |
1878
|
|
|
|
|
|
|
printf "Min value of DS bytesOut = %s\n", $rrd->DS_min("bytesOut"); |
1879
|
|
|
|
|
|
|
|
1880
|
|
|
|
|
|
|
# Set the minimum value allowed for measurements from data-source bytesOut to be 0 |
1881
|
|
|
|
|
|
|
$rrd->set_DS_min("bytesOut",0); |
1882
|
|
|
|
|
|
|
|
1883
|
|
|
|
|
|
|
# Get the maximum value allowed for measurements from data-source bytesOut |
1884
|
|
|
|
|
|
|
printf "Max value of DS bytesOut = %s\n", $rrd->DS_max("bytesOut"); |
1885
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
# Set the maximum value allowed for measurements from data-source bytesOut to be 100 |
1887
|
|
|
|
|
|
|
$rrd->set_DS_max("bytesOut",100); |
1888
|
|
|
|
|
|
|
|
1889
|
|
|
|
|
|
|
=head2 Edit RRAs |
1890
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
# Add a new RRA which stores 1 weeks worth of data (336 data points) at 30 minute |
1892
|
|
|
|
|
|
|
# intervals (30 mins = 6 x 5 mins) |
1893
|
|
|
|
|
|
|
$rrd->add_RRA("RRA:AVERAGE:0.5:6:336"); |
1894
|
|
|
|
|
|
|
|
1895
|
|
|
|
|
|
|
# RRAs are identified by an index in range 0 .. $rrd->num_RRAs(). The index |
1896
|
|
|
|
|
|
|
# of an RRD can also be found using $rrd->info() or $rrd->dump() |
1897
|
|
|
|
|
|
|
my $rra_idx=1; |
1898
|
|
|
|
|
|
|
|
1899
|
|
|
|
|
|
|
# Delete an existing RRA with index $rra_idx. |
1900
|
|
|
|
|
|
|
$rrd->delete_RRA($rra_idx); |
1901
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
# Get the number of rows/data points stored in the RRA with index $rra_idx |
1903
|
|
|
|
|
|
|
$rra_idx=0; |
1904
|
|
|
|
|
|
|
printf "number of rows of RRA %d = %d\n", $rra_idx, $rrd->RRA_numrows($rra_idx); |
1905
|
|
|
|
|
|
|
|
1906
|
|
|
|
|
|
|
# Change the number of rows/data points stored in the RRA with index |
1907
|
|
|
|
|
|
|
# $rra_idx to be 600. |
1908
|
|
|
|
|
|
|
$rra->resize_RRA($rra_idx, 600); |
1909
|
|
|
|
|
|
|
|
1910
|
|
|
|
|
|
|
# Get the value of bytesIn stored at the 10th row/data-point in the |
1911
|
|
|
|
|
|
|
# RRA with index $rra_idx. |
1912
|
|
|
|
|
|
|
printf "Value of data-source bytesIn at row 10 in RRA %d = %d", $rra_idx, $rra->RRA_el($rra_idx, "bytesIn", 10); |
1913
|
|
|
|
|
|
|
|
1914
|
|
|
|
|
|
|
# Set the value of bytesIn at the 10th row/data-point to be 100 |
1915
|
|
|
|
|
|
|
$rra->set_RRA_el($rra_idx, "bytesIn", 10, 100); |
1916
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
# Get the xff value for the RRA with index $rra_idx |
1918
|
|
|
|
|
|
|
printf "Xff value of RRA %d = %d\n", $rra_idx, $rra->RRA_xff($rra_idx); |
1919
|
|
|
|
|
|
|
|
1920
|
|
|
|
|
|
|
# Set the xff value to 0.75 for the RRA with index $rra_idx |
1921
|
|
|
|
|
|
|
$rra->RRA_xff($rra_idx,0.75); |
1922
|
|
|
|
|
|
|
|
1923
|
|
|
|
|
|
|
# Get the type (AVERAGE, LAST etc) of the RRA with index $rra_idx |
1924
|
|
|
|
|
|
|
print $rrd->RRA_type($rra_idx); |
1925
|
|
|
|
|
|
|
|
1926
|
|
|
|
|
|
|
# Get the step (in seconds) of the RRA with index $rra_idx |
1927
|
|
|
|
|
|
|
print $rrd->RRA_step($rra_idx); |
1928
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
|
1930
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1931
|
|
|
|
|
|
|
|
1932
|
|
|
|
|
|
|
RRD:Editor implements most of the functionality of RRDTOOL, apart from graphing, plus adds some new editing and portability features. It aims to be portable and self-contained (no need for RRDs.pm). |
1933
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
RRD::Editor provides the ability to add/delete DSs and RRAs and to get/set most of the parameters in DSs and RRAs (renaming, resizing etc). It also allows the data values stored in each RRA to be inspected and changed individually. That is, it provides almost complete control over the contents of an RRD. |
1935
|
|
|
|
|
|
|
|
1936
|
|
|
|
|
|
|
The RRD files created by RRDTOOL use a binary format (let's call it C) that is not portable across platforms. In addition to this file format, RRD:Editor provides two new portable file formats (C and C) that allow the exchange of files. RRD::Editor can freely convert RRD files between these three formats (C,C and C). |
1937
|
|
|
|
|
|
|
|
1938
|
|
|
|
|
|
|
Notes: |
1939
|
|
|
|
|
|
|
|
1940
|
|
|
|
|
|
|
=over |
1941
|
|
|
|
|
|
|
|
1942
|
|
|
|
|
|
|
=item * times must all be specified as unix timestamps (i.e. -1d, -1w etc don't work, and there is no @ option in rrdupdate). |
1943
|
|
|
|
|
|
|
|
1944
|
|
|
|
|
|
|
=item * there is full support for COUNTER, GAUGE, DERIVE and ABSOLUTE data-source types but the COMPUTE type is only partially supported. |
1945
|
|
|
|
|
|
|
|
1946
|
|
|
|
|
|
|
=item * there is full support for AVERAGE, MIN, MAX, LAST RRA types but the HWPREDICT, MHWPREDICT, SEASONAL etc types are only partially supported). |
1947
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
=back |
1949
|
|
|
|
|
|
|
|
1950
|
|
|
|
|
|
|
=head1 METHODS |
1951
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
=head2 new |
1953
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
my $rrd=new RRD:Editor->new(); |
1955
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
Creates a new RRD::Editor object |
1957
|
|
|
|
|
|
|
|
1958
|
|
|
|
|
|
|
=head2 create |
1959
|
|
|
|
|
|
|
|
1960
|
|
|
|
|
|
|
$rrd->create($args); |
1961
|
|
|
|
|
|
|
|
1962
|
|
|
|
|
|
|
The method will create a new RRD with the data-sources and RRAs specified by C<$args>. C<$args> is a string that contains the same sort of command line arguments that would be passed to C. The format for C<$args> is: |
1963
|
|
|
|
|
|
|
|
1964
|
|
|
|
|
|
|
[--start|-b start time] [--step|-s step] [--format|-f encoding] [DS:ds-name:DST:heartbeat:min:max] [RRA:CF:xff:steps:rows] |
1965
|
|
|
|
|
|
|
|
1966
|
|
|
|
|
|
|
where DST may be one of GAUGE, COUNTER, DERIVE, ABSOLUTE and CF may be one of AVERAGE, MIN, MAX, LAST. Possible values for encoding are C, C, C. If omitted, defaults to C (the non-portable file format used by RRDTOOL). See L for further information. |
1967
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
=head2 open |
1969
|
|
|
|
|
|
|
|
1970
|
|
|
|
|
|
|
$rrd->open($file_name); |
1971
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
Load the RRD in the file called C<$file_name>. Only the file header is loaded initially, to improve efficiency, with the body of the file subsequently loaded if needed. The file format (C, C etc) is detected automagically. |
1973
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
=head2 save |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
$rrd->save(); |
1977
|
|
|
|
|
|
|
$rrd->save($file_name); |
1978
|
|
|
|
|
|
|
$rrd->save($file_name, $encoding); |
1979
|
|
|
|
|
|
|
|
1980
|
|
|
|
|
|
|
Save RRD to a file called $file_name with format specified by C<$encoding>. Possible values for C<$encoding> are C<"native-double">, C<"portable-double">, C<"portable-single">. |
1981
|
|
|
|
|
|
|
|
1982
|
|
|
|
|
|
|
If omitted, C<$encoding> defaults to the format of the file specified when calling C, or to C<"native-double"> if the RRD has just been created using C. C is the non-portable binary format used by RRDTOOL. C is portable across platforms and stores data as double-precision values. C is portable across platforms and stores data as single-precision values (reducing the RRD file size by approximately half). If interested in the gory details, C is just the C format used by Intel 32-bit platforms (i.e. little-endian byte ordering, 32 bit integers, 64 bit IEEE 754 doubles, storage aligned to 32 bit boundaries) - an arbitrary choice, but not unreasonable since Intel platforms are probably the most widespread at the moment, and it is also compatible with web tools such as javascriptRRD L. |
1983
|
|
|
|
|
|
|
|
1984
|
|
|
|
|
|
|
If the RRD was opened using C, then C<$file_name> is optional and if omitted C will save the RRD to the same file as it was read from. |
1985
|
|
|
|
|
|
|
|
1986
|
|
|
|
|
|
|
=head2 close |
1987
|
|
|
|
|
|
|
|
1988
|
|
|
|
|
|
|
$rrd->close(); |
1989
|
|
|
|
|
|
|
|
1990
|
|
|
|
|
|
|
Close an RRD file accessed using C or C. Calling C flushes any cached data to disk. |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
=head2 info |
1993
|
|
|
|
|
|
|
|
1994
|
|
|
|
|
|
|
my $info = $rrd->info(); |
1995
|
|
|
|
|
|
|
|
1996
|
|
|
|
|
|
|
Returns a string containing information on the DSs and RRAs in the RRD (but not showing the data values stored in the RRAs). Also shows details of the file format (C, C etc) if the RRD was read from a file. |
1997
|
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
=head2 dump |
1999
|
|
|
|
|
|
|
|
2000
|
|
|
|
|
|
|
my $dump = $rrd->dump(); |
2001
|
|
|
|
|
|
|
my $dump = $rrd->dump($arg); |
2002
|
|
|
|
|
|
|
|
2003
|
|
|
|
|
|
|
Returns a string containing the complete contents of the RRD (including data) in XML format. C<$arg> is optional. Possible values are "--no-header" or "-n", which remove the XML header from the output string. |
2004
|
|
|
|
|
|
|
|
2005
|
|
|
|
|
|
|
=head2 fetch |
2006
|
|
|
|
|
|
|
|
2007
|
|
|
|
|
|
|
my $vals = $rrd->fetch($args); |
2008
|
|
|
|
|
|
|
|
2009
|
|
|
|
|
|
|
Returns a string containing a table of measurement data from the RRD. C<$args> is a string that contains the same sort of command line arguments that would be passed to C. The format for C<$args> is: |
2010
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
CF [--resolution|-r resolution] [--start|-s start] [--end|-e end] |
2012
|
|
|
|
|
|
|
|
2013
|
|
|
|
|
|
|
where C may be one of AVERAGE, MIN, MAX, LAST. See L for further details. |
2014
|
|
|
|
|
|
|
|
2015
|
|
|
|
|
|
|
=head2 update |
2016
|
|
|
|
|
|
|
|
2017
|
|
|
|
|
|
|
$rrd->update($args); |
2018
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
Feeds new data values into the RRD. C<$args> is a string that contains the same sort of command line arguments that would be passed to C. The format for C<$args> is: |
2020
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
[--template:-t ds-name[:ds-name]...] N|timestamp:value[:value...] [timestamp:value[:value...] ...] |
2022
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
See L for further details. |
2024
|
|
|
|
|
|
|
|
2025
|
|
|
|
|
|
|
Since C is often called repeatedly, in-place updating of RRD files is used where possible for greater efficiency . To understand this, a little knowledge of the RRD file format is needed. RRD files consist of a small header containing details of the DSs and RRAs, and a large body containing the data values stored in the RRAs. Reading the body into memory is relatively costly since it is much larger than the header, and so is only done by RRD::Editor on an "as-needed" basis. So long as the body has not yet been read into memory when C is called, C will update the file on disk i.e. without reading in the body. In this case there is no need to call C. If the body has been loaded into memory when C is called, then the copy of the data stored in memory will be updated and the file on disk left untouched - a call to C is then needed to freshen the file stored on disk. Seems complicated, but its actually ok in practice. If all you want to do is efficiently update a file, just use the following formula: |
2026
|
|
|
|
|
|
|
|
2027
|
|
|
|
|
|
|
$rrd->open($file_name); |
2028
|
|
|
|
|
|
|
$rrd->update($args); |
2029
|
|
|
|
|
|
|
$rrd->close(); |
2030
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
and that's it. If you want to do more, then be sure to call C when you're done. |
2032
|
|
|
|
|
|
|
|
2033
|
|
|
|
|
|
|
=head2 last |
2034
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
my $unixtime = $rrd->last(); |
2036
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
Returns the time when the data stored in the RRD was last updated. The time is returned as a unix timestamp. This value should not be confused with the last modified time of the RRD file. |
2038
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
=head2 set_last |
2040
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
$rrd->set_last($unixtime); |
2042
|
|
|
|
|
|
|
|
2043
|
|
|
|
|
|
|
Set the last update time to equal C<$unixtime>. WARNING: Rarely needed, use with caution ! |
2044
|
|
|
|
|
|
|
|
2045
|
|
|
|
|
|
|
=head2 lastupdate |
2046
|
|
|
|
|
|
|
|
2047
|
|
|
|
|
|
|
my @vals=$rrd->lastupdate(); |
2048
|
|
|
|
|
|
|
|
2049
|
|
|
|
|
|
|
Return a list containing the data-source values inserted at the most recent update to the RRD |
2050
|
|
|
|
|
|
|
|
2051
|
|
|
|
|
|
|
=head2 minstep |
2052
|
|
|
|
|
|
|
|
2053
|
|
|
|
|
|
|
my $minstep = $rrd->minstep(); |
2054
|
|
|
|
|
|
|
|
2055
|
|
|
|
|
|
|
Returns the minimum step size (in seconds) used to store data values in the RRD. RRA data intervals must be integer multiples of this step size. The min step size defaults to 300s when creating an RRD (where it is referred to as the "resolution"). NB: Changing the min step size is hard as it would require resampling all of the stored data, so we leave this "to do". |
2056
|
|
|
|
|
|
|
|
2057
|
|
|
|
|
|
|
=head2 add_DS |
2058
|
|
|
|
|
|
|
|
2059
|
|
|
|
|
|
|
$rrd->add_DS($arg); |
2060
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
Add a new data-source to the RRD. Only one data-source can be added at a time. Details of the data-source to be added are specified by the string C<$arg>. The format of C<$arg> is: |
2062
|
|
|
|
|
|
|
|
2063
|
|
|
|
|
|
|
[DS:ds-name:DST:heartbeat:min:max] |
2064
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
where DST may be one of GAUGE, COUNTER, DERIVE, ABSOLUTE i.e. the same format as used for C. |
2066
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
=head2 delete_DS |
2068
|
|
|
|
|
|
|
|
2069
|
|
|
|
|
|
|
$rrd->delete_DS($ds-name); |
2070
|
|
|
|
|
|
|
|
2071
|
|
|
|
|
|
|
Delete the data-source with name C<$ds-name> from the RRD. WARNING: This will irreversibly delete all of the data stored for the data-source. |
2072
|
|
|
|
|
|
|
|
2073
|
|
|
|
|
|
|
=head2 DS_names |
2074
|
|
|
|
|
|
|
|
2075
|
|
|
|
|
|
|
my @ds-names = $rrd->DS_names(); |
2076
|
|
|
|
|
|
|
|
2077
|
|
|
|
|
|
|
Returns a list containing the names of the data-sources in the RRD. |
2078
|
|
|
|
|
|
|
|
2079
|
|
|
|
|
|
|
=head2 rename_DS |
2080
|
|
|
|
|
|
|
|
2081
|
|
|
|
|
|
|
$rrd->rename_DS($ds-name, $ds-newname); |
2082
|
|
|
|
|
|
|
|
2083
|
|
|
|
|
|
|
Change the name of data-source C<$ds-name> to be C<$ds-newname> |
2084
|
|
|
|
|
|
|
|
2085
|
|
|
|
|
|
|
=head2 DS_heartbeat |
2086
|
|
|
|
|
|
|
|
2087
|
|
|
|
|
|
|
my $hb= $rrd->DS_heartbeat($ds-name); |
2088
|
|
|
|
|
|
|
|
2089
|
|
|
|
|
|
|
Returns the current heartbeat (in seconds) of a data-source. The heartbeat is the max number of seconds that may elapse between data measurements before declaring that data is missing. |
2090
|
|
|
|
|
|
|
|
2091
|
|
|
|
|
|
|
=head2 set_DS_heartbeat |
2092
|
|
|
|
|
|
|
|
2093
|
|
|
|
|
|
|
$rrd->set_DS_heartbeat($ds-name,$hb); |
2094
|
|
|
|
|
|
|
|
2095
|
|
|
|
|
|
|
Sets the heartbeat value (in seconds) of data-source C<$ds-name> to be C<$hb>. |
2096
|
|
|
|
|
|
|
|
2097
|
|
|
|
|
|
|
=head2 DS_type |
2098
|
|
|
|
|
|
|
|
2099
|
|
|
|
|
|
|
my $type = $rrd->DS_type($ds-name); |
2100
|
|
|
|
|
|
|
|
2101
|
|
|
|
|
|
|
Returns the type (GAUGE, COUNTER etc) of a data-source. |
2102
|
|
|
|
|
|
|
|
2103
|
|
|
|
|
|
|
=head2 set_DS_type |
2104
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
$rrd->set_DS_type($ds-name, $type); |
2106
|
|
|
|
|
|
|
|
2107
|
|
|
|
|
|
|
Sets the type of data-source C<$ds-name> to be C<$type>. |
2108
|
|
|
|
|
|
|
|
2109
|
|
|
|
|
|
|
=head2 DS_min |
2110
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
my $min = $rrd->DS_min($ds-name); |
2112
|
|
|
|
|
|
|
|
2113
|
|
|
|
|
|
|
Returns the minimum allowed for measurements from data-source C<$ds-name>. Measurements below this value are set equal to C<$mi>n when stored in the RRD. |
2114
|
|
|
|
|
|
|
|
2115
|
|
|
|
|
|
|
=head2 set_DS_min |
2116
|
|
|
|
|
|
|
|
2117
|
|
|
|
|
|
|
$rrd->set_DS_min($ds-name, $min); |
2118
|
|
|
|
|
|
|
|
2119
|
|
|
|
|
|
|
Set the minimum value for data-source C<$ds-name> to be C<$min>. |
2120
|
|
|
|
|
|
|
|
2121
|
|
|
|
|
|
|
=head2 DS_max |
2122
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
my $max = $rrd->DS_max($ds-name); |
2124
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
Returns the maximum allowed for measurements from data-source C<$ds-name>. Measurements above this value are set equal to C<$max> when stored in the RRD. |
2126
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
=head2 set_DS_max |
2128
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
$rrd->set_DS_max($ds-name, $max); |
2130
|
|
|
|
|
|
|
|
2131
|
|
|
|
|
|
|
Set the maximum value for data-source C<$ds-name> to be C<$max>. |
2132
|
|
|
|
|
|
|
|
2133
|
|
|
|
|
|
|
=head2 add_RRA |
2134
|
|
|
|
|
|
|
|
2135
|
|
|
|
|
|
|
$rrd->add_RRA($arg); |
2136
|
|
|
|
|
|
|
|
2137
|
|
|
|
|
|
|
Add a new RRA to the RRD. Only one RRA can be added at a time. Details of the RRA to be added are specified by the string C<$arg>. The format of C<$arg> is: |
2138
|
|
|
|
|
|
|
|
2139
|
|
|
|
|
|
|
[RRA:CF:xff:steps:rows] |
2140
|
|
|
|
|
|
|
|
2141
|
|
|
|
|
|
|
where CF may be one of AVERAGE, MIN, MAX, LAST i.e. the same format as used for C. |
2142
|
|
|
|
|
|
|
|
2143
|
|
|
|
|
|
|
=head2 num_RRAs |
2144
|
|
|
|
|
|
|
|
2145
|
|
|
|
|
|
|
my $num_RRAs = $rrd->num_RRAs(); |
2146
|
|
|
|
|
|
|
|
2147
|
|
|
|
|
|
|
Returns the number of RRAs stored in the RRD. Unfortunately, unlike data-sources, RRAs are not named and so are only identified by an index in the range 0 .. C. The index of a specific RRD can be found using C or C. |
2148
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
=head2 delete_RRA |
2150
|
|
|
|
|
|
|
|
2151
|
|
|
|
|
|
|
$rrd->delete_RRA($rra_idx); |
2152
|
|
|
|
|
|
|
|
2153
|
|
|
|
|
|
|
Delete the RRA with index C<$rra_idx> (see above discussion for how to determine the index of an RRA). WARNING: This will irreversibly delete all of the data stored in the RRA. |
2154
|
|
|
|
|
|
|
|
2155
|
|
|
|
|
|
|
=head2 RRA_numrows |
2156
|
|
|
|
|
|
|
|
2157
|
|
|
|
|
|
|
my $numrows = $rrd->RRA_numrows($rra_idx); |
2158
|
|
|
|
|
|
|
|
2159
|
|
|
|
|
|
|
Returns the number of rows in the RRA with index C<$rra_idx>. |
2160
|
|
|
|
|
|
|
|
2161
|
|
|
|
|
|
|
=head2 resize_RRA |
2162
|
|
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
$rra->resize_RRA($rra_idx, $numrows); |
2164
|
|
|
|
|
|
|
|
2165
|
|
|
|
|
|
|
Change the number of rows to be C<$numrows> in the RRA with index C<$rra_idx>. WARNING: If C<$numrows> is smaller than the current row size, excess data points will be discarded. |
2166
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
=head2 RRA_el |
2168
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
my ($t,$val) = $rra->RRA_el($rra_idx, $ds-name, $row); |
2170
|
|
|
|
|
|
|
|
2171
|
|
|
|
|
|
|
Returns the timestamp and the value of data-source C<$ds-name> stored at row C<$row> in the RRA with index C<$rra_idx>. C<$row> must be in the range [0 .. C]. Row 0 corresponds to the oldest data point stored and row C to the most recent data point. |
2172
|
|
|
|
|
|
|
|
2173
|
|
|
|
|
|
|
=head2 set_RRA_el |
2174
|
|
|
|
|
|
|
|
2175
|
|
|
|
|
|
|
$rra->set_RRA_el($rra_idx, $ds-name, $row, $val); |
2176
|
|
|
|
|
|
|
|
2177
|
|
|
|
|
|
|
Set the stored value equal to C<$val> for data-source C<$ds-nam>e stored at row C<$row> in the RRA with index C<$rra_idx>. |
2178
|
|
|
|
|
|
|
|
2179
|
|
|
|
|
|
|
=head2 RRA_xff |
2180
|
|
|
|
|
|
|
|
2181
|
|
|
|
|
|
|
my $xff = $rra->RRA_xff($rra_idx); |
2182
|
|
|
|
|
|
|
|
2183
|
|
|
|
|
|
|
Returns the xff value for the RRA with index C<$rra_idx>. The xff value defines the proportion of an RRA data interval that may contain UNKNOWN data (i.e. missing data) and still be treated as known. For example, an xff value 0.5 in an RRA with data interval 300 seconds (5 minutes) means that if less than 150s of valid data is available since the last measurement, UNKNOWN will be stored in the RRA for the next data point. |
2184
|
|
|
|
|
|
|
|
2185
|
|
|
|
|
|
|
=head2 set_RRA_xff |
2186
|
|
|
|
|
|
|
|
2187
|
|
|
|
|
|
|
$rra->RRA_xff($rra_idx,$xff); |
2188
|
|
|
|
|
|
|
|
2189
|
|
|
|
|
|
|
Sets the xff value to C<$xff> for the RRA with index C<$rra_idx>. |
2190
|
|
|
|
|
|
|
|
2191
|
|
|
|
|
|
|
=head2 RRA_step |
2192
|
|
|
|
|
|
|
|
2193
|
|
|
|
|
|
|
my $step = $rrd->RRA_step($rra_idx); |
2194
|
|
|
|
|
|
|
|
2195
|
|
|
|
|
|
|
Returns the data interval (in seconds) of the RRA with index C<$rra_idx>. NB: Changing the step size is hard as it would require resampling the data stored in the RRA, so we leave this "to do". |
2196
|
|
|
|
|
|
|
|
2197
|
|
|
|
|
|
|
=head2 RRA_type |
2198
|
|
|
|
|
|
|
|
2199
|
|
|
|
|
|
|
my $type = $rrd->RRA_type($rra_idx); |
2200
|
|
|
|
|
|
|
|
2201
|
|
|
|
|
|
|
Returns the type of the RRA with index C<$rra_idx> i.e. AVERAGE, MAX, MIN, LAST etc. NB: Changing the type of an RRA is hard (impossible ?) as the stored data doesn't contain enough information to allow its type to be changed. To change type, its recommended instead to delete the RRA and add a new RRA with the desired type. |
2202
|
|
|
|
|
|
|
|
2203
|
|
|
|
|
|
|
=head1 EXPORTS |
2204
|
|
|
|
|
|
|
|
2205
|
|
|
|
|
|
|
You can export the following functions if you do not want to use the object orientated interface: |
2206
|
|
|
|
|
|
|
|
2207
|
|
|
|
|
|
|
create |
2208
|
|
|
|
|
|
|
open |
2209
|
|
|
|
|
|
|
save |
2210
|
|
|
|
|
|
|
close |
2211
|
|
|
|
|
|
|
update |
2212
|
|
|
|
|
|
|
info |
2213
|
|
|
|
|
|
|
dump |
2214
|
|
|
|
|
|
|
fetch |
2215
|
|
|
|
|
|
|
last |
2216
|
|
|
|
|
|
|
set_last |
2217
|
|
|
|
|
|
|
lastupdate |
2218
|
|
|
|
|
|
|
minstep |
2219
|
|
|
|
|
|
|
add_RRA |
2220
|
|
|
|
|
|
|
delete_RRA |
2221
|
|
|
|
|
|
|
num_RRAs |
2222
|
|
|
|
|
|
|
RRA_numrows |
2223
|
|
|
|
|
|
|
resize_RRA |
2224
|
|
|
|
|
|
|
RRA_type |
2225
|
|
|
|
|
|
|
RRA_step |
2226
|
|
|
|
|
|
|
RRA_xff |
2227
|
|
|
|
|
|
|
set_RRA_xff |
2228
|
|
|
|
|
|
|
add_DS |
2229
|
|
|
|
|
|
|
delete_DS |
2230
|
|
|
|
|
|
|
DS_names |
2231
|
|
|
|
|
|
|
rename_DS |
2232
|
|
|
|
|
|
|
DS_heartbeat |
2233
|
|
|
|
|
|
|
set_DS_heartbeat |
2234
|
|
|
|
|
|
|
DS_min |
2235
|
|
|
|
|
|
|
set_DS_min |
2236
|
|
|
|
|
|
|
DS_max |
2237
|
|
|
|
|
|
|
set_DS_max |
2238
|
|
|
|
|
|
|
DS_type |
2239
|
|
|
|
|
|
|
set_DS_type |
2240
|
|
|
|
|
|
|
|
2241
|
|
|
|
|
|
|
The tag C is available to easily export everything: |
2242
|
|
|
|
|
|
|
|
2243
|
|
|
|
|
|
|
use RRD::Editor qw(:all); |
2244
|
|
|
|
|
|
|
|
2245
|
|
|
|
|
|
|
=head1 Portability/Compatibility with RRDTOOL |
2246
|
|
|
|
|
|
|
|
2247
|
|
|
|
|
|
|
The RRD::Editor code is portable, and so long as you stick to using the C and C file formats the RRD files generated will also be portable. Portability issues arise when the C file format of RRD::Editor is used to store RRDs. This format tries to be compatible with the non-portable binary format used by RRDTOOL, which requires RRD::Editor to figure out nasty low-level details of the platform it is running on (byte ordering, byte alignment, representation used for doubles etc). To date, RRD::Editor and RRDTOOL have been confirmed compatible (i.e. they can read each others RRD files in C format) on the following architectures: |
2248
|
|
|
|
|
|
|
|
2249
|
|
|
|
|
|
|
Intel 386 32bit, Intel 686 32bit, AMD64/Intel x86 64bit, Itanium 64bit, Alpha 64bit, MIPS 32bit, MIPSel 32 bit, MIPS 64bit, PowerPC 32bit, ARMv6 (e.g. Raspberry Pi), SPARC 32bit, SPARC 64bit, SH4 |
2250
|
|
|
|
|
|
|
|
2251
|
|
|
|
|
|
|
Known issues: |
2252
|
|
|
|
|
|
|
|
2253
|
|
|
|
|
|
|
On ARMv5 platforms RRD::Editor and RRDTOOL file formats may be only partially compatible (RRD::Editor can read RRDTOOL files, but sometimes not vice-versa depending on ARM config) |
2254
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
For more information on RRD::Editor portability testing, see L. If your platform is not listed, there is a good chance things will "just work" but double checking that RRDTOOL can read the C format RRD files generated by RRD::Editor, and vice-versa, would be a good idea if that's important to you. |
2256
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
=head1 SEE ALSO |
2258
|
|
|
|
|
|
|
|
2259
|
|
|
|
|
|
|
L command line interface for RRD::Editor, L, L, L |
2260
|
|
|
|
|
|
|
|
2261
|
|
|
|
|
|
|
=head1 VERSION |
2262
|
|
|
|
|
|
|
|
2263
|
|
|
|
|
|
|
Ver 0.18 |
2264
|
|
|
|
|
|
|
|
2265
|
|
|
|
|
|
|
=head1 AUTHOR |
2266
|
|
|
|
|
|
|
|
2267
|
|
|
|
|
|
|
Doug Leith |
2268
|
|
|
|
|
|
|
|
2269
|
|
|
|
|
|
|
=head1 BUGS |
2270
|
|
|
|
|
|
|
|
2271
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. |
2272
|
|
|
|
|
|
|
|
2273
|
|
|
|
|
|
|
=head1 COPYRIGHT |
2274
|
|
|
|
|
|
|
|
2275
|
|
|
|
|
|
|
Copyright 2014 D.J.Leith. |
2276
|
|
|
|
|
|
|
|
2277
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. |
2278
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
2280
|
|
|
|
|
|
|
|
2281
|
|
|
|
|
|
|
=cut |
2282
|
|
|
|
|
|
|
|
2283
|
|
|
|
|
|
|
|
2284
|
|
|
|
|
|
|
__END__ |