-
Notifications
You must be signed in to change notification settings - Fork 594
Expand file tree
/
Copy pathceph.py
More file actions
146 lines (121 loc) · 4.55 KB
/
ceph.py
File metadata and controls
146 lines (121 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# coding=utf-8
"""
The CephCollector collects utilization info from the Ceph storage system.
Documentation for ceph perf counters:
http://docs.ceph.com/docs/master/dev/perf_counters/
#### Dependencies
* ceph [http://ceph.com/]
"""
try:
import json
except ImportError:
import simplejson as json
import glob
import os
import subprocess
import diamond.collector
def flatten_dictionary(input, sep='.', prefix=None):
"""Produces iterator of pairs where the first value is
the joined key names and the second value is the value
associated with the lowest level key. For example::
{'a': {'b': 10},
'c': 20,
}
produces::
[('a.b', 10), ('c', 20)]
"""
for name, value in sorted(input.items()):
fullname = sep.join([_f for _f in [prefix, name] if _f])
if isinstance(value, dict):
for result in flatten_dictionary(value, sep, fullname):
yield result
else:
yield (fullname, value)
class CephCollector(diamond.collector.Collector):
def get_default_config_help(self):
config_help = super(CephCollector, self).get_default_config_help()
config_help.update({
'socket_path': 'The location of the ceph monitoring sockets.'
' Defaults to "/var/run/ceph"',
'socket_prefix': 'The first part of all socket names.'
' Defaults to "ceph-"',
'socket_ext': 'Extension for socket filenames.'
' Defaults to "asok"',
'ceph_binary': 'Path to "ceph" executable. '
'Defaults to /usr/bin/ceph.',
})
return config_help
def get_default_config(self):
"""
Returns the default collector settings
"""
config = super(CephCollector, self).get_default_config()
config.update({
'socket_path': '/var/run/ceph',
'socket_prefix': 'ceph-',
'socket_ext': 'asok',
'ceph_binary': '/usr/bin/ceph',
})
return config
def _get_socket_paths(self):
"""Return a sequence of paths to sockets for communicating
with ceph daemons.
"""
socket_pattern = os.path.join(self.config['socket_path'],
(self.config['socket_prefix'] +
'*.' + self.config['socket_ext']))
return glob.glob(socket_pattern)
def _get_counter_prefix_from_socket_name(self, name):
"""Given the name of a UDS socket, return the prefix
for counters coming from that source.
"""
base = os.path.splitext(os.path.basename(name))[0]
if base.startswith(self.config['socket_prefix']):
base = base[len(self.config['socket_prefix']):]
return 'ceph.' + base
def _get_stats_from_socket(self, name):
"""Return the parsed JSON data returned when ceph is told to
dump the stats from the named socket.
In the event of an error error, the exception is logged, and
an empty result set is returned.
"""
try:
json_blob = subprocess.check_output(
[self.config['ceph_binary'],
'--admin-daemon',
name,
'perf',
'dump',
])
except subprocess.CalledProcessError as err:
self.log.info('Could not get stats from %s: %s',
name, err)
self.log.exception('Could not get stats from %s' % name)
return {}
try:
json_data = json.loads(json_blob)
except Exception as err:
self.log.info('Could not parse stats from %s: %s',
name, err)
self.log.exception('Could not parse stats from %s' % name)
return {}
return json_data
def _publish_stats(self, counter_prefix, stats):
"""Given a stats dictionary from _get_stats_from_socket,
publish the individual values.
"""
for stat_name, stat_value in flatten_dictionary(
stats,
prefix=counter_prefix,
):
self.publish_gauge(stat_name, stat_value)
def collect(self):
"""
Collect stats
"""
for path in self._get_socket_paths():
self.log.debug('checking %s', path)
counter_prefix = self._get_counter_prefix_from_socket_name(path)
stats = self._get_stats_from_socket(path)
self._publish_stats(counter_prefix, stats)
return