contrib/collection.cgi: Display MySQL traffic as bits/s, not bytes/s.
[collectd.git] / contrib / collection.cgi
index 159d9e6..a40db87 100755 (executable)
@@ -459,86 +459,139 @@ sub action_show_host
   my @hosts = _get_param_host ();
   @hosts = sort (@hosts);
 
-  my $all_plugins = {};
-  my $plugins_per_host = {};
+  my $timespan = _get_param_timespan ();
+  my $all_plugins = _find_files_for_hosts (@hosts);
+
+  my $url_prefix = script_name () . '?action=show_plugin'
+  . join ('', map { ';host=' . uri_escape ($_) } (@hosts))
+  . ';timespan=' . uri_escape ($timespan);
+
+  print qq(    <div><a href="${\script_name ()}?action=overview">Back to list of hosts</a></div>\n);
+
+  print "    <p>Available plugins:</p>\n"
+  . "    <ul>\n";
+  for (sort (keys %$all_plugins))
+  {
+    my $plugin = $_;
+    my $plugin_html = encode_entities ($plugin);
+    my $url_plugin = $url_prefix . ';plugin=' . uri_escape ($plugin);
+    print qq(      <li><a href="$url_plugin">$plugin_html</a></li>\n);
+  }
+  print "   </ul>\n";
+} # action_show_host
 
+sub action_show_plugin
+{
+  my @hosts = _get_param_host ();
+  my $plugin = shift;
+  my $plugin_instance = shift;
   my $timespan = _get_param_timespan ();
 
+  my $hosts_url = join (';', map { 'host=' . uri_escape ($_) } (@hosts));
+  my $url_prefix = script_name () . "?$hosts_url";
+
+  my $all_plugins = {};
+  my $plugins_per_host = {};
+  my $selected_plugins = {};
+
   for (my $i = 0; $i < @hosts; $i++)
   {
     $plugins_per_host->{$hosts[$i]} = _find_files_for_host ($hosts[$i]);
     _files_union ($all_plugins, $plugins_per_host->{$hosts[$i]});
   }
 
-  my $param_host = join (";", map { "host=" . encode_entities ($_) } (@hosts));
-
-  print '<!-- ', Data::Dumper->Dump ([$all_plugins], ['all_plugins']), " -->\n";
-
-  my @plugins = sort (keys %$all_plugins);
+  for (param ('plugin'))
+  {
+    if (defined ($all_plugins->{$_}))
+    {
+      $selected_plugins->{$_} = 1;
+    }
+  }
 
-  print qq(    <div><a href="${\script_name ()}?action=overview">Back to list of hosts</a></div>\n);
+  print qq(    <div><a href="${\script_name ()}?action=show_host;$hosts_url">Back to list of plugins</a></div>\n);
 
+  # Print table header
   print <<HTML;
     <table class="graphs">
       <tr>
-       <th>Plugin</th>
+        <th>Plugins</th>
 HTML
-  for (my $i = 0; $i < @hosts; $i++)
+  for (@hosts)
   {
-    print "\t<th>", encode_entities ($hosts[$i]), "</th>\n";
+    print "\t<th>", encode_entities ($_), "</th>\n";
   }
   print "      </tr>\n";
-  for (my $i = 0; $i < @plugins; $i++)
-  {
-    my $plugin = $plugins[$i];
-    my $plugin_esc = encode_entities ($plugins[$i]);
 
-    my @pinsts = sort (keys %{$all_plugins->{$plugin}});
+  for (sort (keys %$selected_plugins))
+  {
+    my $plugin = $_;
+    my $plugin_html = encode_entities ($plugin);
+    my $plugin_url = "$url_prefix;plugin=" . uri_escape ($plugin);
+    my $all_pinst = $all_plugins->{$plugin};
 
-    for (my $j = 0; $j < @pinsts; $j++)
+    for (sort (keys %$all_pinst))
     {
-      my $pinst = $pinsts[$j];
-      my $pinst_esc = encode_entities ($pinst);
-      my $title = $plugin . ($pinst ne '-' ? " ($pinst)" : '');
-      my $title_esc = encode_entities ($title);
+      my $pinst = $_;
+      my $pinst_html = '';
+      my $pinst_url = $plugin_url;
 
-      my $param_plugin = "plugin=$plugin_esc";
       if ($pinst ne '-')
       {
-       $param_plugin .= ";plugin_instance=$pinst_esc";
+       $pinst_html = encode_entities ($pinst);
+       $pinst_url .= ';plugin_instance=' . uri_escape ($pinst);
       }
 
       my $files_printed = 0;
-      my $files_num = _files_plugin_inst_count ($all_plugins->{$plugin}{$pinst});
-      next if (!$files_num);
-
+      my $files_num = _files_plugin_inst_count ($all_pinst->{$pinst});
+      if ($files_num < 1)
+      {
+       next;
+      }
       my $rowspan = ($files_num == 1) ? '' : qq( rowspan="$files_num");
 
       for (sort (keys %{$all_plugins->{$plugin}{$pinst}}))
       {
        my $type = $_;
-       my $type_esc = encode_entities ($type);
+       my $type_html = encode_entities ($type);
+       my $type_url = "$pinst_url;type=" . uri_escape ($type);
+
+       if ($files_printed == 0)
+       {
+         my $title = $plugin_html;
+         if ($pinst ne '-')
+         {
+           $title .= " ($pinst_html)";
+         }
+         print "      <tr>\n";
+         print "\t<td$rowspan>$title</td>\n";
+       }
 
        if (exists ($MetaGraphDefs->{$type}))
        {
-         my $param_type = "type=$type_esc";
+         my $graph_url = script_name () . '?action=show_graph'
+         . ';plugin=' . uri_escape ($plugin)
+         . ';type=' . uri_escape ($type)
+         . ';timespan=' . uri_escape ($timespan);
+         if ($pinst ne '-')
+         {
+           $graph_url .= ';plugin_instance=' . uri_escape ($pinst);
+         }
 
-         print "      <tr>\n";
-         if ($files_printed == 0)
+         if ($files_printed != 0)
          {
-           print "\t<td$rowspan>$title_esc</td>\n";
+           print "      <tr>\n";
          }
 
-         for (my $k = 0; $k < @hosts; $k++)
+         for (@hosts)
          {
-           my $host = $hosts[$k];
-           my $host_esc = encode_entities ($host);
+           my $host = $_;
+           my $host_graph_url = $graph_url . ';host=' . uri_escape ($host);
 
            print "\t<td>";
            if (exists $plugins_per_host->{$host}{$plugin}{$pinst}{$type})
            {
-             #print qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />);
-             print encode_entities (qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />));
+             print qq(<img src="$host_graph_url" />);
+             #print encode_entities (qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />));
            }
            print "</td>\n";
          } # for (my $k = 0; $k < @hosts; $k++)
@@ -547,35 +600,40 @@ HTML
 
          $files_printed++;
          next; # pinst
-       }
+       } # if (exists ($MetaGraphDefs->{$type}))
 
        for (sort (keys %{$all_plugins->{$plugin}{$pinst}{$type}}))
        {
          my $tinst = $_;
          my $tinst_esc = encode_entities ($tinst);
-
-         my $param_type = "type=$type_esc";
+         my $graph_url = script_name () . '?action=show_graph'
+         . ';plugin=' . uri_escape ($plugin)
+         . ';type=' . uri_escape ($type)
+         . ';timespan=' . uri_escape ($timespan);
+         if ($pinst ne '-')
+         {
+           $graph_url .= ';plugin_instance=' . uri_escape ($pinst);
+         }
          if ($tinst ne '-')
          {
-           $param_type .= ";type_instance=$tinst_esc";
+           $graph_url .= ';type_instance=' . uri_escape ($tinst);
          }
 
-         print "      <tr>\n";
-         if ($files_printed == 0)
+         if ($files_printed != 0)
          {
-           print "\t<td$rowspan>$title_esc</td>\n";
+           print "      <tr>\n";
          }
 
          for (my $k = 0; $k < @hosts; $k++)
          {
            my $host = $hosts[$k];
-           my $host_esc = encode_entities ($host);
+           my $host_graph_url = $graph_url . ';host=' . uri_escape ($host);
 
            print "\t<td>";
            if ($plugins_per_host->{$host}{$plugin}{$pinst}{$type}{$tinst})
            {
-             #print qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />);
-             print encode_entities (qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />));
+             print qq(<img src="$host_graph_url" />);
+             #print encode_entities (qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />));
            }
            print "</td>\n";
          } # for (my $k = 0; $k < @hosts; $k++)
@@ -585,147 +643,8 @@ HTML
          $files_printed++;
        } # for ($tinst)
       } # for ($type)
-    } # for (my $j = 0; $j < @pinsts; $j++)
-  } # for (my $i = 0; $i < @plugins; $i++)
-  print "   </table>\n";
-} # action_show_host
-
-sub action_show_plugin
-{
-  my @hosts = _get_param_host ();
-  my $plugin = shift;
-  my $plugin_instance = shift;
-  my $timespan = _get_param_timespan ();
-
-  my $hosts_url = join (';', map { 'host=' . uri_escape ($_) } (@hosts));
-  my $plugin_esc = encode_entities ($plugin);
-  my $plugin_url = uri_escape ($plugin);
-  my $plugin_instance_url = defined ($plugin_instance) ? uri_escape ($plugin_instance) : undef;
-
-  my $all_plugins = {};
-  my $plugins_per_host = {};
-
-  for (my $i = 0; $i < @hosts; $i++)
-  {
-    $plugins_per_host->{$hosts[$i]} = _find_files_for_host ($hosts[$i]);
-    _files_union ($all_plugins, $plugins_per_host->{$hosts[$i]});
-  }
-
-  my $url_prefix = script_name () . "?$hosts_url;plugin=$plugin_url";
-  $url_prefix .= ";plugin_instance=$plugin_instance_url" if (defined ($plugin_instance));
-
-  print qq(    <div><a href="${\script_name ()}?action=show_host;$hosts_url">Back to list of plugins</a></div>\n);
-
-  if (!defined ($all_plugins->{$plugin}))
-  {
-    print qq(    <div class="error">Plugin &quot;${\encode_entities ($plugin)}&quot; not found for host &quot;${\encode_entities (@hosts)}&quot;.</div>\n);
-    return;
-  }
-
-  my @pinsts = sort (keys %{$all_plugins->{$plugin}});
-
-  print <<HTML;
-    <table class="graphs">
-      <tr>
-       <th>Plugin</th>
-HTML
-  for (my $i = 0; $i < @hosts; $i++)
-  {
-    print "\t<th>", encode_entities ($hosts[$i]), "</th>\n";
-  }
-  print "      </tr>\n";
-
-  for (my $j = 0; $j < @pinsts; $j++)
-  {
-    my $pinst = $pinsts[$j];
-    my $pinst_esc = encode_entities ($pinst);
-    my $title = $plugin . ($pinst ne '-' ? " ($pinst)" : '');
-    my $title_esc = encode_entities ($title);
-
-    my $param_plugin = "plugin=$plugin_esc";
-    if ($pinst ne '-')
-    {
-      $param_plugin .= ";plugin_instance=$pinst_esc";
-    }
-
-    my $files_printed = 0;
-    my $files_num = _files_plugin_inst_count ($all_plugins->{$plugin}{$pinst});
-    next if (!$files_num);
-
-    my $rowspan = ($files_num == 1) ? '' : qq( rowspan="$files_num");
-
-    for (sort (keys %{$all_plugins->{$plugin}{$pinst}}))
-    {
-      my $type = $_;
-      my $type_esc = encode_entities ($type);
-
-      if (exists ($MetaGraphDefs->{$type}))
-      {
-       my $param_type = "type=$type_esc";
-
-       print "      <tr>\n";
-       if ($files_printed == 0)
-       {
-         print "\t<td$rowspan>$title_esc</td>\n";
-       }
-
-       for (my $k = 0; $k < @hosts; $k++)
-       {
-         my $host = $hosts[$k];
-         my $host_esc = encode_entities ($host);
-
-         print "\t<td>";
-         if (exists $plugins_per_host->{$host}{$plugin}{$pinst}{$type})
-         {
-           print qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />);
-           #print encode_entities (qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />));
-         }
-         print "</td>\n";
-       } # for (my $k = 0; $k < @hosts; $k++)
-
-       print "      </tr>\n";
-
-       $files_printed++;
-       next; # pinst
-      }
-
-      for (sort (keys %{$all_plugins->{$plugin}{$pinst}{$type}}))
-      {
-       my $tinst = $_;
-       my $tinst_esc = encode_entities ($tinst);
-
-       my $param_type = "type=$type_esc";
-       if ($tinst ne '-')
-       {
-         $param_type .= ";type_instance=$tinst_esc";
-       }
-
-       print "      <tr>\n";
-       if ($files_printed == 0)
-       {
-         print "\t<td$rowspan>$title_esc</td>\n";
-       }
-
-       for (my $k = 0; $k < @hosts; $k++)
-       {
-         my $host = $hosts[$k];
-         my $host_esc = encode_entities ($host);
-
-         print "\t<td>";
-         if ($plugins_per_host->{$host}{$plugin}{$pinst}{$type}{$tinst})
-         {
-           print qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />);
-           #print encode_entities (qq(<img src="${\script_name ()}?action=show_graph;host=$host_esc;$param_plugin;$param_type;timespan=$timespan" />));
-         }
-         print "</td>\n";
-       } # for (my $k = 0; $k < @hosts; $k++)
-
-       print "      </tr>\n";
-
-       $files_printed++;
-      } # for ($tinst)
-    } # for ($type)
-  } # for (my $j = 0; $j < @pinsts; $j++)
+    } # for ($pinst)
+  } # for ($plugin)
   print "   </table>\n";
 } # action_show_plugin
 
@@ -830,7 +749,7 @@ HTML
 
   if (keys %selected_hosts)
   {
-    my $all_plugins = _find_files_for_hosts (@hosts);
+    my $all_plugins = _find_files_for_hosts (keys %selected_hosts);
     my %selected_plugins = map { $_ => 1 } (param ('plugin'));
 
     print qq(\t<select name="plugin" multiple="multiple" size="10">\n);
@@ -1669,7 +1588,7 @@ sub load_graph_definitions
     "DEF:val_max={file}:value:MAX",
     "AREA:val_max#$HalfBlue",
     "AREA:val_min#$Canvas",
-    "LINE1:val_avg#$FullBlue:{inst}",
+    "LINE1:val_avg#$FullBlue:Issues/s",
     'GPRINT:val_min:MIN:%5.2lf Min,',
     'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
     'GPRINT:val_max:MAX:%5.2lf Max,',
@@ -1681,20 +1600,19 @@ sub load_graph_definitions
     "DEF:val_max={file}:value:MAX",
     "AREA:val_max#$HalfBlue",
     "AREA:val_min#$Canvas",
-    "LINE1:val_avg#$FullBlue:{inst}",
+    "LINE1:val_avg#$FullBlue:Issues/s",
     'GPRINT:val_min:MIN:%5.2lf Min,',
     'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
     'GPRINT:val_max:MAX:%5.2lf Max,',
     'GPRINT:val_avg:LAST:%5.2lf Last'
     ],
-    mysql_octets => ['-v', 'Bytes/s',
+    mysql_octets => ['-v', 'Bits/s',
     'DEF:out_min={file}:tx:MIN',
     'DEF:out_avg={file}:tx:AVERAGE',
     'DEF:out_max={file}:tx:MAX',
     'DEF:inc_min={file}:rx:MIN',
     'DEF:inc_avg={file}:rx:AVERAGE',
     'DEF:inc_max={file}:rx:MAX',
-    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
     'CDEF:mytime=out_avg,TIME,TIME,IF',
     'CDEF:sample_len_raw=mytime,PREV(mytime),-',
     'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
@@ -1702,18 +1620,25 @@ sub load_graph_definitions
     'CDEF:out_avg_sum=PREV,UN,0,PREV,IF,out_avg_sample,+',
     'CDEF:inc_avg_sample=inc_avg,UN,0,inc_avg,IF,sample_len,*',
     'CDEF:inc_avg_sum=PREV,UN,0,PREV,IF,inc_avg_sample,+',
-    "AREA:out_avg#$HalfGreen",
-    "AREA:inc_avg#$HalfBlue",
+    'CDEF:out_bit_min=out_min,8,*',
+    'CDEF:out_bit_avg=out_avg,8,*',
+    'CDEF:out_bit_max=out_max,8,*',
+    'CDEF:inc_bit_min=inc_min,8,*',
+    'CDEF:inc_bit_avg=inc_avg,8,*',
+    'CDEF:inc_bit_max=inc_max,8,*',
+    'CDEF:overlap=out_bit_avg,inc_bit_avg,GT,inc_bit_avg,out_bit_avg,IF',
+    "AREA:out_bit_avg#$HalfGreen",
+    "AREA:inc_bit_avg#$HalfBlue",
     "AREA:overlap#$HalfBlueGreen",
-    "LINE1:out_avg#$FullGreen:Written",
-    'GPRINT:out_avg:AVERAGE:%5.1lf%s Avg,',
-    'GPRINT:out_max:MAX:%5.1lf%s Max,',
-    'GPRINT:out_avg:LAST:%5.1lf%s Last',
+    "LINE1:out_bit_avg#$FullGreen:Written",
+    'GPRINT:out_bit_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:out_bit_max:MAX:%5.1lf%s Max,',
+    'GPRINT:out_bit_avg:LAST:%5.1lf%s Last',
     'GPRINT:out_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
-    "LINE1:inc_avg#$FullBlue:Read   ",
-    'GPRINT:inc_avg:AVERAGE:%5.1lf%s Avg,',
-    'GPRINT:inc_max:MAX:%5.1lf%s Max,',
-    'GPRINT:inc_avg:LAST:%5.1lf%s Last',
+    "LINE1:inc_bit_avg#$FullBlue:Read   ",
+    'GPRINT:inc_bit_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:inc_bit_max:MAX:%5.1lf%s Max,',
+    'GPRINT:inc_bit_avg:LAST:%5.1lf%s Last',
     'GPRINT:inc_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
     ],
     mysql_qcache => ['-v', 'Queries/s',
@@ -2206,14 +2131,8 @@ sub load_graph_definitions
     'DEF:temp_min={file}:value:MIN',
     'DEF:temp_max={file}:value:MAX',
     'CDEF:average=temp_avg,0.2,*,PREV,UN,temp_avg,PREV,IF,0.8,*,+',
-    'CDEF:derivative=PREV(average),average,-',
-    'CDEF:rising=derivative,-0.01,LT,INF,UNKN,IF',
-    'CDEF:falling=derivative,0.01,GT,INF,UNKN,IF',
-    "AREA:rising#f0fff0",
-    "AREA:falling#fff0f0",
     "AREA:temp_max#$HalfRed",
     "AREA:temp_min#$Canvas",
-    #"LINE1:average#000000",
     "LINE1:temp_avg#$FullRed:Temperature",
     'GPRINT:temp_min:MIN:%4.1lf Min,',
     'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
@@ -2414,6 +2333,11 @@ sub load_graph_definitions
 
   $MetaGraphDefs->{'cpu'} = \&meta_graph_cpu;
   $MetaGraphDefs->{'memory'} = \&meta_graph_memory;
+  $MetaGraphDefs->{'nfs_procedure'} = \&meta_graph_nfs_procedure;
+  $MetaGraphDefs->{'ps_state'} = \&meta_graph_ps_state;
+  $MetaGraphDefs->{'swap'} = \&meta_graph_swap;
+  $MetaGraphDefs->{'mysql_commands'} = \&meta_graph_mysql_commands;
+  $MetaGraphDefs->{'mysql_handler'} = \&meta_graph_mysql_commands;
 } # load_graph_definitions
 
 sub meta_graph_generic_stack
@@ -2499,7 +2423,7 @@ sub meta_graph_generic_stack
       qq(GPRINT:${inst_name}_min:MIN:$number_format Min,),
       qq(GPRINT:${inst_name}_avg:AVERAGE:$number_format Avg,),
       qq(GPRINT:${inst_name}_max:MAX:$number_format Max,),
-      qq(GPRINT:${inst_name}_avg:LAST:$number_format Last\l),
+      qq(GPRINT:${inst_name}_avg:LAST:$number_format Last\\l),
     );
   }
 
@@ -2629,4 +2553,211 @@ sub meta_graph_memory
   return (meta_graph_generic_stack ($opts, $sources));
 } # meta_graph_cpu
 
+sub meta_graph_mysql_commands
+{
+  confess ("Wrong number of arguments") if (@_ != 5);
+
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instances = shift;
+
+  my $opts = {};
+  my $sources = [];
+
+  $opts->{'title'} = "$host/$plugin"
+  . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+  $opts->{'number_format'} = '%5.2lf';
+
+  my @files = ();
+
+  for (sort @$type_instances)
+  {
+    my $inst = $_;
+    my $file = '';
+    my $title = $opts->{'title'};
+
+    for (@DataDirs)
+    {
+      if (-e "$_/$title-$inst.rrd")
+      {
+       $file = "$_/$title-$inst.rrd";
+       last;
+      }
+    }
+    confess ("No file found for $title") if ($file eq '');
+
+    push (@$sources,
+      {
+       name => $inst,
+       file => $file
+      }
+    );
+  } # for (@$type_instances)
+
+  return (meta_graph_generic_stack ($opts, $sources));
+} # meta_graph_mysql_commands
+
+sub meta_graph_nfs_procedure
+{
+  confess ("Wrong number of arguments") if (@_ != 5);
+
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instances = shift;
+
+  my $opts = {};
+  my $sources = [];
+
+  $opts->{'title'} = "$host/$plugin"
+  . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+  $opts->{'number_format'} = '%5.1lf%s';
+
+  my @files = ();
+
+  for (sort @$type_instances)
+  {
+    my $inst = $_;
+    my $file = '';
+    my $title = $opts->{'title'};
+
+    for (@DataDirs)
+    {
+      if (-e "$_/$title-$inst.rrd")
+      {
+       $file = "$_/$title-$inst.rrd";
+       last;
+      }
+    }
+    confess ("No file found for $title") if ($file eq '');
+
+    push (@$sources,
+      {
+       name => $inst,
+       file => $file
+      }
+    );
+  } # for (@$type_instances)
+
+  return (meta_graph_generic_stack ($opts, $sources));
+} # meta_graph_nfs_procedure
+
+sub meta_graph_ps_state
+{
+  confess ("Wrong number of arguments") if (@_ != 5);
+
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instances = shift;
+
+  my $opts = {};
+  my $sources = [];
+
+  $opts->{'title'} = "$host/$plugin"
+  . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+
+  my @files = ();
+
+  $opts->{'colors'} =
+  {
+    'Running'      => '00e000',
+    'Sleeping'  => '0000ff',
+    'Paging'      => 'ffb000',
+    'Zombies'   => 'ff0000',
+    'Blocked'   => 'ff00ff',
+    'Stopped' => 'a000a0'
+  };
+
+  _custom_sort_arrayref ($type_instances,
+    [qw(paging blocked zombies stopped running sleeping)]);
+
+  for (@$type_instances)
+  {
+    my $inst = $_;
+    my $file = '';
+    my $title = $opts->{'title'};
+
+    for (@DataDirs)
+    {
+      if (-e "$_/$title-$inst.rrd")
+      {
+       $file = "$_/$title-$inst.rrd";
+       last;
+      }
+    }
+    confess ("No file found for $title") if ($file eq '');
+
+    push (@$sources,
+      {
+       name => ucfirst ($inst),
+       file => $file
+      }
+    );
+  } # for (@$type_instances)
+
+  return (meta_graph_generic_stack ($opts, $sources));
+} # meta_graph_ps_state
+
+sub meta_graph_swap
+{
+  confess ("Wrong number of arguments") if (@_ != 5);
+
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instances = shift;
+
+  my $opts = {};
+  my $sources = [];
+
+  $opts->{'title'} = "$host/$plugin"
+  . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+  $opts->{'number_format'} = '%5.1lf%s';
+
+  my @files = ();
+
+  $opts->{'colors'} =
+  {
+    'Free'   => '00e000',
+    'Cached'  => '0000ff',
+    'Reserved'      => 'ffb000',
+    'Used'   => 'ff0000'
+  };
+
+  _custom_sort_arrayref ($type_instances,
+    [qw(free cached reserved used)]);
+
+  for (@$type_instances)
+  {
+    my $inst = $_;
+    my $file = '';
+    my $title = $opts->{'title'};
+
+    for (@DataDirs)
+    {
+      if (-e "$_/$title-$inst.rrd")
+      {
+       $file = "$_/$title-$inst.rrd";
+       last;
+      }
+    }
+    confess ("No file found for $title") if ($file eq '');
+
+    push (@$sources,
+      {
+       name => ucfirst ($inst),
+       file => $file
+      }
+    );
+  } # for (@$type_instances)
+
+  return (meta_graph_generic_stack ($opts, $sources));
+} # meta_graph_swap
+
 # vim: shiftwidth=2:softtabstop=2:tabstop=8