#!/usr/bin/perl -w
# $Id: libvirt 24900 2010-03-30 13:50:40Z espen $
#
# Copyright (C) 2010 Espen Braastad
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 dated June,
# 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#
# This multigraph plugin monitors libvirt resource usage.
#
# Configuration variables:
#
#  General variables:
#   address         - to libvirt (default "xen:///")
#   username        - to libvirt (default: "")
#   password        - to libvirt (default: "")
#   tmpfile         - to cache values (default: "/var/lib/munin/plugin-state/libvirt")
#
#  Variables to enable or disable graphs:
#   show_cpu_used           - 1 to enable, 0 to disable [default = 1]
#   show_disk_traffic       - 1 to enable, 0 to disable [default = 1]
#   show_disk_utilization   - 1 to enable, 0 to disable [default = 1]
#   show_disk_latency       - 1 to enable, 0 to disable [default = 1]
#   show_memory_allocated   - 1 to enable, 0 to disable [default = 1]
#   show_network_traffic    - 1 to enable, 0 to disable [default = 1]
#   show_network_drops      - 1 to enable, 0 to disable [default = 1]
#
# Requirements:
#   RHEL5: perl-Sys-Virt.x86_64
#
# Revision 1.4 2010/03/30 espen
#  Added graphs for i/o utilization
#
# Revision 1.3 2010/03/29 espen
#  Added graphs for i/o latency
#
# Revision 1.2 2010/03/19 espen
#  Added support for enable/disable graphs from plugin-conf.d.
#  Added graphs for disk errors and network drops.
#
# Revision 1.1 2010/03/14 espen
#  Added support for monitoring network (bits) and disk (bytes) usage.
#
# Revision 1.0 2010/03/11 espen
#  Initial version. Support for cpu and memory per domain.
#
#%# family=auto
#%# capabilities=autoconf

use strict;
use XML::Twig;
use Sys::Virt;
use Sys::Virt::Domain;
use File::stat;
use Storable;
use Time::localtime;

my $address = $ENV{address} || "xen:///";
my $username = $ENV{username} || "";
my $password = $ENV{password} || "";
my $tmpfile = $ENV{tmpfile} || "/var/lib/munin/plugin-state/libvirt";
my $decimals=5;

my %show=();

# Reading values from plugin-conf.d or set defaults.
$show{'cpu_used'}         = $ENV{show_cpu_used} || 1;
$show{'disk_traffic'}     = $ENV{show_disk_traffic} || 1;
$show{'disk_utilization'} = $ENV{show_disk_utilization} || 1;
$show{'disk_latency'}     = $ENV{show_disk_latency} || 1;
$show{'disk_errors'}      = $ENV{show_disk_errors} || 1;
$show{'memory_allocated'} = $ENV{show_memory_allocated} || 1;
$show{'network_traffic'}  = $ENV{show_network_traffic} || 1;
$show{'network_drops'}    = $ENV{show_network_drops} || 1;

sub init() {
  my $type=undef;
  
  if ($ARGV[0] and $ARGV[0] eq "config"){
    $type="config";
  }
  if ($ARGV[0] and $ARGV[0] eq "autoconf"){
    &autoconf();
    exit;
  }
  if(!$ARGV[0]){
    $type="fetch";
  }

  if(!defined($type)){
    print "Argument not supported, see the howto.\n";
  }

  # Connecting to libvirt
  my $vmm=&connect();

  my @domains = $vmm->list_domains();

  my %hash=();

  my $node = $vmm->get_node_info();
  # 'model' => 'x86_64',
  # 'threads' => 2,
  # 'cores' => 4,
  # 'memory' => 33545216,
  # 'mhz' => 2261,
  # 'sockets' => 2,
  # 'nodes' => 1,
  # 'cpus' => 16

  foreach my $domain (@domains) {

    my $name                  = $domain->get_name();
    $hash{$name}{'name'}      = $name;
    $hash{$name}{'id'}        = $domain->get_id();
    $hash{$name}{'info'}      = $domain->get_info();
    $hash{$name}{'maxvcpus'}  = $domain->get_max_vcpus();
    $hash{$name}{'maxmem'}    = $domain->get_max_memory();
    $hash{$name}{'type'}      = $domain->get_os_type();
    $hash{$name}{'scheduler'} = $domain->get_scheduler_type();
    $hash{$name}{'xml'}       = parse_xml($domain->get_xml_description());
    $hash{$name}{'label'}     = clean_label($name);

    # Calculate cputime used in percent
    if(defined($hash{$name}{'info'}{'cpuTime'}) && defined($node->{'cpus'})){
      $hash{$name}{'info'}{'cpuPercentage'} = sprintf("%d",1.0e-7 * $hash{$name}{'info'}{'cpuTime'} / $node->{'cpus'});
    }

    # Calculate bytes
    if(defined($hash{$name}{'info'}{'memory'})){
      $hash{$name}{'info'}{'memory_bytes'} = 1024 * $hash{$name}{'info'}{'memory'};
    }
 
    # Extract network usage
    if(defined($hash{$name}{'xml'}{'devices'}{'interface'}{'bridge'})){
      my $vif_id=0;
      for my $vif (keys %{$hash{$name}{'xml'}{'devices'}{'interface'}{'bridge'}}){
        $hash{$name}{'devices'}{'network'}{$vif}=$domain->interface_stats($vif);

        # The interface number on this $VM. Will be used as ID later in labels
        $hash{$name}{'devices'}{'network'}{$vif}{'vif_id'}=$vif_id;
        $vif_id++;
      }
    }

    # Extract block device usage
    if(defined($hash{$name}{'xml'}{'devices'}{'disk'}{'block'})){
      for my $block (keys %{$hash{$name}{'xml'}{'devices'}{'disk'}{'block'}}){
        $hash{$name}{'devices'}{'block'}{$block}=$domain->block_stats($block);
      }
    }

    # Gather data later used to calculate latency and utilization
    if(defined($hash{$name}{'xml'}{'devices'}{'disk'}{'block'})){
      for my $block (keys %{$hash{$name}{'xml'}{'devices'}{'disk'}{'block'}}){
        if(defined($hash{$name}{'devices'}{'block'}{$block})){
          my $source_dev=$hash{$name}{'xml'}{'devices'}{'disk'}{'block'}{$block}{'source'};
          $hash{$name}{'devices'}{'block'}{$block}{'source'}{$source_dev}=read_diskstats($source_dev);
        }
      }
    }
  }

  my $prev_time=&get_prev_time($tmpfile);
  my $time=time();
  my $prev_state_ref=undef;

  # Cache the result to disk if we graphs that require cache are enabled
  if($show{'disk_latency'} == 1 || $show{'disk_utilization'} == 1){
    if(-e $tmpfile){
      $prev_state_ref=retrieve($tmpfile);
    }

    store(\%hash,$tmpfile);
  }

  #
  # Disk utilization
  #

  if($show{'disk_utilization'} == 1){

    #
    # Disk utilization, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_disk_utilization\n";
      #print "graph_order rd wr\n";
      print "graph_title Disk utilization per domain in percent\n";
      print "graph_vlabel %\n";
      print "graph_category Virtualization\n";
      print "graph_args -l 0 --base 1000 --upper-limit 100\n";
      #print "graph_width 550\n";
  
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'})){
          print $hash{$vm}{'label'} . ".label " . $hash{$vm}{'name'} . "\n";
          print $hash{$vm}{'label'} . ".info The maximum I/O utilization in percent on " . $hash{$vm}{'name'} . ". If the time spent for I/O is close to 1000 msec for a given second, the device is nearly 100% saturated. If the virtual machine have more than one disk, the value from the disk with the highest utilization is being shown.\n";
          print $hash{$vm}{'label'} . ".min 0\n";
          print $hash{$vm}{'label'} . ".draw LINE2\n";
        }
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_disk_utilization\n";
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){

          my $utilization=0;
 
          for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){

            if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){

              for my $source_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}}){

                for my $slave_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}}){

                  my $prev_ms_spent_doing_io=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'io_ticks'};
                  my $cur_ms_spent_doing_io=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'io_ticks'};

                  if($cur_ms_spent_doing_io > $prev_ms_spent_doing_io){
                    my $ticks=$cur_ms_spent_doing_io-$prev_ms_spent_doing_io;
                    my $interval_ms=($time-$prev_time)*1000;
                    if($interval_ms > 0){
                      my $dev_utilization=($ticks/$interval_ms)*100;

                      # Will only print show the highest utilization on each VM on the top level graph
                      if($dev_utilization > $utilization){
                        $utilization=sprintf("%.${decimals}f",$dev_utilization);
                      }
                    }
                  }
                }
              }
            }
          }
          print $hash{$vm}{'label'} . ".value " . $utilization . "\n";
        }
      }
      print "\n";
    }

    #
    # Disk utilization, second level
    #

    for my $vm (sort keys %hash) {
      my $devices=0;
      if(defined($hash{$vm}{'devices'}{'block'})){
        for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
          $devices++;
        }
      }
      if($type eq "config"){
        print "multigraph libvirt_disk_utilization." . $hash{$vm}{'label'} . "\n";
        #print "graph_order rd wr\n";
        print "graph_title Disk utilization on " . $vm . " in percent\n";
        print "graph_vlabel %\n";
        print "graph_category Virtualization\n";
        print "graph_args -l 0 --base 1000 --upper-limit 100\n";
        #print "graph_width 550\n";
    
        if($devices>0){
          if(defined($hash{$vm}{'devices'}{'block'})){
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
              print $hash{$vm}{'label'} . "_" . $device . ".label " . $device . "\n";
              print $hash{$vm}{'label'} . "_" . $device . ".info The maximum I/O utilization in percent on " . $device . ". If the time spent for I/O is close to 1000 msec for a given second, the device is nearly 100% saturated.\n";
              print $hash{$vm}{'label'} . "_" . $device . ".min 0\n";
              print $hash{$vm}{'label'} . "_" . $device . ".draw LINE2\n";
            }
          }
        }
        print "\n";
    
      } elsif($type eq "fetch"){
        if($devices > 0){
          print "multigraph libvirt_disk_utilization." . $hash{$vm}{'label'} . "\n";
          if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){
  
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
  
              my $utilization=0;
  
              if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){
  
                for my $source_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}}){
  
                  for my $slave_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}}){
  
                    my $prev_ms_spent_doing_io=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'io_ticks'};
                    my $cur_ms_spent_doing_io=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'io_ticks'};
  
                    if($cur_ms_spent_doing_io > $prev_ms_spent_doing_io){
                      my $ticks=$cur_ms_spent_doing_io-$prev_ms_spent_doing_io;
                      my $interval_ms=($time-$prev_time)*1000;
                      if($interval_ms > 0){
                        my $dev_utilization=($ticks/$interval_ms)*100;
                        $utilization=sprintf("%.${decimals}f",$dev_utilization);
                      }
                    }
                  }
                }
              }
              print $hash{$vm}{'label'} . "_" . $device . ".value " . $utilization . "\n";
            }
          }
          print "\n";
        }
      }
    }
  }

  #
  # Disk latency
  #

  if($show{'disk_latency'} == 1){

    #
    # Disk latency, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_disk_latency\n";
      #print "graph_order rd wr\n";
      print "graph_title Disk latency per domain in seconds\n";
      print "graph_args --base 1000\n";
      print "graph_vlabel read (-) / write (+)\n";
      print "graph_category Virtualization\n";
      #print "graph_width 550\n";
  
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'})){

          # Will only graph the domains with one or more block device
          my $devices=0;
          for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
            $devices++;
          }
          if($devices > 0){
            print $hash{$vm}{'label'} . "_rd.label " . $hash{$vm}{'name'} . "_rd\n";
            print $hash{$vm}{'label'} . "_rd.info I/O latency in seconds on " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_rd.min 0\n";
            print $hash{$vm}{'label'} . "_rd.draw LINE2\n";
            print $hash{$vm}{'label'} . "_rd.graph no\n";
    
            print $hash{$vm}{'label'} . "_wr.label " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_wr.info I/O latency in seconds on " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_wr.min 0\n";
            print $hash{$vm}{'label'} . "_wr.draw LINE2\n";
            print $hash{$vm}{'label'} . "_wr.negative " . $hash{$vm}{'label'} . "_rd\n";
          }
        }
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_disk_latency\n";
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){

          my $prev_total_time_spent_writing=0;
          my $prev_total_time_spent_reading=0;
          my $prev_total_ios_read=0;
          my $prev_total_ios_written=0;
          my $cur_total_time_spent_writing=0;
          my $cur_total_time_spent_reading=0;
          my $cur_total_ios_read=0;
          my $cur_total_ios_written=0;
  
          my $devices=0;
 
          for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
            $devices++;

            if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){

              for my $source_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}}){

                for my $slave_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}}){

                  my $prev_time_spent_writing=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'write_ticks'};
                  my $prev_time_spent_reading=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'read_ticks'};
                  my $prev_ios_read=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'read_ios'};
                  my $prev_ios_written=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'write_ios'};

                  my $cur_time_spent_writing=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'write_ticks'};
                  my $cur_time_spent_reading=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'read_ticks'};
                  my $cur_ios_read=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'read_ios'};
                  my $cur_ios_written=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'write_ios'};

                  $prev_total_time_spent_writing+=$prev_time_spent_writing;
                  $prev_total_time_spent_reading+=$prev_time_spent_reading;
                  $prev_total_ios_read+=$prev_ios_read;
                  $prev_total_ios_written+=$prev_ios_written;

                  $cur_total_time_spent_writing+=$cur_time_spent_writing;
                  $cur_total_time_spent_reading+=$cur_time_spent_reading;
                  $cur_total_ios_read+=$cur_ios_read;
                  $cur_total_ios_written+=$cur_ios_written;
                }
              }
            }
          }

          my $read_latency=0;
          my $write_latency=0;

          if($prev_total_time_spent_reading > 0 && $prev_total_ios_read > 0 && ($cur_total_ios_read-$prev_total_ios_read) > 0){
            $read_latency=(($cur_total_time_spent_reading-$prev_total_time_spent_reading)/($cur_total_ios_read-$prev_total_ios_read))/1000;
          }

          if($prev_total_time_spent_writing > 0 && $prev_total_ios_written > 0 && ($cur_total_ios_written-$prev_total_ios_written) > 0){
            $write_latency=(($cur_total_time_spent_writing-$prev_total_time_spent_writing)/($cur_total_ios_written-$prev_total_ios_written))/1000;
          }

          if($devices > 0){
            print $hash{$vm}{'label'} . "_rd.value " . $read_latency . "\n";
            print $hash{$vm}{'label'} . "_wr.value " . $write_latency . "\n";
          }
        }
      }
      print "\n";
    }

    #
    # Disk latency, second level
    #

    for my $vm (sort keys %hash) {
      my $devices=0;
      for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
        $devices++;
      }
      if($devices > 0){
        if($type eq "config"){
          print "multigraph libvirt_disk_latency.$hash{$vm}{'label'}\n";
          #print "graph_order rd wr\n";
          print "graph_title Disk latency per vbd on $vm in seconds\n";
          print "graph_args --base 1000\n";
          print "graph_vlabel read (-) / write (+)\n";
          print "graph_category Virtualization\n";
          #print "graph_width 550\n";
  
          if(defined($hash{$vm}{'devices'}{'block'})){
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
          
              print $hash{$vm}{'label'} . "_" . $device . "_rd.label " . $device . "_rd\n";
              print $hash{$vm}{'label'} . "_" . $device . "_rd.info I/O latency in seconds on " . $hash{$vm}{'name'} . ":" . $device . "\n";
              print $hash{$vm}{'label'} . "_" . $device . "_rd.min 0\n";
              print $hash{$vm}{'label'} . "_" . $device . "_rd.draw LINE2\n";
              print $hash{$vm}{'label'} . "_" . $device . "_rd.graph no\n";
  
              print $hash{$vm}{'label'} . "_" . $device . "_wr.label " . $device . "\n";
              print $hash{$vm}{'label'} . "_" . $device . "_wr.info I/O latency in seconds on " . $hash{$vm}{'name'} . ":" . $device . "\n";
              print $hash{$vm}{'label'} . "_" . $device . "_wr.min 0\n";
              print $hash{$vm}{'label'} . "_" . $device . "_wr.draw LINE2\n";
              print $hash{$vm}{'label'} . "_" . $device . "_wr.negative " . $hash{$vm}{'label'} . "_" . $device . "_rd\n";
            }
            print "\n";
          }

        } elsif($type eq "fetch"){
          print "multigraph libvirt_disk_latency.$hash{$vm}{'label'}\n";
          if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
  
              if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){
  
                for my $source_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}}){
  
                  my $prev_total_time_spent_writing=0;
                  my $prev_total_time_spent_reading=0;
                  my $prev_total_ios_read=0;
                  my $prev_total_ios_written=0;
                  my $cur_total_time_spent_writing=0;
                  my $cur_total_time_spent_reading=0;
                  my $cur_total_ios_read=0;
                  my $cur_total_ios_written=0;
  
                  for my $slave_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}}){

                    my $prev_time_spent_writing=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'write_ticks'};
                    my $prev_time_spent_reading=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'read_ticks'};
                    my $prev_ios_read=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'read_ios'};
                    my $prev_ios_written=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'write_ios'};

                    my $cur_time_spent_writing=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'write_ticks'};
                    my $cur_time_spent_reading=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'read_ticks'};
                    my $cur_ios_read=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'read_ios'};
                    my $cur_ios_written=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'write_ios'};

                    $prev_total_time_spent_writing+=$prev_time_spent_writing;
                    $prev_total_time_spent_reading+=$prev_time_spent_reading;
                    $prev_total_ios_read+=$prev_ios_read;
                    $prev_total_ios_written+=$prev_ios_written;

                    $cur_total_time_spent_writing+=$cur_time_spent_writing;
                    $cur_total_time_spent_reading+=$cur_time_spent_reading;
                    $cur_total_ios_read+=$cur_ios_read;
                    $cur_total_ios_written+=$cur_ios_written;
                  }

                  my $read_latency=0;
                  my $write_latency=0;
  
                  if($prev_total_time_spent_reading > 0 && $prev_total_ios_read > 0 && ($cur_total_ios_read-$prev_total_ios_read) > 0){
                    $read_latency=(($cur_total_time_spent_reading-$prev_total_time_spent_reading)/($cur_total_ios_read-$prev_total_ios_read))/1000;
                  }
  
                  if($prev_total_time_spent_writing > 0 && $prev_total_ios_written > 0 && ($cur_total_ios_written-$prev_total_ios_written) > 0){
                    $write_latency=(($cur_total_time_spent_writing-$prev_total_time_spent_writing)/($cur_total_ios_written-$prev_total_ios_written))/1000;
                  }
  
                  print $hash{$vm}{'label'} . "_" . $device . "_rd.value " . $read_latency . "\n";
                  print $hash{$vm}{'label'} . "_" . $device . "_wr.value " . $write_latency . "\n";
 
                }
              }
            }
          }
        }
        print "\n";
      }
    }
  }

  if($show{'disk_traffic'} == 1){

    #
    # Disk used, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_disk\n";
      #print "graph_order rd wr\n";
      print "graph_title Disk traffic per domain in bytes\n";
      print "graph_args --base 1000\n";
      print "graph_vlabel bytes read (-) / written (+) per \${graph_period}\n";
      print "graph_category Virtualization\n";
      #print "graph_width 550\n";
  
      for my $vm (sort keys %hash) {
        my $devices=0;
        if(defined($hash{$vm}{'devices'}{'block'})){
          for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
            $devices++;
          }
        }

        if($devices > 0){
          if(defined($hash{$vm}{'devices'}{'block'})){
          
            print $hash{$vm}{'label'} . "_rd_bytes.label " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_rd_bytes.type COUNTER\n";
            print $hash{$vm}{'label'} . "_rd_bytes.info The number of bytes read by " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_rd_bytes.min 0\n";
            print $hash{$vm}{'label'} . "_rd_bytes.draw LINE2\n";
            print $hash{$vm}{'label'} . "_rd_bytes.graph no\n";
    
            print $hash{$vm}{'label'} . "_wr_bytes.label " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_wr_bytes.type COUNTER\n";
            print $hash{$vm}{'label'} . "_wr_bytes.info The number of bytes written by " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_wr_bytes.min 0\n";
            print $hash{$vm}{'label'} . "_wr_bytes.draw LINE2\n";
            print $hash{$vm}{'label'} . "_wr_bytes.negative " . $hash{$vm}{'label'} . "_rd_bytes\n";
          }
        }
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_disk\n";
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'})){
          my $total_read=0;
          my $total_write=0;
          for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
            $total_read+=$hash{$vm}{'devices'}{'block'}{$device}{'rd_bytes'};
            $total_write+=$hash{$vm}{'devices'}{'block'}{$device}{'wr_bytes'};
          }
          print $hash{$vm}{'label'} . "_rd_bytes.value " . $total_read . "\n";
          print $hash{$vm}{'label'} . "_wr_bytes.value " . $total_write . "\n";
        }
      }
      print "\n";
    }

    #
    # Disk used, second level
    #
  
    for my $vm (sort keys %hash) {
      my $devices=0;
      if(defined($hash{$vm}{'devices'}{'block'})){
        for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
          $devices++;
        }
      }

      if($devices > 0){
        if(defined($hash{$vm}{'devices'}{'block'})){
          if($type eq "config"){
            print "multigraph libvirt_disk.bytes_" . $hash{$vm}{'label'} . "\n";
            #print "graph_order rd wr\n";
            print "graph_title Disk traffic for " . $hash{$vm}{'name'} . "\n";
            print "graph_args --base 1000\n";
            print "graph_vlabel bytes read (-) / written (+) per \${graph_period}\n";
            print "graph_category Virtualization\n";
            #print "graph_width 550\n";
    
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
              print $device . "_rd_bytes.label " . $device . "_rd\n";
              print $device . "_rd_bytes.type COUNTER\n";
              print $device . "_rd_bytes.info The number of bytes read by " . $hash{$vm}{'name'} . "\n";
              print $device . "_rd_bytes.min 0\n";
              print $device . "_rd_bytes.graph no\n";
    
              print $device . "_wr_bytes.label " . $device . "\n";
              print $device . "_wr_bytes.type COUNTER\n";
              print $device . "_wr_bytes.info The number of bytes written by " . $hash{$vm}{'name'} . "\n";
              print $device . "_wr_bytes.min 0\n";
              print $device . "_wr_bytes.draw LINE2\n";
              print $device . "_wr_bytes.negative " . $device . "_rd_bytes\n";
            }
            print "\n";
    
          } elsif($type eq "fetch"){
            print "multigraph libvirt_disk.bytes_" . $hash{$vm}{'label'} . "\n";
            if(defined($hash{$vm}{'devices'}{'block'})){
              for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
                print $device . "_rd_bytes.value " . $hash{$vm}{'devices'}{'block'}{$device}{'rd_bytes'} . "\n";
                print $device . "_wr_bytes.value " . $hash{$vm}{'devices'}{'block'}{$device}{'wr_bytes'} . "\n";
              }
            }
            print "\n";
          }
        }
      }
    }
  }

  #
  # Disk errors
  #
  #  errs -> Some kind of error count


  if($show{'disk_errors'} == 1){

    #
    # Disk errors, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_disk_errs\n";
      print "graph_title Disk errors per domain\n";
      print "graph_args --base 1000\n";
      print "graph_category Virtualization\n";
  
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'})){
          my $devices=0;
          if(defined($hash{$vm}{'devices'}{'block'})){
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
              $devices++;
            }
          }
          if($devices > 0){
            print $hash{$vm}{'label'} . "_errs.label " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_errs.type COUNTER\n";
            print $hash{$vm}{'label'} . "_errs.info The number of errors by " . $hash{$vm}{'name'} . "\n";
            print $hash{$vm}{'label'} . "_errs.min 0\n";
            print $hash{$vm}{'label'} . "_errs.draw LINE2\n";
          }
        }
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_disk_errs\n";
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'block'})){
          my $errs=0;
          for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
            $errs+=$hash{$vm}{'devices'}{'block'}{$device}{'errs'};
          }
          print $hash{$vm}{'label'} . "_errs.value " . $errs . "\n";
        }
      }
      print "\n";
    }

    #
    # Disk errors, second level
    #
  
    for my $vm (sort keys %hash) {
      my $devices=0;
      if(defined($hash{$vm}{'devices'}{'block'})){
        for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
          $devices++;
        }
      }
      if($devices > 0){
        if(defined($hash{$vm}{'devices'}{'block'})){
          if($type eq "config"){
            print "multigraph libvirt_disk_errs." . $hash{$vm}{'label'} . "\n";
            print "graph_title Disk errors for " . $hash{$vm}{'name'} . "\n";
            print "graph_args --base 1000\n";
            print "graph_category Virtualization\n";
  
            for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
            
              print $device . "_errs.label " . $device . "\n";
              print $device . "_errs.type COUNTER\n";
              print $device . "_errs.info The number of errors by " . $hash{$vm}{'name'} . " on defice " . $device . "\n";
              print $device . "_errs.min 0\n";
              print $device . "_errs.draw LINE2\n";
            }
            print "\n";
  
          } elsif($type eq "fetch"){
            print "multigraph libvirt_disk_errs." . $hash{$vm}{'label'} . "\n";
            if(defined($hash{$vm}{'devices'}{'block'})){
              for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){
                print $device . "_errs.value " . $hash{$vm}{'devices'}{'block'}{$device}{'errs'} . "\n";
              }
            }
            print "\n";
          }
        }
      }
    }
  }

  #
  # Network used
  #

  if($show{'network_traffic'} == 1){

    #
    # Network used, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_network\n";
      print "graph_title Network traffic per domain in bytes\n";
      print "graph_args --base 1000\n";
      print "graph_vlabel Bytes in (-) / out (+) per \${graph_period}\n";
      print "graph_category Virtualization\n";
      #print "graph_width 550\n";
  
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'network'})){
       
          print $hash{$vm}{'label'} . "_rx_bytes.label " . $hash{$vm}{'name'} . "_rx\n";
          print $hash{$vm}{'label'} . "_rx_bytes.type DERIVE\n";
          print $hash{$vm}{'label'} . "_rx_bytes.info The number of bytes read by " . $hash{$vm}{'name'} . " in total.\n";
          print $hash{$vm}{'label'} . "_rx_bytes.min 0\n";
          print $hash{$vm}{'label'} . "_rx_bytes.draw LINE2\n";
          print $hash{$vm}{'label'} . "_rx_bytes.graph no\n";
          #print $hash{$vm}{'label'} . "_rx_bytes.cdef " . $hash{$vm}{'label'} . "_rx_bytes,8,*\n";
  
          print $hash{$vm}{'label'} . "_tx_bytes.label " . $hash{$vm}{'name'} . "\n";
          print $hash{$vm}{'label'} . "_tx_bytes.type DERIVE\n";
          print $hash{$vm}{'label'} . "_tx_bytes.info The number of bytes written by " . $hash{$vm}{'name'} . " in total.\n";
          print $hash{$vm}{'label'} . "_tx_bytes.min 0\n";
          print $hash{$vm}{'label'} . "_tx_bytes.draw LINE2\n";
          print $hash{$vm}{'label'} . "_tx_bytes.negative " . $hash{$vm}{'label'} . "_rx_bytes\n";
          #print $hash{$vm}{'label'} . "_tx_bytes.cdef " . $hash{$vm}{'label'} . "_tx_bytes,8,*\n";
        }
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_network\n";
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'network'})){
          my $read=0;
          my $write=0;
          for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){
            $read+=$hash{$vm}{'devices'}{'network'}{$vif}{'rx_bytes'};
            $write+=$hash{$vm}{'devices'}{'network'}{$vif}{'tx_bytes'};
          }
          print $hash{$vm}{'label'} . "_rx_bytes.value " . $read . "\n";
          print $hash{$vm}{'label'} . "_tx_bytes.value " . $write . "\n";
        }
      }
      print "\n";
    }

    #
    # Network used, second level
    #

    for my $vm (sort keys %hash) {
      if(defined($hash{$vm}{'devices'}{'network'})){
        if($type eq "config"){
          print "multigraph libvirt_network.bytes_" . $hash{$vm}{'label'} . "\n";
          print "graph_title Network traffic for " . $vm . "\n";
          print "graph_args --base 1000\n";
          print "graph_vlabel Bits in (-) / out (+) per \${graph_period}\n";
          print "graph_category Virtualization\n";
          #print "graph_width 550\n";
    
          for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){
            my $vif_id=$hash{$vm}{'devices'}{'network'}{$vif}{'vif_id'};
        
            print "rx_bytes_" . $vif_id . ".label " . $vif . "_rx\n";
            print "rx_bytes_" . $vif_id . ".type DERIVE\n";
            print "rx_bytes_" . $vif_id . ".info The number of bytes read by " . $hash{$vm}{'name'} . "\n";
            print "rx_bytes_" . $vif_id . ".min 0\n";
            print "rx_bytes_" . $vif_id . ".graph no\n";
            print "rx_bytes_" . $vif_id . ".cdef rx_bytes_" . $vif_id . ",8,*\n";

            print "tx_bytes_" . $vif_id . ".label " . $vif . "\n";
            print "tx_bytes_" . $vif_id . ".type DERIVE\n";
            print "tx_bytes_" . $vif_id . ".info The number of bits written by " . $hash{$vm}{'name'} . "\n";
            print "tx_bytes_" . $vif_id . ".min 0\n";
            print "tx_bytes_" . $vif_id . ".draw LINE2\n";
            print "tx_bytes_" . $vif_id . ".negative rx_bytes_" . $vif_id . "\n";
            print "tx_bytes_" . $vif_id . ".cdef tx_bytes_" . $vif_id . ",8,*\n";
          }
          print "\n";

        } elsif($type eq "fetch"){
          print "multigraph libvirt_network.bytes_" . $hash{$vm}{'label'} . "\n";
          for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){
            my $vif_id=$hash{$vm}{'devices'}{'network'}{$vif}{'vif_id'};
            print "rx_bytes_" . $vif_id . ".value " . $hash{$vm}{'devices'}{'network'}{$vif}{'rx_bytes'} . "\n";
            print "tx_bytes_" . $vif_id . ".value " . $hash{$vm}{'devices'}{'network'}{$vif}{'tx_bytes'} . "\n";
          }
          print "\n";
        }
      }
    }
  }

  #
  # Network drops and errors
  #
  #  rx_drop -> Total packets drop at reception
  #  tx_drop -> Total packets dropped at transmission.

  if($show{'network_drops'} == 1){

    #
    # Network drops, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_network_drop\n";
      print "graph_title Network packets dropped per domain\n";
      print "graph_args --base 1000\n";
      print "graph_vlabel Count in (-) / out (+) per \${graph_period}\n";
      print "graph_category Virtualization\n";
      #print "graph_width 550\n";
  
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'network'})){
       
          print $hash{$vm}{'label'} . "_rx_drop.label " . $hash{$vm}{'name'} . "_rx\n";
          print $hash{$vm}{'label'} . "_rx_drop.type DERIVE\n";
          print $hash{$vm}{'label'} . "_rx_drop.info The number of packets dropped at reception by " . $hash{$vm}{'name'} . "\n";
          print $hash{$vm}{'label'} . "_rx_drop.min 0\n";
          print $hash{$vm}{'label'} . "_rx_drop.draw LINE2\n";
          print $hash{$vm}{'label'} . "_rx_drop.graph no\n";
  
          print $hash{$vm}{'label'} . "_tx_drop.label " . $hash{$vm}{'name'} . "\n";
          print $hash{$vm}{'label'} . "_tx_drop.type DERIVE\n";
          print $hash{$vm}{'label'} . "_tx_drop.info The number of packets dropped at transmission by " . $hash{$vm}{'name'} . "\n";
          print $hash{$vm}{'label'} . "_tx_drop.min 0\n";
          print $hash{$vm}{'label'} . "_tx_drop.draw LINE2\n";
          print $hash{$vm}{'label'} . "_tx_drop.negative " . $hash{$vm}{'label'} . "_rx_drop\n";
        }
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_network_drop\n";
      for my $vm (sort keys %hash) {
        if(defined($hash{$vm}{'devices'}{'network'})){
          my $rx_drop=0;
          my $tx_drop=0;
          for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){
            $rx_drop+=$hash{$vm}{'devices'}{'network'}{$vif}{'rx_drop'};
            $tx_drop+=$hash{$vm}{'devices'}{'network'}{$vif}{'tx_drop'};
          }

          print $hash{$vm}{'label'} . "_rx_drop.value " . $rx_drop . "\n";
          print $hash{$vm}{'label'} . "_tx_drop.value " . $tx_drop . "\n";

        }
      }
      print "\n";
    }

    #
    # Network drops, second level
    #

    for my $vm (sort keys %hash) {
      if(defined($hash{$vm}{'devices'}{'network'})){
        if($type eq "config"){
          print "multigraph libvirt_network_drop." . $hash{$vm}{'label'} . "\n";
          print "graph_title Network packeds dropped by " . $vm . "\n";
          print "graph_args --base 1000\n";
          print "graph_vlabel Count in (-) / out (+) per \${graph_period}\n";
          print "graph_category Virtualization\n";
          #print "graph_width 550\n";
    
          for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){
            my $vif_id=$hash{$vm}{'devices'}{'network'}{$vif}{'vif_id'};
        
            print "rx_drop_" . $vif_id . ".label " . $vif . "_rx\n";
            print "rx_drop_" . $vif_id . ".type DERIVE\n";
            print "rx_drop_" . $vif_id . ".info The number of packets dropped by " . $hash{$vm}{'name'} . ", nic " . $vif_id . "\n";
            print "rx_drop_" . $vif_id . ".min 0\n";
            print "rx_drop_" . $vif_id . ".graph no\n";

            print "tx_drop_" . $vif_id . ".label " . $vif . "\n";
            print "tx_drop_" . $vif_id . ".type DERIVE\n";
            print "tx_drop_" . $vif_id . ".info The number of packets dropped by " . $hash{$vm}{'name'} . ", nic " . $vif_id . "\n";
            print "tx_drop_" . $vif_id . ".min 0\n";
            print "tx_drop_" . $vif_id . ".draw LINE2\n";
            print "tx_drop_" . $vif_id . ".negative rx_drop_" . $vif_id . "\n";
          }
          print "\n";

        } elsif($type eq "fetch"){
          print "multigraph libvirt_network_drop." . $hash{$vm}{'label'} . "\n";
          for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){
            my $vif_id=$hash{$vm}{'devices'}{'network'}{$vif}{'vif_id'};
            print "rx_drop_" . $vif_id . ".value " . $hash{$vm}{'devices'}{'network'}{$vif}{'rx_drop'} . "\n";
            print "tx_drop_" . $vif_id . ".value " . $hash{$vm}{'devices'}{'network'}{$vif}{'tx_drop'} . "\n";
          }
          print "\n";
        }
      }
    }
  }

  #
  # CPU used
  #
  
  if($show{'cpu_used'} == 1){

    #
    # CPU used, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_cpu\n";
      print "graph_title Cpu time used per domain in percent\n";
      print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n";
      print "graph_category Virtualization\n";
      #print "graph_width 550\n";
  
      my $draw="AREA";
      for my $vm (sort keys %hash) {
        print $hash{$vm}{'label'} . "_time.label " . $hash{$vm}{'name'} . "\n";
        print $hash{$vm}{'label'} . "_time.type DERIVE\n";
        print $hash{$vm}{'label'} . "_time.info The cpu time used by " . $hash{$vm}{'name'} . " in percent of the total available cpu time on the physical node.\n";
        print $hash{$vm}{'label'} . "_time.min 0\n";
        print $hash{$vm}{'label'} . "_time.draw $draw\n";
        $draw="STACK" if $draw eq "AREA";
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_cpu\n";
      for my $vm (sort keys %hash) {
        print $hash{$vm}{'label'} . "_time.value " . $hash{$vm}{'info'}{'cpuPercentage'} . "\n";
      }
      print "\n";
    }

    #
    # CPU used, second level (pr virtual machine)
    #
  
    if($type eq "config"){
      for my $vm (sort keys %hash) {
        print "multigraph libvirt_cpu.vm_" . $hash{$vm}{'label'} . "\n";
        print "graph_title Cpu time used by " . $hash{$vm}{'name'} . " in percent\n";
        print "graph_args --base 1000\n";
        print "graph_category Virtualization\n";
        #print "graph_width 550\n";
  
        print "time.label " . $hash{$vm}{'name'} . " (" . $hash{$vm}{'type'} . ")\n";
        print "time.type DERIVE\n";
        print "time.info The cpu time used by " . $hash{$vm}{'name'} . " in percent of the total available cpu time on the physical node. This domain has access to " . $hash{$vm}{'info'}{'nrVirtCpu'} . " VCPU(s) now, and $hash{$vm}{'maxvcpus'} at maximum. The scheduler for this domain is " . $hash{$vm}{'scheduler'} . ".\n";
        print "time.min 0\n";
        print "time.draw AREA\n";
        print "\n";
      }
  
    } elsif($type eq "fetch"){
      for my $vm (sort keys %hash) {
        print "multigraph libvirt_cpu.vm_" . $hash{$vm}{'label'} . "\n";
        print "time.value " . $hash{$vm}{'info'}{'cpuPercentage'} . "\n";
        print "\n";
      }
    }
  }

  #
  # Memory allocation
  #

  if($show{'memory_allocated'} == 1){

    #
    # Memory allocation, top level
    #

    if($type eq "config"){
      print "multigraph libvirt_mem\n";
      print "graph_title Memory allocated per domain\n";
      print "graph_args --base 1000\n";
      print "graph_category Virtualization\n";
      #print "graph_width 550\n";
  
      my $draw="AREA";
      for my $vm (sort keys %hash) {
        print $hash{$vm}{'label'} . "_alloc.label " . $hash{$vm}{'name'} . "\n";
        print $hash{$vm}{'label'} . "_alloc.type GAUGE\n";
        print $hash{$vm}{'label'} . "_alloc.info Memory allocation per domain.\n";
        print $hash{$vm}{'label'} . "_alloc.min 0\n";
        print $hash{$vm}{'label'} . "_alloc.draw $draw\n";
        $draw="STACK" if $draw eq "AREA";
      }
      print "\n";
  
    } elsif($type eq "fetch"){
      print "multigraph libvirt_mem\n";
      for my $vm (sort keys %hash) {
        print $hash{$vm}{'label'} . "_alloc.value " . $hash{$vm}{'info'}{'memory_bytes'} . "\n";
      }
      print "\n";
    }

    #
    # Memory allocated, second level (pr virtual machine)
    #
  
    if($type eq "config"){
      for my $vm (sort keys %hash) {
        print "multigraph libvirt_mem.vm_" . $hash{$vm}{'label'} . "\n";
        print "graph_title Memory allocated to " . $hash{$vm}{'name'} . "\n";
        print "graph_args --base 1000\n";
        print "graph_category Virtualization\n";
        #print "graph_width 550\n";
  
        print "mem.label " . $hash{$vm}{'name'} . " (" . $hash{$vm}{'type'} . ")\n";
        print "mem.type GAUGE\n";
        print "mem.info Amount of memory allocated to " . $hash{$vm}{'name'} . ". The maximum amount of memory for this domain is " . $hash{$vm}{'maxmem'}/1024 . " MB.\n";
        print "mem.min 0\n";
        print "mem.draw AREA\n";
        print "\n";
      }
  
    } elsif($type eq "fetch"){
      for my $vm (sort keys %hash) {
        print "multigraph libvirt_mem.vm_" . $hash{$vm}{'label'} . "\n";
        print "mem.value " . $hash{$vm}{'info'}{'memory_bytes'} . "\n";
        print "\n";
      }
    }
  }
}

sub clean_label() {
  my $label=shift;
  $label =~ s/^[^A-Za-z_]/_/;
  $label =~ s/[^A-Za-z0-9_]/_/g;
  return $label;
}

sub connect {
  my $vmm = Sys::Virt->new(address => $address,
                           auth => 1,
                           credlist => [
                             Sys::Virt::CRED_AUTHNAME,
                             Sys::Virt::CRED_PASSPHRASE,
                           ],
                           callback =>
         sub {
               my $creds = shift;

               foreach my $cred (@{$creds}) {
                  if ($cred->{type} == Sys::Virt::CRED_AUTHNAME) {
                      $cred->{result} = $username;
                  }
                  if ($cred->{type} == Sys::Virt::CRED_PASSPHRASE) {
                      $cred->{result} = $password;
                  }
               }
               return 0;
         });
  return $vmm;
}

sub autoconf {
  my $vmm=&connect();
  if($vmm){
    my $node = $vmm->get_node_info();
    if($node){
      print "yes\n";
      exit(0);
    } else {
      print "no (Unable to fetch node info)\n";
      exit(1);
    }
  } else {
    print "no (Unable to connect to libvirt)\n";
    exit(1);
  }

}

#
# Function to extract information from xml and return a hash with the necessary information.
# The logic is really ugly and should be improved.
#
sub parse_xml {
  my $raw_xml=shift;
  my %res=();

  use XML::Simple qw(:strict);
  my $xs=XML::Simple->new;
  my @a=$xs->XMLin($raw_xml,ForceArray => 1, KeyAttr => 'target');

  for (my $i=0 ; $i < scalar(@a) ; $i++){
    # Hack to extract disk information and put it into the hash
    # TODO: Improve
    if(defined($a[$i]{'devices'}[0]{'disk'})){

      my $teller = 0;
      my $fortsette = 1;
       
      while($fortsette){
          if(  $a[$i]{'devices'}[0]{'disk'}[$teller] ){
              my $type=$a[$i]{'devices'}[0]{'disk'}[$teller]{'type'};
              my $source_dev=$a[$i]{'devices'}[0]{'disk'}[$teller]{'source'}[0]{'dev'};
              my $target_bus=$a[$i]{'devices'}[0]{'disk'}[$teller]{'target'}[0]{'bus'};
              my $target_dev=$a[$i]{'devices'}[0]{'disk'}[$teller]{'target'}[0]{'dev'};

              $res{'devices'}{'disk'}{$type}{$target_dev}{'source'}=$source_dev;
              $res{'devices'}{'disk'}{$type}{$target_dev}{'bus'}=$target_bus;
              $res{'devices'}{'disk'}{$type}{$target_dev}{'type'}=$type;

              $teller++;
          }
          else{
              $fortsette = 0;
          }
      } 
    }

    # Hack to extract network information and put it into the hash
    # TODO: Improve
    if(defined($a[$i]{'devices'}[0]{'interface'})){

      my $teller = 0;
      my $fortsette = 1;
       
      while($fortsette){
          if(  $a[$i]{'devices'}[0]{'interface'}[$teller] ){
              my $type=$a[$i]{'devices'}[0]{'interface'}[$teller]{'type'};
              my $target=$a[$i]{'devices'}[0]{'interface'}[$teller]{'target'}[0]{'dev'};
              my $bridge=$a[$i]{'devices'}[0]{'interface'}[$teller]{'source'}[0]{'bridge'};;
              my $mac=$a[$i]{'devices'}[0]{'interface'}[$teller]{'mac'}[0]{'address'};;

              $res{'devices'}{'interface'}{$type}{$target}{'target'}=$target;
              $res{'devices'}{'interface'}{$type}{$target}{'mac'}=$mac;
              $res{'devices'}{'interface'}{$type}{$target}{'bridge'}=$bridge;
              $teller++;
          }
          else{
              $fortsette = 0;
          }
      } 
    }
  }

  return \%res;
}

sub read_diskstats{
  my $dev=shift;

  my %res=();

  # Verify that $dev is a block device.
  if(-b $dev){
    # Read minor and major number
    my $rdev = stat($dev)->rdev; 
    $res{'major'} = $rdev >> 8; 
    $res{'minor'} = $rdev & 0xff;

    # If major numer is 253, then proceed as dm-device
    if($res{'major'} == 253){

      # check that the directory /sys/block/dm-$minor/ exists with a /slaves/ sub directory
      if(-d "/sys/block/dm-" . $res{'minor'} . "/"){
        if(-d "/sys/block/dm-" . $res{'minor'} . "/slaves/"){

          # see if /sys/block/dm-$minor/slaves/ has any slaves
          opendir(DIR, "/sys/block/dm-" . $res{'minor'} . "/slaves/");
          while(my $slave = readdir(DIR)){

            # Exclude directories (. and ..)
            if($slave !~ m/^\.+$/){

              # Check if we have /sys/block/dm-$minor/slaves/$slave/stat
              if(-f "/sys/block/dm-" . $res{'minor'} . "/slaves/" . $slave . "/stat"){
                # Read the stat-file
                open(FILE, "</sys/block/dm-" . $res{'minor'} . "/slaves/" . $slave . "/stat");
                while (my $line = <FILE>) {
                  #               1       2       3       4       5       6       7       8       9      10      11
                  if($line =~ m/(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/){
 
                    # Name            units         description
                    # ----            -----         -----------
                    # read I/Os       requests      number of read I/Os processed
                    # read merges     requests      number of read I/Os merged with in-queue I/O
                    # read sectors    sectors       number of sectors read
                    # read ticks      milliseconds  total wait time for read requests
                    # write I/Os      requests      number of write I/Os processed
                    # write merges    requests      number of write I/Os merged with in-queue I/O
                    # write sectors   sectors       number of sectors written
                    # write ticks     milliseconds  total wait time for write requests
                    # in_flight       requests      number of I/Os currently in flight
                    # io_ticks        milliseconds  total time this block device has been active
                    # time_in_queue   milliseconds  total wait time for all requests
                    #
                    # Documentation fetched from http://www.mjmwired.net/kernel/Documentation/block/stat.txt

                    # store the output in the hash
                    $res{'slaves'}{$slave}{'read_ios'}=$1;
                    $res{'slaves'}{$slave}{'read_merges'}=$2;
                    $res{'slaves'}{$slave}{'read_sectors'}=$3;
                    $res{'slaves'}{$slave}{'read_ticks'}=$4;
                    $res{'slaves'}{$slave}{'write_ios'}=$5;
                    $res{'slaves'}{$slave}{'write_merges'}=$6;
                    $res{'slaves'}{$slave}{'write_sectors'}=$7;
                    $res{'slaves'}{$slave}{'write_ticks'}=$8;
                    $res{'slaves'}{$slave}{'in_flight'}=$9;
                    $res{'slaves'}{$slave}{'io_ticks'}=$10;
                    $res{'slaves'}{$slave}{'time_in_queue'}=$11;
                  }
                }
                close(FILE);
              }
            }
          }
          close(DIR);
        }
      }
    }
  }

  return \%res;
}

# Return the timestamp of a file
sub get_prev_time(){
  my $tmpfile=shift;

  my $time=-1;

  if(-r $tmpfile){
    $time = stat($tmpfile)->mtime;
  }

  return $time;
}

init();

# vim:syntax=perl
