Skip to content

Commit 4935a4f

Browse files
author
Ben Keith
committed
Support Python 3
Ran the module through 2to3 and fixed byte/str issues.
1 parent 28378aa commit 4935a4f

File tree

2 files changed

+89
-95
lines changed

2 files changed

+89
-95
lines changed

redis_client.py

+14-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import socket
22

3-
class RedisClient():
3+
4+
class RedisClient:
45
def __init__(self, host, port, auth):
56
self.host = host
67
self.port = port
@@ -10,10 +11,10 @@ def connect(self):
1011
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1112
self.socket.connect((self.host, self.port))
1213

13-
self.file = self.socket.makefile('r')
14+
self.file = self.socket.makefile("rb")
1415

1516
if self.auth is not None:
16-
self.send('auth %s' % (self.auth))
17+
self.send("auth %s" % (self.auth))
1718

1819
self.read_response()
1920

@@ -26,21 +27,21 @@ def __exit__(self, *args):
2627
self.socket.close()
2728

2829
def send(self, message):
29-
return self.socket.sendall("%s\r\n" % message)
30+
return self.socket.sendall(message.encode("utf-8") + b"\r\n")
3031

3132
def read_response(self):
32-
first_line = self.file.readline()
33-
if first_line.startswith('-'):
33+
first_line = self.file.readline().decode("utf-8")
34+
if first_line.startswith("-"):
3435
raise RedisError(first_line)
3536

36-
if first_line.startswith('*'):
37+
if first_line.startswith("*"):
3738
return self.read_array(first_line)
38-
elif first_line.startswith('$'):
39+
elif first_line.startswith("$"):
3940
return self.read_bulk_string(first_line)
40-
elif first_line.startswith(':'):
41-
return first_line.lstrip(':').rstrip()
42-
elif first_line.startswith('+'):
43-
return first_line.lstrip('+').rstrip()
41+
elif first_line.startswith(":"):
42+
return first_line.lstrip(":").rstrip()
43+
elif first_line.startswith("+"):
44+
return first_line.lstrip("+").rstrip()
4445
else:
4546
raise ValueError("Unknown Redis response: %s" % first_line)
4647

@@ -53,7 +54,7 @@ def read_bulk_string(self, first_line):
5354
if size == -1:
5455
return None
5556

56-
s = self.file.read(size)
57+
s = self.file.read(size).decode("utf-8")
5758
# Get rid of \r\n at end
5859
self.file.read(2)
5960

redis_info.py

+75-82
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@
2929
# collectd-python:
3030
# http://collectd.org/documentation/manpages/collectd-python.5.shtml
3131

32-
import collectd
33-
import socket
3432
import re
33+
import socket
34+
35+
import collectd
3536
from redis_client import RedisClient, RedisError
3637

3738

38-
class RedisCollector():
39+
class RedisCollector:
3940
def __init__(self, host, port, auth, instance, metric_types, verbose, llen_keys):
4041
self.host = host
4142
self.port = port
@@ -47,18 +48,17 @@ def __init__(self, host, port, auth, instance, metric_types, verbose, llen_keys)
4748

4849
def fetch_info(self, client):
4950
"""Request info from the Redis server"""
50-
self.log_verbose('Sending info command')
51-
client.send('info')
51+
self.log_verbose("Sending info command")
52+
client.send("info")
5253
try:
5354
data = client.read_response()
54-
except RedisError, e:
55-
collectd.error('redis_info plugin: Error response from %s:%d - %r'
56-
% (self.host, self.port, e))
55+
except RedisError as e:
56+
collectd.error("redis_info plugin: Error response from %s:%d - %r" % (self.host, self.port, e))
5757
return None
5858

59-
self.log_verbose('Received data: %s' % data)
59+
self.log_verbose("Received data: %s" % data)
6060

61-
linesep = '\r\n' if '\r\n' in data else '\n'
61+
linesep = "\r\n" if "\r\n" in data else "\n"
6262
info_dict = self.parse_info(data.split(linesep))
6363

6464
return info_dict
@@ -67,58 +67,51 @@ def parse_info(self, info_lines):
6767
"""Parse info response from Redis"""
6868
info = {}
6969
for line in info_lines:
70-
if "" == line or line.startswith('#'):
70+
if not line or line.startswith("#"):
7171
continue
7272

73-
if ':' not in line:
74-
collectd.warning('redis_info plugin: Bad format for info line: %s'
75-
% line)
73+
if ":" not in line:
74+
collectd.warning("redis_info plugin: Bad format for info line: %s" % line)
7675
continue
7776

78-
key, val = line.split(':')
77+
key, val = line.split(":")
7978

8079
# Handle multi-value keys (for dbs and slaves).
8180
# db lines look like "db0:keys=10,expire=0"
8281
# slave lines look like
8382
# "slave0:ip=192.168.0.181,port=6379,
8483
# state=online,offset=1650991674247,lag=1"
85-
if ',' in val:
86-
split_val = val.split(',')
84+
if "," in val:
85+
split_val = val.split(",")
8786
for sub_val in split_val:
88-
k, _, v = sub_val.rpartition('=')
87+
k, _, v = sub_val.rpartition("=")
8988
sub_key = "{0}_{1}".format(key, k)
9089
info[sub_key] = v
9190
else:
9291
info[key] = val
9392

9493
# compatibility with pre-2.6 redis (used changes_since_last_save)
95-
info["changes_since_last_save"] = info.get("changes_since_last_save",
96-
info.get(
97-
"rdb_changes_since_last_save"))
94+
info["changes_since_last_save"] = info.get("changes_since_last_save", info.get("rdb_changes_since_last_save"))
9895

9996
return info
10097

10198
def dispatch_info(self, client):
10299
info = self.fetch_info(client)
103100

104101
if not info:
105-
collectd.error('redis plugin: No info received')
102+
collectd.error("redis plugin: No info received")
106103
return
107104

108-
for keyTuple, val in self.metric_types.iteritems():
105+
for keyTuple, val in self.metric_types.items():
109106
key, mtype = keyTuple
110-
if key == 'total_connections_received' and mtype == 'counter':
111-
self.dispatch_value(info['total_connections_received'],
112-
'counter',
113-
type_instance='connections_received')
114-
elif key == 'total_commands_processed' and mtype == 'counter':
115-
self.dispatch_value(info['total_commands_processed'],
116-
'counter',
117-
type_instance='commands_processed')
107+
if key == "total_connections_received" and mtype == "counter":
108+
self.dispatch_value(info["total_connections_received"], "counter", type_instance="connections_received")
109+
elif key == "total_commands_processed" and mtype == "counter":
110+
self.dispatch_value(info["total_commands_processed"], "counter", type_instance="commands_processed")
118111
else:
119112
try:
120113
self.dispatch_value(info[key], mtype, type_instance=key)
121-
except KeyError:
114+
except (KeyError, TypeError):
122115
collectd.error("Metric %s not found in Redis INFO output" % key)
123116
continue
124117

@@ -129,19 +122,19 @@ def dispatch_list_lengths(self, client):
129122
database index.
130123
"""
131124
key_dict = {}
132-
for db, patterns in self.llen_keys.items():
133-
client.send('select %d' % db)
125+
for db, patterns in list(self.llen_keys.items()):
126+
client.send("select %d" % db)
134127
try:
135128
resp = client.read_response()
136-
except RedisError, e:
129+
except RedisError as e:
137130
collectd.error("Could not select Redis db %s: %s" % (db, e))
138131
continue
139132

140133
for pattern in patterns:
141134
keys = []
142135
# If there is a glob, get every key matching it
143-
if '*' in pattern:
144-
client.send('KEYS %s' % pattern)
136+
if "*" in pattern:
137+
client.send("KEYS %s" % pattern)
145138
keys = client.read_response()
146139
else:
147140
keys = [pattern]
@@ -150,70 +143,65 @@ def dispatch_list_lengths(self, client):
150143
self.fetch_and_dispatch_llen_for_key(client, key, db)
151144

152145
def fetch_and_dispatch_llen_for_key(self, client, key, db):
153-
client.send('llen %s' % key)
146+
client.send("llen %s" % key)
154147
try:
155148
val = client.read_response()
156-
except RedisError, e:
157-
collectd.warning('redis_info plugin: could not get length of key %s in db %s: %s' % (key, db, e))
149+
except RedisError as e:
150+
collectd.warning("redis_info plugin: could not get length of key %s in db %s: %s" % (key, db, e))
158151
return
159152

160-
dimensions = {'key_name': key, 'db_index': db}
161-
self.dispatch_value(val, 'gauge', type_instance='key_llen', dimensions=dimensions)
153+
dimensions = {"key_name": key, "db_index": db}
154+
self.dispatch_value(val, "gauge", type_instance="key_llen", dimensions=dimensions)
162155

163-
def dispatch_value(self, value, type, plugin_instance=None, type_instance=None,
164-
dimensions={}):
156+
def dispatch_value(self, value, type, plugin_instance=None, type_instance=None, dimensions={}):
165157
"""Read a key from info response data and dispatch a value"""
166158

167159
try:
168160
value = int(value)
169161
except ValueError:
170162
value = float(value)
171163

172-
self.log_verbose('Sending value: %s=%s (%s)' % (type_instance, value, dimensions))
164+
self.log_verbose("Sending value: %s=%s (%s)" % (type_instance, value, dimensions))
173165

174-
val = collectd.Values(plugin='redis_info')
166+
val = collectd.Values(plugin="redis_info")
175167
val.type = type
176168
val.type_instance = type_instance
177169
val.values = [value]
178170

179171
plugin_instance = self.instance
180172
if plugin_instance is None:
181-
plugin_instance = '{host}:{port}'.format(host=self.host,
182-
port=self.port)
173+
plugin_instance = "{host}:{port}".format(host=self.host, port=self.port)
183174

184175
val.plugin_instance = "{0}{1}".format(plugin_instance, _format_dimensions(dimensions))
185176

186177
# With some versions of CollectD, a dummy metadata map must be added
187178
# to each value for it to be correctly serialized to JSON by the
188179
# write_http plugin. See
189180
# https://github.com/collectd/collectd/issues/716
190-
val.meta = {'0': True}
181+
val.meta = {"0": True}
191182
val.dispatch()
192183

193184
def read_callback(self):
194185
try:
195186
self.get_metrics()
196-
except socket.error, e:
197-
collectd.error('redis_info plugin: Error connecting to %s:%d - %r'
198-
% (self.host, self.port, e))
187+
except socket.error as e:
188+
collectd.error("redis_info plugin: Error connecting to %s:%d - %r" % (self.host, self.port, e))
199189
return
200-
except RedisError, e:
201-
collectd.error('redis_info plugin: Error getting metrics from %s:%d - %r'
202-
% (self.host, self.port, e))
190+
except RedisError as e:
191+
collectd.error("redis_info plugin: Error getting metrics from %s:%d - %r" % (self.host, self.port, e))
203192
return
204193

205194
def get_metrics(self):
206195
with RedisClient(self.host, self.port, self.auth) as client:
207-
self.log_verbose('Connected to Redis at %s:%s' % (self.host, self.port))
196+
self.log_verbose("Connected to Redis at %s:%s" % (self.host, self.port))
208197

209198
self.dispatch_info(client)
210199
self.dispatch_list_lengths(client)
211200

212201
def log_verbose(self, msg):
213202
if not self.verbose:
214203
return
215-
collectd.info('redis plugin [verbose]: %s' % msg)
216-
204+
collectd.info("redis plugin [verbose]: %s" % msg)
217205

218206

219207
def configure_callback(conf):
@@ -229,44 +217,50 @@ def configure_callback(conf):
229217
for node in conf.children:
230218
key = node.key.lower()
231219
val = node.values[0]
232-
searchObj = re.search(r'redis_(.*)$', key, re.M | re.I)
220+
searchObj = re.search(r"redis_(.*)$", key, re.M | re.I)
233221

234-
if key == 'host':
222+
if key == "host":
235223
host = val
236-
elif key == 'port':
224+
elif key == "port":
237225
port = int(val)
238-
elif key == 'auth':
226+
elif key == "auth":
239227
auth = val
240-
elif key == 'verbose':
228+
elif key == "verbose":
241229
verbose = node.values[0]
242-
elif key == 'instance':
230+
elif key == "instance":
243231
instance = val
244-
elif key == 'sendlistlength':
232+
elif key == "sendlistlength":
245233
if (len(node.values)) == 2:
246234
llen_keys.setdefault(int(node.values[0]), []).append(node.values[1])
247235
else:
248-
collectd.warning("redis_info plugin: monitoring length of keys requires both \
249-
database index and key value")
236+
collectd.warning(
237+
"redis_info plugin: monitoring length of keys requires both \
238+
database index and key value"
239+
)
250240

251241
elif searchObj:
252242
metric_types[searchObj.group(1), val] = True
253243
else:
254-
collectd.warning('redis_info plugin: Unknown config key: %s.' %
255-
key)
244+
collectd.warning("redis_info plugin: Unknown config key: %s." % key)
256245
continue
257246

258247
if verbose:
259-
collectd.info('Configured with host=%s, port=%s, instance name=%s, using_auth=%s, llen_keys=%s'
260-
% (host, port, instance, (auth is not None), llen_keys))
261-
262-
collector = RedisCollector(**{
263-
'host': host,
264-
'port': port,
265-
'auth': auth,
266-
'instance': instance,
267-
'metric_types': metric_types,
268-
'verbose': verbose,
269-
'llen_keys': llen_keys})
248+
collectd.info(
249+
"Configured with host=%s, port=%s, instance name=%s, using_auth=%s, llen_keys=%s"
250+
% (host, port, instance, (auth is not None), llen_keys)
251+
)
252+
253+
collector = RedisCollector(
254+
**{
255+
"host": host,
256+
"port": port,
257+
"auth": auth,
258+
"instance": instance,
259+
"metric_types": metric_types,
260+
"verbose": verbose,
261+
"llen_keys": llen_keys,
262+
}
263+
)
270264

271265
collectd.register_read(collector.read_callback, name="%s:%s:%s" % (host, port, instance))
272266

@@ -286,10 +280,9 @@ def _format_dimensions(dimensions):
286280
if not dimensions:
287281
return ""
288282

289-
dim_pairs = ["%s=%s" % (k, v) for k, v in dimensions.iteritems()]
283+
dim_pairs = ["%s=%s" % (k, v) for k, v in dimensions.items()]
290284
return "[%s]" % (",".join(dim_pairs))
291285

292286

293-
294287
# register callbacks
295288
collectd.register_config(configure_callback)

0 commit comments

Comments
 (0)