1 #-*- coding: ISO-8859-1 -*-
2 # collect.py: the python collectd-unixsock module.
3 #
4 # Requires collectd to be configured with the unixsock plugin, like so:
5 #
6 # LoadPlugin unixsock
7 # <Plugin unixsock>
8 # SocketFile "/var/run/collectd-unixsock"
9 # SocketPerms "0775"
10 # </Plugin>
11 #
12 # Copyright (C) 2008 Clay Loveless <clay@killersoft.com>
13 #
14 # This software is provided 'as-is', without any express or implied
15 # warranty. In no event will the author be held liable for any damages
16 # arising from the use of this software.
17 #
18 # Permission is granted to anyone to use this software for any purpose,
19 # including commercial applications, and to alter it and redistribute it
20 # freely, subject to the following restrictions:
21 #
22 # 1. The origin of this software must not be misrepresented; you must not
23 # claim that you wrote the original software. If you use this software
24 # in a product, an acknowledgment in the product documentation would be
25 # appreciated but is not required.
26 # 2. Altered source versions must be plainly marked as such, and must not be
27 # misrepresented as being the original software.
28 # 3. This notice may not be removed or altered from any source distribution.
30 import socket
33 class Collectd():
35 def __init__(self, path='/var/run/collectd-unixsock', noisy=False):
36 self.noisy = noisy
37 self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
38 self._sock.connect(path)
40 def flush(self, timeout=None, plugins=[], identifiers=[]):
41 """Send a FLUSH command.
43 Full documentation:
44 http://collectd.org/wiki/index.php/Plain_text_protocol#FLUSH
46 """
47 # have to pass at least one plugin or identifier
48 if not plugins and not identifiers:
49 return None
50 args = []
51 if timeout:
52 args.append("timeout=%s" % timeout)
53 if plugins:
54 plugin_args = map(lambda x: "plugin=%s" % x, plugins)
55 args.extend(plugin_args)
56 if identifiers:
57 identifier_args = map(lambda x: "identifier=%s" % x, identifiers)
58 args.extend(identifier_args)
59 return self._cmd('FLUSH %s' % ' '.join(args))
61 def getthreshold(self, identifier):
62 """Send a GETTHRESHOLD command.
64 Full documentation:
65 http://collectd.org/wiki/index.php/Plain_text_protocol#GETTHRESHOLD
67 """
68 numvalues = self._cmd('GETTHRESHOLD "%s"' % identifier)
69 lines = []
70 if numvalues:
71 lines = self._readlines(numvalues)
72 return lines
74 def getval(self, identifier, flush_after=True):
75 """Send a GETVAL command.
77 Also flushes the identifier if flush_after is True.
79 Full documentation:
80 http://collectd.org/wiki/index.php/Plain_text_protocol#GETVAL
82 """
83 numvalues = self._cmd('GETVAL "%s"' % identifier)
84 lines = []
85 if numvalues:
86 lines = self._readlines(numvalues)
87 if flush_after:
88 self.flush(identifiers=[identifier])
89 return lines
91 def listval(self):
92 """Send a LISTVAL command.
94 Full documentation:
95 http://collectd.org/wiki/index.php/Plain_text_protocol#LISTVAL
97 """
98 numvalues = self._cmd('LISTVAL')
99 lines = []
100 if numvalues:
101 lines = self._readlines(numvalues)
102 return lines
104 def putnotif(self, message, options={}):
105 """Send a PUTNOTIF command.
107 Options must be passed as a Python dictionary. Example:
108 options={'severity': 'failure', 'host': 'example.com'}
110 Full documentation:
111 http://collectd.org/wiki/index.php/Plain_text_protocol#PUTNOTIF
113 """
114 args = []
115 if options:
116 options_args = map(lambda x: "%s=%s" % (x, options[x]), options)
117 args.extend(options_args)
118 args.append('message="%s"' % message)
119 return self._cmd('PUTNOTIF %s' % ' '.join(args))
121 def putval(self, identifier, values, options={}):
122 """Send a PUTVAL command.
124 Options must be passed as a Python dictionary. Example:
125 options={'interval': 10}
127 Full documentation:
128 http://collectd.org/wiki/index.php/Plain_text_protocol#PUTVAL
130 """
131 args = []
132 args.append('"%s"' % identifier)
133 if options:
134 options_args = map(lambda x: "%s=%s" % (x, options[x]), options)
135 args.extend(options_args)
136 values = map(str, values)
137 args.append(':'.join(values))
138 return self._cmd('PUTVAL %s' % ' '.join(args))
140 def _cmd(self, c):
141 if self.noisy:
142 print "[send] %s" % c
143 self._sock.send(c + "\n")
144 status_message = self._readline()
145 if self.noisy:
146 print "[recive] %s" % status_message
147 if not status_message:
148 return None
149 code, message = status_message.split(' ', 1)
150 if int(code):
151 return int(code)
152 return False
154 def _readline(self):
155 """Read single line from socket"""
156 data = ''
157 buf = []
158 recv = self._sock.recv
159 while data != "\n":
160 data = recv(1)
161 if not data:
162 break
163 if data != "\n":
164 buf.append(data)
165 return ''.join(buf)
167 def _readlines(self, sizehint=0):
168 """Read multiple lines from socket"""
169 total = 0
170 list = []
171 while True:
172 line = self._readline()
173 if not line:
174 break
175 list.append(line)
176 total = len(list)
177 if sizehint and total >= sizehint:
178 break
179 return list
181 def __del__(self):
182 self._sock.close()
185 if __name__ == '__main__':
186 """Collect values from socket and dump to STDOUT"""
188 c = Collectd('/var/run/collectd-unixsock', noisy=True)
189 list = c.listval()
190 for val in list:
191 stamp, identifier = val.split()
192 print "\n%s" % identifier
193 print "\tUpdate time: %s" % stamp
195 values = c.getval(identifier)
196 print "\tValue list: %s" % ', '.join(values)
198 # don't fetch thresholds by default because collectd will crash
199 # if there is no treshold for the given identifier
200 #thresholds = c.getthreshold(identifier)
201 #print "\tThresholds: %s" % ', '.join(thresholds)