Add ceph plugin
Luis Eduardo committed
Dec 21, 2017 at 17:54 UTC
19a9841f394ae88b5971ce26fab94364e7a2858d
2 files changed
+188
conf.d/python.d/ceph.conf
new
+5
@@ -0,0 +1,5 @@
1
+update_every : 10
2
+local:
3
+ config_file: '/etc/ceph/ceph.conf'
4
+ keyring_file: '/etc/ceph/ceph.client.admin.keyring'
5
+ presentation_data: 'KB'
\ No newline at end of file
python.d/ceph.chart.py
new
+183
@@ -0,0 +1,183 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: ceph netdata python.d module
3
+# Author: Luis Eduardo (lets00)
4
+
5
+import rados
6
+import json
7
+from bases.FrameworkServices.SimpleService import SimpleService
8
+
9
+ORDER = ['general', 'pool_usage', 'pool_objects', 'osd_usage']
10
+
11
+CHARTS = {
12
+ 'general': {
13
+ 'options': [None, 'Ceph General Options', 'usage', 'general', 'ceph.general', 'line'],
14
+ 'lines': []
15
+ },
16
+ 'pool_usage': {
17
+ 'options': [None, 'Ceph Pools', 'usage', 'pool usage', 'ceph.pool_usage', 'line'],
18
+ 'lines': []
19
+ },
20
+ 'pool_objects': {
21
+ 'options': [None, 'Ceph Pools', 'objects', 'number of objects', 'ceph.pool_objects', 'line'],
22
+ 'lines': []
23
+ },
24
+ 'osd_usage': {
25
+ 'options': [None, 'Ceph OSDs', 'usage', 'osd usage', 'ceph.osd_usage', 'line'],
26
+ 'lines': []
27
+ }
28
+}
29
+
30
+PRESENTATION_MAP = {
31
+ 'KB': 1,
32
+ 'MB': 1024,
33
+ 'GB': 1024**2,
34
+ 'TB': 1024**3
35
+}
36
+
37
+
38
+class Service(SimpleService):
39
+ def __init__(self, configuration=None, name=None):
40
+ SimpleService.__init__(self, configuration=configuration, name=name)
41
+ self.order = ORDER
42
+ self.definitions = CHARTS
43
+ self.config_file = self.configuration.get('config_file')
44
+ self.keyring_file = self.configuration.get('keyring_file')
45
+ self.presentation_data = self.configuration.get('presentation_data')
46
+ self.presentation_value = None
47
+ self.cluster = rados.Rados(conffile=self.config_file,
48
+ conf=dict(keyring=self.keyring_file))
49
+
50
+ def check(self):
51
+ """
52
+ Checks module
53
+ :return:
54
+ """
55
+ if not (self.config_file and self.keyring_file):
56
+ self.error('config_file and/or keyring_file is not defined')
57
+ return False
58
+
59
+ # Set default presentation_data to GB if it does not defined and declare
60
+ # presentation_value with PRESENTATION_MAP value.
61
+ if not self.presentation_data:
62
+ self.presentation_data = 'GB'
63
+ self.presentation_value = PRESENTATION_MAP[self.presentation_data]
64
+ try:
65
+ self.cluster.connect()
66
+ except Exception as error:
67
+ self.error(error)
68
+ return False
69
+ self.create_definitions()
70
+ return True
71
+
72
+ def create_definitions(self):
73
+ """
74
+ Create dynamically charts options, add presentation data/value
75
+ set on YAML file
76
+ :return: None
77
+ """
78
+ # Options
79
+ self.definitions['general']['options'] = [None, 'Ceph General Options',
80
+ 'usage ({})'.format(self.presentation_data),
81
+ 'general', 'ceph.general', 'areastack']
82
+ self.definitions['pool_usage']['options'] = [None, 'Ceph Pools',
83
+ 'usage ({})'.format(self.presentation_data),
84
+ 'pool usage', 'ceph.pool_usage', 'line']
85
+ self.definitions['osd_usage']['options'] = [None, 'Ceph OSDs',
86
+ 'usage ({})'.format(self.presentation_data),
87
+ 'osd usage', 'ceph.osd_usage', 'line']
88
+
89
+ # Lines
90
+ self.definitions['general']['lines'].append(['general_total', 'Total',
91
+ 'absolute', 1, self.presentation_value])
92
+ self.definitions['general']['lines'].append(['general_usage', 'Usage',
93
+ 'absolute', 1, self.presentation_value])
94
+ self.definitions['general']['lines'].append(['general_available', 'Available',
95
+ 'absolute', 1, self.presentation_value])
96
+
97
+ for pool in self.cluster.list_pools():
98
+ self.definitions['pool_usage']['lines'].append([pool, pool,
99
+ 'absolute', 1, self.presentation_value])
100
+ self.definitions['pool_objects']['lines'].append(["obj_{}".format(pool), pool,
101
+ 'absolute'])
102
+
103
+ for osd in self._get_osds_df()['nodes']:
104
+ self.definitions['osd_usage']['lines'].append([osd['name'], osd['name'],
105
+ 'absolute', 1, self.presentation_value])
106
+
107
+ def get_data(self):
108
+ """
109
+ Catch all ceph data
110
+ :return: dict
111
+ """
112
+ try:
113
+ data = {}
114
+ data.update(self._get_general())
115
+ data.update(self._get_pool_usage())
116
+ data.update(self._get_pool_objects())
117
+ data.update(self._get_osd_usage())
118
+ return data
119
+ except (ValueError, AttributeError):
120
+ return None
121
+
122
+ def _get_general(self):
123
+ """
124
+ Get ceph's general usage
125
+ :return: dict
126
+ """
127
+ info = self.cluster.get_cluster_stats()
128
+ # Values are get in bytes, convert it
129
+ return {'general_total': float(info['kb']),
130
+ 'general_usage': float(info['kb_used']),
131
+ 'general_available': float(info['kb_avail'])
132
+ }
133
+
134
+ def _get_pool_usage(self):
135
+ """
136
+ Process raw data into pool usage dict information
137
+ :return: A pool dict with pool name's key and usage bytes' value
138
+ """
139
+ pool_usage = {}
140
+ for df in self._get_df()['pools']:
141
+ pool_usage[df['name']] = df['stats']['bytes_used']
142
+ return pool_usage
143
+
144
+ def _get_pool_objects(self):
145
+ """
146
+ Process raw data into pool usage dict information
147
+ :return: A pool dict with pool name's key and object numbers
148
+ """
149
+ pool_objects = {}
150
+ for df in self._get_df()['pools']:
151
+ pool_objects["obj_{}".format(df['name'])] = df['stats']['objects']
152
+ return pool_objects
153
+
154
+ def _get_osd_usage(self):
155
+ # TODO: FIX ME
156
+ """
157
+ Process raw data into osd dict information to get osd usage
158
+ :return: A osd dict with osd name's key and usage bytes' value
159
+ """
160
+ osd_usage = {}
161
+ for osd in self._get_osds_df()['nodes']:
162
+ osd_usage[osd['name']] = float(osd['kb_used'])
163
+ return osd_usage
164
+
165
+ def _get_df(self):
166
+ """
167
+ Get ceph df output
168
+ :return: ceph df --format json
169
+ """
170
+ return json.loads(self.cluster.mon_command(json.dumps({
171
+ 'prefix': 'df',
172
+ 'format': 'json'
173
+ }), '')[1])
174
+
175
+ def _get_osds_df(self):
176
+ """
177
+ Get ceph osd df output
178
+ :return: ceph osd df --format json
179
+ """
180
+ return json.loads(self.cluster.mon_command(json.dumps({
181
+ 'prefix': 'osd df',
182
+ 'format': 'json'
183
+ }), '')[1])