monasca-agent/monasca_setup/detection/plugins/influxdb_relay.py

189 lines
6.6 KiB
Python

# Copyright 2017 FUJITSU LIMITED
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from oslo_utils import importutils
from monasca_setup import agent_config
from monasca_setup import detection
from monasca_setup.detection import utils
LOG = logging.getLogger(__name__)
class InfluxDBRelay(detection.Plugin):
"""Detects influxdb-relay and sets up its monitoring
Monitored items:
* process
* http_check
"""
PROC_NAME = 'influxdb-relay'
"""Name of the InfluxDB Relay process expected to be found in the system"""
DEFAULTS = {
'bind_address': '127.0.0.1',
'bind_port': 9096
}
RELAY_NODE_ARG_NAME = 'influxdb_relay_node'
def _detect(self):
"""Run detection, set self.available True if the service is detected.
"""
proc = utils.find_process_name(self.PROC_NAME)
process_found = proc is not None
config_file = self._get_config_file(proc) if process_found else None
config_file_found = config_file is not None
dependencies_installed = self.dependencies_installed()
self.available = (process_found and config_file_found
and dependencies_installed)
if not self.available:
err_chunks = []
if not process_found:
err_chunks.append('\tinfluxdb-relay plugin cannot locate '
'"%s" process.' % self.PROC_NAME)
elif not config_file_found:
err_chunks.append('\tinfluxdb-relay plugin cannot locate '
'configuration file.')
elif not dependencies_installed:
err_chunks.append('\tinfluxdb-relay plugin requires "toml" '
'to be installed')
LOG.warning('Plugin for influxdb-relay will not be configured.\n'
'Following issue have to be resolved: %s' %
'\n'.join(err_chunks))
else:
self._config = self._load_config(config_file)
def build_config(self):
"""Build the config as a Plugins object and return."""
LOG.info("\tEnabling the influxdb-relay check")
config = agent_config.Plugins()
config.merge(self._monitor_process())
config.merge(self._monitor_endpoint())
return config
def _monitor_process(self):
LOG.info("\tMonitoring the influxdb-relay process")
dimensions = {}
if self.args and self.args.get(self.RELAY_NODE_ARG_NAME):
dimensions.update({self.RELAY_NODE_ARG_NAME: self.args.get(self.RELAY_NODE_ARG_NAME)})
return detection.watch_process([self.PROC_NAME],
service='influxdb',
component='influxdb-relay',
exact_match=False,
dimensions=dimensions)
def _monitor_endpoint(self):
config = agent_config.Plugins()
http_conf = self._config.get('http', None)
if isinstance(http_conf, list):
http_conf = http_conf[0]
if http_conf:
host, port = self._explode_bind_address(http_conf)
listening = utils.find_addrs_listening_on_port(port)
if listening:
LOG.info("\tMonitoring the influxdb-relay ping endpoint")
dimensions = {'service': 'influxdb',
'component': 'influxdb-relay'}
if self.args and self.args.get(self.RELAY_NODE_ARG_NAME):
dimensions.update(
{self.RELAY_NODE_ARG_NAME: self.args.get(self.RELAY_NODE_ARG_NAME)})
instance = {
'name': 'influxdb-relay',
'url': 'http://%s:%d/ping' % (host, port),
'dimensions': dimensions
}
config['http_check'] = {
'init_config': None,
'instances': [instance]
}
else:
LOG.warning('\tinfluxdb-relay[http] is enabled but nothing '
'could be found listening at %d port. '
'It might have happened that process '
'was just killed and hence port %d '
'was released.', port, port)
return config
def dependencies_installed(self):
return importutils.try_import('toml', False)
@staticmethod
def _explode_bind_address(http_conf):
bind_address = http_conf['bind-addr']
path, port = bind_address.split(':')
if not path:
path = InfluxDBRelay.DEFAULTS['bind_address']
if not port:
port = InfluxDBRelay.DEFAULTS['bind_port']
return path, int(port)
@staticmethod
def _load_config(config_file):
"""Loads toml configuration from specified path.
Method loads configuration from specified `path`
and parses it with :py:class:`configparser.RawConfigParser`
"""
try:
return importutils.import_module('toml').load(config_file)
except Exception as ex:
LOG.error('Failed to parse %s', config_file)
LOG.exception(ex)
@staticmethod
def _get_config_file(proc):
"""Tries to retrieve config file location.
influxdb-relay is launched with ```-config```
flag in cmdline. That fact is used by this method.
If, by any mean, that switch is not part of
:py:method:`psutil.Process.cmdline`, method simply
fallbacks to None
:param proc: current process, :py:class:`psutil.Process`
:type proc: :py:class:`psutil.Process`
:return: config file path or None
:rtype: str or None
"""
cmdline = proc.as_dict(attrs=['cmdline'])['cmdline']
config_flag = '-config'
if config_flag in cmdline:
pos = cmdline.index(config_flag)
return cmdline[pos + 1]
LOG.warning('%s switch was not found in influxdb-relay cmdline',
config_flag)
return None