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

184 lines
8.7 KiB
Python

# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
import json
import logging
import yaml
import monasca_setup.agent_config
import monasca_setup.detection
from monasca_setup.detection.utils import find_process_cmdline
log = logging.getLogger(__name__)
class ProcessCheck(monasca_setup.detection.Plugin):
"""Setup a process check according to the passed in JSON string or YAML config file path.
A process can be monitored by process_names or by process_username, or by both if
the process_config list contains both dictionary entries. Pass in the dictionary containing process_names
when watching process by name. Pass in the dictionary containing process_username and dimensions with
component when watching process by username. Watching by process_username is useful for groups of processes
that are owned by a specific user. For process monitoring by process_username the component dimension
is required since it is used to initialize the instance name in process.yaml.
service and component dimensions are recommended to distinguish multiple components per service. The component
dimensions will be defaulted to the process name when it is not input when monitoring by process_names.
exact_match is optional and defaults to false, meaning the process name search string can be found within the process name.
exact_match can be set to true if the process_names search string should match the process name.
Pass in a YAML config file path:
monasca-setup -d ProcessCheck -a "conf_file_path=/home/stack/myprocess.yaml"
or
Pass in a JSON string command line argument:
Using monasca-setup, you can pass in a json string with arguments --detection_args_json, or the shortcut -json.
Monitor by process_names:
monasca-setup -d ProcessCheck -json \
'{"process_config":[{"process_names":["monasca-notification","monasca-api"],"dimensions":{"service":"monitoring"}}]}'
Specifiy one or more dictionary entries to the process_config list:
monasca-setup -d ProcessCheck -json \
'{"process_config":[
{"process_names":["monasca-notification","monasca-api"],"dimensions":{"service":"monitoring"}},
{"process_names":["elasticsearch"],"dimensions":{"service":"logging"},"exact_match":"true"},
{"process_names":["monasca-thresh"],"dimensions":{"service":"monitoring","component":"thresh"}}]}'
Monitor by process_username:
monasca-setup -d ProcessCheck -json \
'{"process_config":[{"process_username":"dbadmin","dimensions":{"service":"monitoring","component":"vertica"}}]}'
Can specify monitoring by both process_username and process_names:
monasca-setup -d ProcessCheck -json \
'{"process_config":[{"process_names":["monasca-api"],"dimensions":{"service":"monitoring"}},
{"process_username":"mon-api","dimensions":{"service":"monitoring","component":"monasca-api"}}]}'
"""
def __init__(self, template_dir, overwrite=False, args=None, **kwargs):
self.process_config = []
self.valid_process_names = []
self.valid_usernames = []
if 'process_config' in kwargs:
self.process_config = kwargs['process_config']
super(ProcessCheck, self).__init__(template_dir, overwrite, args)
def _get_config(self):
self.conf_file_path = None
if self.args:
self.conf_file_path = self.args.get('conf_file_path', None)
if self.conf_file_path:
self._read_config(self.conf_file_path)
def _read_config(self, config_file):
log.info("\tUsing parameters from config file: {}".format(config_file))
with open(config_file) as data_file:
try:
data = yaml.safe_load(data_file)
if 'process_config' in data:
self.process_config = data['process_config']
else:
log.error("\tInvalid format yaml file, missing key: process_config")
except yaml.YAMLError as e:
exception_msg = ("Could not read config file. Invalid yaml format detected. {0}.".format(e))
raise Exception(exception_msg)
def _detect(self):
"""Run detection, set self.available True if the service is detected.
"""
self._get_config()
for process_item in self.process_config:
if 'dimensions' not in process_item:
process_item['dimensions'] = {}
if 'process_names' in process_item:
found_process_names = []
not_found_process_names = []
for process_name in process_item['process_names']:
if find_process_cmdline(process_name) is not None:
found_process_names.append(process_name)
else:
not_found_process_names.append(process_name)
# monitoring by process_names
if not_found_process_names:
log.info("\tDid not discover process_name(s): {0}.".format(",".join(not_found_process_names)))
if found_process_names:
process_item['found_process_names'] = found_process_names
if 'exact_match' in process_item:
if isinstance(process_item['exact_match'], basestring):
process_item['exact_match'] = (process_item['exact_match'].lower() == 'true')
else:
process_item['exact_match'] = False
self.valid_process_names.append(process_item)
if 'process_username' in process_item:
if 'component' in process_item['dimensions']:
self.valid_usernames.append(process_item)
else:
log.error("\tMissing required component dimension, when monitoring by "
"process_username: {}".format(process_item['process_username']))
if self.valid_process_names or self.valid_usernames:
self.available = True
def _monitor_by_process_name(self, process_name, exact_match=False, detailed=True, dimensions=None):
config = monasca_setup.agent_config.Plugins()
instance = {'name': process_name,
'detailed': detailed,
'exact_match': exact_match,
'search_string': [process_name],
'dimensions': {}}
# default component to process name if not given
if dimensions:
instance['dimensions'].update(dimensions)
if 'component' not in dimensions:
instance['dimensions']['component'] = process_name
else:
instance['dimensions']['component'] = process_name
config['process'] = {'init_config': None, 'instances': [instance]}
return config
def _monitor_by_process_username(self, process_username, detailed=True, dimensions=None):
config = monasca_setup.agent_config.Plugins()
instance = {'name': dimensions['component'],
'detailed': detailed,
'username': process_username,
'dimensions': {}}
if dimensions:
instance['dimensions'].update(dimensions)
config['process'] = {'init_config': None, 'instances': [instance]}
return config
def build_config(self):
"""Build the config as a Plugins object and return.
"""
config = monasca_setup.agent_config.Plugins()
# Watch by process_names
for process in self.valid_process_names:
log.info("\tMonitoring by process_name(s): {0} "
"for dimensions: {1}.".format(",".join(process['found_process_names']),
json.dumps(process['dimensions'])))
for process_name in process['found_process_names']:
config.merge(self._monitor_by_process_name(process_name=process_name,
dimensions=process['dimensions'],
exact_match=process['exact_match']))
# Watch by process_username
for process in self.valid_usernames:
log.info("\tMonitoring by process_username: {0} "
"for dimensions: {1}.".format(process['process_username'], json.dumps(process['dimensions'])))
config.merge(self._monitor_by_process_username(process_username=process['process_username'],
dimensions=process['dimensions']))
return config
def dependencies_installed(self):
"""Return True if dependencies are installed.
"""
return True