198 lines
6.3 KiB
Python
198 lines
6.3 KiB
Python
# Copyright 2016 FUJITSU LIMITED
|
|
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
|
|
# Copyright 2017 SUSE Linux GmbH
|
|
#
|
|
# 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
|
|
import os
|
|
|
|
import requests
|
|
|
|
from monasca_setup import agent_config
|
|
from monasca_setup import detection
|
|
from monasca_setup.detection import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
_KIBANA_CFG_FILE = '/opt/kibana/config/kibana.yml'
|
|
_API_STATUS = 'api/status'
|
|
_METRIC_ALIASES = {
|
|
'heap_total': 'heap_size',
|
|
'requests_per_second': 'req_sec',
|
|
'response_time_avg': 'resp_time_avg',
|
|
'response_time_max': 'resp_time_max'
|
|
}
|
|
|
|
|
|
def _to_snake_case(word):
|
|
final = ''
|
|
for item in word:
|
|
if item.isupper():
|
|
final += "_" + item.lower()
|
|
else:
|
|
final += item
|
|
if final[0] == "_":
|
|
final = final[1:]
|
|
return final
|
|
|
|
|
|
def get_metric_name(metric):
|
|
actual_name = _to_snake_case(metric)
|
|
return _METRIC_ALIASES.get(actual_name, actual_name)
|
|
|
|
|
|
class Kibana(detection.Plugin):
|
|
def _detect(self):
|
|
# check process and port
|
|
|
|
process_found = utils.find_process_cmdline('kibana') is not None
|
|
has_deps = self.dependencies_installed()
|
|
|
|
has_args = self.args is not None
|
|
cfg_file = self._get_config_file() if has_args else _KIBANA_CFG_FILE
|
|
has_config_file = os.path.isfile(cfg_file)
|
|
|
|
available = process_found and has_deps and has_config_file
|
|
|
|
self.available = available
|
|
|
|
if not self.available:
|
|
err_str = 'Plugin for Kibana will not be configured.'
|
|
if not process_found:
|
|
LOG.info('Kibana process has not been found. %s' % err_str)
|
|
elif not has_deps:
|
|
LOG.error('Kibana plugin dependencies are not satisfied. '
|
|
'Module "pyaml" not found. %s'
|
|
% err_str)
|
|
elif not has_config_file:
|
|
LOG.warning('Kibana plugin cannot find configuration file %s. %s'
|
|
% (cfg_file, err_str))
|
|
|
|
def build_config(self):
|
|
kibana_config = self._get_config_file()
|
|
|
|
try:
|
|
(kibana_host,
|
|
kibana_port,
|
|
kibana_protocol) = self._read_config(kibana_config)
|
|
except Exception as ex:
|
|
LOG.error('Failed to read configuration at %s' % kibana_config)
|
|
LOG.exception(ex)
|
|
return
|
|
|
|
if kibana_protocol == 'https':
|
|
LOG.error('"https" protocol is currently not supported')
|
|
return None
|
|
|
|
config = agent_config.Plugins()
|
|
|
|
# retrieve user name and set in config
|
|
# if passed in args (note args are optional)
|
|
if (self.args and 'kibana-user' in self.args and
|
|
self.args['kibana-user']):
|
|
process = detection.watch_process_by_username(
|
|
username=self.args['kibana-user'],
|
|
process_name='kibana',
|
|
service='monitoring',
|
|
component='kibana'
|
|
)
|
|
else:
|
|
process = detection.watch_process(['kibana'],
|
|
service='monitoring',
|
|
component='kibana',
|
|
process_name='kibana')
|
|
|
|
config.merge(process)
|
|
|
|
kibana_url = '%s://%s:%d' % (
|
|
kibana_protocol,
|
|
kibana_host,
|
|
kibana_port
|
|
)
|
|
|
|
if not self._has_metrics_support(kibana_url):
|
|
LOG.warning('Running kibana does not support metrics, skipping...')
|
|
return None
|
|
else:
|
|
metrics = self._get_all_metrics(kibana_url)
|
|
config['kibana'] = {
|
|
'init_config': {
|
|
'url': '%s/%s' % (kibana_url, _API_STATUS),
|
|
},
|
|
'instances': [
|
|
{
|
|
"name": kibana_url,
|
|
'metrics': metrics
|
|
}
|
|
]
|
|
}
|
|
|
|
LOG.info('\tWatching the kibana process.')
|
|
|
|
return config
|
|
|
|
def dependencies_installed(self):
|
|
try:
|
|
import yaml # noqa
|
|
except Exception:
|
|
return False
|
|
return True
|
|
|
|
def _get_config_file(self):
|
|
if self.args is not None:
|
|
kibana_config = self.args.get('kibana-config', _KIBANA_CFG_FILE)
|
|
else:
|
|
kibana_config = _KIBANA_CFG_FILE
|
|
return kibana_config
|
|
|
|
@staticmethod
|
|
def _read_config(kibana_cfg):
|
|
import yaml
|
|
with open(kibana_cfg, 'r') as stream:
|
|
document = yaml.safe_load(stream=stream)
|
|
|
|
has_ssl_support = ('server.ssl.cert' in document and
|
|
'server.ssl.key' in document)
|
|
|
|
host = document.get('server.host')
|
|
port = int(document.get('server.port'))
|
|
protocol = 'https' if has_ssl_support else 'http'
|
|
|
|
return host, port, protocol
|
|
|
|
def _get_all_metrics(self, kibana_url):
|
|
resp = self._get_metrics_request(kibana_url)
|
|
data = resp.json()
|
|
|
|
metrics = []
|
|
# do not check plugins, check will go for overall status
|
|
|
|
# get metrics
|
|
for metric in data.get('metrics').keys():
|
|
metrics.append(get_metric_name(metric))
|
|
|
|
return metrics
|
|
|
|
def _has_metrics_support(self, kibana_url):
|
|
resp = self._get_metrics_request(kibana_url, method='HEAD')
|
|
status_code = resp.status_code
|
|
# Some Kibana versions may respond with 400:Bad Request
|
|
# it means that URL is available but simply does
|
|
# not support HEAD request
|
|
return (status_code == 400) or (status_code == 200)
|
|
|
|
def _get_metrics_request(self, url, method='GET'):
|
|
request_url = '%s/%s' % (url, _API_STATUS)
|
|
return requests.request(method=method, url=request_url)
|