1#!@@PERL@@ -w 2# -*- perl -*- 3 4=head1 NAME 5 6snmp__df_ram - Plugin to check ram/flash usage of a remote host via SNMP 7 8=head1 CONFIGURATION 9 10The following environment variables are used 11 12 host - SNMP hostname to probe (default undef) 13 port - SNMP port to use (default 161) 14 community - SNMP community string (default "public") 15 16=head1 AUTHOR 17 18=encoding UTF-8 19 20Copyright (C) 2011 Erik Inge Bolsø, Redpill Linpro AS for OSL 21 22Based on snmp__df (C) 2004 Jimmy Olsen 23 24=head1 LICENSE 25 26This program is free software; you can redistribute it and/or 27modify it under the terms of the GNU General Public License 28as published by the Free Software Foundation; version 2 dated June, 291991. 30 31This program is distributed in the hope that it will be useful, 32but WITHOUT ANY WARRANTY; without even the implied warranty of 33MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 34GNU General Public License for more details. 35 36You should have received a copy of the GNU General Public License 37along with this program; if not, write to the Free Software 38Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 39 40=head1 MAGIC MARKERS 41 42 #%# family=snmpauto 43 #%# capabilities=snmpconf 44 45=cut 46 47use strict; 48use Net::SNMP; 49 50my $MAXLABEL = 20; 51 52my $host = $ENV{host} || undef; 53my $port = $ENV{port} || 161; 54my $community = $ENV{community} || "public"; 55 56my $response; 57 58if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") 59{ 60 print "require 1.3.6.1.2.1.25.2.3.1.1.\n"; 61 print "require 1.3.6.1.2.1.25.2.3.1.2. 1.3.6.1.2.1.25.2.1.(1|2|3|9)\n"; # Type=Other/Ram/VirtualMemory/FlashMemory 62 print "require 1.3.6.1.2.1.25.2.3.1.5. [1-9]\n"; # Size > 0 63 exit 0; 64} 65 66if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_df_ram$/) 67{ 68 $host = $1; 69 if ($host =~ /^([^:]+):(\d+)$/) 70 { 71 $host = $1; 72 $port = $2; 73 } 74} 75elsif (!defined($host)) 76{ 77 print "# Debug: $0 -- $1\n" if $Munin::Plugin::SNMP::DEBUG; 78 die "# Error: couldn't understand what I'm supposed to monitor."; 79} 80 81# Disk level 82my $hrDeviceType = "1.3.6.1.2.1.25.3.2.1.2."; # Should be iso.3.6.1.2.1.25.3.1.6 (DiskStorage) 83my $hrDiskStorageRemoveble = "1.3.6.1.2.1.25.3.6.1.3."; # Should be false (2). 84 # Windows reports 0. 85my $hrDiskStorageCapacity = "1.3.6.1.2.1.25.3.6.1.4."; # Should be more than 0 86 87# Partition level 88my $hrPartitionFSIndex = "1.3.6.1.2.1.25.3.7.1.5."; # Should be more than 0 89my $hrFSMountPoint = "1.3.6.1.2.1.25.3.8.1.2."; # Used to look up filesystem 90 91# Filesystem level 92my $hrStorageType = "1.3.6.1.2.1.25.2.3.1.2."; # Backup for hrFS* 93my $hrStorageDesc = "1.3.6.1.2.1.25.2.3.1.3."; # Used as key from partitions 94my $hrStorageSize = "1.3.6.1.2.1.25.2.3.1.5."; # Data point 1 95my $hrStorageUsed = "1.3.6.1.2.1.25.2.3.1.6."; # Data point 2 96 97 98my ($session, $error) = Net::SNMP->session( 99 -hostname => $host, 100 -community => $community, 101 -port => $port 102 ); 103 104if (!defined ($session)) 105{ 106 die "Croaking: $error"; 107} 108 109# First we want to find the harddisks... 110my $correct_capacity = get_by_regex ($session, $hrDiskStorageCapacity, "[1-9]"); 111my $correct_type = get_by_regex ($session, $hrDeviceType, "^1.3.6.1.2.1.25.3.1.6\$"); 112my $correct_removable = get_by_regex ($session, $hrDiskStorageRemoveble, "^(0|2)\$"); 113 114my @keep = (); 115 116foreach my $id (keys %$correct_capacity) 117{ 118 if (exists $correct_type->{$id} and 119 exists $correct_removable->{$id}) 120 { 121 push (@keep, $id); 122 } 123} 124 125print "# Kept: ", join (',', @keep), "\n" if $Munin::Plugin::SNMP::DEBUG; 126 127# Then we take a look at the partitions... 128 129my %partitions; 130 131foreach my $kept (@keep) # For each disk... 132{ 133 my $parts = get_by_regex ($session, $hrPartitionFSIndex . "$kept.", "[1-9]"); 134 135 foreach my $partition (keys %$parts) 136 { 137 my $mp = get_single ($session, $hrFSMountPoint . $partition); 138 $partitions{$mp}{partition} = $partition; 139 print "# Added partition \"$mp\" as $partition...\n" if $Munin::Plugin::SNMP::DEBUG 140 } 141} 142 143my $stor_id; 144 145my $foundpartitions = keys %partitions; 146 147if ($foundpartitions == 0 or defined $partitions{""}) 148{ # Oh bugger. Some (or all) mountpoints were undeterminable. The backup 149 # solution is to just graph everything that claims to be a Other/Ram/VirtualMemory/FlashMemory, 150 # without checking if it's removable etc 151 152 print "# Unable to map mountpoints from filesystems to storages. Bugger.\n" if $Munin::Plugin::SNMP::DEBUG; 153 $stor_id = get_by_regex ($session, $hrStorageType, "1.3.6.1.2.1.25.2.1.(1|2|3|9)"); 154 %partitions = (); 155 156 foreach my $id (keys %$stor_id) 157 { 158 my $part = get_single ($session, $hrStorageDesc . $id); 159 my $spart = $part; 160 $spart =~ s/:\\ Label:.*/:/; 161 $partitions{$spart}{storage} = $id; 162 $partitions{$spart}{extinfo} = $part; 163 $stor_id->{$id} = $spart; 164 } 165} else 166{ # Get the ones we're sure about 167 168 $stor_id = get_by_regex ($session, $hrStorageDesc, '(^'.join('$|^',keys(%partitions)).'$)'); 169} 170 171if (defined $ARGV[0] and $ARGV[0] eq "config") 172{ 173 print "host_name $host\n" unless $host eq 'localhost'; 174 print "graph_title Memory usage in percent\n"; 175 print "graph_args --upper-limit 100 -l 0\n"; 176 print "graph_vlabel %\n"; 177 print "graph_category system\n"; 178 print "graph_info This graph shows memory usage in percent.\n"; 179 180 foreach my $part (sort(keys %partitions)) 181 { 182 print (&get_name_by_mp ($part), ".label "); 183 print (length($part)<=$MAXLABEL ? $part : "...".substr($part,-($MAXLABEL-3))); 184 print ("\n"); 185 print (&get_name_by_mp ($part), ".warning 92\n"); 186 print (&get_name_by_mp ($part), ".critical 98\n"); 187 print (&get_name_by_mp ($part), ".info Usage for ". ($partitions{$part}{extinfo}||$part)."\n"); 188 } 189 exit 0; 190} 191 192foreach my $storage (keys %$stor_id) 193{ 194 $partitions{$stor_id->{$storage}}{storage} = $storage; 195 $partitions{$stor_id->{$storage}}{size} = get_single ($session, $hrStorageSize . $storage); 196 $partitions{$stor_id->{$storage}}{used} = get_single ($session, $hrStorageUsed . $storage); 197} 198 199foreach my $part (keys %partitions) 200{ 201 print &get_name_by_mp ($part), ".value ", ($partitions{$part}{used}*100/$partitions{$part}{size}), "\n" 202 if (defined $partitions{$part}{used} && $partitions{$part}{size}); 203} 204 205sub get_single 206{ 207 my $handle = shift; 208 my $oid = shift; 209 210 print "# Getting single $oid..." if $Munin::Plugin::SNMP::DEBUG; 211 212 $response = $handle->get_request ($oid); 213 214 if (!defined $response->{$oid}) 215 { 216 print "undef\n" if $Munin::Plugin::SNMP::DEBUG; 217 return undef; 218 } 219 else 220 { 221 print "\"$response->{$oid}\"\n" if $Munin::Plugin::SNMP::DEBUG; 222 return $response->{$oid}; 223 } 224} 225 226sub get_by_regex 227{ 228 my $handle = shift; 229 my $oid = shift; 230 my $regex = shift; 231 my $result = {}; 232 my $num = 0; 233 my $ret = $oid . "0"; 234 my $response; 235 236 print "# Starting browse of $oid...\n" if $Munin::Plugin::SNMP::DEBUG; 237 238 while (1) 239 { 240 if ($num == 0) 241 { 242 print "# Checking for $ret...\n" if $Munin::Plugin::SNMP::DEBUG; 243 $response = $handle->get_request ($ret); 244 } 245 if ($num or !defined $response) 246 { 247 print "# Checking for sibling of $ret...\n" if $Munin::Plugin::SNMP::DEBUG; 248 $response = $handle->get_next_request ($ret); 249 } 250 if (!$response) 251 { 252 return undef; 253 } 254 my @keys = keys %$response; 255 $ret = $keys[0]; 256 print "# Analyzing $ret (compared to $oid)...\n" if $Munin::Plugin::SNMP::DEBUG; 257 last unless ($ret =~ /^$oid/); 258 $num++; 259 next unless ($response->{$ret} =~ /$regex/); 260 @keys = split (/\./, $ret); 261 $result->{$keys[-1]} = $response->{$ret};; 262 print "# Index $num: ", $keys[-1], " (", $response->{$ret}, ")\n" if $Munin::Plugin::SNMP::DEBUG; 263 }; 264 return $result; 265} 266 267sub get_name_by_mp 268{ 269 my $mp = shift; 270 $mp =~ s/[^a-z0-9_]/_/gi; 271 $mp =~ tr/A-Z/a-z/; 272 return "p" . $mp; 273} 274