Distinguish between Java and Python for Mon
Detection plugins for monasca exists in particular for monasca-api and monasca-persister. However they only support proper detection of java based implementations. Following change provides support for python based implementation. Additionally written several missing unit tests for Mon* detection plugins. Fixes-Bug: #1642567 Change-Id: I76df0dec03ade146b96cc59e8dd9f9c394f30262
This commit is contained in:
parent
8d133c4013
commit
a442ee9dcb
|
@ -1,4 +1,5 @@
|
|||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
|
||||
# Copyright 2016 FUJITSU LIMITED
|
||||
|
||||
"""Classes for monitoring the monitoring server stack.
|
||||
|
||||
|
@ -9,22 +10,53 @@
|
|||
import logging
|
||||
import yaml
|
||||
|
||||
from six.moves import configparser
|
||||
|
||||
import monasca_setup.agent_config
|
||||
import monasca_setup.detection
|
||||
from monasca_setup.detection import find_process_cmdline
|
||||
from monasca_setup.detection import find_process_name
|
||||
from monasca_setup.detection import watch_process
|
||||
from monasca_setup.detection import watch_process_by_username
|
||||
from monasca_setup.detection.utils import watch_process
|
||||
from monasca_setup.detection.utils import watch_process_by_username
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_PYTHON_LANG_MARKERS = 'python', 'gunicorn',
|
||||
"""List of all strings that if found in process exe
|
||||
mean that application runs under Python"""
|
||||
_JAVA_LANG_MARKERS = 'java',
|
||||
|
||||
_DEFAULT_API_PORT = 8070
|
||||
"""Default TCP port which monasca-api process should be available by"""
|
||||
|
||||
|
||||
def _get_impl_lang(process):
|
||||
"""Return implementation language of the application behind process
|
||||
|
||||
:param process: current process
|
||||
:type process: psutil.Process
|
||||
:return: implementation lang, either java or python
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
p_exe = process.exe()
|
||||
for lm in _PYTHON_LANG_MARKERS:
|
||||
if lm in p_exe:
|
||||
return 'python'
|
||||
for lm in _JAVA_LANG_MARKERS:
|
||||
if lm in p_exe:
|
||||
return 'java'
|
||||
raise Exception(('Cannot determine language '
|
||||
'implementation from process exe %s') % p_exe)
|
||||
|
||||
|
||||
class MonAgent(monasca_setup.detection.Plugin):
|
||||
"""Detect the Monsaca agent engine and setup some simple checks."""
|
||||
def _detect(self):
|
||||
"""Run detection, set self.available True if the service is detected."""
|
||||
self.available = True
|
||||
agent_process_list = ['monasca-collector', 'monasca-forwarder', 'monasca-statsd']
|
||||
agent_process_list = ['monasca-collector', 'monasca-forwarder',
|
||||
'monasca-statsd']
|
||||
for process in agent_process_list:
|
||||
if find_process_cmdline(process) is None:
|
||||
self.available = False
|
||||
|
@ -42,94 +74,82 @@ class MonAgent(monasca_setup.detection.Plugin):
|
|||
|
||||
class MonAPI(monasca_setup.detection.Plugin):
|
||||
"""Detect mon_api and setup monitoring."""
|
||||
|
||||
PARTIAL_ERR_MSG = 'Plugin for monasca-api will not be configured.'
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection, set self.available True if the service is detected."""
|
||||
monasca_api = monasca_setup.detection.find_process_cmdline('monasca-api')
|
||||
if monasca_api is not None:
|
||||
# monasca-api can show up in urls and be an arg to this setup program, check port also
|
||||
# Find the right port from the config, this is specific to the Java version
|
||||
try:
|
||||
with open('/etc/monasca/api-config.yml', 'r') as config:
|
||||
self.api_config = yaml.safe_load(config.read())
|
||||
api_port = self.api_config['server']['applicationConnectors'][0]['port']
|
||||
except Exception:
|
||||
api_port = 8070
|
||||
log.warn('Failed parsing /etc/monasca/api-config.yml, defaulting api port to {0}'.format(api_port))
|
||||
for conn in monasca_api.connections('inet'):
|
||||
"""Detects if monasca-api runs in the system
|
||||
|
||||
Method distinguishes between Java and Python implementation
|
||||
hence provides different agent's configuration.
|
||||
|
||||
"""
|
||||
|
||||
def check_port():
|
||||
for conn in api_process.connections('inet'):
|
||||
if conn.laddr[1] == api_port:
|
||||
self.available = True
|
||||
return
|
||||
return True
|
||||
return False
|
||||
|
||||
api_process = find_process_cmdline('monasca-api')
|
||||
process_found = api_process is not None
|
||||
|
||||
if process_found:
|
||||
impl_lang = _get_impl_lang(api_process)
|
||||
impl_helper = self._init_impl_helper(impl_lang)
|
||||
|
||||
impl_helper.load_configuration()
|
||||
|
||||
api_port = impl_helper.get_bound_port()
|
||||
port_taken = check_port()
|
||||
|
||||
if not port_taken:
|
||||
log.error('monasca-api is not listening on port %d. %s'
|
||||
% (api_port, self.PARTIAL_ERR_MSG))
|
||||
return
|
||||
|
||||
log.info('\tmonasca-api implementation is %s', impl_lang)
|
||||
|
||||
self.available = port_taken
|
||||
self._impl_helper = impl_helper
|
||||
else:
|
||||
log.error('monasca-api process has not been found. %s'
|
||||
% self.PARTIAL_ERR_MSG)
|
||||
|
||||
def build_config(self):
|
||||
"""Build the config as a Plugins object and return."""
|
||||
config = monasca_setup.agent_config.Plugins()
|
||||
|
||||
log.info("\tEnabling the Monasca api process check")
|
||||
config.merge(watch_process(['monasca-api'], 'monitoring', 'monasca-api', exact_match=False))
|
||||
|
||||
log.info("\tEnabling the Monasca api healthcheck")
|
||||
config.merge(dropwizard_health_check('monitoring', 'monasca-api', 'http://localhost:8081/healthcheck'))
|
||||
|
||||
log.info("\tEnabling the Monasca api metrics")
|
||||
whitelist = [
|
||||
{
|
||||
"name": "jvm.memory.total.max",
|
||||
"path": "gauges/jvm.memory.total.max/value",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "jvm.memory.total.used",
|
||||
"path": "gauges/jvm.memory.total.used/value",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "metrics.published",
|
||||
"path": "meters/monasca.api.app.MetricService.metrics.published/count",
|
||||
"type": "rate"
|
||||
}
|
||||
]
|
||||
|
||||
if not self._is_hibernate_on():
|
||||
# if hibernate is not used, it is mysql with DBI
|
||||
# for that case having below entries makes sense
|
||||
log.debug('MonApi has not enabled Hibernate, adding DBI metrics')
|
||||
whitelist.extend([
|
||||
{
|
||||
"name": "raw-sql.time.avg",
|
||||
"path": "timers/org.skife.jdbi.v2.DBI.raw-sql/mean",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "raw-sql.time.max",
|
||||
"path": "timers/org.skife.jdbi.v2.DBI.raw-sql/max",
|
||||
"type": "gauge"
|
||||
}
|
||||
])
|
||||
|
||||
config.merge(dropwizard_metrics('monitoring',
|
||||
'monasca-api',
|
||||
'http://localhost:8081/metrics',
|
||||
whitelist))
|
||||
log.info("\tEnabling the monasca-api process check")
|
||||
config.merge(watch_process(
|
||||
search_strings=['monasca-api'],
|
||||
service='monitoring',
|
||||
component='monasca-api',
|
||||
exact_match=False
|
||||
))
|
||||
impl_config = self._impl_helper.build_config()
|
||||
if impl_config:
|
||||
config.merge(impl_config)
|
||||
|
||||
return config
|
||||
|
||||
def dependencies_installed(self):
|
||||
return True
|
||||
|
||||
def _is_hibernate_on(self):
|
||||
# check if api_config has been declared in __init__
|
||||
# if not it means that configuration file was not found
|
||||
# or monasca-api Python implementation is running
|
||||
@staticmethod
|
||||
def _init_impl_helper(impl_lang):
|
||||
"""Returns appropriate helper implementation.
|
||||
|
||||
api_config = getattr(self, 'api_config', None)
|
||||
if api_config is None:
|
||||
return False
|
||||
:param impl_lang: implementation language, either `java` or `python`
|
||||
:type impl_lang: str
|
||||
:return: implementation helper
|
||||
:rtype: Union(_MonAPIJavaHelper,_MonAPIPythonHelper)
|
||||
|
||||
hibernate_cfg = self.api_config.get('hibernate', None)
|
||||
if hibernate_cfg is None:
|
||||
return False
|
||||
|
||||
return hibernate_cfg.get('supportEnabled', False)
|
||||
"""
|
||||
if impl_lang == 'java':
|
||||
return _MonAPIJavaHelper()
|
||||
else:
|
||||
return _MonAPIPythonHelper()
|
||||
|
||||
|
||||
class MonNotification(monasca_setup.detection.Plugin):
|
||||
|
@ -151,127 +171,75 @@ class MonNotification(monasca_setup.detection.Plugin):
|
|||
|
||||
class MonPersister(monasca_setup.detection.Plugin):
|
||||
"""Detect mon_persister and setup monitoring."""
|
||||
|
||||
PARTIAL_ERR_MSG = 'Plugin for monasca-persister will not be configured.'
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection, set self.available True if the service is detected."""
|
||||
if find_process_cmdline('monasca-persister') is not None:
|
||||
"""Detects if monasca-persister runs in the system
|
||||
|
||||
Method distinguishes between Java and Python implementation
|
||||
hence provides different agent's configuration.
|
||||
|
||||
"""
|
||||
p_process = find_process_cmdline('monasca-persister')
|
||||
process_found = p_process is not None
|
||||
|
||||
if process_found:
|
||||
impl_lang = _get_impl_lang(p_process)
|
||||
impl_helper = self._init_impl_helper(impl_lang)
|
||||
|
||||
if impl_helper is not None:
|
||||
impl_helper.load_configuration()
|
||||
|
||||
self._impl_helper = impl_helper
|
||||
self.available = True
|
||||
|
||||
log.info('\tmonasca-persister implementation is %s', impl_lang)
|
||||
|
||||
else:
|
||||
log.error('monasca-persister process has not been found. %s'
|
||||
% self.PARTIAL_ERR_MSG)
|
||||
|
||||
def build_config(self):
|
||||
"""Build the config as a Plugins object and return."""
|
||||
"""Read persister-config.yml file to find the exact numThreads."""
|
||||
try:
|
||||
with open('/etc/monasca/persister-config.yml', 'r') as config:
|
||||
self.persister_config = yaml.safe_load(config.read())
|
||||
except Exception:
|
||||
log.exception('Failed parsing /etc/monasca/persister-config.yml')
|
||||
self.available = False
|
||||
return
|
||||
|
||||
alarm_num_threads = self.persister_config['alarmHistoryConfiguration']['numThreads']
|
||||
metric_num_threads = self.persister_config['metricConfiguration']['numThreads']
|
||||
|
||||
database_type = self.persister_config['databaseConfiguration']['databaseType']
|
||||
|
||||
config = monasca_setup.agent_config.Plugins()
|
||||
|
||||
log.info("\tEnabling the Monasca persister process check")
|
||||
config.merge(watch_process(['monasca-persister'], 'monitoring', 'monasca-persister', exact_match=False))
|
||||
config.merge(watch_process(
|
||||
search_strings=['monasca-persister'],
|
||||
service='monitoring',
|
||||
component='monasca-persister',
|
||||
exact_match=False
|
||||
))
|
||||
if self._impl_helper is not None:
|
||||
impl_config = self._impl_helper.build_config()
|
||||
if impl_config:
|
||||
config.merge(impl_config)
|
||||
|
||||
adminConnector = self.persister_config['server']['adminConnectors'][0]
|
||||
try:
|
||||
admin_endpoint_type = adminConnector['type']
|
||||
except Exception:
|
||||
admin_endpoint_type = "http"
|
||||
|
||||
try:
|
||||
admin_endpoint_port = adminConnector['port']
|
||||
except Exception:
|
||||
admin_endpoint_port = 8091
|
||||
|
||||
log.info("\tEnabling the Monasca persister healthcheck")
|
||||
config.merge(
|
||||
dropwizard_health_check(
|
||||
'monitoring',
|
||||
'monasca-persister',
|
||||
'{0}://localhost:{1}/healthcheck'.format(admin_endpoint_type,
|
||||
admin_endpoint_port)))
|
||||
|
||||
log.info("\tEnabling the Monasca persister metrics")
|
||||
whitelist = [
|
||||
{
|
||||
"name": "jvm.memory.total.max",
|
||||
"path": "gauges/jvm.memory.total.max/value",
|
||||
"type": "gauge"},
|
||||
{
|
||||
"name": "jvm.memory.total.used",
|
||||
"path": "gauges/jvm.memory.total.used/value",
|
||||
"type": "gauge"}
|
||||
]
|
||||
|
||||
# Generate initial whitelist based on the database type
|
||||
if database_type == 'influxdb':
|
||||
pass
|
||||
elif database_type == 'vertica':
|
||||
whitelist.extend([
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-hit-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-hit-meter/count",
|
||||
"type": "rate"},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-miss-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-miss-meter/count",
|
||||
"type": "rate"},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-hit-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-hit-meter/count",
|
||||
"type": "rate"},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-miss-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-miss-meter/count",
|
||||
"type": "rate"},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-hit-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-hit-meter/count",
|
||||
"type": "rate"},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-miss-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-miss-meter/count",
|
||||
"type": "rate"},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.measurement-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.measurement-meter/count",
|
||||
"type": "rate"}
|
||||
])
|
||||
else:
|
||||
log.warn('Failed finding database type in /etc/monasca/persister-config.yml')
|
||||
|
||||
# Dynamic Whitelist
|
||||
for idx in range(alarm_num_threads):
|
||||
new_thread = {"name": "alarm-state-transitions-added-to-batch-counter[{0}]".format(idx),
|
||||
"path": "counters/monasca.persister.pipeline.event.AlarmStateTransitionHandler[alarm-state-transition-{0}].alarm-state-transitions-added-to-batch-counter/count".format(idx),
|
||||
"type": "rate"
|
||||
}
|
||||
whitelist.append(new_thread)
|
||||
|
||||
for idx in range(metric_num_threads):
|
||||
new_thread = {"name": "metrics-added-to-batch-counter[{0}]".format(idx),
|
||||
"path": "counters/monasca.persister.pipeline.event.MetricHandler[metric-{0}].metrics-added-to-batch-counter/count".format(idx),
|
||||
"type": "rate"
|
||||
}
|
||||
whitelist.append(new_thread)
|
||||
|
||||
config.merge(
|
||||
dropwizard_metrics(
|
||||
'monitoring',
|
||||
'monasca-persister',
|
||||
'{0}://localhost:{1}/metrics'.format(admin_endpoint_type,
|
||||
admin_endpoint_port),
|
||||
whitelist))
|
||||
return config
|
||||
|
||||
def dependencies_installed(self):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _init_impl_helper(impl_lang):
|
||||
"""Returns appropriate helper implementation.
|
||||
|
||||
Note:
|
||||
|
||||
This method returns the helper only for Java.
|
||||
Python implementation comes with no extra setup.
|
||||
|
||||
:param impl_lang: implementation language, either `java` or `python`
|
||||
:type impl_lang: str
|
||||
:return: implementation helper
|
||||
:rtype:_MonPersisterJavaHelper
|
||||
|
||||
"""
|
||||
if impl_lang == 'java':
|
||||
return _MonPersisterJavaHelper()
|
||||
return None
|
||||
|
||||
|
||||
class MonThresh(monasca_setup.detection.Plugin):
|
||||
"""Detect the running mon-thresh and monitor."""
|
||||
|
@ -337,3 +305,384 @@ class MonInfluxDB(monasca_setup.detection.Plugin):
|
|||
|
||||
def dependencies_installed(self):
|
||||
return True
|
||||
|
||||
|
||||
class _MonPersisterJavaHelper(object):
|
||||
"""Encapsulates Java specific configuration for monasca-persister"""
|
||||
|
||||
CONFIG_FILE = '/etc/monasca/persister-config.yml'
|
||||
"""Default location where plugin expects configuration file"""
|
||||
|
||||
def __init__(self):
|
||||
super(_MonPersisterJavaHelper, self).__init__()
|
||||
self._cfg = None
|
||||
|
||||
def build_config(self):
|
||||
config = monasca_setup.agent_config.Plugins()
|
||||
metrics = self._collect_metrics()
|
||||
self._monitor_endpoints(config, metrics)
|
||||
return config
|
||||
|
||||
def _collect_metrics(self):
|
||||
"""Collects all the metrics .
|
||||
|
||||
Methods will return list of all metrics that will
|
||||
later be used in
|
||||
:py:mod:`monasca_agent.collector.checks_d.http_metrics` to query
|
||||
admin endpoint of monasca-persister.
|
||||
|
||||
Following group of metrics are examined:
|
||||
|
||||
* JVM metrics
|
||||
* DB metrics ( see also :py:meth:`._collect_db_metrics` )
|
||||
* Internal metrics ( see also :py:meth:`._collect_internal_metrics` )
|
||||
|
||||
:return: list of metrics
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
log.info("\tEnabling the monasca-persister metrics")
|
||||
|
||||
whitelist = [
|
||||
{
|
||||
"name": "jvm.memory.total.max",
|
||||
"path": "gauges/jvm.memory.total.max/value",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "jvm.memory.total.used",
|
||||
"path": "gauges/jvm.memory.total.used/value",
|
||||
"type": "gauge"
|
||||
}
|
||||
]
|
||||
|
||||
self._collect_db_metrics(whitelist)
|
||||
self._collect_internal_metrics(whitelist)
|
||||
|
||||
return whitelist
|
||||
|
||||
def _collect_db_metrics(self, whitelist):
|
||||
"""Collects DB specific metrics.
|
||||
|
||||
Method retrieves which time-series database is used
|
||||
in monaca-persister and sets up new metrics to be monitored.
|
||||
|
||||
Note:
|
||||
Only if vertica is TSDB in monasca-persister
|
||||
|
||||
"""
|
||||
database_type = self._cfg['databaseConfiguration']['databaseType']
|
||||
if database_type == 'influxdb':
|
||||
pass
|
||||
elif database_type == 'vertica':
|
||||
self._add_vertica_metrics(whitelist)
|
||||
else:
|
||||
log.warn('Failed finding database type in %s', self.CONFIG_FILE)
|
||||
|
||||
def _collect_internal_metrics(self, whitelist):
|
||||
alarm_num_threads = self._cfg['alarmHistoryConfiguration']['numThreads']
|
||||
metric_num_threads = self._cfg['metricConfiguration']['numThreads']
|
||||
|
||||
# Dynamic Whitelist
|
||||
for idx in range(alarm_num_threads):
|
||||
new_thread = {
|
||||
"name": "alarm-state-transitions-added-to-batch-counter[{0}]".format(idx),
|
||||
"path": "counters/monasca.persister.pipeline.event.AlarmStateTransitionHandler[alarm-state-transition-{0}].alarm-state-transitions-added-to-batch-counter/count".format(idx),
|
||||
"type": "rate"
|
||||
}
|
||||
whitelist.append(new_thread)
|
||||
for idx in range(metric_num_threads):
|
||||
new_thread = {
|
||||
"name": "metrics-added-to-batch-counter[{0}]".format(idx),
|
||||
"path": "counters/monasca.persister.pipeline.event.MetricHandler[metric-{0}].metrics-added-to-batch-counter/count".format(idx),
|
||||
"type": "rate"
|
||||
}
|
||||
whitelist.append(new_thread)
|
||||
|
||||
def _add_vertica_metrics(self, whitelist):
|
||||
whitelist.extend([
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-hit-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-hit-meter/count",
|
||||
"type": "rate"
|
||||
},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-miss-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-cache-miss-meter/count",
|
||||
"type": "rate"
|
||||
},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-hit-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-hit-meter/count",
|
||||
"type": "rate"
|
||||
},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-miss-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.definition-dimension-cache-miss-meter/count",
|
||||
"type": "rate"
|
||||
},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-hit-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-hit-meter/count",
|
||||
"type": "rate"
|
||||
},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-miss-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.dimension-cache-miss-meter/count",
|
||||
"type": "rate"
|
||||
},
|
||||
{
|
||||
"name": "monasca.persister.repository.vertica.VerticaMetricRepo.measurement-meter",
|
||||
"path": "meters/monasca.persister.repository.vertica.VerticaMetricRepo.measurement-meter/count",
|
||||
"type": "rate"
|
||||
}
|
||||
])
|
||||
|
||||
def _monitor_endpoints(self, config, metrics):
|
||||
admin_connector = self._cfg['server']['adminConnectors'][0]
|
||||
|
||||
try:
|
||||
admin_endpoint_type = admin_connector['type']
|
||||
except Exception:
|
||||
admin_endpoint_type = "http"
|
||||
try:
|
||||
admin_endpoint_port = admin_connector['port']
|
||||
except Exception:
|
||||
admin_endpoint_port = 8091
|
||||
|
||||
log.info("\tEnabling the Monasca persister healthcheck")
|
||||
config.merge(
|
||||
dropwizard_health_check(
|
||||
'monitoring',
|
||||
'monasca-persister',
|
||||
'{0}://localhost:{1}/healthcheck'.format(admin_endpoint_type,
|
||||
admin_endpoint_port)))
|
||||
|
||||
log.info("\tEnabling the Monasca persister metrics")
|
||||
config.merge(
|
||||
dropwizard_metrics(
|
||||
'monitoring',
|
||||
'monasca-persister',
|
||||
'{0}://localhost:{1}/metrics'.format(admin_endpoint_type,
|
||||
admin_endpoint_port),
|
||||
metrics))
|
||||
|
||||
def load_configuration(self):
|
||||
"""Loads java specific configuration.
|
||||
|
||||
Load java specific configuration from:
|
||||
|
||||
* :py:attr:`API_CONFIG_YML`
|
||||
|
||||
:return: True if both configuration files were successfully loaded
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
try:
|
||||
with open(self.CONFIG_FILE, 'r') as config:
|
||||
self._cfg = yaml.safe_load(config.read())
|
||||
except Exception as ex:
|
||||
log.error('Failed to parse %s', self.CONFIG_FILE)
|
||||
log.exception(ex)
|
||||
raise ex
|
||||
|
||||
|
||||
class _MonAPIJavaHelper(object):
|
||||
"""Encapsulates Java specific configuration for monasca-api"""
|
||||
|
||||
CONFIG_FILE = '/etc/monasca/api-config.yml'
|
||||
|
||||
def __init__(self):
|
||||
super(_MonAPIJavaHelper, self).__init__()
|
||||
self._api_config = None
|
||||
|
||||
def build_config(self):
|
||||
"""Builds monitoring configuration for monasca-api Java flavour.
|
||||
|
||||
Method configures additional checks that are specific for
|
||||
Java implementation of monasca-api.
|
||||
|
||||
List of checks:
|
||||
|
||||
* HttpCheck, :py:mod:`monasca_agent.collector.checks_d.http_check`
|
||||
* HttpMetrics, :py:mod:`monasca_agent.collector.checks_d.http_metrics`
|
||||
|
||||
"""
|
||||
config = monasca_setup.agent_config.Plugins()
|
||||
|
||||
log.info("\tEnabling the Monasca api metrics")
|
||||
whitelist = [
|
||||
{
|
||||
"name": "jvm.memory.total.max",
|
||||
"path": "gauges/jvm.memory.total.max/value",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "jvm.memory.total.used",
|
||||
"path": "gauges/jvm.memory.total.used/value",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "metrics.published",
|
||||
"path": "meters/monasca.api.app.MetricService.metrics.published/count",
|
||||
"type": "rate"
|
||||
}
|
||||
]
|
||||
|
||||
if not self._is_hibernate_on():
|
||||
# if hibernate is not used, it is mysql with DBI
|
||||
# for that case having below entries makes sense
|
||||
log.debug(
|
||||
'monasca-api has not enabled Hibernate, adding DBI metrics')
|
||||
whitelist.extend([
|
||||
{
|
||||
"name": "raw-sql.time.avg",
|
||||
"path": "timers/org.skife.jdbi.v2.DBI.raw-sql/mean",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"name": "raw-sql.time.max",
|
||||
"path": "timers/org.skife.jdbi.v2.DBI.raw-sql/max",
|
||||
"type": "gauge"
|
||||
}
|
||||
])
|
||||
|
||||
self._monitor_endpoints(config, whitelist)
|
||||
|
||||
return config
|
||||
|
||||
def _monitor_endpoints(self, config, metrics):
|
||||
admin_connector = self._api_config['server']['adminConnectors'][0]
|
||||
|
||||
try:
|
||||
admin_endpoint_type = admin_connector['type']
|
||||
except Exception:
|
||||
admin_endpoint_type = "http"
|
||||
try:
|
||||
admin_endpoint_port = admin_connector['port']
|
||||
except Exception:
|
||||
admin_endpoint_port = 8081
|
||||
|
||||
healthcheck_url = '{0}://localhost:{1}/healthcheck'.format(
|
||||
admin_endpoint_type, admin_endpoint_port)
|
||||
metric_url = '{0}://localhost:{1}/metrics'.format(
|
||||
admin_endpoint_type, admin_endpoint_port)
|
||||
|
||||
log.info("\tEnabling the monasca-api healthcheck")
|
||||
config.merge(dropwizard_health_check('monitoring', 'monasca-api',
|
||||
healthcheck_url))
|
||||
log.info("\tEnabling the monasca-api metrics")
|
||||
config.merge(dropwizard_metrics('monitoring', 'monasca-api',
|
||||
metric_url, metrics))
|
||||
|
||||
def _is_hibernate_on(self):
|
||||
# check if api_config has been declared in __init__
|
||||
# if not it means that configuration file was not found
|
||||
# or monasca-api Python implementation is running
|
||||
|
||||
api_config = getattr(self, '_api_config', None)
|
||||
if api_config is None:
|
||||
return False
|
||||
|
||||
hibernate_cfg = self._api_config.get('hibernate', None)
|
||||
if hibernate_cfg is None:
|
||||
return False
|
||||
|
||||
return hibernate_cfg.get('supportEnabled', False)
|
||||
|
||||
def load_configuration(self):
|
||||
"""Loads java specific configuration.
|
||||
|
||||
Load java specific configuration from:
|
||||
|
||||
* :py:attr:`API_CONFIG_YML`
|
||||
|
||||
:return: True if both configuration files were successfully loaded
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
try:
|
||||
with open(self.CONFIG_FILE, 'r') as config:
|
||||
self._api_config = yaml.safe_load(config.read())
|
||||
except Exception as ex:
|
||||
log.error('Failed to parse %s', self.CONFIG_FILE)
|
||||
log.exception(ex)
|
||||
raise ex
|
||||
|
||||
def get_bound_port(self):
|
||||
"""Returns port API is listening on.
|
||||
|
||||
Method tries to read port from the '/etc/monasca/api-config.yml'
|
||||
file. In case if:
|
||||
|
||||
* file was not found in specified location
|
||||
* file could be read from the file system
|
||||
* file was changed and assumed location of port changed
|
||||
|
||||
code rollbacks to :py:const:`_DEFAULT_API_PORT`
|
||||
|
||||
:return: TCP port api is listening on
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
if self._api_config is None:
|
||||
return _DEFAULT_API_PORT
|
||||
try:
|
||||
return self._api_config['server']['applicationConnectors'][0]['port']
|
||||
except Exception as ex:
|
||||
log.error('Failed to read api port from '
|
||||
'/etc/monasca/api-config.yml')
|
||||
log.exception(ex)
|
||||
return _DEFAULT_API_PORT
|
||||
|
||||
|
||||
class _MonAPIPythonHelper(object):
|
||||
"""Encapsulates Python specific configuration for monasca-api"""
|
||||
|
||||
CONFIG_FILE = '/etc/monasca/api-config.ini'
|
||||
|
||||
def __init__(self):
|
||||
super(_MonAPIPythonHelper, self).__init__()
|
||||
self._paste_config = None
|
||||
|
||||
def build_config(self):
|
||||
# note(trebskit) intentionally left empty because gunicorn check
|
||||
# seems to have some serious issues with monitoring gunicorn process
|
||||
# see https://bugs.launchpad.net/monasca/+bug/1646481
|
||||
return None
|
||||
|
||||
def load_configuration(self):
|
||||
"""Loads INI file from specified path.
|
||||
|
||||
Method loads configuration from specified `path`
|
||||
and parses it with :py:class:`configparser.RawConfigParser`
|
||||
|
||||
"""
|
||||
cp = configparser.RawConfigParser()
|
||||
try:
|
||||
cp.readfp(open(self.CONFIG_FILE, 'r'))
|
||||
self._paste_config = cp
|
||||
except Exception as ex:
|
||||
log.error('Failed to parse %s', self.CONFIG_FILE)
|
||||
log.exception(ex)
|
||||
raise ex
|
||||
|
||||
def get_bound_port(self):
|
||||
"""Returns port API is listening on.
|
||||
|
||||
Method tries to read port from the '/etc/monasca/api-config.ini'
|
||||
file. In case if:
|
||||
|
||||
* file was not found in specified location
|
||||
* file could be read from the file system
|
||||
* file was changed and assumed location of port changed
|
||||
|
||||
code rollbacks to :py:const:`_DEFAULT_API_PORT`
|
||||
|
||||
:return: TCP port api is listening on
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
if not self._paste_config:
|
||||
return _DEFAULT_API_PORT
|
||||
return self._paste_config.getint('server:main', 'port')
|
||||
|
|
|
@ -0,0 +1,534 @@
|
|||
# Copyright 2016 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 random
|
||||
import unittest
|
||||
|
||||
import logging
|
||||
import mock
|
||||
import psutil
|
||||
|
||||
from monasca_setup.detection.plugins import mon
|
||||
|
||||
_PYTHON_CMD_API = ('/opt/monasca-api/bin/gunicorn'
|
||||
' -n monasca-api'
|
||||
' -k eventlet --worker-connections=2000 '
|
||||
'--backlog=1000 '
|
||||
'--paste /etc/monasca/api-config.ini -w 9')
|
||||
_JAVA_CMD_API = ('/usr/bin/java '
|
||||
'-Dfile.encoding=UTF-8 -Xmx128m '
|
||||
'-cp /opt/monasca/monasca-api.jar '
|
||||
'monasca.api.MonApiApplication server '
|
||||
'/etc/monasca/api-config.yml')
|
||||
|
||||
_PYTHON_CMD_PERSISTER = ('/opt/monasca-persister/bin/python '
|
||||
'/opt/monasca-persister/lib/python2.7/'
|
||||
'site-packages/monasca_persister/persister.py'
|
||||
' --config-file /etc/monasca/persister.conf')
|
||||
_JAVA_CMD_PERSISTER = ('/usr/bin/java -Dfile.encoding=UTF-8 -Xmx128m '
|
||||
'-cp /opt/monasca/monasca-persister.jar '
|
||||
'monasca.persister.PersisterApplication server '
|
||||
'/etc/monasca/persister-config.yml')
|
||||
|
||||
_JAVA_YML_CFG_BIT_API = '''
|
||||
hibernate:
|
||||
supportEnabled: {hibernate_enabled}
|
||||
server:
|
||||
adminConnectors:
|
||||
- type: {admin_type}
|
||||
port: {admin_port}
|
||||
applicationConnectors:
|
||||
- port: {app_port}
|
||||
'''
|
||||
|
||||
_JAVA_YML_CFG_BIT_PERSISTER = '''
|
||||
alarmHistoryConfiguration:
|
||||
numThreads: {ah_threads}
|
||||
metricConfiguration:
|
||||
numThreads: {mc_threads}
|
||||
databaseConfiguration:
|
||||
databaseType: {db_type}
|
||||
server:
|
||||
adminConnectors:
|
||||
- type: {admin_type}
|
||||
port: {admin_port}
|
||||
'''
|
||||
|
||||
LOG = logging.getLogger(mon.__name__)
|
||||
|
||||
|
||||
class FakeInetConnection(object):
|
||||
def __init__(self, api_port=None):
|
||||
if api_port is None:
|
||||
api_port = 8070 # default one
|
||||
self.laddr = [1, api_port]
|
||||
|
||||
|
||||
class FakeProcesses(object):
|
||||
name = None
|
||||
cmdLine = None
|
||||
inetConnections = [
|
||||
FakeInetConnection()
|
||||
]
|
||||
|
||||
def as_dict(self):
|
||||
return {'name': FakeProcesses.name,
|
||||
'cmdline': FakeProcesses.cmdLine}
|
||||
|
||||
def cmdline(self):
|
||||
return self.cmdLine
|
||||
|
||||
def exe(self):
|
||||
return self.cmdLine[0]
|
||||
|
||||
def connections(self, *args):
|
||||
return FakeProcesses.inetConnections
|
||||
|
||||
|
||||
class TestGetImplLang(unittest.TestCase):
|
||||
@mock.patch('psutil.Process')
|
||||
def test_should_return_python_lang_for_gunicorn_process(self, proc):
|
||||
proc.exe.return_value = '/opt/monasca-api/bin/gunicorn'
|
||||
self.assertEqual('python', mon._get_impl_lang(proc))
|
||||
|
||||
@mock.patch('psutil.Process')
|
||||
def test_should_return_python_lang_for_python_process(self, proc):
|
||||
proc.exe.return_value = '/usr/bin/python'
|
||||
self.assertEqual('python', mon._get_impl_lang(proc))
|
||||
|
||||
@mock.patch('psutil.Process')
|
||||
def test_should_return_java_lang_for_java_process(self, proc):
|
||||
proc.exe.return_value = '/usr/bin/java'
|
||||
self.assertEqual('java', mon._get_impl_lang(proc))
|
||||
|
||||
@mock.patch('psutil.Process')
|
||||
def test_should_throw_error_for_unknown_impl(self, proc):
|
||||
proc.exe.return_value = '/usr/bin/cat'
|
||||
self.assertRaises(Exception, mon._get_impl_lang, proc)
|
||||
|
||||
|
||||
class TestMonPersisterDetectionPlugin(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMonPersisterDetectionPlugin, self).setUp()
|
||||
FakeProcesses.name = 'monasca-persister'
|
||||
with mock.patch.object(mon.MonPersister, '_detect') as mock_detect:
|
||||
self._mon_p = mon.MonPersister('foo')
|
||||
self.assertTrue(mock_detect.called)
|
||||
|
||||
@mock.patch(
|
||||
'monasca_setup.detection.plugins.mon._MonPersisterJavaHelper')
|
||||
def test_should_use_java_helper_if_persister_is_java(self,
|
||||
impl_helper):
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_PERSISTER]
|
||||
|
||||
self._mon_p._init_impl_helper = iih = mock.Mock(
|
||||
return_value=impl_helper)
|
||||
|
||||
self._detect()
|
||||
|
||||
iih.assert_called_once_with('java')
|
||||
self.assertTrue(impl_helper.load_configuration.called_once)
|
||||
|
||||
def test_should_detect_java_persister_has_config(self):
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_PERSISTER]
|
||||
|
||||
yml_cfg = _JAVA_YML_CFG_BIT_PERSISTER.format(
|
||||
ah_threads=5,
|
||||
mc_threads=5,
|
||||
db_type="influxdb",
|
||||
admin_type="http",
|
||||
admin_port=6666
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"__builtin__.open",
|
||||
mock.mock_open(read_data=yml_cfg)) as mf:
|
||||
self._detect()
|
||||
mf.assert_called_once_with('/etc/monasca/persister-config.yml',
|
||||
'r')
|
||||
|
||||
self.assertTrue(self._mon_p.available)
|
||||
|
||||
@mock.patch('six.moves.configparser.RawConfigParser')
|
||||
def test_should_detect_python_persister_has_config(self, _):
|
||||
# NOTE(trebskit) this cannot use mocking the read of the file
|
||||
# because when either RawConfigParser or mock_open messes up with
|
||||
# reading the one file line. Instead returning empty line,
|
||||
# StopIteration is raised and RawConfigParser does not ignore that or
|
||||
# catch it
|
||||
#
|
||||
# to sum it up => ;-(((
|
||||
|
||||
FakeProcesses.cmdLine = [_PYTHON_CMD_PERSISTER]
|
||||
self._mon_p._init_impl_helper = mock.Mock(return_value=mock.Mock())
|
||||
|
||||
self._detect()
|
||||
self.assertTrue(self._mon_p.available)
|
||||
|
||||
def test_build_java_config(self):
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_PERSISTER]
|
||||
|
||||
# note(trebskit) this is always set to 2
|
||||
jvm_metrics_count = 2
|
||||
|
||||
alarm_history_threads = random.randint(1, 5)
|
||||
metrics_history_threads = random.randint(1, 5)
|
||||
admin_port = random.randint(1000, 10000)
|
||||
|
||||
admin_type = 'http'
|
||||
if admin_port % 2 != 0:
|
||||
admin_type += 's'
|
||||
|
||||
yml_cfg = _JAVA_YML_CFG_BIT_PERSISTER.format(
|
||||
ah_threads=alarm_history_threads,
|
||||
mc_threads=metrics_history_threads,
|
||||
db_type="influxdb",
|
||||
admin_type=admin_type,
|
||||
admin_port=admin_port
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"__builtin__.open",
|
||||
mock.mock_open(read_data=yml_cfg)) as mf:
|
||||
self._detect()
|
||||
conf = self._build_config()
|
||||
mf.assert_called_once_with('/etc/monasca/persister-config.yml',
|
||||
'r')
|
||||
|
||||
for key in ('http_check', 'http_metrics', 'process'):
|
||||
self.assertIn(key, conf)
|
||||
bit = conf[key]
|
||||
self.assertIsNotNone(bit)
|
||||
self.assertNotEqual({}, bit)
|
||||
|
||||
# detailed assertions
|
||||
|
||||
# http_check
|
||||
http_check_instance = conf['http_check']['instances'][0]
|
||||
self.assertFalse(http_check_instance['include_content'])
|
||||
self.assertEqual('monitoring-monasca-persister healthcheck',
|
||||
http_check_instance['name'])
|
||||
self.assertEqual(5, http_check_instance['timeout'])
|
||||
self.assertEqual('%s://localhost:%d/healthcheck'
|
||||
% (admin_type, admin_port),
|
||||
http_check_instance['url'])
|
||||
|
||||
# http_metrics
|
||||
http_metrics_instance = conf['http_metrics']['instances'][0]
|
||||
self.assertEqual('monitoring-monasca-persister metrics',
|
||||
http_metrics_instance['name'])
|
||||
self.assertEqual('%s://localhost:%d/metrics'
|
||||
% (admin_type, admin_port),
|
||||
http_metrics_instance['url'])
|
||||
hmi_whitelist = http_metrics_instance['whitelist']
|
||||
self.assertIsNotNone(hmi_whitelist)
|
||||
self.assertEqual(len(hmi_whitelist), (
|
||||
alarm_history_threads +
|
||||
metrics_history_threads +
|
||||
jvm_metrics_count))
|
||||
|
||||
jvm_metrics_found = 0
|
||||
ah_metrics_found = 0
|
||||
mh_metrics_found = 0
|
||||
|
||||
for entry in hmi_whitelist:
|
||||
name = entry['name']
|
||||
if 'jvm' in name:
|
||||
jvm_metrics_found += 1
|
||||
elif 'alarm-state' in name:
|
||||
ah_metrics_found += 1
|
||||
elif 'metrics-added' in name:
|
||||
mh_metrics_found += 1
|
||||
self.assertEqual(jvm_metrics_count, jvm_metrics_found)
|
||||
self.assertEqual(alarm_history_threads, ah_metrics_found)
|
||||
self.assertEqual(metrics_history_threads, mh_metrics_found)
|
||||
|
||||
# process
|
||||
process_instance = conf['process']['instances'][0]
|
||||
self.assertEqual('monasca-persister', process_instance['name'])
|
||||
self.assertFalse(process_instance['exact_match'])
|
||||
self.assertTrue(process_instance['detailed'])
|
||||
self.assertDictEqual({
|
||||
'component': 'monasca-persister',
|
||||
'service': 'monitoring'
|
||||
}, process_instance['dimensions'])
|
||||
|
||||
def test_build_python_config(self):
|
||||
FakeProcesses.cmdLine = [_PYTHON_CMD_PERSISTER]
|
||||
self._detect()
|
||||
conf = self._build_config()
|
||||
|
||||
for key in ('process',):
|
||||
self.assertIn(key, conf)
|
||||
bit = conf[key]
|
||||
self.assertIsNotNone(bit)
|
||||
self.assertNotEqual({}, bit)
|
||||
|
||||
# process
|
||||
process_instance = conf['process']['instances'][0]
|
||||
self.assertEqual('monasca-persister', process_instance['name'])
|
||||
self.assertFalse(process_instance['exact_match'])
|
||||
self.assertTrue(process_instance['detailed'])
|
||||
self.assertDictEqual({
|
||||
'component': 'monasca-persister',
|
||||
'service': 'monitoring'
|
||||
}, process_instance['dimensions'])
|
||||
|
||||
def _detect(self):
|
||||
self._mon_p.available = False
|
||||
process_iter = mock.patch.object(psutil, 'process_iter',
|
||||
return_value=[FakeProcesses()])
|
||||
with process_iter as mock_process_iter:
|
||||
self._mon_p._detect()
|
||||
self.assertTrue(mock_process_iter.called)
|
||||
|
||||
def _build_config(self):
|
||||
conf = self._mon_p.build_config()
|
||||
self.assertIsNotNone(conf)
|
||||
self.assertNotEqual({}, conf)
|
||||
return conf
|
||||
|
||||
|
||||
class TestMonAPIDetectionPlugin(unittest.TestCase):
|
||||
def setUp(self):
|
||||
FakeProcesses.name = 'monasca-api'
|
||||
super(TestMonAPIDetectionPlugin, self).setUp()
|
||||
with mock.patch.object(mon.MonAPI, '_detect') as mock_detect:
|
||||
self._mon_api = mon.MonAPI('foo')
|
||||
self.assertTrue(mock_detect.called)
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.mon._MonAPIPythonHelper')
|
||||
def test_should_use_python_helper_if_api_is_python(self, impl_helper):
|
||||
FakeProcesses.cmdLine = [_PYTHON_CMD_API]
|
||||
|
||||
self._mon_api._init_impl_helper = iih = mock.Mock(
|
||||
return_value=impl_helper)
|
||||
|
||||
self._detect()
|
||||
|
||||
iih.assert_called_once_with('python')
|
||||
self.assertTrue(impl_helper.load_configuration.called_once)
|
||||
self.assertTrue(impl_helper.get_bound_port.called_once)
|
||||
|
||||
@mock.patch('monasca_setup.detection.plugins.mon._MonAPIJavaHelper')
|
||||
def test_should_use_java_helper_if_api_is_java(self, impl_helper):
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_API]
|
||||
|
||||
self._mon_api._init_impl_helper = iih = mock.Mock(
|
||||
return_value=impl_helper)
|
||||
|
||||
self._detect()
|
||||
|
||||
iih.assert_called_once_with('java')
|
||||
self.assertTrue(impl_helper.load_configuration.called_once)
|
||||
self.assertTrue(impl_helper.get_bound_port.called_once)
|
||||
|
||||
def test_should_detect_java_api_has_config(self):
|
||||
app_port = random.randint(1000, 10000)
|
||||
admin_port = random.randint(1000, 10000)
|
||||
admin_type = 'http'
|
||||
if admin_port % 2 != 0:
|
||||
admin_type += 's'
|
||||
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_API]
|
||||
FakeProcesses.inetConnections = [FakeInetConnection(app_port)]
|
||||
|
||||
yml_cfg = _JAVA_YML_CFG_BIT_API.format(
|
||||
app_port=app_port,
|
||||
admin_port=admin_port,
|
||||
admin_type=admin_type,
|
||||
hibernate_enabled=False
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"__builtin__.open",
|
||||
mock.mock_open(read_data=yml_cfg)) as mock_file:
|
||||
self._detect()
|
||||
mock_file.assert_called_once_with('/etc/monasca/api-config.yml',
|
||||
'r')
|
||||
|
||||
self.assertTrue(self._mon_api.available)
|
||||
|
||||
@mock.patch('six.moves.configparser.RawConfigParser')
|
||||
def test_should_detect_python_api_has_config(self, rcp):
|
||||
expected_port = 6666
|
||||
actual_port = 6666
|
||||
|
||||
FakeProcesses.cmdLine = [_PYTHON_CMD_API]
|
||||
FakeProcesses.inetConnections = [FakeInetConnection(actual_port)]
|
||||
|
||||
# make sure we return the port as we would read from the cfg
|
||||
rcp.getint.return_value = expected_port
|
||||
|
||||
# override configuration to make sure we read correct port
|
||||
impl_helper = mon._MonAPIPythonHelper()
|
||||
impl_helper._paste_config = rcp
|
||||
impl_helper.load_configuration = mock.Mock()
|
||||
|
||||
self._mon_api._init_impl_helper = mock.Mock(return_value=impl_helper)
|
||||
|
||||
self._detect()
|
||||
self.assertTrue(self._mon_api.available)
|
||||
|
||||
@mock.patch('six.moves.configparser.RawConfigParser')
|
||||
def test_should_not_detect_if_port_dont_match(self, rcp):
|
||||
expected_port = 6666
|
||||
actual_port = 8070
|
||||
|
||||
# assume having python implementation
|
||||
FakeProcesses.cmdLine = [_PYTHON_CMD_API]
|
||||
FakeProcesses.inetConnections = [FakeInetConnection(actual_port)]
|
||||
|
||||
# make sure we return the port as we would read from the cfg
|
||||
rcp.getint.return_value = expected_port
|
||||
|
||||
# override configuration to make sure we read correct port
|
||||
impl_helper = mon._MonAPIPythonHelper()
|
||||
impl_helper._paste_config = rcp
|
||||
impl_helper.load_configuration = mock.Mock()
|
||||
|
||||
self._mon_api._init_impl_helper = mock.Mock(return_value=impl_helper)
|
||||
|
||||
with mock.patch.object(LOG, 'error') as mock_log_error:
|
||||
self._detect()
|
||||
self.assertFalse(self._mon_api.available)
|
||||
mock_log_error.assert_called_with('monasca-api is not listening '
|
||||
'on port %d. Plugin for '
|
||||
'monasca-api will not '
|
||||
'be configured.' % expected_port)
|
||||
|
||||
def test_build_java_config_no_hibernate(self):
|
||||
self._run_java_build_config(False)
|
||||
|
||||
def test_build_java_config_with_hibernate(self):
|
||||
self._run_java_build_config(True)
|
||||
|
||||
@mock.patch('six.moves.configparser.RawConfigParser')
|
||||
def test_build_python_config(self, rcp):
|
||||
expected_port = 8070
|
||||
|
||||
FakeProcesses.cmdLine = [_PYTHON_CMD_API]
|
||||
FakeProcesses.inetConnections = [FakeInetConnection(expected_port)]
|
||||
|
||||
rcp.getint.return_value = expected_port
|
||||
|
||||
impl_helper = mon._MonAPIPythonHelper()
|
||||
impl_helper._paste_config = rcp
|
||||
impl_helper.load_configuration = mock.Mock()
|
||||
|
||||
self._mon_api._init_impl_helper = mock.Mock(return_value=impl_helper)
|
||||
|
||||
self._detect()
|
||||
conf = self._build_config()
|
||||
|
||||
for key in ('process', ):
|
||||
self.assertIn(key, conf)
|
||||
bit = conf[key]
|
||||
self.assertIsNotNone(bit)
|
||||
self.assertNotEqual({}, bit)
|
||||
|
||||
def _run_java_build_config(self, hibernate_enabled):
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_API]
|
||||
app_port = random.randint(1000, 10000)
|
||||
admin_port = random.randint(1000, 10000)
|
||||
admin_type = 'http'
|
||||
if admin_port % 2 != 0:
|
||||
admin_type += 's'
|
||||
|
||||
FakeProcesses.cmdLine = [_JAVA_CMD_API]
|
||||
FakeProcesses.inetConnections = [FakeInetConnection(app_port)]
|
||||
|
||||
# note(trebskit) this is always set to 2
|
||||
jvm_metrics_count = 2
|
||||
internal_metrics_count = 1
|
||||
sql_timers_count = 2
|
||||
|
||||
total_metrics_count = jvm_metrics_count + internal_metrics_count + (
|
||||
sql_timers_count if not hibernate_enabled else 0)
|
||||
|
||||
yml_cfg = _JAVA_YML_CFG_BIT_API.format(
|
||||
app_port=app_port,
|
||||
admin_port=admin_port,
|
||||
admin_type=admin_type,
|
||||
hibernate_enabled=hibernate_enabled
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"__builtin__.open",
|
||||
mock.mock_open(read_data=yml_cfg)) as mf:
|
||||
self._detect()
|
||||
conf = self._build_config()
|
||||
mf.assert_called_once_with('/etc/monasca/api-config.yml',
|
||||
'r')
|
||||
|
||||
for key in ('http_check', 'http_metrics', 'process'):
|
||||
self.assertIn(key, conf)
|
||||
bit = conf[key]
|
||||
self.assertIsNotNone(bit)
|
||||
self.assertNotEqual({}, bit)
|
||||
|
||||
# verify http_check
|
||||
http_check_instance = conf['http_check']['instances'][0]
|
||||
self.assertFalse(http_check_instance['include_content'])
|
||||
self.assertEqual('monitoring-monasca-api healthcheck',
|
||||
http_check_instance['name'])
|
||||
self.assertEqual(5, http_check_instance['timeout'])
|
||||
self.assertEqual('%s://localhost:%d/healthcheck'
|
||||
% (admin_type, admin_port),
|
||||
http_check_instance['url'])
|
||||
|
||||
# verify http_metrics
|
||||
http_metrics_instance = conf['http_metrics']['instances'][0]
|
||||
self.assertEqual('monitoring-monasca-api metrics',
|
||||
http_metrics_instance['name'])
|
||||
self.assertEqual('%s://localhost:%d/metrics'
|
||||
% (admin_type, admin_port),
|
||||
http_metrics_instance['url'])
|
||||
hmi_whitelist = http_metrics_instance['whitelist']
|
||||
self.assertIsNotNone(hmi_whitelist)
|
||||
self.assertEqual(len(hmi_whitelist), total_metrics_count)
|
||||
|
||||
jvm_metrics_found = 0
|
||||
internal_metrics_found = 0
|
||||
sql_timers_metrics_found = 0
|
||||
|
||||
for entry in hmi_whitelist:
|
||||
name = entry['name']
|
||||
if 'jvm' in name:
|
||||
jvm_metrics_found += 1
|
||||
elif 'metrics.published' in name:
|
||||
internal_metrics_found += 1
|
||||
elif 'raw-sql.time' in name:
|
||||
sql_timers_metrics_found += 1
|
||||
|
||||
self.assertEqual(jvm_metrics_count, jvm_metrics_found)
|
||||
self.assertEqual(internal_metrics_count, internal_metrics_found)
|
||||
if not hibernate_enabled:
|
||||
self.assertEqual(sql_timers_count, sql_timers_count)
|
||||
|
||||
def _build_config(self):
|
||||
conf = self._mon_api.build_config()
|
||||
self.assertIsNotNone(conf)
|
||||
self.assertNotEqual({}, conf)
|
||||
return conf
|
||||
|
||||
def _detect(self):
|
||||
self._mon_api.available = False
|
||||
|
||||
process_iter = mock.patch.object(psutil, 'process_iter',
|
||||
return_value=[FakeProcesses()])
|
||||
|
||||
with process_iter as mock_process_iter:
|
||||
self._mon_api._detect()
|
||||
self.assertTrue(mock_process_iter.called)
|
Loading…
Reference in New Issue