#!/usr/bin/perl -w # vim:ts=4 # # vmware_monitor v0.2 # Steve S 2005 # # Generate a directory filled with .rrd files for usage stats of virtual # machines, plus mrtg .cfg file for their display. # # Usage: # vmware_monitor -c cachefile -d rrddirectory -m mrtg.cfg # -C community -H vmwarehost # default rrddir is . # default cachefile is /cache # default mrtg file is /vmware.cfg # default community is public # default hostname is localhost use strict; use Net::SNMP; use Getopt::Std; use RRDs; my($STATEFILE) = ''; my($RRDDIR) = ''; my($CFGFILE) = ''; my($VMOID) = "1.3.6.1.4.1.6876"; my($DEBUG) = 0; my($TIMEOUT) = 15; my($snmp,$resp,$snmperr); my($hostname) = ''; my($community) = 'public'; # Default community string my($MSG) = ''; my(%lookup) = (); my(%states) = (); my(%tmpnet) = (); my(%vhosts) = (); # location of their rrd, etc my($maxrrd) = 0; my(@list) = (); my($vh); use vars qw($opt_t $opt_C $opt_H $opt_h $opt_c $opt_d $opt_m $opt_D $opt_t); sub dohelp { print "Usage: vmware_monitor [-d][-h] -H host [-C community] [-D directory] [-c cachefile]\n"; print " [-m mrtgfile] [-t timeout]\n"; exit 0; } sub clean($) { # remove any characters that are dodgy in filesystems my($x) = $_[0]; $x =~ s/[:\\\/\[\]#!\*&\?\(\)\s]/-/g; return $x; } sub readstate { return if(! -r $STATEFILE); open STATE, "<$STATEFILE"; flock STATE,1; # read lock while( ) { $states{$1}=$2 if( /^(\S.*)\s*=\s*(.*)/ and $2 ); } flock STATE,8; # unlock close STATE; } sub writestate { open STATE, ">$STATEFILE"; # foreach ( keys %states ) { print STATE "$_=".$states{$_}."\n" ; } foreach ( keys %vhosts ) { next if(!$_ or !$vhosts{$_}{rrd}); print STATE "vhost-$_=".$vhosts{$_}{rrd}."\n"; print STATE "ifname-$_=".(join " ",(keys %{$vhosts{$_}{net}}))."\n" if(defined $vhosts{$_}{net}); print STATE "hbaname-$_=".(join " ",(keys %{$vhosts{$_}{hba}}))."\n" if(defined $vhosts{$_}{hba}); } close STATE; } sub dooutput { print "$MSG\n"; } # Create all RRD files for the given vhost sub createrrd($) { my($rrd) = $_[0]; my($err); print "Creating RRD #$rrd\n" if($DEBUG); RRDs::create( "$RRDDIR/$rrd-cpu.rrd", qw/RRA:AVERAGE:0.5:1:800 RRA:AVERAGE:0.25:6:800 RRA:AVERAGE:0.25:24:800 RRA:AVERAGE:0.25:288:800 RRA:MAX:0.5:1:800 RRA:MAX:0.25:6:800 RRA:MAX:0.25:24:800 RRA:MAX:0.25:288:800/, qw/DS:ds0:COUNTER:600:0:32 DS:ds1:COUNTER:600:0:32/ ); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return 1; } RRDs::create( "$RRDDIR/$rrd-mem.rrd", qw/RRA:AVERAGE:0.5:1:800 RRA:AVERAGE:0.25:6:800 RRA:AVERAGE:0.25:24:800 RRA:AVERAGE:0.25:288:800 RRA:MAX:0.5:1:800 RRA:MAX:0.25:6:800 RRA:MAX:0.25:24:800 RRA:MAX:0.25:288:800/, qw/DS:ds0:GAUGE:600:0:1024000000 DS:ds1:GAUGE:600:0:1024000000/ ); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return 1; } return 0; } sub createrrdx($$) { my($rrdno,$ifname) = @_; my($err); my($rrd) = "$rrdno-x-".clean($ifname); print "Creating RRD #$rrd\n" if($DEBUG); RRDs::create( "$RRDDIR/$rrd.rrd", qw/RRA:AVERAGE:0.5:1:800 RRA:AVERAGE:0.25:6:800 RRA:AVERAGE:0.25:24:800 RRA:AVERAGE:0.25:288:800 RRA:MAX:0.5:1:800 RRA:MAX:0.25:6:800 RRA:MAX:0.25:24:800 RRA:MAX:0.25:288:800/, qw/DS:ds0:COUNTER:600:0:10240000 DS:ds1:COUNTER:600:0:10240000/ ); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return 1; } return 0; } # Update the RRD files as per the data held in %vhosts sub updaterrd { my($err,$vh,$id); foreach $vh ( keys %vhosts ) { next if(!$vh); print "Updating vhost=$vh ID=".$vhosts{$vh}{rrd}."\n" if($DEBUG); next if(!$vhosts{$vh}{rrd}); RRDs::update ( "$RRDDIR/".$vhosts{$vh}{rrd}."-cpu.rrd", "--template", "ds0:ds1", "N:".$vhosts{$vh}{cpu}.":".$vhosts{$vh}{cpu} ) if(defined $vhosts{$vh}{cpu}); print "Updating CPU to ".$vhosts{$vh}{cpu}."\n" if(defined $vhosts{$vh}{cpu} and $DEBUG); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return; } RRDs::update ( "$RRDDIR/".$vhosts{$vh}{rrd}."-mem.rrd", "--template", "ds0:ds1", "N:".$vhosts{$vh}{mem}.":".$vhosts{$vh}{maxmem} ) if(defined $vhosts{$vh}{mem}); print "Updating MEM to ".$vhosts{$vh}{mem}."\n" if(defined $vhosts{$vh}{mem} and $DEBUG); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return; } foreach $id ( keys %{$vhosts{$vh}{net}} ) { next if( !defined $vhosts{$vh}{net}{$id}{in} ); RRDs::update ( "$RRDDIR/".$vhosts{$vh}{rrd}."-x-".clean($id).".rrd", "--template", "ds0:ds1", "N:".$vhosts{$vh}{net}{$id}{in}.":" .$vhosts{$vh}{net}{$id}{out} ); print "Updating NET:$id to ".$vhosts{$vh}{net}{$id}{in} .":".$vhosts{$vh}{net}{$id}{out}."\n" if($DEBUG); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return; } } foreach $id ( keys %{$vhosts{$vh}{hba}} ) { next if( !defined $vhosts{$vh}{hba}{$id}{in} ); RRDs::update ( "$RRDDIR/".$vhosts{$vh}{rrd}."-x-".clean($id).".rrd", "--template", "ds0:ds1", "N:".$vhosts{$vh}{hba}{$id}{in}.":" .$vhosts{$vh}{hba}{$id}{out} ); print "Updating HBA:$id to ".$vhosts{$vh}{hba}{$id}{in} .":".$vhosts{$vh}{hba}{$id}{out}."\n" if($DEBUG); $err = RRDs::error; if($err) { print "RRD Error: $err\n"; return; } } } } # Write the corresponding MRTG configuration file. sub writecfg { my( $vhname, $vhid, $vhseq, $vhno, $id, $ifseq, $hbaid ); my($down, $target); $ifseq = ""; open CFG,">$CFGFILE" or do { print "$CFGFILE: $!\n"; return; }; print CFG "# routers2 configuration for host $hostname\n"; print CFG "# THIS FILE IS AUTOGENERATED! Do not change it!\n\n"; print CFG "# NOT FOR USE IN MRTG: The data is colected by the vmgather process.\n# This is only for routers2/14all/mrtg-rrd display rules\n\n"; print CFG "Workdir: $RRDDIR\nLogformat: rrdtool\nOptions[_]: growright\n"; print CFG "routers.cgi*ShortDesc: $hostname\n"; print CFG "routers.cgi*Description: VMWare server $hostname VHosts\n"; print CFG "routers.cgi*RoutingTable: no\n"; print CFG "routers.cgi*Icon: vmware-sm.gif\n"; print CFG "routers.cgi*NoCache: yes\n"; print CFG "EnableIPv6: no\n"; print CFG "routers.cgi*Extension: Management http://$hostname/ cog-sm.gif _new noopts\n\n"; # the various per-vhost graphs foreach $vhname ( keys %vhosts ) { if( defined $lookup{$vhname} ) { $vhid = $lookup{$vhname}; } else { $vhid = -999; } $vhno = $vhosts{$vhname}{rrd}; next if(!$vhno); if( $vhid == -999 ) { $vhseq = 9999; $vhid = 9999; $down = " (Undefined)"; } elsif( $vhid < 0) { $vhseq = 8888; $vhid = 8888; $down = " (DOWN)"; } else { $vhseq = $lookup{$vhid}; $down = ""; } print CFG "\n#########################################\n"; print CFG "# VHost: $vhname $down\n"; print CFG "#########################################\n\n"; print CFG "# CPU: number of CPUs in use. CPUsec per sec\n"; print CFG "Target[$vhno-cpu]: $VMOID.3.1.2.1.3.$vhid&$VMOID.3.1.2.1.3.$vhid:$community\@$hostname\n"; print CFG "Title[$vhno-cpu]: Number of CPUs in use on $vhname $down\n"; print CFG "routers.cgi*ShortName[$vhno-cpu]: $vhname CPUs\n"; print CFG "MaxBytes[$vhno-cpu]: 32\n"; print CFG "Options[$vhno-cpu]: growright noo\n"; print CFG "Legend1[$vhno-cpu]: CPU usage\n"; print CFG "Legend3[$vhno-cpu]: Max CPU usage\n"; print CFG "YLegend[$vhno-cpu]: CPUs\n"; print CFG "ShortLegend[$vhno-cpu]:  \n"; print CFG "LegendI[$vhno-cpu]: CPUs:\n"; print CFG "routers.cgi*InMenu[$vhno-cpu]: no\n"; print CFG "routers.cgi*InOut[$vhno-cpu]: no\n"; print CFG "routers.cgi*InSummary[$vhno-cpu]: no\n"; print CFG "routers.cgi*InCompact[$vhno-cpu]: no\n"; print CFG "routers.cgi*Options[$vhno-cpu]: scaled fixunit nomax nopercent\n"; print CFG "routers.cgi*Graph[$vhno-cpu]: CPU total\n"; print CFG "routers.cgi*Graph[$vhno-cpu]: runningCPU total\n" if($vhid < 8888); print CFG "routers.cgi*Graph[$vhno-cpu]: definedCPU total\n" if($vhid < 9999); print CFG "# Memory usage\n"; print CFG "Target[$vhno-mem]: $VMOID.3.2.4.1.4.$vhid&$VMOID.3.2.4.1.3.$vhid:$community\@$hostname\n"; print CFG "Title[$vhno-mem]: Memory in use on $vhname $down\n"; print CFG "routers.cgi*ShortName[$vhno-mem]: $vhname Memory\n"; print CFG "MaxBytes[$vhno-mem]: 10240000000\n"; print CFG "Factor[$vhno-mem]: 1024\n"; # because we retrieve K print CFG "Options[$vhno-mem]: growright gauge\n"; print CFG "Legend1[$vhno-mem]: Memory usage\n"; print CFG "Legend2[$vhno-mem]: Memory available\n"; print CFG "Legend3[$vhno-mem]: Max memory usage\n"; print CFG "Legend4[$vhno-mem]: Max memory available\n"; print CFG "YLegend[$vhno-mem]: Bytes\n"; print CFG "ShortLegend[$vhno-mem]: b\n"; print CFG "LegendI[$vhno-mem]: used :\n"; print CFG "LegendO[$vhno-mem]: avail:\n"; print CFG "routers.cgi*InMenu[$vhno-mem]: no\n"; print CFG "routers.cgi*InOut[$vhno-mem]: no\n"; print CFG "routers.cgi*InSummary[$vhno-mem]: no\n"; print CFG "routers.cgi*InCompact[$vhno-mem]: no\n"; print CFG "routers.cgi*Options[$vhno-mem]: scaled nomax nopercent\n"; print CFG "routers.cgi*Graph[$vhno-mem]: MEM total noo\n"; print CFG "routers.cgi*Graph[$vhno-mem]: runningMEM total noo\n" if($vhid < 8888); print CFG "routers.cgi*Graph[$vhno-mem]: definedMEM total noo\n" if($vhid < 9999); print CFG "# Network usage\n"; foreach $id ( keys %{$vhosts{$vhname}{net}} ) { $target = "$vhno-x-".clean($id); print CFG "Target[$target]: $VMOID.3.4.1.7.$ifseq&$VMOID.3.4.1.9.$ifseq:$community\@$hostname\n"; print CFG "Title[$target]: Network usage on $vhname/$id $down\n"; print CFG "routers.cgi*ShortName[$target]: $vhname:$id\n"; print CFG "MaxBytes[$target]: 10240000000\n"; print CFG "Options[$target]: growright bits\n"; print CFG "routers.cgi*InMenu[$target]: no\n"; print CFG "routers.cgi*InOut[$target]: no\n"; print CFG "routers.cgi*InSummary[$target]: no\n"; print CFG "routers.cgi*InCompact[$target]: no\n"; print CFG "routers.cgi*Icon[$target]: interface-sm.gif\n"; print CFG "routers.cgi*Options[$target]: scaled nomax nopercent\n"; print CFG "routers.cgi*Graph[$target]: $id-in total noo\n"; print CFG "routers.cgi*Icon[$id-in]: interface-sm.gif\n"; print CFG "routers.cgi*ShortName[$id-in]: $id [IN] (All VMs)\n"; print CFG "routers.cgi*Title[$id-in]: $id inbound on $hostname\n"; print CFG "routers.cgi*InSummary[$id-in]: no\n"; print CFG "routers.cgi*InMenu[$id-in]: no\n"; print CFG "routers.cgi*Summary[$id-in]: Network nodetails\n"; print CFG "routers.cgi*Graph[$target]: $id-out total noi\n"; print CFG "routers.cgi*Icon[$id-out]: interface-sm.gif\n"; print CFG "routers.cgi*ShortName[$id-out]: $id [OUT] (All VMs)\n"; print CFG "routers.cgi*Title[$id-out]: $id outbound on $hostname\n"; print CFG "routers.cgi*InSummary[$id-out]: no\n"; print CFG "routers.cgi*InMenu[$id-out]: no\n"; print CFG "routers.cgi*Summary[$id-out]: Network nodetails\n"; print CFG "routers.cgi*Graph[$target]: $id total\n"; print CFG "routers.cgi*Icon[$id]: interface2-sm.gif\n"; print CFG "routers.cgi*ShortName[$id]: $id\n"; print CFG "routers.cgi*GraphStyle[$id]: mirror\n"; print CFG "routers.cgi*Title[$id]: $id on $hostname\n"; print CFG "routers.cgi*InSummary[$id]: yes\n"; # if($vhid < 8888 ) { # print CFG "routers.cgi*Graph[$target]: running-$id-in total noo\n"; # print CFG "routers.cgi*Graph[$target]: running-$id-out total noi\n"; # print CFG "routers.cgi*Icon[running-$id-in]: interface-sm.gif\n"; # print CFG "routers.cgi*ShortName[running-$id-in]: $id [IN] (Running VMs)\n"; # print CFG "routers.cgi*Title[running-$id-in]: $id inbound on $hostname (Running VMs)\n"; # print CFG "routers.cgi*InSummary[running-$id-in]: yes\n"; # print CFG "routers.cgi*Icon[running-$id-out]: interface-sm.gif\n"; #print CFG "routers.cgi*ShortName[running-$id-out]: $id [OUT] (Running VMs)\n"; # print CFG "routers.cgi*Title[running-$id-out]: $id outbound on $hostname (Running VMs)\n"; # print CFG "routers.cgi*InSummary[running-$id-out]: yes\n"; # } } print CFG "# HBA usage\n"; foreach $id ( keys %{$vhosts{$vhname}{hba}} ) { $target = "$vhno-x-".clean($id); print CFG "Target[$target]: $VMOID.3.3.1.6.$ifseq&$VMOID.3.3.1.8.$ifseq:$community\@$hostname\n"; print CFG "Title[$target]: HBA usage on $vhname/$id $down\n"; print CFG "routers.cgi*ShortName[$target]: $vhname:$id\n"; print CFG "MaxBytes[$target]: 10240000000\n"; print CFG "Options[$target]: growright bits\n"; print CFG "routers.cgi*InMenu[$target]: no\n"; print CFG "routers.cgi*InOut[$target]: no\n"; print CFG "routers.cgi*InSummary[$target]: no\n"; print CFG "routers.cgi*InCompact[$target]: no\n"; print CFG "routers.cgi*Options[$target]: scaled nomax nopercent\n"; print CFG "routers.cgi*Icon[$target]: disk-sm.gif\n"; print CFG "routers.cgi*Graph[$target]: $id-in total noo\n"; print CFG "routers.cgi*Icon[$id-in]: disk-sm.gif\n"; print CFG "routers.cgi*ShortName[$id-in]: $id [IN] (All VMs)\n"; print CFG "routers.cgi*Title[$id-in]: $id inbound on $hostname\n"; print CFG "routers.cgi*InSummary[$id-in]: no\n"; print CFG "routers.cgi*InMenu[$id-in]: no\n"; print CFG "routers.cgi*Summary[$id-in]: HBAs nodetails\n"; print CFG "routers.cgi*Graph[$target]: $id-out total noi\n"; print CFG "routers.cgi*Icon[$id-out]: disk-sm.gif\n"; print CFG "routers.cgi*ShortName[$id-out]: $id [OUT] (All VMs)\n"; print CFG "routers.cgi*Title[$id-out]: $id outbound on $hostname\n"; print CFG "routers.cgi*InSummary[$id-out]: no\n"; print CFG "routers.cgi*InMenu[$id-out]: no\n"; print CFG "routers.cgi*Summary[$id-out]: HBAs nodetails\n"; $hbaid=$id; $hbaid=~s/:.*$//; print CFG "routers.cgi*Graph[$target]: $hbaid total\n"; print CFG "routers.cgi*Icon[$hbaid]: disk-sm.gif\n"; print CFG "routers.cgi*ShortName[$hbaid]: $hbaid\n"; print CFG "routers.cgi*Title[$hbaid]: $hbaid on $hostname\n"; print CFG "routers.cgi*InSummary[$hbaid]: yes\n"; print CFG "routers.cgi*InMenu[$hbaid]: yes\n"; print CFG "routers.cgi*GraphStyle[$hbaid]: mirror\n"; # if($vhid < 8888 ) { # print CFG "routers.cgi*Graph[$target]: running-$id-in total noo\n"; # print CFG "routers.cgi*Graph[$target]: running-$id-out total noi\n"; # print CFG "routers.cgi*Icon[running-$id-in]: disk-sm.gif\n"; # print CFG "routers.cgi*ShortName[running-$id-in]: $id [IN] (Running VMs)\n"; # print CFG "routers.cgi*Title[running-$id-in]: $id inbound on $hostname (Running VMs)\n"; # print CFG "routers.cgi*InSummary[running-$id-in]: yes\n"; # print CFG "routers.cgi*Icon[running-$id-out]: disk-sm.gif\n"; #print CFG "routers.cgi*ShortName[running-$id-out]: $id [OUT] (Running VMs)\n"; # print CFG "routers.cgi*Title[running-$id-out]: $id outbound on $hostname (Running VMs)\n"; # print CFG "routers.cgi*InSummary[running-$id-out]: yes\n"; # } } } # Now the options to the sumary graphs in routers2 print CFG "routers.cgi*Icon[CPU]: chip-sm.gif\n"; print CFG "routers.cgi*ShortName[CPU]: CPU (All VMs)\n"; print CFG "routers.cgi*Title[CPU]: Guest OS CPU usage on $hostname\n"; print CFG "routers.cgi*InSummary[CPU]: no \n"; print CFG "routers.cgi*Options[CPU]: nototal \n"; print CFG "routers.cgi*Icon[runningCPU]: chip-sm.gif\n"; print CFG "routers.cgi*ShortName[runningCPU]: CPU (Running VMs)\n"; print CFG "routers.cgi*Title[runningCPU]: Guest OS CPU usage on $hostname\n"; print CFG "routers.cgi*Options[runningCPU]: nototal \n"; print CFG "routers.cgi*InSummary[runningCPU]: no \n"; print CFG "routers.cgi*Icon[definedCPU]: chip-sm.gif\n"; print CFG "routers.cgi*ShortName[definedCPU]: CPU (Defined VMs)\n"; print CFG "routers.cgi*Title[definedCPU]: Guest OS CPU usage on $hostname\n"; print CFG "routers.cgi*InSummary[definedCPU]: yes\n"; print CFG "routers.cgi*Options[definedCPU]: nototal \n"; print CFG "routers.cgi*Icon[MEM]: chip-sm.gif\n"; print CFG "routers.cgi*ShortName[MEM]: Memory (All VMs)\n"; print CFG "routers.cgi*Title[MEM]: Guest OS Memory usage on $hostname\n"; print CFG "routers.cgi*InSummary[MEM]: no \n"; print CFG "routers.cgi*Options[MEM]: nototal \n"; print CFG "routers.cgi*Icon[runningMEM]: chip-sm.gif\n"; print CFG "routers.cgi*ShortName[runningMEM]: Memory (Running VMs)\n"; print CFG "routers.cgi*Title[runningMEM]: Guest OS Memory usage on $hostname\n"; print CFG "routers.cgi*InSummary[runningMEM]: no \n"; print CFG "routers.cgi*Options[runningMEM]: nototal \n"; print CFG "routers.cgi*Icon[definedMEM]: chip-sm.gif\n"; print CFG "routers.cgi*ShortName[definedMEM]: Memory (Defined VMs)\n"; print CFG "routers.cgi*Title[definedMEM]: Guest OS Memory usage on $hostname\n"; print CFG "routers.cgi*InSummary[definedMEM]: yes\n"; print CFG "routers.cgi*Options[definedMEM]: nototal \n"; close CFG; } sub getvmid { print "(snmp lookup of vmids)\n" if($DEBUG); ($snmp,$snmperr) = Net::SNMP->session( -hostname=>$hostname, -community=>$community, -timeout=>$TIMEOUT ); if($snmperr) { print "($snmperr)\n" if($DEBUG); $MSG = "Error: $snmperr"; dooutput; # exit exit(0); } $resp = $snmp->get_table( -baseoid=>"$VMOID.2.1.1"); if(!$resp) { $MSG = "Error: Unable to retrieve SNMP VHost data"; exit(0); } foreach my $oid ( keys %$resp ) { $oid =~ /(\d+)\.(\d+)$/; if( $1 == 2 ) { $lookup{$2} = $resp->{$oid}; $lookup{$resp->{$oid}} = $resp->{"$VMOID.2.1.1.7.$2"}; $lookup{$resp->{"$VMOID.2.1.1.7.$2"}} = $2 if($resp->{"$VMOID.2.1.1.7.$2"} > 0); print "$2: ".$lookup{$2}.": ".$lookup{$lookup{$2}}."\n" if($DEBUG>1); } } print "Table retrieved\n" if($DEBUG); } sub readnet { my($found); my($vmname,$vmid,$ifid,$ifname); $resp = $snmp->get_table( -baseoid=>"$VMOID.3.4.1"); if(!$resp) { $MSG = "Error: Unable to retrieve SNMP Network data"; return; } foreach my $oid ( keys %$resp ) { $oid =~ /(\d+)\.(\d+)$/; # Type, index. if( $1 == 3 ) { # Running VMID. There may be more than one! $ifid = $2; $vmid = $resp->{$oid}; next if(!defined $lookup{$vmid}); $vmname = $lookup{$lookup{$vmid}}; next if(!$vmname or !defined $vhosts{$vmname}); $ifname = $resp->{"$VMOID.3.4.1.2.$ifid"}; next if(!$ifname); $vhosts{$vmname}{net} = {} if(!defined $vhosts{$vmname}{net}); if(!defined $vhosts{$vmname}{net}{$ifname}) { createrrdx($vhosts{$vmname}{rrd},$ifname); } $vhosts{$vmname}{net}{$ifname} = { ifname=>$ifname, in=>($resp->{"$VMOID.3.4.1.7.$ifid"}*1024), out=>($resp->{"$VMOID.3.4.1.9.$ifid"}*1024) }; print "NET: $vmname($ifname): " .$vhosts{$vmname}{net}{$ifname}{in}."/" .$vhosts{$vmname}{net}{$ifname}{out}."\n" if($DEBUG); } } } sub readhba { my($found); my($vmname,$vmid,$ifid,$ifname); $resp = $snmp->get_table( -baseoid=>"$VMOID.3.3.1"); if(!$resp) { $MSG = "Error: Unable to retrieve SNMP HBA data"; return; } foreach my $oid ( keys %$resp ) { $oid =~ /(\d+)\.(\d+)$/; # Type, index. if( $1 == 3 ) { # Running VMID. There may be more than one! $ifid = $2; $vmid = $resp->{$oid}; next if(!defined $lookup{$vmid}); $vmname = $lookup{$lookup{$vmid}}; next if(!$vmname or !defined $vhosts{$vmname}); $ifname = $resp->{"$VMOID.3.3.1.2.$ifid"}; next if(!$ifname); $vhosts{$vmname}{hba} = {} if(!defined $vhosts{$vmname}{hba}); if(!defined $vhosts{$vmname}{hba}{$ifname}) { createrrdx($vhosts{$vmname}{rrd},$ifname); } $vhosts{$vmname}{hba}{$ifname} = { ifname=>$ifname, in=>($resp->{"$VMOID.3.3.1.6.$ifid"}*1024), out=>($resp->{"$VMOID.3.3.1.8.$ifid"}*1024) }; print "HBA: $vmname:$ifname = " .$vhosts{$vmname}{hba}{$ifname}{in}."/" .$vhosts{$vmname}{hba}{$ifname}{out}."\n" if($DEBUG); } } } sub readcpu { my($k,@k); @k = (); foreach ( keys %lookup ) { push @k, "$VMOID.3.1.2.1.3.".$_ if( /^\d+$/ and $_>99); #print "ID: $_\n" if($DEBUG); } $resp = $snmp->get_request( -varbindlist=>\@k ); if( $resp ) { foreach( keys %$resp ) { if( /\.(\d+)$/ ) { $vhosts{$lookup{$lookup{$1}}}{cpu} = $resp->{$_}; if($DEBUG) { print "CPU: $1 -> ".$lookup{$lookup{$1}}." = ".$resp->{$_}."\n"; } } } } else { $MSG = "Unable to retrieve CPU statistics for ESX server: ".$snmp->error; } } sub readmem { my($k,@k); @k = (); foreach ( keys %lookup ) { push @k, "$VMOID.3.2.4.1.3.$_", "$VMOID.3.2.4.1.4.$_" if( /^\d+$/ and $_>99); } $resp = $snmp->get_request( -varbindlist=>\@k ); if( $resp ) { foreach( keys %$resp ) { if( /\.3\.(\d+)$/ ) { $vhosts{$lookup{$lookup{$1}}}{maxmem} = $resp->{$_} * 1024; print "MEM: $1 -> ".$lookup{$lookup{$1}}." = ".$resp->{$_}." Kb\n" if($DEBUG); } elsif( /\.4\.(\d+)$/ ) { $vhosts{$lookup{$lookup{$1}}}{mem} = $resp->{$_}; } } } else { $MSG = "Unable to retrieve memory statistics for ESX server: ".$snmp->error; } } ########################################################################### getopts('t:hdH:c:C:m:D:'); $hostname = $opt_H if($opt_H); $TIMEOUT = $opt_t if($opt_t); $community = $opt_C if($opt_C); $community = 'public' if(!$community); $DEBUG = 1 if($opt_d); $RRDDIR = $opt_D if($opt_D); $RRDDIR = '/tmp' if(!$RRDDIR); $STATEFILE = $opt_c if($opt_c); $STATEFILE = "$RRDDIR/cache" if(!$STATEFILE); $CFGFILE = $opt_m if($opt_m); $CFGFILE = "$RRDDIR/$hostname.cfg" if(!$CFGFILE); dohelp if($opt_h); if(!$hostname) { $MSG = "No ESX server hostname specified with -H"; dooutput; exit 0; } readstate; # get the previous state getvmid; # get list of VMs, also opens SNMP object $maxrrd = 0; foreach ( keys %states ) { if( /^vhost-(.*)/ ) { # print "vhost = $1\n" if($DEBUG); $vhosts{$1} = {} if(!defined $vhosts{$1} ); $vhosts{$1}{name} = $1; $vhosts{$1}{rrd} = $states{$_}; $maxrrd = $states{$_} if($states{$_}>$maxrrd); next; } if( /^ifname-(.*)/ ) { $vh = $1; # print "IF list = ".$states{$_}."\n" if($DEBUG); @list = split / /,$states{$_}; $vhosts{$vh} = {} if(!defined $vhosts{$vh} ); $vhosts{$vh}{net} = {}; foreach ( @list ) { # print "Already know of interface $_\n" if($DEBUG); $vhosts{$vh}{net}{$_} = { ifname=>$_ }; } next; } if( /^hbaname-(.*)/ ) { $vh = $1; # print "HBA list = ".$states{$_}."\n" if($DEBUG); @list = split / /,$states{$_}; $vhosts{$vh} = {} if(!defined $vhosts{$vh} ); $vhosts{$vh}{hba} = {}; foreach ( @list ) { # print "Already know of HBA $_\n" if($DEBUG); $vhosts{$vh}{hba}{$_} = { ifname=>$_ }; } next; } print "Bad key: $_\n"; } print "Highest index is $maxrrd\n" if($DEBUG); foreach ( keys %lookup ) { next if( /^-?\d+$/ ); if(!defined $vhosts{$_} ) { $maxrrd++; $vhosts{$_} = { name=>$_, rrd=>$maxrrd }; createrrd $vhosts{$_}{rrd}; } # if( $lookup{$_} == -1 ) { # # vhost is down # $vhosts{$_}{mem} = 'U'; # $vhosts{$_}{maxmem} = 'U'; # $vhosts{$_}{cpu} = 'U'; # } } print "Highest index is $maxrrd\n" if($DEBUG); readcpu; readmem; readnet; readhba; # get all the details $snmp->close; # Done with querying SNMP updaterrd; # update the RRDs with the details in the vhosts hash, if held writestate; # output all the config details writecfg; # create the cfg file for routers2 to display this dooutput; # Print any message exit 0;