contrib/collectd_unixsock.py: Support all plain text protocol commands
[collectd.git] / contrib / collectd_unixsock.py
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.
29
30 import socket
31
32
33 class Collectd():
34
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)
39
40     def flush(self, timeout=None, plugins=[], identifiers=[]):
41         """Send a FLUSH command.
42
43         Full documentation:
44             http://collectd.org/wiki/index.php/Plain_text_protocol#FLUSH
45
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))
60
61     def getthreshold(self, identifier):
62         """Send a GETTHRESHOLD command.
63
64         Full documentation:
65             http://collectd.org/wiki/index.php/Plain_text_protocol#GETTHRESHOLD
66
67         """
68         numvalues = self._cmd('GETTHRESHOLD "%s"' % identifier)
69         lines = []
70         if numvalues:
71             lines = self._readlines(numvalues)
72         return lines
73
74     def getval(self, identifier, flush_after=True):
75         """Send a GETVAL command.
76
77         Also flushes the identifier if flush_after is True.
78
79         Full documentation:
80             http://collectd.org/wiki/index.php/Plain_text_protocol#GETVAL
81
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
90
91     def listval(self):
92         """Send a LISTVAL command.
93
94         Full documentation:
95             http://collectd.org/wiki/index.php/Plain_text_protocol#LISTVAL
96
97         """
98         numvalues = self._cmd('LISTVAL')
99         lines = []
100         if numvalues:
101             lines = self._readlines(numvalues)
102         return lines
103
104     def putnotif(self, message, options={}):
105         """Send a PUTNOTIF command.
106
107         Options must be passed as a Python dictionary. Example:
108           options={'severity': 'failure', 'host': 'example.com'}
109
110         Full documentation:
111             http://collectd.org/wiki/index.php/Plain_text_protocol#PUTNOTIF
112
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))
120
121     def putval(self, identifier, values, options={}):
122         """Send a PUTVAL command.
123
124         Options must be passed as a Python dictionary. Example:
125           options={'interval': 10}
126
127         Full documentation:
128             http://collectd.org/wiki/index.php/Plain_text_protocol#PUTVAL
129
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))
139
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
153
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)
166
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
180
181     def __del__(self):
182         self._sock.close()
183
184
185 if __name__ == '__main__':
186     """Collect values from socket and dump to STDOUT"""
187
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
194
195         values = c.getval(identifier)
196         print "\tValue list: %s" % ', '.join(values)
197
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)