--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+extractDS.px - Extract a single data-source from an RRD-file
+
+=head1 SYNOPSYS
+
+ extractDS.px -i input.rrd -s source_ds -o output.rrd -d destination_ds
+
+=head1 DEPENDENCIES
+
+extractDS.px requires Perl and the included L<Getopt::Long> module, as well as
+the L<XML::Simple> module.
+
+=cut
+
+use Getopt::Long ('GetOptions');
+use XML::Simple (qw(xml_in xml_out));
+use Data::Dumper ();
+
+our $InFile;
+our $InDS;
+our $OutFile;
+our $OutDS;
+
+GetOptions ("infile|i=s" => \$InFile,
+ "inds|s=s" => \$InDS,
+ "outfile|o=s" => \$OutFile,
+ "outds|d=s" => \$OutDS) or exit (1);
+
+if (!$InFile || !$OutFile || !$InDS || !$OutDS)
+{
+ print "$InFile $InDS $OutFile $OutDS\n";
+ print STDERR "Usage: $0 -i <infile> -I <inds> -o <outfile> -O <outds>\n";
+ exit (1);
+}
+if (!-f $InFile)
+{
+ print STDERR "Input file does not exist\n";
+ exit (1);
+}
+if (-f $OutFile)
+{
+ print STDERR "Output file does exist\n";
+ exit (1);
+}
+
+extract_ds ($InFile, $OutFile, $InDS, $OutDS);
+exit (0);
+
+{
+my $ds_index = -1;
+my $current_index = -1;
+# state 0 == searching for DS index
+# state 1 == parse RRA header
+# state 2 == parse <ds> in RRA header
+# state 3 == parse values
+my $state = 0;
+my $out_cache = '';
+sub handle_line
+{
+ my $fh = shift;
+ my $line = shift;
+
+ if (!defined ($state))
+ {
+ $ds_index = -1;
+ $current_index = -1;
+ $state = 0;
+ $out_cache = '';
+ }
+
+ if ($state == 0)
+ {
+ if ($line =~ m/<ds>/)
+ {
+ $out_cache = $line;
+ $current_index++;
+ }
+ elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
+ {
+ if ($1 eq $InDS)
+ {
+ $ds_index = $current_index;
+ $out_cache .= "\t\t<name>$OutDS</name>\n";
+ }
+ }
+ elsif ($line =~ m#</ds>#)
+ {
+ $out_cache .= $line;
+ if ($ds_index == $current_index)
+ {
+ print $fh $out_cache;
+ }
+ }
+ elsif ($line =~ m#<rra>#)
+ {
+ print $fh $line;
+ $current_index = -1;
+ $state = 1;
+ }
+ elsif ($current_index == -1)
+ {
+ print $fh $line;
+ }
+ else
+ {
+ $out_cache .= $line;
+ }
+ }
+ elsif ($state == 1)
+ {
+ if ($line =~ m#<ds>#)
+ {
+ $current_index++;
+ if ($current_index == $ds_index)
+ {
+ print $fh $line;
+ }
+
+ if ($line =~ m#</ds>#) { $state = 1; }
+ else { $state = 2; }
+ }
+ elsif ($line =~ m#<database>#)
+ {
+ print $fh $line;
+ $state = 3;
+ }
+ else
+ {
+ print $fh $line;
+ }
+ }
+ elsif ($state == 2)
+ {
+ if ($current_index == $ds_index)
+ {
+ print STDERR $line;
+ print $fh $line;
+ }
+ if ($line =~ m#</ds>#)
+ {
+ $state = 1;
+ }
+ }
+ else
+ {
+ if ($line =~ m#</database>#)
+ {
+ print $fh $line;
+ $current_index = -1;
+ $state = 1;
+ }
+ else
+ {
+ my $line_begin = "\t\t";
+ $current_index = 0;
+ if ($line =~ m#(<!-- .*? -->)#)
+ {
+ $line_begin .= "$1 ";
+ }
+
+ while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#)
+ {
+ my $value = $1;
+ if ($current_index == $ds_index)
+ {
+ print $fh "$line_begin<row> <v>$value</v> </row>\n";
+ last;
+ }
+ $current_index++;
+ }
+ }
+ }
+}} # handle_line
+
+sub extract_ds
+{
+ my $in_file = shift;
+ my $out_file = shift;
+ my $in_ds = shift;
+ my $out_ds = shift;
+
+ my $in_fh;
+ my $out_fh;
+
+ open ($in_fh, '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
+ open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
+
+ while (my $line = <$in_fh>)
+ {
+ handle_line ($out_fh, $line);
+ }
+
+ close ($in_fh);
+ close ($out_fh);
+} # extract_ds
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo at verplant.orgE<gt>
+
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Getopt::Long ('GetOptions');
+use Data::Dumper ();
+use File::Basename ('dirname');
+
+our $InDir = '/var/lib/collectd';
+our $OutDir = '/tmp/collectd-4';
+our $Hostname = 'localhost';
+
+# Types:
+# +------------+----------------------+----+----+----+
+# ! Subdir ! Type ! ti ! pi ! ex !
+# +------------+----------------------+----+----+----+
+# ! apache ! apache_bytes ! ! ! !
+# ! apache ! apache_requests ! ! ! !
+# ! apache ! apache_scoreboard ! x ! ! !
+# ! battery ! charge ! ! x ! !
+# ! apcups ! charge_percent ! ! ! !
+# ! ! cpu ! x ! ! x !
+# ! ! cpufreq ! x ! ! !
+# ! battery ! current ! ! x ! !
+# ! ntpd ! delay ! x ! ! !
+# ! ! df ! x ! ! !
+# ! ! disk ! x ! ! !
+# ! dns ! dns_traffic ! ! ! !
+# ! apple_se.. ! fanspeed ! x ! ! !
+# ! mbmon ! fanspeed ! x ! ! !
+# ! apcups ! frequency ! x ! ! !
+# ! ntpd ! frequency_offset ! x ! ! !
+# ! ! hddtemp ! x ! ! !
+# ! interface ! if_errors ! ! x ! !
+# ! interface ! if_packets ! ! x ! !
+# ! ! lm_sensors ! ! ! !
+# ! ! load ! ! ! !
+# ! apcups ! load_percent ! ! ! !
+# ! ! memory ! ! ! !
+# ! ! multimeter ! ! ! !
+# ! mysql ! mysql_commands ! x ! ! !
+# ! mysql ! mysql_handler ! x ! ! !
+# ! mysql ! mysql_qcache ! ! ! !
+# ! mysql ! mysql_threads ! ! ! !
+# ! ! nfs2_procedures ! x ! ! x !
+# ! ! nfs3_procedures ! x ! ! x !
+# ! dns ! opcode ! x ! ! !
+# ! ! partition ! x ! ! !
+# ! ! ping ! x ! ! !
+# ! ! processes ! ! ! !
+# ! processes ! ps_count ! x ! ! !
+# ! processes ! ps_cputime ! x ! ! !
+# ! processes ! ps_pagefaults ! x ! ! !
+# ! processes ! ps_rss ! x ! ! !
+# ! dns ! qtype ! x ! ! !
+# ! dns ! rcode ! x ! ! !
+# ! (*) ! sensors ! x ! ! !
+# ! ! serial ! x ! ! !
+# ! ! swap ! ! ! !
+# ! ! tape ! x ! ! !
+# ! apple_se.. ! temperature ! x ! ! !
+# ! mbmon ! temperature ! x ! ! !
+# ! ntpd ! time_dispersion ! x ! ! !
+# ! ntpd ! time_offset ! x ! ! !
+# ! apcups ! timeleft ! ! ! !
+# ! ! traffic ! x ! ! ! ->rx,tx
+# ! vserver ! traffic ! x ! x ! ! ->rx.tx
+# ! ! users ! ! ! !
+# ! apucups ! voltage ! x ! ! !
+# ! battery ! voltage ! ! x ! !
+# ! mbmon ! voltage ! x ! ! !
+# ! vserver ! vs_memory ! ! x ! !
+# ! vserver ! vs_processes ! ! x ! !
+# ! vserver ! vs_threads ! ! x ! !
+# ! ! wireless ! x ! ! !
+# +------------+----------------------+----+----+----+
+
+our %Subdirs =
+(
+ apache => 0,
+ apcups => 0,
+ apple_sensors => 0,
+ battery => 1,
+ dns => 0,
+ interface => 1,
+ mbmon => 0,
+ mysql => 0,
+ ntpd => 0,
+ processes => 0,
+ sensors => 1,
+ vserver => 1
+);
+
+our %TypeTranslate =
+(
+ cpu => sub { $_ = shift; $_->{'plugin_instance'} = $_->{'type_instance'}; $_->{'type_instance'} = undef; $_; },
+ if_errors => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
+ if_packets => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
+ nfs2_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v2' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
+ nfs3_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v3' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
+ partition => sub { $_ = shift; $_->{'plugin'} = 'disk'; $_; },
+ processes => sub { $_ = shift; $_->{'type'} = 'ps_state'; $_; },
+ traffic => sub { $_ = shift; $_->{'plugin'} =~ s/^traffic$/interface/; @$_{qw(plugin_instance type)} = (undef, 'if_octets'); $_; }
+);
+
+our %TypeSplit =
+(
+ cpu => { from => [qw(user nice syst idle wait)], to => 'value', type_instance => [qw(user nice system idle wait)] },
+ nfs3_procedures => { from => [qw(null getattr lookup access readlink
+ read write create mkdir symlink mknod remove rmdir rename link
+ readdir readdirplus fsstat fsinfo pathconf commit)], to => 'value' },
+ nfs2_procedures => { from => [qw(create fsstat getattr link lookup
+ mkdir null read readdir readlink remove rename rmdir root
+ setattr symlink wrcache write)], to => 'value' },
+ processes => { from => [qw(running sleeping zombies stopped paging blocked)], to => 'value' },
+ swap => { from => [qw(cached free used resv)], to => 'value', type_instance => [qw(cached free used reserved)] }
+);
+
+our %TypeRename =
+(
+ traffic => { from => [qw(incoming outgoing)], to => [qw(rx tx)] }
+);
+
+GetOptions ("indir|i=s" => \$InDir,
+ "outdir|o=s" => \$OutDir,
+ "hostname=s" => \$Hostname) or exit (1);
+
+die "No such directory: $InDir" if (!-d $InDir);
+if (!-e $OutDir)
+{
+ mkdir ($OutDir) or die ("mkdir ($OutDir): $!");
+}
+die "Not a directory: $OutDir" if (!-d $OutDir);
+
+our @Files = ();
+our %OutDirs = ();
+
+@Files = find_files ();
+
+for (@Files)
+{
+ my $orig_filename = $_;
+ my $orig = parse_file ($orig_filename);
+ my $dest = translate_file ($orig);
+ my $dest_filename = get_filename ($dest);
+
+ my $dest_directory = dirname ($dest_filename);
+ if (!exists ($OutDirs{$dest_directory}))
+ {
+ print "[ -d '$OutDir/$dest_directory' ] || mkdir -p '$OutDir/$dest_directory'\n";
+ $OutDirs{$dest_directory} = 1;
+ }
+
+ if (exists ($TypeSplit{$orig->{'type'}}))
+ {
+ my $src_dses = $TypeSplit{$orig->{'type'}}->{'from'};
+ my $dst_ds = $TypeSplit{$orig->{'type'}}->{'to'};
+ my $type_instances = exists ($TypeSplit{$orig->{'type'}}->{'type_instance'})
+ ? $TypeSplit{$orig->{'type'}}->{'type_instance'}
+ : $TypeSplit{$orig->{'type'}}->{'from'};
+
+ for (my $i = 0; $i < @$src_dses; $i++)
+ {
+ my $src_ds = $src_dses->[$i];
+ $dest->{'type_instance'} = $type_instances->[$i];
+ $dest_filename = get_filename ($dest);
+ print "./extractDS.px -i '$InDir/$orig_filename' -s '$src_ds' -o '$OutDir/$dest_filename' -d '$dst_ds'\n";
+ }
+ }
+ elsif (exists ($TypeRename{$orig->{'type'}}))
+ {
+ my $src_dses = $TypeRename{$orig->{'type'}}->{'from'};
+ my $dst_dses = $TypeRename{$orig->{'type'}}->{'to'};
+ my @sed_prog = ();
+
+ for (my $i = 0; $i < @$src_dses; $i++)
+ {
+ push (@sed_prog, 's/^' . $src_dses->[$i] . '$/' . $dst_dses->[$i] . '/g;');
+ }
+
+ print "rrdtool dump '$InDir/$orig_filename' | sed -e '" . join (' ', @sed_prog) . "' | rrdtool restore - '$OutDir/$dest_filename'\n";
+ }
+ else
+ {
+ print "cp '$InDir/$orig_filename' '$OutDir/$dest_filename'\n";
+ }
+}
+
+exit (0);
+
+sub translate_file
+{
+ my $orig = shift;
+ my $dest = {};
+ %$dest = %$orig;
+
+ if (defined ($TypeTranslate{$orig->{'type'}}))
+ {
+ $TypeTranslate{$orig->{'type'}}->($dest);
+ }
+
+ return ($dest);
+} # translate_file
+
+sub get_filename
+{
+ my $args = shift;
+ my $filename = $args->{'host'}
+ . '/' . $args->{'plugin'} . (defined ($args->{'plugin_instance'}) ? '-'.$args->{'plugin_instance'} : '')
+ . '/' . $args->{'type'} . (defined ($args->{'type_instance'}) ? '-'.$args->{'type_instance'} : '') . '.rrd';
+
+ return ($filename);
+}
+
+sub parse_file
+{
+ my $fullname = shift;
+ my @parts = split ('/', $fullname);
+
+ my $filename;
+
+ my $host;
+ my $plugin;
+ my $plugin_instance;
+ my $type;
+ my $type_instance;
+
+ $filename = pop (@parts);
+
+ if ($filename =~ m/^([^-]+)(?:-(.*))?\.rrd$/)
+ {
+ $type = $1;
+ $type_instance = $2;
+ }
+ else
+ {
+ return;
+ }
+
+ if (@parts)
+ {
+ my $dirname = pop (@parts);
+ my $regex_str = join ('|', keys (%Subdirs));
+ if ($dirname =~ m/^($regex_str)(?:-(.*))?$/)
+ {
+ $plugin = $1;
+ $plugin_instance = $2;
+ }
+ else
+ {
+ push (@parts, $dirname);
+ }
+ }
+ if (!$plugin)
+ {
+ $plugin = $type;
+ }
+
+ if (@parts)
+ {
+ $host = pop (@parts);
+ }
+ else
+ {
+ $host = $Hostname;
+ }
+
+ return
+ ({
+ host => $host,
+ plugin => $plugin,
+ plugin_instance => $plugin_instance,
+ type => $type,
+ type_instance => $type_instance
+ });
+} # parse_file
+
+sub find_files
+{
+ my $reldir = @_ ? shift : '';
+ my $absdir = $InDir . ($reldir ? "/$reldir" : '');
+
+ my $dh;
+
+ my @files = ();
+ my @dirs = ();
+
+ opendir ($dh, $absdir) or die ("opendir ($absdir): $!");
+ while (my $file = readdir ($dh))
+ {
+ next if ($file =~ m/^\./);
+ next if (-l "$absdir/$file");
+ if (-d "$absdir/$file")
+ {
+ push (@dirs, ($reldir ? "$reldir/" : '') . $file);
+ }
+ elsif ($file =~ m/\.rrd$/)
+ {
+ push (@files, ($reldir ? "$reldir/" : '') . $file);
+ }
+ }
+ closedir ($dh);
+
+ for (my $i = 0; $i < @dirs; $i++)
+ {
+ push (@files, find_files ($dirs[$i]));
+ }
+
+ return (@files);
+} # find_files
+
+sub special_cpu
+{
+ my %file_orig = @_;
+ my %file_dest = %file_orig;
+
+ $file_dest{'plugin_instance'} = $file_dest{'type_instance'}
+
+}