]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/influx/module.py
1 from datetime
import datetime
2 from threading
import Event
8 from mgr_module
import MgrModule
11 from influxdb
import InfluxDBClient
12 from influxdb
.exceptions
import InfluxDBClientError
13 from requests
.exceptions
import ConnectionError
18 class Module(MgrModule
):
21 "cmd": "influx config-set name=key,type=CephString "
22 "name=value,type=CephString",
23 "desc": "Set a configuration value",
27 "cmd": "influx config-show",
28 "desc": "Show current configuration",
33 "desc": "Force sending data to Influx",
37 "cmd": "influx self-test",
38 "desc": "debug the module",
54 def __init__(self
, *args
, **kwargs
):
55 super(Module
, self
).__init
__(*args
, **kwargs
)
61 return self
.get('mon_map')['fsid']
63 def get_latest(self
, daemon_type
, daemon_name
, stat
):
64 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
70 def get_df_stats(self
):
74 now
= datetime
.utcnow().isoformat() + 'Z'
91 for df_type
in df_types
:
92 for pool
in df
['pools']:
94 "measurement": "ceph_pool_stats",
96 "pool_name": pool
['name'],
97 "pool_id": pool
['id'],
98 "type_instance": df_type
,
99 "fsid": self
.get_fsid()
103 "value": pool
['stats'][df_type
],
109 def get_daemon_stats(self
):
112 now
= datetime
.utcnow().isoformat() + 'Z'
114 for daemon
, counters
in six
.iteritems(self
.get_all_perf_counters()):
115 svc_type
, svc_id
= daemon
.split(".", 1)
116 metadata
= self
.get_metadata(svc_type
, svc_id
)
118 for path
, counter_info
in counters
.items():
119 if counter_info
['type'] & self
.PERFCOUNTER_HISTOGRAM
:
122 value
= counter_info
['value']
125 "measurement": "ceph_daemon_stats",
127 "ceph_daemon": daemon
,
128 "type_instance": path
,
129 "host": metadata
['hostname'],
130 "fsid": self
.get_fsid()
140 def set_config_option(self
, option
, value
):
141 if option
not in self
.config_keys
.keys():
142 raise RuntimeError('{0} is a unknown configuration '
143 'option'.format(option
))
145 if option
in ['port', 'interval']:
148 except (ValueError, TypeError):
149 raise RuntimeError('invalid {0} configured. Please specify '
150 'a valid integer'.format(option
))
152 if option
== 'interval' and value
< 5:
153 raise RuntimeError('interval should be set to at least 5 seconds')
155 if option
in ['ssl', 'verify_ssl']:
156 value
= value
.lower() == 'true'
158 self
.config
[option
] = value
160 def init_module_config(self
):
161 self
.config
['hostname'] = \
162 self
.get_config("hostname", default
=self
.config_keys
['hostname'])
163 self
.config
['port'] = \
164 int(self
.get_config("port", default
=self
.config_keys
['port']))
165 self
.config
['database'] = \
166 self
.get_config("database", default
=self
.config_keys
['database'])
167 self
.config
['username'] = \
168 self
.get_config("username", default
=self
.config_keys
['username'])
169 self
.config
['password'] = \
170 self
.get_config("password", default
=self
.config_keys
['password'])
171 self
.config
['interval'] = \
172 int(self
.get_config("interval",
173 default
=self
.config_keys
['interval']))
174 ssl
= self
.get_config("ssl", default
=self
.config_keys
['ssl'])
175 self
.config
['ssl'] = ssl
.lower() == 'true'
177 self
.get_config("verify_ssl", default
=self
.config_keys
['verify_ssl'])
178 self
.config
['verify_ssl'] = verify_ssl
.lower() == 'true'
180 def send_to_influx(self
):
181 if not self
.config
['hostname']:
182 self
.log
.error("No Influx server configured, please set one using: "
183 "ceph influx config-set hostname <hostname>")
184 self
.set_health_checks({
185 'MGR_INFLUX_NO_SERVER': {
186 'severity': 'warning',
187 'summary': 'No InfluxDB server configured',
188 'detail': ['Configuration option hostname not set']
193 # If influx server has authentication turned off then
194 # missing username/password is valid.
195 self
.log
.debug("Sending data to Influx host: %s",
196 self
.config
['hostname'])
197 client
= InfluxDBClient(self
.config
['hostname'], self
.config
['port'],
198 self
.config
['username'],
199 self
.config
['password'],
200 self
.config
['database'],
202 self
.config
['verify_ssl'])
204 # using influx client get_list_database requires admin privs,
205 # instead we'll catch the not found exception and inform the user if
206 # db can not be created
208 client
.write_points(self
.get_df_stats(), 'ms')
209 client
.write_points(self
.get_daemon_stats(), 'ms')
210 self
.set_health_checks(dict())
211 except ConnectionError
as e
:
212 self
.log
.exception("Failed to connect to Influx host %s:%d",
213 self
.config
['hostname'], self
.config
['port'])
214 self
.set_health_checks({
215 'MGR_INFLUX_SEND_FAILED': {
216 'severity': 'warning',
217 'summary': 'Failed to send data to InfluxDB server at %s:%d'
218 ' due to an connection error'
219 % (self
.config
['hostname'], self
.config
['port']),
223 except InfluxDBClientError
as e
:
225 self
.log
.info("Database '%s' not found, trying to create "
226 "(requires admin privs). You can also create "
227 "manually and grant write privs to user "
228 "'%s'", self
.config
['database'],
229 self
.config
['username'])
230 client
.create_database(self
.config
['database'])
232 self
.set_health_checks({
233 'MGR_INFLUX_SEND_FAILED': {
234 'severity': 'warning',
235 'summary': 'Failed to send data to InfluxDB',
242 self
.log
.info('Stopping influx module')
246 def handle_command(self
, cmd
):
247 if cmd
['prefix'] == 'influx config-show':
248 return 0, json
.dumps(self
.config
), ''
249 elif cmd
['prefix'] == 'influx config-set':
253 return -errno
.EINVAL
, '', 'Value should not be empty or None'
255 self
.log
.debug('Setting configuration option %s to %s', key
, value
)
256 self
.set_config_option(key
, value
)
257 self
.set_config(key
, value
)
258 return 0, 'Configuration option {0} updated'.format(key
), ''
259 elif cmd
['prefix'] == 'influx send':
260 self
.send_to_influx()
261 return 0, 'Sending data to Influx', ''
262 if cmd
['prefix'] == 'influx self-test':
263 daemon_stats
= self
.get_daemon_stats()
264 assert len(daemon_stats
)
265 df_stats
= self
.get_df_stats()
268 'daemon_stats': daemon_stats
,
272 return 0, json
.dumps(result
, indent
=2), 'Self-test OK'
274 return (-errno
.EINVAL
, '',
275 "Command not found '{0}'".format(cmd
['prefix']))
278 if InfluxDBClient
is None:
279 self
.log
.error("Cannot transmit statistics: influxdb python "
280 "module not found. Did you install it?")
283 self
.log
.info('Starting influx module')
284 self
.init_module_config()
289 self
.send_to_influx()
290 runtime
= time
.time() - start
291 self
.log
.debug('Finished sending data in Influx in %.3f seconds',
293 self
.log
.debug("Sleeping for %d seconds", self
.config
['interval'])
294 self
.event
.wait(self
.config
['interval'])