|
Server : Apache/2.2.2 (Fedora) System : Linux App1.pathumtani.go.th 2.6.20-1.2320.fc5smp #1 SMP Tue Jun 12 19:40:16 EDT 2007 i686 User : apache ( 48) PHP Version : 5.2.9 Disable Function : NONE Directory : /usr/local/src/munin-1.2.6/node/node.d/ |
Upload File : |
#!@@PYTHON@@
# -*- encoding: iso-8859-1 -*-
#
# Wildcard-plugin to monitor S.M.A.R.T attribute values through smartctl,
# which is part of smartmontools package:
# http://smartmontools.sourceforge.net/
#
# To monitor a S.M.A.R.T device, link smart_<device> to this file.
# E.g.
# ln -s /usr/share/munin/plugins/smart_ /etc/munin/plugins/smart_hda
# ...will monitor /dev/hda.
#
# Needs following minimal configuration in plugin-conf.d/munin-node:
# [smart_*]
# user root
# group disk
#
# Parameters
# smartpath - Specify path to smartctl program (Default: /usr/sbin/smartctl)
# smartargs - Override '-a' argument passed to smartctl with '-A -i'+smartargs
#
# Parameters can be specified on a per-drive basis, eg:
# [smart_hda]
# user root
# group disk
# env.smartargs -H -c -l error -l selftest -l selective -d ata
# env.smartpath /usr/local/sbin/smartctl
#
# [smart_twa0-1]
# user root
# group disk
# env.smartargs -H -l error -d 3ware,1
#
# [smart_twa0-2]
# user root
# group disk
# env.smartargs -H -l error -d 3ware,2
#
# Author: Nicolas Stransky <Nico@neo-lan.net>
#
# v1.0 22/08/2004 - First draft
# v1.2 28/08/2004 - Clean up the code, add a verbose option
# v1.3 14/11/2004 - Compatibility with python<2.2. See comments in the code
# v1.4 17/11/2004 - Deal with non zero exit codes of smartctl
# - config now prints the critical thresholds, as reported by smartctl
# v1.5 18/11/2004 - Plot smartctl_exit_code bitmask
# v1.6 21/11/2004 - Add autoconf and suggest capabilities
# - smartctl path can be passed through "smartpath" environment variable
# - Additional smartctl args can be passed through "smartargs" environment variable
# v1.7 29/11/2004 - Add suggest capabilities for NetBSD, OpenBSD, FreeBSD and SunOS.
# - Allow to override completely the smartctl arguments with "smartargs"
# v1.8 16/02/2005 - Exit status field now only triggers warnings, not criticals.
# v1.9 07/07/2005 - Allow to query several drives on the same 3ware card.
# - Correct a bug when '-i' was not listed in smartargs
# - Don't fail if no value was obtained for hard drive model
# v1.10 19/08/2005 - smartctl_exit_code is now a numerical value
#
# Copyright (c) 2004,2005 Nicolas Stransky.
#
# Permission to use, copy, and modify this software with or without fee
# is hereby granted, provided that this entire notice is included in
# all source code copies of any software which is or includes a copy or
# modification of this software.
#
# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
# MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
# PURPOSE.
#
#
# Magic markers
#%# capabilities=autoconf suggest
#%# family=auto
import os, sys, string, pickle
from math import log
# Increase verbosity of the plugin by putting verbose=True (for
# debugging purposes)
# For python < 2.2 compliance, put verbose=0 instead of "False"
verbose=False
# Modify to your needs:
statefiledir='@@PLUGSTATE@@/'
def verboselog(s):
sys.stderr.write(plugin_name+': '+s+'\n')
if not verbose :
verboselog = lambda s: None
def read_values(hard_drive) :
num_exit_status=0
try :
verboselog('Reading S.M.A.R.T values')
os.putenv('LC_ALL','C')
smart_output=os.popen(os.getenv('smartpath','/usr/sbin/smartctl')+' '+os.getenv('smartargs','-a')+' -A -i /dev/'+hard_drive)
read_values=0
# For python < 2.2 compliance, switch comments on next 2 lines.
for l in smart_output :
#for l in smart_output.readlines() :
if l[:-1]=='' :
read_values=0
elif l[:13]=='Device Model:' or l[:7]=='Device:' :
model_list=string.split(string.split(l,':')[1])
try: model_list.remove('Version')
except : None
model=string.join(model_list)
if read_values==1 :
smart_attribute=string.split(l)
smart_values[string.replace(smart_attribute[1],'-','_')]={"value":smart_attribute[3],"threshold":smart_attribute[5]}
elif l[:18]=="ID# ATTRIBUTE_NAME" :
# Start reading the Attributes block
read_values=1
exit_status=smart_output.close()
if exit_status!=None :
# smartctl exit code is a bitmask, check man page.
num_exit_status=int(exit_status/256)
if num_exit_status<=2 :
verboselog('smartctl cannot access S.M.A.R.T values on drive '+hard_drive+'. Command exited with code '+str(num_exit_status)+' ('+str(exit_status/256)+')')
sys.exit(1)
else :
verboselog('smartctl exited with code '+str(num_exit_status)+'. '+hard_drive+' may be FAILING RIGHT NOW !')
except :
verboselog('Cannot access S.M.A.R.T values ! Check user rights or propper smartmontools installation. Quitting...')
sys.exit(1)
if smart_values=={} :
verboselog('Can\'t find any S.M.A.R.T value to plot ! Quitting...')
sys.exit(1)
smart_values["smartctl_exit_status"]={"value":str(num_exit_status),"threshold":"1"}
try: smart_values["model"]=model
# For some reason we may have no value for "model"
except : smart_values["model"]="unknown"
def open_state_file(mode) :
return open(statefiledir+'/smart-'+string.join(hard_drive,"-")+'.state',mode)
def update_state_file() :
try:
verboselog('Saving statefile')
pickle.dump(smart_values,open_state_file("w"))
except :
verboselog('Error trying to save state file ! Check access rights')
def print_plugin_values() :
verboselog('Printing S.M.A.R.T values')
for key in smart_values.keys() :
if key=="model" : continue
print(key+".value "+smart_values[key]["value"])
def print_config(hard_drive,smart_values) :
if os.path.exists(statefiledir+'/smart-'+string.join(hard_drive,"-")+'.state'):
try :
verboselog('Try to recall previous S.M.A.R.T attributes for '+string.join(hard_drive,","))
smart_values_state=pickle.load(open_state_file("r"))
except :
verboselog('Error opening existing state file !')
sys.exit(1)
else :
verboselog('No state file, reading S.M.A.R.T values for the first time')
read_values(hard_drive[0])
pickle.dump(smart_values,open_state_file("w"))
smart_values_state=smart_values
verboselog('Printing configuration')
print('graph_title S.M.A.R.T values for drive '+string.join(hard_drive,","))
print('graph_vlabel Attribute S.M.A.R.T value')
print('graph_args --base 1000 --lower-limit 0')
print('graph_category disk')
print('graph_info This graph shows the value of all S.M.A.R.T attributes of drive '+string.join(hard_drive,",")+' ('+smart_values_state['model']+'). smartctl_exit_status is the return value of smartctl. A non-zero return value indicates an error, a potential error, or a fault on the drive.')
attributes=smart_values_state.keys()
attributes.sort()
for key in attributes :
if key in ['smartctl_exit_status','model'] : continue
print(key+'.label '+key)
print(key+'.draw LINE2')
# Following line can be commented out if necessary. Only useful for warning reports about S.M.A.R.T values
print(key+'.critical '+smart_values_state[key]["threshold"]+':')
# Let's place smartctl_exit_status at the end
print('smartctl_exit_status.label smartctl_exit_status')
print('smartctl_exit_status.draw LINE2')
# Following line can be commented out if necessary. Only useful for warning reports about S.M.A.R.T values
print('smartctl_exit_status.warning '+smart_values_state['smartctl_exit_status']["threshold"])
def get_hard_drive_name() :
try :
hard_drive=[plugin_name[string.rindex(plugin_name,'_')+1:]]
if os.uname()[0]=="SunOS" :
try :
# if hard_drive name starts with "rdsk" or "rmt", try to reconstruct the path
if hard_drive[0][0:4]=="rdsk":
hard_drive[0]=os.path.join("rdsk",hard_drive[0][4:])
elif hard_drive[0][0:3]=="rmt":
hard_drive[0]=os.path.join("rmt",hard_drive[0][3:])
except :
verboselog('Failed to find SunOS hard_drive')
# For 3ware cards, we have to set multiple plugins for the same hard drive name.
# Let's see if we find a '-' in the drive name.
if hard_drive[0].find('-')!=-1:
# Put the drive name and it's number in a list
hard_drive=[hard_drive[0][:string.rindex(hard_drive[0],'-')],hard_drive[0][string.rindex(hard_drive[0],'-')+1:]]
# Chech that the drive exists in /dev
if not os.path.exists('/dev/'+hard_drive[0]):
verboselog('/dev/'+hard_drive[0]+' not found !')
sys.exit(1)
return(hard_drive)
except :
verboselog('No S.M.A.R.T device name found in plugin\'s symlink !')
sys.exit(1)
def find_smart_drives() :
# Try to autodetect Linux, *BSD, SunOS drives. Don't try to autodetect drives on a 3Ware card.
drives=[]
if os.uname()[0]=="Linux" :
if os.path.exists('/sys/block/'):
# Running 2.6, yeah
try :
for drive in os.listdir('/sys/block/') :
if drive[:2] in ['md','fd','lo','ra','dm'] : continue # Ignore MD, Floppy, loop , RAM and LVM devices.
try :
read_values(drive)
drives.append(drive)
except :
continue
except :
verboselog('Failed to list devices in /sys/block')
else :
verboselog('Not running linux2.6, failing back to /proc/partitions')
try :
partitions=open('/proc/partitions','r')
L=partitions.readlines()
for l in L :
words=string.split(l)
if len(words)==0 or words[0][0] not in string.digits : continue
if words[0] in ['1','9','58','254'] : continue # Ignore RAM, md, LVM and LVM2 devices
if words[-1][-1] not in string.digits :
try :
read_values(words[-1])
drives.append(words[-1])
except :
continue
verboselog('Found drives in /proc/partitions ! '+str(drives))
except :
verboselog('Failed to list devices in /proc/partitions')
elif os.uname()[0]=="OpenBSD" :
try :
sysctl_kerndisks=os.popen('sysctl hw.disknames')
kerndisks=string.strip(sysctl_kerndisks.readline())
for drive in string.split(kerndisks[string.rindex(kerndisks,'=')+1:],',') :
if drive[:2] in ['md','cd','fd'] : continue # Ignore Memory Disks, CD-ROM drives and Floppy
try :
read_values(drive+'c')
drives.append(drive+'c')
except :
continue
except :
verboselog('Failed to list OpenBSD disks')
elif os.uname()[0]=="FreeBSD" :
try :
sysctl_kerndisks=os.popen('sysctl kern.disks')
kerndisks=string.strip(sysctl_kerndisks.readline())
for drive in string.split(kerndisks)[1:] :
if drive[:2] in ['md','cd','fd'] : continue # Ignore Memory Disks, CD-ROM drives and Floppy
try :
read_values(drive)
drives.append(drive)
except :
continue
except :
verboselog('Failed to list FreeBSD disks')
elif os.uname()[0]=="NetBSD" :
try :
sysctl_kerndisks=os.popen('sysctl hw.disknames')
kerndisks=string.strip(sysctl_kerndisks.readline())
for drive in string.split(kerndisks)[2:] :
if drive[:2] in ['md','cd','fd'] : continue # Ignore Memory Disks, CD-ROM drives and Floppy
try :
read_values(drive+'c')
drives.append(drive+'c')
except :
continue
except :
verboselog('Failed to list NetBSD disks')
elif os.uname()[0]=="SunOS" :
try :
from glob import glob
for drivepath in glob('/dev/rdsk/*s2') :
try :
drive=os.path.basename(drivepath)
read_values('rdsk'+drive)
drives.append('rdsk'+drive)
except :
continue
for drivepath in glob('/dev/rmt/*') :
try :
drive=os.path.basename(drivepath)
read_values('rmt'+drive)
drives.append('rmt'+drive)
except :
continue
except :
verboselog('Failed to list SunOS disks')
return(drives)
### Main part ###
smart_values={}
model=''
plugin_name=list(os.path.split(sys.argv[0]))[1]
verboselog('plugins\' UID: '+str(os.geteuid())+' / plugins\' GID: '+str(os.getegid()))
# Parse arguments
if len(sys.argv)>1 :
if sys.argv[1]=="config" :
hard_drive=get_hard_drive_name()
print_config(hard_drive,smart_values)
sys.exit(0)
elif sys.argv[1]=="autoconf" :
if os.path.exists(os.getenv('smartpath','/usr/sbin/smartctl')) :
print('yes')
sys.exit(0)
else :
print('no (smartmontools not found)')
sys.exit(1)
elif sys.argv[1]=="suggest" :
for drive in find_smart_drives() :
print(drive)
sys.exit(0)
elif sys.argv[1]!="" :
verboselog('unknown argument "'+sys.argv[1]+'"')
sys.exit(1)
# No argument given, doing the real job:
hard_drive=get_hard_drive_name()
read_values(hard_drive[0])
update_state_file()
print_plugin_values()