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 Data::Dumper ();
24 our $InFile;
25 our $InDS = [];
26 our $OutFile;
27 our $OutDS = [];
29 GetOptions ("infile|i=s" => \$InFile,
30 "inds|s=s" => sub { push (@$InDS, $_[1]); },
31 "outfile|o=s" => \$OutFile,
32 "outds|d=s" => sub { push (@$OutDS, $_[1]); })
33 or exit (1);
35 if (!$InFile || !$OutFile || !@$InDS || !@$OutDS)
36 {
37 print STDERR "Usage: $0 -i <infile> -s <inds> -o <outfile> -d <outds>\n";
38 exit (1);
39 }
40 if (!-f $InFile)
41 {
42 print STDERR "Input file does not exist\n";
43 exit (1);
44 }
45 if (-f $OutFile)
46 {
47 print STDERR "Output file does exist\n";
48 exit (1);
49 }
50 if ((1 + @$InDS) != (1 + @$OutDS))
51 {
52 print STDERR "You need the same amount of in- and out-DSes\n";
53 exit (1);
54 }
56 extract_ds ($InFile, $OutFile);
57 exit (0);
59 {
60 my $ds_index;
61 my $current_index;
62 # state 0 == searching for DS index
63 # state 1 == parse RRA header
64 # state 2 == parse values
65 my $state;
66 my $out_cache;
67 sub handle_line
68 {
69 my $fh = shift;
70 my $line = shift;
72 if (!defined ($state))
73 {
74 $current_index = -1;
75 $state = 0;
76 $out_cache = [];
78 # $ds_index->[new_index] = old_index
79 $ds_index = [];
80 for (my $i = 0; $i < @$InDS; $i++)
81 {
82 $ds_index->[$i] = -1;
83 }
84 }
86 if ($state == 0)
87 {
88 if ($line =~ m/<ds>/)
89 {
90 $current_index++;
91 $out_cache->[$current_index] = $line;
92 }
93 elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
94 {
95 # old_index == $current_index
96 # new_index == $i
97 for (my $i = 0; $i < @$InDS; $i++)
98 {
99 next if ($ds_index->[$i] >= 0);
101 if ($1 eq $InDS->[$i])
102 {
103 $line =~ s#<name>\s*([^<\s]+)\s*</name>#<name> $OutDS->[$i] </name>#;
104 $ds_index->[$i] = $current_index;
105 last;
106 }
107 }
109 $out_cache->[$current_index] .= $line;
110 }
111 elsif ($line =~ m#</ds>#)
112 {
113 $out_cache->[$current_index] .= $line;
114 }
115 elsif ($line =~ m#<rra>#)
116 {
117 # Print out all the DS definitions we need
118 for (my $new_index = 0; $new_index < @$InDS; $new_index++)
119 {
120 my $old_index = $ds_index->[$new_index];
121 print $fh $out_cache->[$old_index];
122 }
124 # Clear the cache - it's used in state1, too.
125 for (my $i = 0; $i <= $current_index; $i++)
126 {
127 $out_cache->[$i] = '';
128 }
130 print $fh $line;
131 $current_index = -1;
132 $state = 1;
133 }
134 elsif ($current_index == -1)
135 {
136 # Print all the lines before the first DS definition
137 print $fh $line;
138 }
139 else
140 {
141 # Something belonging to a DS-definition
142 $out_cache->[$current_index] .= $line;
143 }
144 }
145 elsif ($state == 1)
146 {
147 if ($line =~ m#<ds>#)
148 {
149 $current_index++;
150 $out_cache->[$current_index] .= $line;
151 }
152 elsif ($line =~ m#</cdp_prep>#)
153 {
154 # Print out all the DS definitions we need
155 for (my $new_index = 0; $new_index < @$InDS; $new_index++)
156 {
157 my $old_index = $ds_index->[$new_index];
158 print $fh $out_cache->[$old_index];
159 }
161 # Clear the cache
162 for (my $i = 0; $i <= $current_index; $i++)
163 {
164 $out_cache->[$i] = '';
165 }
167 print $fh $line;
168 $current_index = -1;
169 }
170 elsif ($line =~ m#<database>#)
171 {
172 print $fh $line;
173 $state = 2;
174 }
175 elsif ($current_index == -1)
176 {
177 # Print all the lines before the first DS definition
178 # and after cdp_prep
179 print $fh $line;
180 }
181 else
182 {
183 # Something belonging to a DS-definition
184 $out_cache->[$current_index] .= $line;
185 }
186 }
187 elsif ($state == 2)
188 {
189 if ($line =~ m#</database>#)
190 {
191 print $fh $line;
192 $current_index = -1;
193 $state = 1;
194 }
195 else
196 {
197 my @values = ();
198 my $i;
199 my $output = "\t\t";
201 if ($line =~ m#(<!-- .*? -->)#)
202 {
203 $output .= "$1 ";
204 }
205 $output .= "<row> ";
207 $i = 0;
208 while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#g)
209 {
210 $values[$i] = $1;
211 $i++;
212 }
214 for (my $new_index = 0; $new_index < @$InDS; $new_index++)
215 {
216 my $old_index = $ds_index->[$new_index];
217 $output .= '<v> ' . $values[$old_index] . ' </v> ';
218 }
219 $output .= "</row>\n";
220 print $fh $output;
221 }
222 }
223 else
224 {
225 die;
226 }
227 }} # handle_line
229 sub extract_ds
230 {
231 my $in_file = shift;
232 my $out_file = shift;
234 my $in_fh;
235 my $out_fh;
237 open ($in_fh, '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
238 open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
240 while (my $line = <$in_fh>)
241 {
242 handle_line ($out_fh, $line);
243 }
245 close ($in_fh);
246 close ($out_fh);
247 } # extract_ds
249 =head1 AUTHOR
251 Florian octo Forster E<lt>octo at verplant.orgE<gt>