Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / sensors / freeipmi_ @ 97cf6d32

Historique | Voir | Annoter | Télécharger (7,3 ko)

1
#!/usr/bin/python
2
#
3
#    Copyright (C) 2011,2012  Andreas Thienemann <andreas@bawue.net>
4
#
5
#    This program is free software: you can redistribute it and/or modify
6
#    it under the terms of the GNU General Public License as published by
7
#    the Free Software Foundation, either version 3 of the License, or
8
#    (at your option) any later version.
9
#
10
#    This program is distributed in the hope that it will be useful,
11
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
#    GNU General Public License for more details.
14
#
15
#    You should have received a copy of the GNU General Public License
16
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
#
18

    
19
"""
20
=head1 NAME
21

    
22
freeipmi_ - Munin plugin to retreive temperature and fan speed measurements
23
from a local machine via IPMI.
24

    
25
=head1 APPLICABLE SYSTEMS
26

    
27
All machines with an IPMI capable baseboard management controller.
28

    
29
=head1 CONFIGURATION
30

    
31
On most supported systems this plugin works nearly out of the box as long as
32
both Python and the freeipmi binaries in a semi-recent version are installed.
33

    
34
If the machine works out of the box can be tested by calling bmc-info.
35
If there's text output, a bmc card was detected. If there's an entry for
36
"Sensor Device" visible in the "Additional Device Support" entry you're good.
37

    
38
If you get a "ipmi_cmd_get_device_id: driver timeout" message you have most
39
likely no bmc to query.
40

    
41
In certain cases however bmc-info will just seem to hang for quite some time.
42
In this case, autodetection does not work because the smbios table has
43
incorrect information. One system known to experience this problem is the
44
HP Proliant Microserver.
45

    
46
Adding env.freeipmi_args "--no-probing --driver-type=KCS --driver-address=0xca2 --register-spacing=1"
47
to the munin plugin configuration will make the plugin work. This is the
48
specific line for the HP Proliant Microserver mentioned above. Your mileage
49
may vary.
50

    
51
Basic configuration for every system is that the plugin needs to be called as root.
52

    
53
Add the following to your /etc/munin/plugin-conf.d/freeipmi:
54

    
55
 [freeipmi_*]
56
 user root
57

    
58
=head1 INTERPRETATION
59

    
60
The plugin shows the temperature in Celsius or the fanspeed in rotations per minute.
61

    
62
=head1 MAGIC MARKERS
63

    
64
  #%# family=contrib
65
  #%# capabilities=autoconf suggest
66

    
67
=head1 VERSION
68

    
69
0.0.1
70

    
71
=head1 BUGS
72

    
73
Only local support for now. Remote could be hacked in via freeipmi_args for now.
74

    
75
=head1 AUTHOR
76

    
77
Andreas Thienemann <andreas@bawue.net>
78

    
79
=head1 LICENSE
80

    
81
GPLv3+
82

    
83
=cut
84
"""
85

    
86
import subprocess
87
import sys
88
import os
89
import re
90
import pprint
91

    
92
# Parse some environment variables
93
if os.getenv("freeipmi_args") is not None:
94
    freeipmi_args = " %s" % (os.getenv("freeipmi_args"))
95
else:
96
    freeipmi_args = ""
97

    
98
# We are a wildcard plugin, figure out whether we are called for temp or fan
99
if sys.argv[0].split("_")[1] == "temp":
100
    mode = "Temperature"
101
elif sys.argv[0].split("_")[1] == "fan":
102
    mode = "Fan"
103
else:
104
    mode = None
105

    
106
def whereis(prog):
107
    """Check if prog can be found in the path and if yes, return the full pathname"""
108
    prog = os.path.basename(prog)
109
    for dir in os.getenv("PATH").split(":"):
110
        for root, dirs, files in os.walk(dir):
111
            if prog in files:
112
                return os.path.join(dir, prog)
113
    return None
114

    
115
def normalize_sensor(name):
116
    name = name.lower().replace("-","M").replace("+","P")
117
    name = re.sub("[^a-z0-9A-Z]","_", name)
118
    return name
119

    
120
# Code sniplet from Philipp Keller
121
# http://code.pui.ch/2007/02/19/set-timeout-for-a-shell-command-in-python/
122
def timeout_command(command, timeout):
123
    """call shell-command and either return its output or kill it
124
    if it doesn't normally exit within timeout seconds and return None"""
125
    import subprocess, datetime, os, time, signal
126
    start = datetime.datetime.now()
127
    process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
128
    while process.poll() is None:
129
        time.sleep(0.1)
130
        now = datetime.datetime.now()
131
        if (now - start).seconds> timeout:
132
            os.kill(process.pid, signal.SIGKILL)
133
            os.waitpid(-1, os.WNOHANG)
134
            return None
135
    return process.stdout.read()
136

    
137
def bmc_detect():
138
    """Check whether there's a baseboard management controller we can query."""
139
    if whereis("bmc-info") is None:
140
        print "no (bmc-info not found in path. Please install FreeIPMI.)"
141
        sys.exit(0)
142
    else:
143
        out = timeout_command("bmc-info%s" % (freeipmi_args), 2)
144
        if out is not None and "[Sensor Device]" in out:
145
            print "yes"
146
            sys.exit(0)
147
        else:
148
            print "no (no supported bmc found)"
149
            sys.exit(0)
150

    
151
def read_sensors():
152
    """Return all sensor data as a dict"""
153
    out = timeout_command("ipmi-sensors --verbose%s" % (freeipmi_args), 2)
154
    sensors = dict()
155
    sensor = dict()
156
    sensor_id = None
157
    for line in out.split("\n"):
158
        if ":" in line:
159
            k,v = line.split(": ")
160
            if k == "Record ID":
161
                sensor = dict()
162
                sensor_id = int(v)
163
                sensor[k] = v
164
            else:
165
                sensor[k] = v
166
        else:
167
            sensors[sensor_id] = sensor
168
    return sensors
169

    
170
def print_config():
171
    """Return configuration arguments for munin"""
172
    print "graph_title FreeIPMI Sensors: %s" % (mode)
173
    if mode == "Fan":
174
        print "graph_vlabel RPM"
175
        print "graph_info This graph shows the RPMs of the fans as reported by IPMI"
176
    elif mode == "Temperature":
177
        print "graph_vlabel Degrees C"
178
        print "graph_info This graph shows the temperatures as reported by IPMI"
179
    print "graph_category sensors"
180
    sensors = read_sensors()
181

    
182
    for id in sorted(sensors):
183
        if sensors[id]["Group Name"] == mode:
184
            label = normalize_sensor(sensors[id]["Sensor Name"])
185
            for n in ["Normal Max.", "Normal Min.", "Sensor Reading", "Lower Critical Threshold", "Upper Critical Threshold", "Lower Non-Critical Threshold", "Upper Non-Critical Threshold"]:
186
                sensors[id][n] = sensors[id][n].replace("NA","")
187
                sensors[id][n] = sensors[id][n].split('.')[0]
188

    
189
            print "%s.label %s" % (label, label)
190
            print "%s.warning %s:%s" % (label, sensors[id]["Lower Non-Critical Threshold"], sensors[id]["Upper Non-Critical Threshold"])
191
            print "%s.critical %s:%s" % (label, sensors[id]["Lower Critical Threshold"], sensors[id]["Upper Critical Threshold"])
192
            print "%s.graph_args --base 1000 -l 0" % (label)
193
            print "%s.graph_scale no" % (label)
194
#            pprint.pprint(sensors[id])
195
    sys.exit(0)
196

    
197
def fetch():
198
    sensors = read_sensors()
199

    
200
    for id in sorted(sensors):
201
        if sensors[id]["Group Name"] == mode:
202
            label = normalize_sensor(sensors[id]["Sensor Name"])
203
            print "%s.value %s" % (label, sensors[id]["Sensor Reading"].split(".")[0])
204
    sys.exit(0)
205

    
206

    
207
if "config" in sys.argv[1:]:
208
    print_config()
209

    
210
elif "autoconf" in sys.argv[1:]:
211
    bmc_detect()
212

    
213
elif "suggest" in sys.argv[1:]:
214
    sensors = read_sensors()
215
    fan, temperature = [0, 0]
216
    for id in sensors:
217
        if sensors[id]["Group Name"] == "Fan":
218
            fan += 1
219
        elif sensors[id]["Group Name"] == "Temperature":
220
            temperature += 1
221
    if fan > 0:
222
        print "fan"
223
    if temperature > 0:
224
        print "temp"
225
    sys.exit(0)
226

    
227
else:
228
    fetch()