1 #!/usr/bin/perl
3 use strict;
4 use warnings;
6 =head1 NAME
8 extractDS.px - Extract a single data-source from an RRD-file
10 =head1 SYNOPSYS
12 extractDS.px -i input.rrd -s source_ds -o output.rrd -d destination_ds
14 =head1 DEPENDENCIES
16 extractDS.px requires Perl and the included L<Getopt::Long> module, as well as
17 the L<XML::Simple> module.
19 =cut
21 use Getopt::Long ('GetOptions');
22 use XML::Simple (qw(xml_in xml_out));
23 use Data::Dumper ();
25 our $InFile;
26 our $InDS = [];
27 our $OutFile;
28 our $OutDS = [];
30 GetOptions ("infile|i=s" => \$InFile,
31 "inds|s=s" => sub { push (@$InDS, $_[1]); },
32 "outfile|o=s" => \$OutFile,
33 "outds|d=s" => sub { push (@$OutDS, $_[1]); })
34 or exit (1);
36 if (!$InFile || !$OutFile || !@$InDS || !@$OutDS)
37 {
38 print STDERR "Usage: $0 -i <infile> -s <inds> -o <outfile> -d <outds>\n";
39 exit (1);
40 }
41 if (!-f $InFile)
42 {
43 print STDERR "Input file does not exist\n";
44 exit (1);
45 }
46 if (-f $OutFile)
47 {
48 print STDERR "Output file does exist\n";
49 exit (1);
50 }
51 if ((1 + @$InDS) != (1 + @$OutDS))
52 {
53 print STDERR "You need the same amount of in- and out-DSes\n";
54 exit (1);
55 }
57 extract_ds ($InFile, $OutFile);
58 exit (0);
60 {
61 my $ds_index;
62 my $current_index;
63 # state 0 == searching for DS index
64 # state 1 == parse RRA header
65 # state 2 == parse values
66 my $state;
67 my $out_cache;
68 sub handle_line
69 {
70 my $fh = shift;
71 my $line = shift;
73 if (!defined ($state))
74 {
75 $current_index = -1;
76 $state = 0;
77 $out_cache = [];
79 # $ds_index->[new_index] = old_index
80 $ds_index = [];
81 for (my $i = 0; $i < @$InDS; $i++)
82 {
83 $ds_index->[$i] = -1;
84 }
85 }
87 if ($state == 0)
88 {
89 if ($line =~ m/<ds>/)
90 {
91 $current_index++;
92 $out_cache->[$current_index] = $line;
93 }
94 elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
95 {
96 # old_index == $current_index
97 # new_index == $i
98 for (my $i = 0; $i < @$InDS; $i++)
99 {
100 next if ($ds_index->[$i] >= 0);
102 if ($1 eq $InDS->[$i])
103 {
104 $line =~ s#<name>\s*([^<\s]+)\s*</name>#<name> $OutDS->[$i] </name>#;
105 $ds_index->[$i] = $current_index;
106 last;
107 }
108 }
110 $out_cache->[$current_index] .= $line;
111 }
112 elsif ($line =~ m#</ds>#)
113 {
114 $out_cache->[$current_index] .= $line;
115 }
116 elsif ($line =~ m#<rra>#)
117 {
118 # Print out all the DS definitions we need
119 for (my $new_index = 0; $new_index < @$InDS; $new_index++)
120 {
121 my $old_index = $ds_index->[$new_index];
122 print $fh $out_cache->[$old_index];
123 }
125 # Clear the cache - it's used in state1, too.
126 for (my $i = 0; $i <= $current_index; $i++)
127 {
128 $out_cache->[$i] = '';
129 }
131 print $fh $line;
132 $current_index = -1;
133 $state = 1;
134 }
135 elsif ($current_index == -1)
136 {
137 # Print all the lines before the first DS definition
138 print $fh $line;
139 }
140 else
141 {
142 # Something belonging to a DS-definition
143 $out_cache->[$current_index] .= $line;
144 }
145 }
146 elsif ($state == 1)
147 {
148 if ($line =~ m#<ds>#)
149 {
150 $current_index++;
151 $out_cache->[$current_index] .= $line;
152 }
153 elsif ($line =~ m#</cdp_prep>#)
154 {
155 # Print out all the DS definitions we need
156 for (my $new_index = 0; $new_index < @$InDS; $new_index++)
157 {
158 my $old_index = $ds_index->[$new_index];
159 print $fh $out_cache->[$old_index];
160 }
162 # Clear the cache
163 for (my $i = 0; $i <= $current_index; $i++)
164 {
165 $out_cache->[$i] = '';
166 }
168 print $fh $line;
169 $current_index = -1;
170 }
171 elsif ($line =~ m#<database>#)
172 {
173 print $fh $line;
174 $state = 2;
175 }
176 elsif ($current_index == -1)
177 {
178 # Print all the lines before the first DS definition
179 # and after cdp_prep
180 print $fh $line;
181 }
182 else
183 {
184 # Something belonging to a DS-definition
185 $out_cache->[$current_index] .= $line;
186 }
187 }
188 elsif ($state == 2)
189 {
190 if ($line =~ m#</database>#)
191 {
192 print $fh $line;
193 $current_index = -1;
194 $state = 1;
195 }
196 else
197 {
198 my @values = ();
199 my $i;
200 my $output = "\t\t";
202 if ($line =~ m#(<!-- .*? -->)#)
203 {
204 $output .= "$1 ";
205 }
206 $output .= "<row> ";
208 $i = 0;
209 while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#g)
210 {
211 $values[$i] = $1;
212 $i++;
213 }
215 for (my $new_index = 0; $new_index < @$InDS; $new_index++)
216 {
217 my $old_index = $ds_index->[$new_index];
218 $output .= '<v> ' . $values[$old_index] . ' </v> ';
219 }
220 $output .= "</row>\n";
221 print $fh $output;
222 }
223 }
224 else
225 {
226 die;
227 }
228 }} # handle_line
230 sub extract_ds
231 {
232 my $in_file = shift;
233 my $out_file = shift;
235 my $in_fh;
236 my $out_fh;
238 open ($in_fh, '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
239 # open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
240 $out_fh = \*STDOUT;
242 while (my $line = <$in_fh>)
243 {
244 handle_line ($out_fh, $line);
245 }
247 close ($in_fh);
248 close ($out_fh);
249 } # extract_ds
251 =head1 AUTHOR
253 Florian octo Forster E<lt>octo at verplant.orgE<gt>