monasca-agent/monasca_setup/detection/utils.py

265 lines
8.9 KiB
Python

# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
""" Util functions to assist in detection.
"""
import subprocess
from subprocess import CalledProcessError
from subprocess import PIPE
from subprocess import Popen
from oslo_config import cfg
from monasca_agent.common.psutil_wrapper import psutil
from monasca_setup import agent_config
# check_output was introduced in python 2.7, function added
# to accommodate python 2.6
try:
check_output = subprocess.check_output
except AttributeError:
def check_output(*popenargs, **kwargs):
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = Popen(stdout=PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
raise CalledProcessError(retcode, cmd)
return output
def find_process_cmdline(search_string):
"""Simple function to search running process for one with cmdline
containing search_string.
"""
for process in psutil.process_iter():
try:
process_cmdline = ' '.join(process.as_dict(['cmdline'])['cmdline'])
if (search_string in process_cmdline and
'monasca-setup' not in process_cmdline):
return process
except psutil.NoSuchProcess:
continue
return None
def find_process_name(pname):
"""Simple function to search running process for one with pname.
"""
for process in psutil.process_iter():
try:
if pname == process.as_dict(['name'])['name']:
return process
except psutil.NoSuchProcess:
continue
return None
def find_process_service(sname):
"""Simple function to call systemctl (service) to check if a service is running.
"""
try:
subprocess.check_call(['service', sname, 'status'], stdout=PIPE, stderr=PIPE)
return True
except subprocess.CalledProcessError:
return False
return False
def find_addrs_listening_on_port(port, kind='inet'):
"""Return the list of IP addresses which are listening
on the specified port.
"""
listening = []
for connection in psutil.net_connections(kind):
if (connection.status == psutil.CONN_LISTEN and
connection.laddr[1] == int(port)):
listening.append(connection.laddr[0])
return listening
def find_addr_listening_on_port_over_tcp(port):
"""Return the IP address which is listening on the specified TCP port."""
ip = find_addrs_listening_on_port(port, 'tcp')
if ip:
return ip[0].lstrip("::ffff:")
# NOTE(trebskit) a little poetry never hurt anyone before...right ?
def load_oslo_configuration(from_cmd, in_project,
for_opts, of_prog=None):
"""Loads configuration of an OpenStack project.
for_opts should be a :py:class:`list` containing dictionaries
with keys as expected by :py:class:meth:`cfg.ConfigOpts.register_opt`::
>>> for_opts = [
>>> {'opt': cfg.StrOpt('region_name')},
>>> {'opt': cfg.StrOpt('username'), 'group': 'keystoneauth'},
>>> {'opt': cfg.StrOpt('password'), 'group': 'keystoneauth'},
>>> ]
Example::
>>> nova_proc = find_process_name('nova-compute')
>>> proc_cmd = nova_proc.as_dict(['cmdline'])['cmdline']
>>> load_oslo_configuration(
>>> from_cmd=proc_cmd,
>>> in_project='nova',
>>> for_opts=for_opts
>>> )
which will load three [region_name, username and password] settings from
Nova configuration regardless of where those
settings are actually defined.
:param from_cmd: cmdline of a process, used also to retrieve arguments
:type from_cmd: list[basestring]
:param in_project: the project name as defined in its oslo setup
:type in_project: basestring
:param for_opts: list of dict containing options to look for inside config
:type for_opts: list[dict]
:param of_prog: program name within the project [optional]
:type of_prog: basestring
:return: oslo configuration object
:rtype: oslo_config.cfg.CONF
"""
conf_holder = cfg.ConfigOpts()
for no in for_opts:
conf_holder.register_opt(**no)
# NOTE(trebskit) we need to remove everything from the beginning
# of the cmd arg list that is not an argument of the application
# we want to get configuration from, i.e.;
# /usr/bin/python, /usr/bin/python3
# and next actual binary of the program
# /usr/local/bin/nova-compute
args = from_cmd[2:]
conf_holder(
args=args,
project=in_project,
prog=of_prog
)
return conf_holder
def watch_process(search_strings, service=None, component=None,
exact_match=True, detailed=True, process_name=None, dimensions=None):
"""Takes a list of process search strings and returns a Plugins object with the config set.
This was built as a helper as many plugins setup process watching
"""
config = agent_config.Plugins()
# Fallback to default process_name strategy if process_name is not defined
process_name = process_name if process_name else search_strings[0]
parameters = {'name': process_name,
'detailed': detailed,
'exact_match': exact_match,
'search_string': search_strings}
dimensions = _get_dimensions(service, component, dimensions)
if len(dimensions) > 0:
parameters['dimensions'] = dimensions
config['process'] = {'init_config': None,
'instances': [parameters]}
return config
def watch_process_by_username(username, process_name, service=None,
component=None, detailed=True, dimensions=None):
"""Takes a user and returns a Plugins object with the config set for a process check by user.
This was built as a helper as many plugins setup process watching.
"""
config = agent_config.Plugins()
parameters = {'name': process_name,
'detailed': detailed,
'username': username}
dimensions = _get_dimensions(service, component, dimensions)
if len(dimensions) > 0:
parameters['dimensions'] = dimensions
config['process'] = {'init_config': None,
'instances': [parameters]}
return config
def watch_file_size(directory_name, file_names, file_recursive=False,
service=None, component=None, dimensions=None):
"""Takes a directory, a list of files, recursive flag and returns a
Plugins object with the config set.
"""
config = agent_config.Plugins()
parameters = {'directory_name': directory_name,
'file_names': file_names,
'recursive': file_recursive}
dimensions = _get_dimensions(service, component, dimensions)
if len(dimensions) > 0:
parameters['dimensions'] = dimensions
config['file_size'] = {'init_config': None,
'instances': [parameters]}
return config
def watch_directory(directory_name, service=None, component=None, dimensions=None):
"""Takes a directory name and returns a Plugins object with the config set.
"""
config = agent_config.Plugins()
parameters = {'directory': directory_name}
dimensions = _get_dimensions(service, component, dimensions)
if len(dimensions) > 0:
parameters['dimensions'] = dimensions
config['directory'] = {'init_config': None,
'instances': [parameters]}
return config
def service_api_check(name, url, pattern, use_keystone=True,
service=None, component=None, dimensions=None):
"""Setup a service api to be watched by the http_check plugin.
"""
config = agent_config.Plugins()
parameters = {'name': name,
'url': url,
'match_pattern': pattern,
'timeout': 10,
'use_keystone': use_keystone}
dimensions = _get_dimensions(service, component, dimensions)
if len(dimensions) > 0:
parameters['dimensions'] = dimensions
config['http_check'] = {'init_config': None,
'instances': [parameters]}
return config
def _get_dimensions(service, component, dimensions=None):
if dimensions is None:
dimensions = {}
# If service parameter is set in the plugin config, add the service dimension which
# will override the service in the agent config
if service:
dimensions.update({'service': service})
# If component parameter is set in the plugin config, add the component dimension which
# will override the component in the agent config
if component:
dimensions.update({'component': component})
return dimensions