GenaSYS.sh: updated monitring configuration
authorAndy Witter <awitter@georgialibraries.org>
Wed, 8 May 2013 14:38:11 +0000 (10:38 -0400)
committerAndy Witter <awitter@georgialibraries.org>
Wed, 8 May 2013 14:38:11 +0000 (10:38 -0400)
setup-drone-sh setup-head.sh and setup.sh updated monitoring configuration.
Included additional files needed for monitoring the cluster.

12 files changed:
GenaSYS.sh
templates/monitor/check_cpu.sh [new file with mode: 0755]
templates/monitor/check_hpacucli [new file with mode: 0755]
templates/monitor/check_hpasm [new file with mode: 0755]
templates/monitor/commands.cfg [new file with mode: 0644]
templates/monitor/nrpe_local.cfg [new file with mode: 0644]
templates/monitor/reporter_status.sh [new file with mode: 0755]
templates/monitor/services.cfg
templates/setup-drone.sh
templates/setup-functions
templates/setup-head.sh
templates/setup.sh

index b2e1385..d890d26 100755 (executable)
@@ -25,8 +25,8 @@
     PURPOSE_="Generate config files needed for Evergreen-ILS Cluster"
    SYNOPSIS_="$NAME_"
    REQUIRES_="standard GNU commands, apt, dpkg"
-    VERSION_="1.03"
-       DATE_="2010-09-18; last update: 2013-04-30"
+    VERSION_="1.06"
+       DATE_="2010-09-18; last update: 2013-05-08"
      AUTHOR_="Andy Witter <awitter@georgialibraries.org>"
         URL_="http://evergreen-ils.org"
    CATEGORY_="devel"
@@ -2145,11 +2145,12 @@ for NAGINODE in ${MACHINES_NO_BRICKS}
 do
         ### Generate the hosts.cfg file
         MONITOR_HOST=$( echo "${NAGINODE}" | tr  '[:lower:]'  '[:upper:]' )
+       echo '### Generated by GenaSYS' >>$TMPNAGIHOSTS
        printf 'define host {\n' >>$TMPNAGIHOSTS;printf "\thost_name\t${NAGINODE}\n" >>$TMPNAGIHOSTS
        printf "\talias\t\t$NAGINODE\n" >>$TMPNAGIHOSTS
        MACHINES_LIST="$MACHINES_LIST$(printf "$NAGINODE ")"
         
-       if echo $NAGINODE | grep lvs0
+       if echo $NAGINODE | grep -q lvs0
         then
                 eval printf "\\\taddress\\\t\\\t\$${MONITOR_HOST#*-}_PRIVATE_IP\\\n" >>$TMPNAGIHOSTS
         else
@@ -2163,13 +2164,16 @@ done
 ### Build lists to generate hostgroups for Icinga/Nagios monitoring server
 OSRF_MACHINES_LIST="$BRICKDRONELIST $MACHINES_UTILITY $MACHINES_SIP_ONLY $MACHINES_REPORTER"
 ALL_MACHINES_LIST="$BRICKDRONELIST $MACHINES_LIST ${REPORTS_NFS_HOSTNAME}"
-echo $OSRF_MACHINES_LIST
 
 ### Define the temp hostgroups.cfg file
 TMPNAGHOSTGROUPS="$TMPFOLDER/hostgroups.cfg"
 
 ### Generate block for hostgroups.cfg for Icinga/Nagios monitoring server.
 GenHostGroup () { ### Usage: $0 "hostgroup_name" "alias" "members"
+if ! grep -q "GenaSYS" $TMPNAGHOSTGROUPS
+then
+       echo '### Generated by GenaSYS.' >> $TMPNAGHOSTGROUPS
+fi
 printf 'define hostgroup {\n' >>$TMPNAGHOSTGROUPS
 printf "\thostgroup_name ${1}\n" >>$TMPNAGHOSTGROUPS
 printf "\talias ${2}\n" >>$TMPNAGHOSTGROUPS
@@ -2195,24 +2199,26 @@ do
        cp -f "$TMPFOLDER/hosts.cfg" "$OUTDIR/$MONITOR/etc/icinga/objects"
        cp -f "$TMPFOLDER/hostgroups.cfg" "$OUTDIR/$MONITOR/etc/icinga/objects"
        cp -f "$TEMPLATEDIR/monitor/services.cfg" "$OUTDIR/$MONITOR/etc/icinga/objects"
-       cp -f "$TEMPLATEDIR/monitor/commands.cfg" "$OUTDIR/$MONITOR/etc/icinga/objects"
+       #cp -f "$TEMPLATEDIR/monitor/commands.cfg" "$OUTDIR/$MONITOR/etc/icinga"
 
        ### Generate sources.list for apt to install icinga
-        mkdir -p "${OUTDIR}/${MONITOR}/etc/apt/sources.list.d/"
-        echo "#PPA for Icinga monitoring for Evergreen" >> "${OUTDIR}/${MONITOR}/etc/apt/sources.list.d/icinga_ppa.list"
-        echo "deb http://ppa.launchpad.net/formorer/icinga/ubuntu $UBUNTU_CODENAME main" >> "${OUTDIR}/${MONITOR}/etc/apt/sources.list.d/icinga_ppa.list"
-        echo "deb-src http://ppa.launchpad.net/formorer/icinga/ubuntu $UBUNTU_CODENAME main" >> "${OUTDIR}/${MONITOR}/etc/apt/sources.list.d/icinga_ppa.list"
+        #mkdir -p "${OUTDIR}/${MONITOR}/etc/apt/sources.list.d/"
+       echo '### Generated by GenaSYS' >> "${TMPFOLDER}/icinga_ppa.list"
+        echo "#PPA for Icinga monitoring for Evergreen" >> "${TMPFOLDER}/icinga_ppa.list"
+        echo "deb http://ppa.launchpad.net/formorer/icinga/ubuntu $UBUNTU_CODENAME main" >> "${TMPFOLDER}/icinga_ppa.list"
+        echo "deb-src http://ppa.launchpad.net/formorer/icinga/ubuntu $UBUNTU_CODENAME main" >> "${TMPFOLDER}/icinga_ppa.list"
 done
 
 ### Generate npre setup script for all monitored hosts.
 TMPSETUPMONITOR="/$TMPFOLDER/.setup_monitoring.sh"
+echo '#/bin/bash' >>$TMPSETUPMONITOR
 echo sed -i \'s^allowed_hosts=127.0.0.1^#allowed_hosts=127.0.0.1^g\' "/etc/nagios/nrpe.cfg" >>$TMPSETUPMONITOR
-for NAGINODE in ${MACHINES_MONITOR} ## strip prefix
+for NAGINODE in ${MACHINES_MONITOR} 
 do
        MONITOR_SERVER=$( echo "${NAGINODE}" | tr  '[:lower:]'  '[:upper:]' )
        ### remove prefix ${MONITOR_SERVER#*-}
        echo ${MACHINES_MONITOR#*-}
-       THIS_MONITOR_SERVER_IP=$(eval echo \$${MONITOR_SERVER#*-}_IP)
+       THIS_MONITOR_SERVER_IP=$(eval echo \$${MONITOR_SERVER#*-}_IP) ## strip prefix
        if [ -z "$MONITOR_SERVER_IP_LIST" ]
        then
                MONITOR_SERVER_IP_LIST="$THIS_MONITOR_SERVER_IP"
@@ -2220,9 +2226,11 @@ do
                MONITOR_SERVER_IP_LIST="$MONITOR_SERVER_IP_LIST $THIS_MONITOR_SERVER_IP"
        fi
 done
-echo MONITOR_SERVER_IP_LIST is: $MONITOR_SERVER_IP_LIST
 printf "echo \"allowed_hosts=${MONITOR_SERVER_IP_LIST// /,}\" >> /etc/nagios/nrpe.cfg\n" >>$TMPSETUPMONITOR
 echo sed -i \'s^dont_blame_nrpe=0^dont_blame_nrpe=1^g\' "/etc/nagios/nrpe.cfg" >>$TMPSETUPMONITOR
+echo 'cp "${WD}/monitoring/check_cpu.sh" "/usr/lib/nagios/plugins"' >>$TMPSETUPMONITOR
+echo 'cp "${WD}/monitoring/reporter_status.sh" "/usr/lib/nagios/plugins"' >>$TMPSETUPMONITOR
+echo 'cp "${WD}/monitoring/nrpe_local.cfg" "/etc/nagios"' >>$TMPSETUPMONITOR
 chmod 755 $TMPSETUPMONITOR
 
 
@@ -2391,7 +2399,11 @@ do
                mkdir ../setup_${SUBFOLDER}
                mv ${SUBFOLDER}_apt.list ../setup_${SUBFOLDER}
                cp $TMPFOLDER/debconf-settings ../setup_${SUBFOLDER}
-               cp $TMPSETUPMONITOR ../setup_${SUBFOLDER}
+               mkdir -p ../setup_${SUBFOLDER}/monitoring
+               cp $TMPSETUPMONITOR ../setup_${SUBFOLDER}/monitoring
+               cp $TEMPLATEDIR/monitor/check_cpu.sh ../setup_${SUBFOLDER}/monitoring
+               cp $TEMPLATEDIR/monitor/nrpe_local.cfg ../setup_${SUBFOLDER}/monitoring
+               cp $TEMPLATEDIR/monitor/reporter_status.sh ../setup_${SUBFOLDER}/monitoring
                touch ../setup_${SUBFOLDER}/.osrf_node
                sed -i "s^HOSTNAME^${SUBFOLDER}^g" ../setup_${SUBFOLDER}/debconf-settings
                        
@@ -2435,22 +2447,31 @@ printf " |\r";sleep 0.05; printf " /\r";sleep 0.05; printf -- ' - \r';sleep 0.05
 cd $NODE_FOLDER
 mkdir "setup_${NODE_FOLDER}"
 cp "$TEMPLATEDIR/setup.sh" "setup_${NODE_FOLDER}/setup_$NODE_FOLDER.sh"
-cp "$TMPSETUPMONITOR" "setup_${NODE_FOLDER}"
+#cp "$TMPSETUPMONITOR" "setup_${NODE_FOLDER}"
 chmod 755 "setup_${NODE_FOLDER}/setup_$NODE_FOLDER.sh"
 cp "$TEMPLATEDIR/setup-vars" "setup_${NODE_FOLDER}/setup-vars"
 cp "$TEMPLATEDIR/setup-functions" "setup_${NODE_FOLDER}/setup-functions"
-if [[ "$USE_HOSTNAME_PREFIX" = "y" || "$USE_HOSTNAME_PREFIX" = "Y" ]]
+### Monitoring
+mkdir -p "setup_${NODE_FOLDER}/monitoring"
+cp "$TMPSETUPMONITOR" "setup_${NODE_FOLDER}/monitoring"
+cp "$TEMPLATEDIR/monitor/check_cpu.sh" "setup_${NODE_FOLDER}/monitoring"
+cp "$TEMPLATEDIR/monitor/nrpe_local.cfg" "setup_${NODE_FOLDER}/monitoring"
+cp "$TEMPLATEDIR/monitor/reporter_status.sh" "setup_${NODE_FOLDER}/monitoring"
+
+if [[ "$USE_HOSTNAME_PREFIX" = "y" || "$USE_HOSTNAME_PREFIX" = "Y" ]] #TODO check this use strip prefix instead
 then
        NODE_FOLDER_NO_PREFIX=$(echo $NODE_FOLDER|awk -F"-" '{ print $2  }')
 else
        NODE_FOLDER_NO_PREFIX=$NODE_FOLDER
 fi
-if [ -e "$TEMPLATEDIR/apt/${NODE_FOLDER_NO_PREFIX}_apt.list" ]
+
+if [ -e "$TEMPLATEDIR/apt/${NODE_FOLDER_NO_PREFIX}_apt.list" ] #TODO check this use strip prefix instead
 then
        cp "$TEMPLATEDIR/apt/${NODE_FOLDER_NO_PREFIX}_apt.list" "setup_${NODE_FOLDER}/${NODE_FOLDER}_apt.list"
 else
        cp "$TEMPLATEDIR/apt/apt.list" "setup_${NODE_FOLDER}/${NODE_FOLDER}_apt.list"
 fi
+
 grep -qx ${NODE_FOLDER} "$TMPOSRFNODES" && \
 cp "$TMPFOLDER/debconf-settings" "setup_${NODE_FOLDER}" && \
 touch "setup_${NODE_FOLDER}/.osrf_node" && \
@@ -2484,6 +2505,17 @@ if echo ${NODE_FOLDER} | grep -q lvs0
 then
        cp -f $TEMPLATEDIR/lvs/ldirector*.deb "setup_${NODE_FOLDER}"
 fi
+if echo ${NODE_FOLDER} | grep -q monitor0
+then
+       mkdir "setup_${NODE_FOLDER}/Icinga-Nagios"
+       cp -f "$TEMPLATEDIR/monitor/commands.cfg" "setup_${NODE_FOLDER}/Icinga-Nagios"
+       cp -f "$TMPFOLDER/hosts.cfg" "setup_${NODE_FOLDER}/Icinga-Nagios"
+        cp -f "$TMPFOLDER/hostgroups.cfg" "setup_${NODE_FOLDER}/Icinga-Nagios"
+        cp -f "$TEMPLATEDIR/monitor/services.cfg" "setup_${NODE_FOLDER}/Icinga-Nagios"
+       cp -f "$TEMPLATEDIR/monitor/check_hpacucli" "setup_${NODE_FOLDER}/Icinga-Nagios"
+       cp -f "$TEMPLATEDIR/monitor/check_hpasm" "setup_${NODE_FOLDER}/Icinga-Nagios"
+       cp -f "${TMPFOLDER}/icinga_ppa.list" "setup_${NODE_FOLDER}/Icinga-Nagios"
+fi
 sed -i "s^tar_file^$NODE_FOLDER^g" "setup_${NODE_FOLDER}/setup_$NODE_FOLDER.sh"
 sed -i "s^_EGVER^$EG_VERSION^g" "setup_${NODE_FOLDER}/setup_$NODE_FOLDER.sh"
 sed -i "s^_GENVER^$VERSION_^g" "setup_${NODE_FOLDER}/setup-vars"
diff --git a/templates/monitor/check_cpu.sh b/templates/monitor/check_cpu.sh
new file mode 100755 (executable)
index 0000000..897a9f6
--- /dev/null
@@ -0,0 +1,220 @@
+#!/bin/bash
+# ========================================================================================
+# CPU Utilization Statistics plugin for Nagios 
+#
+# Written by   : Andreas Baess based on a script by Steve Bosek
+# Release      : 1.1
+# Creation date : 3 May 2008
+# Package       : DTB Nagios Plugin
+# Description   : Nagios plugin (script) to check cpu utilization statistics.
+#              This script has been designed and written on Unix plateform (Linux, Aix, Solaris), 
+#              requiring iostat as external program. The locations of these can easily 
+#              be changed by editing the variables $IOSTAT at the top of the script. 
+#              The script is used to query 4 of the key cpu statistics (user,system,iowait,idle)
+#              at the same time. 
+#
+# Usage         : ./check_cpu.sh [-w <warn>] [-c <crit]
+#                                [-uw <user_cpu warn>] [-uc <user_cpu crit>]
+#                                [-sw <sys_cpu warn>] [-sc <sys_cpu crit>]
+#                                [-iw <io_wait_cpu warn>] [-ic <io_wait_cpu crit>]
+#                                [-i <intervals in second>] [-n <report number>] 
+# ----------------------------------------------------------------------------------------
+# ========================================================================================
+#
+# HISTORY :
+#     Release  |     Date      |    Authors    |       Description
+# --------------+---------------+---------------+------------------------------------------
+#      1.1     |    03.05.08   | Andreas Baess | Changed script to use vmstat on Linux because
+#               |               |               | iostat does not use integers
+#               |               |               | Fixed output to display the IO-wait warning threshhold
+# --------------+---------------+---------------+------------------------------------------
+#      1.0     |    03.05.08   | Andreas Baess | Changed script so that thresholds are global
+#               |               |               | and output can be parsed by perfprocessing
+#               |               |               | changed default warning to 70 critical to 90
+# =========================================================================================
+
+# Paths to commands used in this script.  These may have to be modified to match your system setup.
+
+IOSTAT=/usr/bin/iostat
+
+# Nagios return codes
+STATE_OK=0
+STATE_WARNING=1
+STATE_CRITICAL=2
+STATE_UNKNOWN=3
+
+# Plugin parameters value if not define
+WARNING_THRESHOLD=${WARNING_THRESHOLD:="70"}
+CRITICAL_THRESHOLD=${CRITICAL_THRESHOLD:="90"}
+INTERVAL_SEC=${INTERVAL_SEC:="1"}
+NUM_REPORT=${NUM_REPORT:="3"}
+U_CPU_W=${WARNING_THRESHOLD}
+S_CPU_W=${WARNING_THRESHOLD}
+IO_CPU_W=${WARNING_THRESHOLD}
+U_CPU_C=${CRITICAL_THRESHOLD}
+S_CPU_C=${CRITICAL_THRESHOLD}
+IO_CPU_C=${CRITICAL_THRESHOLD}
+
+# Plugin variable description
+PROGNAME=$(basename $0)
+RELEASE="Revision 1.0"
+AUTHOR="by Andreas Baess <ab@gun.de> based on a work from Steve Bosek (sbosek@mac.com)"
+
+if [ ! -x $IOSTAT ]; then
+       echo "UNKNOWN: iostat not found or is not executable by the nagios user."
+       exit $STATE_UNKNOWN
+fi
+
+# Functions plugin usage
+print_release() {
+    echo "$RELEASE $AUTHOR"
+}
+
+print_usage() {
+       echo ""
+       echo "$PROGNAME $RELEASE - CPU Utilization check script for Nagios"
+       echo ""
+       echo "Usage: check_cpu.sh [flags]"
+       echo ""
+       echo "Flags:"
+       echo "  -w  <number> : Global Warning level in % for user/system/io-wait cpu"
+       echo "  -uw <number> : Warning level in % for user cpu"
+       echo "  -iw <number> : Warning level in % for IO_wait cpu"
+       echo "  -sw <number> : Warning level in % for system cpu"
+       echo "  -c  <number> : Global Critical level in % for user/system/io-wait cpu"
+       echo "  -uc <number> : Critical level in % for user cpu"
+       echo "  -ic <number> : Critical level in % for IO_wait cpu"
+       echo "  -sc <number> : Critical level in % for system cpu"
+       echo "  -i  <number> : Interval in seconds for iostat (default : 1)"
+       echo "  -n  <number> : Number report for iostat (default : 3)"
+       echo "  -h  Show this page"
+       echo ""
+    echo "Usage: $PROGNAME"
+    echo "Usage: $PROGNAME --help"
+    echo ""
+}
+
+print_help() {
+       print_usage
+        echo ""
+        echo "This plugin will check cpu utilization (user,system,iowait,idle in %)"
+        echo ""
+       exit 0
+}
+
+# Parse parameters
+while [ $# -gt 0 ]; do
+    case "$1" in
+        -h | --help)
+            print_help
+            exit $STATE_OK
+            ;;
+        -v | --version)
+                print_release
+                exit $STATE_OK
+                ;;
+        -w | --warning)
+                shift
+                WARNING_THRESHOLD=$1
+               U_CPU_W=$1
+               S_CPU_W=$1
+               IO_CPU_W=$1
+                ;;
+        -c | --critical)
+               shift
+                CRITICAL_THRESHOLD=$1
+               U_CPU_C=$1
+               S_CPU_C=$1
+               IO_CPU_C=$1
+                ;;
+        -uw | --uwarn)
+               shift
+               U_CPU_W=$1
+                ;;
+        -uc | --ucrit)
+               shift
+               U_CPU_C=$1
+                ;;
+        -sw | --swarn)
+               shift
+               S_CPU_W=$1
+                ;;
+        -sc | --scrit)
+               shift
+               S_CPU_C=$1
+                ;;
+        -iw | --iowarn)
+               shift
+               IO_CPU_W=$1
+                ;;
+        -ic | --iocrit)
+               shift
+               IO_CPU_C=$1
+                ;;
+        -i | --interval)
+               shift
+               INTERVAL_SEC=$1
+                ;;
+        -n | --number)
+               shift
+               NUM_REPORT=$1
+                ;;        
+        *)  echo "Unknown argument: $1"
+            print_usage
+            exit $STATE_UNKNOWN
+            ;;
+        esac
+shift
+done
+
+# CPU Utilization Statistics Unix Plateform ( Linux,AIX,Solaris are supported )
+case `uname` in
+#      Linux ) CPU_REPORT=`iostat -c $INTERVAL_SEC $NUM_REPORT |  tr -s ' ' ';' | sed '/^$/d' | tail -1`
+#                      CPU_USER=`echo $CPU_REPORT | cut -d ";" -f 2`
+#                      CPU_SYSTEM=`echo $CPU_REPORT | cut -d ";" -f 4`
+#                      CPU_IOWAIT=`echo $CPU_REPORT | cut -d ";" -f 5`
+#                      CPU_IDLE=`echo $CPU_REPORT | cut -d ";" -f 6`
+#            ;;
+       AIX ) CPU_REPORT=`iostat -t $INTERVAL_SEC $NUM_REPORT | sed -e 's/,/./g'|tr -s ' ' ';' | tail -1`
+                       CPU_USER=`echo $CPU_REPORT | cut -d ";" -f 4`
+                       CPU_SYSTEM=`echo $CPU_REPORT | cut -d ";" -f 5`
+                       CPU_IOWAIT=`echo $CPU_REPORT | cut -d ";" -f 7`
+                       CPU_IDLE=`echo $CPU_REPORT | cut -d ";" -f 6`
+            ;;
+       Linux ) CPU_REPORT=`vmstat -n $INTERVAL_SEC $NUM_REPORT | tail -1`
+                       CPU_USER=`echo $CPU_REPORT | awk '{ print $13 }'`
+                       CPU_SYSTEM=`echo $CPU_REPORT | awk '{ print $14 }'`
+                       CPU_IOWAIT=`echo $CPU_REPORT | awk '{ print $16 }'`
+                       CPU_IDLE=`echo $CPU_REPORT | awk '{ print $15 }'`
+            ;;
+       SunOS ) CPU_REPORT=`iostat -c $INTERVAL_SEC $NUM_REPORT | tail -1`
+                       CPU_USER=`echo $CPU_REPORT | awk '{ print $1 }'`
+                       CPU_SYSTEM=`echo $CPU_REPORT | awk '{ print $2 }'`
+                       CPU_IOWAIT=`echo $CPU_REPORT | awk '{ print $3 }'`
+                       CPU_IDLE=`echo $CPU_REPORT | awk '{ print $4 }'`
+            ;;
+       *)              echo "UNKNOWN: `uname` not yet supported by this plugin. Coming soon !"
+                       exit $STATE_UNKNOWN 
+           ;;
+       esac
+
+# Return
+
+# Are we in a critical state?
+if [ ${CPU_IOWAIT} -ge ${IO_CPU_C} -o ${CPU_USER} -ge ${U_CPU_C} -o ${CPU_SYSTEM} -ge ${S_CPU_C}  ];
+then
+       echo "CPU CRITICAL : user=${CPU_USER}% system=${CPU_SYSTEM}% iowait=${CPU_IOWAIT}% idle=${CPU_IDLE}% | cpu_user=${CPU_USER}%;${U_CPU_W};${U_CPU_C}; cpu_sys=${CPU_SYSTEM}%;${S_CPU_W};${S_CPU_C}; cpu_iowait=${CPU_IOWAIT}%;${IO_CPU_W};${IO_CPU_C}; cpu_idle=${CPU_IDLE}%;"
+       exit $STATE_CRITICAL
+fi
+
+# Are we in a warning state?
+if [ ${CPU_IOWAIT} -ge ${IO_CPU_W} -o ${CPU_USER} -ge ${U_CPU_W} -o ${CPU_SYSTEM} -ge ${S_CPU_W}  ];
+then
+       echo "CPU WARNING : user=${CPU_USER}% system=${CPU_SYSTEM}% iowait=${CPU_IOWAIT}% idle=${CPU_IDLE}% | cpu_user=${CPU_USER}%;${U_CPU_W};${U_CPU_C}; cpu_sys=${CPU_SYSTEM}%;${S_CPU_W};${S_CPU_C}; cpu_iowait=${CPU_IOWAIT}%;${IO_CPU_W};${IO_CPU_C}; cpu_idle=${CPU_IDLE}%;"
+       exit $STATE_WARNING
+fi
+
+# If we got this far, everything seems to be OK - IDLE has no threshold
+echo "CPU OK : user=${CPU_USER}% system=${CPU_SYSTEM}% iowait=${CPU_IOWAIT}% idle=${CPU_IDLE}% | cpu_user=${CPU_USER}%;${U_CPU_W};${U_CPU_C}; cpu_sys=${CPU_SYSTEM}%;${S_CPU_W};${S_CPU_C}; cpu_iowait=${CPU_IOWAIT}%;${IO_CPU_W};${IO_CPU_C}; cpu_idle=${CPU_IDLE}%;"
+exit $STATE_OK
+
diff --git a/templates/monitor/check_hpacucli b/templates/monitor/check_hpacucli
new file mode 100755 (executable)
index 0000000..beb1c95
--- /dev/null
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+#
+# check_hpacucli
+#
+# Copyright Â© 2012 Philip Garner, Technophobia Limited.
+#
+#    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 3 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, see <http://www.gnu.org/licenses/>.
+#
+# Orginaly written by John Carroll for use with BCNU.
+# Cannibalised by Phil Garner for Nagios.
+#
+# This plugin was written for and at the expense of Technophobia Limited 
+# and is being made available with there authorisation. 
+#
+# Authors: Phil Garner - phil.garner@technophobia.com
+#          John Carroll - john.carroll@technophobia.com
+#
+# v0.1 06/03/2012
+# v0.2 11/06/2012 change file opp test on hpacucli
+#
+# NOTES: Requires Perl Module Nagios::Plugin & hpacucli to be installed
+#        Nagios user will need sudo access - suggest adding line below to
+#        sudoers:
+#           nagios  ALL=(ALL) NOPASSWD: /usr/sbin/hpacucli
+#
+#           In sudoers if requiretty is on (off state is default)
+#           you will also need to add the line below
+#           Defaults:nagios !requiretty
+#
+
+use strict;
+use warnings;
+use Nagios::Plugin;
+
+# These may need changing depending on the configuration of your system
+my $hpacucli = '/usr/sbin/hpacucli';
+my $sudo     = '/usr/bin/sudo';
+
+my $np = Nagios::Plugin->new(
+    shortname => 'check_hpacucli',
+    version   => '0.2',
+    usage     => "Usage: %s <ARGS>\n\t\t--help for help\n",
+    license   => "License - GPL v3 see code for more details",
+    url       => "http://www.technophobia.com",
+    blurb =>
+      "Blurb:
+\tNagios plugin that checks HP RAID controller status with hpacucli, 
+\trequires CPAN module Nagios::Plugin & hpacucli to be installed.  
+\tTo work it will need a sudoers line see NOTES in code for details.",
+);
+
+# Define Extra Args
+$np->add_arg(
+    spec     => 'informative|i',
+    help     => 'Adds information on each check even if OK',
+    required => 0,
+);
+
+$np->getopts;
+
+my ( $slot, $fh, $fh2 );
+
+# Check sudo is installed and executable
+if ( !-x $sudo ) {
+    $np->nagios_exit( UNKNOWN, "No executable sudo at $sudo" );
+}
+
+# Check hpacucli is installed 
+if ( !-e $hpacucli ) {
+    $np->nagios_exit( UNKNOWN, "No hpacucli at $hpacucli" );
+}
+
+# Get controller status
+open( $fh, "$sudo $hpacucli controller all show status|" )
+  or $np->nagios_exit( CRITICAL, "Failed to run hpacucli" );
+
+# Spin through output
+foreach my $line (<$fh>) {
+
+    if ( $line =~ m/Another instance of hpacucli is running! Stop it first\./i )
+    {
+        $np->nagios_exit( CRITICAL,
+            "Another instance of hpacucli is running!" );
+    }
+
+    elsif ( $line =~ m/Slot (\d+)/i ) {
+        my $slot = $1;
+
+        # Now get further details on each controller
+
+        foreach my $PARAM qw(array physicaldrive logicaldrive) {
+
+            open( $fh2,
+                "$sudo $hpacucli controller slot=$slot $PARAM all show status|"
+              )
+              or $np->add_message( CRITICAL,
+                "Failed to get info for $PARAM slot $slot" );
+
+            foreach my $line2 (<$fh2>) {
+                if ( $line2 =~ /^\s*$PARAM.*:\s*(\w+[\w\s]*)$/i ) {
+                    my $result = $1;
+                    chomp $result;
+                    if ( $result ne "OK" ) {
+                        chomp $line2;
+                        $np->add_message( CRITICAL, "$line2" );
+                    }
+                    elsif ( $np->opts->informative ) {
+                        chomp $line2;
+                        $np->add_message( OK, "$line2" );
+                    }
+                }
+            }
+
+            close($fh2)
+              or $np->add_message( CRITICAL,
+                "Failed to get info for $PARAM slot $slot" );
+        }
+    }
+    else {
+
+        # Check the overall controller status is OK
+        if ( $line =~ /Status\:\s*([\w\s]+)$/ ) {
+            my $result = $1;
+            chomp $result;
+            if ( $result ne "OK" ) {
+                chomp $line;
+                $np->add_message( CRITICAL, "$line" );
+            }
+            elsif ( $np->opts->informative ) {
+                chomp $line;
+                $np->add_message( OK, "$line" );
+            }
+        }
+
+    }
+}
+close($fh)
+  or $np->nagios_exit( CRITICAL,
+    "Failed to run $sudo $hpacucli controller all show status" );
+
+# Output Info and Exit
+$np->nagios_exit( $np->check_messages() );
diff --git a/templates/monitor/check_hpasm b/templates/monitor/check_hpasm
new file mode 100755 (executable)
index 0000000..07da9b3
--- /dev/null
@@ -0,0 +1,10256 @@
+#! /usr/bin/perl -w
+# nagios: -epn
+
+package Nagios::MiniPlugin;
+
+use strict;
+use Getopt::Long qw(:config no_ignore_case bundling);
+
+our @STATUS_CODES = qw(OK WARNING CRITICAL UNKNOWN DEPENDENT);
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = (@STATUS_CODES, qw(nagios_exit nagios_die check_messages));
+our @EXPORT_OK = qw(%ERRORS);
+
+use constant OK         => 0;
+use constant WARNING    => 1;
+use constant CRITICAL   => 2;
+use constant UNKNOWN    => 3;
+use constant DEPENDENT  => 4;
+
+our %ERRORS = (
+    'OK'        => OK,
+    'WARNING'   => WARNING,
+    'CRITICAL'  => CRITICAL,
+    'UNKNOWN'   => UNKNOWN,
+    'DEPENDENT' => DEPENDENT,
+);
+
+our %STATUS_TEXT = reverse %ERRORS;
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+       perfdata => [],
+       messages => {
+         ok => [],
+         warning => [],
+         critical => [],
+         unknown => [],
+       },
+       args => [],
+       opts => Nagios::MiniPlugin::Getopt->new(%params),
+  };
+  foreach (qw(shortname usage version url plugin blurb extra
+      license timeout)) {
+    $self->{$_} = $params{$_};
+  }
+  bless $self, $class;
+}
+
+sub add_arg {
+  my $self = shift;
+  $self->{opts}->add_arg(@_);
+}
+
+sub getopts { 
+       my $self = shift;
+       $self->{opts}->getopts();
+}
+
+sub opts {
+       my $self = shift;
+       return $self->{opts};
+}
+
+sub add_message {
+  my $self = shift;
+  my ($code, @messages) = @_;
+  $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/;
+  $code = lc $code;
+  push @{$self->{messages}->{$code}}, @messages;
+}
+
+sub remove_message {
+  my $self = shift;
+  my ($code, @messages) = @_;
+  $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/;
+  $code = lc $code;
+  pop @{$self->{messages}->{$code}};
+}
+
+sub add_perfdata {
+  my ($self, %args) = @_;
+  my $str = $args{label}.'='.$args{value};
+  if ($args{uom}) {
+    $str .= $args{uom};
+  }
+  if ($args{warning}) {
+    $str .= ';'.$args{warning};
+  }
+  if ($args{critical}) {
+    $str .= ';'.$args{critical};
+  }
+  push @{$self->{perfdata}}, $str;
+}
+
+
+sub check_messages {
+  my $self = shift;
+  my %args = @_;
+
+  # Add object messages to any passed in as args
+  for my $code (qw(critical warning unknown ok)) {
+    my $messages = $self->{messages}->{$code} || [];
+    if ($args{$code}) {
+      unless (ref $args{$code} eq 'ARRAY') {
+        if ($code eq 'ok') {
+          $args{$code} = [ $args{$code} ];
+        }
+      }
+      push @{$args{$code}}, @$messages;
+    } else {
+      $args{$code} = $messages;
+    }
+  }
+  my %arg = %args;
+  $arg{join} = ' ' unless defined $arg{join};
+
+  # Decide $code
+  my $code = OK;
+  $code ||= CRITICAL  if @{$arg{critical}};
+  $code ||= WARNING   if @{$arg{warning}};
+  $code ||= UNKNOWN   if @{$arg{unknown}};
+  return $code unless wantarray;
+
+  # Compose message
+  my $message = '';
+  if ($arg{join_all}) {
+      $message = join( $arg{join_all},
+          map { @$_ ? join( $arg{'join'}, @$_) : () }
+              $arg{critical},
+              $arg{warning},
+              $arg{unknown},
+              $arg{ok} ? (ref $arg{ok} ? $arg{ok} : [ $arg{ok} ]) : []
+      );
+  }
+
+  else {
+      $message ||= join( $arg{'join'}, @{$arg{critical}} )
+          if $code == CRITICAL;
+      $message ||= join( $arg{'join'}, @{$arg{warning}} )
+          if $code == WARNING;
+      $message ||= join( $arg{'join'}, @{$arg{unknown}} )
+          if $code == UNKNOWN;
+      $message ||= ref $arg{ok} ? join( $arg{'join'}, @{$arg{ok}} ) : $arg{ok}
+          if $arg{ok};
+  }
+
+  return ($code, $message);
+}
+
+sub nagios_exit {
+  my $self = shift;
+  my ($code, $message, $arg) = @_;
+  $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code};
+  $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code};
+  $message = '' unless defined $message;
+  if (ref $message && ref $message eq 'ARRAY') {
+      $message = join(' ', map { chomp; $_ } @$message);
+  } else {
+      chomp $message;
+  }
+  my $output = "$STATUS_TEXT{$code}";
+  $output .= " - $message" if defined $message && $message ne '';
+  if (scalar (@{$self->{perfdata}})) {
+    $output .= " | ".join(" ", @{$self->{perfdata}});
+  }
+  $output .= "\n";
+  print $output;
+  exit $code;
+}
+
+package Nagios::MiniPlugin::Getopt;
+
+use strict;
+use File::Basename;
+use Data::Dumper;
+use Getopt::Long qw(:config no_ignore_case bundling);
+
+# Standard defaults
+my %DEFAULT = (
+  timeout => 15,
+  verbose => 0,
+  license =>
+"This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
+It may be used, redistributed and/or modified under the terms of the GNU
+General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).",
+);
+# Standard arguments
+my @ARGS = ({
+    spec => 'usage|?',
+    help => "-?, --usage\n   Print usage information",
+  }, {
+    spec => 'help|h',
+    help => "-h, --help\n   Print detailed help screen",
+  }, {
+    spec => 'version|V',
+    help => "-V, --version\n   Print version information",
+  }, {
+    #spec => 'extra-opts:s@',
+    #help => "--extra-opts=[<section>[@<config_file>]]\n   Section and/or config_file from which to load extra options (may repeat)",
+  }, {
+    spec => 'timeout|t=i',
+    help => "-t, --timeout=INTEGER\n   Seconds before plugin times out (default: %s)",
+    default => $DEFAULT{timeout},
+  }, {
+    spec => 'verbose|v+',
+    help => "-v, --verbose\n   Show details for command-line debugging (can repeat up to 3 times)",
+    default => $DEFAULT{verbose},
+  },
+);
+# Standard arguments we traditionally display last in the help output
+my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose);
+
+sub _init
+{
+  my $self = shift;
+  my %params = @_;
+  # Check params
+  my $plugin = basename($ENV{NAGIOS_PLUGIN} || $0);
+  #my %attr = validate( @_, {
+  my %attr = (
+    usage => 1,
+    version => 0,
+    url => 0,
+    plugin => { default => $plugin },
+    blurb => 0,
+    extra => 0,
+    'extra-opts' => 0,
+    license => { default => $DEFAULT{license} },
+    timeout => { default => $DEFAULT{timeout} },
+  );
+
+  # Add attr to private _attr hash (except timeout)
+  $self->{timeout} = delete $attr{timeout};
+  $self->{_attr} = { %attr };
+  foreach (keys %{$self->{_attr}}) {
+    if (exists $params{$_}) {
+      $self->{_attr}->{$_} = $params{$_};
+    } else {
+      $self->{_attr}->{$_} = $self->{_attr}->{$_}->{default} 
+          if ref ($self->{_attr}->{$_}) eq 'HASH' &&
+              exists $self->{_attr}->{$_}->{default};
+    }
+  }
+  # Chomp _attr values
+  chomp foreach values %{$self->{_attr}};
+
+  # Setup initial args list
+  $self->{_args} = [ grep { exists $_->{spec} } @ARGS ];
+
+  $self
+}
+
+sub new
+{
+  my $class = shift;
+  my $self = bless {}, $class;
+  $self->_init(@_);
+}
+
+sub add_arg {
+       my $self = shift;
+       my %arg = @_;
+       push (@{$self->{_args}}, \%arg);
+}
+
+sub getopts { 
+  my $self = shift;
+  my %commandline = ();
+  my @params = map { $_->{spec} } @{$self->{_args}};
+  if (! GetOptions(\%commandline, @params)) {
+    $self->print_help();
+    exit 0;
+  } else {
+    no strict 'refs';
+    do { $self->print_help(); exit 0; } if $commandline{help};
+    do { $self->print_version(); exit 0 } if $commandline{version};
+    do { $self->print_usage(); exit 0 } if $commandline{usage};
+    foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; } @{$self->{_args}}) {
+      my $field = $_;
+      *{"$field"} = sub {
+        return $self->{opts}->{$field};
+      };
+    }
+    foreach (grep { exists $_->{default} } @{$self->{_args}}) {
+      $_->{spec} =~ /^([\w\-]+)/;
+      my $spec = $1;
+      $self->{opts}->{$spec} = $_->{default};
+    }
+    foreach (keys %commandline) {
+      $self->{opts}->{$_} = $commandline{$_};
+    }
+  }
+}
+
+sub get {
+       my $self = shift;
+       my $opt = shift;
+       return $self->{opts}->{$opt};
+}
+
+sub print_help {
+  my $self = shift;
+  $self->print_version();
+  printf "\n%s\n", $self->{_attr}->{license};
+  printf "\n%s\n\n", $self->{_attr}->{blurb};
+  $self->print_usage();
+  foreach (@{$self->{_args}}) {
+    printf " %s\n", $_->{help};
+  }
+  exit 0;
+}
+
+sub print_usage {
+  my $self = shift;
+  printf $self->{_attr}->{usage}, $self->{_attr}->{plugin};
+  print "\n";
+}
+
+sub print_version {
+  my $self = shift;
+  printf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version};
+  printf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url};
+  print "\n";
+}
+
+sub print_license {
+  my $self = shift;
+  printf "%s\n", $self->{_attr}->{license};
+  print "\n";
+}
+
+
+package SNMP::Utils;
+
+use strict;
+
+{
+  sub get_indices {
+    my $oids = shift;
+    my $entry = shift;
+    my $numindices = shift;
+    # find all oids beginning with $entry
+    # then skip one field for the sequence
+    # then read the next numindices fields
+    my $entrypat = $entry;
+    $entrypat =~ s/\./\\\./g;
+    my @indices = map {
+        /^$entrypat\.\d+\.(.*)/ && $1;
+    } grep {
+        /^$entrypat/
+    } keys %{$oids};
+    my %seen = ();
+    my @o = map {[split /\./]} sort grep !$seen{$_}++, @indices;
+    return @o;
+  }
+
+  sub get_size {
+    my $oids = shift;
+    my $entry = shift;
+    my $entrypat = $entry;
+    $entrypat =~ s/\./\\\./g;
+    my @entries = grep {
+        /^$entrypat/
+    } keys %{$oids};
+    return scalar(@entries);
+  }
+
+  sub get_object {
+    my $oids = shift;
+    my $object = shift;
+    my @indices = @_;
+    #my $oid = $object.'.'.join('.', @indices);
+    my $oid = $object;
+    $oid .= '.'.join('.', @indices) if (@indices);
+    return $oids->{$oid};
+  }
+
+  sub get_object_value {
+    my $oids = shift;
+    my $object = shift;
+    my $values = shift;
+    my @indices = @_;
+    my $key = get_object($oids, $object, @indices);
+    if (defined $key) {
+      return $values->{$key};
+    } else {
+      return undef;
+    }
+  }
+
+  #SNMP::Utils::counter([$idxs1, $idxs2], $idx1, $idx2),
+  # this flattens a n-dimensional array and returns the absolute position
+  # of the element at position idx1,idx2,...,idxn
+  # element 1,2 in table 0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2 is at pos 6
+  sub get_number {
+    my $indexlists = shift; #, zeiger auf array aus [1, 2]
+    my @element = @_;
+    my $dimensions = scalar(@{$indexlists->[0]});
+    my @sorted = ();
+    my $number = 0;
+    if ($dimensions == 1) {
+      @sorted =
+          sort { $a->[0] <=> $b->[0] } @{$indexlists};
+    } elsif ($dimensions == 2) {
+      @sorted =
+          sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @{$indexlists};
+    } elsif ($dimensions == 3) {
+      @sorted =
+          sort { $a->[0] <=> $b->[0] || 
+                 $a->[1] <=> $b->[1] ||
+                 $a->[2] <=> $b->[2] } @{$indexlists};
+    }
+    foreach (@sorted) {
+      if ($dimensions == 1) {
+        if ($_->[0] == $element[0]) {
+          last;
+        }
+      } elsif ($dimensions == 2) {
+        if ($_->[0] == $element[0] && $_->[1] == $element[1]) {
+          last;
+        }
+      } elsif ($dimensions == 3) {
+        if ($_->[0] == $element[0] && 
+            $_->[1] == $element[1] &&
+            $_->[2] == $element[2]) {
+          last;
+        }
+      }
+      $number++;
+    }
+    return ++$number;
+  }
+
+}
+
+package HP::Proliant::Component::EventSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    events => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    $self = HP::Proliant::Component::EventSubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    $self = HP::Proliant::Component::EventSubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+  # repair dates
+  my $lasttime = 0;
+  for my $event (reverse @{$self->{events}}) {
+    if ($event->{cpqHeEventLogUpdateTime} != 0) {
+      $lasttime = $event->{cpqHeEventLogUpdateTime};
+    } else {
+      $event->{cpqHeEventLogUpdateTime} = $lasttime;
+    }
+  }
+  # maybe the most recent events had zero timestamps.
+  # fill them up with timestamps from the past.
+  for my $event (@{$self->{events}}) {
+    if ($event->{cpqHeEventLogUpdateTime} != 0) {
+      $lasttime = $event->{cpqHeEventLogUpdateTime};
+    } else {
+      $event->{cpqHeEventLogUpdateTime} = $lasttime;
+    }
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking events');
+  if (scalar (@{$self->{events}}) == 0) {
+    #$self->overall_check(); 
+    $self->add_info('no events found');
+  } else {
+    foreach (sort { $a->{cpqHeEventLogEntryNumber} <=> $b->{cpqHeEventLogEntryNumber}}
+        @{$self->{events}}) {
+      $_->check($self->{warningtime}, $self->{criticaltime});
+    }
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{events}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::EventSubsystem::Event;
+our @ISA = qw(HP::Proliant::Component::EventSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+{
+  our $interesting_events = {
+    # POST Error: 201-Memory Error Multi-bit error occurred during memory initialization, Board 1, Bank B. Bank containing DIMM(s) has been disabled..
+    # POST Error: 201-Memory Error Single-bit error occured during memory initialization, Board 1, DIMM 1. Bank containing DIMM(s) has been disabled..
+    # POST Error: 207-Memory initialization error on Memory Board 5 DIMM 7. The operating system may not have access to all of the memory installed in the system..
+    # POST Error: 207-Invalid Memory Configuration-Mismatched DIMMs within DIMM Bank Memory in Bank A Not Utilized..  
+    # POST Error: 210 - Quick Path Interconnect (QPI) Link Degradation. A QPI link is operating in a degraded performace state..
+    'POST Messages' => [
+      '201-Memory', '207-Memory',
+      '210\s*-\s*Quick Path Interconnect.*degraded.*'
+    ],
+    'Main Memory' => [
+      'Corrected Memory Error threshold exceeded',
+    ],
+  };
+}
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqHeEventLogEntryNumber => $params{cpqHeEventLogEntryNumber},
+    cpqHeEventLogEntrySeverity => lc $params{cpqHeEventLogEntrySeverity},
+    cpqHeEventLogEntryClass => $params{cpqHeEventLogEntryClass},
+    cpqHeEventLogEntryCount => $params{cpqHeEventLogEntryCount} || 1,
+    cpqHeEventLogInitialTime => $params{cpqHeEventLogInitialTime},
+    cpqHeEventLogUpdateTime => $params{cpqHeEventLogUpdateTime},
+    cpqHeEventLogErrorDesc => $params{cpqHeEventLogErrorDesc},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  if (! $self->{cpqHeEventLogInitialTime}) {
+    $self->{cpqHeEventLogInitialTime} = $self->{cpqHeEventLogUpdateTime};
+  }
+  # 
+  #
+  #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  #                      |warn                 |crit                          |now
+  #
+  #<----- ignore -------><----- warning ------><---------- critical --------->
+  #
+  # If we have --eventrange <warnlookback>/<critlookback>
+  #  Very young events are shown as critical
+  #  If the event gets older, it is shown as warning
+  #  At some time, the event is no longer shown
+  # Without --eventrange the event is shown as critical until you manually repair it
+  if ($params{runtime}->{options}->{eventrange}) {
+    my ($warningrange, $criticalrange) = split(/\//, $params{runtime}->{options}->{eventrange});
+    if (! $criticalrange) {
+      $criticalrange = $warningrange;
+    }
+    if ($criticalrange =~ /^(\d+)[s]*$/) {
+      $criticalrange = $1;
+    } elsif ($criticalrange =~ /^(\d+)m$/) {
+      $criticalrange = $1 * 60;
+    } elsif ($criticalrange =~ /^(\d+)h$/) {
+      $criticalrange = $1 * 3600;
+    } elsif ($criticalrange =~ /^(\d+)d$/) {
+      $criticalrange = $1 * 3600 * 24;
+    } else {
+      die "range has to be <number>[smhd]";
+    }
+    if ($warningrange =~ /^(\d+)[s]*$/) {
+      $warningrange = $1;
+    } elsif ($warningrange =~ /^(\d+)m$/) {
+      $warningrange = $1 * 60;
+    } elsif ($warningrange =~ /^(\d+)h$/) {
+      $warningrange = $1 * 3600;
+    } elsif ($warningrange =~ /^(\d+)d$/) {
+      $warningrange = $1 * 3600 * 24;
+    } else {
+      die "range has to be <number>[smhd]";
+    }
+    $self->{warningtime} = time - $warningrange;
+    $self->{criticaltime} = time - $criticalrange;
+  } else {
+    $self->{warningtime} = 0;
+    $self->{criticaltime} = 0;
+  }
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('evt', $self->{cpqHeEventLogEntryNumber});
+  # only check severity "critical" and "caution"
+  # optional: only check interesting events
+  # POST events only if they date maximum from reboot-5min
+  # younger than critical? -> critical
+  # 
+  my $uptime = do { local (@ARGV, $/) = "/proc/uptime"; my $x = <>; close ARGV; $x };
+  # also watch 10 minutes of booting before the operating system starts ticking
+  my $boottime = time - int((split(/\s+/, $uptime))[0]) - 600;
+  $self->add_info(sprintf "Event: %d Added: %s Class: (%s) %s %s",
+      $self->{cpqHeEventLogEntryNumber},
+      $self->{cpqHeEventLogUpdateTime},
+      $self->{cpqHeEventLogEntryClass},
+      $self->{cpqHeEventLogEntrySeverity},
+      $self->{cpqHeEventLogErrorDesc});
+  if ($self->{cpqHeEventLogEntrySeverity} eq "caution" ||
+      $self->{cpqHeEventLogEntrySeverity} eq "critical") {
+    if ($self->{cpqHeEventLogUpdateTime} >= $boottime) {
+      foreach my $class (keys %{$HP::Proliant::Component::EventSubsystem::Event::interesting_events}) {
+        foreach my $pattern (@{$HP::Proliant::Component::EventSubsystem::Event::interesting_events->{$class}}) {
+          if ($self->{cpqHeEventLogErrorDesc} =~ /$pattern/) {
+            if ($self->{cpqHeEventLogUpdateTime} < $self->{warningtime}) {
+              # you didn't care for this problem too long.
+              # don't say i didn't warn you.
+              if (0) {
+                # auto-ack?
+              } 
+            } elsif ($self->{cpqHeEventLogUpdateTime} < $self->{criticaltime}) {
+              $self->add_message(WARNING, $self->{info});
+            } else {
+              $self->add_message(CRITICAL, $self->{info});
+            }
+          }
+        }
+      }
+    }
+  } else {
+    # info, repair...
+  }
+}
+
+sub dump { 
+  my $self = shift;
+  printf "[EVENT_%s]\n", $self->{cpqHeEventLogEntryNumber};
+  foreach (qw(cpqHeEventLogEntryNumber cpqHeEventLogEntrySeverity
+      cpqHeEventLogEntryCount cpqHeEventLogInitialTime
+      cpqHeEventLogUpdateTime cpqHeEventLogErrorDesc)) {
+    if ($_ =~ /.*Time$/) {
+      printf "%s: %s\n", $_, scalar localtime $self->{$_};
+    } else {
+      printf "%s: %s\n", $_, $self->{$_};
+    }
+  }
+  printf "info: %s\n\n", $self->{info};
+}
+
+
+
+package HP::Proliant::Component::EventSubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::EventSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+use Time::Local;
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    events => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my %tmpevent = (
+    runtime => $params{runtime},
+  );
+  my $inblock = 0;
+  foreach (grep(/^iml/, split(/\n/, $self->{rawdata}))) {
+    s/^iml\s*//g;
+    if (/^Event:\s+(\d+)\s+[\w]+:\s+(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+)/) {
+      # Event: 31 Added: 09/22/2011 05:11
+      #         1         2  3    4  5  6
+      $tmpevent{cpqHeEventLogEntryNumber} = $1;
+      if ($4 == 0) {
+        # Event: 29 Added: 00/00/0000 00:00
+        $tmpevent{cpqHeEventLogUpdateTime} = 0;
+      } else {
+        eval {
+          $tmpevent{cpqHeEventLogUpdateTime} = timelocal(0, $6, $5, $3, $2 - 1, $4);
+        };
+        if ($@) {
+          # Event: 10 Added: 27/27/2027 27:27
+          $tmpevent{cpqHeEventLogUpdateTime} = 0;
+        }
+      }
+      $inblock = 1;
+    } elsif (/^(\w+):\s+(.*?)\s+\-\s+(.*)/) {
+      $tmpevent{cpqHeEventLogEntrySeverity} = $1;
+      $tmpevent{cpqHeEventLogEntryClass} = $2;
+      $tmpevent{cpqHeEventLogErrorDesc} = $3;
+      if ($tmpevent{cpqHeEventLogErrorDesc} =~ /.*?:\s+(\d+)/) {
+          $tmpevent{cpqHeEventLogEntryCode} = $1;
+      } else {
+          $tmpevent{cpqHeEventLogEntryCode} = 0;
+      }
+    } elsif (/^\s*$/) {
+      if ($inblock) {
+        $inblock = 0;
+        push(@{$self->{events}},
+            HP::Proliant::Component::EventSubsystem::Event->new(%tmpevent));
+        %tmpevent = (
+          runtime => $params{runtime},
+        );
+      }
+    }
+  }
+  if ($inblock) {
+    push(@{$self->{events}},
+        HP::Proliant::Component::EventSubsystem::Event->new(%tmpevent));
+  }
+}
+
+
+
+package HP::Proliant::Component::EventSubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::EventSubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+use Time::Local;
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    events => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->overall_init(%params);
+  $self->init(%params);
+  return $self;
+}
+
+sub overall_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  # overall
+  my $cpqHeEventLogSupported  = '1.3.6.1.4.1.232.6.2.11.1';
+  my $cpqHeEventLogSupportedValue = {
+    1 => 'other',
+    2 => 'notSupported',
+    3 => 'supported',
+    4 => 'clear',
+  };
+  my $cpqHeEventLogCondition  = '1.3.6.1.4.1.232.6.2.11.2';
+  my $cpqHeEventLogConditionValue = {
+    1 => 'other',
+    2 => 'ok',
+    3 => 'degraded',
+    4 => 'failed',
+  };
+  $self->{eventstatus} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqHeEventLogCondition,
+      $cpqHeEventLogSupportedValue);
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeEventLogEntry => "1.3.6.1.4.1.232.6.2.11.3.1",
+      cpqHeEventLogEntryNumber => "1.3.6.1.4.1.232.6.2.11.3.1.1",
+      cpqHeEventLogEntrySeverity => "1.3.6.1.4.1.232.6.2.11.3.1.2",
+      cpqHeEventLogEntryClass => "1.3.6.1.4.1.232.6.2.11.3.1.3",
+      cpqHeEventLogEntryCode => "1.3.6.1.4.1.232.6.2.11.3.1.4",
+      cpqHeEventLogEntryCount => "1.3.6.1.4.1.232.6.2.11.3.1.5",
+      cpqHeEventLogInitialTime => "1.3.6.1.4.1.232.6.2.11.3.1.6",
+      cpqHeEventLogUpdateTime => "1.3.6.1.4.1.232.6.2.11.3.1.7",
+      cpqHeEventLogErrorDesc => "1.3.6.1.4.1.232.6.2.11.3.1.8",
+
+      cpqHeEventLogEntryClassValue => {
+          #  2 Fan Failure (Fan 1, Location I/O Board)
+          #    Internal Storage System Overheating (Slot 0, Zone 1, Location Storage, Temperature Unknown)
+          #    System Fans Not Redundant (Location I/O Board)
+          #  MY MUSTARD: only critical events should lead to an alert, if at all. The caution events mean "loss of redundancy".
+          #              We monitor temperatures and fan status anyway.
+          #2 => "",
+          #  3 Corrected Memory Error threshold exceeded (System Memory, Memory Module 1)
+          #    Uncorrectable Memory Error detected by ROM-based memory validation (Board 1, Memory Module 4)
+          #  MY MUSTARD: threshold exceeded is caution. Uncorrectable errors are critical. Both should be detected anyway.
+          3 => "Main Memory",
+          #  4 Accelerator Cache Memory Parity Error (Socket 1)
+          #4 => "",
+          #  5 Processor Correctable error threshold exceeded (Board 0, Processor 2)
+          #5 => "",
+          #  6 Unrecoverable Intermodule Bus error (Error code 0x00000000)
+          #6 => "",
+          #  8 PCI Bus Error (Slot 0, Bus 0, Device 0, Function 0)
+          8 => "PCI Bus",
+          # 10 1720-S.M.A.R.T. Hard Drive Detects Imminent Failure
+          #    POST Error: 201-Memory Error Multi-bit error occurred during memory initialization, Board 1, Bank B. Bank containing DIMM(s) has been disabled..
+          #    POST Error: 201-Memory Error Single-bit error occured during memory initialization, Board 1, DIMM 1. Bank containing DIMM(s) has been disabled..
+          #    POST Error: 207-Memory Configuration Warning - memory boards should be installed sequentially.
+          #    POST Error: 210-Memory Board Failure on board 4.
+          #    POST Error: 210-Memory Board Power Fault on board 3.
+          #    POST Error: 207-Memory initialization error on Memory Board 5 DIMM 7. The operating system may not have access to all of the memory installed in the system..         
+          #    POST Error: 207-Invalid Memory Configuration-Mismatched DIMMs within DIMM Bank Memory in Bank A Not Utilized..
+          10 => "POST Messages",
+          11 => "Power Subsystem",
+          13 => "ASR",
+          # 14 Automatic Operating System Shutdown Initiated Due to Overheat Condition
+          #    Automatic Operating System Shutdown Initiated Due to Fan Failure
+          #    Blue Screen Trap (BugCheck, STOP: 0x00000050 (0x9CB2C5B4, 0x00000001, 0x00000004, 0x00000000))
+          #    Operating System failure (BugCheck, STOP: 0x000000AB (0x00000005, 0x00000488, 0x00000000, 0x00000002))
+          14 => "OS Class",
+          # 15 Unknown Event (Class 15, Code 255)
+          #15 => "",
+          # 17 Network Adapter Link Down (Slot 0, Port 4)
+          #    Network Adapters Redundancy Reduced (Slot 0, Port 1)
+          17 => "Network Adapter",
+          # 19 Drive Array Device Failure (Slot 0, Bus 2, Bay 4)
+          #    Internal SAS Enclosure Device Failure (Bay 1, Box 1, Port 2I, Slot 1)
+          #19 => "",
+          # 20 An Unrecoverable System Error (NMI) has occurred
+          #    Unrecoverable System Error has occurred (Error code 0x01AE0E2F, 0x00000000)
+          20 => "Unrecoverable System Error",
+          # 32 ROM flashed (New version: 01/09/2008)
+          32 => "System Revision",
+          # 33 IML Cleared (Administrator)
+          #    IML cleared through HP ProLiant Health Agent (cmahealthd)
+          #    Insight Diagnostics Note: Physisches Festplattenlaufwerk 5, Controller Steckplatz 0-Diagnosis: Fehlgeschlagen
+          33 => "Maintenance Note",
+          # 34 New Chassis Connected (Enclosure Address 27AC)
+          #    Loss Of Chassis Connectivity (Enclosure Serial Number 8004******)
+          #    Server Blade Enclosure Server Blade Inserted (Slot 16, Enclosure Address 0000)
+          #34 => "",
+      },
+      cpqHeEventLogEntrySeverityValue => {
+          2 => "informational",
+          3 => "infoWithAlert",
+          6 => "repaired",
+          9 => "caution",
+          15 => "critical",
+      },
+      # Time 
+      # 07 D8 09 02 11 11
+  };
+  # INDEX { cpqHeEventLogEntryNumber }
+  foreach ($self->get_entries($oids, 'cpqHeEventLogEntry')) {
+    if ($_->{cpqHeEventLogInitialTime} =~ /^(([0-9a-fA-F]{2})( [0-9a-fA-F]{2})*)\s*$/) {
+      $_->{cpqHeEventLogInitialTime} =~ s/ //;
+      my  ($year, $month, $day, $hour, $min) = map { hex($_) } split(/\s+/, $_->{cpqHeEventLogInitialTime});
+      if ($year == 0) {
+        $_->{cpqHeEventLogInitialTime} = 0;
+      } else {
+        eval {
+          $_->{cpqHeEventLogInitialTime} = timelocal(0, $min, $hour, $day, $month - 1, $year);
+        };
+        if ($@) {
+          $_->{cpqHeEventLogInitialTime} = 0;
+        }
+      }
+    }
+    if ($_->{cpqHeEventLogUpdateTime} =~ /^(([0-9a-fA-F]{2})( [0-9a-fA-F]{2})*)\s*$/) {
+      $_->{cpqHeEventLogUpdateTime} =~ s/ //;
+      my  ($year, $month, $day, $hour, $min) = map { hex($_) } split(/\s+/, $_->{cpqHeEventLogUpdateTime});
+      if ($year == 0) {
+        $_->{cpqHeEventLogUpdateTime} = 0;
+      } else {
+        eval {
+          $_->{cpqHeEventLogUpdateTime} = timelocal(0, $min, $hour, $day, $month - 1, $year);
+        };
+        if ($@) {
+          $_->{cpqHeEventLogUpdateTime} = 0;
+        }
+      }
+    }
+    if ($_->{cpqHeEventLogErrorDesc} =~ /^(([0-9a-fA-F]{2})(\s+[0-9a-fA-F]{2})*)\s*$/) {
+      $_->{cpqHeEventLogErrorDesc} = join "", map { chr($_) } map { if (hex($_) > 127) { 20; } else { hex($_) } } split(/\s+/, $_->{cpqHeEventLogErrorDesc});
+    }
+    push(@{$self->{events}},
+        HP::Proliant::Component::EventSubsystem::Event->new(%{$_}));
+  }
+}
+
+sub overall_check {
+  my $self = shift;
+  my $result = 0;
+  $self->blacklist('oe', '');
+  if ($self->{eventstatus}) {
+    if ($self->{eventstatus} eq "ok") {
+      $result = 0;
+      $self->add_info('eventlog system is ok');
+    } else {
+      $result = 0;
+      $self->add_info(sprintf "eventlog system is %s", $self->{eventstatus});
+    }
+  } else {
+    $result = 0;
+    $self->add_info('no event status found');
+  }
+}
+
+
+package HP::Proliant::Component::PowersupplySubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    powersupplies => [],
+    powerconverters => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::PowersupplySubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::PowersupplySubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking power supplies');
+  if (scalar (@{$self->{powersupplies}}) == 0) {
+    #$self->overall_check();
+  } else {
+    foreach (@{$self->{powersupplies}}) {
+      $_->check();
+    }
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{powersupplies}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::PowersupplySubsystem::Powersupply;
+our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqHeFltTolPowerSupplyChassis => $params{cpqHeFltTolPowerSupplyChassis},
+    cpqHeFltTolPowerSupplyBay => $params{cpqHeFltTolPowerSupplyBay},
+    cpqHeFltTolPowerSupplyPresent => $params{cpqHeFltTolPowerSupplyPresent},
+    cpqHeFltTolPowerSupplyCondition => $params{cpqHeFltTolPowerSupplyCondition},
+    cpqHeFltTolPowerSupplyRedundant => $params{cpqHeFltTolPowerSupplyRedundant},
+    blacklisted => 0,
+    info => undef,
+    extendexinfo => undef,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('p', $self->{cpqHeFltTolPowerSupplyBay});
+  if ($self->{cpqHeFltTolPowerSupplyPresent} eq "present") {
+    if ($self->{cpqHeFltTolPowerSupplyCondition} ne "ok") {
+      if ($self->{cpqHeFltTolPowerSupplyCondition} eq "other") {
+        $self->add_info(sprintf "powersupply %d is missing",
+            $self->{cpqHeFltTolPowerSupplyBay});
+      } else {
+        $self->add_info(sprintf "powersupply %d needs attention (%s)",
+            $self->{cpqHeFltTolPowerSupplyBay},
+            $self->{cpqHeFltTolPowerSupplyCondition});
+      }
+      $self->add_message(CRITICAL, $self->{info});
+    } else {
+      $self->add_info(sprintf "powersupply %d is %s",
+          $self->{cpqHeFltTolPowerSupplyBay},
+          $self->{cpqHeFltTolPowerSupplyCondition});
+    }
+    $self->add_extendedinfo(sprintf "ps_%s=%s",
+        $self->{cpqHeFltTolPowerSupplyBay},
+        $self->{cpqHeFltTolPowerSupplyCondition});
+  } else {
+    $self->add_info(sprintf "powersupply %d is %s",
+        $self->{cpqHeFltTolPowerSupplyBay},
+        $self->{cpqHeFltTolPowerSupplyPresent});
+    $self->add_extendedinfo(sprintf "ps_%s=%s",
+        $self->{cpqHeFltTolPowerSupplyBay},
+        $self->{cpqHeFltTolPowerSupplyPresent});
+  }
+}
+
+
+sub dump {
+  my $self = shift;
+  printf "[PS_%s]\n", $self->{cpqHeFltTolPowerSupplyBay};
+  foreach (qw(cpqHeFltTolPowerSupplyBay cpqHeFltTolPowerSupplyChassis
+      cpqHeFltTolPowerSupplyPresent cpqHeFltTolPowerSupplyCondition
+      cpqHeFltTolPowerSupplyRedundant)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n\n", $self->{info};
+}
+
+
+package HP::Proliant::Component::PowersupplySubsystem::Powerconverter;
+our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+
+    cpqHePowerConvEntry => $params{cpqHePowerConvEntry},
+    cpqHePowerConvChassis => $params{cpqHePowerConvChassis},
+    cpqHePowerConvIndex => $params{cpqHePowerConvIndex},
+    cpqHePowerConvPresent => $params{cpqHePowerConvPresent},
+    cpqHePowerConvRedundant => $params{cpqHePowerConvRedundant},
+    cpqHePowerConvCondition => $params{cpqHePowerConvCondition},
+    cpqHePowerConvHwLocation => $params{cpqHePowerConvHwLocation},
+    blacklisted => 0,
+    info => undef,
+    extendexinfo => undef,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('pc', $self->{cpqHePowerConvIndex});
+  if ($self->{cpqHePowerConvPresent} eq "present") {
+    if ($self->{cpqHePowerConvCondition} ne "ok") {
+      if ($self->{cpqHePowerConvCondition} eq "other") {
+        $self->add_info(sprintf "powerconverter %d is missing",
+            $self->{cpqHePowerConvIndex});
+      } else {
+        $self->add_info(sprintf "powerconverter %d needs attention (%s)",
+            $self->{cpqHePowerConvIndex},
+            $self->{cpqHePowerConvCondition});
+      }
+      $self->add_message(CRITICAL, $self->{info});
+    } else {
+      $self->add_info(sprintf "powerconverter %d is %s",
+          $self->{cpqHePowerConvIndex},
+          $self->{cpqHePowerConvCondition});
+    }
+    $self->add_extendedinfo(sprintf "pc_%s=%s",
+        $self->{cpqHePowerConvIndex},
+        $self->{cpqHePowerConvCondition});
+  } else {
+    $self->add_info(sprintf "powerconverter %d is %s",
+        $self->{cpqHePowerConvIndex},
+        $self->{cpqHePowerConvPresent});
+    $self->add_extendedinfo(sprintf "pc_%s=%s",
+        $self->{cpqHePowerConvIndex},
+        $self->{cpqHePowerConvPresent});
+  }
+}
+
+
+sub dump {
+  my $self = shift;
+  printf "[PS_%s]\n", ($self->{cpqHePowerConvChassis} ? $self->{cpqHePowerConvChassis}.":" : "").$self->{cpqHePowerConvIndex};
+  foreach (qw(cpqHePowerConvIndex cpqHePowerConvPresent cpqHePowerConvRedundant cpqHePowerConvCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n\n", $self->{info};
+}
+
+
+
+package HP::Proliant::Component::PowersupplySubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    powersupplies => [],
+    powerconverters => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my %tmpps = (
+    runtime => $self->{runtime},
+    cpqHeFltTolPowerSupplyChassis => 1,
+  );
+  my $inblock = 0;
+  foreach (grep(/^powersupply/, split(/\n/, $self->{rawdata}))) {
+    s/^powersupply\s*//g;
+    if (/^Power supply #(\d+)/) {
+      if ($inblock) {
+        $inblock = 0;
+        push(@{$self->{powersupplies}},
+            HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%tmpps));
+        %tmpps = (
+          runtime => $self->{runtime},
+          cpqHeFltTolPowerSupplyChassis => 1,
+        );
+      }
+      $tmpps{cpqHeFltTolPowerSupplyBay} = $1;
+      $inblock = 1;
+    } elsif (/\s*Present\s+:\s+(\w+)/) {
+      $tmpps{cpqHeFltTolPowerSupplyPresent} = lc $1 eq 'yes' ? 'present' :
+          lc $1 eq 'no' ? 'absent': 'other';
+    } elsif (/\s*Redundant\s*:\s+(\w+)/) {
+      $tmpps{cpqHeFltTolPowerSupplyRedundant} = lc $1 eq 'yes' ? 'redundant' :
+          lc $1 eq 'no' ? 'notRedundant' : 'other';
+    } elsif (/\s*Condition\s*:\s+(\w+)/) {
+      $tmpps{cpqHeFltTolPowerSupplyCondition} = lc $1;
+    } elsif (/\s*Power Supply not present/) {
+      $tmpps{cpqHeFltTolPowerSupplyPresent} = "absent";
+      $tmpps{cpqHeFltTolPowerSupplyCondition} = "other";
+      $tmpps{cpqHeFltTolPowerSupplyRedundant} = "notRedundant";
+    } elsif (/^\s*$/) {
+      if ($inblock) {
+        $inblock = 0;
+        push(@{$self->{powersupplies}},
+            HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%tmpps));
+        %tmpps = (
+          runtime => $self->{runtime},
+          cpqHeFltTolPowerSupplyChassis => 1,
+        );
+      }
+    }
+  }
+  if ($inblock) {
+    push(@{$self->{powersupplies}},
+        HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%tmpps));
+    %tmpps = (
+      runtime => $params{runtime},
+    );
+  }
+}
+
+
+package HP::Proliant::Component::PowersupplySubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    powersupplies => [],
+    powerconverters => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeFltTolPowerSupplyEntry => "1.3.6.1.4.1.232.6.2.9.3.1",
+      cpqHeFltTolPowerSupplyChassis => "1.3.6.1.4.1.232.6.2.9.3.1.1",
+      cpqHeFltTolPowerSupplyBay => "1.3.6.1.4.1.232.6.2.9.3.1.2",
+      cpqHeFltTolPowerSupplyPresent => "1.3.6.1.4.1.232.6.2.9.3.1.3",
+      cpqHeFltTolPowerSupplyCondition => "1.3.6.1.4.1.232.6.2.9.3.1.4",
+      cpqHeFltTolPowerSupplyRedundant => "1.3.6.1.4.1.232.6.2.9.3.1.9",
+      cpqHeFltTolPowerSupplyPresentValue => {
+          1 => "other",
+          2 => "absent",
+          3 => "present",
+      },
+      cpqHeFltTolPowerSupplyConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqHeFltTolPowerSupplyRedundantValue => {
+          1 => "other",
+          2 => "notRedundant",
+          3 => "redundant",
+      },
+  };
+
+  # INDEX { cpqHeFltTolPowerSupplyChassis, cpqHeFltTolPowerSupplyBay }
+  foreach ($self->get_entries($oids, 'cpqHeFltTolPowerSupplyEntry')) {
+    push(@{$self->{powersupplies}},
+        HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%{$_}));
+  }
+
+  $oids = {
+      cpqHePowerConvEntry => "1.3.6.1.4.1.232.6.2.13.3.1",
+      cpqHePowerConvChassis => "1.3.6.1.4.1.232.6.2.13.3.1.1",
+      cpqHePowerConvIndex => "1.3.6.1.4.1.232.6.2.13.3.1.2",
+      cpqHePowerConvPresent => "1.3.6.1.4.1.232.6.2.13.3.1.3",
+      cpqHePowerConvRedundant => "1.3.6.1.4.1.232.6.2.13.3.1.6",
+      cpqHePowerConvCondition => "1.3.6.1.4.1.232.6.2.13.3.1.8",
+      cpqHePowerConvPresentValue => {
+          1 => "other",
+          2 => "absent",
+          3 => "present",
+      },
+      cpqHePowerConvRedundantValue => {
+          1 => "other",
+          2 => "notRedundant",
+          3 => "redundant",
+      },
+      cpqHePowerConvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqHePowerConvHwLocation => "1.3.6.1.4.1.232.6.2.13.3.1.9",
+  };
+
+  # INDEX { cpqHePowerConvChassis cpqHePowerConvIndex }
+  foreach ($self->get_entries($oids, 'cpqHePowerConvEntry')) {
+    push(@{$self->{powerconverters}},
+        HP::Proliant::Component::PowersupplySubsystem::Powerconverter->new(%{$_}));
+  }
+  # keine ahnung, was man damit machen kann
+
+}
+
+package HP::Proliant::Component::TemperatureSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+################################## custom_thresholds
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    temperatures => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($params{runtime}->{options}->{customthresholds}) {
+    if (-f $params{runtime}->{options}->{customthresholds}) {
+      $params{runtime}->{options}->{customthresholds} = 
+          do { local (@ARGV, $/) = 
+              $params{runtime}->{options}->{customthresholds}; <> };
+    }
+    foreach my $ct_items
+        (split(/\//, $params{runtime}->{options}->{customthresholds})) {
+      if ($ct_items =~ /^(\d+):(\d+)$/) {
+        $params{runtime}->{options}->{thresholds}->{$1} = $2;
+      } else {
+        die sprintf "invalid threshold %s", $ct_items;
+      }
+    }
+  }
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::TemperatureSubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::TemperatureSubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking temperatures');
+  if (scalar (@{$self->{temperatures}}) == 0) {
+    #$self->overall_check(); 
+    $self->add_info('no temperatures found');
+  } else {
+    foreach (sort { $a->{cpqHeTemperatureIndex} <=> $b->{cpqHeTemperatureIndex}}
+        @{$self->{temperatures}}) {
+      $_->check();
+    }
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{temperatures}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::TemperatureSubsystem::Temperature;
+our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqHeTemperatureChassis => $params{cpqHeTemperatureChassis},
+    cpqHeTemperatureIndex => $params{cpqHeTemperatureIndex},
+    cpqHeTemperatureLocale => $params{cpqHeTemperatureLocale},
+    cpqHeTemperatureCelsius => $params{cpqHeTemperatureCelsius},
+    cpqHeTemperatureThresholdCelsius => $params{cpqHeTemperatureThresholdCelsius},
+    cpqHeTemperatureCondition => $params{cpqHeTemperatureCondition},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($params{runtime}->{options}->{celsius}) {
+    $self->{cpqHeTemperatureUnits} = 'C';
+    $self->{cpqHeTemperature} = $self->{cpqHeTemperatureCelsius};
+    $self->{cpqHeTemperatureThreshold} = 
+        $self->{cpqHeTemperatureThresholdCelsius};
+  } else {
+    $self->{cpqHeTemperatureUnits} = 'F';
+    $self->{cpqHeTemperature} = 
+        (($self->{cpqHeTemperatureCelsius} * 9) / 5) + 32;
+    $self->{cpqHeTemperatureThreshold} = 
+        (($self->{cpqHeTemperatureThresholdCelsius} * 9) / 5) + 32;
+  }
+  my $index = $self->{cpqHeTemperatureIndex};
+  if (exists $params{runtime}->{options}->{thresholds}->{$index}) {
+    $self->{cpqHeTemperatureThreshold} = 
+        $params{runtime}->{options}->{thresholds}->{$index};
+        
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('t', $self->{cpqHeTemperatureIndex});
+  if ($self->{cpqHeTemperature} > $self->{cpqHeTemperatureThreshold}) {
+    $self->add_info(sprintf "%d %s temperature too high (%d%s, %d max)",
+        $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale},
+        $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits},
+        $self->{cpqHeTemperatureThreshold});
+    $self->add_message(CRITICAL, $self->{info});
+  } elsif ($self->{cpqHeTemperature} < 0) {
+    # #21 SCSI_BACKPLANE_ZONE -1C/31F 60C/140F OK - can't be true
+    $self->add_info(sprintf "%d %s temperature too low (%d%s)",
+        $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale},
+        $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits});
+    $self->add_message(CRITICAL, $self->{info});
+  } else {
+    $self->add_info(sprintf "%d %s temperature is %d%s (%d max)",
+        $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale}, 
+        $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits},
+        $self->{cpqHeTemperatureThreshold});
+  }
+  if ($self->{runtime}->{options}->{perfdata} == 2) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => sprintf('temp_%s', $self->{cpqHeTemperatureIndex}),
+        value => $self->{cpqHeTemperature},
+        warning => $self->{cpqHeTemperatureThreshold},
+        critical => $self->{cpqHeTemperatureThreshold}
+    );
+  } elsif ($self->{runtime}->{options}->{perfdata} == 1) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => sprintf('temp_%s_%s', $self->{cpqHeTemperatureIndex},
+            $self->{cpqHeTemperatureLocale}),
+        value => $self->{cpqHeTemperature},
+        warning => $self->{cpqHeTemperatureThreshold},
+        critical => $self->{cpqHeTemperatureThreshold}
+    );
+  } 
+  $self->add_extendedinfo(sprintf "temp_%s=%d",
+      $self->{cpqHeTemperatureIndex},
+      $self->{cpqHeTemperature});
+}
+
+sub dump { 
+  my $self = shift;
+  printf "[TEMP_%s]\n", $self->{cpqHeTemperatureIndex};
+  foreach (qw(cpqHeTemperatureChassis cpqHeTemperatureIndex 
+      cpqHeTemperatureLocale cpqHeTemperatureCelsius cpqHeTemperatureThreshold
+      cpqHeTemperatureCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n\n", $self->{info};
+}
+
+
+
+package HP::Proliant::Component::TemperatureSubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    temperatures => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my $tempcnt = 1;
+  foreach (grep(/^temp/, split(/\n/, $params{rawdata}))) {
+    s/^temp\s*//g;
+    if (/^#(\d+)\s+([\w_\/\-#]+)\s+(-*\d+)C\/(\d+)F\s+(\d+)C\/(\d+)F/) {
+      my %params = ();
+      $params{runtime} = $self->{runtime};
+      $params{cpqHeTemperatureChassis} = 1;
+      $params{cpqHeTemperatureIndex} = $1;
+      $params{cpqHeTemperatureLocale} = lc $2;
+      $params{cpqHeTemperatureCelsius} = $3;
+      $params{cpqHeTemperatureThresholdCelsius} = $5;
+      $params{cpqHeTemperatureCondition} = 'unknown';
+      push(@{$self->{temperatures}},
+          HP::Proliant::Component::TemperatureSubsystem::Temperature->new(
+              %params));
+    } elsif (/^#(\d+)\s+([\w_\/\-#]+)\s+\-\s+(\d+)C\/(\d+)F/) {
+      # #3        CPU#2                -       0C/0F
+      $self->trace(2, sprintf "skipping temperature %s", $_);
+    } elsif (/^#(\d+)\s+([\w_\/\-#]+)\s+(\d+)C\/(\d+)F\s+\-/) {
+      # #3        CPU#2                0C/0F       -
+      $self->trace(2, sprintf "skipping temperature %s", $_);
+    } elsif (/^#(\d+)\s+([\w_\/\-#]+)\s+\-\s+\-/) {
+      # #3        CPU#2                -       -
+      $self->trace(2, sprintf "skipping temperature %s", $_);
+    } elsif (/^#(\d+)/) {
+      $self->trace(0, sprintf "send this to lausser: %s", $_);
+    }
+  }
+}
+
+
+package HP::Proliant::Component::TemperatureSubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    temperatures => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->overall_init(%params);
+  $self->init(%params);
+  return $self;
+}
+
+sub overall_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  # overall
+  my $cpqHeThermalTempStatus  = '1.3.6.1.4.1.232.6.2.6.3.0';
+  my $cpqHeThermalTempStatusValue = {
+    1 => 'other',
+    2 => 'ok',
+    3 => 'degraded',
+    4 => 'failed',
+  };
+  $self->{tempstatus} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqHeThermalTempStatus,
+      $cpqHeThermalTempStatusValue);
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeTemperatureEntry => "1.3.6.1.4.1.232.6.2.6.8.1",
+      cpqHeTemperatureChassis => "1.3.6.1.4.1.232.6.2.6.8.1.1",
+      cpqHeTemperatureIndex => "1.3.6.1.4.1.232.6.2.6.8.1.2",
+      cpqHeTemperatureLocale => "1.3.6.1.4.1.232.6.2.6.8.1.3",
+      cpqHeTemperatureCelsius => "1.3.6.1.4.1.232.6.2.6.8.1.4",
+      cpqHeTemperatureThresholdCelsius => "1.3.6.1.4.1.232.6.2.6.8.1.5",
+      cpqHeTemperatureCondition => "1.3.6.1.4.1.232.6.2.6.8.1.6",
+      cpqHeTemperatureLocaleValue => {
+          1 => "other",
+          2 => "unknown",
+          3 => "system",
+          4 => "systemBoard",
+          5 => "ioBoard",
+          6 => "cpu",
+          7 => "memory",
+          8 => "storage",
+          9 => "removableMedia",
+          10 => "powerSupply",
+          11 => "ambient",
+          12 => "chassis",
+          13 => "bridgeCard",
+      },
+      cpqHeTemperatureConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      }
+  };
+  # INDEX { cpqHeTemperatureChassis, cpqHeTemperatureIndex }
+  foreach ($self->get_entries($oids, 'cpqHeTemperatureEntry')) {
+    push(@{$self->{temperatures}},
+        HP::Proliant::Component::TemperatureSubsystem::Temperature->new(%{$_}));
+  }
+}
+
+sub overall_check {
+  my $self = shift;
+  my $result = 0;
+  $self->blacklist('ots', '');
+  if ($self->{tempstatus}) {
+    if ($self->{tempstatus} eq "ok") {
+      $result = 0;
+      $self->add_info('all temp sensors are within normal operating range');
+    } elsif ($self->{tempstatus} eq "degraded") {
+      $result = 1;
+      $self->add_info('a temp sensor is outside of normal operating range');
+    } elsif ($self->{tempstatus} eq "failed") {
+      $result = 2;
+      $self->add_info('a temp sensor detects a condition that could permanently
+damage the system');
+    } elsif ($self->{tempstatus} eq "other") {
+      $result = 0;
+      $self->add_info('temp sensing is not supported by this system or driver');
+    }
+  } else {
+    $result = 0;
+    $self->add_info('no global temp status found');
+  }
+}
+
+
+package HP::Proliant::Component::CpuSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+################################## scrapiron ##########
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    cpus => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::CpuSubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::CpuSubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking cpus');
+  if (scalar (@{$self->{cpus}}) == 0) {
+    # sachen gibts.....
+  #  $self->overall_check(); # sowas ist mir nur einmal untergekommen
+  } else {
+    foreach (@{$self->{cpus}}) {
+      $_->check();
+    }
+  }
+}
+
+sub num_cpus {
+  my $self = shift;
+  return scalar @{$self->{cpus}};
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{cpus}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::CpuSubsystem::Cpu;
+our @ISA = qw(HP::Proliant::Component::CpuSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqSeCpuSlot => $params{cpqSeCpuSlot},
+    cpqSeCpuUnitIndex => $params{cpqSeCpuUnitIndex},
+    cpqSeCpuName => $params{cpqSeCpuName},
+    cpqSeCpuStatus => $params{cpqSeCpuStatus},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('c', $self->{cpqSeCpuUnitIndex});
+  if ($self->{cpqSeCpuStatus} ne "ok") {
+    if ($self->{runtime}->{options}{scrapiron} &&
+        ($self->{cpqSeCpuStatus} eq "unknown")) {
+      $self->add_info(sprintf "cpu %d probably ok (%s)",
+          $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
+    } else {
+      $self->add_info(sprintf "cpu %d needs attention (%s)",
+          $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
+      $self->add_message(CRITICAL, $self->{info});
+    }
+  } else {
+    $self->add_info(sprintf "cpu %d is %s", 
+        $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
+  }
+  $self->add_extendedinfo(sprintf "cpu_%s=%s",
+      $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
+}
+
+sub dump {
+  my $self = shift;
+  printf "[CPU_%s]\n", $self->{cpqSeCpuUnitIndex};
+  foreach (qw(cpqSeCpuSlot cpqSeCpuUnitIndex cpqSeCpuName cpqSeCpuStatus)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+package HP::Proliant::Component::CpuSubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::CpuSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    cpus => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my %tmpcpu = (
+    runtime => $params{runtime},
+  );
+  my $inblock = 0;
+  foreach (grep(/^server/, split(/\n/, $self->{rawdata}))) {
+    if (/Processor:\s+(\d+)/) {
+      $tmpcpu{cpqSeCpuUnitIndex} = $1;
+      $inblock = 1;
+    } elsif (/Name\s*:\s+(.+?)\s*$/) {
+      $tmpcpu{cpqSeCpuName} = $1;
+    } elsif (/Status\s*:\s+(.+?)\s*$/) {
+      $tmpcpu{cpqSeCpuStatus} = lc $1;
+    } elsif (/Socket\s*:\s+(.+?)\s*$/) {
+      $tmpcpu{cpqSeCpuSlot} = $1;
+    } elsif (/^server\s*$/) {
+      if ($inblock) {
+        $inblock = 0;
+        push(@{$self->{cpus}},
+            HP::Proliant::Component::CpuSubsystem::Cpu->new(%tmpcpu));
+        %tmpcpu = (
+          runtime => $params{runtime},
+        );
+      }
+    }
+  }
+  if ($inblock) {
+    push(@{$self->{cpus}},
+        HP::Proliant::Component::CpuSubsystem::Cpu->new(%tmpcpu));
+  }
+}
+
+
+package HP::Proliant::Component::CpuSubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::CpuSubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    cpus => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  # CPQSTDEQ-MIB
+  my $oids = {
+      cpqSeCpuEntry => '1.3.6.1.4.1.232.1.2.2.1.1',
+      cpqSeCpuUnitIndex => '1.3.6.1.4.1.232.1.2.2.1.1.1',
+      cpqSeCpuSlot => '1.3.6.1.4.1.232.1.2.2.1.1.2',
+      cpqSeCpuName => '1.3.6.1.4.1.232.1.2.2.1.1.3',
+      cpqSeCpuStatus => '1.3.6.1.4.1.232.1.2.2.1.1.6',
+      cpqSeCpuStatusValue => {
+          1 => "unknown",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+          5 => "disabled",
+      },
+  };
+
+  # INDEX { cpqSeCpuUnitIndex }
+  foreach ($self->get_entries($oids, 'cpqSeCpuEntry')) {
+    push(@{$self->{cpus}},
+        HP::Proliant::Component::CpuSubsystem::Cpu->new(%{$_}));
+  }
+}
+
+
+package HP::Proliant::Component::FanSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+################################## fan_redundancy ##########
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    fans => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::FanSubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::FanSubsystem::CLI->new(%params);
+  } else {
+    die 'unknown method';
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking fans');
+  $self->blacklist('ff', '');
+  if (scalar (@{$self->{fans}}) == 0) {
+    $self->overall_check(); # sowas ist mir nur einmal untergekommen
+    # die maschine hatte alles in allem nur 2 oids (cpqHeFltTolFanChassis)
+    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.1 = INTEGER: 0
+    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.2 = INTEGER: 0
+  } else {
+    my $overallhealth = $self->overall_check(); 
+    foreach (@{$self->{fans}}) {
+      $_->{overallhealth} = $overallhealth;
+      $_->check();
+    }
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{fans}}) {
+    $_->dump();
+  }
+}
+
+sub get_fan_by_index {
+  my $self = shift;
+  my $index;
+  foreach (@{$self->{fans}}) {
+    return $_ if exists $_->{cpqHeFltTolFanIndex} && 
+        $_->{cpqHeFltTolFanIndex} == $index;
+  }
+  return undef;
+}
+
+
+package HP::Proliant::Component::FanSubsystem::Fan;
+our @ISA = qw(HP::Proliant::Component::FanSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  if (exists $params{cpqHeFltTolFanRedundant}) {
+    return HP::Proliant::Component::FanSubsystem::Fan::FTol->new(%params);
+  } else {
+    return HP::Proliant::Component::FanSubsystem::Fan::Thermal->new(%params);
+  }
+}
+
+
+package HP::Proliant::Component::FanSubsystem::Fan::FTol;
+our @ISA = qw(HP::Proliant::Component::FanSubsystem::Fan);
+
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqHeFltTolFanChassis => $params{cpqHeFltTolFanChassis},
+    cpqHeFltTolFanIndex => $params{cpqHeFltTolFanIndex},
+    cpqHeFltTolFanLocale => $params{cpqHeFltTolFanLocale},
+    cpqHeFltTolFanPresent => $params{cpqHeFltTolFanPresent},
+    cpqHeFltTolFanType => $params{cpqHeFltTolFanType},
+    cpqHeFltTolFanSpeed => $params{cpqHeFltTolFanSpeed},
+    cpqHeFltTolFanRedundant => $params{cpqHeFltTolFanRedundant},
+    cpqHeFltTolFanRedundantPartner => $params{cpqHeFltTolFanRedundantPartner},
+    cpqHeFltTolFanCondition => $params{cpqHeFltTolFanCondition},
+    cpqHeFltTolFanPctMax => $params{cpqHeFltTolFanPctMax}, #!!!
+    cpqHeFltTolFanHotPlug => $params{cpqHeFltTolFanHotPlug}, #!!!
+    partner => $params{partner},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if (($self->{cpqHeFltTolFanRedundant} eq 'redundant') &&
+     ((! defined $self->{cpqHeFltTolFanRedundantPartner}) ||
+     (! $self->{cpqHeFltTolFanRedundantPartner}))) {
+    $self->{cpqHeFltTolFanRedundant} = 'notRedundant';
+      # cpqHeFltTolFanRedundantPartner=0: partner not avail
+  }
+  return $self;
+} 
+
+sub check { 
+  my $self = shift;
+  $self->blacklist('f', $self->{cpqHeFltTolFanIndex});
+  $self->add_info(sprintf 'fan %d is %s, speed is %s, pctmax is %s%%, '.
+      'location is %s, redundance is %s, partner is %s',
+      $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanPresent},
+      $self->{cpqHeFltTolFanSpeed}, $self->{cpqHeFltTolFanPctMax},
+      $self->{cpqHeFltTolFanLocale}, $self->{cpqHeFltTolFanRedundant},
+      $self->{cpqHeFltTolFanRedundantPartner});
+  $self->add_extendedinfo(sprintf 'fan_%s=%d%%',
+      $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanPctMax});
+  if ($self->{cpqHeFltTolFanPresent} eq 'present') {
+    if ($self->{cpqHeFltTolFanSpeed} eq 'high') { 
+      $self->add_info(sprintf 'fan %d (%s) runs at high speed',
+          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+      $self->add_message(CRITICAL, $self->{info});
+    } elsif ($self->{cpqHeFltTolFanSpeed} ne 'normal') {
+      $self->add_info(sprintf 'fan %d (%s) needs attention',
+          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+      $self->add_message(CRITICAL, $self->{info});
+    }
+    if ($self->{cpqHeFltTolFanCondition} eq 'failed') {
+      $self->add_info(sprintf 'fan %d (%s) failed',
+          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+      $self->add_message(CRITICAL, $self->{info});
+    } elsif ($self->{cpqHeFltTolFanCondition} eq 'degraded') {
+      $self->add_info(sprintf 'fan %d (%s) degraded',
+          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+      $self->add_message(WARNING, $self->{info});
+    } elsif ($self->{cpqHeFltTolFanCondition} ne 'ok') {
+      $self->add_info(sprintf 'fan %d (%s) is not ok',
+          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+      $self->add_message(WARNING, $self->{info});
+    }
+    if ($self->{cpqHeFltTolFanRedundant} eq 'notRedundant') {
+      # sieht so aus, als waere notRedundant und partner=0 normal z.b. dl360
+      # das duerfte der fall sein, wenn nur eine cpu verbaut wurde und
+      # statt einem redundanten paar nur dummies drinstecken.
+      # "This specifies if the fan is in a redundant configuration"
+      # notRedundant heisst also sowohl nicht redundant wegen ausfall
+      # des partners als auch von haus aus nicht redundant ausgelegt
+      if ($self->{cpqHeFltTolFanRedundantPartner}) {
+        # nicht redundant, hat aber einen partner. da muss man genauer
+        # hinschauen
+        #if (my $partner = $self->{partner}) {
+        #}
+        if ($self->{overallhealth}) {
+          # da ist sogar das system der meinung, dass etwas faul ist
+          if (! $self->{runtime}->{options}->{ignore_fan_redundancy}) {
+            $self->add_info(sprintf 'fan %d (%s) is not redundant',
+                $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+            $self->add_message(WARNING, $self->{info});
+          }
+        } else {
+          # das ist wohl so gewollt, dass einzelne fans eingebaut werden,
+          # obwohl redundante paerchen vorgesehen sind.
+          # scheint davon abzuhaengen, wieviele cpus geordert wurden.
+        }
+      }
+    } elsif ($self->{cpqHeFltTolFanRedundant} eq 'other') {
+      #seen on a dl320 g5p with bios from 2008.
+      # maybe redundancy is not supported at all
+    }
+  } elsif ($self->{cpqHeFltTolFanPresent} eq 'failed') { # from cli
+    $self->add_info(sprintf 'fan %d (%s) failed',
+        $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+    $self->add_message(CRITICAL, $self->{info});
+  } elsif ($self->{cpqHeFltTolFanPresent} eq 'absent') {
+    $self->add_info(sprintf 'fan %d (%s) needs attention (is absent)',
+        $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
+    # weiss nicht, ob absent auch kaputt bedeuten kann
+    # wenn nicht, dann wuerde man sich hier dumm und daemlich blacklisten
+    #$self->add_message(CRITICAL, $self->{info});
+    $self->add_message(WARNING, $self->{info}) if $self->{overallhealth};
+  }
+  if ($self->{runtime}->{options}->{perfdata}) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => sprintf('fan_%s', $self->{cpqHeFltTolFanIndex}),
+        value => $self->{cpqHeFltTolFanPctMax},
+        uom => '%',
+    );
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[FAN_%s]\n", $self->{cpqHeFltTolFanIndex};
+  foreach (qw(cpqHeFltTolFanChassis cpqHeFltTolFanIndex cpqHeFltTolFanLocale
+      cpqHeFltTolFanPresent cpqHeFltTolFanType cpqHeFltTolFanSpeed
+      cpqHeFltTolFanRedundant cpqHeFltTolFanRedundantPartner
+      cpqHeFltTolFanCondition cpqHeFltTolFanHotPlug)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::FanSubsystem::Fan::Thermal;
+our @ISA = qw(HP::Proliant::Component::FanSubsystem::Fan);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqHeThermalFanIndex => $params{cpqHeThermalFanIndex},
+    cpqHeThermalFanRequired => $params{cpqHeThermalFanRequired},
+    cpqHeThermalFanPresent => $params{cpqHeThermalFanPresent},
+    cpqHeThermalFanCpuFan => $params{cpqHeThermalFanCpuFan},
+    cpqHeThermalFanStatus => $params{cpqHeThermalFanStatus},
+    cpqHeThermalFanHwLocation => $params{cpqHeThermalFanHwLocation},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+}
+
+sub dump {
+  my $self = shift;
+  printf "[FAN_%s]\n", $self->{cpqHeThermalFanIndex};
+  foreach (qw(cpqHeThermalFanIndex cpqHeThermalFanRequired 
+      cpqHeThermalFanPresent cpqHeThermalFanCpuFan cpqHeThermalFanStatus
+      cpqHeThermalFanHwLocation)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+package HP::Proliant::Component::FanSubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::FanSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    fans => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+# partner not available = cpqHeFltTolFanRedundantPartner=0
+# cpqHeFltTolFanTypeValue = other
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my %tmpfan = ();
+  foreach (grep(/^fans/, split(/\n/, $self->{rawdata}))) {
+    s/^fans //g;
+    if (/^#(\d+)\s+([\w#_\/\-]+)\s+(\w+)\s+(\w+)\s+(FAILED|[N\/A\d]+)%*\s+([\w\/]+)\s+(FAILED|[N\/A\d]+)\s+(\w+)/) {
+      %tmpfan = (
+          cpqHeFltTolFanIndex => $1, 
+          cpqHeFltTolFanLocale => lc $2,
+          cpqHeFltTolFanPresent => lc $3,
+          cpqHeFltTolFanSpeed => lc $4,
+          cpqHeFltTolFanPctMax => lc $5,                 # (FAILED|[N\/A\d]+)
+          cpqHeFltTolFanRedundant => lc $6,
+          cpqHeFltTolFanRedundantPartner => lc $7,       # (FAILED|[N\/A\d]+)
+          cpqHeFltTolFanHotPlug => lc $8,
+      ); 
+    } elsif (/^#(\d+)\s+([\w#_\/\-]+?)(Yes|No|N\/A)\s+(\w+)\s+(FAILED|[N\/A\d]+)%*\s+([\w\/]+)\s+(FAILED|[N\/A\d]+)\s+(\w+)/) { 
+      # #5   SCSI_BACKPLANE_ZONEYes     NORMAL N/A  .... 
+      %tmpfan = (
+          cpqHeFltTolFanIndex => $1,
+          cpqHeFltTolFanLocale => lc $2,
+          cpqHeFltTolFanPresent => lc $3,
+          cpqHeFltTolFanSpeed => lc $4, 
+          cpqHeFltTolFanPctMax => lc $5,
+          cpqHeFltTolFanRedundant => lc $6,
+          cpqHeFltTolFanRedundantPartner => lc $7,
+          cpqHeFltTolFanHotPlug => lc $8,
+      );
+    } elsif (/^#(\d+)\s+([\w#_\/\-]+)\s+[NOno]+\s/) {
+      # Fan is not installed. #2   CPU#2   No   -   -    No      N/A      -
+    } elsif (/^#(\d+)/) {
+      main::contact_author("FAN", $_); 
+    }
+    if (%tmpfan) {
+      $tmpfan{runtime} = $params{runtime};
+      $tmpfan{cpqHeFltTolFanChassis} = 1; # geht aus hpasmcli nicht hervor
+      $tmpfan{cpqHeFltTolFanType} = 'other';
+      if ($tmpfan{cpqHeFltTolFanPctMax} !~ /^\d+$/) {
+        if ($tmpfan{cpqHeFltTolFanSpeed} eq 'normal') {
+          $tmpfan{cpqHeFltTolFanPctMax} = 50;
+        } elsif ($tmpfan{cpqHeFltTolFanSpeed} eq 'high') {
+          $tmpfan{cpqHeFltTolFanPctMax} = 100;
+        } else {
+          $tmpfan{cpqHeFltTolFanPctMax} = 0;
+        }
+      }
+      if($tmpfan{cpqHeFltTolFanSpeed} eq 'failed') {
+        $tmpfan{cpqHeFltTolFanCondition} = 'failed';
+      } elsif($tmpfan{cpqHeFltTolFanSpeed} eq 'n/a') {
+        $tmpfan{cpqHeFltTolFanCondition} = 'other';
+      } else {
+        $tmpfan{cpqHeFltTolFanCondition} = 'ok';
+      }
+      $tmpfan{cpqHeFltTolFanRedundant} = 
+          $tmpfan{cpqHeFltTolFanRedundant} eq 'yes' ? 'redundant' :
+          $tmpfan{cpqHeFltTolFanRedundant} eq 'no' ? 'notRedundant' : 'other';
+      $tmpfan{cpqHeFltTolFanPresent} = 
+          $tmpfan{cpqHeFltTolFanPresent} eq 'yes' ? 'present' :
+          $tmpfan{cpqHeFltTolFanPresent} eq 'failed' ? 'present' :
+          $tmpfan{cpqHeFltTolFanPresent} eq 'no' ? 'absent' : 'other';
+      $tmpfan{cpqHeFltTolFanHotPlug} = 
+          $tmpfan{cpqHeFltTolFanHotPlug} eq 'yes' ? 'hotPluggable' :
+          $tmpfan{cpqHeFltTolFanHotPlug} eq 'no' ? 'nonHotPluggable' : 'other';
+      push(@{$self->{fans}},
+          HP::Proliant::Component::FanSubsystem::Fan->new(%tmpfan));
+      %tmpfan = ();
+    }
+  }
+}
+
+sub overall_check {
+  my $self = shift;
+  # nix. nur wegen der gleichheit mit snmp
+  return 0;
+}
+
+package HP::Proliant::Component::FanSubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::FanSubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    fans => [],
+    he_fans => [],
+    th_fans => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->overall_init(%params);
+  $self->he_init(%params);
+  $self->te_init(%params);
+  $self->unite();
+  return $self;
+}
+
+sub overall_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  # overall
+  my $cpqHeThermalSystemFanStatus = '1.3.6.1.4.1.232.6.2.6.4.0';
+  my $cpqHeThermalSystemFanStatusValue = {
+    1 => 'other',
+    2 => 'ok',
+    3 => 'degraded',
+    4 => 'failed',
+  };
+  my $cpqHeThermalCpuFanStatus = '1.3.6.1.4.1.232.6.2.6.5.0';
+  my $cpqHeThermalCpuFanStatusValue = {
+    1 => 'other',
+    2 => 'ok',
+    4 => 'failed', # shutdown
+  };
+  $self->{sysstatus} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqHeThermalSystemFanStatus,
+      $cpqHeThermalSystemFanStatusValue);
+  $self->{cpustatus} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqHeThermalCpuFanStatus,
+      $cpqHeThermalCpuFanStatusValue);
+}
+
+sub te_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  my $ignore_redundancy = $params{ignore_redundancy};
+  # cpqHeThermalFanTable
+  my $oids = {
+      cpqHeThermalFanEntry => '1.3.6.1.4.1.232.6.2.6.6.1',
+      cpqHeThermalFanIndex => '1.3.6.1.4.1.232.6.2.6.6.1.1',
+      cpqHeThermalFanRequired => '1.3.6.1.4.1.232.6.2.6.6.1.2',
+      cpqHeThermalFanPresent => '1.3.6.1.4.1.232.6.2.6.6.1.3',
+      cpqHeThermalFanCpuFan => '1.3.6.1.4.1.232.6.2.6.6.1.4',
+      cpqHeThermalFanStatus => '1.3.6.1.4.1.232.6.2.6.6.1.5',
+      cpqHeThermalFanHwLocation => '1.3.6.1.4.1.232.6.2.6.6.1.6',
+      cpqHeThermalFanRequiredValue => {
+        1 => 'other',
+        2 => 'nonRequired',
+        3 => 'required',
+      },
+      cpqHeThermalFanPresentValue => {
+        1 => 'other',
+        2 => 'absent',
+        3 => 'present',
+      },
+      cpqHeThermalFanCpuFanValue => {
+        1 => 'other',
+        2 => 'systemFan',
+        3 => 'cpuFan',
+      },
+      cpqHeThermalFanStatusValue => {
+        1 => 'other',
+        2 => 'ok',
+        4 => 'failed',
+      },
+  };
+  # INDEX { cpqHeThermalFanIndex }
+  foreach ($self->get_entries($oids, 'cpqHeThermalFanEntry')) {
+    next if ! $_->{cpqHeThermalFanPresent};
+    push(@{$self->{th_fans}},
+        HP::Proliant::Component::FanSubsystem::Fan->new(%{$_}));
+  }
+}
+
+sub he_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  my $ignore_redundancy = $params{ignore_redundancy};
+  # cpqHeFltTolFanTable
+  my $oids = {
+      cpqHeFltTolFanEntry => '1.3.6.1.4.1.232.6.2.6.7.1',
+      cpqHeFltTolFanChassis => '1.3.6.1.4.1.232.6.2.6.7.1.1',
+      cpqHeFltTolFanIndex => '1.3.6.1.4.1.232.6.2.6.7.1.2',
+      cpqHeFltTolFanLocale => '1.3.6.1.4.1.232.6.2.6.7.1.3',
+      cpqHeFltTolFanPresent => '1.3.6.1.4.1.232.6.2.6.7.1.4',
+      cpqHeFltTolFanType => '1.3.6.1.4.1.232.6.2.6.7.1.5',
+      cpqHeFltTolFanSpeed => '1.3.6.1.4.1.232.6.2.6.7.1.6',
+      cpqHeFltTolFanRedundant => '1.3.6.1.4.1.232.6.2.6.7.1.7',
+      cpqHeFltTolFanRedundantPartner => '1.3.6.1.4.1.232.6.2.6.7.1.8',
+      cpqHeFltTolFanCondition => '1.3.6.1.4.1.232.6.2.6.7.1.9',
+      cpqHeFltTolFanHotPlug => '1.3.6.1.4.1.232.6.2.6.7.1.10',
+      cpqHeFltTolFanHwLocation => '1.3.6.1.4.1.232.6.2.6.7.1.11',
+      cpqHeFltTolFanCurrentSpeed => '1.3.6.1.4.1.232.6.2.6.7.1.12',
+      cpqHeFltTolFanLocaleValue => {
+          1 => "other",
+          2 => "unknown",
+          3 => "system",
+          4 => "systemBoard",
+          5 => "ioBoard",
+          6 => "cpu",
+          7 => "memory",
+          8 => "storage",
+          9 => "removableMedia",
+          10 => "powerSupply", 
+          11 => "ambient",
+          12 => "chassis",
+          13 => "bridgeCard",
+      },
+      cpqHeFltTolFanPresentValue => {
+          1 => "other",
+          2 => "absent",
+          3 => "present",
+      },
+      cpqHeFltTolFanSpeedValue => {
+          1 => "other",
+          2 => "normal",
+          3 => "high",
+      },
+      cpqHeFltTolFanRedundantValue => {
+          1 => "other",
+          2 => "notRedundant",
+          3 => "redundant",
+      },
+      cpqHeFltTolFanTypeValue => {
+          1 => "other",
+          2 => "tachInput",
+          3 => "spinDetect",
+      },
+      cpqHeFltTolFanConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqHeFltTolFanHotPlugValue => {
+          1 => "other",
+          2 => "nonHotPluggable",
+          3 => "hotPluggable",
+      },
+  };
+  # INDEX { cpqHeFltTolFanChassis, cpqHeFltTolFanIndex }
+  foreach ($self->get_entries($oids, 'cpqHeFltTolFanEntry')) {
+    next if ! defined $_->{cpqHeFltTolFanIndex};
+    # z.b. USM65201WS hat nur solche fragmente. die werden erst gar nicht
+    # als fans akzeptiert. dafuer gibts dann die overall condition
+    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.1 = INTEGER: 0
+    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.2 = INTEGER: 0
+    $_->{cpqHeFltTolFanPctMax} = ($_->{cpqHeFltTolFanPresent} eq 'present') ?
+        50 : 0;
+    push(@{$self->{he_fans}},
+        HP::Proliant::Component::FanSubsystem::Fan->new(%{$_}));
+  }
+
+}
+
+sub unite {
+  my $self = shift;
+  my $tmpfans = {};
+  foreach (@{$self->{he_fans}}) {
+    $tmpfans->{$_->{cpqHeFltTolFanIndex}} = $_;
+  }
+  foreach (@{$self->{he_fans}}) {
+    if (exists $tmpfans->{$_->{cpqHeFltTolFanRedundantPartner}}) {
+      $_->{partner} = $tmpfans->{$_->{cpqHeFltTolFanRedundantPartner}};
+    } else {
+      $_->{partner} = undef;
+    }
+  }
+  @{$self->{fans}} = @{$self->{he_fans}};
+}
+
+sub overall_check {
+  my $self = shift;
+  my $result = 0;
+  $self->blacklist('ofs', '');
+  if ($self->{sysstatus} && $self->{cpustatus}) {
+    if ($self->{sysstatus} eq 'degraded') {
+      $result = 1;
+      $self->add_message(WARNING,
+          sprintf 'system fan overall status is %s', $self->{sysstatus});
+    } elsif ($self->{sysstatus} eq 'failed') {
+      $result = 2;
+      $self->add_message(CRITICAL,
+          sprintf 'system fan overall status is %s', $self->{sysstatus});
+    } 
+    if ($self->{cpustatus} eq 'degraded') {
+      $result = 1;
+      $self->add_message(WARNING,
+          sprintf 'cpu fan overall status is %s', $self->{cpustatus});
+    } elsif ($self->{cpustatus} eq 'failed') {
+      $result = 2;
+      $self->add_message(CRITICAL,
+          sprintf 'cpu fan overall status is %s', $self->{cpustatus});
+    } 
+    $self->add_info(sprintf 'overall fan status: system=%s, cpu=%s',
+        $self->{sysstatus}, $self->{cpustatus});
+  } else {
+    $result = 0;
+    $self->add_info('this system seems to be water-cooled. no fans found');
+  }
+  return $result;
+}
+
+
+
+package HP::Proliant::Component::MemorySubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::MemorySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    dimms => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  $self->{dimms} = [];
+  my %tmpdimm = (
+    runtime => $params{runtime},
+  );
+  my $inblock = 0;
+  foreach (grep(/^dimm/, split(/\n/, $self->{rawdata}))) {
+    s/^dimm\s*$//g;
+    if (/Cartridge #:\s+(\d+)/ || /Processor #:\s+(\d+)/) {
+      # neuerdings (g6) tauchen hier prozessor- statt cartridge-angaben auf
+      $tmpdimm{cartridge} = $1;
+      $tmpdimm{board} = $1;
+      $inblock = 1;
+    } elsif (/Module #:\s+(\d+)/) {
+      $tmpdimm{module} = $1;
+    } elsif (/Present:\s+(\w+)/) {
+      $tmpdimm{status} = lc $1 eq 'yes' ? 'present' :
+          lc $1 eq 'no' ? 'notPresent' : 'other';
+    } elsif (/Status:\s+(.+?)\s*$/) {
+      $tmpdimm{condition} = lc $1 =~ /degraded/ ? 'degraded' :
+          lc $1 eq 'ok' ? 'ok' : lc $1 =~ /n\/a/ ? 'n/a' : 'other';
+    } elsif (/Size:\s+(\d+)\s*(.+?)\s*$/) {
+      $tmpdimm{size} = $1 * (lc $2 eq 'mb' ? 1024*1024 :
+          lc $2 eq 'gb' ? 1024*1024*1024 : 1);
+    } elsif (/^\s*$/) {
+      if ($inblock) {
+        $inblock = 0;
+        push(@{$self->{dimms}},
+            HP::Proliant::Component::MemorySubsystem::Dimm->new(%tmpdimm));
+        %tmpdimm = (
+          runtime => $params{runtime},
+        );
+      }
+    } elsif (/(\d+)\s+(\d+)\s+(\w+)\s+(0x\w+)\s+(0x\w+)\s+(\d+[MGT]B)\s+(\d+MHz)\s+(\w+)/) {
+      $tmpdimm{cartridge} = $1;
+      $tmpdimm{module} = $2;
+      $tmpdimm{status} = lc $3 eq 'yes' ? 'present' :
+          lc $3 eq 'no' ? 'notPresent' : 'other';
+      my $formfactor = $4;
+      my $memorytype = $5;
+      my $memorysize = $6;
+      my $memoryspeed = $7;
+      $tmpdimm{condition} = lc $8 =~ /degraded/ ? 'degraded' :
+          lc $8 eq 'ok' ? 'ok' : lc $8 =~ /n\/a/ ? 'n/a' : 'other';
+      $memorysize =~ /(\d+)([MGT]B)/;
+      $tmpdimm{size} = $1 * (lc $2 eq 'mb' ? 1024*1024 :
+          lc $2 eq 'gb' ? 1024*1024*1024 : 1);
+      push(@{$self->{dimms}},
+          HP::Proliant::Component::MemorySubsystem::Dimm->new(%tmpdimm));
+    }
+  }
+  if ($inblock) {
+    push(@{$self->{dimms}},
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(%tmpdimm));
+  }
+}
+
+sub is_faulty {
+  my $self = shift;
+  return 0; # cli hat so einen globalen status nicht
+}
+
+
+package HP::Proliant::Component::MemorySubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::MemorySubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    dimms => [],
+    si_dimms => [],
+    he_dimms => [],
+    h2_dimms => [],
+    he_cartridges => [],
+    h2_cartridges => [],
+    si_overall_condition => undef,
+    he_overall_condition => undef,
+    h2_overall_condition => undef,
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->si_init();
+  $self->he_init();
+  $self->he_cartridge_init();
+  $self->h2_init();
+  #$self->h2_cartridge_init();
+  $self->condense();
+  return $self;
+}
+
+sub si_init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqSiMemModuleEntry => '1.3.6.1.4.1.232.2.2.4.5.1',
+      cpqSiMemBoardIndex => '1.3.6.1.4.1.232.2.2.4.5.1.1',
+      cpqSiMemModuleIndex => '1.3.6.1.4.1.232.2.2.4.5.1.2',
+      cpqSiMemModuleSize => '1.3.6.1.4.1.232.2.2.4.5.1.3',
+      cpqSiMemModuleType => '1.3.6.1.4.1.232.2.2.4.5.1.4',
+      cpqSiMemECCStatus => '1.3.6.1.4.1.232.2.2.4.5.1.11',
+      cpqSiMemModuleHwLocation => '1.3.6.1.4.1.232.2.2.4.5.1.12',
+      cpqSiMemModuleTypeValue => {
+          1 => 'other',
+          2 => 'board',
+          3 => 'cpqSingleWidthModule',
+          4 => 'cpqDoubleWidthModule',
+          5 => 'simm',
+          6 => 'pcmcia',
+          7 => 'compaq-specific',
+          8 => 'dimm',
+          9 => 'smallOutlineDimm',
+          10 => 'rimm',
+          11 => 'srimm',
+      },
+      cpqSiMemECCStatusValue => {
+          0 => "n/a",
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "degradedModuleIndexUnknown",
+          34 => 'n/a', # es ist zum kotzen...
+          104 => 'n/a',
+      },
+  };
+  # INDEX { cpqSiMemBoardIndex, cpqSiMemModuleIndex }
+  foreach ($self->get_entries($oids, 'cpqSiMemModuleEntry')) {
+    $_->{cartridge} = $_->{cpqSiMemBoardIndex};
+    $_->{module} = $_->{cpqSiMemModuleIndex};
+    next if (! defined $_->{cartridge} || ! defined $_->{module});
+    $_->{size} = $_->{cpqSiMemModuleSize};
+    $_->{type} = $_->{cpqSiMemModuleType};
+    $_->{condition} = $_->{cpqSiMemECCStatus};
+    $_->{status} = ($_->{cpqSiMemModuleSize} > 0) ? 'present' : 'notPresent';
+    push(@{$self->{si_dimms}},
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(%{$_})
+    );
+  } 
+  my $cpqSiMemECCCondition = '1.3.6.1.4.1.232.2.2.4.15.0';
+  my $cpqSiMemECCConditionValue = {
+    1 => 'other',
+    2 => 'ok',
+    3 => 'degraded',
+  };
+  $self->{si_overall_condition} = SNMP::Utils::get_object_value(
+        $self->{rawdata}, $cpqSiMemECCCondition,
+        $cpqSiMemECCConditionValue);
+  $self->trace(2, sprintf 'overall si condition is %s', 
+      $self->{si_overall_condition} || 'undefined');
+}
+
+sub he_init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeResMemModuleEntry => '1.3.6.1.4.1.232.6.2.14.11.1',
+      cpqHeResMemBoardIndex => '1.3.6.1.4.1.232.6.2.14.11.1.1',
+      cpqHeResMemModuleIndex => '1.3.6.1.4.1.232.6.2.14.11.1.2',
+      cpqHeResMemModuleStatus => '1.3.6.1.4.1.232.6.2.14.11.1.4',
+      cpqHeResMemModuleCondition => '1.3.6.1.4.1.232.6.2.14.11.1.5',
+      cpqHeResMemModuleStatusValue => {
+          1 => "other",         # unknown or could not be determined
+          2 => "notPresent",    # not present or un-initialized
+          3 => "present",       # present but not in use
+          4 => "good",          # present and in use. ecc threshold not exceeded
+          5 => "add",           # added but not yet in use
+          6 => "upgrade",       # upgraded but not yet in use
+          7 => "missing",       # expected but missing
+          8 => "doesNotMatch",  # does not match the other modules in the bank
+          9 => "notSupported",  # module not supported
+          10 => "badConfig",    # violates add/upgrade configuration
+          11 => "degraded",     # ecc exceeds threshold
+      },
+      # condition = status of the correctable memory errors
+      cpqHeResMemModuleConditionValue => {
+          0 => "n/a", # this appears only with buggy firmwares.
+          # (only 1 module shows up)
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+      },
+  };
+  my $tablesize = SNMP::Utils::get_size($snmpwalk, 
+      $oids->{cpqHeResMemModuleEntry});
+  # INDEX { cpqHeResMemBoardIndex, cpqHeResMemModuleIndex }
+  foreach ($self->get_entries($oids, 'cpqHeResMemModuleEntry')) {
+    $_->{cartridge} = $_->{cpqHeResMemBoardIndex};
+    $_->{module} = $_->{cpqHeResMemModuleIndex};
+    $_->{present} = $_->{cpqHeResMemModuleStatus};
+    $_->{status} = $_->{cpqHeResMemModuleStatus};
+    $_->{condition} = $_->{cpqHeResMemModuleCondition};
+    if ((! defined $_->{module}) && ($_->{cartridge} == 0)) {
+      $_->{module} = $_->{index2}; # auf dem systemboard verbaut
+    }
+
+    push(@{$self->{he_dimms}}, 
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(%{$_})
+    ) unless (! defined $_->{cartridge} || ! defined $_->{module} ||
+        $tablesize == 1);
+  }
+  my $cpqHeResilientMemCondition = '1.3.6.1.4.1.232.6.2.14.4.0';
+  my $cpqHeResilientMemConditionValue = {
+    1 => 'other',
+    2 => 'ok',
+    3 => 'degraded',
+  };
+  $self->{he_overall_condition} = SNMP::Utils::get_object_value(
+        $self->{rawdata}, $cpqHeResilientMemCondition,
+        $cpqHeResilientMemConditionValue);
+  $self->trace(2, sprintf 'overall he condition is %s', 
+      $self->{hei_overall_condition} || 'undefined');
+}
+
+sub he_cartridge_init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeResMemBoardEntry => '1.3.6.1.4.1.232.6.2.14.10.1',
+      cpqHeResMemBoardSlotIndex => '1.3.6.1.4.1.232.6.2.14.10.1.1',
+      cpqHeResMemBoardOnlineStatus => '1.3.6.1.4.1.232.6.2.14.10.1.2',
+      cpqHeResMemBoardErrorStatus => '1.3.6.1.4.1.232.6.2.14.10.1.3',
+      cpqHeResMemBoardNumSockets => '1.3.6.1.4.1.232.6.2.14.10.1.5',
+      cpqHeResMemBoardOsMemSize => '1.3.6.1.4.1.232.6.2.14.10.1.6',
+      cpqHeResMemBoardTotalMemSize => '1.3.6.1.4.1.232.6.2.14.10.1.7',
+      cpqHeResMemBoardCondition => '1.3.6.1.4.1.232.6.2.14.10.1.8',
+      # onlinestatus
+      cpqHeResMemBoardOnlineStatusValue => {
+          0 => "n/a", # this appears only with buggy firmwares.
+          # (only 1 module shows up)
+          1 => "other",
+          2 => "present",
+          3 => "absent",
+      },
+      cpqHeResMemBoardErrorStatusValue => {
+          1 => "other",         #
+          2 => "noError",       #
+          3 => "dimmEccError",  #
+          4 => "unlockError",   #
+          5 => "configError",   #
+          6 => "busError",      #
+          7 => "powerError",    #
+      },
+      # condition = status of the correctable memory errors
+      cpqHeResMemBoardConditionValue => {
+          0 => "n/a", # this appears only with buggy firmwares.
+          # (only 1 module shows up)
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+      },
+  };
+  my $tablesize = SNMP::Utils::get_size($snmpwalk,
+      $oids->{cpqHeResMemBoardEntry});
+  # INDEX { cpqHeResMemBoardIndex, cpqHeResMemBoardIndex }
+  foreach ($self->get_entries($oids, 'cpqHeResMemBoardEntry')) {
+    push(@{$self->{he_cartridges}},
+        HP::Proliant::Component::MemorySubsystem::Cartridge->new(%{$_})
+    ) unless (! defined $_->{cpqHeResMemBoardSlotIndex} || $tablesize == 1);
+  }
+}
+
+sub h2_init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeResMem2ModuleEntry => '1.3.6.1.4.1.232.6.2.14.13.1',
+      cpqHeResMem2BoardNum => '1.3.6.1.4.1.232.6.2.14.13.1.2',
+      cpqHeResMem2ModuleNum => '1.3.6.1.4.1.232.6.2.14.13.1.5',
+      cpqHeResMem2ModuleStatus => '1.3.6.1.4.1.232.6.2.14.13.1.19',
+      cpqHeResMem2ModuleCondition => '1.3.6.1.4.1.232.6.2.14.13.1.20',
+      cpqHeResMem2ModuleSize => '1.3.6.1.4.1.232.6.2.14.13.1.6',
+    
+      cpqHeResMem2ModuleStatusValue => {
+          1 => "other",         # unknown or could not be determined
+          2 => "notPresent",    # not present or un-initialized
+          3 => "present",       # present but not in use
+          4 => "good",          # present and in use. ecc threshold not exceeded
+          5 => "add",           # added but not yet in use
+          6 => "upgrade",       # upgraded but not yet in use
+          7 => "missing",       # expected but missing
+          8 => "doesNotMatch",  # does not match the other modules in the bank
+          9 => "notSupported",  # module not supported
+          10 => "badConfig",    # violates add/upgrade configuration
+          11 => "degraded",     # ecc exceeds threshold
+      },
+      # condition = status of the correctable memory errors
+      cpqHeResMem2ModuleConditionValue => {
+          0 => "n/a", # this appears only with buggy firmwares.
+          # (only 1 module shows up)
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+      },
+  };
+  # INDEX { cpqHeResMem2ModuleNum }
+  my $lastboard = 0;
+  my $lastmodule = 0;
+  my $myboard= 0;
+  my $hpboard = 0;
+  foreach (sort { $a->{index1} <=> $b->{index1} }
+      $self->get_entries($oids, 'cpqHeResMem2ModuleEntry')) {
+    $hpboard = $_->{cpqHeResMem2BoardNum};
+      # dass hier faelschlicherweise 0 zurueckkommt, wundert mich schon
+      # gar nicht mehr
+    $_->{module} = $_->{cpqHeResMem2ModuleNum};
+    if ($_->{module} < $lastmodule) {
+      # sieht so aus, als haette man es mit einem neuen board zu tun
+      # da hp zu bloed ist, selber hochzuzaehlen, muss ich das tun
+      $myboard++;
+    }
+    $lastmodule = $_->{cpqHeResMem2ModuleNum};
+    $_->{cartridge} = ($myboard != $hpboard) ? $myboard : $hpboard;
+    $_->{present} = $_->{cpqHeResMem2ModuleStatus};
+    $_->{status} = $_->{cpqHeResMem2ModuleStatus};
+    $_->{condition} = $_->{cpqHeResMem2ModuleCondition};
+    $_->{size} = $_->{cpqHeResMem2ModuleSize};
+    push(@{$self->{h2_dimms}},
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(%{$_})
+    ) unless (! defined $_->{cpqHeResMem2BoardNum} ||
+        ! defined $_->{cpqHeResMem2ModuleNum});
+  }
+}
+
+sub h2_cartridge_init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqHeResMem2BoardEntry => '1.3.6.1.4.1.232.6.2.14.12.1',
+      cpqHeResMem2BoardIndex => '1.3.6.1.4.1.232.6.2.14.12.1.1',
+      cpqHeResMem2BoardOnlineStatus => '1.3.6.1.4.1.232.6.2.14.12.1.5',
+      cpqHeResMem2BoardErrorStatus => '1.3.6.1.4.1.232.6.2.14.12.1.6',
+      cpqHeResMem2BoardNumSockets => '1.3.6.1.4.1.232.6.2.14.12.1.8',
+      cpqHeResMem2BoardOsMemSize => '1.3.6.1.4.1.232.6.2.14.12.1.9',
+      cpqHeResMem2BoardTotalMemSize => '1.3.6.1.4.1.232.6.2.14.12.1.10',
+      cpqHeResMem2BoardCondition => '1.3.6.1.4.1.232.6.2.14.12.1.11',
+      # onlinestatus
+      cpqHeResMem2BoardOnlineStatusValue => {
+          0 => "n/a", # this appears only with buggy firmwares.
+          # (only 1 module shows up)
+          1 => "other",
+          2 => "present",
+          3 => "absent",
+      },
+      cpqHeResMem2BoardErrorStatusValue => {
+          1 => "other",         #
+          2 => "noError",       #
+          3 => "dimmEccError",  #
+          4 => "unlockError",   #
+          5 => "configError",   #
+          6 => "busError",      #
+          7 => "powerError",    #
+      },
+      # condition = status of the correctable memory errors
+      cpqHeResMem2BoardConditionValue => {
+          0 => "n/a", # this appears only with buggy firmwares.
+          # (only 1 module shows up)
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+      },
+  };
+  my $tablesize = SNMP::Utils::get_size($snmpwalk,
+      $oids->{cpqHeResMemBoardEntry});
+  # INDEX { cpqHeResMem2BoardIndex, cpqHeResMem2BoardIndex }
+  foreach ($self->get_entries($oids, 'cpqHeResMem2BoardEntry')) {
+    push(@{$self->{h2_cartridges}},
+        HP::Proliant::Component::MemorySubsystem::Cartridge->new(%{$_})
+    ) unless (! defined $_->{cpqHeRes2MemBoardIndex} || $tablesize == 1);
+  }
+}
+
+sub condense {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  # wenn saemtliche dimms n/a sind
+  #  wenn ignore dimms: ignoring %d dimms with status 'n/a'
+  #  wenn buggyfirmware: ignoring %d dimms with status 'n/a' because of buggy firmware
+  # if buggy firmware : condition n/a ist normal
+  # ignore-dimms :
+  # es gibt si_dimms und he_dimms
+  my $si_dimms = scalar(@{$self->{si_dimms}});
+  my $he_dimms = scalar(@{$self->{he_dimms}});
+  my $h2_dimms = scalar(@{$self->{h2_dimms}});
+  $self->trace(2, sprintf "SI: %02d   HE: %02d   H2: %02d",
+      $si_dimms, $he_dimms, $h2_dimms)
+      if ($self->{runtime}->{options}->{verbose} >= 2);
+  foreach ($self->get_si_boards()) {
+    printf "SI%02d-> ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
+    foreach ($self->get_si_modules($_)) {
+      printf "%02d ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
+    }
+    printf "\n" if ($self->{runtime}->{options}->{verbose} >= 2);
+  }
+  foreach ($self->get_he_boards()) {
+    printf "HE%02d-> ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
+    foreach ($self->get_he_modules($_)) {
+      printf "%02d ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
+    }
+    printf "\n" if ($self->{runtime}->{options}->{verbose} >= 2);
+  }
+  foreach ($self->get_h2_boards()) {
+    printf "H2%02d-> ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
+    foreach ($self->get_h2_modules($_)) {
+      printf "%02d ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
+    }
+    printf "\n" if ($self->{runtime}->{options}->{verbose} >= 2);
+  }
+  if (($h2_dimms == 0) && ($he_dimms == 0) && ($si_dimms > 0)) {
+    printf "TYP1 %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+    @{$self->{dimms}} = $self->update_si_with_si();
+  } elsif (($h2_dimms == 0) && ($he_dimms > 0) && ($si_dimms > 0)) {
+    printf "TYP2 %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+    @{$self->{dimms}} = $self->update_si_with_he();
+  } elsif (($h2_dimms == 0) && ($he_dimms > 0) && ($si_dimms == 0)) {
+    printf "TYP3 %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+    @{$self->{dimms}} = $self->update_he_with_he();
+  } elsif (($h2_dimms > 0) && ($he_dimms == 0) && ($si_dimms == 0)) {
+    printf "TYP4 %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+    @{$self->{dimms}} = $self->update_h2_with_h2();
+  } elsif (($h2_dimms > 0) && ($he_dimms > 0) && ($si_dimms == 0)) {
+    printf "TYP5 %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+    @{$self->{dimms}} = $self->update_he_with_h2();
+  } elsif (($h2_dimms > 0) && ($he_dimms == 0) && ($si_dimms > 0)) {
+    printf "TYP6 %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+    @{$self->{dimms}} = $self->update_si_with_h2();
+  } elsif (($h2_dimms > 0) && ($he_dimms > 0) && ($si_dimms > 0)) {
+    if ($h2_dimms > $si_dimms) {
+      printf "TYP7 %s\n", $self->{runtime}->{product}
+          if ($self->{runtime}->{options}->{verbose} >= 2);
+      @{$self->{dimms}} = $self->update_he_with_h2();
+    } else {
+      printf "TYP8 %s\n", $self->{runtime}->{product}
+          if ($self->{runtime}->{options}->{verbose} >= 2);
+      @{$self->{dimms}} = $self->update_si_with_he();
+    }
+  } else {
+    printf "TYPX %s\n", $self->{runtime}->{product}
+        if ($self->{runtime}->{options}->{verbose} >= 2);
+  }
+  my $all_dimms = scalar(@{$self->{dimms}});
+  $self->trace(2, sprintf "ALL: %02d", $all_dimms);
+}
+
+sub dump {
+  my $self = shift;
+  if ($self->{runtime}->{options}->{verbose} > 2) {
+    printf "[SI]\n";
+    foreach (@{$self->{si_dimms}}) {
+      $_->dump();
+    }
+    printf "[HE]\n";
+    foreach (@{$self->{he_dimms}}) {
+      $_->dump();
+    }
+    printf "[H2]\n";
+    foreach (@{$self->{h2_dimms}}) {
+      $_->dump();
+    }
+  }
+  $self->SUPER::dump();
+}
+
+sub update_si_with_si {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my @dimms = ();
+  my $repaircondition = undef;
+  # wenn si keine statusinformationen liefert, dann besteht die chance
+  # dass ein undokumentiertes he-fragment vorliegt
+  # 1.3.6.1.4.1.232.6.2.14.11.1.1.0.<anzahl der dimms>
+  my $cpqHeResMemModuleEntry = "1.3.6.1.4.1.232.6.2.14.11.1";
+  if (SNMP::Utils::get_size($snmpwalk, $cpqHeResMemModuleEntry) == 1) {
+    $repaircondition = lc SNMP::Utils::get_object(
+        $snmpwalk, $cpqHeResMemModuleEntry.'.1.0.'.scalar(@{$self->{si_dimms}}));
+    # repaircondition 0 (ok) biegt alles wieder gerade
+  } else { 
+    # anderer versuch
+    if ($self->{si_overall_condition} &&
+        $self->{si_overall_condition} eq 'ok') {
+      $repaircondition = 0;
+    }
+  }
+  foreach my $si_dimm (@{$self->{si_dimms}}) {
+    if (($si_dimm->{condition} eq 'n/a') ||
+        ($si_dimm->{condition} eq 'other')) {
+      $si_dimm->{condition} = 'ok' if
+          (defined $repaircondition && $repaircondition == 0);
+    }
+    push(@dimms,
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(
+            runtime => $si_dimm->{runtime},
+            cartridge => $si_dimm->{cartridge},
+            module => $si_dimm->{module},
+            size => $si_dimm->{size},
+            status => $si_dimm->{status},
+            condition => $si_dimm->{condition},
+    ));
+  }
+  return @dimms;
+}
+
+sub update_si_with_he {
+  my $self = shift;
+  my @dimms = ();
+  my $first_si_cartridge = ($self->get_si_boards())[0];
+  my $first_he_cartridge = ($self->get_he_boards())[0];
+  my $offset = 0;
+  if (scalar(@{$self->{si_dimms}}) == scalar(@{$self->{he_dimms}})) {
+    # aufpassen! sowas kann vorkommen: si cartridge 0...6, he cartridge 1...7
+    if ($first_si_cartridge != $first_he_cartridge) {
+      # README case 5
+      $offset = $first_si_cartridge - $first_he_cartridge;
+    }
+  } elsif ((scalar(@{$self->{si_dimms}}) > 1) && 
+      (scalar(@{$self->{he_dimms}}) == 1)) {
+    # siehe update_si_with_si. he-fragment
+    return $self->update_si_with_si();
+  } else { 
+    # z.b. 4 si notpresent, 4 si present, 4 he
+  }
+  foreach my $si_dimm (@{$self->{si_dimms}}) {
+    if (($si_dimm->{condition} eq 'n/a') || 
+        ($si_dimm->{condition} eq 'other')) {
+      if (my $he_dimm = $self->get_he_module(
+          $si_dimm->{cartridge} - $offset, $si_dimm->{module})) {
+        # vielleicht hat he mehr ahnung
+        $si_dimm->{condition} = $he_dimm->{condition};
+        if (($si_dimm->{condition} eq 'n/a') || 
+            ($si_dimm->{condition} eq 'other')) {
+          # wenns immer noch kein brauchbares ergebnis gibt....
+          if ($self->{he_overall_condition} &&
+              $self->{he_overall_condition} eq 'ok') {
+            # wird schon stimmen...
+            $si_dimm->{condition} = 'ok';
+          } else {
+            # ansonsten stellen wir uns dumm
+            $si_dimm->{status} = 'notPresent';
+          }
+        }
+      } else {
+        # in dem fall zeigt si unbestueckte cartridges an
+      }
+    }
+    push(@dimms,
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(
+            runtime => $si_dimm->{runtime},
+            cartridge => $si_dimm->{cartridge},
+            module => $si_dimm->{module},
+            size => $si_dimm->{size},
+            status => $si_dimm->{status},
+            condition => $si_dimm->{condition},
+    ));
+  }
+  return @dimms;
+}
+
+sub update_he_with_he {
+  my $self = shift;
+  my @dimms = ();
+  foreach my $he_dimm (@{$self->{he_dimms}}) {
+    push(@dimms,
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(
+            runtime => $he_dimm->{runtime},
+            cartridge => $he_dimm->{cartridge},
+            module => $he_dimm->{module},
+            size => $he_dimm->{size},
+            status => $he_dimm->{status},
+            condition => $he_dimm->{condition},
+    ));
+  }
+  return @dimms;
+}
+
+sub update_si_with_h2 {
+  my $self = shift;
+  my @dimms = ();
+  my $first_si_cartridge = ($self->get_si_boards())[0];
+  my $first_h2_cartridge = ($self->get_h2_boards())[0];
+  my $offset = 0;
+  if (scalar(@{$self->{si_dimms}}) == scalar(@{$self->{h2_dimms}})) {
+    # aufpassen! sowas kann vorkommen: si cartridge 0...6, he cartridge 1...7
+    if ($first_si_cartridge != $first_h2_cartridge) {
+      # README case 5
+      $offset = $first_si_cartridge - $first_h2_cartridge;
+    }
+  } else { 
+    # z.b. 4 si notpresent, 4 si present, 4 he
+  }
+  foreach my $si_dimm (@{$self->{si_dimms}}) {
+    if (($si_dimm->{condition} eq 'n/a') || 
+        ($si_dimm->{condition} eq 'other')) {
+      if (my $h2_dimm = $self->get_h2_module(
+          $si_dimm->{cartridge} - $offset, $si_dimm->{module})) {
+        # vielleicht hat h2 mehr ahnung
+        $si_dimm->{condition} = $h2_dimm->{condition};
+        if (1) {
+          # ist zwar da, aber irgendwie auskonfiguriert
+          $si_dimm->{status} = 'notPresent' if $h2_dimm->{status} eq 'other';
+        }
+      } else {
+        # in dem fall zeigt si unbestueckte cartridges an
+      }
+    }
+    push(@dimms,
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(
+            runtime => $si_dimm->{runtime},
+            cartridge => $si_dimm->{cartridge},
+            module => $si_dimm->{module},
+            size => $si_dimm->{size},
+            status => $si_dimm->{status},
+            condition => $si_dimm->{condition},
+    ));
+  }
+  return @dimms;
+}
+
+sub update_he_with_h2 {
+  my $self = shift;
+  my @dimms = ();
+  my $first_he_cartridge = ($self->get_he_boards())[0];
+  my $first_h2_cartridge = ($self->get_h2_boards())[0];
+  my $offset = 0;
+  # auch hier koennte sowas u.u.vorkommen: he cartridge 0..6, h2 cartridge 1..7
+  # ich habs zwar nie gesehen, aber wer weiss...
+  if ($first_h2_cartridge != $first_he_cartridge) {
+    $offset = $first_h2_cartridge - $first_he_cartridge;
+  }
+  foreach my $he_dimm (@{$self->{he_dimms}}) {
+    if (($he_dimm->{condition} eq 'n/a') || 
+        ($he_dimm->{condition} eq 'other')) {
+      if (my $h2_dimm = $self->get_h2_module(
+          $he_dimm->{cartridge} + $offset, $he_dimm->{module})) {
+        # vielleicht hat h2 mehr ahnung
+        $he_dimm->{condition} = $h2_dimm->{condition};
+        if (1) {
+          # ist zwar da, aber irgendwie auskonfiguriert
+          $he_dimm->{status} = 'notPresent' if $h2_dimm->{status} eq 'other';
+        }
+      } else {
+        # in dem fall weiss he mehr als h2
+      }
+    }
+    if ($he_dimm->{size} == 0) {
+      if (my $h2_dimm = $self->get_h2_module(
+          $he_dimm->{cartridge} + $offset, $he_dimm->{module})) {
+        $he_dimm->{size} = $h2_dimm->{size};
+        # h2 beinhaltet eine size-oid
+      }
+    }
+    push(@dimms,
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(
+            runtime => $he_dimm->{runtime},
+            cartridge => $he_dimm->{cartridge},
+            module => $he_dimm->{module},
+            size => $he_dimm->{size},
+            status => $he_dimm->{status},
+            condition => $he_dimm->{condition},
+    ));
+  }
+  return @dimms;
+}
+
+sub update_h2_with_h2 {
+  my $self = shift;
+  my @dimms = ();
+  foreach my $h2_dimm (@{$self->{h2_dimms}}) {
+    push(@dimms,
+        HP::Proliant::Component::MemorySubsystem::Dimm->new(
+            runtime => $h2_dimm->{runtime},
+            cartridge => $h2_dimm->{cartridge},
+            module => $h2_dimm->{module},
+            size => $h2_dimm->{size},
+            status => $h2_dimm->{status},
+            condition => $h2_dimm->{condition},
+    ));
+  }
+  return @dimms;
+}
+
+sub is_faulty {
+  my $self = shift;
+  if (scalar(@{$self->{si_dimms}}) > 0 && 
+      scalar(@{$self->{he_dimms}}) > 0) {
+    return $self->si_is_faulty() || $self->he_is_faulty();
+  } elsif (scalar(@{$self->{si_dimms}}) > 0 &&
+        scalar(@{$self->{he_dimms}}) == 0) {
+    return $self->si_is_faulty();
+  } elsif (scalar(@{$self->{si_dimms}}) == 0 &&
+        scalar(@{$self->{he_dimms}}) > 0) {
+    return $self->he_is_faulty();
+  } else {
+    return 0;
+  }
+}
+
+sub si_is_faulty {
+  my $self = shift;
+  return ! defined $self->{si_overall_condition} ? 0 :
+      $self->{si_overall_condition} eq 'degraded' ? 1 : 0;
+}
+
+sub si_is_ok {
+  my $self = shift;
+  return ! defined $self->{si_overall_condition} ? 1 :
+      $self->{si_overall_condition} eq 'ok' ? 1 : 0;
+}
+
+sub he_is_faulty {
+  my $self = shift;
+  return ! defined $self->{he_overall_condition} ? 0 :
+      $self->{he_overall_condition} eq 'degraded' ? 1 : 0;
+}
+
+sub he_is_ok {
+  my $self = shift;
+  return ! defined $self->{he_overall_condition} ? 1 :
+      $self->{he_overall_condition} eq 'ok' ? 1 : 0;
+}
+
+sub get_si_boards {
+  my $self = shift;
+  my %found = ();
+  foreach (@{$self->{si_dimms}}) {
+    $found{$_->{cartridge}} = 1;
+  }
+  return sort { $a <=> $b } keys %found;
+}
+
+sub get_si_modules {
+  my $self = shift;
+  my $board = shift;
+  my %found = ();
+  foreach (grep { $_->{cartridge} == $board } @{$self->{si_dimms}}) {
+    $found{$_->{module}} = 1;
+  }
+  return sort { $a <=> $b } keys %found;
+}
+
+sub get_he_boards {
+  my $self = shift;
+  my %found = ();
+  foreach (@{$self->{he_dimms}}) {
+    $found{$_->{cartridge}} = 1;
+  }
+  return sort { $a <=> $b } keys %found;
+}
+
+sub get_he_modules {
+  my $self = shift;
+  my $board = shift;
+  my %found = ();
+  foreach (grep { $_->{cartridge} == $board } @{$self->{he_dimms}}) {
+    $found{$_->{module}} = 1;
+  }
+  return sort { $a <=> $b } keys %found;
+}
+
+sub get_he_module {
+  my $self = shift;
+  my $board = shift;
+  my $module = shift;
+  my $found = (grep { $_->{cartridge} == $board && $_->{module} == $module } 
+      @{$self->{he_dimms}})[0];
+  return $found;
+}
+
+sub get_h2_boards {
+  my $self = shift;
+  my %found = ();
+  # 
+  foreach (@{$self->{h2_dimms}}) {
+    $found{$_->{cartridge}} = 1;
+  }
+  return sort { $a <=> $b } keys %found;
+}
+
+sub get_h2_modules {
+  my $self = shift;
+  my $board = shift;
+  my %found = ();
+  foreach (grep { $_->{cartridge} == $board } @{$self->{h2_dimms}}) {
+    $found{$_->{module}} = 1;
+  }
+  return sort { $a <=> $b } keys %found;
+}
+
+sub get_h2_module {
+  my $self = shift;
+  my $board = shift;
+  my $module = shift;
+  my $found = (grep { $_->{cartridge} == $board && $_->{module} == $module } 
+      @{$self->{h2_dimms}})[0];
+  return $found;
+}
+
+
+package HP::Proliant::Component::MemorySubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+    dimms => [],
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::MemorySubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::MemorySubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking memory');
+  foreach (@{$self->{dimms}}) {
+    $_->check(); # info ausfuellen
+  }
+  if ((scalar(grep {
+      $_->is_present() && 
+      ($_->{condition} ne 'n/a' && $_->{condition} ne 'other' ) 
+  } @{$self->{dimms}})) != 0) {
+    foreach (@{$self->{dimms}}) {
+      if (($_->is_present()) && ($_->{condition} ne 'ok')) {
+        $_->add_message(CRITICAL, $_->{info});
+        $errorfound++;
+      }
+    }
+  } else {
+    if ($self->{runtime}->{options}->{ignore_dimms}) {
+      $self->add_message(OK,
+          sprintf "ignoring %d dimms with status 'n/a' ",
+          scalar(grep { ($_->is_present()) } @{$self->{dimms}}));
+    } elsif ($self->{runtime}->{options}->{buggy_firmware}) {
+      $self->add_message(OK,
+          sprintf "ignoring %d dimms with status 'n/a' because of buggy firmware",
+          scalar(grep { ($_->is_present()) } @{$self->{dimms}}));
+    } else {
+      $self->add_message(WARNING,
+        sprintf "status of all %d dimms is n/a (please upgrade firmware)",
+        scalar(grep { $_->is_present() } @{$self->{dimms}}));
+        $errorfound++;
+    }
+  }
+  foreach (@{$self->{dimms}}) {
+    printf "%s\n", $_->{info} if $self->{runtime}->{options}->{verbose} >= 2;
+  }
+  if (! $errorfound && $self->is_faulty()) {
+  #if ($self->is_faulty()) {
+    $self->add_message(WARNING,
+        sprintf 'overall memory error found');
+  }
+}
+
+sub dump {
+  my $self = shift;
+ printf "i dump the memory\n";
+  foreach (@{$self->{dimms}}) {
+    $_->dump();
+  }
+}
+
+package HP::Proliant::Component::MemorySubsystem::Dimm;
+our @ISA = qw(HP::Proliant::Component::MemorySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cartridge => $params{cartridge},
+    module => $params{module},
+    size => $params{size} || 0,
+    status => $params{status},
+    condition => $params{condition},
+    type => $params{type},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->{name} = sprintf '%s:%s',
+      $self->{cartridge}, $self->{module};
+  $self->{location} = sprintf 'module %s @ cartridge %s',
+      $self->{module}, $self->{cartridge};
+  return $self;
+}  
+
+sub check {
+  my $self = shift;
+  # check dient nur dazu, info und extended_info zu füllen
+  # die eigentliche bewertung findet eins höher statt
+  $self->blacklist('d', $self->{name});
+  if (($self->{status} eq 'present') || ($self->{status} eq 'good')) {
+    if ($self->{condition} eq 'other') {
+      $self->add_info(sprintf 'dimm %s (%s) is n/a',
+          $self->{name}, $self->{location});
+    } elsif ($self->{condition} ne 'ok') {
+      $self->add_info(
+        sprintf "dimm module %s (%s) needs attention (%s)",
+        $self->{name}, $self->{location}, $self->{condition});
+    } else {
+      $self->add_info(sprintf 'dimm module %s (%s) is %s',
+          $self->{name}, $self->{location}, $self->{condition});
+    }
+  } elsif ($self->{status} eq 'notPresent') {
+    $self->add_info(sprintf 'dimm module %s (%s) is not present',
+        $self->{name}, $self->{location});
+  } else {
+    $self->add_info(
+      sprintf "dimm module %s (%s) needs attention (%s)",
+      $self->{name}, $self->{location}, $self->{condition});
+  }
+}
+
+sub is_present {
+  my $self = shift;
+  my @signs_of_presence = (qw(present good add upgraded doesnotmatch 
+      notsupported badconfig degraded));
+  return scalar(grep { $self->{status} eq $_ } @signs_of_presence);
+}
+
+
+sub dump {
+  my $self = shift;
+  #printf "[DIMM_%s_%s]\n", $self->{cartridge}, $self->{module};
+  #foreach (qw(cartridge module size status condition info)) {
+  #  printf "%s: %s\n", $_, $self->{$_};
+  #}
+  #printf "status: %s\n", $self->{status} if exists $self->{status};
+  #printf "\n";
+  printf "car %02d  mod %02d  siz %.0f  sta %-12s  con %-10s  typ %s\n",
+    $self->{cartridge}, $self->{module}, $self->{size}, 
+    $self->{status}, $self->{condition}, defined $self->{type} ? $self->{type} : "";
+}
+
+
+package HP::Proliant::Component::MemorySubsystem::Cartridge;
+our @ISA = qw(HP::Proliant::Component::MemorySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    cpqHeResMemBoardSlotIndex => $params{cpqHeResMemBoardSlotIndex},
+    cpqHeResMemBoardOnlineStatus => $params{cpqHeResMemBoardOnlineStatus},
+    cpqHeResMemBoardErrorStatus => $params{cpqHeResMemBoardErrorStatus},
+    cpqHeResMemBoardNumSockets => $params{cpqHeResMemBoardNumSockets},
+    cpqHeResMemBoardOsMemSize => $params{cpqHeResMemBoardOsMemSize},
+    cpqHeResMemBoardTotalMemSize => $params{cpqHeResMemBoardTotalMemSize},
+    cpqHeResMemBoardCondition => $params{cpqHeResMemBoardCondition},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  return $self;
+}  
+
+sub dump {
+  my $self = shift;
+  #printf "[CARTRIDGE_%s_%s]\n", $self->{cpqHeResMemBoardSlotIndex};
+  #foreach (qw(cpqHeResMemBoardSlotIndex cpqHeResMemBoardOnlineStatus
+  #    cpqHeResMemBoardErrorStatus cpqHeResMemBoardNumSockets
+  #    cpqHeResMemBoardOsMemSize cpqHeResMemBoardTotalMemSize
+  #    cpqHeResMemBoardCondition)) {
+  #  printf "%s: %s\n", $_, $self->{$_};
+  #}
+  #printf "\n";
+}
+
+
+package HP::Proliant::Component::NicSubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::NicSubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+    logical_nics => [],
+    physical_nics => [],
+  };
+  bless $self, $class;
+  $self->overall_init();
+  $self->init();
+  return $self;
+}
+
+sub overall_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  # overall
+  my $cpqNicIfLogMapOverallCondition  = '1.3.6.1.4.1.232.18.2.2.2.0';
+  my $cpqNicIfLogMapOverallConditionValue = {
+    1 => 'other',
+    2 => 'ok',
+    3 => 'degraded',
+    4 => 'failed',
+  };
+  $self->{lognicstatus} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqNicIfLogMapOverallCondition,
+      $cpqNicIfLogMapOverallConditionValue);
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my $ifconnect = {};
+  # CPQNIC-MIB
+  my $oids = {
+      cpqNicIfLogMapEntry => '1.3.6.1.4.1.232.18.2.2.1.1',
+      cpqNicIfLogMapIndex => '1.3.6.1.4.1.232.18.2.2.1.1.1',
+      cpqNicIfLogMapIfNumber => '1.3.6.1.4.1.232.18.2.2.1.1.2',
+      cpqNicIfLogMapDescription => '1.3.6.1.4.1.232.18.2.2.1.1.3',
+      cpqNicIfLogMapGroupType => '1.3.6.1.4.1.232.18.2.2.1.1.4',
+      cpqNicIfLogMapAdapterCount => '1.3.6.1.4.1.232.18.2.2.1.1.5',
+      cpqNicIfLogMapAdapterOKCount => '1.3.6.1.4.1.232.18.2.2.1.1.6',
+      cpqNicIfLogMapPhysicalAdapters => '1.3.6.1.4.1.232.18.2.2.1.1.7',
+      cpqNicIfLogMapMACAddress => '1.3.6.1.4.1.232.18.2.2.1.1.8',
+      cpqNicIfLogMapSwitchoverMode => '1.3.6.1.4.1.232.18.2.2.1.1.9',
+      cpqNicIfLogMapCondition => '1.3.6.1.4.1.232.18.2.2.1.1.10',
+      cpqNicIfLogMapStatus => '1.3.6.1.4.1.232.18.2.2.1.1.11',
+      cpqNicIfLogMapNumSwitchovers => '1.3.6.1.4.1.232.18.2.2.1.1.12',
+      cpqNicIfLogMapHwLocation => '1.3.6.1.4.1.232.18.2.2.1.1.13',
+      cpqNicIfLogMapSpeed => '1.3.6.1.4.1.232.18.2.2.1.1.14',
+      cpqNicIfLogMapVlanCount => '1.3.6.1.4.1.232.18.2.2.1.1.15',
+      cpqNicIfLogMapVlans => '1.3.6.1.4.1.232.18.2.2.1.1.16',
+
+      cpqNicIfLogMapGroupTypeValue => {
+          1 => "unknown",
+          2 => "none",
+          3 => "redundantPair",
+          4 => "nft",
+          5 => "alb",
+          6 => "fec",
+          7 => "gec",
+          8 => "ad",
+          9 => "slb",
+          10 => "tlb",
+          11 => "redundancySet",
+      },
+      cpqNicIfLogMapConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqNicIfLogMapStatusValue => {
+          1 => "unknown",
+          2 => "ok",
+          3 => "primaryFailed",
+          4 => "standbyFailed",
+          5 => "groupFailed",
+          6 => "redundancyReduced",
+          7 => "redundancyLost",
+      },
+      cpqNicIfLogMapSwitchoverModeValue => {
+          1 => "unknown",
+          2 => "none",
+          3 => "manual",
+          4 => "switchOnFail",
+          5 => "preferredPrimary",
+      },
+  };
+
+  # INDEX { cpqNicIfLogMapIndex }
+  foreach ($self->get_entries($oids, 'cpqNicIfLogMapEntry')) {
+    push(@{$self->{logical_nics}}, 
+        HP::Proliant::Component::NicSubsystem::LogicalNic->new(%{$_})
+    );
+  }
+
+  $oids = {
+      cpqNicIfPhysAdapterEntry => '1.3.6.1.4.1.232.18.2.3.1.1',
+      cpqNicIfPhysAdapterIndex => '1.3.6.1.4.1.232.18.2.3.1.1.1',
+      cpqNicIfPhysAdapterIfNumber => '1.3.6.1.4.1.232.18.2.3.1.1.2',
+      cpqNicIfPhysAdapterRole => '1.3.6.1.4.1.232.18.2.3.1.1.3',
+      cpqNicIfPhysAdapterMACAddress => '1.3.6.1.4.1.232.18.2.3.1.1.4',
+      cpqNicIfPhysAdapterSlot => '1.3.6.1.4.1.232.18.2.3.1.1.5',
+      cpqNicIfPhysAdapterIoAddr => '1.3.6.1.4.1.232.18.2.3.1.1.6',
+      cpqNicIfPhysAdapterIrq => '1.3.6.1.4.1.232.18.2.3.1.1.7',
+      cpqNicIfPhysAdapterDma => '1.3.6.1.4.1.232.18.2.3.1.1.8',
+      cpqNicIfPhysAdapterMemAddr => '1.3.6.1.4.1.232.18.2.3.1.1.9',
+      cpqNicIfPhysAdapterPort => '1.3.6.1.4.1.232.18.2.3.1.1.10',
+      cpqNicIfPhysAdapterDuplexState => '1.3.6.1.4.1.232.18.2.3.1.1.11',
+      cpqNicIfPhysAdapterCondition => '1.3.6.1.4.1.232.18.2.3.1.1.12',
+      cpqNicIfPhysAdapterState => '1.3.6.1.4.1.232.18.2.3.1.1.13',
+      cpqNicIfPhysAdapterStatus => '1.3.6.1.4.1.232.18.2.3.1.1.14',
+      cpqNicIfPhysAdapterStatsValid => '1.3.6.1.4.1.232.18.2.3.1.1.15',
+      cpqNicIfPhysAdapterGoodTransmits => '1.3.6.1.4.1.232.18.2.3.1.1.16',
+      cpqNicIfPhysAdapterGoodReceives => '1.3.6.1.4.1.232.18.2.3.1.1.17',
+      cpqNicIfPhysAdapterBadTransmits => '1.3.6.1.4.1.232.18.2.3.1.1.18',
+      cpqNicIfPhysAdapterBadReceives => '1.3.6.1.4.1.232.18.2.3.1.1.19',
+      cpqNicIfPhysAdapterAlignmentErrors => '1.3.6.1.4.1.232.18.2.3.1.1.20',
+      cpqNicIfPhysAdapterFCSErrors => '1.3.6.1.4.1.232.18.2.3.1.1.21',
+      cpqNicIfPhysAdapterSingleCollisionFrames => '1.3.6.1.4.1.232.18.2.3.1.1.22',
+      cpqNicIfPhysAdapterMultipleCollisionFrames => '1.3.6.1.4.1.232.18.2.3.1.1.23',
+      cpqNicIfPhysAdapterDeferredTransmissions => '1.3.6.1.4.1.232.18.2.3.1.1.24',
+      cpqNicIfPhysAdapterLateCollisions => '1.3.6.1.4.1.232.18.2.3.1.1.25',
+      cpqNicIfPhysAdapterExcessiveCollisions => '1.3.6.1.4.1.232.18.2.3.1.1.26',
+      cpqNicIfPhysAdapterInternalMacTransmitErrors => '1.3.6.1.4.1.232.18.2.3.1.1.27',
+      cpqNicIfPhysAdapterCarrierSenseErrors => '1.3.6.1.4.1.232.18.2.3.1.1.28',
+      cpqNicIfPhysAdapterFrameTooLongs => '1.3.6.1.4.1.232.18.2.3.1.1.29',
+      cpqNicIfPhysAdapterInternalMacReceiveErrors => '1.3.6.1.4.1.232.18.2.3.1.1.30',
+      cpqNicIfPhysAdapterHwLocation => '1.3.6.1.4.1.232.18.2.3.1.1.31',
+      cpqNicIfPhysAdapterPartNumber => '1.3.6.1.4.1.232.18.2.3.1.1.32',
+      cpqNicIfPhysAdapterRoleValue => {
+          1 => "unknown",
+          2 => "primary",
+          3 => "secondary",
+          4 => "member",
+          5 => "txRx",
+          6 => "tx",
+          7 => "standby",
+          8 => "none",
+          255 => "notApplicable",
+      },
+      cpqNicIfPhysAdapterDuplexStateValue => {
+          1 => "unknown",
+          2 => "half",
+          3 => "full",
+      },
+      cpqNicIfPhysAdapterConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqNicIfPhysAdapterStateValue => {
+          1 => "unknown",
+          2 => "ok",
+          3 => "standby",
+          4 => "failed",
+      },
+      cpqNicIfPhysAdapterStatusValue => {
+          1 => "unknown",
+          2 => "ok",
+          3 => "generalFailure",
+          4 => "linkFailure",
+      },
+
+  };
+  # INDEX { cpqNicIfPhysAdapterIndex }
+  foreach ($self->get_entries($oids, 'cpqNicIfPhysAdapterEntry')) {
+    push(@{$self->{physical_nics}},
+        HP::Proliant::Component::NicSubsystem::PhysicalNic->new(%{$_}));
+  }
+
+}
+
+
+package HP::Proliant::Component::NicSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    logical_nics => [],
+    physical_nics => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::NicSubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::NicSubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking nic teams');
+  if (scalar (@{$self->{logical_nics}}) == 0) {
+    $self->add_info('no logical nics found');
+    $self->overall_check();
+  } else {
+    foreach (@{$self->{logical_nics}}) {
+      $_->check();
+    }
+  }
+  if (scalar (@{$self->{physical_nics}}) == 0) {
+    $self->add_info('no physical nics found. do you connect with slip?');
+  } else {
+    foreach (@{$self->{physical_nics}}) {
+      $_->check();
+    }
+  }
+}
+
+sub num_logical_nics {
+  my $self = shift;
+  return scalar @{$self->{logical_nics}};
+}
+
+sub num_physical_nics {
+  my $self = shift;
+  return scalar @{$self->{physical_nics}};
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{logical_nics}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{physical_nics}}) {
+    $_->dump();
+  }
+}
+
+sub overall_check {
+  my $self = shift;
+  if ($self->{lognicstatus} ne "ok") {
+    $self->add_info(sprintf 'overall logical nic status is %s',
+        $self->{lognicstatus});
+  }
+}
+
+
+package HP::Proliant::Component::NicSubsystem::LogicalNic;
+our @ISA = qw(HP::Proliant::Component::NicSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  foreach (qw(cpqNicIfLogMapIndex cpqNicIfLogMapIfNumber cpqNicIfLogMapDescription cpqNicIfLogMapGroupType cpqNicIfLogMapAdapterCount cpqNicIfLogMapAdapterOKCount cpqNicIfLogMapPhysicalAdapters cpqNicIfLogMapSwitchoverMode cpqNicIfLogMapCondition cpqNicIfLogMapStatus cpqNicIfLogMapNumSwitchovers cpqNicIfLogMapHwLocation cpqNicIfLogMapSpeed cpqNicIfLogMapVlanCount cpqNicIfLogMapVlans)) {
+    $self->{$_} = $params{$_};
+  }
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('lni', $self->{cpqNicIfLogMapIndex});
+  if ($self->{cpqNicIfLogMapAdapterCount} > 0) {
+    if ($self->{cpqNicIfLogMapCondition} eq "other") {
+      # simply ignore this. if there is a physical nic
+      # it is usually unknown/other/scheissegal
+      $self->add_info(sprintf "logical nic %d (%s) is %s",
+          $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription},
+          $self->{cpqNicIfLogMapCondition});
+    } elsif ($self->{cpqNicIfLogMapCondition} ne "ok") {
+      $self->add_info(sprintf "logical nic %d (%s) is %s (%s)",
+          $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription},
+          $self->{cpqNicIfLogMapCondition}, $self->{cpqNicIfLogMapStatus});
+      $self->add_message(CRITICAL, $self->{info});
+    } else {
+      $self->add_info(sprintf "logical nic %d (%s) is %s",
+          $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription},
+          $self->{cpqNicIfLogMapCondition});
+    }
+  } else {
+    $self->add_info(sprintf "logical nic %d (%s) has 0 physical nics",
+        $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription});
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[LNIC_%s]\n", $self->{cpqNicIfLogMapIndex};
+  foreach (qw(cpqNicIfLogMapIndex cpqNicIfLogMapIfNumber cpqNicIfLogMapDescription cpqNicIfLogMapAdapterCount cpqNicIfLogMapGroupType cpqNicIfLogMapSwitchoverMode cpqNicIfLogMapCondition cpqNicIfLogMapStatus cpqNicIfLogMapNumSwitchovers cpqNicIfLogMapHwLocation cpqNicIfLogMapSpeed)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::NicSubsystem::PhysicalNic;
+our @ISA = qw(HP::Proliant::Component::NicSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  foreach (qw(cpqNicIfPhysAdapterIndex cpqNicIfPhysAdapterIfNumber cpqNicIfPhysAdapterRole cpqNicIfPhysAdapterDuplexState cpqNicIfPhysAdapterCondition cpqNicIfPhysAdapterState cpqNicIfPhysAdapterStatus cpqNicIfPhysAdapterBadTransmits cpqNicIfPhysAdapterBadReceives)) {
+    $self->{$_} = $params{$_};
+  }
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('pni', $self->{cpqNicIfPhysAdapterIndex});
+  if ($self->{cpqNicIfPhysAdapterCondition} eq "other") {
+    # hp doesnt output a clear status. i am optimistic, unknown/other
+    # means "dont care"
+    $self->add_info(sprintf "physical nic %d (%s) is %s",
+        $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
+        $self->{cpqNicIfPhysAdapterCondition});
+  } elsif ($self->{cpqNicIfPhysAdapterCondition} ne "ok") {
+    $self->add_info(sprintf "physical nic %d (%s) is %s (%s,%s)",
+        $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
+        $self->{cpqNicIfPhysAdapterCondition},
+        $self->{cpqNicIfPhysAdapterState}, $self->{cpqNicIfPhysAdapterStatus});
+    $self->add_message(CRITICAL, $self->{info});
+  } else {
+    if ($self->{cpqNicIfPhysAdapterDuplexState} ne "full") {
+      $self->add_info(sprintf "physical nic %d (%s) is %s duplex",
+          $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
+          $self->{cpqNicIfPhysAdapterDuplexState});
+    } else {
+      $self->add_info(sprintf "physical nic %d (%s) is %s",
+          $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
+          $self->{cpqNicIfPhysAdapterCondition});
+    }
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[PNIC_%s]\n", $self->{cpqNicIfPhysAdapterIndex};
+  foreach (qw(cpqNicIfPhysAdapterIndex cpqNicIfPhysAdapterIfNumber cpqNicIfPhysAdapterRole cpqNicIfPhysAdapterDuplexState cpqNicIfPhysAdapterCondition cpqNicIfPhysAdapterState cpqNicIfPhysAdapterStatus cpqNicIfPhysAdapterBadTransmits cpqNicIfPhysAdapterBadReceives)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::AsrSubsystem::CLI;
+our @ISA = qw(HP::Proliant::Component::AsrSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init(%params);
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+}
+
+sub overall_check {
+  my $self = shift;
+  my %params = @_;
+}
+
+
+package HP::Proliant::Component::AsrSubsystem::SNMP;
+our @ISA = qw(HP::Proliant::Component::AsrSubsystem
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->overall_init(%params);
+  return $self;
+}
+
+sub overall_init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $params{rawdata};
+  my $cpqHeAsrStatus = "1.3.6.1.4.1.232.6.2.5.1.0";
+  my $cpqHeAsrStatusValue = {
+    1 => "other",
+    2 => "notAvailable",
+    3 => "disabled",
+    4 => "enabled",
+  };
+  my $cpqHeAsrCondition = "1.3.6.1.4.1.232.6.2.5.17.0";
+  my $cpqHeAsrConditionValue = {
+    1 => "other",
+    2 => "ok",
+    3 => "degraded",
+    4 => "failed",
+  };
+  $self->{asrcondition} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqHeAsrCondition,
+      $cpqHeAsrConditionValue);
+  $self->{asrstatus} = lc SNMP::Utils::get_object_value(
+      $snmpwalk, $cpqHeAsrStatus,
+      $cpqHeAsrStatusValue);
+}
+
+sub overall_check {
+  my $self = shift;
+  my $result = 0;
+  $self->blacklist('asr', '');
+  if ($self->{asrstatus} and $self->{asrstatus} eq "enabled") {
+    my $info = sprintf 'ASR overall condition is %s', $self->{asrcondition};
+    if ($self->{asrcondition} eq "degraded") {
+      $self->add_message(WARNING, $info);
+    } elsif ($self->{asrcondition} eq "failed") {
+      $self->add_message(CRITICAL, $info);
+    }
+    $self->add_info($info);
+  } else {
+    $self->add_info('This system does not have ASR.');
+  }
+}
+
+
+package HP::Proliant::Component::AsrSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    temperatures => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    return HP::Proliant::Component::AsrSubsystem::SNMP->new(%params);
+  } elsif ($self->{method} eq 'cli') {
+    return HP::Proliant::Component::AsrSubsystem::CLI->new(%params);
+  } else {
+    die "unknown method";
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  $self->add_info('checking ASR');
+  $self->overall_check(); 
+}
+
+sub dump {
+  my $self = shift;
+}
+
+
+
+
+package HP::Proliant::Component::SNMP;
+
+sub get_entries {
+  my $self = shift;
+  my $oids = shift;
+  my $entry = shift;
+  my $snmpwalk = $self->{rawdata};
+  my @params = ();
+  my @indices = SNMP::Utils::get_indices($snmpwalk, $oids->{$entry});
+  foreach (@indices) {
+    my @idx = @{$_};
+    my %params = ( 
+      runtime => $self->{runtime},
+    );
+    my $maxdimension = scalar(@idx) - 1;
+    foreach my $idxnr (1..scalar(@idx)) {
+      $params{'index'.$idxnr} = $_->[$idxnr - 1];
+    }
+    foreach my $oid (keys %{$oids}) {
+      next if $oid =~ /Entry$/;
+      next if $oid =~ /Value$/;
+      if (exists $oids->{$oid.'Value'}) {
+        $params{$oid} = SNMP::Utils::get_object_value(
+            $snmpwalk, $oids->{$oid}, $oids->{$oid.'Value'}, @idx);
+        if (! defined  $params{$oid}) {
+          my $numerical_value = SNMP::Utils::get_object(
+              $snmpwalk, $oids->{$oid}, @idx);
+          if (! defined $numerical_value) {
+            # maschine liefert schrott
+            $params{$oid} = 'value_unknown';
+          } else {
+            $params{$oid} = 'value_'.SNMP::Utils::get_object(
+                $snmpwalk, $oids->{$oid}, @idx);
+          }
+        }
+      } else {  
+        $params{$oid} = SNMP::Utils::get_object(
+            $snmpwalk, $oids->{$oid}, @idx);
+      }         
+    }     
+    push(@params, \%params);
+  }
+  return @params;
+}
+
+sub mib {
+  my $self = shift;
+  my $mib = shift;
+  my $condition = {
+      0 => 'other',
+      1 => 'ok',
+      2 => 'degraded',
+      3 => 'failed',
+  };
+  my $MibRevMajor = $mib.'.1.0';
+  my $MibRevMinor = $mib.'.2.0';
+  my $MibRevCondition = $mib.'.3.0';
+  return (
+      $self->SNMP::Utils::get_object($self->{rawdata},
+          $MibRevMajor),
+      $self->SNMP::Utils::get_object($self->{rawdata},
+          $MibRevMinor),
+      $self->SNMP::Utils::get_object_value($self->{rawdata},
+          $MibRevCondition, $condition));
+};
+
+
+package HP::Proliant::Component::DiskSubsystem::Da::CLI;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $hpacucli = $self->{rawdata};
+  my $slot = 0;
+  my $type = "unkn";
+  my @lines = ();
+  my $thistype = 0;
+  my $tmpcntl = {};
+  my $tmpaccel = {};
+  my $tmpld = {};
+  my $tmppd = {};
+  my $cntlindex = 0;
+  my $ldriveindex = 0;
+  my $pdriveindex = 0;
+  my $incontroller = 0;
+  foreach (split(/\n/, $hpacucli)) {
+    next unless /^status/;
+    next if /^status\s*$/;
+    s/^status\s*//;
+    if (/([\s\w]+) in Slot\s+(\d+)/) {
+      $incontroller = 1;
+      $slot = $2;
+      $cntlindex++;
+      $tmpcntl->{$slot}->{cpqDaCntlrIndex} = $cntlindex;
+      $tmpcntl->{$slot}->{cpqDaCntlrModel} = $1;
+      $tmpcntl->{$slot}->{cpqDaCntlrSlot} = $slot;
+    } elsif (/(MSA[\s\w]+)\s+in\s+(\w+)/) { 
+      $incontroller = 1;
+      $slot = $2;
+      $cntlindex++;
+      $tmpcntl->{$slot}->{cpqDaCntlrIndex} = $cntlindex;
+      $tmpcntl->{$slot}->{cpqDaCntlrModel} = $1;
+      $tmpcntl->{$slot}->{cpqDaCntlrSlot} = $slot;
+    } elsif (/Controller Status: (\w+)/) {
+      $tmpcntl->{$slot}->{cpqDaCntlrBoardCondition} = lc $1;
+      $tmpcntl->{$slot}->{cpqDaCntlrCondition} = lc $1;
+    } elsif (/Cache Status: ([\w\s]+\s*$)/) {
+      # Cache Status: OK
+      # Cache Status: Not Configured
+      # Cache Status: Temporarily Disabled
+      $tmpaccel->{$slot}->{cpqDaAccelCntlrIndex} = $cntlindex;
+      $tmpaccel->{$slot}->{cpqDaAccelSlot} = $slot;
+      #condition: other,ok,degraded,failed
+      #status: other,invalid,enabled,tmpDisabled,permDisabled
+      $tmpaccel->{$slot}->{cpqDaAccelCondition} = lc $1; 
+      if ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'ok') {
+        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
+      } elsif ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'not configured') {
+        $tmpaccel->{$slot}->{cpqDaAccelCondition} = 'ok';
+        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
+      } elsif ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'temporarily disabled') {
+        $tmpaccel->{$slot}->{cpqDaAccelCondition} = 'ok';
+        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'tmpDisabled';
+      } elsif ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'permanently disabled') {
+        $tmpaccel->{$slot}->{cpqDaAccelCondition} = 'ok';
+        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'permDisabled';
+      } else {
+        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
+      }
+    } elsif (/Battery.* Status: (\w+)/) {
+      # sowas gibts auch Battery/Capacitor Status: OK
+      $tmpaccel->{$slot}->{cpqDaAccelBattery} = lc $1;
+    } elsif (/^\s*$/) {
+    }
+  }
+  $cntlindex = 0;
+  $ldriveindex = 0;
+  $pdriveindex = 0;
+  foreach (split(/\n/, $hpacucli)) {
+    next unless /^config/;
+    next if /^config\s*$/;
+    s/^config\s*//;
+    if (/([\s\w]+) in Slot\s+(\d+)/) {
+      $slot = $2;
+      $cntlindex++;
+      $pdriveindex = 1;
+    } elsif (/(MSA[\s\w]+)\s+in\s+(\w+)/) {
+      $slot = $2;
+      $cntlindex++;
+      $pdriveindex = 1;
+    } elsif (/logicaldrive\s+(.+?)\s+\((.*)\)/) {
+      # logicaldrive 1 (683.5 GB, RAID 5, OK)
+      # logicaldrive 1 (683.5 GB, RAID 5, OK)
+      # logicaldrive 2 (442 MB, RAID 1+0, OK)
+      $ldriveindex = $1;
+      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvCntlrIndex} = $cntlindex;
+      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvIndex} = $ldriveindex;
+      ($tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvSize},
+          $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvFaultTol},
+          $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvCondition}) =
+          map { lc $_ } split(/,\s*/, $2);
+      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvStatus} =
+          $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvCondition};
+      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvPhyDrvIDs} = 'unknown';
+    } elsif (/physicaldrive\s+(.+?)\s+\((.*)\)/) {
+      # physicaldrive 2:0   (port 2:id 0 , Parallel SCSI, 36.4 GB, OK)
+      # physicaldrive 2I:1:6 (port 2I:box 1:bay 6, SAS, 146 GB, OK)
+      # physicaldrive 1:1 (box 1:bay 1, Parallel SCSI, 146 GB, OK)
+      my $name = $1;
+      my($location, $type, $size, $status) = split(/,/, $2);
+      $status =~ s/^\s+//g;
+      $status =~ s/\s+$//g;
+      $status = lc $status;
+      my %location = ();
+      foreach (split(/:/, $location)) {
+        $location{$1} = $2 if /(\w+)\s+(\w+)/;
+      }
+      $location{box} ||= 0;
+      $location{id} ||= $pdriveindex;
+      $location{bay} ||= $location{id};
+      $location{port} ||= $location{bay};
+      $tmppd->{$slot}->{$name}->{name} = lc $name;
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvCntlrIndex} = $cntlindex;
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvIndex} = $location{id};
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvBay} = $location{bay};
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvBusNumber} = $location{port};
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvSize} = $size;
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvStatus} = $status;
+      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvCondition} = $status;
+      $tmppd->{$slot}->{$name}->{ldriveindex} = $ldriveindex || -1;
+      foreach (keys %{$tmppd->{$slot}->{$name}}) {
+        $tmppd->{$slot}->{$name}->{$_} =~ s/^\s+//g;
+        $tmppd->{$slot}->{$name}->{$_} =~ s/\s+$//g;
+        $tmppd->{$slot}->{$name}->{$_} = lc $tmppd->{$slot}->{$name}->{$_};
+      }
+      $pdriveindex++;
+    }
+  }
+
+  foreach my $slot (keys %{$tmpcntl}) {
+    if (exists $tmpcntl->{$slot}->{cpqDaCntlrModel} &&
+        ! $self->identified($tmpcntl->{$slot}->{cpqDaCntlrModel})) {
+      delete $tmpcntl->{$slot};
+      delete $tmpaccel->{$slot};
+      delete $tmpld->{$slot};
+      delete $tmppd->{$slot};
+    }
+  }
+
+#printf "%s\n", Data::Dumper::Dumper($tmpcntl);
+#printf "%s\n", Data::Dumper::Dumper($tmpaccel);
+#printf "%s\n", Data::Dumper::Dumper($tmpld);
+#printf "%s\n", Data::Dumper::Dumper($tmppd);
+  foreach my $slot (sort {
+      $tmpcntl->{$a}->{cpqDaCntlrIndex} <=> $tmpcntl->{$b}->{cpqDaCntlrIndex}
+      }keys %{$tmpcntl}) {
+    $tmpcntl->{$slot}->{runtime} = $self->{runtime};
+    push(@{$self->{controllers}},
+        HP::Proliant::Component::DiskSubsystem::Da::Controller->new(
+            %{$tmpcntl->{$slot}}));
+  }
+  foreach my $slot (sort {
+      $tmpaccel->{$a}->{cpqDaAccelCntlrIndex} <=> $tmpaccel->{$b}->{cpqDaAccelCntlrIndex}
+      } keys %{$tmpaccel}) {
+    $tmpaccel->{$slot}->{runtime} = $self->{runtime};
+    push(@{$self->{accelerators}},
+        HP::Proliant::Component::DiskSubsystem::Da::Accelerator->new(
+            %{$tmpaccel->{$slot}}));
+  }
+  foreach my $slot (keys %{$tmpld}) {
+    foreach my $ldriveindex (keys %{$tmpld->{$slot}}) {
+      $tmpld->{$slot}->{$ldriveindex}->{runtime} = $self->{runtime};
+      push(@{$self->{logical_drives}},
+          HP::Proliant::Component::DiskSubsystem::Da::LogicalDrive->new(
+              %{$tmpld->{$slot}->{$ldriveindex}}));
+    }
+    foreach my $pdriveindex (sort {
+        (split ':', $a, 2)[0] cmp (split ':', $b, 2)[0] ||
+        (split ':', $a, 2)[1] cmp (split ':', $b, 2)[1] ||
+        (split ':', $a, 2)[2] <=> (split ':', $b, 2)[2]
+        } keys %{$tmppd->{$slot}}) {
+      $tmppd->{$slot}->{$pdriveindex}->{runtime} = $self->{runtime};
+      push(@{$self->{physical_drives}},
+          HP::Proliant::Component::DiskSubsystem::Da::PhysicalDrive->new(
+              %{$tmppd->{$slot}->{$pdriveindex}}));
+    }
+  }
+}
+
+sub identified {
+  my $self = shift;
+  my $info = shift;
+  return 1 if $info =~ /Parallel SCSI/;
+  return 1 if $info =~ /Smart Array/; # Trond: works fine on E200i, P400, E400
+  return 1 if $info =~ /MSA500/;
+  #return 1 if $info =~ /Smart Array (5|6)/;
+  #return 1 if $info =~ /Smart Array P400i/; # snmp sagt Da, trotz SAS in cli
+  #return 1 if $info =~ /Smart Array P410i/; # dto
+  return 0;
+}
+package HP::Proliant::Component::DiskSubsystem::Da::SNMP;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = { 
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+
+  # CPQIDA-MIB
+  my $oids = {
+    cpqDaCntlrEntry => "1.3.6.1.4.1.232.3.2.2.1.1",
+    cpqDaCntlrIndex => "1.3.6.1.4.1.232.3.2.2.1.1.1",
+    cpqDaCntlrModel => "1.3.6.1.4.1.232.3.2.2.1.1.2",
+    cpqDaCntlrSlot => "1.3.6.1.4.1.232.3.2.2.1.1.5",
+    cpqDaCntlrCondition => "1.3.6.1.4.1.232.3.2.2.1.1.6",
+    cpqDaCntlrBoardCondition => "1.3.6.1.4.1.232.3.2.2.1.1.12",
+    cpqDaCntlrModelValue => {
+        1 => 'other',
+        2 => 'ida',
+        3 => 'idaExpansion',
+        4 => 'ida-2',
+        5 => 'smart',
+        6 => 'smart-2e',
+        7 => 'smart-2p',
+        8 => 'smart-2sl',
+        9 => 'smart-3100es',
+        10 => 'smart-3200',
+        11 => 'smart-2dh',
+        12 => 'smart-221',
+        13 => 'sa-4250es',
+        14 => 'sa-4200',
+        15 => 'sa-integrated',
+        16 => 'sa-431',
+        17 => 'sa-5300',
+        18 => 'raidLc2',
+        19 => 'sa-5i',
+        20 => 'sa-532',
+        21 => 'sa-5312',
+        22 => 'sa-641',
+        23 => 'sa-642',
+        24 => 'sa-6400',
+        25 => 'sa-6400em',
+        26 => 'sa-6i',
+    },
+    cpqDaCntlrConditionValue => {
+        1 => "other",
+        2 => "ok",
+        3 => "degraded",
+        4 => "failed",
+    },
+    cpqDaCntlrBoardConditionValue => {
+        1 => "other",
+        2 => "ok",
+        3 => "degraded",
+        4 => "failed",
+    },
+  };
+
+  # INDEX { cpqDaCntlrIndex }
+  foreach ($self->get_entries($oids, 'cpqDaCntlrEntry')) {
+    push(@{$self->{controllers}},
+        HP::Proliant::Component::DiskSubsystem::Da::Controller->new(%{$_}));
+  }
+
+  $oids = {
+      cpqDaAccelEntry => "1.3.6.1.4.1.232.3.2.2.2.1",
+      cpqDaAccelCntlrIndex => "1.3.6.1.4.1.232.3.2.2.2.1.1",
+      cpqDaAccelStatus => "1.3.6.1.4.1.232.3.2.2.2.1.2",
+      cpqDaAccelSlot => "1.3.6.1.4.1.232.3.2.2.2.1.5",
+      cpqDaAccelBattery  => "1.3.6.1.4.1.232.3.2.2.2.1.6",
+      cpqDaAccelCondition  => "1.3.6.1.4.1.232.3.2.2.2.1.9",
+      cpqDaAccelBatteryValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'recharging',
+          4 => 'failed',
+          5 => 'degraded',
+          6 => 'notPresent',
+      },
+      cpqDaAccelConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqDaAccelStatusValue => {
+          1 => "other",
+          2 => "invalid",
+          3 => "enabled",
+          4 => "tmpDisabled",
+          5 => "permDisabled",
+      }
+  };
+    
+  # INDEX { cpqDaAccelCntlrIndex }
+  foreach ($self->get_entries($oids, 'cpqDaAccelEntry')) {
+    push(@{$self->{accelerators}},
+        HP::Proliant::Component::DiskSubsystem::Da::Accelerator->new(%{$_}));
+  }
+
+  $oids = {
+      cpqDaLogDrvEntry => "1.3.6.1.4.1.232.3.2.3.1.1",
+      cpqDaLogDrvCntlrIndex => "1.3.6.1.4.1.232.3.2.3.1.1.1",
+      cpqDaLogDrvIndex => "1.3.6.1.4.1.232.3.2.3.1.1.2",
+      cpqDaLogDrvFaultTol => "1.3.6.1.4.1.232.3.2.3.1.1.3",
+      cpqDaLogDrvStatus => "1.3.6.1.4.1.232.3.2.3.1.1.4",
+      cpqDaLogDrvSize => "1.3.6.1.4.1.232.3.2.3.1.1.9",
+      cpqDaLogDrvPhyDrvIDs => "1.3.6.1.4.1.232.3.2.3.1.1.10",
+      cpqDaLogDrvCondition => "1.3.6.1.4.1.232.3.2.3.1.1.11",
+      cpqDaLogDrvPercentRebuild => "1.3.6.1.4.1.232.3.2.3.1.1.12",
+      cpqDaLogDrvFaultTolValue => {
+          1 => "other",
+          2 => "none",
+          3 => "mirroring",
+          4 => "dataGuard",
+          5 => "distribDataGuard",
+          7 => "advancedDataGuard",
+      },
+      cpqDaLogDrvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqDaLogDrvStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+          4 => "unconfigured",
+          5 => "recovering",
+          6 => "readyForRebuild",
+          7 => "rebuilding",
+          8 => "wrongDrive",
+          9 => "badConnect",
+          10 => "overheating",
+          11 => "shutdown",
+          12 => "expanding",
+          13 => "notAvailable",
+          14 => "queuedForExpansion",
+      },
+  };
+
+  # INDEX { cpqDaLogDrvCntlrIndex, cpqDaLogDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqDaLogDrvEntry')) {
+    $_->{cpqDaLogDrvPhyDrvIDs} ||= 'empty';
+    push(@{$self->{logical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Da::LogicalDrive->new(%{$_}));
+  }
+
+  $oids = {
+      cpqDaPhyDrvEntry => "1.3.6.1.4.1.232.3.2.5.1.1",
+      cpqDaPhyDrvCntlrIndex => "1.3.6.1.4.1.232.3.2.5.1.1.1",
+      cpqDaPhyDrvIndex => "1.3.6.1.4.1.232.3.2.5.1.1.2",
+      cpqDaPhyDrvBay => "1.3.6.1.4.1.232.3.2.5.1.1.5",
+      cpqDaPhyDrvStatus => "1.3.6.1.4.1.232.3.2.5.1.1.6",
+      cpqDaPhyDrvSize => "1.3.6.1.4.1.232.3.2.5.1.1.9",
+      cpqDaPhyDrvCondition => "1.3.6.1.4.1.232.3.2.5.1.1.37",
+      cpqDaPhyDrvBusNumber => "1.3.6.1.4.1.232.3.2.5.1.1.50",
+      cpqDaPhyDrvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqDaPhyDrvStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+          4 => "predictiveFailure",
+      },
+  };
+    
+  # INDEX { cpqDaPhyDrvCntlrIndex, cpqDaPhyDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqDaPhyDrvEntry')) {
+    push(@{$self->{physical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Da::PhysicalDrive->new(%{$_}));
+  }
+
+}
+package HP::Proliant::Component::DiskSubsystem::Da;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    condition => undef,
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Da::SNMP';
+  } else {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Da::CLI';
+  }
+  $self->init();
+  $self->assemble();
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->check();
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->dump();
+  }
+}
+
+package HP::Proliant::Component::DiskSubsystem::Da::Controller;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqDaCntlrIndex => $params{cpqDaCntlrIndex},
+    cpqDaCntlrSlot => $params{cpqDaCntlrSlot},
+    cpqDaCntlrModel => $params{cpqDaCntlrModel},
+    cpqDaCntlrCondition => $params{cpqDaCntlrCondition},
+    cpqDaCntlrBoardCondition => $params{cpqDaCntlrBoardCondition},
+    blacklisted => 0,
+  };
+  $self->{name} = $params{name} || $self->{cpqDaCntlrSlot};
+  $self->{controllerindex} = $self->{cpqDaCntlrIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+#$self->dumper($self);
+  $self->blacklist('daco', $self->{cpqDaCntlrIndex});
+  foreach (@{$self->{accelerators}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{logical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{physical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{spare_drives}}) {
+    $_->check();
+  } 
+  if ($self->{cpqDaCntlrCondition} eq 'other') {
+    if (scalar(@{$self->{physical_drives}})) {
+      $self->add_message(CRITICAL,
+          sprintf 'da controller %s in slot %s needs attention', 
+              $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+      $self->add_info(sprintf 'da controller %s in slot %s needs attention',
+          $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+    } else {
+      $self->add_info(sprintf 'da controller %s in slot %s is ok and unused',
+          $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+      $self->{blacklisted} = 1;
+    }
+  } elsif ($self->{cpqDaCntlrCondition} eq 'degraded') {
+    # maybe only the battery has failed and is disabled, no problem
+    if (scalar(grep {
+        $_->has_failed() && $_->is_disabled()
+    } @{$self->{accelerators}})) {
+      # message was already written in the accel code
+    } else {
+      $self->add_message(CRITICAL,
+          sprintf 'da controller %s in slot %s needs attention', 
+              $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+      $self->add_info(sprintf 'da controller %s in slot %s needs attention',
+          $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+    }
+  } elsif ($self->{cpqDaCntlrCondition} ne 'ok') {
+    $self->add_message(CRITICAL,
+        sprintf 'da controller %s in slot %s needs attention', 
+            $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+    $self->add_info(sprintf 'da controller %s in slot %s needs attention',
+        $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+  } else {
+    $self->add_info(sprintf 'da controller %s in slot %s is ok', 
+        $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
+  }
+} 
+
+sub dump {
+  my $self = shift;
+  printf "[DA_CONTROLLER_%s]\n", $self->{name};
+  foreach (qw(cpqDaCntlrSlot cpqDaCntlrIndex cpqDaCntlrCondition 
+      cpqDaCntlrModel)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+  foreach (@{$self->{accelerators}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{logical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{physical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{spare_drives}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Da::Accelerator;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqDaAccelCntlrIndex => $params{cpqDaAccelCntlrIndex},
+    cpqDaAccelBattery => $params{cpqDaAccelBattery} || 'notPresent',
+    cpqDaAccelCondition => $params{cpqDaAccelCondition},
+    cpqDaAccelStatus => $params{cpqDaAccelStatus},
+    blacklisted => 0,
+    failed => 0,
+  };
+  $self->{controllerindex} = $self->{cpqDaAccelCntlrIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('daac', $self->{cpqDaAccelCntlrIndex});
+  $self->add_info(sprintf 'controller accelerator is %s',
+      $self->{cpqDaAccelCondition});
+  if ($self->{cpqDaAccelStatus} ne "enabled") {
+  } elsif ($self->{cpqDaAccelCondition} ne "ok") {
+    if ($self->{cpqDaAccelBattery} eq "failed" &&
+        $self->{cpqDaAccelStatus} eq "tmpDisabled") {
+      # handled later
+    } else {
+      $self->add_message(CRITICAL, "controller accelerator needs attention");
+    }
+  }
+  $self->blacklist('daacb', $self->{cpqDaAccelCntlrIndex});
+  $self->add_info(sprintf 'controller accelerator battery is %s',
+      $self->{cpqDaAccelBattery});
+  if ($self->{cpqDaAccelBattery} eq "notPresent") {
+  } elsif ($self->{cpqDaAccelBattery} eq "recharging") {
+    $self->add_message(WARNING, "controller accelerator battery recharging");
+  } elsif ($self->{cpqDaAccelBattery} eq "failed" &&
+      $self->{cpqDaAccelStatus} eq "tmpDisabled") {
+    $self->add_message(WARNING, "controller accelerator battery needs attention");
+  } elsif ($self->{cpqDaAccelBattery} ne "ok") {
+    # (other) failed degraded
+    $self->add_message(CRITICAL, "controller accelerator battery needs attention");
+  } 
+}
+
+sub has_failed {
+  my $self = shift;
+  return $self->{cpqDaAccelStatus} =~ /Disabled/ ? 1 : 0;
+}
+
+sub is_disabled {
+  my $self = shift;
+  return $self->{cpqDaAccelStatus} =~ /Disabled/ ? 1 : 0;
+}
+
+sub dump {
+  my $self = shift;
+  printf "[ACCELERATOR]\n";
+  foreach (qw(cpqDaAccelCntlrIndex cpqDaAccelBattery
+      cpqDaAccelStatus cpqDaAccelCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Da::LogicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqDaLogDrvIndex => $params{cpqDaLogDrvIndex},
+    cpqDaLogDrvCntlrIndex => $params{cpqDaLogDrvCntlrIndex},
+    cpqDaLogDrvSize => $params{cpqDaLogDrvSize},
+    cpqDaLogDrvFaultTol => $params{cpqDaLogDrvFaultTol},
+    cpqDaLogDrvPercentRebuild => $params{cpqDaLogDrvPercentRebuild},
+    cpqDaLogDrvStatus => $params{cpqDaLogDrvStatus},
+    cpqDaLogDrvCondition => $params{cpqDaLogDrvCondition},
+    cpqDaLogDrvPhyDrvIDs => $params{cpqDaLogDrvPhyDrvIDs},
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  $self->{name} = $params{name} || 
+      $self->{cpqDaLogDrvCntlrIndex}.':'.$self->{cpqDaLogDrvIndex}; ##vorerst
+  $self->{controllerindex} = $self->{cpqDaLogDrvCntlrIndex};
+  if (! $self->{cpqDaLogDrvPercentRebuild} ||
+      $self->{cpqDaLogDrvPercentRebuild} == 4294967295) {
+    $self->{cpqDaLogDrvPercentRebuild} = 100;
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('dald', $self->{name});
+  $self->add_info(sprintf "logical drive %s is %s (%s)",
+          $self->{name}, $self->{cpqDaLogDrvStatus},
+          $self->{cpqDaLogDrvFaultTol});
+  if ($self->{cpqDaLogDrvCondition} ne "ok") {
+    if ($self->{cpqDaLogDrvStatus} =~ 
+        /rebuild|recovering|expanding|queued/) {
+      $self->add_message(WARNING,
+          sprintf "logical drive %s is %s", 
+              $self->{name}, $self->{cpqDaLogDrvStatus});
+    } else {
+      $self->add_message(CRITICAL,
+          sprintf "logical drive %s is %s",
+              $self->{name}, $self->{cpqDaLogDrvStatus});
+    }
+  } 
+}
+
+sub dump {
+  my $self = shift;
+  printf "[LOGICAL_DRIVE]\n";
+  foreach (qw(cpqDaLogDrvCntlrIndex cpqDaLogDrvIndex cpqDaLogDrvSize
+      cpqDaLogDrvFaultTol cpqDaLogDrvStatus cpqDaLogDrvCondition
+      cpqDaLogDrvPercentRebuild cpqDaLogDrvPhyDrvIDs)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Da::PhysicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    name => $params{name},
+    cpqDaPhyDrvCntlrIndex => $params{cpqDaPhyDrvCntlrIndex},
+    cpqDaPhyDrvIndex => $params{cpqDaPhyDrvIndex},
+    cpqDaPhyDrvBay => $params{cpqDaPhyDrvBay},
+    cpqDaPhyDrvBusNumber => $params{cpqDaPhyDrvBusNumber},
+    cpqDaPhyDrvSize => $params{cpqDaPhyDrvSize},
+    cpqDaPhyDrvStatus => $params{cpqDaPhyDrvStatus},
+    cpqDaPhyDrvCondition => $params{cpqDaPhyDrvCondition},
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  $self->{name} = $params{name} ||
+      $self->{cpqDaPhyDrvCntlrIndex}.':'.$self->{cpqDaPhyDrvIndex}; ##vorerst
+  $self->{controllerindex} = $self->{cpqDaPhyDrvCntlrIndex};
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('dapd', $self->{name});
+  $self->add_info(
+      sprintf "physical drive %s is %s",
+          $self->{name}, $self->{cpqDaPhyDrvCondition});
+  if ($self->{cpqDaPhyDrvCondition} ne 'ok') {
+    $self->add_message(CRITICAL,
+        sprintf "physical drive %s is %s", 
+            $self->{name}, $self->{cpqDaPhyDrvCondition});
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[PHYSICAL_DRIVE]\n";
+  foreach (qw(cpqDaPhyDrvCntlrIndex cpqDaPhyDrvIndex cpqDaPhyDrvBay
+      cpqDaPhyDrvBusNumber cpqDaPhyDrvSize cpqDaPhyDrvStatus
+      cpqDaPhyDrvCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Da::SpareDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub dump {
+  my $self = shift;
+  printf "[LOGICAL_DRIVE]\n";
+  foreach (qw(cntrlcpqDaCntlrIndex cpqDaCntlrIndex size level status condition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+
+package HP::Proliant::Component::DiskSubsystem::Sas::CLI;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Sas::SNMP;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = { 
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+
+  # CPQSCSI-MIB
+  my $oids = {
+      cpqSasHbaEntry => "1.3.6.1.4.1.232.5.5.1.1.1",
+      cpqSasHbaIndex => "1.3.6.1.4.1.232.5.5.1.1.1.1",
+      cpqSasHbaLocation => "1.3.6.1.4.1.232.5.5.1.1.1.2",
+      cpqSasHbaSlot  => "1.3.6.1.4.1.232.5.5.1.1.1.6",
+      cpqSasHbaStatus  => "1.3.6.1.4.1.232.5.5.1.1.1.4",
+      cpqSasHbaStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+      },
+      cpqSasHbaCondition  => "1.3.6.1.4.1.232.5.5.1.1.1.5",
+      cpqSasHbaConditionValue => {
+          1 => "other",
+          2 => "ok", 
+          3 => "degraded", 
+          4 => "failed",
+      },
+  };
+
+  # INDEX { cpqSasHbaIndex } 
+  foreach ($self->get_entries($oids, 'cpqSasHbaEntry')) {
+    push(@{$self->{controllers}},
+        HP::Proliant::Component::DiskSubsystem::Sas::Controller->new(%{$_}));
+  }
+
+  $oids = {
+      cpqSasLogDrvEntry => "1.3.6.1.4.1.232.5.5.3.1.1",
+      cpqSasLogDrvHbaIndex => "1.3.6.1.4.1.232.5.5.3.1.1.1",
+      cpqSasLogDrvIndex => "1.3.6.1.4.1.232.5.5.3.1.1.2",
+      cpqSasLogDrvStatus => "1.3.6.1.4.1.232.5.5.3.1.1.4",
+      cpqSasLogDrvCondition => "1.3.6.1.4.1.232.5.5.3.1.1.5",
+      cpqSasLogDrvRebuildingPercent => "1.3.6.1.4.1.232.5.5.3.1.1.12",
+      cpqSasLogDrvRaidLevel => "1.3.6.1.4.1.232.5.5.3.1.1.3",
+      cpqSasLogDrvRaidLevelValue => {
+          1 => "other",
+          2 => "raid0",
+          3 => "raid1",
+          4 => "raid0plus1",
+          5 => "raid5",
+          6 => "raid15",
+          7 => "volume",
+      },
+      cpqSasLogDrvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqSasLogDrvStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "rebuilding",
+          5 => "failed",
+          6 => "offline",
+      }
+  };
+  # INDEX { cpqSasLogDrvCntlrIndex, cpqSasLogDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqSasLogDrvEntry')) {
+    push(@{$self->{logical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Sas::LogicalDrive->new(%{$_}));
+  }
+
+  $oids = {
+      cpqSasPhyDrvEntry => "1.3.6.1.4.1.232.5.5.2.1.1",
+      cpqSasPhyDrvHbaIndex => "1.3.6.1.4.1.232.5.5.2.1.1.1",
+      cpqSasPhyDrvIndex => "1.3.6.1.4.1.232.5.5.2.1.1.2",
+      cpqSasPhyDrvLocationString => "1.3.6.1.4.1.232.5.5.2.1.1.3",
+      cpqSasPhyDrvStatus => "1.3.6.1.4.1.232.5.5.2.1.1.5",
+      cpqSasPhyDrvSize => "1.3.6.1.4.1.232.5.5.2.1.1.8",
+      cpqSasPhyDrvCondition => "1.3.6.1.4.1.232.5.5.2.1.1.6",
+      cpqSasPhyDrvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqSasPhyDrvStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "predictiveFailure",
+          4 => "offline",
+          5 => "failed",
+          6 => "missingWasOk",
+          7 => "missingWasPredictiveFailure",
+          8 => "missingWasOffline",
+          9 => "missingWasFailed",
+      },
+  };
+    
+  # INDEX { cpqPhyLogDrvCntlrIndex, cpqSasPhyDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqSasPhyDrvEntry')) {
+    push(@{$self->{physical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Sas::PhysicalDrive->new(%{$_}));
+  }
+
+}
+package HP::Proliant::Component::DiskSubsystem::Sas;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    controllers => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    condition => undef,
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Sas::SNMP';
+  } else {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Sas::CLI';
+  }
+  $self->init();
+  $self->assemble();
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->check();
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->dump();
+  }
+}
+
+package HP::Proliant::Component::DiskSubsystem::Sas::Controller;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqSasHbaIndex => $params{cpqSasHbaIndex},
+    cpqSasHbaLocation => $params{cpqSasHbaLocation},
+    cpqSasHbaSlot => $params{cpqSasHbaSlot},
+    cpqSasHbaStatus => $params{cpqSasHbaStatus},
+    cpqSasHbaCondition => $params{cpqSasHbaCondition},
+    blacklisted => 0,
+  };
+  $self->{name} = $params{name} || $self->{cpqSasHbaSlot};
+  $self->{controllerindex} = $self->{cpqSasHbaIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('saco', $self->{cpqSasHbaSlot});
+  if ($self->{cpqSasHbaCondition} eq 'other') {
+    if (scalar(@{$self->{physical_drives}})) {
+      $self->add_message(CRITICAL,
+          sprintf 'sas controller in slot %s needs attention',
+              $self->{cpqSasHbaSlot});
+      $self->add_info(sprintf 'sas controller in slot %s needs attention',
+          $self->{cpqSasHbaSlot});
+    } else {
+      $self->add_info(sprintf 'sas controller in slot %s is ok and unused',
+          $self->{cpqSasHbaSlot});
+      $self->{blacklisted} = 1;
+    }
+  } elsif ($self->{cpqSasHbaCondition} ne 'ok') {
+    $self->add_message(CRITICAL,
+        sprintf 'sas controller in slot %s needs attention',
+            $self->{cpqSasHbaSlot});
+    $self->add_info(sprintf 'sas controller in slot %s needs attention',
+        $self->{cpqSasHbaSlot});
+  } else {
+    $self->add_info(sprintf 'sas controller in slot %s is ok',
+        $self->{cpqSasHbaSlot});
+  }
+  foreach (@{$self->{logical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{physical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{spare_drives}}) {
+    $_->check();
+  } 
+} 
+
+sub dump {
+  my $self = shift;
+  printf "[SAS_HBA%s]\n", $self->{name};
+  foreach (qw(cpqSasHbaSlot cpqSasHbaIndex cpqSasHbaCondition
+      cpqSasHbaStatus cpqSasHbaLocation)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+  foreach (@{$self->{logical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{physical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{spare_drives}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Sas::LogicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqSasLogDrvHbaIndex => $params{cpqSasLogDrvHbaIndex},
+    cpqSasLogDrvIndex => $params{cpqSasLogDrvIndex},
+    cpqSasLogDrvStatus => $params{cpqSasLogDrvStatus},
+    cpqSasLogDrvCondition => $params{cpqSasLogDrvCondition},
+    cpqSasLogDrvRebuildingPercent => $params{cpqSasLogDrvRebuildingPercent},
+    cpqSasLogDrvRaidLevel => $params{cpqSasLogDrvRaidLevel},
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  $self->{name} = $params{name} || 
+      $self->{cpqSasLogDrvHbaIndex}.':'.$self->{cpqSasLogDrvIndex}; ####vorerst
+  $self->{controllerindex} = $self->{cpqSasLogDrvHbaIndex};
+  if (! $self->{cpqSasLogDrvRebuildingPercent} ||
+      $self->{cpqSasLogDrvRebuildingPercent} == 4294967295) {
+    $self->{cpqSasLogDrvRebuildingPercent} = 100;
+  }
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('sald', $self->{name});
+  if ($self->{cpqSasLogDrvCondition} ne "ok") {
+    if ($self->{cpqSasLogDrvStatus} =~ 
+        /rebuild|recovering|expanding|queued/) {
+      $self->add_message(WARNING,
+          sprintf "logical drive %s is %s", 
+              $self->{name}, $self->{cpqSasLogDrvStatus});
+    } else {
+      $self->add_message(CRITICAL,
+          sprintf "logical drive %s is %s",
+              $self->{name}, $self->{cpqSasLogDrvStatus});
+    }
+  } 
+  $self->add_info(
+      sprintf "logical drive %s is %s (%s)", $self->{name},
+          $self->{cpqSasLogDrvStatus}, $self->{cpqSasLogDrvRaidLevel});
+}
+
+sub dump {
+  my $self = shift;
+  printf "[LOGICAL_DRIVE]\n";
+  foreach (qw(cpqSasLogDrvHbaIndex cpqSasLogDrvIndex cpqSasLogDrvRaidLevel
+      cpqSasLogDrvStatus cpqSasLogDrvCondition
+      cpqSasLogDrvRebuildingPercent)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Sas::PhysicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqSasPhyDrvHbaIndex => $params{cpqSasPhyDrvHbaIndex},
+    cpqSasPhyDrvIndex => $params{cpqSasPhyDrvIndex},
+    cpqSasPhyDrvLocationString => $params{cpqSasPhyDrvLocationString},
+    cpqSasPhyDrvStatus => $params{cpqSasPhyDrvStatus},
+    cpqSasPhyDrvSize => $params{cpqSasPhyDrvSize},
+    cpqSasPhyDrvCondition => $params{cpqSasPhyDrvCondition},
+    blacklisted => 0,
+  };
+  $self->{name} = $params{name} || 
+      $self->{cpqSasPhyDrvHbaIndex}.':'.$self->{cpqSasPhyDrvIndex}; ####vorerst
+  $self->{controllerindex} = $self->{cpqSasPhyDrvHbaIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('sapd', $self->{name});
+  if ($self->{cpqSasPhyDrvCondition} ne 'ok') {
+    $self->add_message(CRITICAL,
+        sprintf "physical drive %s is %s", 
+            $self->{name}, $self->{cpqSasPhyDrvCondition});
+  }
+  $self->add_info(
+      sprintf "physical drive %s is %s", 
+          $self->{name}, $self->{cpqSasPhyDrvCondition});
+}
+
+sub dump {
+  my $self = shift;
+  printf "[PHYSICAL_DRIVE]\n";
+  foreach (qw(cpqSasPhyDrvHbaIndex cpqSasPhyDrvIndex cpqSasPhyDrvLocationString
+      cpqSasPhyDrvSize cpqSasPhyDrvStatus cpqSasPhyDrvCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Sas::SpareDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub dump {
+  my $self = shift;
+  printf "[SPARE_DRIVE]\n";
+}
+
+
+
+package HP::Proliant::Component::DiskSubsystem::Scsi::CLI;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Scsi::SNMP;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = { 
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+
+  # CPQSCSI-MIB
+  my $oids = {
+      cpqScsiCntlrEntry => '1.3.6.1.4.1.232.5.2.2.1.1',
+      cpqScsiCntlrIndex => '1.3.6.1.4.1.232.5.2.2.1.1.1',
+      cpqScsiCntlrBusIndex => '1.3.6.1.4.1.232.5.2.2.1.1.2',
+      cpqScsiCntlrSlot => '1.3.6.1.4.1.232.5.2.2.1.1.6',
+      cpqScsiCntlrStatus => '1.3.6.1.4.1.232.5.2.2.1.1.7',
+      cpqScsiCntlrCondition => '1.3.6.1.4.1.232.5.2.2.1.1.12',
+      cpqScsiCntlrHwLocation => '1.3.6.1.4.1.232.5.2.2.1.1.16',
+      cpqScsiCntlrStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+      },
+      cpqScsiCntlrConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      }
+  };
+
+  # INDEX { cpqScsiCntlrIndex, cpqScsiCntlrBusIndex }
+  foreach ($self->get_entries($oids, 'cpqScsiCntlrEntry')) {
+    push(@{$self->{controllers}},
+        HP::Proliant::Component::DiskSubsystem::Scsi::Controller->new(%{$_}));
+  }
+
+  $oids = {
+      cpqScsiLogDrvEntry => '1.3.6.1.4.1.232.5.2.3.1.1',
+      cpqScsiLogDrvCntlrIndex => '1.3.6.1.4.1.232.5.2.3.1.1.1',
+      cpqScsiLogDrvBusIndex => '1.3.6.1.4.1.232.5.2.3.1.1.2',
+      cpqScsiLogDrvIndex => '1.3.6.1.4.1.232.5.2.3.1.1.3',
+      cpqScsiLogDrvFaultTol => '1.3.6.1.4.1.232.5.2.3.1.1.4',
+      cpqScsiLogDrvStatus => '1.3.6.1.4.1.232.5.2.3.1.1.5',
+      cpqScsiLogDrvSize => '1.3.6.1.4.1.232.5.2.3.1.1.6',
+      cpqScsiLogDrvPhyDrvIDs => '1.3.6.1.4.1.232.5.2.3.1.1.7',
+      cpqScsiLogDrvCondition => '1.3.6.1.4.1.232.5.2.3.1.1.8',
+      cpqScsiLogDrvStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+          4 => "unconfigured",
+          5 => "recovering",
+          6 => "readyForRebuild",
+          7 => "rebuilding",
+          8 => "wrongDrive",
+          9 => "badConnect",
+      },
+      cpqScsiLogDrvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqScsiLogDrvFaultTolValue => {
+          1 => "other",
+          2 => "none",
+          3 => "mirroring",
+          4 => "dataGuard",
+          5 => "distribDataGuard",
+      },
+
+  };
+  # INDEX { cpqScsiLogDrvCntlrIndex, cpqScsiLogDrvBusIndex, cpqScsiLogDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqScsiLogDrvEntry')) {
+    push(@{$self->{logical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Scsi::LogicalDrive->new(%{$_}));
+  }
+
+  $oids = {
+      cpqScsiPhyDrvEntry => '1.3.6.1.4.1.232.5.2.4.1.1',
+      cpqScsiPhyDrvCntlrIndex => '1.3.6.1.4.1.232.5.2.4.1.1.1',
+      cpqScsiPhyDrvBusIndex => '1.3.6.1.4.1.232.5.2.4.1.1.2',
+      cpqScsiPhyDrvIndex => '1.3.6.1.4.1.232.5.2.4.1.1.3',
+      cpqScsiPhyDrvStatus => '1.3.6.1.4.1.232.5.2.4.1.1.9',
+      cpqScsiPhyDrvSize => '1.3.6.1.4.1.232.5.2.4.1.1.7',
+      cpqScsiPhyDrvCondition => '1.3.6.1.4.1.232.5.2.4.1.1.26',
+      cpqScsiPhyDrvConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqScsiPhyDrvStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+          4 => "notConfigured",
+          5 => "badCable",
+          6 => "missingWasOk",
+          7 => "missingWasFailed",
+          8 => "predictiveFailure",
+          9 => "missingWasPredictiveFailure",
+          10 => "offline",
+          11 => "missingWasOffline",
+          12 => "hardError",
+      },
+  };
+    
+  # INDEX { cpqScsiPhyDrvCntlrIndex, cpqScsiPhyDrvBusIndex, cpqScsiPhyDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqScsiPhyDrvEntry')) {
+    push(@{$self->{physical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Scsi::PhysicalDrive->new(%{$_}));
+  }
+
+}
+package HP::Proliant::Component::DiskSubsystem::Scsi;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    controllers => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    condition => undef,
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Scsi::SNMP';
+  } else {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Scsi::CLI';
+  }
+  $self->init();
+  $self->assemble();
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->check();
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->dump();
+  }
+}
+
+package HP::Proliant::Component::DiskSubsystem::Scsi::Controller;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqScsiCntlr/, keys %params;
+  $self->{name} = $params{name} || $params{cpqScsiCntlrIndex}.':'.$params{cpqScsiCntlrBusIndex};
+  $self->{controllerindex} = $self->{cpqScsiCntlrIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('scco', $self->{name});
+  my $info = sprintf 'scsi controller %s in slot %s is %s',
+      $self->{name}, $self->{cpqScsiCntlrSlot}, $self->{cpqScsiCntlrCondition};
+  if ($self->{cpqScsiCntlrCondition} eq 'other') {
+    if (scalar(@{$self->{physical_drives}})) {
+      $info .= ' and needs attention';
+      $self->add_message(CRITICAL, $info);
+      $self->add_info($info);
+    } else {
+      $info .= ' and unused';
+      $self->add_info($info);
+      $self->{blacklisted} = 1;
+    }
+  } elsif ($self->{cpqScsiCntlrCondition} ne 'ok') {
+    $info .= ' and needs attention';
+    $self->add_message(CRITICAL, $info);
+    $self->add_info($info);
+  } else {
+    $self->add_info($info);
+  }
+  foreach (@{$self->{logical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{physical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{spare_drives}}) {
+    $_->check();
+  } 
+} 
+
+sub dump {
+  my $self = shift;
+  printf "[SCSI_CONTROLLER_%s]\n", $self->{name};
+  foreach (qw(cpqScsiCntlrIndex cpqScsiCntlrBusIndex cpqScsiCntlrSlot
+      cpqScsiCntlrStatus cpqScsiCntlrCondition cpqScsiCntlrHwLocation)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+  foreach (@{$self->{logical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{physical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{spare_drives}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Scsi::LogicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqScsiLogDrv/, keys %params;
+  $self->{name} = $params{name} || $params{cpqScsiLogDrvCntlrIndex}.':'.$params{cpqScsiLogDrvBusIndex}.':'.$params{cpqScsiLogDrvIndex};
+  bless $self, $class;
+  $self->{controllerindex} = $self->{cpqScsiLogDrvCntlrIndex};
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('scld', $self->{name});
+  my $info = sprintf 'logical drive %s is %s', $self->{name}, $self->{cpqScsiLogDrvStatus};
+  if ($self->{cpqScsiLogDrvCondition} ne "ok") {
+    if ($self->{cpqScsiLogDrvStatus} =~ 
+        /rebuild|recovering/) {
+      $self->add_message(WARNING, $info);
+    } else {
+      $self->add_message(CRITICAL, $info);
+    }
+  } 
+  $self->add_info($info);
+}
+
+sub dump {
+  my $self = shift;
+  printf "[LOGICAL_DRIVE_%s]\n", $self->{name};
+  foreach (qw(cpqScsiLogDrvCntlrIndex cpqScsiLogDrvBusIndex cpqScsiLogDrvIndex
+      cpqScsiLogDrvFaultTol cpqScsiLogDrvStatus cpqScsiLogDrvSize 
+      cpqScsiLogDrvPhyDrvIDs cpqScsiLogDrvCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Scsi::PhysicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqScsiPhyDrv/, keys %params;
+  $self->{name} = $params{name} || 
+      $self->{cpqScsiPhyDrvCntlrIndex}.':'.$self->{cpqScsiPhyDrvBusIndex}.':'.$self->{cpqScsiPhyDrvIndex}; 
+  $self->{controllerindex} = $self->{cpqScsiPhyDrvCntlrIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('scpd', $self->{name});
+  my $info = sprintf 'physical drive %s is %s', $self->{name}, $self->{cpqScsiPhyDrvCondition};
+  if ($self->{cpqScsiPhyDrvCondition} ne 'ok') {
+    $self->add_message(CRITICAL, $info);
+  }
+  $self->add_info($info);
+}
+
+sub dump {
+  my $self = shift;
+  printf "[PHYSICAL_DRIVE_%s]\n", $self->{name};
+  foreach (qw(cpqScsiPhyDrvCntlrIndex cpqScsiPhyDrvBusIndex cpqScsiPhyDrvIndex
+      cpqScsiPhyDrvStatus cpqScsiPhyDrvSize cpqScsiPhyDrvCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Scsi::SpareDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub dump {
+  my $self = shift;
+  printf "[SPARE_DRIVE]\n";
+}
+
+
+
+package HP::Proliant::Component::DiskSubsystem::Ide::CLI;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Ide::SNMP;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = { 
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+
+  # CPQIDE-MIB
+  my $oids = {
+      cpqIdeControllerEntry => '1.3.6.1.4.1.232.14.2.3.1.1',
+      cpqIdeControllerIndex => '1.3.6.1.4.1.232.14.2.3.1.1.1',
+      cpqIdeControllerOverallCondition => '1.3.6.1.4.1.232.14.2.3.1.1.2',
+      cpqIdeControllerModel => '1.3.6.1.4.1.232.14.2.3.1.1.3',
+      cpqIdeControllerSlot => '1.3.6.1.4.1.232.14.2.3.1.1.5',
+      cpqIdeControllerOverallConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+  };
+
+  # INDEX { cpqIdeControllerIndex }
+  foreach ($self->get_entries($oids, 'cpqIdeControllerEntry')) {
+    push(@{$self->{controllers}},
+        HP::Proliant::Component::DiskSubsystem::Ide::Controller->new(%{$_}));
+  }
+
+  $oids = {
+      cpqIdeLogicalDriveEntry => '1.3.6.1.4.1.232.14.2.6.1.1',
+      cpqIdeLogicalDriveControllerIndex => '1.3.6.1.4.1.232.14.2.6.1.1.1',
+      cpqIdeLogicalDriveIndex => '1.3.6.1.4.1.232.14.2.6.1.1.2',
+      cpqIdeLogicalDriveRaidLevel => '1.3.6.1.4.1.232.14.2.6.1.1.3',
+      cpqIdeLogicalDriveCapacity => '1.3.6.1.4.1.232.14.2.6.1.1.4',
+      cpqIdeLogicalDriveStatus => '1.3.6.1.4.1.232.14.2.6.1.1.5',
+      cpqIdeLogicalDriveCondition => '1.3.6.1.4.1.232.14.2.6.1.1.6',
+      cpqIdeLogicalDriveDiskIds => '1.3.6.1.4.1.232.14.2.6.1.1.7',
+      cpqIdeLogicalDriveSpareIds => '1.3.6.1.4.1.232.14.2.6.1.1.9',
+      cpqIdeLogicalDriveRebuildingDisk => '1.3.6.1.4.1.232.14.2.6.1.1.10',
+      cpqIdeLogicalDriveRaidLevelValue => {
+          1 => "other",
+          2 => "raid0",
+          3 => "raid1",
+          4 => "raid0plus1",
+      },
+      cpqIdeLogicalDriveStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "rebuilding",
+          5 => "failed",
+      },
+      cpqIdeLogicalDriveConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+  };
+  # INDEX { cpqIdeLogicalDriveControllerIndex, cpqIdeLogicalDriveIndex }
+  foreach ($self->get_entries($oids, 'cpqIdeLogicalDriveEntry')) {
+    push(@{$self->{logical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Ide::LogicalDrive->new(%{$_}));
+  }
+
+  $oids = {
+      cpqIdeAtaDiskEntry => '1.3.6.1.4.1.232.14.2.4.1.1',
+      cpqIdeAtaDiskControllerIndex => '1.3.6.1.4.1.232.14.2.4.1.1.1',
+      cpqIdeAtaDiskIndex => '1.3.6.1.4.1.232.14.2.4.1.1.2',
+      cpqIdeAtaDiskModel => '1.3.6.1.4.1.232.14.2.4.1.1.3',
+      cpqIdeAtaDiskStatus => '1.3.6.1.4.1.232.14.2.4.1.1.6',
+      cpqIdeAtaDiskCondition => '1.3.6.1.4.1.232.14.2.4.1.1.7',
+      cpqIdeAtaDiskCapacity => '1.3.6.1.4.1.232.14.2.4.1.1.8',
+      cpqIdeAtaDiskLogicalDriveMember => '1.3.6.1.4.1.232.14.2.4.1.1.13',
+      cpqIdeAtaDiskIsSpare => '1.3.6.1.4.1.232.14.2.4.1.1.14',
+      cpqIdeAtaDiskStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "smartError",
+          4 => "failed",
+      },
+      cpqIdeAtaDiskConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+  };
+  # INDEX { cpqIdeAtaDiskControllerIndex, cpqIdeAtaDiskIndex }
+  foreach ($self->get_entries($oids, 'cpqIdeAtaDiskEntry')) {
+    push(@{$self->{physical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Ide::PhysicalDrive->new(%{$_}));
+  }
+
+}
+package HP::Proliant::Component::DiskSubsystem::Ide;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    controllers => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    condition => undef,
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Ide::SNMP';
+  } else {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Ide::CLI';
+  }
+  $self->init();
+  $self->assemble();
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->check();
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{controllers}}) {
+    $_->dump();
+  }
+}
+
+package HP::Proliant::Component::DiskSubsystem::Ide::Controller;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqIdeControllerIndex => $params{cpqIdeControllerIndex},
+    cpqIdeControllerOverallCondition => $params{cpqIdeControllerOverallCondition},
+    cpqIdeControllerModel => $params{cpqIdeControllerModel},
+    cpqIdeControllerSlot => $params{cpqIdeControllerSlot}, # -1 ist sysboard?
+    blacklisted => 0,
+  };
+  $self->{name} = $params{name} || $self->{cpqIdeControllerIndex};
+  $self->{controllerindex} = $self->{cpqIdeControllerIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  if ($self->{cpqIdeControllerOverallCondition} eq 'other') {
+    if (scalar(@{$self->{physical_drives}})) {
+      $self->add_message(CRITICAL,
+          sprintf 'ide controller %s in slot %s needs attention',
+              $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
+      $self->add_info(sprintf 'ide controller %s in slot %s needs attention',
+          $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
+    } else {
+      $self->add_info(sprintf 'ide controller %s in slot %s is ok and unused',
+          $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
+      $self->{blacklisted} = 1;
+    }
+  } elsif ($self->{cpqIdeControllerOverallCondition} ne 'ok') {
+    $self->add_message(CRITICAL,
+        sprintf 'ide controller %s in slot %s needs attention',
+            $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
+    $self->add_info(sprintf 'ide controller %s in slot %s needs attention',
+        $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
+  } else {
+    $self->add_info(sprintf 'ide controller %s in slot %s is ok',
+        $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
+  }
+  foreach (@{$self->{logical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{physical_drives}}) {
+    $_->check();
+  } 
+  foreach (@{$self->{spare_drives}}) {
+    $_->check();
+  } 
+} 
+
+sub dump {
+  my $self = shift;
+  printf "[IDE_CONTROLLER_%s]\n", $self->{name};
+  foreach (qw(cpqIdeControllerIndex cpqIdeControllerSlot
+      cpqIdeControllerModel cpqIdeControllerOverallCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+  foreach (@{$self->{logical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{physical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{spare_drives}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Ide::LogicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqIdeLogicalDriveControllerIndex => $params{cpqIdeLogicalDriveControllerIndex},
+    cpqIdeLogicalDriveIndex => $params{cpqIdeLogicalDriveIndex},
+    cpqIdeLogicalDriveRaidLevel => $params{cpqIdeLogicalDriveRaidLevel},
+    cpqIdeLogicalDriveCapacity => $params{cpqIdeLogicalDriveCapacity},
+    cpqIdeLogicalDriveStatus => $params{cpqIdeLogicalDriveStatus},
+    cpqIdeLogicalDriveCondition => $params{cpqIdeLogicalDriveCondition},
+    cpqIdeLogicalDriveDiskIds => $params{cpqIdeLogicalDriveDiskIds},
+    cpqIdeLogicalDriveSpareIds => $params{cpqIdeLogicalDriveSpareIds},
+    cpqIdeLogicalDriveRebuildingDisk => $params{cpqIdeLogicalDriveRebuildingDisk},
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  $self->{name} = $params{name} || 
+      $self->{cpqIdeLogicalDriveControllerIndex}.':'.
+      $self->{cpqIdeLogicalDriveIndex};
+  $self->{controllerindex} = $self->{cpqIdeLogicalDriveControllerIndex};
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  if ($self->{cpqIdeLogicalDriveCondition} ne "ok") {
+    if ($self->{cpqIdeLogicalDriveStatus} =~ 
+        /rebuild/) {
+      $self->add_message(WARNING,
+          sprintf "logical drive %s is %s", 
+              $self->{name}, $self->{cpqIdeLogicalDriveStatus});
+    } else {
+      $self->add_message(CRITICAL,
+          sprintf "logical drive %s is %s",
+              $self->{name}, $self->{cpqIdeLogicalDriveStatus});
+    }
+  } 
+  $self->add_info(
+      sprintf "logical drive %s is %s", $self->{name},
+          $self->{cpqIdeLogicalDriveStatus});
+}
+
+sub dump {
+  my $self = shift;
+  printf "[LOGICAL_DRIVE]\n";
+  foreach (qw(cpqIdeLogicalDriveControllerIndex cpqIdeLogicalDriveIndex
+      cpqIdeLogicalDriveRaidLevel cpqIdeLogicalDriveCapacity 
+      cpqIdeLogicalDriveDiskIds cpqIdeLogicalDriveSpareIds 
+      cpqIdeLogicalDriveRebuildingDisk cpqIdeLogicalDriveStatus 
+      cpqIdeLogicalDriveCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Ide::PhysicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqIdeAtaDiskControllerIndex => $params{cpqIdeAtaDiskControllerIndex},
+    cpqIdeAtaDiskIndex => $params{cpqIdeAtaDiskIndex},
+    cpqIdeAtaDiskModel => $params{cpqIdeAtaDiskModel},
+    cpqIdeAtaDiskStatus => $params{cpqIdeAtaDiskStatus},
+    cpqIdeAtaDiskCondition => $params{cpqIdeAtaDiskCondition},
+    cpqIdeAtaDiskCapacity => $params{cpqIdeAtaDiskCapacity},
+    cpqIdeAtaDiskLogicalDriveMember => $params{cpqIdeAtaDiskLogicalDriveMember},
+    cpqIdeAtaDiskIsSpare => $params{cpqIdeAtaDiskIsSpare},
+    blacklisted => 0,
+  };
+  $self->{name} = $params{name} || 
+      $self->{cpqIdeAtaDiskControllerIndex}.':'.
+      $self->{cpqIdeAtaDiskIndex}; ####vorerst
+  $self->{controllerindex} = $self->{cpqIdeAtaDiskControllerIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  if ($self->{cpqIdeAtaDiskCondition} ne 'ok') {
+    $self->add_message(CRITICAL,
+        sprintf "physical drive %s is %s", 
+            $self->{name}, $self->{cpqIdeAtaDiskCondition});
+  }
+  $self->add_info(
+      sprintf "physical drive %s is %s", 
+          $self->{name}, $self->{cpqIdeAtaDiskCondition});
+}
+
+sub dump {
+  my $self = shift;
+  printf "[PHYSICAL_DRIVE]\n";
+  foreach (qw(cpqIdeAtaDiskControllerIndex cpqIdeAtaDiskIndex
+      cpqIdeAtaDiskModel cpqIdeAtaDiskCapacity cpqIdeAtaDiskLogicalDriveMember 
+      cpqIdeAtaDiskStatus cpqIdeAtaDiskCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Ide::SpareDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub dump {
+  my $self = shift;
+  printf "[SPARE_DRIVE]\n";
+}
+
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::CLI;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  # .....
+  $self->{global_status} =
+      HP::Proliant::Component::DiskSubsystem::Fca::GlobalStatus->new(
+          runtime => $self->{runtime},
+          cpqFcaMibCondition => 'n/a',
+      );
+
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::SNMP;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca
+    HP::Proliant::Component::SNMP);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = { 
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+
+  # CPQFCA-MIB
+  my $oids = {
+      cpqFcaHostCntlrEntry => '1.3.6.1.4.1.232.16.2.7.1.1',
+      cpqFcaHostCntlrIndex => '1.3.6.1.4.1.232.16.2.7.1.1.1',
+      cpqFcaHostCntlrSlot => '1.3.6.1.4.1.232.16.2.7.1.1.2',
+      cpqFcaHostCntlrModel => '1.3.6.1.4.1.232.16.2.7.1.1.3',
+      cpqFcaHostCntlrStatus => '1.3.6.1.4.1.232.16.2.7.1.1.4',
+      cpqFcaHostCntlrCondition => '1.3.6.1.4.1.232.16.2.7.1.1.5',
+      cpqFcaHostCntlrOverallCondition => '1.3.6.1.4.1.232.16.2.7.1.1.8',
+      cpqFcaHostCntlrModelValue => {
+          1 => "other",
+          2 => "fchc-p",
+          3 => "fchc-e",
+          4 => "fchc64",
+          5 => "sa-sam",
+          6 => "fca-2101",
+          7 => "sw64-33",
+          8 => "fca-221x",
+          9 => "dpfcmc",
+      },
+      cpqFcaHostCntlrStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+          4 => "shutdown",
+          5 => "loopDegraded",
+          6 => "loopFailed",
+      },
+      cpqFcaHostCntlrConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+      cpqFcaHostCntlrOverallConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      }, # cntrl + alle associated storage boxes
+  };
+
+  # INDEX { cpqFcaHostCntlrIndex }
+  foreach ($self->get_entries($oids, 'cpqFcaHostCntlrEntry')) {
+    push(@{$self->{host_controllers}},
+        HP::Proliant::Component::DiskSubsystem::Fca::HostController->new(%{$_}));
+  }
+
+  $oids = {
+      cpqFcaCntlrEntry => '1.3.6.1.4.1.232.16.2.2.1.1',
+      cpqFcaCntlrBoxIndex => '1.3.6.1.4.1.232.16.2.2.1.1.1',
+      cpqFcaCntlrBoxIoSlot => '1.3.6.1.4.1.232.16.2.2.1.1.2',
+      cpqFcaCntlrModel => '1.3.6.1.4.1.232.16.2.2.1.1.3',
+      cpqFcaCntlrStatus => '1.3.6.1.4.1.232.16.2.2.1.1.5',
+      cpqFcaCntlrCondition => '1.3.6.1.4.1.232.16.2.2.1.1.6',
+      cpqFcaCntlrModelValue => {
+        1 => "other",
+        2 => "fibreArray",
+        3 => "msa1000",
+        4 => "smartArrayClusterStorage",
+        5 => "hsg80",
+        6 => "hsv110", 
+        7 => "msa500g2", 
+        8 => "msa20",
+      },
+      cpqFcaCntlrStatusValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "failed",
+          4 => "offline",
+          4 => "redundantPathOffline",
+      },
+      cpqFcaCntlrConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+  };
+
+  # INDEX { cpqFcaCntlrBoxIndex, cpqFcaCntlrBoxIoSlot }
+  foreach ($self->get_entries($oids, 'cpqFcaCntlrEntry')) {
+    push(@{$self->{controllers}},
+        HP::Proliant::Component::DiskSubsystem::Fca::Controller->new(%{$_}));
+  }
+
+  $oids = {
+      cpqFcaAccelEntry => '1.3.6.1.4.1.232.16.2.2.2.1',
+      cpqFcaAccelBoxIndex => '1.3.6.1.4.1.232.16.2.2.2.1.1',
+      cpqFcaAccelBoxIoSlot => '1.3.6.1.4.1.232.16.2.2.2.1.2',
+      cpqFcaAccelStatus => '1.3.6.1.4.1.232.16.2.2.2.1.3',
+      cpqFcaAccelErrCode => '1.3.6.1.4.1.232.16.2.2.2.1.5',
+      cpqFcaAccelBatteryStatus => '1.3.6.1.4.1.232.16.2.2.2.1.6',
+      cpqFcaAccelCondition => '1.3.6.1.4.1.232.16.2.2.2.1.9',
+      cpqFcaAccelStatusValue => {
+          1 => "other",
+          2 => "invalid",
+          3 => "enabled",
+          4 => "tmpDisabled",
+          5 => "permDisabled",
+      },
+      cpqFcaAccelErrCodeValue => {
+          1 => 'other',
+          2 => 'invalid',
+          3 => 'badConfig',
+          4 => 'lowBattery',
+          5 => 'disableCmd',
+          6 => 'noResources',
+          7 => 'notConnected',
+          8 => 'badMirrorData',
+          9 => 'readErr',
+          10 => 'writeErr',
+          11 => 'configCmd',
+          12 => 'expandInProgress',
+          13 => 'snapshotInProgress',
+          14 => 'redundantLowBattery',
+          15 => 'redundantSizeMismatch',
+          16 => 'redundantCacheFailure',
+          17 => 'excessiveEccErrors',
+          19 => 'postEccErrors',
+      },
+      cpqFcaAccelBatteryStatusValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'recharging',
+          4 => 'failed',
+          5 => 'degraded',
+          6 => 'notPresent',
+      },
+      cpqFcaAccelConditionValue => {
+          1 => "other",
+          2 => "ok",
+          3 => "degraded",
+          4 => "failed",
+      },
+  };
+
+  # INDEX { cpqFcaAccelBoxIndex, cpqFcaAccelBoxIoSlot }
+  foreach ($self->get_entries($oids, 'cpqFcaAccelEntry')) {
+    push(@{$self->{accelerators}},
+        HP::Proliant::Component::DiskSubsystem::Fca::Accelerator->new(%{$_}));
+  }
+
+  $oids = {
+      cpqFcaLogDrvEntry => '1.3.6.1.4.1.232.16.2.3.1.1',
+      cpqFcaLogDrvBoxIndex => '1.3.6.1.4.1.232.16.2.3.1.1.1',
+      cpqFcaLogDrvIndex => '1.3.6.1.4.1.232.16.2.3.1.1.2',
+      cpqFcaLogDrvFaultTol => '1.3.6.1.4.1.232.16.2.3.1.1.3',
+      cpqFcaLogDrvStatus => '1.3.6.1.4.1.232.16.2.3.1.1.4',
+      cpqFcaLogDrvPercentRebuild => '1.3.6.1.4.1.232.16.2.3.1.1.6',
+      cpqFcaLogDrvSize => '1.3.6.1.4.1.232.16.2.3.1.1.9',
+      cpqFcaLogDrvPhyDrvIDs => '1.3.6.1.4.1.232.16.2.3.1.1.10',
+      cpqFcaLogDrvCondition => '1.3.6.1.4.1.232.16.2.3.1.1.11',
+      cpqFcaLogDrvFaultTolValue => {
+          1 => 'other',
+          2 => 'none',
+          3 => 'mirroring',
+          4 => 'dataGuard',
+          5 => 'distribDataGuard',
+          7 => 'advancedDataGuard',
+      },
+      cpqFcaLogDrvStatusValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'failed',
+          4 => 'unconfigured',
+          5 => 'recovering',
+          6 => 'readyForRebuild',
+          7 => 'rebuilding',
+          8 => 'wrongDrive',
+          9 => 'badConnect',
+          10 => 'overheating',
+          11 => 'shutdown',
+          12 => 'expanding',
+          13 => 'notAvailable',
+          14 => 'queuedForExpansion',
+          15 => 'hardError',
+      },
+      cpqFcaLogDrvConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+  };
+
+  # INDEX { cpqFcaLogDrvBoxIndex, cpqFcaLogDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqFcaLogDrvEntry')) {
+    push(@{$self->{logical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Fca::LogicalDrive->new(%{$_}));
+  }
+
+  $oids = {
+      cpqFcaPhyDrvEntry => '1.3.6.1.4.1.232.16.2.5.1.1',
+      cpqFcaPhyDrvBoxIndex => '1.3.6.1.4.1.232.16.2.5.1.1.1',
+      cpqFcaPhyDrvIndex => '1.3.6.1.4.1.232.16.2.5.1.1.2',
+      cpqFcaPhyDrvModel => '1.3.6.1.4.1.232.16.2.5.1.1.3',
+      cpqFcaPhyDrvBay => '1.3.6.1.4.1.232.16.2.5.1.1.5',
+      cpqFcaPhyDrvStatus => '1.3.6.1.4.1.232.16.2.5.1.1.6',
+      cpqFcaPhyDrvCondition => '1.3.6.1.4.1.232.16.2.5.1.1.31',
+      cpqFcaPhyDrvSize => '1.3.6.1.4.1.232.16.2.5.1.1.38',
+      cpqFcaPhyDrvBusNumber => '1.3.6.1.4.1.232.16.2.5.1.1.42',
+      cpqFcaPhyDrvStatusValue => {
+          1 => 'other',
+          2 => 'unconfigured',
+          3 => 'ok',
+          4 => 'threshExceeded',
+          5 => 'predictiveFailure',
+          6 => 'failed',
+      },
+      cpqFcaPhyDrvConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+  };
+
+  # INDEX { cpqFcaPhyDrvBoxIndex, cpqFcaPhyDrvIndex }
+  foreach ($self->get_entries($oids, 'cpqFcaPhyDrvEntry')) {
+    push(@{$self->{physical_drives}},
+        HP::Proliant::Component::DiskSubsystem::Fca::PhysicalDrive->new(%{$_}));
+  }
+
+  $oids = {
+      cpqFcaMibRevMajor => '1.3.6.1.4.1.232.16.1.1.0',
+      cpqFcaMibRevMinor => '1.3.6.1.4.1.232.16.1.2.0',
+      cpqFcaMibCondition => '1.3.6.1.4.1.232.16.1.3.0',
+      cpqFcaMibConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+  };
+  $self->{global_status} =
+      HP::Proliant::Component::DiskSubsystem::Fca::GlobalStatus->new(
+          runtime => $self->{runtime},
+          cpqFcaMibCondition => 
+            SNMP::Utils::get_object_value($snmpwalk,
+                $oids->{cpqFcaMibCondition}, $oids->{cpqFcaMibConditionValue})
+      );
+}
+package HP::Proliant::Component::DiskSubsystem::Fca;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    host_controllers => [],
+    controllers => [],
+    accelerators => [],
+    physical_drives => [],
+    logical_drives => [],
+    spare_drives => [],
+    global_status => undef,
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  if ($self->{method} eq 'snmp') {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Fca::SNMP';
+  } else {
+    bless $self, 'HP::Proliant::Component::DiskSubsystem::Fca::CLI';
+  }
+  $self->init();
+  $self->assemble();
+  return $self;
+}
+
+sub assemble {
+  my $self = shift;
+  $self->trace(3, sprintf "%s controllers und platten zusammenführen",
+      ref($self));
+  $self->trace(3, sprintf "has %d host controllers", 
+      scalar(@{$self->{host_controllers}}));
+  $self->trace(3, sprintf "has %d controllers",
+      scalar(@{$self->{controllers}}));
+  $self->trace(3, sprintf "has %d physical_drives",
+      scalar(@{$self->{physical_drives}}));
+  $self->trace(3, sprintf "has %d logical_drives",
+      scalar(@{$self->{logical_drives}}));
+  $self->trace(3, sprintf "has %d spare_drives",
+      scalar(@{$self->{spare_drives}}));
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{host_controllers}}) {
+    $_->check();
+  }
+  foreach (@{$self->{controllers}}) {
+    $_->check();
+  }
+  foreach (@{$self->{accelerators}}) {
+    $_->check();
+  }
+  foreach (@{$self->{logical_drives}}) {
+    $_->check();
+  }
+  foreach (@{$self->{physical_drives}}) {
+    $_->check();
+  }
+  # wozu eigentlich?
+  #if (! $self->has_controllers()) {
+    #$self->{global_status}->check();
+  #}
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{host_controllers}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{controllers}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{accelerators}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{logical_drives}}) {
+    $_->dump();
+  }
+  foreach (@{$self->{physical_drives}}) {
+    $_->dump();
+  }
+  #$self->{global_status}->dump();
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::GlobalStatus;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    cpqFcaMibCondition => $params{cpqFcaMibCondition},
+  };
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  if ($self->{cpqFcaMibCondition} ne 'ok') {
+    $self->add_message(CRITICAL, 
+        sprintf 'fcal overall condition is %s', $self->{cpqFcaMibCondition});
+  }
+  $self->{info} = 
+      sprintf 'fcal overall condition is %s', $self->{cpqFcaMibCondition};
+}
+
+sub dump {
+  my $self = shift;
+  printf "[FCAL]\n";
+  foreach (qw(cpqFcaMibCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::HostController;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef, 
+  }; 
+  map { $self->{$_} = $params{$_} } grep /cpqFcaHostCntlr/, keys %params;
+  $self->{name} = $params{name} || $self->{cpqFcaHostCntlrIndex};
+  $self->{controllerindex} = $self->{cpqFcaHostCntlrIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('fcahc', $self->{name});
+  my $info = sprintf 'fcal host controller %s in slot %s is %s',
+      $self->{name}, $self->{cpqFcaHostCntlrSlot}, $self->{cpqFcaHostCntlrCondition};
+  if ($self->{cpqFcaHostCntlrCondition} eq 'other') {
+    $info .= sprintf ' and needs attention (%s)', $self->{cpqFcaHostCntlrStatus};
+    $self->add_message(CRITICAL, $info);
+    $self->add_info($info);
+  } elsif ($self->{cpqFcaHostCntlrCondition} ne 'ok') {
+    $self->add_message(CRITICAL, $info);
+    $self->add_info($info);
+  } else { 
+    $self->add_info($info);
+  }
+  $self->blacklist('fcahco', $self->{name});
+  $info = sprintf 'fcal host controller %s overall condition is %s',
+      $self->{name}, $self->{cpqFcaHostCntlrOverallCondition};
+  if ($self->{cpqFcaHostCntlrOverallCondition} ne 'ok') {
+    $self->add_message(WARNING, $info);
+  }
+  $self->add_info($info);
+}   
+
+sub dump { 
+  my $self = shift;
+  printf "[FCAL_HOST_CONTROLLER_%s]\n", $self->{name};
+  foreach (qw(cpqFcaHostCntlrIndex cpqFcaHostCntlrSlot
+      cpqFcaHostCntlrModel cpqFcaHostCntlrStatus cpqFcaHostCntlrCondition
+      cpqFcaHostCntlrOverallCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::Controller;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef, 
+  }; 
+  map { $self->{$_} = $params{$_} } grep /cpqFcaCntlr/, keys %params;
+  $self->{name} = $params{name} || 
+      $self->{cpqFcaCntlrBoxIndex}.':'.$self->{cpqFcaCntlrBoxIoSlot};
+  $self->{controllerindex} = $self->{cpqFcaCntlrBoxIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('fcaco', $self->{name});
+  my $info = sprintf 'fcal controller %s in box %s/slot %s is %s',
+      $self->{name}, $self->{cpqFcaCntlrBoxIndex}, $self->{cpqFcaCntlrBoxIoSlot},
+      $self->{cpqFcaCntlrCondition};
+  if ($self->{cpqFcaCntlrCondition} eq 'other') {
+    if (1) { # was ist mit phys. drives?
+      $info .= ' needs attention';
+      $info .= sprintf(' (%s)', $self->{cpqFcaCntlrStatus}) unless $self->{cpqFcaCntlrStatus} eq 'ok';
+      $self->add_message(CRITICAL, $info);
+      $self->add_info($info);
+    } else {
+      $self->add_info($info);
+      $self->{blacklisted} = 1;
+    }
+  } elsif ($self->{cpqFcaCntlrCondition} ne 'ok') {
+    $info .= ' needs attention';
+    $info .= sprintf(' (%s)', $self->{cpqFcaCntlrStatus}) unless $self->{cpqFcaCntlrStatus} eq 'ok';
+    $self->add_message(CRITICAL, $info);
+    $self->add_info($info);
+  } else {
+    $self->add_info($info);
+  }
+} 
+
+sub dump {
+  my $self = shift;
+  printf "[FCAL_CONTROLLER_%s]\n", $self->{name};
+  foreach (qw(cpqFcaCntlrBoxIndex cpqFcaCntlrBoxIoSlot cpqFcaCntlrModel
+      cpqFcaCntlrStatus cpqFcaCntlrCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::Accelerator;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef, 
+  }; 
+  map { $self->{$_} = $params{$_} } grep /cpqFcaAccel/, keys %params;
+  $self->{name} = $params{name} ||
+      $self->{cpqFcaAccelBoxIndex}.':'.$self->{cpqFcaAccelBoxIoSlot};
+  $self->{controllerindex} = $self->{cpqFcaAccelBoxIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  # !!! cpqFcaAccelStatus
+  $self->blacklist('fcaac', $self->{name});
+  my $info = sprintf 'fcal accelerator %s in box %s/slot %s is ',
+      $self->{name}, $self->{cpqFcaAccelBoxIndex}, $self->{cpqFcaAccelBoxIoSlot};
+  if ($self->{cpqFcaAccelStatus} eq 'invalid') {
+    $info .= 'not installed';
+    $self->add_info($info);
+  } elsif ($self->{cpqFcaAccelStatus} eq 'tmpDisabled') {
+    $info .= 'temp disabled';
+    $self->add_info($info);
+  } elsif ($self->{cpqFcaAccelCondition} eq 'other') {
+    $info .= sprintf '%s and needs attention (%s)',
+        $self->{cpqFcaAccelCondition}, $self->{cpqFcaAccelErrCode};
+    $self->add_message(CRITICAL, $info);
+    $self->add_info($info);
+  } elsif ($self->{cpqFcaAccelCondition} ne 'ok') {
+    $info .= sprintf '%s and needs attention (%s)',
+        $self->{cpqFcaAccelCondition}, $self->{cpqFcaAccelErrCode};
+    $self->add_message(CRITICAL, $info);
+    $self->add_info($info);
+  } else {
+    $info .= sprintf '%s', $self->{cpqFcaAccelCondition};
+    $self->add_info($info);
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[FCAL_ACCELERATOR_%s]\n", $self->{name};
+  foreach (qw(cpqFcaAccelBoxIndex cpqFcaAccelBoxIoSlot cpqFcaAccelStatus
+      cpqFcaAccelErrCode cpqFcaAccelBatteryStatus cpqFcaAccelCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::LogicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef, 
+  }; 
+  map { $self->{$_} = $params{$_} } grep /cpqFcaLogDrv/, keys %params;
+  bless $self, $class;
+  $self->{name} = $params{name} || 
+      $self->{cpqFcaLogDrvBoxIndex}.':'.
+      $self->{cpqFcaLogDrvIndex};
+  $self->{controllerindex} = $self->{cpqFcaLogDrvBoxIndex};
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('fcald', $self->{name});
+  my $info = sprintf 'logical drive %s (%s) is %s',
+      $self->{name}, $self->{cpqFcaLogDrvFaultTol}, $self->{cpqFcaLogDrvCondition};
+  if ($self->{cpqFcaLogDrvCondition} ne "ok") {
+    $info .= sprintf ' (%s)', $self->{cpqFcaLogDrvStatus};
+    if ($self->{cpqFcaLogDrvStatus} =~ 
+        /rebuild|recovering|expand/) {
+      $info .= sprintf ' (%s)', $self->{cpqFcaLogDrvStatus};
+      $self->add_message(WARNING, $info);
+    } else {
+      $self->add_message(CRITICAL, $info);
+    }
+  } 
+  $self->add_info($info);
+}
+
+sub dump {
+  my $self = shift;
+  printf "[LOGICAL_DRIVE_%s]\n", $self->{name};
+  foreach (qw(cpqFcaLogDrvBoxIndex cpqFcaLogDrvIndex cpqFcaLogDrvFaultTol
+      cpqFcaLogDrvStatus cpqFcaLogDrvPercentRebuild cpqFcaLogDrvSize 
+      cpqFcaLogDrvPhyDrvIDs cpqFcaLogDrvCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::PhysicalDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef, 
+  }; 
+  map { $self->{$_} = $params{$_} } grep /cpqFcaPhyDrv/, keys %params;
+  $self->{name} = $params{name} || 
+      $self->{cpqFcaPhyDrvBoxIndex}.':'.$self->{cpqFcaPhyDrvIndex}; ####vorerst
+  $self->{controllerindex} = $self->{cpqScsiPhyDrvCntlrIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('fcapd', $self->{name});
+  my $info = sprintf "physical drive %s is %s", 
+      $self->{name}, $self->{cpqFcaPhyDrvStatus};
+  if ($self->{cpqFcaPhyDrvStatus} eq 'unconfigured') {
+    # not part of a logical drive
+    # condition will surely be other
+  } elsif ($self->{cpqFcaPhyDrvCondition} ne 'ok') {
+    $self->add_message(CRITICAL, $info);
+  }
+  $self->add_info($info);
+}
+
+sub dump {
+  my $self = shift;
+  printf "[PHYSICAL_DRIVE_%s]\n", $self->{name};
+  foreach (qw(cpqFcaPhyDrvBoxIndex cpqFcaPhyDrvIndex cpqFcaPhyDrvModel
+      cpqFcaPhyDrvBay cpqFcaPhyDrvStatus cpqFcaPhyDrvCondition
+      cpqFcaPhyDrvSize cpqFcaPhyDrvBusNumber)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::Proliant::Component::DiskSubsystem::Fca::SpareDrive;
+our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub dump {
+  my $self = shift;
+  printf "[SPARE_DRIVE]\n";
+}
+
+
+
+package HP::Proliant::Component::DiskSubsystem;
+our @ISA = qw(HP::Proliant::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    da_subsystem => undef,
+    sas_da_subsystem => undef,
+    ide_da_subsystem => undef,
+    fca_da_subsystem => undef,
+    scsi_da_subsystem => undef,
+    condition => $params{condition},
+    blacklisted => 0,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  $self->{da_subsystem} = HP::Proliant::Component::DiskSubsystem::Da->new(
+    runtime => $self->{runtime},
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+  );
+  $self->{sas_subsystem} = HP::Proliant::Component::DiskSubsystem::Sas->new(
+    runtime => $self->{runtime},
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+  );
+  $self->{scsi_subsystem} = HP::Proliant::Component::DiskSubsystem::Scsi->new(
+    runtime => $self->{runtime},
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+  );
+  $self->{ide_subsystem} = HP::Proliant::Component::DiskSubsystem::Ide->new(
+    runtime => $self->{runtime},
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+  );
+  $self->{fca_subsystem} = HP::Proliant::Component::DiskSubsystem::Fca->new(
+    runtime => $self->{runtime},
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+  );
+}
+
+sub check {
+  my $self = shift;
+  $self->add_info('checking disk subsystem');
+  $self->{da_subsystem}->check();
+  $self->{sas_subsystem}->check();
+  $self->{scsi_subsystem}->check();
+  $self->{ide_subsystem}->check();
+  $self->{fca_subsystem}->check();
+  $self->disk_summary();
+}
+
+sub dump {
+  my $self = shift;
+  $self->{da_subsystem}->dump();
+  $self->{sas_subsystem}->dump();
+  $self->{scsi_subsystem}->dump();
+  $self->{ide_subsystem}->dump();
+  $self->{fca_subsystem}->dump();
+}
+
+sub disk_summary {
+  my $self = shift;
+  foreach my $subsys (qw(da sas scsi ide fca)) {
+    if (my $pd = $self->{$subsys.'_subsystem'}->has_physical_drives()) {
+      my $ld = $self->{$subsys.'_subsystem'}->has_logical_drives();
+      $self->add_summary(sprintf '%s: %d logical drives, %d physical drives',
+          $subsys, $ld, $pd);
+    }
+  }
+}
+
+sub assemble {
+  my $self = shift;
+  $self->trace(3, sprintf "%s controllers und platten zusammenführen",
+      ref($self));
+  $self->trace(3, sprintf "has %d controllers",
+      scalar(@{$self->{controllers}}));
+  $self->trace(3, sprintf "has %d accelerators",
+      scalar(@{$self->{accelerators}})) if exists $self->{accelerators};
+  $self->trace(3, sprintf "has %d physical_drives",
+      scalar(@{$self->{physical_drives}}));
+  $self->trace(3, sprintf "has %d logical_drives",
+      scalar(@{$self->{logical_drives}}));
+  $self->trace(3, sprintf "has %d spare_drives",
+      scalar(@{$self->{spare_drives}}));
+  my $found = {
+      accelerators => {},
+      logical_drives => {},
+      physical_drives => {},
+      spare_drives => {},
+  };
+  # found->{komponente}->{controllerindex} ist ein array
+  # von teilen, die zu einem controller gehoeren
+  foreach my $item (qw(accelerators logical_drives physical_drives spare_drives)) {
+    foreach (@{$self->{$item}}) {
+      $found->{item}->{$_->{controllerindex}} = []
+          unless exists $found->{$item}->{$_->{controllerindex}};
+      push(@{$found->{$item}->{$_->{controllerindex}}}, $_);
+    }
+  }
+  foreach my $item (qw(accelerators logical_drives physical_drives spare_drives)) {
+    foreach (@{$self->{controllers}}) {
+      if (exists $found->{$item}->{$_->{controllerindex}}) {
+        $_->{$item} = $found->{$item}->{$_->{controllerindex}};
+        delete $found->{$item}->{$_->{controllerindex}};
+      } else {
+        $_->{$item} = []; # z.b. ein leerer controller: physical_drives = []
+      }
+    }
+  }
+  # was jetzt noch in $found uebrig ist, gehoert zu keinem controller
+  # d.h. komponenten mit ungueltigen cnrtlindex wurden gefunden
+}
+
+sub has_controllers {
+  my $self = shift;
+  return scalar(@{$self->{controllers}});
+}
+
+sub has_accelerators {
+  my $self = shift;
+  return exists $self->{accelerators} ? scalar(@{$self->{accelerators}}) : 0;
+}
+
+sub has_physical_drives {
+  my $self = shift;
+  return scalar(@{$self->{physical_drives}});
+}
+
+sub has_logical_drives {
+  my $self = shift;
+  return scalar(@{$self->{logical_drives}});
+}
+
+
+package HP::Proliant::Component;
+our @ISA = qw(HP::Proliant);
+
+
+
+package HP::Proliant;
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+use Data::Dumper;
+
+our @ISA = qw(HP::Server);
+
+sub init {
+  my $self = shift;
+  $self->{components} = {
+      powersupply_subsystem => undef,
+      fan_subsystem => undef,
+      temperature_subsystem => undef,
+      cpu_subsystem => undef,
+      memory_subsystem => undef,
+      nic_subsystem => undef,
+      disk_subsystem => undef,
+      asr_subsystem => undef,
+      event_subsystem => undef,
+  };
+  $self->{serial} = 'unknown';
+  $self->{product} = 'unknown';
+  $self->{romversion} = 'unknown';
+  $self->collect();
+  if (! $self->{runtime}->{plugin}->check_messages() && 
+      ! exists $self->{noinst_hint}) {
+    $self->set_serial();
+    $self->check_for_buggy_firmware();
+    $self->analyze_cpus();
+    $self->analyze_powersupplies();
+    $self->analyze_fan_subsystem();
+    $self->analyze_temperatures();
+    $self->analyze_memory_subsystem();
+    $self->analyze_nic_subsystem();
+    $self->analyze_disk_subsystem();
+    $self->analyze_asr_subsystem();
+    $self->analyze_event_subsystem();
+    $self->auto_blacklist();
+    $self->check_cpus();
+    $self->check_powersupplies();
+    $self->check_fan_subsystem();
+    $self->check_temperatures();
+    $self->check_memory_subsystem();
+    $self->check_nic_subsystem();
+    $self->check_disk_subsystem();
+    $self->check_asr_subsystem();
+    $self->check_event_subsystem();
+  }
+}
+
+sub identify {
+  my $self = shift;
+  return sprintf "System: '%s', S/N: '%s', ROM: '%s'", 
+      $self->{product}, $self->{serial}, $self->{romversion};
+}
+
+sub check_for_buggy_firmware {
+  my $self = shift;
+  my @buggyfirmwares = (
+      "P24 12/11/2001",
+      "P24 11/15/2002",
+      "D13 06/03/2003",
+      "D13 09/15/2004",
+      "P20 12/17/2002"
+  );
+  $self->{runtime}->{options}->{buggy_firmware} =
+      grep /^$self->{romversion}/, @buggyfirmwares;
+}
+
+sub dump {
+  my $self = shift;
+  printf STDERR "serial %s\n", $self->{serial};
+  printf STDERR "product %s\n", $self->{product};
+  printf STDERR "romversion %s\n", $self->{romversion};
+  printf STDERR "%s\n", Data::Dumper::Dumper($self->{components});
+}
+
+sub analyze_powersupplies {
+  my $self = shift;
+  $self->{components}->{powersupply_subsystem} =
+      HP::Proliant::Component::PowersupplySubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_fan_subsystem {
+  my $self = shift;
+  $self->{components}->{fan_subsystem} = 
+      HP::Proliant::Component::FanSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_temperatures {
+  my $self = shift;
+  $self->{components}->{temperature_subsystem} = 
+      HP::Proliant::Component::TemperatureSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_cpus {
+  my $self = shift;
+  $self->{components}->{cpu_subsystem} =
+      HP::Proliant::Component::CpuSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_memory_subsystem {
+  my $self = shift;
+  $self->{components}->{memory_subsystem} = 
+      HP::Proliant::Component::MemorySubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_nic_subsystem {
+  my $self = shift;
+  return if $self->{method} ne "snmp";
+  $self->{components}->{nic_subsystem} = 
+      HP::Proliant::Component::NicSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_disk_subsystem {
+  my $self = shift;
+  $self->{components}->{disk_subsystem} =
+      HP::Proliant::Component::DiskSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_asr_subsystem {
+  my $self = shift;
+  $self->{components}->{asr_subsystem} =
+      HP::Proliant::Component::AsrSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_event_subsystem {
+  my $self = shift;
+  $self->{components}->{event_subsystem} =
+      HP::Proliant::Component::EventSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub check_cpus {
+  my $self = shift;
+  $self->{components}->{cpu_subsystem}->check();
+  $self->{components}->{cpu_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_powersupplies {
+  my $self = shift;
+  $self->{components}->{powersupply_subsystem}->check();
+  $self->{components}->{powersupply_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_fan_subsystem {
+  my $self = shift;
+  $self->{components}->{fan_subsystem}->check();
+  $self->{components}->{fan_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_temperatures {
+  my $self = shift;
+  $self->{components}->{temperature_subsystem}->check();
+  $self->{components}->{temperature_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_memory_subsystem {
+  my $self = shift;
+  $self->{components}->{memory_subsystem}->check();
+  $self->{components}->{memory_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_nic_subsystem {
+  my $self = shift;
+  return if $self->{method} ne "snmp";
+  if ($self->{runtime}->{plugin}->{opts}->get('eval-nics')) {
+    $self->{components}->{nic_subsystem}->check();
+    $self->{components}->{nic_subsystem}->dump()
+        if $self->{runtime}->{options}->{verbose} >= 2;
+  }
+}
+sub check_disk_subsystem {
+  my $self = shift;
+  $self->{components}->{disk_subsystem}->check();
+  $self->{components}->{disk_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+  # zum anhaengen an die normale ausgabe... da: 2 logical drives, 5 physical...
+  $self->{runtime}->{plugin}->add_message(OK,
+      $self->{components}->{disk_subsystem}->{summary})
+      if $self->{components}->{disk_subsystem}->{summary};
+}
+
+sub check_asr_subsystem {
+  my $self = shift;
+  $self->{components}->{asr_subsystem}->check();
+  $self->{components}->{asr_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_event_subsystem {
+  my $self = shift;
+  $self->{components}->{event_subsystem}->check();
+  $self->{components}->{event_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub auto_blacklist() {
+  my $self = shift;
+  if ($self->{product} =~ /380 g6/) {
+    # http://bizsupport1.austin.hp.com/bc/docs/support/SupportManual/c01723408/c01723408.pdf seite 19
+    if ($self->{components}->{cpu_subsystem}->num_cpus() == 1) {
+      $self->add_blacklist('ff/f:5,6');
+    }
+  } elsif ($self->{product} =~ /380 g6/) {
+    # http://bizsupport1.austin.hp.com/bc/docs/support/SupportManual/c01704762/c01704762.pdf Fan 2 is only required when processor 2 is installed in the server.
+  }
+}
+
+
+package HP::Proliant::CLI;
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+our @ISA = qw(HP::Proliant);
+
+sub collect {
+  my $self = shift;
+  my $hpasmcli = undef;
+  if (($self->{runtime}->{plugin}->opts->hpasmcli) &&
+      (-f $self->{runtime}->{plugin}->opts->hpasmcli) &&
+      (! -x $self->{runtime}->{plugin}->opts->hpasmcli)) {
+    no strict 'refs';
+    open(BIRK, $self->{runtime}->{plugin}->opts->hpasmcli);
+    # all output in one file prefixed with server|powersupply|fans|temp|dimm
+    while(<BIRK>) {
+      chomp;
+      $self->{rawdata} .= $_."\n";
+    }
+    close BIRK;
+    # If you run this script and redirect it's output to a file
+    # you can use it for testing purposes with
+    # --hpasmcli <output>
+    # It must not be executable. (chmod 644)
+    my $diag = <<'EOEO';
+    hpasmcli=$(which hpasmcli)
+    hpacucli=$(which hpacucli)
+    for i in server powersupply fans temp dimm
+    do
+      $hpasmcli -s "show $i" | while read line
+      do
+        printf "%s %s\n" $i "$line"
+      done
+    done 
+    if [ -x "$hpacucli" ]; then
+      for i in config status
+      do
+        $hpacucli ctrl all show $i | while read line
+        do
+          printf "%s %s\n" $i "$line"
+        done
+      done
+    fi
+EOEO
+  } else {
+    #die "exec hpasmcli";
+    # alles einsammeln und in rawdata stecken
+    my $hpasmcli = undef;
+    $hpasmcli = $self->{runtime}->{plugin}->opts->hpasmcli ?
+        $self->{runtime}->{plugin}->opts->hpasmcli : '/sbin/hpasmcli';
+# check if this exists at all
+# descend the directory
+    if ($self->{runtime}->{plugin}->opts->hpasmcli &&
+        -e $self->{runtime}->{plugin}->opts->hpasmcli) {
+      $hpasmcli = $self->{runtime}->{plugin}->opts->hpasmcli;
+    } elsif (-e '/sbin/hpasmcli') {
+      $hpasmcli = '/sbin/hpasmcli';
+    } else {
+      $hpasmcli = undef;
+    }
+    if ($hpasmcli) {
+      if ($< != 0) {
+        close STDIN;
+        $hpasmcli = "sudo -S ".$hpasmcli;
+      }
+      $self->trace(2, sprintf "calling %s\n", $hpasmcli);
+      $self->check_daemon();
+      if (! $self->{runtime}->{plugin}->check_messages()) {
+        $self->check_hpasm_client($hpasmcli);
+        if (! $self->{runtime}->{plugin}->check_messages()) {
+          foreach my $component (qw(server fans temp dimm powersupply iml)) {
+            if (open HPASMCLI, "$hpasmcli -s \"show $component\"|") {
+              my @output = <HPASMCLI>;
+              close HPASMCLI;
+              $self->{rawdata} .= join("\n", map {
+                  $component.' '.$_;
+              } @output);
+            }
+          }
+          if ($self->{runtime}->{options}->{hpacucli}) {
+            #1 oder 0. pfad selber finden
+            my $hpacucli = undef;
+            if (-e '/usr/sbin/hpacucli') {
+              $hpacucli = '/usr/sbin/hpacucli';
+            } elsif (-e '/usr/local/sbin/hpacucli') {
+              $hpacucli = '/usr/local/sbin/hpacucli';
+            } else {
+              $hpacucli = $hpasmcli;
+              $hpacucli =~ s/^sudo\s*//;
+              $hpacucli =~ s/hpasmcli/hpacucli/;
+              $hpacucli = -e $hpacucli ? $hpacucli : undef;
+            }
+            if ($hpacucli) {
+              if ($< != 0) {
+                close STDIN;
+                $hpacucli = "sudo -S ".$hpacucli;
+              }
+              $self->trace(2, sprintf "calling %s\n", $hpacucli);
+              $self->check_hpacu_client($hpacucli);
+              if (! $self->{runtime}->{plugin}->check_messages()) {
+                if (open HPACUCLI, "$hpacucli ctrl all show config 2>&1|") {
+                  my @output = <HPACUCLI>;
+                  close HPACUCLI;
+                  $self->{rawdata} .= join("\n", map {
+                      'config '.$_;
+                  } @output);
+                }
+                if (open HPACUCLI, "$hpacucli ctrl all show status 2>&1|") {
+                  my @output = <HPACUCLI>;
+                  close HPACUCLI;
+                  $self->{rawdata} .= join("\n", map {
+                      'status '.$_;
+                  } @output);
+                }
+              } elsif ($self->{runtime}->{options}->{hpacucli} == 2) {
+                # we probably don't have sudo-privileges, but we were compiled with
+                # --enable-hpacucli=maybe
+                # so we cover it up in silence
+                $self->remove_message(UNKNOWN);
+                $self->trace(2, sprintf "calling %s seems to have failed, but nobody cares\n", $hpacucli);
+              }
+            } else {
+              if ($self->{runtime}->{options}->{noinstlevel} eq 'ok') {
+                $self->add_message(OK,
+                    'hpacucli is not installed. let\'s hope the best...');
+              } else {
+                $self->add_message(
+                    uc $self->{runtime}->{options}->{noinstlevel},
+                    'hpacucli is not installed.');
+              }
+            }
+          }
+        }
+      }
+    } else {
+      if ($self->{runtime}->{options}->{noinstlevel} eq 'ok') {
+        $self->add_message(OK,
+            'hpasm is not installed, i can only guess');
+        $self->{noinst_hint} = 1;
+      } else {
+        $self->add_message(
+            uc $self->{runtime}->{options}->{noinstlevel},
+            'hpasmcli is not installed.');
+      }
+    }
+  }
+}
+
+
+sub check_daemon {
+  my $self = shift;
+  my $multiproc_os_signatures_files = {
+      '/etc/SuSE-release' => 'VERSION\s*=\s*8',
+      '/etc/trustix-release' => '.*',
+      '/etc/redhat-release' => '.*Pensacola.*',
+      '/etc/debian_version' => '3\.1',
+      '/etc/issue' => '.*Kernel 2\.4\.9-vmnix2.*', # VMware ESX Server 2.5.4
+  };
+  if (open PS, "/bin/ps -e -ocmd|") {
+    my $numprocs = 0;
+    my $numcliprocs = 0;
+    my @procs = <PS>;
+    close PS;
+    $numprocs = grep /hpasm.*d$/, map { (split /\s+/, $_)[0] } @procs;
+    $numcliprocs = grep /hpasmcli/, grep !/check_hpasm/, @procs;
+    if (! $numprocs ) {
+      $self->add_message(CRITICAL, 'hpasmd needs to be restarted');
+    } elsif ($numprocs > 1) {
+      my $known = 0;
+      foreach my $osfile (keys %{$multiproc_os_signatures_files}) {
+        if (-f $osfile) {
+          open OSSIG, $osfile;
+          if (grep /$multiproc_os_signatures_files->{$osfile}/, <OSSIG>) {
+            $known = 1;
+          }
+          close OSSIG;
+        }
+      }
+      if (! $known) {
+        $self->add_message(UNKNOWN, 'multiple hpasmd procs');
+      }
+    }
+    if ($numcliprocs == 1) {
+      $self->add_message(UNKNOWN, 'another hpasmcli is running');
+    } elsif ($numcliprocs > 1) {
+      $self->add_message(UNKNOWN, 'hanging hpasmcli processes');
+    }
+  }
+}
+
+sub check_hpasm_client {
+  my $self = shift;
+  my $hpasmcli = shift;
+  if (open HPASMCLI, "$hpasmcli -s help 2>&1 |") {
+    my @output = <HPASMCLI>;
+    close HPASMCLI;
+    if (grep /Could not communicate with hpasmd/, @output) {
+      $self->add_message(CRITICAL, 'hpasmd needs to be restarted');
+    } elsif (grep /(asswor[dt]:)|(You must be root)/, @output) {
+      $self->add_message(UNKNOWN,
+          sprintf "insufficient rights to call %s", $hpasmcli);
+    } elsif (grep /must have a tty/, @output) {
+      $self->add_message(CRITICAL,
+          'sudo must be configured with requiretty=no (man sudo)');
+    } elsif (! grep /CLEAR/, @output) {
+      $self->add_message(UNKNOWN,
+          sprintf "insufficient rights to call %s", $hpasmcli);
+    }
+  } else {
+    $self->add_message(UNKNOWN,
+        sprintf "insufficient rights to call %s", $hpasmcli);
+  }
+}
+
+sub check_hpacu_client {
+  my $self = shift;
+  my $hpacucli = shift;
+  if (open HPACUCLI, "$hpacucli help 2>&1 |") {
+    my @output = <HPACUCLI>;
+    close HPACUCLI;
+    if (grep /Another instance of hpacucli is running/, @output) {
+      $self->add_message(UNKNOWN, 'another hpacucli is running');
+    } elsif (grep /You need to have administrator rights/, @output) {
+      $self->add_message(UNKNOWN,
+          sprintf "insufficient rights to call %s", $hpacucli);
+    } elsif (grep /(asswor[dt]:)|(You must be root)/, @output) {
+      $self->add_message(UNKNOWN,
+          sprintf "insufficient rights to call %s", $hpacucli);
+    } elsif (! grep /CLI Syntax/, @output) {
+      $self->add_message(UNKNOWN,
+          sprintf "insufficient rights to call %s", $hpacucli);
+    }
+  } else {
+    $self->add_message(UNKNOWN,
+        sprintf "insufficient rights to call %s", $hpacucli);
+  }
+}
+
+sub set_serial {
+  my $self = shift;
+  foreach (grep(/^server/, split(/\n/, $self->{rawdata}))) {
+    if (/System\s+:\s+(.*[^\s])/) {
+      $self->{product} = lc $1;
+    } elsif (/Serial No\.\s+:\s+(\w+)/) {
+      $self->{serial} = $1;
+    } elsif (/ROM version\s+:\s+(.*[^\s])/) {
+      $self->{romversion} = $1;
+    }
+  }
+  $self->{serial} = $self->{serial};
+  $self->{product} = lc $self->{product};
+  $self->{romversion} = $self->{romversion};
+}
+
+
+package HP::Proliant::SNMP;
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+our @ISA = qw(HP::Proliant);
+
+sub collect {
+  my $self = shift;
+  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
+    my $cpqSeMibCondition = '1.3.6.1.4.1.232.1.1.3.0'; # 2=ok
+    my $cpqHeMibCondition = '1.3.6.1.4.1.232.6.1.3.0'; # hat nicht jeder
+    if ($self->{productname} =~ /4LEE/) {
+      # rindsarsch!
+      $self->{rawdata}->{$cpqHeMibCondition} = 0;
+    }
+    if (! exists $self->{rawdata}->{$cpqHeMibCondition} &&
+        ! exists $self->{rawdata}->{$cpqSeMibCondition}) { # vlt. geht doch was
+        $self->add_message(CRITICAL,
+            'snmpwalk returns no health data (cpqhlth-mib)');
+    }
+  } else {
+    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
+    #$params{'-translate'} = [
+    #  -all => 0x0
+    #];
+    my ($session, $error) = 
+        Net::SNMP->session(%{$self->{runtime}->{snmpparams}});
+    if (! defined $session) {
+      $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
+      $self->trace(1, Data::Dumper::Dumper($self->{runtime}->{snmpparams}));
+    } else {
+      # revMajor is often used for discovery of hp devices
+      my $cpqHeMibRev = '1.3.6.1.4.1.232.6.1';
+      my $cpqHeMibRevMajor = '1.3.6.1.4.1.232.6.1.1.0';
+      my $cpqHeMibCondition = '1.3.6.1.4.1.232.6.1.3.0';
+      my $result = $session->get_request(
+          -varbindlist => [$cpqHeMibCondition]
+      );
+      if ($self->{productname} =~ /4LEE/) {
+        # rindsarsch!
+        $result->{$cpqHeMibCondition} = 0;
+      }
+      if (!defined($result) || 
+          $result->{$cpqHeMibCondition} eq 'noSuchInstance' ||
+          $result->{$cpqHeMibCondition} eq 'noSuchObject' ||
+          $result->{$cpqHeMibCondition} eq 'endOfMibView') {
+        $self->add_message(CRITICAL,
+            'snmpwalk returns no health data (cpqhlth-mib)');
+        $session->close;
+      } else {
+        # this is not reliable. many agents return 4=failed
+        #if ($result->{$cpqHeMibCondition} != 2) {
+        #  $obstacle = "cmapeerstart";
+        #}
+      }
+    }
+    if (! $self->{runtime}->{plugin}->check_messages()) {
+      # snmp peer is alive
+      $self->trace(2, sprintf "Protocol is %s", 
+          $self->{runtime}->{snmpparams}->{'-version'});
+      my $cpqStdEquipment = "1.3.6.1.4.1.232";
+      my $cpqSeProcessor =  "1.3.6.1.4.1.232.1.2.2";
+      my $cpqSeRom =        "1.3.6.1.4.1.232.1.2.6";
+      my $cpqHeComponent =  "1.3.6.1.4.1.232.6.2";
+      my $cpqHePWSComponent = "1.3.6.1.4.1.232.6.2.9";
+      my $cpqHeThermal = "1.3.6.1.4.1.232.6.2.6";
+      #my $cpqHeFComponent = "1.3.6.1.4.1.232.6.2.6.7";
+      #my $cpqHeTComponent = "1.3.6.1.4.1.232.6.2.6.8";
+      my $cpqHeMComponent = "1.3.6.1.4.1.232.6.2.14";
+      my $cpqDaComponent =  "1.3.6.1.4.1.232.3.2";
+      my $cpqSasComponent =  "1.3.6.1.4.1.232.5";
+      my $cpqIdeComponent =  "1.3.6.1.4.1.232.14";
+      my $cpqFcaComponent =  "1.3.6.1.4.1.232.16.2";
+      my $cpqSiComponent =  "1.3.6.1.4.1.232.2.2";
+      my $cpqHeAsr = "1.3.6.1.4.1.232.6.2.5";
+      my $cpqNic = "1.3.6.1.4.1.232.18.2";
+      $session->translate;
+      my $response = {}; #break the walk up in smaller pieces
+      my $tic = time; my $tac = $tic;
+      my $response1 = $session->get_table(
+          -baseoid => $cpqSeProcessor);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqSeProcessor (%d oids)",
+          $tac - $tic, scalar(keys %{$response1}));
+      # Walk for PowerSupply
+      $tic = time;
+      my $response2p = $session->get_table(
+          -maxrepetitions => 1,
+          -baseoid => $cpqHePWSComponent);
+      if (scalar (keys %{$response2p}) == 0) {
+        $self->trace(2, sprintf "maxrepetitions failed. fallback");
+        $response2p = $session->get_table(
+            -baseoid => $cpqHePWSComponent);
+      }
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqHePWSComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response2p}));
+
+      # Walk for Fans/Temp/Overall
+      $tic = time;
+      my $response2f = $session->get_table(
+          -maxrepetitions => 1,
+          -baseoid => $cpqHeThermal);
+      if (scalar (keys %{$response2f}) == 0) {
+        $self->trace(2, sprintf "maxrepetitions failed. fallback");
+        $response2f = $session->get_table(
+            -baseoid => $cpqHeThermal);
+      }
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqHeThermal (%d oids)",
+          $tac - $tic, scalar(keys %{$response2f}));
+
+#      # Walk for Fans
+#      $tic = time;
+#      my $response2f = $session->get_table(
+#          -maxrepetitions => 1,
+#          -baseoid => $cpqHeFComponent);
+#      if (scalar (keys %{$response2f}) == 0) {
+#        $self->trace(2, sprintf "maxrepetitions failed. fallback");
+#        $response2f = $session->get_table(
+#            -baseoid => $cpqHeFComponent);
+#      }
+#      $tac = time;
+#      $self->trace(2, sprintf "%03d seconds for walk cpqHeFComponent (%d oids)",
+#          $tac - $tic, scalar(keys %{$response2f}));
+#      # Walk for Temp
+#      $tic = time;
+#      my $response2t = $session->get_table(
+#          -maxrepetitions => 1,
+#          -baseoid => $cpqHeTComponent);
+#      if (scalar (keys %{$response2t}) == 0) {
+#        $self->trace(2, sprintf "maxrepetitions failed. fallback");
+#        $response2t = $session->get_table(
+#            -baseoid => $cpqHeTComponent);
+#      }
+#      $tac = time;
+#      $self->trace(2, sprintf "%03d seconds for walk cpqHeTComponent (%d oids)",
+#          $tac - $tic, scalar(keys %{$response2t}));
+      # Walk for Mem
+      $tic = time;
+      my $response2m = $session->get_table(
+          -maxrepetitions => 1,
+          -baseoid => $cpqHeMComponent);
+      if (scalar (keys %{$response2m}) == 0) {
+        $self->trace(2, sprintf "maxrepetitions failed. fallback");
+        $response2m = $session->get_table(
+            -baseoid => $cpqHeMComponent);
+      }
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqHeMComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response2m}));
+      #
+      $tic = time;
+      my $response3 = $session->get_table(
+          -baseoid => $cpqDaComponent);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqDaComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response3}));
+      $tic = time;
+      my $response4 = $session->get_table(
+          -baseoid => $cpqSiComponent);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqSiComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response4}));
+      $tic = time;
+      my $response5 = $session->get_table(
+          -baseoid => $cpqSeRom);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqSeRom (%d oids)",
+          $tac - $tic, scalar(keys %{$response5}));
+      $tic = time;
+      my $response6 = $session->get_table(
+          -baseoid => $cpqSasComponent);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqSasComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response6}));
+      $tic = time;
+      my $response7 = $session->get_table(
+          -baseoid => $cpqIdeComponent);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqIdeComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response7}));
+      $tic = time;
+      my $response8 = $session->get_table(
+          -baseoid => $cpqFcaComponent);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqFcaComponent (%d oids)",
+          $tac - $tic, scalar(keys %{$response8}));
+      $tic = time;
+
+      # Walk for ASR
+      $tic = time;
+      my $response9 = $session->get_table(
+          -maxrepetitions => 1,
+          -baseoid => $cpqHeAsr);
+      if (scalar (keys %{$response9}) == 0) {
+        $self->trace(2, sprintf "maxrepetitions failed. fallback");
+        $response9 = $session->get_table(
+            -baseoid => $cpqHeAsr);
+      }
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk $cpqHeAsr (%d oids)",
+          $tac - $tic, scalar(keys %{$response9}));
+      $tic = time;
+      my $response10 = $session->get_table(
+          -baseoid => $cpqNic);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqNic (%d oids)",
+          $tac - $tic, scalar(keys %{$response10}));
+      $session->close();
+
+      map { $response->{$_} = $response1->{$_} } keys %{$response1};
+      map { $response->{$_} = $response2p->{$_} } keys %{$response2p};
+      map { $response->{$_} = $response2f->{$_} } keys %{$response2f};
+#      map { $response->{$_} = $response2t->{$_} } keys %{$response2t};
+      map { $response->{$_} = $response2m->{$_} } keys %{$response2m};
+      map { $response->{$_} = $response3->{$_} } keys %{$response3};
+      map { $response->{$_} = $response4->{$_} } keys %{$response4};
+      map { $response->{$_} = $response5->{$_} } keys %{$response5};
+      map { $response->{$_} = $response6->{$_} } keys %{$response6};
+      map { $response->{$_} = $response7->{$_} } keys %{$response7};
+      map { $response->{$_} = $response8->{$_} } keys %{$response8};
+      map { $response->{$_} = $response9->{$_} } keys %{$response9};
+      map { $response->{$_} = $response10->{$_} } keys %{$response10};
+      map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
+          keys %$response;
+      $self->{rawdata} = $response;
+    }
+  }
+  return $self->{runtime}->{plugin}->check_messages();
+}
+
+sub set_serial {
+  my $self = shift;
+
+  my $cpqSiSysSerialNum = "1.3.6.1.4.1.232.2.2.2.1.0";
+  my $cpqSiProductName = "1.3.6.1.4.1.232.2.2.4.2.0";
+  my $cpqSeSysRomVer = "1.3.6.1.4.1.232.1.2.6.1.0";
+
+  $self->{serial} = 
+      SNMP::Utils::get_object($self->{rawdata}, $cpqSiSysSerialNum);
+  $self->{product} =
+      SNMP::Utils::get_object($self->{rawdata}, $cpqSiProductName);
+  $self->{romversion} =
+      SNMP::Utils::get_object($self->{rawdata}, $cpqSeSysRomVer);
+  if ($self->{romversion} && $self->{romversion} =~
+      #/(\d{2}\/\d{2}\/\d{4}).*?([ADP]{1}\d{2}).*/) {
+      /(\d{2}\/\d{2}\/\d{4}).*?Family.*?([A-Z]{1})(\d+).*/) {
+    $self->{romversion} = sprintf("%s%02d %s", $2, $3, $1);
+  } elsif ($self->{romversion} && $self->{romversion} =~
+      /([ADP]{1}\d{2})\-(\d{2}\/\d{2}\/\d{4})/) {
+    $self->{romversion} = sprintf("%s %s", $1, $2);
+  }
+  if (!$self->{serial} && $self->{romversion}) {
+    # this probably is a very, very old server.
+    $self->{serial} = "METHUSALEM";
+    $self->{runtime}->{scrapiron} = 1;
+  }
+  $self->{serial} = $self->{serial};
+  $self->{product} = lc $self->{product};
+  $self->{romversion} = $self->{romversion};
+  $self->{runtime}->{product} = $self->{product};
+}
+
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem;
+our @ISA = qw(HP::BladeSystem::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    common_enclosures => [],
+    common_enclosure_temp_subsys => undef,
+    common_enclosure_fan_subsys => undef,
+    common_enclosure_fuse_subsys => undef,
+    common_enclosure_manager_subsys => undef,
+    common_enclosure_frus => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  # jeweils ein block fuer
+  # enclosures, temps, fans, fuses
+  # loop ueber oids und entspr. new
+  my $oids = {
+      cpqRackCommonEnclosureEntry => '1.3.6.1.4.1.232.22.2.3.1.1.1',
+      cpqRackCommonEnclosureRack => '1.3.6.1.4.1.232.22.2.3.1.1.1.1',
+      cpqRackCommonEnclosureIndex => '1.3.6.1.4.1.232.22.2.3.1.1.1.2',
+      cpqRackCommonEnclosureModel => '1.3.6.1.4.1.232.22.2.3.1.1.1.3',
+      cpqRackCommonEnclosureSparePartNumber => '1.3.6.1.4.1.232.22.2.3.1.1.1.6',
+      cpqRackCommonEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.3.1.1.1.7',
+      cpqRackCommonEnclosureFWRev => '1.3.6.1.4.1.232.22.2.3.1.1.1.8',
+      cpqRackCommonEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.1.1.9',
+      cpqRackCommonEnclosureCondition => '1.3.6.1.4.1.232.22.2.3.1.1.1.16',
+      cpqRackCommonEnclosureHasServerBlades => '1.3.6.1.4.1.232.22.2.3.1.1.1.17',
+      cpqRackCommonEnclosureHasPowerBlades => '1.3.6.1.4.1.232.22.2.3.1.1.1.18',
+      cpqRackCommonEnclosureHasNetConnectors => '1.3.6.1.4.1.232.22.2.3.1.1.1.19',
+      cpqRackCommonEnclosureHasTempSensors => '1.3.6.1.4.1.232.22.2.3.1.1.1.20',
+      cpqRackCommonEnclosureHasFans => '1.3.6.1.4.1.232.22.2.3.1.1.1.21',
+      cpqRackCommonEnclosureHasFuses => '1.3.6.1.4.1.232.22.2.3.1.1.1.22',
+      cpqRackCommonEnclosureConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+      cpqRackCommonEnclosureHasServerBladesValue => {
+          1 => 'false',
+          2 => 'true',
+      },
+  };
+  $oids->{cpqRackCommonEnclosureHasPowerBladesValue} =
+    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
+  $oids->{cpqRackCommonEnclosureHasNetConnectorsValue} =
+    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
+  $oids->{cpqRackCommonEnclosureHasTempSensorsValue} =
+    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
+  $oids->{cpqRackCommonEnclosureHasFansValue} =
+    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
+  $oids->{cpqRackCommonEnclosureHasServerBladesValue} =
+    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
+  # INDEX { cpqRackCommonEnclosureRack cpqRackCommonEnclosureIndex }
+  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureEntry')) {
+    push(@{$self->{common_enclosures}},
+        HP::BladeSystem::Component::CommonEnclosureSubsystem::CommonEnclosure->new(%{$_}));
+  }
+
+  $self->{common_enclosure_fan_subsys} = HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem->new(
+      rawdata => $self->{rawdata},
+      method => $self->{method},
+      runtime => $self->{runtime},
+  );
+  $self->{common_enclosure_temp_subsys} = HP::BladeSystem::Component::CommonEnclosureSubsystem::TempSubsystem->new(
+      rawdata => $self->{rawdata},
+      method => $self->{method},
+      runtime => $self->{runtime},
+  );
+  $self->{common_enclosure_fuse_subsys} = HP::BladeSystem::Component::CommonEnclosureSubsystem::FuseSubsystem->new(
+      rawdata => $self->{rawdata},
+      method => $self->{method},
+      runtime => $self->{runtime},
+  );
+  $self->{common_enclosure_manager_subsys} = HP::BladeSystem::Component::CommonEnclosureSubsystem::ManagerSubsystem->new(
+      rawdata => $self->{rawdata},
+      method => $self->{method},
+      runtime => $self->{runtime},
+  );
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{common_enclosures}}) {
+    $_->check();
+  }
+  $self->{common_enclosure_fan_subsys}->check();
+  $self->{common_enclosure_temp_subsys}->check();
+  $self->{common_enclosure_fuse_subsys}->check();
+  $self->{common_enclosure_manager_subsys}->check();
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{common_enclosures}}) {
+    $_->dump();
+  }
+  $self->{common_enclosure_fan_subsys}->dump();
+  $self->{common_enclosure_temp_subsys}->dump();
+  $self->{common_enclosure_fuse_subsys}->dump();
+  $self->{common_enclosure_manager_subsys}->dump();
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::CommonEnclosure;
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift; 
+  my %params = @_;
+  my $self = { 
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    blacklisted => 0,
+    info => undef, 
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackCommonEnclosure/, keys %params;
+  $self->{name} = $self->{cpqRackCommonEnclosureRack}.':'.$self->{cpqRackCommonEnclosureIndex};
+  $self->{serfw} = sprintf "Ser: %s, FW: %s", $self->{cpqRackCommonEnclosureSerialNum},
+      $self->{cpqRackCommonEnclosureFWRev};
+  bless $self, $class;
+  return $self;
+}
+
+
+sub check {
+  my $self = shift;
+  $self->blacklist('ce', $self->{cpqRackCommonEnclosureName});
+  my $info = sprintf 'common enclosure %s condition is %s (%s)',
+      $self->{cpqRackCommonEnclosureName}, $self->{cpqRackCommonEnclosureCondition}, $self->{serfw};
+  $self->add_info($info);
+  if ($self->{cpqRackCommonEnclosureCondition} eq 'failed') {
+    $info .= sprintf " (SparePartNum %s)", $self->{cpqRackCommonEnclosureSparePartNumber};
+    $self->add_message(CRITICAL, $info);
+  } elsif ($self->{cpqRackCommonEnclosureCondition} eq 'degraded') {
+    $info .= sprintf " (SparePartNum %s)", $self->{cpqRackCommonEnclosureSparePartNumber};
+    $self->add_message(WARNING, $info);
+  } 
+}
+
+sub dump {
+  my $self = shift;
+    printf "[COMMON_ENCLOSURE_%s]\n", $self->{cpqRackCommonEnclosureName};
+  foreach (qw(cpqRackCommonEnclosureRack cpqRackCommonEnclosureIndex cpqRackCommonEnclosureModel
+      cpqRackCommonEnclosureSerialNum cpqRackCommonEnclosureFWRev cpqRackCommonEnclosureFWRev
+      cpqRackCommonEnclosureName
+      cpqRackCommonEnclosureCondition cpqRackCommonEnclosureHasServerBlades 
+      cpqRackCommonEnclosureHasPowerBlades cpqRackCommonEnclosureHasNetConnectors 
+      cpqRackCommonEnclosureHasTempSensors cpqRackCommonEnclosureHasFans cpqRackCommonEnclosureHasFuses)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem;
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    fans => [],
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $oids = {
+      cpqRackCommonEnclosureFanEntry => '1.3.6.1.4.1.232.22.2.3.1.3.1',
+      cpqRackCommonEnclosureFanRack => '1.3.6.1.4.1.232.22.2.3.1.3.1.1',
+      cpqRackCommonEnclosureFanChassis => '1.3.6.1.4.1.232.22.2.3.1.3.1.2',
+      cpqRackCommonEnclosureFanIndex => '1.3.6.1.4.1.232.22.2.3.1.3.1.3',
+      cpqRackCommonEnclosureFanEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.3.1.4',
+      cpqRackCommonEnclosureFanLocation => '1.3.6.1.4.1.232.22.2.3.1.3.1.5',
+      cpqRackCommonEnclosureFanPartNumber => '1.3.6.1.4.1.232.22.2.3.1.3.1.6',
+      cpqRackCommonEnclosureFanSparePartNumber => '1.3.6.1.4.1.232.22.2.3.1.3.1.7',
+      cpqRackCommonEnclosureFanPresent => '1.3.6.1.4.1.232.22.2.3.1.3.1.8',
+      cpqRackCommonEnclosureFanRedundant => '1.3.6.1.4.1.232.22.2.3.1.3.1.9',
+      cpqRackCommonEnclosureFanRedundantGroupId => '1.3.6.1.4.1.232.22.2.3.1.3.1.10',
+      cpqRackCommonEnclosureFanCondition => '1.3.6.1.4.1.232.22.2.3.1.3.1.11',
+      cpqRackCommonEnclosureFanEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.3.1.3.1.12',
+      cpqRackCommonEnclosureFanPresentValue => {
+          1 => 'other',
+          2 => 'absent',
+          3 => 'present',
+      },
+      cpqRackCommonEnclosureFanRedundantValue => {
+          0 => 'other', # meiner phantasie entsprungen, da sich hp nicht aeussert
+          1 => 'other',
+          2 => 'notRedundant',
+          3 => 'redundant',
+      },
+      cpqRackCommonEnclosureFanConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      }
+  };
+  # INDEX { cpqRackCommonEnclosureFanRack, cpqRackCommonEnclosureFanChassis, cpqRackCommonEnclosureFanIndex }
+  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureFanEntry')) {
+    push(@{$self->{fans}},
+        HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem::Fan->new(%{$_}));
+  }
+
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{fans}}) {
+    $_->check() if $_->{cpqRackCommonEnclosureFanPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{fans}}) {
+    $_->dump() if $_->{cpqRackCommonEnclosureFanPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem::Fan;
+
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackCommonEnclosureFan/, keys %params;
+  $self->{name} = $self->{cpqRackCommonEnclosureFanRack}.':'.$self->{cpqRackCommonEnclosureFanChassis}.':'.$self->{cpqRackCommonEnclosureFanIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('f', $self->{name});
+  $self->add_info(sprintf 'fan %s is %s, location is %s, redundance is %s, condition is %s',
+      $self->{name}, $self->{cpqRackCommonEnclosureFanPresent},
+      $self->{cpqRackCommonEnclosureFanLocation},
+      $self->{cpqRackCommonEnclosureFanRedundant},
+      $self->{cpqRackCommonEnclosureFanCondition});
+  if ($self->{cpqRackCommonEnclosureFanCondition} eq 'degraded') {
+    $self->{info} .= sprintf ", (SparePartNum: %s)", $self->{cpqRackCommonEnclosureFanSparePartNumber};
+    $self->add_message(WARNING, $self->{info});
+  } elsif ($self->{cpqRackCommonEnclosureFanCondition} eq 'failed') {
+    $self->{info} .= sprintf ", (SparePartNum: %s)", $self->{cpqRackCommonEnclosureFanSparePartNumber};
+    $self->add_message(CRITICAL, $self->{info});
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[FAN_%s]\n", $self->{name};
+  foreach (qw(cpqRackCommonEnclosureFanRack cpqRackCommonEnclosureFanChassis 
+      cpqRackCommonEnclosureFanIndex cpqRackCommonEnclosureFanEnclosureName 
+      cpqRackCommonEnclosureFanLocation cpqRackCommonEnclosureFanPartNumber 
+      cpqRackCommonEnclosureFanSparePartNumber cpqRackCommonEnclosureFanPresent 
+      cpqRackCommonEnclosureFanRedundant cpqRackCommonEnclosureFanRedundantGroupId
+      cpqRackCommonEnclosureFanCondition cpqRackCommonEnclosureFanEnclosureSerialNum)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::TempSubsystem;
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    condition => $params{condition},
+    status => $params{status},
+    temperatures => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  if ($params{runtime}->{options}->{customthresholds}) {
+    if (-f $params{runtime}->{options}->{customthresholds}) {
+      open CT, $params{runtime}->{options}->{customthresholds};
+      $params{runtime}->{options}->{customthresholds} = <CT>;
+      close CT;
+    }
+    foreach my $ct_items
+        (split(/\//, $params{runtime}->{options}->{customthresholds})) {
+      if ($ct_items =~ /^(\d+):(\d+)$/) {
+        my $temp = $2;
+        $params{runtime}->{options}->{thresholds}->{$1} = $temp;
+      } else {
+        die sprintf "invalid threshold %s", $ct_items;
+      }
+    }
+  }
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my %params = @_;
+  my $snmpwalk = $self->{rawdata};
+  my $oids = {
+      cpqRackCommonEnclosureTempEntry => '1.3.6.1.4.1.232.22.2.3.1.2.1',
+      cpqRackCommonEnclosureTempRack => '1.3.6.1.4.1.232.22.2.3.1.2.1.1',
+      cpqRackCommonEnclosureTempChassis => '1.3.6.1.4.1.232.22.2.3.1.2.1.2',
+      cpqRackCommonEnclosureTempSensorIndex => '1.3.6.1.4.1.232.22.2.3.1.2.1.3',
+      cpqRackCommonEnclosureTempSensorEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.2.1.4',
+      cpqRackCommonEnclosureTempLocation => '1.3.6.1.4.1.232.22.2.3.1.2.1.5',
+      cpqRackCommonEnclosureTempCurrent => '1.3.6.1.4.1.232.22.2.3.2.1.6',
+      cpqRackCommonEnclosureTempThreshold => '1.3.6.1.4.1.232.22.2.3.1.2.1.7',
+      cpqRackCommonEnclosureTempCondition => '1.3.6.1.4.1.232.22.2.3.1.2.1.8',
+      cpqRackCommonEnclosureTempType => '1.3.6.1.4.1.232.22.2.3.1.2.1.9',
+      cpqRackCommonEnclosureTempConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+      cpqRackCommonEnclosureTempTypeValue => {
+          1 => 'other',
+          5 => 'blowout',
+          9 => 'caution',
+          15 => 'critical',
+      },
+  };
+  # INDEX { cpqRackCommonEnclosureTempRack cpqRackCommonEnclosureTempChassis
+  #         cpqRackCommonEnclosureTempSensorIndex }
+  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureTempEntry')) {
+    push(@{$self->{temperatures}},
+       HP::BladeSystem::Component::CommonEnclosureSubsystem::TemperatureSubsystem::Temperature->new(%{$_}));
+  }
+
+}
+
+
+sub check {
+  my $self = shift;
+  my $errorfound = 0;
+  if (scalar (@{$self->{temperatures}}) == 0) {
+    #$self->overall_check();
+  } else {
+    foreach (@{$self->{temperatures}}) {
+      $_->check();
+    }
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{temperatures}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::TempSubsystem::Temp;
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem::TempSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackCommonEnclosureTemp/, keys %params;
+  $self->{name} = $params{cpqRackCommonEnclosureTempRack}.':'.
+      $params{cpqRackCommonEnclosureTempChassis}.':'.
+      $params{cpqRackCommonEnclosureTempSensorIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('t', $self->{name});
+  if ($self->{cpqRackCommonEnclosureTempCurrent} > $self->{cpqRackCommonEnclosureTempThreshold}) {
+    $self->add_info(sprintf "%s temperature too high (%d%s)",
+        $self->{cpqRackCommonEnclosureTempLocation}, $self->{cpqRackCommonEnclosureTempCurrent},
+        $self->{runtime}->{options}->{celsius} ? "C" : "F");
+    $self->add_message(CRITICAL, $self->{info});
+  } else {
+    $self->add_info(sprintf "%d %s temperature is %d (%d max)",
+        $self->{name}, $self->{    cpqRackCommonEnclosureTempLocation},
+        $self->{cpqRackCommonEnclosureTempCurrent}, $self->{cpqRackCommonEnclosureTempThreshold});
+  }
+  if ($self->{runtime}->{options}->{perfdata} == 2) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => sprintf('temp_%s', $self->{name}),
+        value => $self->{cpqRackCommonEnclosureTempCurrent},
+        warning => $self->{cpqRackCommonEnclosureTempThreshold},
+        critical => $self->{cpqRackCommonEnclosureTempThreshold}
+    );
+  } elsif ($self->{runtime}->{options}->{perfdata} == 1) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => sprintf('temp_%s_%s', $self->{name}, $self->{location}),
+        value => $self->{degrees},
+        warning => $self->{threshold},
+        critical => $self->{threshold}
+    );
+  }
+  $self->add_extendedinfo(sprintf "temp_%s=%d", 
+      $self->{name}, $self->{cpqRackCommonEnclosureTempCurrent});
+
+}
+
+
+sub dump {
+  my $self = shift;
+  printf "[TEMP_%s]\n", $self->{name};
+  foreach (qw(cpqRackCommonEnclosureTempRack cpqRackCommonEnclosureTempChassis
+      cpqRackCommonEnclosureTempSensorIndex cpqRackCommonEnclosureTempSensorEnclosureName
+      cpqRackCommonEnclosureTempLocation
+      cpqRackCommonEnclosureTempCurrent cpqRackCommonEnclosureTempThreshold 
+      cpqRackCommonEnclosureTempCondition cpqRackCommonEnclosureTempType)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n\n", $self->{info};
+}
+
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::FuseSubsystem;
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    fuses => [],
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $oids = {
+      cpqRackCommonEnclosureFuseEntry => '1.3.6.1.4.1.232.22.2.3.1.4.1',
+      cpqRackCommonEnclosureFuseRack => '1.3.6.1.4.1.232.22.2.3.1.4.1.1',
+      cpqRackCommonEnclosureFuseChassis => '1.3.6.1.4.1.232.22.2.3.1.4.1.2',
+      cpqRackCommonEnclosureFuseIndex => '1.3.6.1.4.1.232.22.2.3.1.4.1.3',
+      cpqRackCommonEnclosureFuseEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.4.1.4',
+      cpqRackCommonEnclosureFuseLocation => '1.3.6.1.4.1.232.22.2.3.1.4.1.5',
+      cpqRackCommonEnclosureFusePresent => '1.3.6.1.4.1.232.22.2.3.1.4.1.8',
+      cpqRackCommonEnclosureFuseCondition => '1.3.6.1.4.1.232.22.2.3.1.4.1.11',
+      cpqRackCommonEnclosureFusePresentValue => {
+          1 => 'other',
+          2 => 'absent',
+          3 => 'present',
+      },
+      cpqRackCommonEnclosureFuseConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          4 => 'failed',
+      }
+  };
+  # INDEX { cpqRackCommonEnclosureFuseRack, cpqRackCommonEnclosureFuseChassis, cpqRackCommonEnclosureFuseIndex }
+  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureFuseEntry')) {
+    push(@{$self->{fuses}},
+        HP::BladeSystem::Component::CommonEnclosureSubsystem::FuseSubsystem::Fuse->new(%{$_}));
+  }
+
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{fuses}}) {
+    $_->check() if $_->{cpqRackCommonEnclosureFusePresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{fuses}}) {
+    $_->dump() if $_->{cpqRackCommonEnclosureFusePresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::FuseSubsystem::Fuse;
+
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem::FuseSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackCommonEnclosureFuse/, keys %params;
+  $self->{name} = $self->{cpqRackCommonEnclosureFuseRack}.':'.$self->{cpqRackCommonEnclosureFuseChassis}.':'.$self->{cpqRackCommonEnclosureFuseIndex};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('fu', $self->{name});
+  $self->add_info(sprintf 'fuse %s is %s, location is %s, condition is %s',
+      $self->{name}, $self->{cpqRackCommonEnclosureFusePresent},
+      $self->{cpqRackCommonEnclosureFuseLocation}, $self->{cpqRackCommonEnclosureFuseCondition});
+  if ($self->{cpqRackCommonEnclosureFuseCondition} eq 'failed') {
+    $self->add_message(CRITICAL, $self->{info});
+  } elsif ($self->{cpqRackCommonEnclosureFuseCondition} ne 'ok') {
+    $self->add_message(WARNING, $self->{info});
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[FUSE_%s]\n", $self->{name};
+  foreach (qw(cpqRackCommonEnclosureFuseRack cpqRackCommonEnclosureFuseChassis 
+      cpqRackCommonEnclosureFuseIndex cpqRackCommonEnclosureFuseEnclosureName 
+      cpqRackCommonEnclosureFuseLocation cpqRackCommonEnclosureFusePresent 
+      cpqRackCommonEnclosureFuseCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::ManagerSubsystem;
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    managers => [],
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $oids = {
+      cpqRackCommonEnclosureManagerEntry => '1.3.6.1.4.1.232.22.2.3.1.6.1',
+      cpqRackCommonEnclosureManagerRack => '1.3.6.1.4.1.232.22.2.3.1.6.1.1',
+      cpqRackCommonEnclosureManagerChassis => '1.3.6.1.4.1.232.22.2.3.1.6.1.2',
+      cpqRackCommonEnclosureManagerIndex => '1.3.6.1.4.1.232.22.2.3.1.6.1.3',
+      cpqRackCommonEnclosureManagerEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.6.1.4',
+      cpqRackCommonEnclosureManagerLocation => '1.3.6.1.4.1.232.22.2.3.1.6.1.5',
+      cpqRackCommonEnclosureManagerPartNumber => '1.3.6.1.4.1.232.22.2.3.1.6.1.6',
+      cpqRackCommonEnclosureManagerSparePartNumber => '1.3.6.1.4.1.232.22.2.3.1.6.1.7',
+      cpqRackCommonEnclosureManagerSerialNum => '1.3.6.1.4.1.232.22.2.3.1.6.1.8',
+      cpqRackCommonEnclosureManagerRole => '1.3.6.1.4.1.232.22.2.3.1.6.1.9',
+      cpqRackCommonEnclosureManagerPresent => '1.3.6.1.4.1.232.22.2.3.1.6.1.10',
+      cpqRackCommonEnclosureManagerRedundant => '1.3.6.1.4.1.232.22.2.3.1.6.1.11',
+      cpqRackCommonEnclosureManagerCondition => '1.3.6.1.4.1.232.22.2.3.1.6.1.12',
+      cpqRackCommonEnclosureManagerFWRev => '1.3.6.1.4.1.232.22.2.3.1.6.1.15',
+      cpqRackCommonEnclosureManagerRoleValue => {
+          1 => 'standby',
+          2 => 'active',
+      },
+      cpqRackCommonEnclosureManagerPresentValue => {
+          1 => 'other',
+          2 => 'absent', # mit vorsicht zu geniessen!
+          3 => 'present',
+      },
+      cpqRackCommonEnclosureManagerRedundantValue => {
+          0 => 'other', # meiner phantasie entsprungen, da sich hp nicht aeussert
+          1 => 'other',
+          2 => 'notRedundant',
+          3 => 'redundant',
+      },
+      cpqRackCommonEnclosureManagerConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      }
+  };
+  # INDEX { cpqRackCommonEnclosureManagerRack, cpqRackCommonEnclosureManagerChassis, cpqRackCommonEnclosureManagerIndex }
+  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureManagerEntry')) {
+    push(@{$self->{managers}},
+        HP::BladeSystem::Component::CommonEnclosureSubsystem::ManagerSubsystem::Manager->new(%{$_}));
+  }
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{managers}}) {
+    $_->check() if $_->{cpqRackCommonEnclosureManagerPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{managers}}) {
+    $_->dump() if $_->{cpqRackCommonEnclosureManagerPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+
+package HP::BladeSystem::Component::CommonEnclosureSubsystem::ManagerSubsystem::Manager;
+
+our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem::ManagerSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackCommonEnclosureManager/, keys %params;
+  $self->{name} = $self->{cpqRackCommonEnclosureManagerRack}.
+      ':'.$self->{cpqRackCommonEnclosureManagerChassis}.
+      ':'.$self->{cpqRackCommonEnclosureManagerIndex};
+  if ($self->{cpqRackCommonEnclosureManagerPresent} eq "absent" &&
+      defined $self->{cpqRackCommonEnclosureManagerEnclosureName}) {
+    $self->{cpqRackCommonEnclosureManagerPresent} = "present";
+  }
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('em', $self->{name});
+  my $info = sprintf 'manager %s is %s, location is %s, redundance is %s, condition is %s, role is %s',
+      $self->{name}, $self->{cpqRackCommonEnclosureManagerPresent},
+      $self->{cpqRackCommonEnclosureManagerLocation},
+      $self->{cpqRackCommonEnclosureManagerRedundant},
+      $self->{cpqRackCommonEnclosureManagerCondition},
+      $self->{cpqRackCommonEnclosureManagerRole};
+  $self->add_info($info) if $self->{cpqRackCommonEnclosureManagerPresent} eq 'present' ||
+      $self->{runtime}->{options}->{verbose} >= 3; # absent managers nur bei -vvv
+  if ($self->{cpqRackCommonEnclosureManagerCondition} eq 'degraded') {
+    $self->{info} .= sprintf ' (SparePartNum: %s)',
+        $self->{cpqRackCommonEnclosureManagerSparePartNumber};
+    $self->add_message(WARNING, $self->{info});
+  } elsif ($self->{cpqRackCommonEnclosureManagerCondition} eq 'failed') {
+    $self->{info} .= sprintf ' (SparePartNum: %s)',
+        $self->{cpqRackCommonEnclosureManagerSparePartNumber};
+    $self->add_message(CRITICAL, $self->{info});
+  }
+}
+
+sub dump {
+  my $self = shift;
+  printf "[ENCLOSURE_MANAGER_%s]\n", $self->{name};
+  foreach (qw(cpqRackCommonEnclosureManagerRack cpqRackCommonEnclosureManagerChassis 
+      cpqRackCommonEnclosureManagerIndex cpqRackCommonEnclosureManagerEnclosureName 
+      cpqRackCommonEnclosureManagerLocation cpqRackCommonEnclosureManagerPartNumber 
+      cpqRackCommonEnclosureManagerSparePartNumber cpqRackCommonEnclosureManagerPresent 
+      cpqRackCommonEnclosureManagerRedundant 
+      cpqRackCommonEnclosureManagerCondition cpqRackCommonEnclosureManagerFWRev)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "info: %s\n", $self->{info};
+  printf "\n";
+}
+
+
+package HP::BladeSystem::Component::PowerEnclosureSubsystem;
+our @ISA = qw(HP::BladeSystem::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    power_enclosures => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+
+# cpqRackPowerEnclosureTable
+  my $oids = {
+      cpqRackPowerEnclosureEntry => '1.3.6.1.4.1.232.22.2.3.3.1.1',
+      cpqRackPowerEnclosureRack => '1.3.6.1.4.1.232.22.2.3.3.1.1.1',
+      cpqRackPowerEnclosureIndex => '1.3.6.1.4.1.232.22.2.3.3.1.1.2',
+      cpqRackPowerEnclosureName => '1.3.6.1.4.1.232.22.2.3.3.1.1.3',
+      cpqRackPowerEnclosureMgmgtBoardSerialNum => '1.3.6.1.4.1.232.22.2.3.3.1.1.4',
+      cpqRackPowerEnclosureRedundant => '1.3.6.1.4.1.232.22.2.3.3.1.1.5',
+      cpqRackPowerEnclosureLoadBalanced => '1.3.6.1.4.1.232.22.2.3.3.1.1.6',
+      cpqRackPowerEnclosureInputPwrType => '1.3.6.1.4.1.232.22.2.3.3.1.1.7',
+      cpqRackPowerEnclosurePwrFeedMax => '1.3.6.1.4.1.232.22.2.3.3.1.1.8',
+      cpqRackPowerEnclosureCondition => '1.3.6.1.4.1.232.22.2.3.3.1.1.9',
+      cpqRackPowerEnclosureRedundantValue => {
+          1 => 'other',
+          2 => 'notRedundant',
+          3 => 'redundant',
+      },
+      cpqRackPowerEnclosureLoadBalancedValue => {
+          0 => 'aechz',
+          1 => 'other',
+          2 => 'notLoadBalanced',
+          3 => 'loadBalanced',
+      },
+      cpqRackPowerEnclosureInputPwrTypeValue => {
+          1 => 'other',
+          2 => 'singlePhase',
+          3 => 'threePhase',
+          4 => 'directCurrent',
+      },
+      cpqRackPowerEnclosureConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+      },
+  };
+  # INDEX { cpqRackPowerEnclosureRack, cpqRackPowerEnclosureIndex }
+  # dreckada dreck, dreckada
+  foreach ($self->get_entries($oids, 'cpqRackPowerEnclosureEntry')) {
+    push(@{$self->{power_enclosures}},
+        HP::BladeSystem::Component::PowerEnclosureSubsystem::PowerEnclosure->new(%{$_}));
+  }
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{power_enclosures}}) {
+    $_->check();
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{power_enclosures}}) {
+    $_->dump();
+  }
+}
+
+
+package HP::BladeSystem::Component::PowerEnclosureSubsystem::PowerEnclosure;
+our @ISA = qw(HP::BladeSystem::Component::PowerEnclosureSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackPowerEnclosure/, keys %params;
+  $self->{name} = $self->{cpqRackPowerEnclosureRack}.':'.$self->{cpqRackPowerEnclosureIndex};
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('pe', $self->{name});
+  my $info = sprintf 'power enclosure %s \'%s\' condition is %s',
+      $self->{name}, $self->{cpqRackPowerEnclosureName}, $self->{cpqRackPowerEnclosureCondition};
+  $self->add_info($info);
+  if ($self->{cpqRackPowerEnclosureCondition} eq 'degraded') {
+    $self->add_message(WARNING, $info);
+  } 
+} 
+  
+sub dump {
+  my $self = shift;
+    printf "[POWER_ENCLOSURE_%s]\n", $self->{cpqRackPowerEnclosureName};
+  foreach (qw(cpqRackPowerEnclosureRack cpqRackPowerEnclosureIndex 
+      cpqRackPowerEnclosureName cpqRackPowerEnclosureMgmgtBoardSerialNum
+      cpqRackPowerEnclosureRedundant cpqRackPowerEnclosureLoadBalanced
+      cpqRackPowerEnclosureInputPwrType cpqRackPowerEnclosurePwrFeedMax
+      cpqRackPowerEnclosureCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+
+package HP::BladeSystem::Component::PowerSupplySubsystem;
+our @ISA = qw(HP::BladeSystem::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    power_supplies => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $oids = {
+      cpqRackPowerSupplyEntry => '1.3.6.1.4.1.232.22.2.5.1.1.1',
+      cpqRackPowerSupplyRack => '1.3.6.1.4.1.232.22.2.5.1.1.1.1',
+      cpqRackPowerSupplyChassis => '1.3.6.1.4.1.232.22.2.5.1.1.1.2',
+      cpqRackPowerSupplyIndex => '1.3.6.1.4.1.232.22.2.5.1.1.1.3',
+      cpqRackPowerSupplyEnclosureName => '1.3.6.1.4.1.232.22.2.5.1.1.1.4',
+      cpqRackPowerSupplySerialNum => '1.3.6.1.4.1.232.22.2.5.1.1.1.5',
+      cpqRackPowerSupplySparePartNumber => '1.3.6.1.4.1.232.22.2.5.1.1.1.7',
+      cpqRackPowerSupplyFWRev => '1.3.6.1.4.1.232.22.2.5.1.1.1.8',
+      cpqRackPowerSupplyMaxPwrOutput => '1.3.6.1.4.1.232.22.2.5.1.1.1.9',
+      cpqRackPowerSupplyCurPwrOutput => '1.3.6.1.4.1.232.22.2.5.1.1.1.10',
+      cpqRackPowerSupplyIntakeTemp => '1.3.6.1.4.1.232.22.2.5.1.1.1.12',
+      cpqRackPowerSupplyExhaustTemp => '1.3.6.1.4.1.232.22.2.5.1.1.1.13',
+      cpqRackPowerSupplyStatus => '1.3.6.1.4.1.232.22.2.5.1.1.1.14',
+      cpqRackPowerSupplySupplyInputLineStatus => '1.3.6.1.4.1.232.22.2.5.1.1.1.15',
+      cpqRackPowerSupplyPresent => '1.3.6.1.4.1.232.22.2.5.1.1.1.16',
+      cpqRackPowerSupplyCondition => '1.3.6.1.4.1.232.22.2.5.1.1.1.17',
+      cpqRackPowerSupplySupplyInputLineStatusValue => {
+          1 => 'noError',
+          2 => 'lineOverVoltage',
+          3 => 'lineUnderVoltage',
+          4 => 'lineHit',
+          5 => 'brownOut',
+          6 => 'linePowerLoss',
+      },
+      cpqRackPowerSupplyStatusValue => {
+          1 => 'noError',
+          2 => 'generalFailure',
+          3 => 'bistFailure',
+          4 => 'fanFailure',
+          5 => 'tempFailure',
+          6 => 'interlockOpen',
+          7 => 'epromFailed',
+          8 => 'vrefFailed',
+          9 => 'dacFailed',
+          10 => 'ramTestFailed',
+          11 => 'voltageChannelFailed',
+          12 => 'orringdiodeFailed',
+          13 => 'brownOut',
+          14 => 'giveupOnStartup',
+          15 => 'nvramInvalid',
+          16 => 'calibrationTableInvalid',
+      },
+      cpqRackPowerSupplyPresentValue => {
+          1 => 'other',
+          2 => 'absent',
+          3 => 'present',
+      },
+      cpqRackPowerSupplyConditionValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+  };
+  # INDEX { cpqRackPowerSupplyRack, cpqRackPowerSupplyChassis, cpqRackPowerSupplyIndex }
+  foreach ($self->get_entries($oids, 'cpqRackPowerSupplyEntry')) {
+    push(@{$self->{power_supplies}},
+        HP::BladeSystem::Component::PowerSupplySubsystem::PowerSupply->new(%{$_}));
+  }
+}
+
+sub check {
+  my $self = shift;
+  my $total_current_watt = 0;
+  my $total_max_watt = 0;
+  my $total_in_temp = 0;
+  my $total_out_temp = 0;
+  my $num_ps = 0;
+  foreach (@{$self->{power_supplies}}) {
+    $_->check() if $_->{cpqRackPowerSupplyPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+    if ($_->{cpqRackPowerSupplyPresent} eq 'present') {
+      $total_max_watt += $_->{cpqRackPowerSupplyMaxPwrOutput};
+      $total_current_watt += $_->{cpqRackPowerSupplyCurPwrOutput};
+      $total_in_temp += $_->{cpqRackPowerSupplyIntakeTemp} 
+          if $_->{cpqRackPowerSupplyIntakeTemp} != -1;
+      $total_out_temp += $_->{cpqRackPowerSupplyExhaustTemp}
+          if $_->{cpqRackPowerSupplyExhaustTemp} != -1;
+      $num_ps++;
+    }
+  }
+  $self->{runtime}->{plugin}->add_perfdata(
+      label => 'watt_total',
+      value => $total_current_watt,
+      warning => $total_max_watt,
+      critical => $total_max_watt,
+  );
+  #$self->{runtime}->{plugin}->add_perfdata(
+  #    label => 'watt_total_pct',
+  #    value => ($total_current_watt == 0 ? 0 :
+  #        sprintf("%.2f",
+  #        ($total_current_watt / $total_max_watt * 100))),
+  #    warning => 100,
+  #    critical => 100,
+  #    uom => '%',
+  #);
+  if ($total_in_temp) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => 'in_temp',
+        value => $total_in_temp / $num_ps,
+    );
+  }
+  if ($total_out_temp) {
+    $self->{runtime}->{plugin}->add_perfdata(
+        label => 'out_temp',
+        value => $total_out_temp / $num_ps,
+    );
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{power_supplies}}) {
+    $_->dump() if $_->{cpqRackPowerSupplyPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+
+package HP::BladeSystem::Component::PowerSupplySubsystem::PowerSupply;
+our @ISA = qw(HP::BladeSystem::Component::PowerSupplySubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackPowerSupply/, keys %params;
+  $self->{name} = $params{cpqRackPowerSupplyRack}.
+      ':'.$params{cpqRackPowerSupplyChassis}.
+      ':'.$params{cpqRackPowerSupplyIndex};
+  $self->{serfw} = sprintf "Ser: %s, FW: %s", $self->{cpqRackPowerSupplySerialNum}, $self->{cpqRackPowerSupplyFWRev};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('ps', $self->{name});
+  my $info = sprintf 'power supply %s is %s, condition is %s (%s)', 
+      $self->{name}, $self->{cpqRackPowerSupplyPresent},
+      $self->{cpqRackPowerSupplyCondition}, $self->{serfw};
+  $self->add_info($info);
+  if ($self->{cpqRackPowerSupplyPresent} eq 'present') {
+    if ($self->{cpqRackPowerSupplyCondition} eq 'degraded') {
+      $info .= sprintf " (SparePartNum %s)", $self->{cpqRackPowerSupplySparePartNumber};
+      $self->add_message(WARNING, $info);
+      $self->add_info(sprintf 'power supply %s status is %s, inp.line status is %s',
+          $self->{name}, $self->{cpqRackPowerSupplyStatus},
+          $self->{cpqRackPowerSupplySupplyInputLineStatus});
+    } elsif ($self->{cpqRackPowerSupplyCondition} eq 'failed') {
+      $info .= sprintf " (SparePartNum %s)", $self->{cpqRackPowerSupplySparePartNumber};
+      $self->add_message(CRITICAL, $info);
+      $self->add_info(sprintf 'power supply %s status is %s, inp.line status is %s',
+          $self->{name}, $self->{cpqRackPowerSupplyStatus},
+          $self->{cpqRackPowerSupplySupplyInputLineStatus});
+    } 
+    if ($self->{runtime}->{options}->{perfdata} != 2) {
+      $self->{runtime}->{plugin}->add_perfdata(
+          label => sprintf('watt_%s', $self->{name}),
+          value => $self->{cpqRackPowerSupplyCurPwrOutput},
+          warning => $self->{cpqRackPowerSupplyMaxPwrOutput},
+          critical => $self->{cpqRackPowerSupplyMaxPwrOutput}
+      );
+      #$self->{runtime}->{plugin}->add_perfdata(
+      #    label => sprintf('watt_pct_%s', $self->{name}),
+      #    value => ($self->{cpqRackPowerSupplyCurPwrOutput} == 0 ? 0 :
+      #        sprintf ("%.2f",
+      #        ($self->{cpqRackPowerSupplyCurPwrOutput} /
+      #        $self->{cpqRackPowerSupplyMaxPwrOutput} * 100))),
+      #    warning => 100,
+      #    critical => 100,
+      #    uom => '%',
+      #);
+      if ($self->{cpqRackPowerSupplyIntakeTemp} != -1) {
+        $self->{runtime}->{plugin}->add_perfdata(
+            label => sprintf('in_temp_%s', $self->{name}),
+            value => $self->{cpqRackPowerSupplyIntakeTemp},
+        );
+      }
+      if ($self->{cpqRackPowerSupplyExhaustTemp} != -1) {
+        $self->{runtime}->{plugin}->add_perfdata(
+            label => sprintf('out_temp_%s', $self->{name}),
+            value => $self->{cpqRackPowerSupplyExhaustTemp},
+        );
+      }
+    }
+  }
+} 
+  
+sub dump {
+  my $self = shift;
+    printf "[POWER_SUPPLY%s]\n", $self->{name};
+  foreach (qw(cpqRackPowerSupplyRack cpqRackPowerSupplyChassis cpqRackPowerSupplyIndex cpqRackPowerSupplyEnclosureName cpqRackPowerSupplySerialNum cpqRackPowerSupplySparePartNumber cpqRackPowerSupplyFWRev cpqRackPowerSupplyMaxPwrOutput cpqRackPowerSupplyCurPwrOutput cpqRackPowerSupplyIntakeTemp cpqRackPowerSupplyExhaustTemp cpqRackPowerSupplyStatus cpqRackPowerSupplySupplyInputLineStatus cpqRackPowerSupplyPresent cpqRackPowerSupplyCondition)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+
+package HP::BladeSystem::Component::NetConnectorSubsystem;
+our @ISA = qw(HP::BladeSystem::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    net_connectors => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $oids = {
+      cpqRackNetConnectorEntry => '1.3.6.1.4.1.232.22.2.6.1.1.1',
+      cpqRackNetConnectorRack => '1.3.6.1.4.1.232.22.2.6.1.1.1.1',
+      cpqRackNetConnectorChassis => '1.3.6.1.4.1.232.22.2.6.1.1.1.2',
+      cpqRackNetConnectorIndex => '1.3.6.1.4.1.232.22.2.6.1.1.1.3',
+      cpqRackNetConnectorEnclosureName => '1.3.6.1.4.1.232.22.2.6.1.1.1.4',
+      cpqRackNetConnectorName => '1.3.6.1.4.1.232.22.2.6.1.1.1.5',
+      cpqRackNetConnectorModel => '1.3.6.1.4.1.232.22.2.6.1.1.1.6',
+      cpqRackNetConnectorSerialNum => '1.3.6.1.4.1.232.22.2.6.1.1.1.7',
+      cpqRackNetConnectorPartNumber => '1.3.6.1.4.1.232.22.2.6.1.1.1.8',
+      cpqRackNetConnectorSparePartNumber => '1.3.6.1.4.1.232.22.2.6.1.1.1.9',
+      cpqRackNetConnectorFWRev => '1.3.6.1.4.1.232.22.2.6.1.1.1.10',
+      cpqRackNetConnectorType => '1.3.6.1.4.1.232.22.2.6.1.1.1.11',
+      cpqRackNetConnectorLocation => '1.3.6.1.4.1.232.22.2.6.1.1.1.12',
+      cpqRackNetConnectorPresent => '1.3.6.1.4.1.232.22.2.6.1.1.1.13',
+      cpqRackNetConnectorHasFuses => '1.3.6.1.4.1.232.22.2.6.1.1.1.14',
+      cpqRackNetConnectorEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.6.1.1.1.15',
+      cpqRackNetConnectorTypeValue => {
+          0 => 'other', # undefined
+          1 => 'other',
+          2 => 'active',
+          3 => 'passive',
+      },
+      cpqRackNetConnectorPresentValue => {
+          1 => 'other',
+          2 => 'absent',
+          3 => 'present',
+      },
+      cpqRackNetConnectorHasFusesValue => {
+          -1 => 'false', # wird geliefert, also vermute ich false
+          1 => 'false',
+          2 => 'true',
+      },
+  };
+  # INDEX { cpqRackNetConnectorRack, cpqRackNetConnectorChassis, cpqRackNetConnectorIndex }
+  # dreckada dreck, dreckada
+  foreach ($self->get_entries($oids, 'cpqRackNetConnectorEntry')) {
+    push(@{$self->{net_connectors}},
+        HP::BladeSystem::Component::NetConnectorSubsystem::NetConnector->new(%{$_}));
+  }
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{net_connectors}}) {
+    $_->check() if $_->{cpqRackNetConnectorPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{net_connectors}}) {
+    $_->dump() if $_->{cpqRackNetConnectorPresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent nur bei -vvv
+  }
+}
+
+
+package HP::BladeSystem::Component::NetConnectorSubsystem::NetConnector;
+our @ISA = qw(HP::BladeSystem::Component::NetConnectorSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackNetConnector/, keys %params;
+  $self->{name} = $params{cpqRackNetConnectorRack}.
+      ':'.$params{cpqRackNetConnectorChassis}.
+      ':'.$params{cpqRackNetConnectorIndex};
+  $self->{serfw} = sprintf "Ser: %s, FW: %s", $self->{cpqRackNetConnectorSerialNum}, $self->{cpqRackNetConnectorFWRev};
+  bless $self, $class;
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('nc', $self->{name});
+  my $info = sprintf 'net connector %s is %s, model is %s (%s)', 
+      $self->{name}.($self->{cpqRackNetConnectorName} ? ' \''.$self->{cpqRackNetConnectorName}.'\'' : ''),
+      $self->{cpqRackNetConnectorPresent}, $self->{cpqRackNetConnectorModel}, $self->{serfw};
+  $self->add_info($info);
+  # hat weder status noch condition, vielleicht spaeter mal
+  $info .= sprintf " (SparePartNum %s)", $self->{cpqRackNetConnectorSparePartNumber};
+} 
+  
+sub dump {
+  my $self = shift;
+    printf "[NET_CONNECTOR_%s]\n", $self->{cpqRackNetConnectorName};
+  foreach (qw(cpqRackNetConnectorRack cpqRackNetConnectorChassis cpqRackNetConnectorIndex cpqRackNetConnectorEnclosureName cpqRackNetConnectorName cpqRackNetConnectorModel cpqRackNetConnectorSerialNum cpqRackNetConnectorPartNumber cpqRackNetConnectorSparePartNumber cpqRackNetConnectorFWRev cpqRackNetConnectorType cpqRackNetConnectorLocation cpqRackNetConnectorPresent cpqRackNetConnectorHasFuses cpqRackNetConnectorEnclosureSerialNum)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+
+package HP::BladeSystem::Component::ServerBladeSubsystem;
+our @ISA = qw(HP::BladeSystem::Component);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    server_blades => [],
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub init {
+  my $self = shift;
+  my $oids = {
+      cpqRackServerBladeEntry => '1.3.6.1.4.1.232.22.2.4.1.1.1',
+      cpqRackServerBladeRack => '1.3.6.1.4.1.232.22.2.4.1.1.1.1',
+      cpqRackServerBladeChassis => '1.3.6.1.4.1.232.22.2.4.1.1.1.2',
+      cpqRackServerBladeIndex => '1.3.6.1.4.1.232.22.2.4.1.1.1.3',
+      cpqRackServerBladeName => '1.3.6.1.4.1.232.22.2.4.1.1.1.4',
+      cpqRackServerBladeEnclosureName => '1.3.6.1.4.1.232.22.2.4.1.1.1.5',
+      cpqRackServerBladePartNumber => '1.3.6.1.4.1.232.22.2.4.1.1.1.6',
+      cpqRackServerBladeSparePartNumber => '1.3.6.1.4.1.232.22.2.4.1.1.1.7',
+      cpqRackServerBladePosition => '1.3.6.1.4.1.232.22.2.4.1.1.1.8',
+      cpqRackServerBladeHeight => '1.3.6.1.4.1.232.22.2.4.1.1.1.9',
+      cpqRackServerBladeWidth => '1.3.6.1.4.1.232.22.2.4.1.1.1.10',
+      cpqRackServerBladeDepth => '1.3.6.1.4.1.232.22.2.4.1.1.1.11',
+      cpqRackServerBladePresent => '1.3.6.1.4.1.232.22.2.4.1.1.1.12',
+      cpqRackServerBladeHasFuses => '1.3.6.1.4.1.232.22.2.4.1.1.1.13',
+      cpqRackServerBladeEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.4.1.1.1.14',
+      cpqRackServerBladeSlotsUsed => '1.3.6.1.4.1.232.22.2.4.1.1.1.15',
+      cpqRackServerBladeStatus => '1.3.6.1.4.1.232.22.2.4.1.1.1.21',
+      cpqRackServerBladeDiagnosticString => '1.3.6.1.4.1.232.22.2.4.1.1.1.24',
+      cpqRackServerBladePowered => '1.3.6.1.4.1.232.22.2.4.1.1.1.25',
+      cpqRackServerBladePOSTStatus => '1.3.6.1.4.1.232.22.2.4.1.1.1.35',
+      cpqRackServerBladePresentValue => {
+          1 => 'other',
+          2 => 'absent',
+          3 => 'present',
+      },
+      cpqRackServerBladeStatusValue => {
+          1 => 'other',
+          2 => 'ok',
+          3 => 'degraded',
+          4 => 'failed',
+      },
+      cpqRackServerBladePoweredValue => {
+          0 => 'aechz',
+          1 => 'other',
+          2 => 'on',
+          3 => 'off',
+          4 => 'powerStagedOff',
+          5 => 'reboot',
+      },
+      cpqRackServerBladePOSTStatusValue => {
+          1 => 'other',
+          2 => 'started',
+          3 => 'completed',
+          4 => 'failed',
+      },
+  };
+  # INDEX { cpqRackServerBladeRack, cpqRackServerBladeChassis, cpqRackServerBladeIndex }
+  # dreckada dreck, dreckada
+  foreach ($self->get_entries($oids, 'cpqRackServerBladeEntry')) {
+    push(@{$self->{server_blades}},
+        HP::BladeSystem::Component::ServerBladeSubsystem::ServerBlade->new(%{$_}));
+  }
+}
+
+sub check {
+  my $self = shift;
+  foreach (@{$self->{server_blades}}) {
+    $_->check() if $_->{cpqRackServerBladePresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent blades nur bei -vvv
+  }
+}
+
+sub dump {
+  my $self = shift;
+  foreach (@{$self->{server_blades}}) {
+    $_->dump() if $_->{cpqRackServerBladePresent} eq 'present' ||
+        $self->{runtime}->{options}->{verbose} >= 3; # absent blades nur bei -vvv
+  }
+}
+
+
+package HP::BladeSystem::Component::ServerBladeSubsystem::ServerBlade;
+our @ISA = qw(HP::BladeSystem::Component::ServerBladeSubsystem);
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    rawdata => $params{rawdata},
+    method => $params{method},
+    blacklisted => 0,
+    info => undef,
+    extendedinfo => undef,
+  };
+  map { $self->{$_} = $params{$_} } grep /cpqRackServerBlade/, keys %params;
+  $self->{cpqRackServerBladeDiagnosticString} ||= '';
+  $self->{name} = $self->{cpqRackServerBladeRack}.
+      ':'.$self->{cpqRackServerBladeChassis}.
+      ':'.$self->{cpqRackServerBladeIndex};
+  bless $self, $class;
+  $self->init();
+  return $self;
+}
+
+sub check {
+  my $self = shift;
+  $self->blacklist('sb', $self->{name});
+  my $info = sprintf 'server blade %s \'%s\' is %s, status is %s, powered is %s',
+      $self->{name}, $self->{cpqRackServerBladeName}, $self->{cpqRackServerBladePresent},
+      $self->{cpqRackServerBladeStatus}, $self->{cpqRackServerBladePowered};
+  $self->add_info($info);
+  if ($self->{cpqRackServerBladePowered} eq 'on') {
+    if ($self->{cpqRackServerBladeCondition} eq 'degraded') {
+      $self->add_message(WARNING, sprintf 'server blade %s diag is \'%s\', post status is %s',
+          $self->{cpqRackServerBladeName}, $self->{cpqRackServerBladeDiagnosticString},
+          $self->{cpqRackServerBladePOSTStatus});
+    } elsif ($self->{cpqRackServerBladeCondition} eq 'failed') {
+      $self->add_message(CRITICAL, sprintf 'server blade %s diag is \'%s\', post status is %s',
+          $self->{cpqRackServerBladeName}, $self->{cpqRackServerBladeDiagnosticString},
+          $self->{cpqRackServerBladePOSTStatus});
+    } 
+  }
+} 
+  
+sub dump {
+  my $self = shift;
+    printf "[SERVER_BLADE_%s]\n", $self->{cpqRackServerBladeName};
+  foreach (qw(cpqRackServerBladeRack cpqRackServerBladeChassis cpqRackServerBladeIndex cpqRackServerBladeName cpqRackServerBladeEnclosureName cpqRackServerBladePartNumber cpqRackServerBladeSparePartNumber cpqRackServerBladePosition cpqRackServerBladeHeight cpqRackServerBladeWidth cpqRackServerBladeDepth cpqRackServerBladePresent cpqRackServerBladeHasFuses cpqRackServerBladeEnclosureSerialNum cpqRackServerBladeSlotsUsed cpqRackServerBladeStatus cpqRackServerBladeDiagnosticString cpqRackServerBladePowered cpqRackServerBladePOSTStatus)) {
+    printf "%s: %s\n", $_, $self->{$_};
+  }
+  printf "\n";
+}
+
+
+
+package HP::BladeSystem::Component;
+
+use strict;
+
+our @ISA = qw(HP::BladeSystem);
+
+
+package HP::BladeSystem;
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+use Data::Dumper;
+
+our @ISA = qw(HP::Server HP::Proliant::Component::SNMP);
+
+sub init {
+  my $self = shift;
+  $self->{components} = {
+      common_enclosure_subsystem => undef,
+      power_enclosure_subsystem => undef,
+      power_supply_subsystem => undef,
+      net_connector_subsystem => undef,
+      server_blade_subsystem => undef,
+  };
+  $self->{serial} = 'unknown';
+  $self->{product} = 'unknown';
+  $self->{romversion} = 'unknown';
+  $self->trace(3, 'BladeSystem identified');
+  $self->collect();
+  if (! $self->{runtime}->{plugin}->check_messages()) {
+    $self->set_serial();
+    $self->analyze_common_enclosures();
+    $self->analyze_power_enclosures();
+    $self->analyze_power_supplies();
+    $self->analyze_net_connectors();
+    $self->analyze_server_blades();
+    $self->check_common_enclosures();
+    $self->check_power_enclosures();
+    $self->check_power_supplies();
+    $self->check_net_connectors();
+    $self->check_server_blades();
+  }
+}
+
+sub identify {
+  my $self = shift;
+  return sprintf "System: '%s', S/N: '%s'",
+      $self->{product}, $self->{serial};
+}
+
+sub dump {
+  my $self = shift;
+  printf STDERR "serial %s\n", $self->{serial};
+  printf STDERR "product %s\n", $self->{product};
+  printf STDERR "romversion %s\n", $self->{romversion};
+  printf STDERR "%s\n", Data::Dumper::Dumper($self->{enclosures});
+}
+
+sub analyze_common_enclosures {
+  my $self = shift;
+  $self->{components}->{common_enclosure_subsystem} =
+      HP::BladeSystem::Component::CommonEnclosureSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_power_enclosures {
+  my $self = shift;
+  $self->{components}->{power_enclosure_subsystem} =
+      HP::BladeSystem::Component::PowerEnclosureSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_power_supplies {
+  my $self = shift;
+  $self->{components}->{power_supply_subsystem} =
+      HP::BladeSystem::Component::PowerSupplySubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_net_connectors {
+  my $self = shift;
+  $self->{components}->{net_connector_subsystem} =
+      HP::BladeSystem::Component::NetConnectorSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_server_blades {
+  my $self = shift;
+  $self->{components}->{server_blade_subsystem} =
+      HP::BladeSystem::Component::ServerBladeSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub check_common_enclosures {
+  my $self = shift;
+  $self->{components}->{common_enclosure_subsystem}->check();
+  $self->{components}->{common_enclosure_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_power_enclosures {
+  my $self = shift;
+  $self->{components}->{power_enclosure_subsystem}->check();
+  $self->{components}->{power_enclosure_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_power_supplies {
+  my $self = shift;
+  $self->{components}->{power_supply_subsystem}->check();
+  $self->{components}->{power_supply_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_net_connectors {
+  my $self = shift;
+  $self->{components}->{net_connector_subsystem}->check();
+  $self->{components}->{net_connector_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_server_blades {
+  my $self = shift;
+  $self->{components}->{server_blade_subsystem}->check();
+  $self->{components}->{server_blade_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub collect {
+  my $self = shift;
+  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
+    my $cpqRackMibCondition = '1.3.6.1.4.1.232.22.1.3.0';
+    $self->trace(3, 'getting cpqRackMibCondition');
+    if (! exists $self->{rawdata}->{$cpqRackMibCondition}) {
+        $self->add_message(CRITICAL,
+            'snmpwalk returns no health data (cpqrack-mib)');
+    }
+  } else {
+    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
+    #$params{'-translate'} = [
+    #  -all => 0x0
+    #];
+    my ($session, $error) =
+        Net::SNMP->session(%{$self->{runtime}->{snmpparams}});
+    if (! defined $session) {
+      $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
+      $self->trace(1, Data::Dumper::Dumper($self->{runtime}->{snmpparams}));
+    } else {
+      # revMajor is often used for discovery of hp devices
+      my $cpqSeMibRev = '1.3.6.1.4.1.232.22.1';
+      my $cpqSeMibRevMajor = '1.3.6.1.4.1.232.22.1.1.0';
+      my $cpqRackMibCondition = '1.3.6.1.4.1.232.22.1.3.0';
+      $self->trace(3, 'getting cpqRackMibCondition');
+      my $result = $session->get_request(
+          -varbindlist => [$cpqRackMibCondition]
+      );
+      if (!defined($result) ||
+          $result->{$cpqRackMibCondition} eq 'noSuchInstance' ||
+          $result->{$cpqRackMibCondition} eq 'noSuchObject' ||
+          $result->{$cpqRackMibCondition} eq 'endOfMibView') {
+        $self->add_message(CRITICAL,
+            'snmpwalk returns no health data (cpqrack-mib)');
+        $session->close;
+      } else {
+        $self->trace(3, 'getting cpqRackMibCondition done');
+      }
+    }
+    if (! $self->{runtime}->{plugin}->check_messages()) {
+      # snmp peer is alive
+      $self->trace(2, sprintf "Protocol is %s",
+          $self->{runtime}->{snmpparams}->{'-version'});
+      my $oidtrees = [
+          ["cpqSiComponent", "1.3.6.1.4.1.232.2.2"],
+          ["cpqSiAsset", "1.3.6.1.4.1.232.2.2.2"],
+          #["cpqRackInfo", "1.3.6.1.4.1.232.22"],
+          ['cpqRackCommonEnclosureEntry', '1.3.6.1.4.1.232.22.2.3.1.1.1'],
+          ['cpqRackCommonEnclosureTempEntry', '1.3.6.1.4.1.232.22.2.3.1.2.1'],
+          ['cpqRackCommonEnclosureFanEntry', '1.3.6.1.4.1.232.22.2.3.1.3.1'],
+          ['cpqRackCommonEnclosureFuseEntry', '1.3.6.1.4.1.232.22.2.3.1.4.1'],
+          ['cpqRackCommonEnclosureManagerEntry', '1.3.6.1.4.1.232.22.2.3.1.6.1'],
+          ['cpqRackPowerEnclosureEntry', '1.3.6.1.4.1.232.22.2.3.3.1.1'],
+          ['cpqRackServerBladeEntry', '1.3.6.1.4.1.232.22.2.4.1.1.1'],
+          ['cpqRackPowerSupplyEntry', '1.3.6.1.4.1.232.22.2.5.1.1.1'],
+          ['cpqRackNetConnectorEntry', '1.3.6.1.4.1.232.22.2.6.1.1.1'],
+          ['cpqRackMibCondition', '1.3.6.1.4.1.232.22.1.3.0'],
+      ];
+      my $cpqSiComponent = "1.3.6.1.4.1.232.2.2";
+      my $cpqSiAsset = "1.3.6.1.4.1.232.2.2.2";
+      my $cpqRackInfo = "1.3.6.1.4.1.232.22";
+      $session->translate;
+      my $response = {}; #break the walk up in smaller pieces
+      foreach my $subtree (@{$oidtrees}) {
+          my $tic = time; my $tac = $tic;
+          my $response0 = $session->get_table(
+              -maxrepetitions => 1,
+              -baseoid => $subtree->[1]);
+          if (scalar (keys %{$response0}) == 0) {
+            $self->trace(2, sprintf "maxrepetitions failed. fallback");
+            $response0 = $session->get_table(
+                -baseoid => $subtree->[1]);
+          }
+          $tac = time;
+          $self->trace(2, sprintf "%03d seconds for walk %s (%d oids)",
+              $tac - $tic, $subtree->[0], scalar(keys %{$response0}));
+          map { $response->{$_} = $response0->{$_} } keys %{$response0};
+      }
+      $session->close;
+      map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
+          keys %$response;
+      $self->{rawdata} = $response;
+    }
+  }
+  return $self->{runtime}->{plugin}->check_messages();
+}
+
+sub set_serial {
+  my $self = shift;
+
+  my $cpqSiSysSerialNum = "1.3.6.1.4.1.232.2.2.2.1.0";
+  my $cpqSiProductName = "1.3.6.1.4.1.232.2.2.4.2.0";
+
+  $self->{serial} =
+      SNMP::Utils::get_object($self->{rawdata}, $cpqSiSysSerialNum);
+  $self->{product} =
+      SNMP::Utils::get_object($self->{rawdata}, $cpqSiProductName);
+  $self->{serial} = $self->{serial};
+  $self->{product} = lc $self->{product};
+  $self->{romversion} = 'unknown';
+#####################################################################
+$self->{runtime}->{product} = $self->{product};
+}
+
+package HP::Storage;
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+use Data::Dumper;
+
+our @ISA = qw(HP::Server);
+
+sub init {
+  my $self = shift;
+  $self->{components} = {
+      powersupply_subsystem => undef,
+      fan_subsystem => undef,
+      temperature_subsystem => undef,
+      cpu_subsystem => undef,
+      memory_subsystem => undef,
+      disk_subsystem => undef,
+      sensor_subsystem => undef,
+  };
+  $self->{serial} = 'unknown';
+  $self->{product} = 'unknown';
+  $self->{romversion} = 'unknown';
+  $self->collect();
+  if (! $self->{runtime}->{plugin}->check_messages()) {
+    $self->set_serial();
+#    $self->check_for_buggy_firmware();
+#    $self->analyze_cpus();
+#    $self->analyze_powersupplies();
+#    $self->analyze_fan_subsystem();
+#    $self->analyze_temperatures();
+#    $self->analyze_memory_subsystem();
+    $self->analyze_disk_subsystem();
+##    $self->analyze_sensor_subsystem();
+#    $self->check_cpus();
+#    $self->check_powersupplies();
+#    $self->check_fan_subsystem();
+#    $self->check_temperatures();
+#    $self->check_memory_subsystem();
+    $self->check_disk_subsystem();
+##    $self->check_sensor_subsystem();
+  }
+}
+
+sub identify {
+  my $self = shift;
+  return sprintf "System: '%s', S/N: '%s', ROM: '%s'", 
+      $self->{product}, $self->{serial}, $self->{romversion};
+}
+
+sub check_for_buggy_firmware {
+  my $self = shift;
+  my @buggyfirmwares = (
+      "P24 12/11/2001",
+      "P24 11/15/2002",
+      "D13 06/03/2003",
+      "D13 09/15/2004",
+      "P20 12/17/2002"
+  );
+  $self->{runtime}->{options}->{buggy_firmware} =
+      grep /^$self->{romversion}/, @buggyfirmwares;
+}
+
+sub dump {
+  my $self = shift;
+  printf STDERR "serial %s\n", $self->{serial};
+  printf STDERR "product %s\n", $self->{product};
+  printf STDERR "romversion %s\n", $self->{romversion};
+  printf STDERR "%s\n", Data::Dumper::Dumper($self->{components});
+}
+
+sub analyze_powersupplies {
+  my $self = shift;
+  $self->{components}->{powersupply_subsystem} =
+      HP::Storage::Component::PowersupplySubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_fan_subsystem {
+  my $self = shift;
+  $self->{components}->{fan_subsystem} = 
+      HP::Storage::Component::FanSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_temperatures {
+  my $self = shift;
+  $self->{components}->{temperature_subsystem} = 
+      HP::Storage::Component::TemperatureSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_cpus {
+  my $self = shift;
+  $self->{components}->{cpu_subsystem} =
+      HP::Storage::Component::CpuSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_memory_subsystem {
+  my $self = shift;
+  $self->{components}->{memory_subsystem} = 
+      HP::Storage::Component::MemorySubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_disk_subsystem {
+  my $self = shift;
+  $self->{components}->{disk_subsystem} =
+      HP::Proliant::Component::DiskSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub analyze_sensor_subsystem {
+  my $self = shift;
+  $self->{components}->{sensor_subsystem} =
+      HP::FCMGMT::Component::SensorSubsystem->new(
+    rawdata => $self->{rawdata},
+    method => $self->{method},
+    runtime => $self->{runtime},
+  );
+}
+
+sub check_cpus {
+  my $self = shift;
+  $self->{components}->{cpu_subsystem}->check();
+  $self->{components}->{cpu_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_powersupplies {
+  my $self = shift;
+  $self->{components}->{powersupply_subsystem}->check();
+  $self->{components}->{powersupply_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_fan_subsystem {
+  my $self = shift;
+  $self->{components}->{fan_subsystem}->check();
+  $self->{components}->{fan_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_temperatures {
+  my $self = shift;
+  $self->{components}->{temperature_subsystem}->check();
+  $self->{components}->{temperature_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_memory_subsystem {
+  my $self = shift;
+  $self->{components}->{memory_subsystem}->check();
+  $self->{components}->{memory_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 2;
+}
+
+sub check_disk_subsystem {
+  my $self = shift;
+  $self->{components}->{disk_subsystem}->check();
+  $self->{components}->{disk_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 1;
+}
+
+sub check_sensor_subsystem {
+  my $self = shift;
+  $self->{components}->{isensor_subsystem}->check();
+  $self->{components}->{sensor_subsystem}->dump()
+      if $self->{runtime}->{options}->{verbose} >= 1;
+}
+
+
+sub collect {
+  my $self = shift;
+  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
+    my $cpqSeMibCondition = '1.3.6.1.4.1.232.6.1.3.0';
+    # rindsarsch!
+    $self->{rawdata}->{$cpqSeMibCondition} = 0;
+    if (! exists $self->{rawdata}->{$cpqSeMibCondition}) {
+        $self->add_message(CRITICAL,
+            'snmpwalk returns no health data (cpqhlth-mib)');
+    }
+  } else {
+    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
+    #$params{'-translate'} = [
+    #  -all => 0x0
+    #];
+    my ($session, $error) = 
+        Net::SNMP->session(%{$self->{runtime}->{snmpparams}});
+    if (! defined $session) {
+      $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
+      $self->trace(1, Data::Dumper::Dumper($self->{runtime}->{snmpparams}));
+    } else {
+      # revMajor is often used for discovery of hp devices
+      my $cpqSeMibRev = '1.3.6.1.4.1.232.6.1';
+      my $cpqSeMibRevMajor = '1.3.6.1.4.1.232.6.1.1.0';
+      my $cpqSeMibCondition = '1.3.6.1.4.1.232.6.1.3.0';
+      my $result = $session->get_request(
+          -varbindlist => [$cpqSeMibCondition]
+      );
+      # rindsarsch!
+      $result->{$cpqSeMibCondition} = 0;
+      if (!defined($result) || 
+          $result->{$cpqSeMibCondition} eq 'noSuchInstance' ||
+          $result->{$cpqSeMibCondition} eq 'noSuchObject' ||
+          $result->{$cpqSeMibCondition} eq 'endOfMibView') {
+        $self->add_message(CRITICAL,
+            'snmpwalk returns no health data (cpqhlth-mib)');
+        $session->close;
+      } else {
+        # this is not reliable. many agents return 4=failed
+        #if ($result->{$cpqSeMibCondition} != 2) {
+        #  $obstacle = "cmapeerstart";
+        #}
+      }
+    }
+    if (! $self->{runtime}->{plugin}->check_messages()) {
+      # snmp peer is alive
+      $self->trace(2, sprintf "Protocol is %s", 
+          $self->{runtime}->{snmpparams}->{'-version'});
+      my $cpqSsSys =  "1.3.6.1.4.1.232.8";
+      $session->translate;
+      my $response = {}; #break the walk up in smaller pieces
+      my $tic = time; my $tac = $tic;
+      my $response1 = $session->get_table(
+          -baseoid => $cpqSsSys);
+      $tac = time;
+      $self->trace(2, sprintf "%03d seconds for walk cpqSsSys (%d oids)",
+          $tac - $tic, scalar(keys %{$response1}));
+      $session->close;
+      map { $response->{$_} = $response1->{$_} } keys %{$response1};
+      map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
+          keys %$response;
+      $self->{rawdata} = $response;
+    }
+  }
+  return $self->{runtime}->{plugin}->check_messages();
+}
+
+sub set_serial {
+  my $self = shift;
+  my $snmpwalk = $self->{rawdata};
+  my @serials = ();
+  my @models = ();
+  my @fws = ();
+  my $cpqSsBackplaneEntry = '1.3.6.1.4.1.232.8.2.2.6.1';
+  my $cpqSsBackplaneFWRev = '1.3.6.1.4.1.232.8.2.2.6.1.3';
+  my $cpqSsBackplaneModel = '1.3.6.1.4.1.232.8.2.2.6.1.9';
+  my $cpqSsBackplaneSerialNumber = '1.3.6.1.4.1.232.8.2.2.6.1.13';
+  # INDEX { cpqSsBackplaneChassisIndex, cpqSsBackplaneIndex }
+  my @indexes = SNMP::Utils::get_indices($snmpwalk,
+      $cpqSsBackplaneEntry);
+  foreach (@indexes) {
+    my($idx1, $idx2) = ($_->[0], $_->[1]);
+    my $fw = SNMP::Utils::get_object($snmpwalk,
+        $cpqSsBackplaneFWRev, $idx1, $idx2);
+    my $model = SNMP::Utils::get_object($snmpwalk,
+        $cpqSsBackplaneModel, $idx1, $idx2);
+    my $serial = SNMP::Utils::get_object($snmpwalk,
+        $cpqSsBackplaneSerialNumber, $idx1, $idx2);
+    push(@serials, $serial);
+    push(@models, $model);
+    push(@fws, $fw);
+  }
+  
+  $self->{serial} = join('/', @serials);
+  $self->{product} = join('/', @models);
+  $self->{romversion} = join('/', @fws);
+  $self->{runtime}->{product} = $self->{product};
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+package HP::Server;
+
+use strict;
+use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };
+
+sub new {
+  my $class = shift;
+  my %params = @_;
+  my $self = {
+    runtime => $params{runtime},
+    productname => 'unknown',
+  };
+  bless $self, $class;
+  if (! ($self->{runtime}->{plugin}->opts->hostname ||
+      $self->{runtime}->{plugin}->opts->snmpwalk)) {
+    bless $self, 'HP::Proliant::CLI';
+    $self->{method} = 'cli';
+  } else {
+    $self->check_snmp_and_model();
+    if ($self->{runtime}->{options}->{servertype}) {
+      $self->{productname} = 'ProLiant' if 
+          $self->{runtime}->{options}->{servertype} eq 'proliant';
+      $self->{productname} = 'BladeSystem' if 
+          $self->{runtime}->{options}->{servertype} eq 'bladesystem';
+      $self->{productname} = 'Storage' if 
+          $self->{runtime}->{options}->{servertype} eq 'storage';
+    }
+    if (! $self->{runtime}->{plugin}->check_messages()) {
+      if ($self->{productname} =~ /ProLiant/) {
+        bless $self, 'HP::Proliant::SNMP';
+        $self->trace(3, 'using HP::Proliant::SNMP');
+      } elsif ($self->{productname} =~ /^DL\d+\s*G\d+/) {
+        bless $self, 'HP::Proliant::SNMP';
+        $self->trace(3, 'using HP::Proliant::SNMP');
+      } elsif ($self->{productname} =~ /OpenView .* appliance/) {
+        bless $self, 'HP::Proliant::SNMP';
+        $self->trace(3, 'using HP::Proliant::SNMP');
+      } elsif ($self->{productname} =~ /BladeSystem/) {
+        bless $self, 'HP::BladeSystem';
+        $self->trace(3, 'using HP::BladeSystem');
+      } elsif ($self->{productname} =~ /PROLIANT 4LEE/) {
+        bless $self, 'HP::Storage';
+        $self->trace(3, 'using HP::Storage');
+      } elsif ($self->{productname} =~ /X\d+[\s\w]* Network Storage/) {
+        # HP X1600 Network Storage System
+        # HP X1600 G2 Network Storage System
+        bless $self, 'HP::Proliant::SNMP';
+        $self->trace(3, 'using HP::Proliant::SNMP');
+      } elsif ($self->{productname} =~ /Storage/) { # fake
+        bless $self, 'HP::Storage';
+        $self->trace(3, 'using HP::Storage');
+      } else {
+        $self->add_message(CRITICAL,
+            sprintf('unknown device%s', $self->{productname} eq 'unknown' ?
+                '' : '('.$self->{productname}.')'));
+      }
+      $self->{method} = 'snmp';
+    }
+  }
+  if ($self->{runtime}->{options}->{blacklist} &&
+      -f $self->{runtime}->{options}->{blacklist}) {
+    $self->{runtime}->{options}->{blacklist} = do {
+        local (@ARGV, $/) = $self->{runtime}->{options}->{blacklist}; <> };
+  }
+  return $self;
+}
+
+sub check_snmp_and_model {
+# uptime pruefen
+# dann whoami
+  my $self = shift;
+  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
+    my $response = {};
+    if (! -f $self->{runtime}->{plugin}->opts->snmpwalk) {
+      $self->{runtime}->{plugin}->add_message(CRITICAL, 
+          sprintf 'file %s not found',
+          $self->{runtime}->{plugin}->opts->snmpwalk);
+    } elsif (-x $self->{runtime}->{plugin}->opts->snmpwalk) {
+      my $cmd = sprintf "%s -On -v%s -c%s %s 1.3.6.1.4.1.232 2>&1",
+          $self->{runtime}->{plugin}->opts->snmpwalk,
+          $self->{runtime}->{plugin}->opts->protocol,
+          $self->{runtime}->{plugin}->opts->community,
+          $self->{runtime}->{plugin}->opts->hostname;
+      open(WALK, "$cmd |");
+      while (<WALK>) {
+        if (/^.*?\.(232\.[\d\.]+) = .*?: (\-*\d+)/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+        } elsif (/^.*?\.(232\.[\d\.]+) = .*?: "(.*?)"/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
+        }
+      }
+      close WALK;
+    } else {
+      open(MESS, $self->{runtime}->{plugin}->opts->snmpwalk);
+      my $in_string = 0;
+      my $in_hex_string = 0;
+      my $hex_oid = 0;
+      while(<MESS>) {
+        chomp;
+        if ($in_hex_string && /^(([0-9a-fA-F]{2})( [0-9a-fA-F]{2})*)\s*$/) {
+          $response->{$hex_oid} .= " ".$1;
+        } elsif ($in_string) {
+          if (/(.*)"$/) {
+            $response->{$hex_oid} .= $1;
+            $in_string = 0;
+          } else {
+            $response->{$hex_oid} .= $_;
+          }
+        } elsif (/^.*?\.(232\.[\d\.]+) = .*?: (\-*\d+)\s*$/) {
+          # SNMPv2-SMI::enterprises.232.6.2.6.7.1.3.1.4 = INTEGER: 6
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $in_hex_string = 0;
+        } elsif (/^.*?\.(232\.[\d\.]+) = .*?: "(.*?)"/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
+          $in_hex_string = 0;
+        } elsif (/^.*?\.(232\.[\d\.]+) = (\-*\d+)/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $in_hex_string = 0;
+        } elsif (/^.*?\.(232\.[\d\.]+) = "(.*?)"/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
+          $in_hex_string = 0;
+        } elsif (/^.*?\.(232\.[\d\.]+) = STRING: "(.*?[^"])$/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
+          $in_string = 0;
+          $in_hex_string = 0;
+        } elsif (/^.*?\.(232\.[\d\.]+) = Hex-STRING: (.*)/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $in_hex_string = 1;
+          $hex_oid = '1.3.6.1.4.1.'.$1;
+        } elsif (/^.*?\.(232\.[\d\.]+) =[ ]{1,2}Hex: (.*)/) {
+          $response->{'1.3.6.1.4.1.'.$1} = $2;
+          $in_hex_string = 1;
+          $hex_oid = '1.3.6.1.4.1.'.$1;
+        }
+      }
+      close MESS;
+    }
+    map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
+        keys %$response;
+    $self->{rawdata} = $response;
+    $self->whoami();
+  } else {
+    if (eval "require Net::SNMP") {
+      my %params = ();
+      my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
+      #$params{'-translate'} = [
+      #  -all => 0x0
+      #];
+      $params{'-hostname'} = $self->{runtime}->{plugin}->opts->hostname;
+      $params{'-version'} = $self->{runtime}->{plugin}->opts->protocol;
+      if ($self->{runtime}->{plugin}->opts->port) {
+        $params{'-port'} = $self->{runtime}->{plugin}->opts->port;
+      }
+      if ($self->{runtime}->{plugin}->opts->protocol eq '3') {
+        $params{'-username'} = $self->{runtime}->{plugin}->opts->username;
+        if ($self->{runtime}->{plugin}->opts->authpassword) {
+          $params{'-authpassword'} = $self->{runtime}->{plugin}->opts->authpassword;
+        }
+        if ($self->{runtime}->{plugin}->opts->authprotocol) {
+          $params{'-authprotocol'} = $self->{runtime}->{plugin}->opts->authprotocol;
+        }
+        if ($self->{runtime}->{plugin}->opts->privpassword) {
+          $params{'-privpassword'} = $self->{runtime}->{plugin}->opts->privpassword;
+        }
+        if ($self->{runtime}->{plugin}->opts->privprotocol) {
+          $params{'-privprotocol'} = $self->{runtime}->{plugin}->opts->privprotocol;
+        }
+      } else {
+        $params{'-community'} = $self->{runtime}->{plugin}->opts->community;
+      }
+      $self->{runtime}->{snmpparams} = \%params;
+      my ($session, $error) = Net::SNMP->session(%params);
+      $self->{session} = $session;
+      if (! defined $session) {
+        $self->add_message(CRITICAL, 'cannot create session object (maybe wrong hostname)');
+        $self->trace(1, Data::Dumper::Dumper(\%params));
+      } else {
+        my $sysUpTime = '1.3.6.1.2.1.1.3.0';
+        my $result = $session->get_request(
+            -varbindlist => [$sysUpTime]
+        );
+        if (!defined($result)) {
+          $self->add_message(CRITICAL,
+              'could not contact snmp agent');
+          $session->close;
+        } else {
+          $self->trace(3, 'snmp agent answered');
+          $self->whoami();
+        }
+      }
+    } else {
+      $self->add_message(CRITICAL,
+          'could not find Net::SNMP module');
+    }
+  }
+}
+
+sub whoami {
+  my $self = shift;
+  my $productname = undef;
+  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
+    my $cpqSiProductName = '1.3.6.1.4.1.232.2.2.4.2.0';
+    my $cpqSsMibRevMajor = '1.3.6.1.4.1.232.8.1.1.0';
+    my $cpqSsBackplaneModel = '1.3.6.1.4.1.232.8.2.2.6.1.9'.'.1.1';
+    if ($productname = $self->{rawdata}->{$cpqSiProductName}) {
+      if (! $productname) {
+        $self->{productname} = 'ProLiant';
+      } else {
+        $self->{productname} = $self->{rawdata}->{$cpqSiProductName};
+      }
+    } elsif (exists $self->{rawdata}->{$cpqSsBackplaneModel}) {
+      $self->{productname} = $self->{rawdata}->{$cpqSsBackplaneModel};
+    } elsif (exists $self->{rawdata}->{$cpqSsMibRevMajor}) {
+      # at least there is a CPQSTSYS-MIB
+      $self->{productname} = 'Storage'
+    } else {
+      $self->add_message(CRITICAL,
+          'snmpwalk returns no product name (cpqsinfo-mib)');
+    }
+  } else {
+    my $cpqSiProductName = '1.3.6.1.4.1.232.2.2.4.2.0';
+    my $cpqSsMibRevMajor = '1.3.6.1.4.1.232.8.1.1.0';
+    my $cpqSsBackplaneModel = '1.3.6.1.4.1.232.8.2.2.6.1.9'.'.1.1';
+    my $dummy = '1.3.6.1.2.1.1.5.0';
+    if ($productname = $self->valid_response($cpqSiProductName)) {
+      if ($productname eq '') {
+        $self->{productname} = 'ProLiant';
+      } else {
+        $self->{productname} = $productname;
+      }
+    } elsif ($productname = $self->valid_response($cpqSsBackplaneModel)) {
+      $self->{productname} = $productname;
+    } elsif ($self->valid_response($cpqSsMibRevMajor)) {
+      # at least there is a CPQSTSYS-MIB
+      $self->{productname} = 'Storage'
+    } else {
+      $self->add_message(CRITICAL,
+          'snmpwalk returns no product name (cpqsinfo-mib)');
+      $self->{session}->close;
+    }
+    $self->trace(3, 'whoami: '.$self->{productname});
+  }
+}
+
+sub valid_response {
+  my $self = shift;
+  my $oid = shift;
+  my $result = $self->{session}->get_request(
+      -varbindlist => [$oid]
+  );
+  if (!defined($result) ||
+      ! defined $result->{$oid} ||
+      $result->{$oid} eq 'noSuchInstance' ||
+      $result->{$oid} eq 'noSuchObject' ||
+      $result->{$oid} eq 'endOfMibView') {
+    return undef;
+  } else {
+    return $result->{$oid};
+  }
+}
+
+sub trace {
+  my $self = shift;
+  my $level = shift;
+  my $message = shift;
+  if ($self->{runtime}->{options}->{verbose} >= $level) {
+    printf "%s\n", $message;
+  }
+}
+
+sub blacklist {
+  my $self = shift;
+  my $type = shift;
+  my $name = shift;
+  $self->{blacklisted} = $self->is_blacklisted($type, $name);
+}
+
+sub add_blacklist {
+  my $self = shift;
+  my $list = shift;
+  $self->{runtime}->{options}->{blacklist} = join('/',
+      (split('/', $self->{runtime}->{options}->{blacklist}), $list));
+}
+
+sub is_blacklisted {
+  my $self = shift;
+  my $type = shift;
+  my $name = shift;
+  my $blacklisted = 0;
+#  $name =~ s/\:/-/g;
+  foreach my $bl_items (split(/\//, $self->{runtime}->{options}->{blacklist})) {
+    if ($bl_items =~ /^(\w+):([\:\d\-,]+)$/) {
+      my $bl_type = $1;
+      my $bl_names = $2;
+      foreach my $bl_name (split(/,/, $bl_names)) {
+        if ($bl_type eq $type && $bl_name eq $name) {
+          $blacklisted = 1;
+        }
+      }
+    } elsif ($bl_items =~ /^(\w+)$/) {
+      my $bl_type = $1;
+      if ($bl_type eq $type) {
+        $blacklisted = 1;
+      }
+    }
+  }
+  return $blacklisted;
+}
+
+sub add_message {
+  my $self = shift;
+  my $level = shift;
+  my $message = shift;
+  $self->{runtime}->{plugin}->add_message($level, $message) 
+      unless $self->{blacklisted};
+  if (exists $self->{failed}) {
+    if ($level == UNKNOWN && $self->{failed} == OK) {
+      $self->{failed} = $level;
+    } elsif ($level > $self->{failed}) {
+      $self->{failed} = $level;
+    }
+  }
+}
+
+sub remove_message {
+  my $self = shift;
+  my $level = shift;
+  my $message = shift;
+  $self->{runtime}->{plugin}->remove_message($level) ;
+}
+
+sub has_failed {
+  my $self = shift;
+  return $self->{failed};
+}
+
+sub add_info {
+  my $self = shift;
+  my $info = shift;
+  $info = $self->{blacklisted} ? $info.' (blacklisted)' : $info;
+  $self->{info} = $info;
+  if (! exists $self->{runtime}->{plugin}->{info}) {
+    $self->{runtime}->{plugin}->{info} = [];
+  }
+  push(@{$self->{runtime}->{plugin}->{info}}, $info);
+}
+
+sub annotate_info {
+  my $self = shift;
+  my $annotation = shift;
+  my $lastinfo = pop(@{$self->{runtime}->{plugin}->{info}});
+  $lastinfo .= sprintf ' (%s)', $annotation;
+  push(@{$self->{runtime}->{plugin}->{info}}, $lastinfo);
+}
+
+sub add_extendedinfo {
+  my $self = shift;
+  my $info = shift;
+  $self->{extendedinfo} = $info;
+  return if ! $self->{runtime}->{options}->{extendedinfo};
+  if (! exists $self->{runtime}->{plugin}->{extendedinfo}) {
+    $self->{runtime}->{plugin}->{extendedinfo} = [];
+  }
+  push(@{$self->{runtime}->{plugin}->{extendedinfo}}, $info);
+}
+
+sub get_extendedinfo {
+  my $self = shift;
+  if (! exists $self->{runtime}->{plugin}->{extendedinfo}) {
+    $self->{runtime}->{plugin}->{extendedinfo} = [];
+  }
+  return join(' ', @{$self->{runtime}->{plugin}->{extendedinfo}});
+}
+
+sub add_summary {
+  my $self = shift;
+  my $summary = shift;
+  if (! exists $self->{runtime}->{plugin}->{summary}) {
+    $self->{runtime}->{plugin}->{summary} = [];
+  }
+  push(@{$self->{runtime}->{plugin}->{summary}}, $summary);
+}
+
+sub get_summary {
+  my $self = shift;
+  if (! exists $self->{runtime}->{plugin}->{summary}) {
+    $self->{runtime}->{plugin}->{summary} = [];
+  }
+  return join(', ', @{$self->{runtime}->{plugin}->{summary}});
+}
+
+sub dumper {
+  my $self = shift;
+  my $object = shift;
+  my $run = $object->{runtime};
+  delete $object->{runtime};
+  printf STDERR "%s\n", Data::Dumper::Dumper($object);
+  $object->{runtime} = $run;
+}
+package main;
+#! /usr/bin/perl
+
+use strict;
+
+my $CELSIUS = 1;
+my $PERFDATA = 1;
+my $EXTENDEDINFO = 1;
+my $HWINFO = 1;
+my $HPACUCLI = 0;
+my $NOINSTLEVEL = 'unknown';
+
+use constant OK         => 0;
+use constant WARNING    => 1;
+use constant CRITICAL   => 2;
+use constant UNKNOWN    => 3;
+use constant DEPENDENT  => 4;
+
+my $plugin = Nagios::MiniPlugin->new(
+    shortname => '',
+    usage => 'Usage: %s [ -v|--verbose ] [ -t <timeout> ] '.
+        '--hostname <proliant> --community <snmp-community>'.
+        '  ...]',
+    version => '4.5.2',
+    blurb => 'This plugin checks the hardware of hp/compaq proliant servers',
+    url => 'http://labs.consol.de/nagios/check_hpasm',
+    timeout => 60,
+    shortname => '',
+);
+$plugin->add_arg(
+    spec => 'blacklist|b=s',
+    help => '--blacklist
+   Blacklist some (missing/failed) components',
+    required => 0,
+    default => '',
+);
+$plugin->add_arg(
+    spec => 'ignore-dimms|i',
+    help => '--ignore-dimms
+   Ignore "N/A"-DIMM status on misc. servers (e.g. older DL320)',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'ignore-fan-redundancy',
+    help => '--ignore-fan-redundancy
+   Ignore missing redundancy partners',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'customthresholds|c=s',
+    help => '--customthresholds
+   Use custom thresholds for certain temperatures',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'eventrange=s',
+    help => '--eventrange=<warningrange>/<criticalrange>
+   Period of time before critical IML events respecively become warnings or vanish
+   A range is descibed as a number and a unit (s, m, h, d), e.g. --eventrange 1h/20m',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'perfdata=s',
+    help => '--perfdata=[short]
+   Output performance data. If your performance data string becomes
+   too long and is truncated by Nagios, then you can use --perfdata=short
+   instead. This will output temperature tags without location information',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'hostname|H=s',
+    help => '--hostname
+   Hostname or IP-address of the server (SNMP mode only)',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'port=i',
+    help => '--port
+   The SNMP port to use (default: 161)',
+    required => 0,
+    default => 161,
+);
+$plugin->add_arg(
+    spec => 'protocol|P=s',
+    help => '--protocol
+   The SNMP protocol to use (default: 2c, other possibilities: 1,3)',
+    required => 0,
+    default => '2c',
+);
+$plugin->add_arg(
+    spec => 'community|C=s',
+    help => '--community
+   SNMP community of the server (SNMP v1/2 only)',
+    required => 0,
+    default => 'public',
+);
+$plugin->add_arg(
+    spec => 'username=s',
+    help => '--username
+   The securityName for the USM security model (SNMPv3 only)',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'authpassword=s',
+    help => '--authpassword
+   The authentication password for SNMPv3',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'authprotocol=s',
+    help => '--authprotocol
+   The authentication protocol for SNMPv3 (md5|sha)',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'privpassword=s',
+    help => '--privpassword
+   The password for authPriv security level',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'privprotocol=s',
+    help => '--privprotocol
+   The private protocol for SNMPv3 (des|aes|aes128|3des|3desde)',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'snmpwalk=s',
+    help => '--snmpwalk
+   A file with the output of snmpwalk 1.3.6.1.4.1.232',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'hpasmcli=s',
+    help => '--hpasmcli
+   A file with the output of hpasmcli',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'servertype=s',
+    help => '--servertype
+   The type of the server: proliant (default) or bladesystem',
+    required => 0,
+);
+$plugin->add_arg(
+    spec => 'eval-nics',
+    help => '--eval-nics
+   Check network interfaces (and groups). Try it and report me whyt you think about it. I need to build up some know how on this subject. If get an error and you think, it is not justified for your configuration, please tell me about it. (alwasy send the output of "snmpwalk -On .... 1.3.6.1.4.1.232" and a description how you setup your nics and why it is correct opposed to the plugins error message',
+    required => 0,
+);
+
+$plugin->getopts();
+if (! $PERFDATA && $plugin->opts->get('perfdata')) {
+  $PERFDATA = 1;
+}
+if ($PERFDATA && $plugin->opts->get('perfdata') &&
+    ($plugin->opts->get('perfdata') eq 'short')) {
+  $PERFDATA = 2;
+}
+$plugin->{messages}->{unknown} = []; # wg. add_message(UNKNOWN,...)
+
+$plugin->{info} = []; # gefrickel
+
+$SIG{'ALRM'} = sub {
+  printf "UNKNOWN - check_hpasm timed out after %d seconds\n", 
+      $plugin->opts->get('timeout');
+  exit $ERRORS{UNKNOWN};
+};
+alarm($plugin->opts->get('timeout'));
+
+my $server = HP::Server->new( runtime => {
+    plugin => $plugin,
+    options => {
+        servertype => $plugin->opts->get('servertype'),
+        verbose => $plugin->opts->get('verbose'),
+        scrapiron => 0,
+        ignore_fan_redundancy => $plugin->opts->get('ignore-fan-redundancy'),
+        ignore_dimms => $plugin->opts->get('ignore-dimms'),
+        customthresholds => $plugin->opts->get('customthresholds'),
+        eventrange => $plugin->opts->get('eventrange'),
+        blacklist => $plugin->opts->get('blacklist'),
+        celsius => $CELSIUS,
+        perfdata => $PERFDATA,
+        extendedinfo => $EXTENDEDINFO,
+        hwinfo => $HWINFO,
+        hpacucli => $HPACUCLI,
+        noinstlevel => $NOINSTLEVEL,
+    },
+},);
+if (! $plugin->check_messages()) {
+  $server->init();
+  $plugin->add_message(OK, $server->identify()) if $HWINFO;
+  if (! $plugin->check_messages()) {
+    $plugin->add_message(OK, 'hardware working fine');
+    $plugin->add_message(OK, $server->get_summary()) 
+        if $server->get_summary();
+    $plugin->add_message(OK, $server->get_extendedinfo()) 
+        if $server->get_extendedinfo();
+  } 
+} else {
+  $plugin->add_message(CRITICAL, 'wrong device');
+}
+
+my ($code, $message) = $plugin->check_messages(join => ', ', join_all => ', ');
+$message .= sprintf "\n%s\n", join("\n", @{$plugin->{info}})
+    if $plugin->opts->get('verbose') >= 1;
+#printf "%s\n", Data::Dumper::Dumper($plugin->{info});
+$plugin->nagios_exit($code, $message);
+
diff --git a/templates/monitor/commands.cfg b/templates/monitor/commands.cfg
new file mode 100644 (file)
index 0000000..cb61527
--- /dev/null
@@ -0,0 +1,78 @@
+###############################################################################
+# COMMANDS.CFG - SAMPLE COMMAND DEFINITIONS FOR ICINGA
+# NOTES: This config file provides you with some example command definitions
+#        that you can reference in host, service, and contact definitions.
+#       
+#        You don't need to keep commands in a separate file from your other
+#        object definitions.  This has been done just to make things easier to
+#        understand.
+###############################################################################
+define command{
+        command_name    check_ldirector
+        command_line    /usr/lib/nagios/plugins/check_http -I $HOSTADDRESS$ -u http://$HOSTADDRESS$/ldirectorping.txt -s pong
+}
+define command{
+        #-t sets the timeout to 180 seconds. At the default, it triggered many "unknown status" alerts
+        command_name    check_hphealth
+        command_line    /usr/lib/nagios/plugins/check_hpasm -t 180 --hostname $HOSTADDRESS$ --community pinesclusterro
+}
+define command {
+        command_name CHECK_NRPE
+        command_line /usr/lib/nagios/plugins/check_nrpe -t 60 -H $HOSTADDRESS$ -c $ARG1$ -a $ARG2$ $ARG3$ $ARG4$ $ARG5$ $ARG6$ $ARG7$ $ARG8$ $ARG9$ $ARG10$
+}
+define command {
+        command_name check_ssh_altport
+        command_line /usr/lib/nagios/plugins/check_ssh -p $ARG1$ $HOSTADDRESS$
+}
+
+################################################################################
+# SAMPLE NOTIFICATION COMMANDS
+# These are some example notification commands.  They may or may not work on
+# your system without modification.  As an example, some systems will require 
+# you to use "/usr/bin/mailx" instead of "/usr/bin/mail" in the commands below.
+################################################################################
+
+
+# 'notify-host-by-email' command definition
+define command{
+        command_name    notify-host-by-email
+        command_line    /usr/bin/printf "%b" "***** Icinga *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$
+}
+
+# 'notify-service-by-email' command definition
+define command{
+        command_name    notify-service-by-email
+        command_line    /usr/bin/printf "%b" "***** Icinga *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **" $CONTACTEMAIL$
+}
+# 'notify-by-pipe' command definition(s)
+define command {
+        command_name                    notify-service-by-pipe
+        command_line                    /usr/bin/printf "%b" "*$NOTIFICATIONTYPE$* $SERVICEDESC$ on $HOSTALIAS$ $SERVICESTATE$\n" >> /var/run/nagibot/nagibot.fifo 2>&1
+}
+
+define command {
+        command_name                    notify-host-by-pipe
+        command_line                    /usr/bin/printf "%b" "*$NOTIFICATIONTYPE$* $HOSTNAME$ is $HOSTSTATE$\n" >> /var/run/nagibot/nagibot.fifo 2>&1
+}
+
+
+
+################################################################################
+# PERFORMANCE DATA COMMANDS
+################################################################################
+
+
+# 'process-host-perfdata' command definition
+define command{
+        command_name    process-host-perfdata
+        command_line    /usr/bin/printf "%b" "$LASTHOSTCHECK$\t$HOSTNAME$\t$HOSTSTATE$\t$HOSTATTEMPT$\t$HOSTSTATETYPE$\t$HOSTEXECUTIONTIME$\t$HOSTOUTPUT$\t$HOSTPERFDATA$\n" >> /var/lib/icinga/host-perfdata.out
+}
+
+
+# 'process-service-perfdata' command definition
+define command{
+        command_name    process-service-perfdata
+        command_line    /usr/bin/printf "%b" "$LASTSERVICECHECK$\t$HOSTNAME$\t$SERVICEDESC$\t$SERVICESTATE$\t$SERVICEATTEMPT$\t$SERVICESTATETYPE$\t$SERVICEEXECUTIONTIME$\t$SERVICELATENCY$\t$SERVICEOUTPUT$\t$SERVICEPERFDATA$\n" >> /var/lib/icinga/service-perfdata.out
+}
+
+
diff --git a/templates/monitor/nrpe_local.cfg b/templates/monitor/nrpe_local.cfg
new file mode 100644 (file)
index 0000000..06581f5
--- /dev/null
@@ -0,0 +1,20 @@
+### nrpe_local.cfg. 
+### Generated by GenaSYS.
+
+### Load
+command[check_load_args]=/usr/lib/nagios/plugins/check_load -w $ARG1$ $ARG2$ $ARG3$ -c $ARG4$ $ARG5$ $ARG6$
+
+### Disk Free and Swap Free
+command[check_all_disks]=/usr/lib/nagios/plugins/check_disk -u GB -w $ARG1$ -c $ARG2$
+command[check_disk_root_args]=/usr/lib/nagios/plugins/check_disk -u GB -w $ARG1$ -c $ARG2$ -p /
+command[check_swap]=/usr/lib/nagios/plugins/check_swap -w $ARG1$ -c $ARG2$
+command[check_storage_disk]=/usr/lib/nagios/plugins/check_disk -u GB -w $ARG1$ -c $ARG2$
+
+### Check Processes
+command[check_proc]=/usr/lib/nagios/plugins/check_procs -C $ARG1$ -w $ARG2$ -c $ARG3$ 
+
+### Clark
+command[check_clark]=/usr/lib/nagios/plugins/check_procs -w1:5 -c1:5 -C Clark
+
+### Hold Targeter Lock File Age
+command[check_hold_targeter_lock]=/usr/lib/nagios/plugins/check_file_age -w 14400 -c 21600 /tmp/hold_targeter-LOCK
diff --git a/templates/monitor/reporter_status.sh b/templates/monitor/reporter_status.sh
new file mode 100755 (executable)
index 0000000..08dceae
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+# Copyright (C) 2008-2011  Equinox Software, Inc.
+# Written by Michael Tate <mtate@esilibrary.com>
+#
+# 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.
+#
+#
+# Author       : MTate, Sys Admin, ESI: Added arguments
+# Modified         : Michael Peters, Sys Admin , Evergreen Indiana
+# Purpose      : Count overdue reports
+# Usage        : check_overdue_reports <db; default to "evergreen" if empty> <username; default "evergreen"> <port database runs on, default "5432">
+
+CTWARN=5
+CTCRIT=10
+
+if [[ $1 == *help* ]]; then
+  echo "Usage: check_dbquery <db; default to 'evergreen' if empty> <username; default 'evergreen'> <port database runs on, default '5432'>"
+  exit 0
+else
+
+# Set/get database name
+if [ -n "$1" ]; then
+  dbname="$1"
+else
+  dbname="evergreen"
+fi
+
+
+# Set/get database user name
+if [ -n "$2" ]; then
+  dbuser="$1"
+else
+  dbuser="evergreen"
+fi
+
+# Set/get port database runs on
+if [ -n "$3" ]; then
+  dbport="$3"
+else
+  dbport=5432
+fi
+
+# Execute DB Query Count
+## postgres outputs in rows:
+OVERDUEREPORT=`PGUSER=postgres psql -U $dbuser -d $dbname -p $dbport -c "select count(id) from reporter.currently_running WHERE run_time > now() - interval '15 minutes';"|sed -n '3'p`
+
+# Return results
+if [[ $OVERDUEREPORT == "-00" ]]; then
+   echo "OK: No long running reports"
+   exit 0
+
+else
+
+  if [ $OVERDUEREPORT -gt $CTCRIT ]; then
+    echo "CRITICAL: $OVERDUEREPORT report started more than 15 minutes ago!"
+    exit 2
+
+  elif [ $OVERDUEREPORT -gt $CTWARN ]; then
+    echo "WARNING:  $OVERDUEREPORT report started more than 15 minutes ago!"
+    exit 1
+
+  else
+    echo "OK:  $OVERDUEREPORT : No long running reports"
+    exit 0
+  fi
+fi
+fi
index 2cd1726..dd80238 100644 (file)
@@ -1,3 +1,4 @@
+### Generated by GenaSYS.
 define service {
         hostgroup_name                  all
         service_description             ping
@@ -9,7 +10,7 @@ define service {
 define service {
         hostgroup_name                  all
         service_description             Root Disk Space
-        check_command                   CHECK_NRPE!check_disk_root_args!20%!10%
+        check_command                   check_nrpe!check_disk_root_args!20%!10%
         use                             generic-service
         notification_interval           0
 }
@@ -17,7 +18,7 @@ define service {
 define service {
         hostgroup_name                  db-servers
         service_description             Data Directory Disk Space #TODO  Check this
-        check_command                   CHECK_NRPE!check_data_disk!20%!10%
+        check_command                   check_nrpe!check_data_disk!20%!10%
         use                             generic-service
         notification_interval           0
 }
@@ -52,7 +53,7 @@ define service {
         service_description             Jabber processes
         use                             generic-service
         notification_interval           240
-        check_command                   CHECK_NRPE!check_proc!beam!1:2!1:2
+        check_command                   check_nrpe!check_proc!beam!1:2!1:2
 }
 
 define service {
@@ -60,19 +61,49 @@ define service {
         service_description             Clark Processes
         use                             generic-service
         notification_interval           240
-        check_command                   CHECK_NRPE!check_clark!1:15!1:15
+        check_command                   check_nrpe!check_clark!1:15!1:15
 }
 
+#=============
+# Swap Usage #  
+#=============
+define service {
+        hostgroup_name                  all
+        service_description             Swap Usage
+        use                             generic-service
+        notification_interval           1800
+        check_command                   check_nrpe!check_swap!20%!10%
+}
 
 #=============#
-#  postgres   #
+# System Load #
 #=============#
 
 define service {
-        hostgroup_name                  db-servers
-        service_description             PostgreSQL
+        hostgroup_name                  all
+        service_description             Load average
         use                             generic-service
         notification_interval           240
-        check_command                   check_nrpe_1arg!check_pgsql
+        check_command                   check_nrpe_1arg!check_load
+}
+
+#==============#
+#  DB Servers  #
+#==============#
+
+define service {
+        hostgroup_name                 db-servers
+        service_description            PostgreSQL
+        use                            generic-service
+        notification_interval          240
+        check_command                  check_nrpe_1arg!check_pgsql
+}
+
+define service {
+        hostgroup_name                 db-servers
+        service_description            cpu-io
+        use                            generic-service
+        notification_interval          240
+        check_command                  check_nrpe_1arg!check_cpu_io
 }
 
index fa27d1b..73f9904 100755 (executable)
@@ -2,7 +2,7 @@
 
 #TODO: export GenaSYS version to install logs.
 
-WD=$(dirname $(readlink -f $0))
+export WD=$(dirname $(readlink -f $0))
 . "$WD/setup-vars"
 . "$WD/setup-functions"
 
@@ -191,18 +191,19 @@ sleep 1
 
 ### Setup Monitoring
 ## Monitored hosts
-if [ -e /etc/nagios/nrpe.cfg ]
-then
-        if [ -e ./.setup_monitoring.sh ]
-        then
-                mkdir -p /etc/nagios
-                ./.setup_monitoring.sh
-        else
-                echo
-                echo "Cannot configure monitoring"
-                echo ".setup_monitoring.sh is missing."
-        fi
-fi
+SetupMonitoring
+#if [ -e /etc/nagios/nrpe.cfg ]
+#then
+#        if [ -e ${WD}/monitoring/.setup_monitoring.sh ]
+#        then
+#                mkdir -p /etc/nagios
+#                ${WD}/monitoring/.setup_monitoring.sh
+#        else
+#                echo
+#                echo "Cannot configure monitoring"
+#                echo ".setup_monitoring.sh is missing."
+#        fi
+#fi
 
 
 echo
index de45931..bb3ca41 100644 (file)
@@ -85,3 +85,20 @@ else
        read -p "Press [Enter] to continue."
 fi
 }
+
+Setup monitoring.
+SetupMonitoring () {
+## Monitored hosts
+if [ -e /etc/nagios/nrpe.cfg ]
+then
+        if [ -e ${WD}/monitoring/.setup_monitoring.sh ]
+        then
+                mkdir -p /etc/nagios
+                ${WD}/monitoring/.setup_monitoring.sh
+        else
+                echo
+                echo "Cannot configure monitoring"
+                echo ".setup_monitoring.sh is missing."
+        fi
+fi
+}
index 87f2dc3..105b6e8 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-WD=$(dirname $(readlink -f $0))
+export WD=$(dirname $(readlink -f $0))
 . "$WD/setup-vars"
 . "$WD/setup-functions"
 
@@ -347,18 +347,20 @@ echo 'PS1="\[\033[1;33m\]\`if [[ -e /openils/var/web/ldirectorping.txt ]]; then
 
 ### Setup Monitoring
 ## Monitored hosts
-if [ -e /etc/nagios/nrpe.cfg ]
-then
-        if [ -e ./.setup_monitoring.sh ]
-        then
-                mkdir -p /etc/nagios
-                ./.setup_monitoring.sh
-        else
-                echo
-                echo "Cannot configure monitoring"
-                echo ".setup_monitoring.sh is missing."
-        fi
-fi
+SetupMonitoring
+#if [ -e /etc/nagios/nrpe.cfg ]
+#then
+#        if [ -e ${WD}/monitoring/.setup_monitoring.sh ]
+#        then
+#                mkdir -p /etc/nagios
+#                ${WD}/monitoring/.setup_monitoring.sh
+#        else
+#                echo
+#                echo "Cannot configure monitoring"
+#                echo ".setup_monitoring.sh is missing."
+#        fi
+#fi
+
 
 echo
 echo
index 6adf002..58fd355 100755 (executable)
@@ -2,7 +2,7 @@
 
 ###TODO: export GenaSYS version to install logs.
 
-WD=$(dirname $(readlink -f $0))
+export WD=$(dirname $(readlink -f $0))
 . "$WD/setup-vars"
 . "$WD/setup-functions"
 
@@ -445,6 +445,46 @@ if [ "$OSRF_NODE" = "1" ]
 fi
 }
 
+### Setup Basic Monitoring. Nagios/Icinga
+## Monitor server
+if cat /etc/hostname | grep -q "monitor0"
+then
+       MONITOR_DIR="$WD/Icinga-Nagios"
+        ### Add PPA for lastest Icinga Monitoring Server.
+        if [ -e "$MONITOR_DIR/icinga_ppa.list" ]
+        then
+               cp -f "$MONITOR_DIR/icinga_ppa.list" "/etc/apt/sources.list.d/icinga_ppa.list"
+                if apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36862847
+                then
+                       ### Install Icinga 
+                        echo;echo -e $COL_BR_GREEN "Installing latest Icinga for Monitoring"$COL_RESET;echo
+                        apt-get update && apt-get -y install icinga nagios-nrpe-plugin
+                       ### Configure Icinga/Nagios
+                       [ -e "/etc/icinga/commands.cfg" ] && cp /etc/icinga/commands.cfg /etc/icinga/commands.cfg-orig
+                       [ -e "/etc/icinga/objects/services.cfg" ] && cp /etc/icinga/objects/services.cfg /etc/icinga/objects/services.cfg-orig
+                       [ -e "/etc/icinga/objects/hostgroups.cfg" ] && cp /etc/icinga/objects/hostgroups.cfg /etc/icinga/objects/hostgroups.cfg-orig
+                       [ -e "/etc/icinga/objects/hosts.cfg" ] && cp /etc/icinga/objects/hosts.cfg /etc/icinga/objects/hosts.cfg-orig
+                       cp -f "$MONITOR_DIR/commands.cfg" "/etc/icinga"
+                       cp -f "$MONITOR_DIR/services.cfg" "/etc/icinga/objects"
+                       cp -f "$MONITOR_DIR/hostgroups.cfg" "/etc/icinga/objects"
+                       cp -f "$MONITOR_DIR/hosts.cfg" "/etc/icinga/objects"
+                       cp -f "$MONITOR_DIR/check_hpacucli" "/usr/lib/nagios/plugins"
+                       cp -f "$MONITOR_DIR/check_hpasm" "/usr/lib/nagios/plugins"
+                       ### Restart Icinga.
+                       /etc/init.d/icinga restart
+                else
+                        echo;echo -e $COL_BR_RED"Could not install signing key."
+                        echo -e "Not installing the monitoring server."
+                        echo -e "You will have to do this manually."$COL_RESET
+                fi
+        else
+                echo 
+                echo -e $COL_BR_RED"Could not install icinga"
+                echo -e "Failed to add icinga PPA"
+                echo -e $COL_RED"$MONITOR_DIR/icinga_ppa.list not found."$COL_RESET
+        fi
+fi
+
 
 ### Extract system files
 echo "Installing system files"
@@ -580,42 +620,20 @@ then
        fi
 fi
 
-### Setup Basic Monitoring. Nagios/Icinga
-## Monitor server
-if cat /etc/hostname | grep -q "monitor0"
-then   
-       ### Add PPA for lastest Icinga Monitoring Server.
-       if [ -e "/etc/apt/sources.list.d/icinga_ppa.list" ]
-       then
-               if apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36862847
-               then
-                       echo;echo -e $COL_BR_GREEN "Installing latest Icinga for Monitoring"$COL_RESET;echo
-                       apt-get update && apt-get -y install icinga nagios-nrpe-plugin
-               else
-                       echo;echo -e $COL_BR_RED"Could not install signing key."
-                       echo -e "Not installing the monitoring server."
-                       echo -e "You will have to do this manually."$COL_RESET
-               fi
-       else
-               echo 
-               echo -e $COL_BR_RED"Could not install icinga"
-               echo -e "Failed to add icinga PPA"
-               echo -e $COL_RED"/etc/apt/sources.list.d/icinga_ppa.list not found."$COL_RESET
-       fi
-fi
 ## Monitored hosts
-if [ -e /etc/nagios/nrpe.cfg ]
-then
-       if [ -e ./.setup_monitoring.sh ]
-       then
-               mkdir -p /etc/nagios
-               ./.setup_monitoring.sh
-       else
-               echo
-               echo "Cannot configure monitoring"
-               echo ".setup_monitoring.sh is missing."
-       fi
-fi
+SetupMonitoring
+#if [ -e /etc/nagios/nrpe.cfg ]
+#then
+#      if [ -e ${WD}/monitoring/.setup_monitoring.sh ]
+#      then
+#              mkdir -p /etc/nagios
+#              ${WD}/monitoring/.setup_monitoring.sh
+#      else
+#              echo
+#              echo "Cannot configure monitoring"
+#              echo ".setup_monitoring.sh is missing."
+#      fi
+#fi
 
 
 ### Enhance bashrc for root.