Removed dependency on supervisor
To support python3 in the near future this was done: * Removed dependency on supervisor. * Added template configuration for systemd target that includes all services. * Added templates configuration for systemd service for every single service. * Changed monasca_setup to use the new templates. In the meanwhile code was formated to cope with pep8 settings and some other small changes were done to comply with pycodestyle and pydocstring. Task: 4126 Story: 2000975 Depends-On: https://review.openstack.org/#/c/566475/ Change-Id: I0d0c4ea41a830581d6b9f247fad6a2dda1f96cbe
This commit is contained in:
parent
dd66bd1504
commit
a8a2bb845b
3
.gitignore
vendored
3
.gitignore
vendored
@ -19,3 +19,6 @@ htmlcov/
|
||||
cover/
|
||||
.stestr
|
||||
virtenv/
|
||||
Pipfile
|
||||
Pipfile.lock
|
||||
.vscode
|
||||
|
@ -35,12 +35,13 @@ The Agent is composed of the following components:
|
||||
|
||||
| Component Name | Process Name | Description |
|
||||
| -------------- | ------------ | ----------- |
|
||||
| Supervisor | supervisord | Runs as root, launches all other processes as the user configured to run monasca-agent. This process manages the lifecycle of the Collector, Forwarder and Statsd Daemon. It allows Start, Stop and Restart of all the agent processes together. |
|
||||
| Collector | monasca-collector | Gathers system & application metrics on a configurable interval and sends them to the Forwarder process. The collector runs various plugins for collection of different plugins.|
|
||||
| Forwarder | monasca-forwarder | Gathers data from the collector and statsd and submits it to Monasca API over SSL (tcp/17123) |
|
||||
| Statsd Daemon | monasca-statsd | Statsd engine capable of handling dimensions associated with metrics submitted by a client that supports them. Also supports metrics from the standard statsd client. (udp/8125) |
|
||||
| Monasca Setup | monasca-setup | The monasca-setup script configures the agent. The Monasca Setup program can also auto-detect and configure certain agent plugins |
|
||||
|
||||
It is possible to Start, Stop and Reset all components by using monasca-agent.target. Monasca-agent.target is the systemd configuration that allows to manage all monasca-agent services.
|
||||
|
||||
# Installing
|
||||
The Agent (monasca-agent) is available for installation from the Python Package Index (PyPI). To install it, you first need `pip` installed on the node to be monitored. Instructions on installing pip may be found at https://pip.pypa.io/en/latest/installing.html. The Agent will NOT run under any flavor of Windows or Mac OS at this time but has been tested thoroughly on Ubuntu and should work under most flavors of Linux. Support may be added for Mac OS and Windows in the future. Example of an Ubuntu or Debian based install:
|
||||
|
||||
@ -277,7 +278,7 @@ The number of threads to use for running the plugins is via num_collector_thread
|
||||
|
||||
The collector is optimized for collecting as many metrics on schedule as possible. The plugins are run in reverse order of their collection time, i.e., the fastest plugin first. Also, if a plugin does not complete within the collection frequency, that plugin will be skipped in the next collection cycle. These two optimizations together ensure that plugins that complete with collection frequency seconds will get run on every collection cycle.
|
||||
|
||||
If there is some problem with multiple plugins that end up blocking the entire thread pool, the collector will exit so that it can be restarted by the supervisord. The parameter pool_full_max_retries controls when this happens. If pool_full_max_retries consecutive collection cycles have ended with the Thread Pool completely full, the collector will exit.
|
||||
If there is some problem with multiple plugins that end up blocking the entire thread pool, the collector will exit so that it can be restarted by the monasca-agent systemd target. The parameter pool_full_max_retries controls when this happens. If pool_full_max_retries consecutive collection cycles have ended with the Thread Pool completely full, the collector will exit.
|
||||
|
||||
Some of the plugins have their own thread pools to handle asynchronous checks. The collector thread pool is separate and has no special interaction with those thread pools.
|
||||
# License
|
||||
|
@ -23,7 +23,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Plugin(object):
|
||||
"""Abstract class implemented by the monasca-agent plugin detection classes. """
|
||||
"""Abstract class for the monasca-agent plugin detection."""
|
||||
|
||||
def __init__(self, template_dir, overwrite=True, args=None):
|
||||
self.available = False
|
||||
@ -32,7 +32,8 @@ class Plugin(object):
|
||||
self.overwrite = overwrite
|
||||
if args is not None and isinstance(args, str):
|
||||
try:
|
||||
# Turn 'hostname=host type=ping' to dictionary {'hostname': 'host', 'type': 'ping'}
|
||||
# Turn 'hostname=host type=ping' to dictionary
|
||||
# {'hostname': 'host', 'type': 'ping'}
|
||||
self.args = dict([a.split('=') for a in args.split()])
|
||||
except Exception:
|
||||
log.exception('Error parsing detection arguments')
|
||||
@ -44,21 +45,25 @@ class Plugin(object):
|
||||
self._detect()
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection, set self.available True if the service is detected.
|
||||
"""Run detection.
|
||||
|
||||
Set self.available True if the service is detected.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def build_config(self):
|
||||
"""Build the config as a Plugins object and return.
|
||||
|
||||
"""
|
||||
"""Build the config as a Plugins object and return."""
|
||||
raise NotImplementedError
|
||||
|
||||
def build_config_with_name(self):
|
||||
"""Builds the config and then adds a field 'built_by' to each instance in the config.
|
||||
"""Build the config and then add 'built_by' field.
|
||||
|
||||
Build the config and then add 'built_by' field to each instance in
|
||||
the config.
|
||||
|
||||
built_by is set to the plugin name
|
||||
:return: An agent_config.Plugins object
|
||||
|
||||
:return: An agent_config.Plugins object
|
||||
"""
|
||||
conf = self.build_config()
|
||||
if conf is None:
|
||||
|
@ -28,12 +28,12 @@ import sys
|
||||
|
||||
import six
|
||||
|
||||
import agent_config
|
||||
from monasca_setup import agent_config
|
||||
from monasca_setup.service.detection import detect_init
|
||||
import monasca_setup.utils as utils
|
||||
from monasca_setup.utils import write_template
|
||||
from service.detection import detect_init
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CUSTOM_PLUGIN_PATH = '/usr/lib/monasca/agent/custom_detect.d'
|
||||
# dirname is called twice to get the dir 1 above the location of the script
|
||||
@ -42,22 +42,26 @@ PREFIX_DIR = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Configure and setup the agent. In a full run it will detect running' +
|
||||
' daemons then configure and start the agent.',
|
||||
description='Configure and setup the agent. In a full run it will' +
|
||||
' detect running daemons then configure and start the agent.',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
args = parse_arguments(parser)
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s")
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format="%(levelname)s: %(message)s")
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format="%(levelname)s: %(message)s")
|
||||
|
||||
if args.dry_run:
|
||||
log.info("Running in dry run mode, no changes will be made only reported")
|
||||
LOG.info("Running in dry run mode, no changes will be made only"
|
||||
" reported")
|
||||
|
||||
# Detect and if possibly enable the agent service
|
||||
agent_service = detect_init(PREFIX_DIR, args.config_dir, args.log_dir, args.template_dir,
|
||||
username=args.user, name=args.agent_service_name)
|
||||
agent_service = detect_init(PREFIX_DIR, args.config_dir, args.log_dir,
|
||||
args.template_dir, username=args.user,
|
||||
name=args.agent_service_name)
|
||||
|
||||
# Skip base setup if only installing plugins or running specific detection
|
||||
# plugins
|
||||
@ -66,9 +70,11 @@ def main(argv=None):
|
||||
agent_service.enable()
|
||||
|
||||
# Verify required options
|
||||
if args.username is None or args.password is None or args.keystone_url is None:
|
||||
log.error('Username, password and keystone_url are required when running full'
|
||||
'configuration.')
|
||||
if (args.username is None or
|
||||
args.password is None or
|
||||
args.keystone_url is None):
|
||||
LOG.error('Username, password and keystone_url are required when'
|
||||
' running full configuration.')
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
base_configuration(args)
|
||||
@ -79,50 +85,59 @@ def main(argv=None):
|
||||
from detection.plugins.system import System
|
||||
plugins = [System]
|
||||
elif args.detection_plugins is not None:
|
||||
plugins = utils.select_plugins(args.detection_plugins, detected_plugins)
|
||||
plugins = utils.select_plugins(args.detection_plugins,
|
||||
detected_plugins)
|
||||
elif args.skip_detection_plugins is not None:
|
||||
plugins = utils.select_plugins(args.skip_detection_plugins, detected_plugins, skip=True)
|
||||
plugins = utils.select_plugins(args.skip_detection_plugins,
|
||||
detected_plugins, skip=True)
|
||||
else:
|
||||
plugins = detected_plugins
|
||||
plugin_names = [p.__name__ for p in plugins]
|
||||
|
||||
if args.remove: # Remove entries for each plugin from the various plugin config files
|
||||
# Remove entries for each plugin from the various plugin config files.
|
||||
if args.remove:
|
||||
changes = remove_config(args, plugin_names)
|
||||
else:
|
||||
# Run detection for all the plugins, halting on any failures if plugins
|
||||
# were specified in the arguments
|
||||
detected_config = plugin_detection(plugins, args.template_dir, args.detection_args,
|
||||
detected_config = plugin_detection(plugins, args.template_dir,
|
||||
args.detection_args,
|
||||
args.detection_args_json,
|
||||
skip_failed=(args.detection_plugins is None))
|
||||
skip_failed=(args.detection_plugins
|
||||
is None))
|
||||
if detected_config is None:
|
||||
# Indicates detection problem, skip remaining steps and give non-zero exit code
|
||||
# Indicates detection problem, skip remaining steps and give
|
||||
# non-zero exit code
|
||||
return 1
|
||||
|
||||
changes = modify_config(args, detected_config)
|
||||
|
||||
# Don't restart if only doing detection plugins and no changes found
|
||||
if args.detection_plugins is not None and not changes:
|
||||
log.info(
|
||||
LOG.info(
|
||||
'No changes found for plugins {0}, skipping restart of'
|
||||
'Monasca Agent'.format(plugin_names))
|
||||
return 0
|
||||
elif args.dry_run:
|
||||
log.info('Running in dry mode, skipping changes and restart of Monasca Agent')
|
||||
LOG.info('Running in dry mode, skipping changes and restart of'
|
||||
' Monasca Agent')
|
||||
return 0
|
||||
|
||||
# Now that the config is built, start the service
|
||||
if args.install_plugins_only:
|
||||
log.info('Command line option install_plugins_only set, skipping '
|
||||
LOG.info('Command line option install_plugins_only set, skipping '
|
||||
'service (re)start.')
|
||||
else:
|
||||
try:
|
||||
agent_service.start(restart=True)
|
||||
except subprocess.CalledProcessError:
|
||||
log.error('The service did not startup correctly see %s' % args.log_dir)
|
||||
LOG.error('The service did not startup correctly see %s',
|
||||
args.log_dir)
|
||||
|
||||
|
||||
def base_configuration(args):
|
||||
"""Write out the primary Agent configuration and setup the service.
|
||||
|
||||
:param args: Arguments from the command line
|
||||
:return: None
|
||||
"""
|
||||
@ -132,15 +147,17 @@ def base_configuration(args):
|
||||
gid = stat.pw_gid
|
||||
|
||||
# Write the main agent.yaml - Note this is always overwritten
|
||||
log.info('Configuring base Agent settings.')
|
||||
LOG.info('Configuring base Agent settings.')
|
||||
dimensions = {}
|
||||
# Join service in with the dimensions
|
||||
if args.service:
|
||||
dimensions.update({'service': args.service})
|
||||
if args.dimensions:
|
||||
dimensions.update(dict(item.strip().split(":") for item in args.dimensions.split(",")))
|
||||
dimensions.update(dict(item.strip().split(":")
|
||||
for item in args.dimensions.split(",")))
|
||||
|
||||
args.dimensions = dict((name, value) for (name, value) in dimensions.items())
|
||||
args.dimensions = dict((name, value)
|
||||
for (name, value) in dimensions.items())
|
||||
write_template(os.path.join(args.template_dir, 'agent.yaml.template'),
|
||||
os.path.join(args.config_dir, 'agent.yaml'),
|
||||
{'args': args, 'hostname': socket.getfqdn()},
|
||||
@ -148,13 +165,6 @@ def base_configuration(args):
|
||||
user=uid,
|
||||
is_yaml=True)
|
||||
|
||||
# Write the supervisor.conf
|
||||
write_template(os.path.join(args.template_dir, 'supervisor.conf.template'),
|
||||
os.path.join(args.config_dir, 'supervisor.conf'),
|
||||
{'prefix': PREFIX_DIR, 'log_dir': args.log_dir, 'monasca_user': args.user},
|
||||
user=uid,
|
||||
group=gid)
|
||||
|
||||
|
||||
def modify_config(args, detected_config):
|
||||
"""Compare existing and detected config for each check plugin and write out
|
||||
@ -169,7 +179,8 @@ def modify_config(args, detected_config):
|
||||
continue
|
||||
else:
|
||||
agent_config.save_plugin_config(
|
||||
args.config_dir, detection_plugin_name, args.user, new_config)
|
||||
args.config_dir, detection_plugin_name, args.user,
|
||||
new_config)
|
||||
else:
|
||||
config = agent_config.read_plugin_config_from_disk(
|
||||
args.config_dir, detection_plugin_name)
|
||||
@ -191,9 +202,11 @@ def modify_config(args, detected_config):
|
||||
# Check endpoint change, use new protocol instead
|
||||
# Note: config is possibly changed after running
|
||||
# check_endpoint_changes function.
|
||||
config = agent_config.check_endpoint_changes(new_config, config)
|
||||
config = agent_config.check_endpoint_changes(new_config,
|
||||
config)
|
||||
|
||||
agent_config.merge_by_name(new_config['instances'], config['instances'])
|
||||
agent_config.merge_by_name(new_config['instances'],
|
||||
config['instances'])
|
||||
# Sort before compare, if instances have no name the sort will
|
||||
# fail making order changes significant
|
||||
try:
|
||||
@ -203,7 +216,8 @@ def modify_config(args, detected_config):
|
||||
pass
|
||||
|
||||
if detection_plugin_name == "http_check":
|
||||
new_config_urls = [i['url'] for i in new_config['instances'] if 'url' in i]
|
||||
new_config_urls = [i['url'] for i in new_config['instances']
|
||||
if 'url' in i]
|
||||
# Don't write config if no change
|
||||
if new_config_urls == config_urls and new_config == config:
|
||||
continue
|
||||
@ -212,18 +226,20 @@ def modify_config(args, detected_config):
|
||||
continue
|
||||
modified_config = True
|
||||
if args.dry_run:
|
||||
log.info("Changes would be made to the config file for the {0}"
|
||||
LOG.info("Changes would be made to the config file for the {0}"
|
||||
" check plugin".format(detection_plugin_name))
|
||||
else:
|
||||
agent_config.save_plugin_config(
|
||||
args.config_dir, detection_plugin_name, args.user, new_config)
|
||||
args.config_dir, detection_plugin_name, args.user,
|
||||
new_config)
|
||||
return modified_config
|
||||
|
||||
|
||||
def validate_positive(value):
|
||||
int_value = int(value)
|
||||
if int_value <= 0:
|
||||
raise argparse.ArgumentTypeError("%s must be greater than zero" % value)
|
||||
raise argparse.ArgumentTypeError("%s must be greater than zero" %
|
||||
value)
|
||||
return int_value
|
||||
|
||||
|
||||
@ -231,11 +247,13 @@ def parse_arguments(parser):
|
||||
parser.add_argument(
|
||||
'-u',
|
||||
'--username',
|
||||
help="Username used for keystone authentication. Required for basic configuration.")
|
||||
help="Username used for keystone authentication. " +
|
||||
"Required for basic configuration.")
|
||||
parser.add_argument(
|
||||
'-p',
|
||||
'--password',
|
||||
help="Password used for keystone authentication. Required for basic configuration.")
|
||||
help="Password used for keystone authentication. " +
|
||||
"Required for basic configuration.")
|
||||
|
||||
parser.add_argument(
|
||||
'--user_domain_id',
|
||||
@ -245,8 +263,9 @@ def parse_arguments(parser):
|
||||
'--user_domain_name',
|
||||
help="User domain name for keystone authentication",
|
||||
default='')
|
||||
parser.add_argument('--keystone_url', help="Keystone url. Required for basic configuration.")
|
||||
|
||||
parser.add_argument(
|
||||
'--keystone_url',
|
||||
help="Keystone url. Required for basic configuration.")
|
||||
parser.add_argument(
|
||||
'--project_name',
|
||||
help="Project name for keystone authentication",
|
||||
@ -263,7 +282,6 @@ def parse_arguments(parser):
|
||||
'--project_id',
|
||||
help="Keystone project id for keystone authentication",
|
||||
default='')
|
||||
|
||||
parser.add_argument(
|
||||
'--monasca_url',
|
||||
help="Monasca API url, if not defined the url is pulled from keystone",
|
||||
@ -281,7 +299,6 @@ def parse_arguments(parser):
|
||||
'--region_name',
|
||||
help="Monasca API url region name in keystone catalog",
|
||||
default='')
|
||||
|
||||
parser.add_argument(
|
||||
'--system_only',
|
||||
help="Setup the service but only configure the base config and system " +
|
||||
@ -292,10 +309,12 @@ def parse_arguments(parser):
|
||||
'-d',
|
||||
'--detection_plugins',
|
||||
nargs='*',
|
||||
help="Skip base config and service setup and only configure this space separated list. " +
|
||||
"This assumes the base config has already run.")
|
||||
parser.add_argument('--skip_detection_plugins', nargs='*',
|
||||
help="Skip detection for all plugins in this space separated list.")
|
||||
help="Skip base config and service setup and only configure this " +
|
||||
"space separated list. " +
|
||||
"This assumes the base config has already run.")
|
||||
parser.add_argument(
|
||||
'--skip_detection_plugins', nargs='*',
|
||||
help="Skip detection for all plugins in this space separated list.")
|
||||
detection_args_group = parser.add_mutually_exclusive_group()
|
||||
detection_args_group.add_argument(
|
||||
'-a',
|
||||
@ -445,12 +464,13 @@ def plugin_detection(
|
||||
if detect.available:
|
||||
new_config = detect.build_config_with_name()
|
||||
if not remove:
|
||||
log.info('Configuring {0}'.format(detect.name))
|
||||
LOG.info('Configuring {0}'.format(detect.name))
|
||||
if new_config is not None:
|
||||
plugin_config.merge(new_config)
|
||||
elif not skip_failed:
|
||||
log.warn('Failed detection of plugin {0}.'.format(detect.name) +
|
||||
"\n\tPossible causes: Service not found or missing arguments.")
|
||||
LOG.warning("Failed detection of plugin %s."
|
||||
"\n\tPossible causes: Service not found or missing"
|
||||
"arguments.", detect.name)
|
||||
return None
|
||||
|
||||
return plugin_config
|
||||
@ -495,7 +515,10 @@ def remove_config(args, plugin_names):
|
||||
deletes = True
|
||||
config['instances'].remove(inst)
|
||||
if deletes:
|
||||
agent_config.delete_from_config(args, config, file_path, plugin_name)
|
||||
agent_config.delete_from_config(args, config, file_path,
|
||||
plugin_name)
|
||||
return changes
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
@ -1,14 +1,5 @@
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
# 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.
|
||||
"""Setup system service for monasca-agent and its dependencies.
|
||||
|
||||
from service import Service # noqa
|
||||
Configure systemd to start monasca-collector, monasca-forwarder,
|
||||
and monasca-statsd services using a target named monasca-agent.
|
||||
"""
|
||||
|
@ -15,43 +15,30 @@ import logging
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import linux
|
||||
from monasca_setup.service import linux
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def detect_init(*args, **kwargs):
|
||||
"""Detect the service manager running on this box
|
||||
args/kwargs match those of service.Service
|
||||
:return: The appropriate Service object for this system
|
||||
"""Create a service object if possible.
|
||||
|
||||
Detect if systemd is present on the system and if so return the service
|
||||
object.
|
||||
|
||||
:return: a systemd service object for this system.
|
||||
"""
|
||||
detected_os = platform.system()
|
||||
if detected_os == 'Linux':
|
||||
supported_linux_flavors = [
|
||||
'ubuntu', 'debian',
|
||||
'centos linux', 'red hat enterprise linux server',
|
||||
'suse linux enterprise server'
|
||||
]
|
||||
flavor = platform.linux_distribution()[0].strip()
|
||||
if flavor.lower() not in supported_linux_flavors:
|
||||
log.warn('{0} is not a supported Linux distribution'.format(flavor))
|
||||
return detect_linux_init(*args, **kwargs)
|
||||
else:
|
||||
print("{0} is not currently supported by the Monasca Agent".format(detected_os))
|
||||
sys.exit(1)
|
||||
|
||||
# Service enable, includes setup of users/config directories so must be
|
||||
# done before configuration
|
||||
if has_systemd():
|
||||
return linux.Systemd(*args, **kwargs)
|
||||
LOG.error("{0} is not currently supported by the Monasca Agent"
|
||||
.format(detected_os))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def detect_linux_init(*args, **kwargs):
|
||||
"""Detect which of the linux inits is running
|
||||
:return: Return a valid Linux service manager object
|
||||
"""
|
||||
def has_systemd():
|
||||
"""Detect if Linux init is systemd."""
|
||||
with open('/proc/1/comm', 'r') as init_proc:
|
||||
init = init_proc.readline().strip()
|
||||
if init == 'systemd':
|
||||
return linux.Systemd(*args, **kwargs)
|
||||
else:
|
||||
return linux.SysV(*args, **kwargs)
|
||||
return init == 'systemd'
|
||||
|
@ -13,24 +13,37 @@
|
||||
|
||||
""" Systemd based service
|
||||
"""
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import subprocess
|
||||
|
||||
import service
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
class Systemd(object):
|
||||
"""Manage service using systemd."""
|
||||
|
||||
def __init__(self, prefix_dir, config_dir, log_dir, template_dir,
|
||||
name='monasca-agent', username='mon-agent'):
|
||||
"""Create a service."""
|
||||
self.prefix_dir = prefix_dir
|
||||
self.config_dir = config_dir
|
||||
self.log_dir = log_dir
|
||||
self.template_dir = template_dir
|
||||
self.name = name
|
||||
self.username = username
|
||||
|
||||
class LinuxInit(service.Service):
|
||||
"""Parent class for all Linux based init systems.
|
||||
"""
|
||||
def enable(self):
|
||||
"""Does user/group directory creation.
|
||||
"""Set monasca-agent to start on boot.
|
||||
|
||||
Generally this requires running as super user.
|
||||
"""
|
||||
if os.geteuid() != 0:
|
||||
LOG.error('This service must be run as root')
|
||||
raise OSError
|
||||
|
||||
# LinuxInit.enable(self)
|
||||
# Create user/group if needed
|
||||
try:
|
||||
user = pwd.getpwnam(self.username)
|
||||
@ -39,171 +52,89 @@ class LinuxInit(service.Service):
|
||||
user = pwd.getpwnam(self.username)
|
||||
|
||||
# Create dirs
|
||||
# todo log dir is hardcoded
|
||||
for path in (self.log_dir, self.config_dir, '%s/conf.d' % self.config_dir):
|
||||
for path in (self.log_dir, self.config_dir,
|
||||
'%s/conf.d' % self.config_dir):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path, 0o755)
|
||||
os.chown(path, 0, user.pw_gid)
|
||||
# the log dir needs to be writable by the user
|
||||
# log dir needs to be writable by the user
|
||||
os.chown(self.log_dir, user.pw_uid, user.pw_gid)
|
||||
|
||||
def start(self, restart=True):
|
||||
if not self.is_enabled():
|
||||
log.error('The service is not enabled')
|
||||
return False
|
||||
# Get systemd services and target template
|
||||
templates = [f for f in os.listdir(self.template_dir)
|
||||
if (f.endswith('service.template') or
|
||||
f.endswith('target.template'))]
|
||||
systemd_path = '/etc/systemd/system/'
|
||||
|
||||
def stop(self):
|
||||
if not self.is_enabled():
|
||||
log.error('The service is not enabled')
|
||||
return True
|
||||
|
||||
def is_enabled(self):
|
||||
"""Returns True if monasca-agent is setup to start on boot, false otherwise.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Systemd(LinuxInit):
|
||||
def enable(self):
|
||||
"""Sets monasca-agent to start on boot.
|
||||
|
||||
Generally this requires running as super user
|
||||
"""
|
||||
LinuxInit.enable(self)
|
||||
|
||||
# Write the systemd script
|
||||
init_path = '/etc/systemd/system/{0}.service'.format(self.name)
|
||||
with open(os.path.join(self.template_dir, 'monasca-agent.service.template'),
|
||||
'r') as template:
|
||||
with open(init_path, 'w') as service_script:
|
||||
service_script.write(
|
||||
template.read().format(
|
||||
prefix=self.prefix_dir,
|
||||
monasca_user=self.username,
|
||||
config_dir=self.config_dir))
|
||||
os.chown(init_path, 0, 0)
|
||||
os.chmod(init_path, 0o644)
|
||||
# Write the systemd units configuration file: we have 3 services and
|
||||
# 1 target grouping all of them together
|
||||
for template_file_name in templates:
|
||||
service_file_name, e = os.path.splitext(template_file_name)
|
||||
service_file_path = os.path.join(systemd_path,
|
||||
service_file_name)
|
||||
with open(os.path.join(self.template_dir,
|
||||
template_file_name), 'r') as template:
|
||||
with open(service_file_path, 'w') as service_file:
|
||||
LOG.info('Creating service file %s', service_file_name)
|
||||
service_file.write(template.read().
|
||||
format(prefix=self.prefix_dir,
|
||||
monasca_user=self.username))
|
||||
os.chown(service_file_path, 0, 0)
|
||||
os.chmod(service_file_path, 0o644)
|
||||
|
||||
# Enable the service
|
||||
subprocess.check_call(['systemctl', 'daemon-reload'])
|
||||
subprocess.check_call(['systemctl', 'enable', '{0}.service'.format(self.name)])
|
||||
log.info('Enabled {0} service via systemd'.format(self.name))
|
||||
subprocess.check_call(
|
||||
['systemctl', 'enable', '{0}.target'.format(self.name)])
|
||||
LOG.info('Enabled %s target via systemd', self.name)
|
||||
|
||||
def start(self, restart=True):
|
||||
"""Starts monasca-agent.
|
||||
"""Start monasca-agent.
|
||||
|
||||
If the agent is running and restart is True, restart
|
||||
If the agent is running and restart is True restart it.
|
||||
|
||||
:return: True if monasca-agent is enabled on boot, False otherwise..
|
||||
"""
|
||||
LinuxInit.start(self)
|
||||
log.info('Starting {0} service via systemd'.format(self.name))
|
||||
if restart:
|
||||
subprocess.check_call(['systemctl', 'restart', '{0}.service'.format(self.name)])
|
||||
else:
|
||||
subprocess.check_call(['systemctl', 'start', '{0}.service'.format(self.name)])
|
||||
if not self.is_enabled():
|
||||
LOG.error('The service is not enabled')
|
||||
return False
|
||||
|
||||
LOG.info('Starting %s services via systemd', self.name)
|
||||
if self.is_running() and restart:
|
||||
subprocess.check_call(
|
||||
['systemctl', 'restart', '{0}.target'.format(self.name)])
|
||||
else:
|
||||
subprocess.check_call(
|
||||
['systemctl', 'start', '{0}.target'.format(self.name)])
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
"""Stops monasca-agent.
|
||||
"""
|
||||
LinuxInit.stop(self)
|
||||
log.info('Stopping {0} service'.format(self.name))
|
||||
subprocess.check_call(['systemctl', 'stop', '{0}.service'.format(self.name)])
|
||||
return True
|
||||
|
||||
def is_enabled(self):
|
||||
"""Returns True if monasca-agent is setup to start on boot, false otherwise.
|
||||
"""Stop monasca-agent.
|
||||
:return: True if monasca-agent was stopped successfully, False otherwise
|
||||
"""
|
||||
LOG.info('Stopping %s services', self.name)
|
||||
try:
|
||||
subprocess.check_output(['systemctl', 'is-enabled', '{0}.service'.format(self.name)])
|
||||
except subprocess.CalledProcessError:
|
||||
subprocess.check_call(
|
||||
['systemctl', 'stop', '{0}.target'.format(self.name)])
|
||||
except subprocess.CalledProcessError as call_error:
|
||||
LOG.error('Unable to stop monasca-agent.')
|
||||
LOG.error(call_error.output)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class SysV(LinuxInit):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
prefix_dir,
|
||||
config_dir,
|
||||
log_dir,
|
||||
template_dir,
|
||||
username,
|
||||
name='monasca-agent'):
|
||||
"""Setup this service with the given init template.
|
||||
|
||||
"""
|
||||
service.Service.__init__(
|
||||
self,
|
||||
prefix_dir,
|
||||
config_dir,
|
||||
log_dir,
|
||||
template_dir,
|
||||
name,
|
||||
username)
|
||||
self.init_script = '/etc/init.d/%s' % self.name
|
||||
self.init_template = os.path.join(template_dir, 'monasca-agent.init.template')
|
||||
|
||||
def enable(self):
|
||||
"""Sets monasca-agent to start on boot.
|
||||
|
||||
Generally this requires running as super user
|
||||
"""
|
||||
LinuxInit.enable(self)
|
||||
# Write the init script and enable.
|
||||
with open(self.init_template, 'r') as template:
|
||||
with open(self.init_script, 'w') as conf:
|
||||
conf.write(
|
||||
template.read().format(
|
||||
prefix=self.prefix_dir,
|
||||
monasca_user=self.username,
|
||||
config_dir=self.config_dir))
|
||||
os.chown(self.init_script, 0, 0)
|
||||
os.chmod(self.init_script, 0o755)
|
||||
|
||||
for runlevel in ['2', '3', '4', '5']:
|
||||
link_path = '/etc/rc%s.d/S10monasca-agent' % runlevel
|
||||
if not os.path.exists(link_path):
|
||||
os.symlink(self.init_script, link_path)
|
||||
|
||||
log.info('Enabled {0} service via SysV init script'.format(self.name))
|
||||
|
||||
def start(self, restart=True):
|
||||
"""Starts monasca-agent.
|
||||
|
||||
If the agent is running and restart is True, restart
|
||||
"""
|
||||
LinuxInit.start(self)
|
||||
|
||||
log.info('Starting {0} service via SysV init script'.format(self.name))
|
||||
if restart:
|
||||
# Throws CalledProcessError on error
|
||||
subprocess.check_call([self.init_script, 'restart'])
|
||||
else:
|
||||
subprocess.check_call([self.init_script, 'start']) # Throws CalledProcessError on error
|
||||
return True
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
"""Stops monasca-agent.
|
||||
def is_running(self):
|
||||
"""Check if monasca-agent is running.
|
||||
|
||||
:return: True if monasca-agent is running, false otherwise.
|
||||
"""
|
||||
LinuxInit.stop(self)
|
||||
|
||||
log.info('Stopping {0} service via SysV init script'.format(self.name))
|
||||
subprocess.check_call([self.init_script, 'stop']) # Throws CalledProcessError on error
|
||||
return True
|
||||
return(subprocess.call(['systemctl', 'is-active', '--quiet',
|
||||
'{0}.target'.format(self.name)]) == 0)
|
||||
|
||||
def is_enabled(self):
|
||||
"""Returns True if monasca-agent is setup to start on boot, false otherwise.
|
||||
"""Check if monasca-agent is setup to start at boot time.
|
||||
|
||||
:return: True if monasca-agent is enabled on boot, False otherwise.
|
||||
"""
|
||||
if not os.path.exists(self.init_script):
|
||||
return False
|
||||
|
||||
if len(glob.glob('/etc/rc?.d/S??monasca-agent')) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return(subprocess.call(['systemctl', 'is-enabled', '--quiet',
|
||||
'{0}.target'.format(self.name)]) == 0)
|
||||
|
@ -1,70 +0,0 @@
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
# 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.
|
||||
|
||||
"""Code to handle various service managers used on different OS
|
||||
|
||||
"""
|
||||
from monasca_agent.common.psutil_wrapper import psutil
|
||||
|
||||
|
||||
class Service(object):
|
||||
"""Abstract base class implementing the interface for various service types.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, prefix_dir, config_dir, log_dir, template_dir,
|
||||
name='monasca-agent', username='monasca-agent'):
|
||||
self.prefix_dir = prefix_dir
|
||||
self.config_dir = config_dir
|
||||
self.log_dir = log_dir
|
||||
self.template_dir = template_dir
|
||||
self.name = name
|
||||
self.username = username
|
||||
|
||||
def enable(self):
|
||||
"""Sets monasca-agent to start on boot.
|
||||
|
||||
Generally this requires running as super user
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def start(self, restart=True):
|
||||
"""Starts monasca-agent.
|
||||
|
||||
If the agent is running and restart is True, restart
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def stop(self):
|
||||
"""Stops monasca-agent.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_enabled(self):
|
||||
"""Returns True if monasca-agent is setup to start on boot, false otherwise.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_running(self):
|
||||
"""Returns True if monasca-agent is running, false otherwise.
|
||||
|
||||
"""
|
||||
# Looking for the supervisor process not the individual components
|
||||
for process in psutil.process_iter():
|
||||
if ('{0}/supervisor.conf'.format(self.config_dir)
|
||||
in process.as_dict(['cmdline'])['cmdline']):
|
||||
return True
|
||||
|
||||
return False
|
@ -69,9 +69,6 @@ install_deb: install_full
|
||||
mkdir -p $(BUILD)/etc/init.d
|
||||
cp monasca-agent-deb/monasca-agent.init $(BUILD)/etc/init.d/monasca-agent
|
||||
chmod 755 $(BUILD)/etc/init.d/monasca-agent
|
||||
# Install supervisor config.
|
||||
cp monasca-agent-deb/supervisor.conf $(BUILD)/etc/monasca/agent/supervisor.conf
|
||||
|
||||
|
||||
# Make the monasca agent debian package that includes supervisor, the forwarder
|
||||
# etc.
|
||||
@ -81,7 +78,6 @@ $(FPM_BUILD) -t deb \
|
||||
-n monasca-agent \
|
||||
-d "python (>= 2.6)" \
|
||||
-d "python-tornado (>= 2.3)" \
|
||||
-d "supervisor (>= 3.0)" \
|
||||
-d "adduser" \
|
||||
-d "sysstat" \
|
||||
-d "python-pycurl" \
|
||||
|
@ -1,12 +0,0 @@
|
||||
[Unit]
|
||||
Description=Monasca Agent
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={monasca_user}
|
||||
Group={monasca_user}
|
||||
Restart=on-failure
|
||||
ExecStart={prefix}/bin/supervisord -c {config_dir}/supervisor.conf -n
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
6
packaging/monasca-agent.target.template
Normal file
6
packaging/monasca-agent.target.template
Normal file
@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Monasca Agent
|
||||
Wants=monasca-collector.service monasca-forwarder.service monasca-statsd.service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
15
packaging/monasca-collector.service.template
Normal file
15
packaging/monasca-collector.service.template
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Monasca Agent - Collector
|
||||
PartOf=monasca-agent.target
|
||||
After=monasca-forwarder.service
|
||||
Wants=monasca-forwarder.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={monasca_user}
|
||||
Group={monasca_user}
|
||||
Restart=always
|
||||
ExecStart={prefix}/bin/monasca-collector foreground
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
15
packaging/monasca-forwarder.service.template
Normal file
15
packaging/monasca-forwarder.service.template
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Monasca Agent - Forwarder
|
||||
PartOf=monasca-agent.target
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={monasca_user}
|
||||
Group={monasca_user}
|
||||
Restart=always
|
||||
ExecStart={prefix}/bin/monasca-forwarder
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
15
packaging/monasca-statsd.service.template
Normal file
15
packaging/monasca-statsd.service.template
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Monasca Agent - Statsd
|
||||
PartOf=monasca-agent.target
|
||||
After=monasca-forwarder.service
|
||||
Wants=monasca-forwarder.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={monasca_user}
|
||||
Group={monasca_user}
|
||||
Restart=always
|
||||
ExecStart={prefix}/bin/monasca-statsd
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,48 +0,0 @@
|
||||
[supervisorctl]
|
||||
serverurl = unix:///var/tmp/monasca-agent-supervisor.sock
|
||||
|
||||
[unix_http_server]
|
||||
file=/var/tmp/monasca-agent-supervisor.sock
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||
|
||||
[supervisord]
|
||||
minfds = 1024
|
||||
minprocs = 200
|
||||
loglevel = info
|
||||
logfile = {log_dir}/supervisord.log
|
||||
logfile_maxbytes = 50MB
|
||||
nodaemon = false
|
||||
pidfile = /var/run/monasca-agent-supervisord.pid
|
||||
logfile_backups = 10
|
||||
|
||||
[program:collector]
|
||||
command={prefix}/bin/monasca-collector foreground
|
||||
stdout_logfile=NONE
|
||||
stderr_logfile=NONE
|
||||
priority=999
|
||||
startsecs=2
|
||||
user={monasca_user}
|
||||
autorestart=true
|
||||
|
||||
[program:forwarder]
|
||||
command={prefix}/bin/monasca-forwarder
|
||||
stdout_logfile=NONE
|
||||
stderr_logfile=NONE
|
||||
startsecs=3
|
||||
priority=998
|
||||
user={monasca_user}
|
||||
autorestart=true
|
||||
|
||||
[program:statsd]
|
||||
command={prefix}/bin/monasca-statsd
|
||||
stdout_logfile=NONE
|
||||
stderr_logfile=NONE
|
||||
startsecs=3
|
||||
priority=998
|
||||
user={monasca_user}
|
||||
autorestart=true
|
||||
|
||||
[group:monasca-agent]
|
||||
programs=forwarder,collector,statsd
|
@ -20,7 +20,6 @@ python-keystoneclient>=3.8.0 # Apache-2.0
|
||||
redis>=2.10.0 # MIT
|
||||
six>=1.10.0 # MIT
|
||||
stevedore>=1.20.0 # Apache-2.0
|
||||
supervisor>=3.3.3,<3.4 # BSD-derived
|
||||
tornado>=4.5.3 # Apache-2.0
|
||||
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
|
||||
# NOTE(sdague): before allowing in >= 0.21 please be sure
|
||||
|
@ -23,9 +23,10 @@ packages =
|
||||
data_files=
|
||||
share/monasca/agent =
|
||||
agent.yaml.template
|
||||
packaging/supervisor.conf.template
|
||||
packaging/monasca-agent.init.template
|
||||
packaging/monasca-agent.service.template
|
||||
packaging/monasca-collector.service.template
|
||||
packaging/monasca-forwarder.service.template
|
||||
packaging/monasca-statsd.service.template
|
||||
packaging/monasca-agent.target.template
|
||||
share/monasca/agent/conf.d = conf.d/*
|
||||
|
||||
[entry_points]
|
||||
|
Loading…
Reference in New Issue
Block a user