Merge branch 'collectd-4.2' of git://git.verplant.org/collectd into collectd-4.2
[collectd.git] / contrib / migrate-3-4.px
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 use Getopt::Long ('GetOptions');
7 use Data::Dumper ();
8 use File::Basename ('dirname');
9
10 our $InDir = '/var/lib/collectd';
11 our $OutDir = '/tmp/collectd-4';
12 our $Hostname = 'localhost';
13
14 # Types:
15 # +------------+----------------------+----+----+----+
16 # ! Subdir     ! Type                 ! ti ! pi ! ex !
17 # +------------+----------------------+----+----+----+
18 # ! apache     ! apache_bytes         !    !    !    !
19 # ! apache     ! apache_requests      !    !    !    !
20 # ! apache     ! apache_scoreboard    ! x  !    !    !
21 # ! battery    ! charge               !    ! x  !    !
22 # ! apcups     ! charge_percent       !    !    !    !
23 # !            ! cpu                  ! x  !    ! x  !
24 # !            ! cpufreq              ! x  !    !    !
25 # ! battery    ! current              !    ! x  !    !
26 # ! ntpd       ! delay                ! x  !    !    !
27 # !            ! df                   ! x  !    !    !
28 # !            ! disk                 ! x  !    !    !
29 # ! dns        ! dns_traffic          !    !    !    !
30 # ! apple_se.. ! fanspeed             ! x  !    !    !
31 # ! mbmon      ! fanspeed             ! x  !    !    !
32 # ! apcups     ! frequency            ! x  !    !    !
33 # ! ntpd       ! frequency_offset     ! x  !    !    !
34 # !            ! hddtemp              ! x  !    !    !
35 # ! interface  ! if_errors            !    ! x  !    !
36 # ! interface  ! if_packets           !    ! x  !    !
37 # !            ! lm_sensors           !    !    !    !
38 # !            ! load                 !    !    !    !
39 # ! apcups     ! load_percent         !    !    !    !
40 # !            ! memory               !    !    !    !
41 # !            ! multimeter           !    !    !    !
42 # ! mysql      ! mysql_commands       ! x  !    !    !
43 # ! mysql      ! mysql_handler        ! x  !    !    !
44 # ! mysql      ! mysql_qcache         !    !    !    !
45 # ! mysql      ! mysql_threads        !    !    !    !
46 # !            ! nfs2_procedures      ! x  !    ! x  !
47 # !            ! nfs3_procedures      ! x  !    ! x  !
48 # ! dns        ! opcode               ! x  !    !    !
49 # !            ! partition            ! x  !    !    !
50 # !            ! ping                 ! x  !    !    !
51 # !            ! processes            !    !    !    !
52 # ! processes  ! ps_count             ! x  !    !    !
53 # ! processes  ! ps_cputime           ! x  !    !    !
54 # ! processes  ! ps_pagefaults        ! x  !    !    !
55 # ! processes  ! ps_rss               ! x  !    !    !
56 # ! dns        ! qtype                ! x  !    !    !
57 # ! dns        ! rcode                ! x  !    !    !
58 # ! (*)        ! sensors              ! x  !    !    !
59 # !            ! serial               ! x  !    !    !
60 # !            ! swap                 !    !    !    !
61 # !            ! tape                 ! x  !    !    !
62 # ! apple_se.. ! temperature          ! x  !    !    !
63 # ! mbmon      ! temperature          ! x  !    !    !
64 # ! ntpd       ! time_dispersion      ! x  !    !    !
65 # ! ntpd       ! time_offset          ! x  !    !    !
66 # ! apcups     ! timeleft             !    !    !    !
67 # !            ! traffic              ! x  !    !    ! ->rx,tx
68 # ! vserver    ! traffic              ! x  ! x  !    ! ->rx.tx
69 # !            ! users                !    !    !    !
70 # ! apucups    ! voltage              ! x  !    !    !
71 # ! battery    ! voltage              !    ! x  !    !
72 # ! mbmon      ! voltage              ! x  !    !    !
73 # ! vserver    ! vs_memory            !    ! x  !    !
74 # ! vserver    ! vs_processes         !    ! x  !    !
75 # ! vserver    ! vs_threads           !    ! x  !    !
76 # !            ! wireless             ! x  !    !    !
77 # +------------+----------------------+----+----+----+
78
79 our %Subdirs =
80 (
81         apache => 0,
82         apcups => 0,
83         apple_sensors => 0,
84         battery => 1,
85         dns => 0,
86         interface => 1,
87         mbmon => 0,
88         mysql => 0,
89         ntpd => 0,
90         processes => 0,
91         sensors => 1,
92         vserver => 1
93 );
94
95 our %TypeTranslate =
96 (
97         cpu => sub { $_ = shift; $_->{'plugin_instance'} = $_->{'type_instance'}; $_->{'type_instance'} = undef; $_; },
98         hddtemp => sub { $_ = shift; $_->{'plugin'} = 'hddtemp'; $_->{'type'} = 'temperature'; $_->{'type_instance'} = $_->{'type_instance'}; $_; },
99         if_errors => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
100         if_packets => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
101         nfs2_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v2' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
102         nfs3_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v3' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
103         partition => sub { $_ = shift; $_->{'plugin'} = 'disk'; $_; },
104         processes => sub { $_ = shift; $_->{'type'} = 'ps_state'; $_; },
105         traffic => sub { $_ = shift; $_->{'plugin'} =~ s/^traffic$/interface/; @$_{qw(plugin_instance type)} = (undef, 'if_octets'); $_; }
106 );
107
108 our %TypeSplit =
109 (
110         cpu => { from => [qw(user nice syst idle wait)], to => 'value', type_instance => [qw(user nice system idle wait)] },
111         memory => { from => [qw(used free buffers cached)], to => 'value', type_instance => [qw(used free buffered cached)] },
112         nfs3_procedures => { from => [qw(null getattr lookup access readlink
113                 read write create mkdir symlink mknod remove rmdir rename link
114                 readdir readdirplus fsstat fsinfo pathconf commit)], to => 'value' },
115         nfs2_procedures => { from => [qw(create fsstat getattr link lookup
116                 mkdir null read readdir readlink remove rename rmdir root
117                 setattr symlink wrcache write)], to => 'value' },
118         processes => { from => [qw(running sleeping zombies stopped paging blocked)], to => 'value' },
119         swap => { from => [qw(cached free used resv)], to => 'value', type_instance => [qw(cached free used reserved)] }
120 );
121
122 our %TypeRename =
123 (
124         traffic => { from => [qw(incoming outgoing)], to => [qw(rx tx)] },
125         vs_processes => { from => [qw(total)], to => [qw(value)] },
126 );
127
128 GetOptions ("indir|i=s" => \$InDir,
129         "outdir|o=s" => \$OutDir,
130         "hostname=s" => \$Hostname) or exit_usage ();
131
132 die "No such directory: $InDir" if (!-d $InDir);
133
134 our @Files = ();
135 our %OutDirs = ();
136
137 @Files = find_files ();
138
139 for (@Files)
140 {
141         my $orig_filename = $_;
142         my $orig = parse_file ($orig_filename);
143         my $dest = translate_file ($orig);
144         my $dest_filename = get_filename ($dest);
145
146         my $dest_directory = dirname ($dest_filename);
147         if (!exists ($OutDirs{$dest_directory}))
148         {
149                 print "[ -d '$OutDir/$dest_directory' ] || mkdir -p '$OutDir/$dest_directory'\n";
150                 $OutDirs{$dest_directory} = 1;
151         }
152
153         if (($orig->{'type'} eq 'disk') || ($orig->{'type'} eq 'partition'))
154         {
155                 special_disk ($orig_filename, $orig, $dest_filename, $dest);
156         }
157         elsif (exists ($TypeSplit{$orig->{'type'}}))
158         {
159                 my $src_dses = $TypeSplit{$orig->{'type'}}->{'from'};
160                 my $dst_ds = $TypeSplit{$orig->{'type'}}->{'to'};
161                 my $type_instances = exists ($TypeSplit{$orig->{'type'}}->{'type_instance'})
162                         ? $TypeSplit{$orig->{'type'}}->{'type_instance'}
163                         : $TypeSplit{$orig->{'type'}}->{'from'};
164
165                 for (my $i = 0; $i < @$src_dses; $i++)
166                 {
167                         my $src_ds = $src_dses->[$i];
168                         $dest->{'type_instance'} = $type_instances->[$i];
169                         $dest_filename = get_filename ($dest);
170                         print "./extractDS.px -i '$InDir/$orig_filename' -s '$src_ds' -o '$OutDir/$dest_filename' -d '$dst_ds'\n";
171                 }
172         }
173         elsif (exists ($TypeRename{$orig->{'type'}}))
174         {
175                 my $src_dses = $TypeRename{$orig->{'type'}}->{'from'};
176                 my $dst_dses = $TypeRename{$orig->{'type'}}->{'to'};
177                 my @sed_prog = ();
178
179                 for (my $i = 0; $i < @$src_dses; $i++)
180                 {
181                         push (@sed_prog, 's/^' . $src_dses->[$i] . '$/' . $dst_dses->[$i] . '/g;');
182                 }
183
184                 print "rrdtool dump '$InDir/$orig_filename' | sed -e '" . join (' ', @sed_prog) . "' | rrdtool restore - '$OutDir/$dest_filename'\n";
185         }
186         else
187         {
188                 print "cp '$InDir/$orig_filename' '$OutDir/$dest_filename'\n";
189         }
190 }
191
192 exit (0);
193
194 sub translate_file
195 {
196         my $orig = shift;
197         my $dest = {};
198         %$dest = %$orig;
199
200         if (defined ($TypeTranslate{$orig->{'type'}}))
201         {
202                 $TypeTranslate{$orig->{'type'}}->($dest);
203         }
204
205         return ($dest);
206 } # translate_file
207
208 sub get_filename
209 {
210         my $args = shift;
211         my $filename = $args->{'host'}
212         . '/' . $args->{'plugin'} . (defined ($args->{'plugin_instance'}) ? '-'.$args->{'plugin_instance'} : '')
213         . '/' . $args->{'type'} . (defined ($args->{'type_instance'}) ? '-'.$args->{'type_instance'} : '') . '.rrd';
214
215         return ($filename);
216 }
217
218 sub parse_file
219 {
220         my $fullname = shift;
221         my @parts = split ('/', $fullname);
222
223         my $filename;
224
225         my $host;
226         my $plugin;
227         my $plugin_instance;
228         my $type;
229         my $type_instance;
230
231         $filename = pop (@parts);
232
233         if ($filename =~ m/^([^-]+)(?:-(.*))?\.rrd$/)
234         {
235                 $type = $1;
236                 $type_instance = $2;
237         }
238         else
239         {
240                 return;
241         }
242
243         if (@parts)
244         {
245                 my $dirname = pop (@parts);
246                 my $regex_str = join ('|', keys (%Subdirs));
247                 if ($dirname =~ m/^($regex_str)(?:-(.*))?$/)
248                 {
249                         $plugin = $1;
250                         $plugin_instance = $2;
251                 }
252                 else
253                 {
254                         push (@parts, $dirname);
255                 }
256         }
257         if (!$plugin)
258         {
259                 $plugin = $type;
260         }
261
262         if (@parts)
263         {
264                 $host = pop (@parts);
265         }
266         else
267         {
268                 $host = $Hostname;
269         }
270
271         return
272         ({
273                 host => $host,
274                 plugin => $plugin,
275                 plugin_instance => $plugin_instance,
276                 type => $type,
277                 type_instance => $type_instance
278         });
279 } # parse_file
280
281 sub find_files
282 {
283         my $reldir = @_ ? shift : '';
284         my $absdir = $InDir . ($reldir ? "/$reldir" : '');
285
286         my $dh;
287
288         my @files = ();
289         my @dirs = ();
290
291         opendir ($dh, $absdir) or die ("opendir ($absdir): $!");
292         while (my $file = readdir ($dh))
293         {
294                 next if ($file =~ m/^\./);
295                 next if (-l "$absdir/$file");
296                 if (-d "$absdir/$file")
297                 {
298                         push (@dirs, ($reldir ? "$reldir/" : '') . $file);
299                 }
300                 elsif ($file =~ m/\.rrd$/)
301                 {
302                         push (@files, ($reldir ? "$reldir/" : '') . $file);
303                 }
304         }
305         closedir ($dh);
306
307         for (my $i = 0; $i < @dirs; $i++)
308         {
309                 push (@files, find_files ($dirs[$i]));
310         }
311
312         return (@files);
313 } # find_files
314
315 {my $cache;
316 sub _special_disk_instance
317 {
318         my $orig_instance = shift;
319
320         if (!defined ($cache))
321         {
322                 my $fh;
323                 open ($fh, "< /proc/diskstats") or die ("open (/proc/diststats): $!");
324
325                 $cache = {};
326                 while (my $line = <$fh>)
327                 {
328                         chomp ($line);
329                         my @fields = split (' ', $line);
330                         $cache->{$fields[0] . '-' . $fields[1]} = $fields[2];
331                 }
332                 close ($fh);
333         }
334
335         return (defined ($cache->{$orig_instance})
336                 ? $cache->{$orig_instance}
337                 : $orig_instance);
338 }}
339
340 sub special_disk
341 {
342         my $orig_filename = shift;
343         my $orig = shift;
344         my $dest_filename = shift;
345         my $dest = shift;
346         my $dest_directory;
347
348         $dest->{'type_instance'} = undef;
349         $dest->{'plugin_instance'} = _special_disk_instance ($orig->{'type_instance'});
350         if ($dest->{'plugin_instance'} eq $orig->{'type_instance'})
351         {
352                 print qq(echo "You may need to rename these files" >&2\n);
353         }
354
355         $dest->{'type'} = 'disk_merged';
356         $dest_filename = get_filename ($dest);
357
358         $dest_directory = dirname ($dest_filename);
359         if (!exists ($OutDirs{$dest_directory}))
360         {
361                 print "[ -d '$OutDir/$dest_directory' ] || mkdir -p '$OutDir/$dest_directory'\n";
362                 $OutDirs{$dest_directory} = 1;
363         }
364
365         print "./extractDS.px -i '$InDir/$orig_filename' -s 'rmerged' -s 'wmerged' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
366
367         $dest->{'type'} = 'disk_octets';
368         $dest_filename = get_filename ($dest);
369         print "./extractDS.px -i '$InDir/$orig_filename' -s 'rbytes' -s 'wbytes' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
370
371         $dest->{'type'} = 'disk_ops';
372         $dest_filename = get_filename ($dest);
373         print "./extractDS.px -i '$InDir/$orig_filename' -s 'rcount' -s 'wcount' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
374
375         $dest->{'type'} = 'disk_time';
376         $dest_filename = get_filename ($dest);
377         print "./extractDS.px -i '$InDir/$orig_filename' -s 'rtime' -s 'wtime' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
378 }
379
380 sub exit_usage
381 {
382         print <<EOF;
383 Usage: $0 [-i indir] [-o outdir] [--hostname myhostname]
384 EOF
385         exit (1);
386 }