#!/usr/local/bin/perl -w # check_snmp_printer - check for printer status via snmp # Supports both standard PRINT-MIB (RFC-1759) and HP Enterprise print-mib # that is supported by some of the older JetDirect interfaces # Acknowledgements: # the JetDirect code is taken from check_hpjd.c by Ethan Galstad # # The idea for the plugin (as well as some code) were taken from Jim # Trocki's pinter alert script in his "mon" utility, found at # http://www.kernel.org/software/mon # # Notes: # 'JetDirect' is copyrighted by Hewlett-Packard # # # License Information: # 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; either version 2 of the License, or # (at your option) any later version. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # ############################################################################ # # TODO: Query HOST-RESOURCE MIB for a quick status # # hrPrinterStatus = .1.3.6.1.2.1.25.3.5.1; # hrPrinterDetectedErrorState = .1.3.6.1.2.1.25.3.5.1.2 # # hrPrinterStatus OBJECT-TYPE # SYNTAX INTEGER { # other(1), # unknown(2), # idle(3), # printing(4), # warmup(5) # } # # hrPrinterDetectedErrorState OBJECT-TYPE # SYNTAX OCTET STRING # MAX-ACCESS read-only # STATUS current # DESCRIPTION # "This object represents any error conditions detected # by the printer. The error conditions are encoded as # bits in an octet string, with the following # definitions: # # Condition Bit # # # lowPaper 0 # # noPaper 1 # lowToner 2 # noToner 3 # doorOpen 4 # jammed 5 # offline 6 # serviceRequested 7 # inputTrayMissing 8 # outputTrayMissing 9 # markerSupplyMissing 10 # outputNearFull 11 # outputFull 12 # inputTrayEmpty 13 # overduePreventMaint 14 # # # use strict; use Getopt::Long; use vars qw($opt_V $opt_h $opt_H $opt_P $opt_t $opt_d $session $error $answer $key $response $PROGNAME $port $hostname ); use lib "utils.pm"; use utils qw(%ERRORS &print_revision &support &usage ); use Net::SNMP; sub print_help (); sub print_usage (); $ENV{'PATH'}=''; $ENV{'BASH_ENV'}=''; $ENV{'ENV'}=''; # defaults my $ptype = 1; # to standard RFC printer type my $state = $ERRORS{'UNKNOWN'}; my $community = "public"; my $snmp_version = 1; my $port = 161; Getopt::Long::Configure('bundling'); GetOptions ("d" => \$opt_d, "debug" => \$opt_d, "V" => \$opt_V, "version" => \$opt_V, "P=s" => \$opt_P, "Printer=s" => \$opt_P, # printer type - HP or RFC "v=i" => \$snmp_version, "snmp_version=i" => \$snmp_version, "p=i" => \$port, "port=i" => \$port, "C=s" => \$community,"community=s" => \$community, "h" => \$opt_h, "help" => \$opt_h, "H=s" => \$opt_H, "hostname=s" => \$opt_H); $PROGNAME = "check_snmp_printer"; if ($opt_V) { print_revision($PROGNAME,'$Revision: 1.1 $'); exit $ERRORS{'OK'}; } if ($opt_h) {print_help(); exit $ERRORS{'OK'};} unless (defined $opt_H) { print "No target hostname specified\n"; exit $ERRORS{"UNKNOWN"}; } $hostname = $opt_H; if (! utils::is_hostname($hostname)){ usage(" $hostname did not match pattern\n"); exit $ERRORS{"UNKNOWN"}; } if (defined $opt_P) { if ($opt_P eq "HP" ) { $ptype = 2; }elsif ($opt_P eq "RFC" ) { $ptype = 1; }else{ print "Only \"HP\" and \"RFC\" are supported as printer options at this time.\n"; exit $ERRORS{"UNKNOWN"}; } } if ( $snmp_version =~ /[12]/ ) { ($session, $error) = Net::SNMP->session( -hostname => $hostname, -community => $community, -port => $port, -version => $snmp_version ); if (!defined($session)) { $state='UNKNOWN'; $answer=$error; print ("$state: no session - $answer\n"); exit $ERRORS{$state}; } print "Opened session|" if (defined ($opt_d)); }elsif ( $snmp_version =~ /3/ ) { $state='UNKNOWN'; print ("$state: No support for SNMP v3 yet\n"); exit $ERRORS{$state}; }else{ $state='UNKNOWN'; print ("$state: No support for SNMP v$snmp_version yet\n"); exit $ERRORS{$state}; } ### main logic if ( $ptype == 1 ) { # STD MIB print "STD-MIB|" if (defined ($opt_d)); my %snmp_response; my $snmp_index; my $col_oid; my %std_mib_inst_count ; my %std_mib_instances; my $display; my $inst; my $group; #### RFC1759 MIB OIDS # sub-unit status - textual convention my $subunit_status; # integer from 0-126 # column oid - not instances my %std_mib = ( std_mib_input_status => ".1.3.6.1.2.1.43.8.2.1.11", # 2 element index std_mib_input_name => ".1.3.6.1.2.1.43.8.2.1.13", std_mib_output_remaining_capacity => ".1.3.6.1.2.1.43.9.2.1.5", std_mib_output_status => ".1.3.6.1.2.1.43.9.2.1.6", std_mib_marker_tech => ".1.3.6.1.2.1.43.10.2.1.2", std_mib_marker_counter_unit => ".1.3.6.1.2.1.43.10.2.1.3", std_mib_marker_life_count => ".1.3.6.1.2.1.43.10.2.1.4", std_mib_marker_status => ".1.3.6.1.2.1.43.10.2.1.15", std_mib_supplies_type => ".1.3.6.1.2.1.43.11.1.1.5", std_mib_supplies_level => ".1.3.6.1.2.1.43.11.1.1.9", std_mib_media_path_type => ".1.3.6.1.2.1.43.13.4.1.9", std_mib_media_path_status => ".1.3.6.1.2.1.43.13.4.1.11", std_mib_status_display => ".1.3.6.1.2.1.43.16.5.1.2", # 2 element index std_mib_alert_sev_level => ".1.3.6.1.2.1.43.18.1.1.2", std_mib_alert_grp => ".1.3.6.1.2.1.43.18.1.1.4", std_mib_alert_location => ".1.3.6.1.2.1.43.18.1.1.5", ); my %std_mib_marker_tech = ( 1 => "other", 2 => "unknown", 3 => "electrophotographicLED", 4 => "electrophotographicLaser", 5 => "electrophotographicOther", 6 => "impactMovingHeadDotMatrix9pin", 7 => "impactMovingHeadDotMatrix24pin", 8 => "impactMovingHeadDotMatrixOther", 9 => "impactMovingHeadFullyFormed", 10 => "impactBand", 11 => "impactOther", 12 => "inkjectAqueous", 13 => "inkjetSolid", 14 => "inkjetOther", 15 => "pen", 16 => "thermalTransfer", 17 => "thermalSensitive", 18 => "thermalDiffusion", 19 => "thermalOther", 20 => "electroerosion", 21 => "electrostatic", 22 => "photographicMicrofiche", 23 => "photographicImagesetter", 24 => "photographicOther", 25 => "ionDeposition", 26 => "eBeam", 27 => "typesetter", ); my %std_mib_marker_counter_units = ( 3 => "tenThousandthsOfInches", 4 => "micrometers", 5 => "characters", 6 => "lines", 7 => "impressions", 8 => "sheets", 9 => "dotRow", 11 => "hours", 16 => "feet", 17 => "meters", ); my %std_mib_alert_groups = ( 1 => "unspecifiedOther", 3 => "printerStorageMemory", # hostResourcesMIBStorageTable 4 => "internalDevice", # hostResourcesMIBDeviceTable 5 => "generalPrinter", 6 => "cover", 7 => "localization", 8 => "input", 9 => "output", 10 => "marker", 11 => "markerSupplies", 12 => "markerColorant", 13 => "mediaPath", 14 => "connectionChannel", 15 => "interpreter", 16 => "consoleDisplayBuffer", 17 => "consoleLights", ); my %std_mib_prt_alert_code = ( 1 => "other", # ok if on power save 2 => "unknown", # -- codes common to serveral groups 3 => "coverOpen", 4 => "coverClosed", 5 => "interlockOpen", 6 => "interlockClosed", 7 => "configurationChange", 8 => "jam", # critical # -- general Printer group 501 => "doorOpen", 502 => "doorClosed", 503 => "powerUp", 504 => "powerDown", # -- Input Group 801 => "inputMediaTrayMissing", 802 => "inputMediaSizeChange", 803 => "inputMediaWeightChange", 804 => "inputMediaTypeChange", 805 => "inputMediaColorChange", 806 => "inputMediaFormPartsChange", 807 => "inputMediaSupplyLow", 808 => "inputMediaSupplyEmpty", # -- Output Group 901 => "outputMediaTrayMissing", 902 => "outputMediaTrayAlmostFull", 903 => "outputMediaTrayFull", # -- Marker group 1001 => "markerFuserUnderTemperature", 1002 => "markerFuserOverTemperature", # -- Marker Supplies group 1101 => "markerTonerEmpty", 1102 => "markerInkEmpty", 1103 => "markerPrintRibbonEmpty", 1104 => "markerTonerAlmostEmpty", 1105 => "markerInkAlmostEmpty", 1106 => "markerPrintRibbonAlmostEmpty", 1107 => "markerWasteTonerReceptacleAlmostFull", 1108 => "markerWasteInkReceptacleAlmostFull", 1109 => "markerWasteTonerReceptacleFull", 1110 => "markerWasteInkReceptacleFull", 1111 => "markerOpcLifeAlmostOver", 1112 => "markerOpcLifeOver", 1113 => "markerDeveloperAlmostEmpty", 1114 => "markerDeveloperEmpty", # -- Media Path Device Group 1301 => "mediaPathMediaTrayMissing", 1302 => "mediaPathMediaTrayAlmostFull", 1303 => "mediaPathMediaTrayFull", # -- interpreter Group 1501 => "interpreterMemoryIncrease", 1502 => "interpreterMemoryDecrease", 1503 => "interpreterCartridgeAdded", 1504 => "interpreterCartridgeDeleted", 1505 => "interpreterResourceAdded", 1506 => "interpreterResourceDeleted", ); ## Need multiple passes as oids are all part of tables foreach $col_oid (sort keys %std_mib ){ if ( !defined( $response = $session->get_table($std_mib{$col_oid}) ) ) { print "Error col_oid $col_oid|" if (defined ($opt_d)); if (! ($col_oid =~ m/std_mib_alert/ ) ) { # alerts don't have to exist all the time! $answer=$session->error; $session->close; $state = 'CRITICAL'; print ("$state: $answer for $std_mib{$col_oid}\n"); exit $ERRORS{$state}; } } print "NoError col_oid $col_oid|" if (defined ($opt_d)); foreach $key (keys %{$response}) { $key =~ /.*\.(\d+)\.(\d+)$/; # all oids have a two part index appended $snmp_index = $1 . "." . $2; print "\n$key => $col_oid.$snmp_index = $response->{$key} \n" if (defined ($opt_d)); $snmp_response{$key} = $response->{$key} ; $std_mib_inst_count{$col_oid} += 1 ; # count how many instances $std_mib_instances{$col_oid} .= $snmp_index .":" ; } } #foreach $key ( keys %std_mib_inst_count) { # print "$key = $std_mib_inst_count{$key} $std_mib_instances{$key} \n"; #} # get (total) "page count" - perfdata #print "\n \n $std_mib_instances{'std_mib_marker_tech'} \n"; # how many marker technologies are in use? my ($pg, $pt, $pfd); my @mark_tech = split(/:/, $std_mib_instances{'std_mib_marker_tech'}); foreach $inst (sort @mark_tech){ $pfd = $std_mib_marker_tech{$snmp_response{$std_mib{'std_mib_marker_tech'}."." .$inst}} ; $pfd .= ",".$snmp_response{$std_mib{'std_mib_marker_life_count'}.".".$inst}; $pfd .= ",".$std_mib_marker_counter_units{$snmp_response{$std_mib{'std_mib_marker_counter_unit'}.".".$inst}}; $pfd .= ";"; #perf data separator for multiple marker tech print "pfd = $pfd\n" if (defined ($opt_d)); }; # combine all lines of status display into one line #$std_mib_instances{'std_mib_status_display'} = substr($std_mib_instances{'std_mib_status_display'}, 1); my @display_index = split(/:/, $std_mib_instances{'std_mib_status_display'} ); foreach $inst ( sort @display_index) { $display .= $snmp_response{$std_mib{'std_mib_status_display'} . "." . $inst} . " "; } # see if there are any alerts if (defined ( $std_mib_inst_count{'std_mib_alert_sev_level'} ) ) { if ( ( lc($display) =~ /save/ || lc($display) =~ /warm/ ) && $std_mib_inst_count{'std_mib_alert_sev_level'} == 1 ) { $state='OK'; $answer = "Printer ok - $display"; print $answer . "|$pfd\n"; exit $ERRORS{$state}; } # sometime during transitions from power save to warming there are 2 alerts # if the 2nd alert is for something else it should get caught in the # next call since warmup typically is much smaller than check time # interval. if ( lc($display) =~ /warm/ && $std_mib_inst_count{'std_mib_alert_sev_level'} == 2 ) { $state='OK'; $answer = "$state: Printer - $display"; print $answer . "|$pfd\n"; exit $ERRORS{$state}; } # We have alerts and the display does not say power save or warming up $std_mib_instances{'std_mib_alert_sev_level'} = substr($std_mib_instances{'std_mib_alert_sev_level'}, 1); @display_index = split(/:/, $std_mib_instances{'std_mib_alert_sev_level'} ); $answer = "Alert location(s): "; for $inst (@display_index) { $state = 'WARNING'; if ( $snmp_response{$std_mib{'std_mib_alert_location'} . "." . $inst} < 1) { $answer .= "unknown location "; }else{ $answer .= $std_mib_prt_alert_code{$snmp_response{$std_mib{'std_mib_alert_location'} . "." . $inst} } . " "; #print $std_mib_prt_alert_code{$snmp_response{$std_mib{'std_mib_alert_location'}. "." . $inst}} ; } } print "$state: $answer|$pfd\n"; exit $ERRORS{$state}; }else{ $state='OK'; $answer = "$state: Printer ok - $display "; print $answer . "|$pfd\n"; exit $ERRORS{$state}; } } elsif( $ptype == 2 ) { # HP MIB - JetDirect #### HP MIB OIDS - instance OIDs my $HPJD_LINE_STATUS= ".1.3.6.1.4.1.11.2.3.9.1.1.2.1.0"; my $HPJD_PAPER_STATUS= ".1.3.6.1.4.1.11.2.3.9.1.1.2.2.0"; my $HPJD_INTERVENTION_REQUIRED= ".1.3.6.1.4.1.11.2.3.9.1.1.2.3.0"; my $HPJD_GD_PERIPHERAL_ERROR= ".1.3.6.1.4.1.11.2.3.9.1.1.2.6.0"; my $HPJD_GD_PAPER_JAM= ".1.3.6.1.4.1.11.2.3.9.1.1.2.8.0"; my $HPJD_GD_PAPER_OUT= ".1.3.6.1.4.1.11.2.3.9.1.1.2.9.0"; my $HPJD_GD_TONER_LOW= ".1.3.6.1.4.1.11.2.3.9.1.1.2.10.0"; my $HPJD_GD_PAGE_PUNT= ".1.3.6.1.4.1.11.2.3.9.1.1.2.11.0"; my $HPJD_GD_MEMORY_OUT= ".1.3.6.1.4.1.11.2.3.9.1.1.2.12.0"; my $HPJD_GD_DOOR_OPEN= ".1.3.6.1.4.1.11.2.3.9.1.1.2.17.0"; my $HPJD_GD_PAPER_OUTPUT= ".1.3.6.1.4.1.11.2.3.9.1.1.2.19.0"; my $HPJD_GD_STATUS_DISPLAY= ".1.3.6.1.4.1.11.2.3.9.1.1.3.0"; #define ONLINE 0 #define OFFLINE 1 my @hp_oids = ( $HPJD_LINE_STATUS,$HPJD_PAPER_STATUS,$HPJD_INTERVENTION_REQUIRED,$HPJD_GD_PERIPHERAL_ERROR, $HPJD_GD_PAPER_JAM,$HPJD_GD_PAPER_OUT,$HPJD_GD_TONER_LOW,$HPJD_GD_PAGE_PUNT,$HPJD_GD_MEMORY_OUT, $HPJD_GD_DOOR_OPEN,$HPJD_GD_PAPER_OUTPUT,$HPJD_GD_STATUS_DISPLAY); $state = $ERRORS{'OK'}; if (!defined($response = $session->get_request(@hp_oids))) { $answer=$session->error; $session->close; $state = 'CRITICAL'; print ("$state: $answer \n"); exit $ERRORS{$state}; } # cycle thru the responses and set the appropriate state if($response->{$HPJD_GD_PAPER_JAM} ) { $state='WARNING'; $answer = "Paper Jam"; } elsif($response->{$HPJD_GD_PAPER_OUT} ) { $state='WARNING'; $answer = "Out of Paper"; } elsif($response->{$HPJD_LINE_STATUS} ) { if ($response->{$HPJD_LINE_STATUS} ne "POWERSAVE ON" ) { $state='WARNING'; $answer = "Printer Offline"; } } elsif($response->{$HPJD_GD_PERIPHERAL_ERROR} ) { $state='WARNING'; $answer = "Peripheral Error"; } elsif($response->{$HPJD_INTERVENTION_REQUIRED} ) { $state='WARNING'; $answer = "Intervention Required"; } elsif($response->{$HPJD_GD_TONER_LOW} ) { $state='WARNING'; $answer = "Toner Low"; } elsif($response->{$HPJD_GD_MEMORY_OUT} ) { $state='WARNING'; $answer = "Insufficient Memory"; } elsif($response->{$HPJD_GD_DOOR_OPEN} ) { $state='WARNING'; $answer = "Insufficient Memory"; } elsif($response->{$HPJD_GD_PAPER_OUTPUT} ) { $state='WARNING'; $answer = "OutPut Tray is Full"; } elsif($response->{$HPJD_GD_PAGE_PUNT} ) { $state='WARNING'; $answer = "Data too slow for Engine"; } elsif($response->{$HPJD_PAPER_STATUS} ) { $state='WARNING'; $answer = "Unknown Paper Error"; } else # add code to parse STATUS DISPLAY here { $state='OK'; $answer = "Printer ok - $response->{$HPJD_GD_STATUS_DISPLAY} "; } # print and exit print "$state: $answer \n"; exit $ERRORS{$state}; } else{ # 3rd printer type - not yet supported print "Printer type $opt_P has not been implemented\n"; $state='UNKNOWN'; exit $ERRORS{$state}; } #### subroutines sub unit_status { my $stat = shift; } sub print_usage () { print "Usage: $PROGNAME -H [-C community] [-P HP or RFC] [-p port] [-v snmp_version] [-h help] [-V version]\n"; } sub print_help () { print_revision($PROGNAME,'$Revision: 1.1 $'); print "Copyright (c) 2002 Subhendu Ghosh/Ethan Galstad. This plugin reports the status of an network printer with an SNMP management module. "; print_usage(); print " -H, --hostname=HOST Name or IP address of host to check -C --community snmp community string (default: public) -P --Printer supported values are \"HP\" for Jetdirect printers and \"RFC\" for RFC 1759 Print MIB based implementations (default: RFC) -p --port Port where snmp agent is listening (default: 161) -v --snmp_version SNMP version to use (default: version 1) -h --help This screen -V --version Plugin version "; support(); }