Initial working mon-setup
This commit is contained in:
parent
4a791b3716
commit
771f63128a
|
@ -1,5 +1,4 @@
|
|||
mon-agent
|
||||
=========
|
||||
#mon-agent
|
||||
|
||||
The Monitoring Agent is a system for gathering metrics and sending them to
|
||||
the Monitoring API. In its basic configuration, the Agent collects metrics
|
||||
|
@ -10,4 +9,8 @@ these may be used to gather metrics against remote systems and services as well.
|
|||
For information on deploying and using the Monitoring Agent, please see the
|
||||
[Wiki](https://github.com/hpcloud-mon/mon-agent/wiki)
|
||||
|
||||
# Simple Installation
|
||||
- `pip install mon-agent`
|
||||
- Run configuration `mon-setup -u me -p pass -s mini-mon --keystone_url https://keystone --mon_url https://mon-api`
|
||||
|
||||
Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
[Api]
|
||||
# Monitoring API URL: URL for the monitoring API
|
||||
# Example: https://region-a.geo-1.monitoring.hpcloudsvc.com/v1.1/metrics
|
||||
url: CHANGE_ME
|
||||
# Monitoring API Project Id: Project Id for the monitoring API
|
||||
project_id: CHANGE_ME
|
||||
# Monitoring API Username: Username for the monitoring API
|
||||
username: CHANGE_ME
|
||||
# Monitoring API Password: Password for the monitoring API
|
||||
password: CHANGE_ME
|
||||
# Use Keystone for Authentication?: Use Keystone for authentication
|
||||
# True or False
|
||||
use_keystone: True
|
||||
url: {args.mon_url}
|
||||
# Keystone Username
|
||||
username: {args.username}
|
||||
# Keystone Password
|
||||
password: {args.password}
|
||||
# Keystone API URL: URL for the Keystone server to use
|
||||
# Example: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v3/auth/tokens
|
||||
keystone_url: CHANGE_ME
|
||||
keystone_url: {args.keystone_url}
|
||||
|
||||
[Main]
|
||||
|
||||
# Force the hostname to whatever you want.
|
||||
#hostname: mymachine.mydomain
|
||||
hostname: {hostname}
|
||||
|
||||
# Optional dimensions to be sent with every metric from this node
|
||||
# They should be in the format name:value and seperated by a comma
|
||||
# Example (dimensions: service:nova, group:group_a, ...)
|
||||
dimensions: service:mini-mon
|
||||
dimensions: service:{args.service}
|
||||
|
||||
# Set the threshold for accepting points to allow anything
|
||||
# with recent_point_threshold seconds
|
||||
|
@ -119,7 +114,7 @@ monstatsd_port : 8125
|
|||
# Logging
|
||||
# ========================================================================== #
|
||||
|
||||
log_level: DEBUG
|
||||
log_level: INFO
|
||||
|
||||
collector_log_file: /var/log/mon-agent/collector.log
|
||||
forwarder_log_file: /var/log/mon-agent/forwarder.log
|
|
@ -824,7 +824,7 @@ def get_mon_api_config(config):
|
|||
'project_id': '',
|
||||
'username': '',
|
||||
'password': False,
|
||||
'use_keystone': False,
|
||||
'use_keystone': True,
|
||||
'keystone_url': '',
|
||||
'dimensions': None}
|
||||
|
||||
|
|
|
@ -3,44 +3,77 @@
|
|||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
|
||||
import detection
|
||||
import service
|
||||
from detection import nova
|
||||
from service import sysv
|
||||
|
||||
# List of all detection plugins to run
|
||||
DETECTION_PLUGINS = [nova.Nova]
|
||||
# Map OS to service type
|
||||
OS_SERVICE_MAP = {'linux': sysv.SysV}
|
||||
|
||||
DETECTION_CLASSES = [detection.Core]
|
||||
OS_SERVICE_MAP = {'linux': service.sysv.SysV}
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description='Detect running daemons then configure and start the agent.')
|
||||
# parser.add_argument('--foo')
|
||||
# - It will need to be invoked with keystone credentials. Other options you can invoke with include:
|
||||
# - non-interactive
|
||||
# - force overwrite of existing config
|
||||
# - alternative config output directory
|
||||
# - Optional active check config to include -
|
||||
parser.add_argument('-u', '--username', help="Keystone username used to post metrics", required=True)
|
||||
parser.add_argument('-p', '--password', help="Keystone password used to post metrics", required=True)
|
||||
parser.add_argument('-s', '--service', help="Service this node is associated with.", required=True)
|
||||
parser.add_argument('--keystone_url', help="Keystone url", required=True)
|
||||
parser.add_argument('--mon_url', help="Mon API url", required=True)
|
||||
parser.add_argument('--config_dir', help="Alternative configuration directory", default='/etc/mon-agent')
|
||||
parser.add_argument('--template_dir', help="Alternative template directory", default='/usr/local/share/mon/agent')
|
||||
parser.add_argument('--headless', help="Run in a non-interactive mode", action="store_true")
|
||||
parser.add_argument('--overwrite', help="Overwrite existing configuration", action="store_true")
|
||||
parser.add_argument('-v', '--verbose', help="Verbose Output", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
# todo these are currently hardcoded
|
||||
config_dir = '/etc/mon-agent'
|
||||
overwrite = True
|
||||
# todo add the ability to build the config in a temp dir then diff
|
||||
if args.verbose:
|
||||
log.setLevel(logging.DEBUG)
|
||||
else:
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
# todo implement a way to include some predefined configuration, useful for active checks
|
||||
# todo if overwrite is set I should either warn or just delete any config files not in the new config
|
||||
# todo add the ability to build the config in a temp dir then diff
|
||||
|
||||
# Detect os
|
||||
detected_os = 'linux' # todo add detection
|
||||
|
||||
# Run through detection and config building
|
||||
for detect_class in DETECTION_CLASSES:
|
||||
detect = detect_class(config_dir, overwrite)
|
||||
detect.build_config()
|
||||
|
||||
# Service enable/start
|
||||
agent_service = OS_SERVICE_MAP[detected_os]
|
||||
# Service enable, includes setup of config directories so must be done before configuration
|
||||
# todo is there a better place for basic directories to be made then the service enabling?
|
||||
agent_service = OS_SERVICE_MAP[detected_os](os.path.join(args.template_dir, 'mon-agent.init'), args.config_dir)
|
||||
# Todo add logic for situations where either enable or start is not needed or if not running as root isn't possible
|
||||
agent_service.enable()
|
||||
|
||||
# Write the main agent.conf
|
||||
agent_template = open(os.path.join(args.template_dir, 'agent.conf.template'), 'r')
|
||||
agent_conf = open(os.path.join(args.config_dir, 'agent.conf'), 'w')
|
||||
agent_conf.write(agent_template.read().format(args=args, hostname=socket.gethostname()))
|
||||
agent_template.close()
|
||||
agent_conf.close()
|
||||
# Link the supervisor.conf
|
||||
supervisor_path = os.path.join(args.config_dir, 'supervisor.conf')
|
||||
if os.path.exists(supervisor_path):
|
||||
os.remove(supervisor_path)
|
||||
os.symlink(os.path.join(args.template_dir, 'supervisor.conf'), supervisor_path)
|
||||
|
||||
# Run through detection and config building for the plugins
|
||||
for detect_class in DETECTION_PLUGINS:
|
||||
detect = detect_class(args.config_dir, args.overwrite)
|
||||
if detect.has_dependencies():
|
||||
detect.build_config()
|
||||
else:
|
||||
log.warn('{0} found but not configured as it is missing dependencies: {1}'.format(detect.name,
|
||||
detect.dependencies))
|
||||
#todo add option to install dependencies
|
||||
|
||||
# Now that the config is build start the service
|
||||
agent_service.start(restart=True)
|
||||
|
||||
|
||||
|
|
|
@ -3,21 +3,30 @@
|
|||
"""
|
||||
|
||||
|
||||
class Base(object):
|
||||
"""Base detection class implementing the interface."""
|
||||
class Plugin(object):
|
||||
"""Abstract class implemented by the mon-agent plugin detection classes"""
|
||||
# todo these should include dependency detection
|
||||
|
||||
def __init__(self, config_dir, overwrite=True):
|
||||
self.config_dir = config_dir
|
||||
self.dependencies = ()
|
||||
self.overwrite = overwrite
|
||||
self._detect()
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection"""
|
||||
raise NotImplementedError
|
||||
|
||||
def build_config(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def has_dependencies(self):
|
||||
raise NotImplementedError
|
||||
|
||||
class Core(Base):
|
||||
"""Detect details related to the core mon-agent configuration."""
|
||||
|
||||
|
||||
class Plugin(Base):
|
||||
"""Abstract class implemented by the mon-agent plugin detection classes"""
|
||||
# todo these should include dependency detection
|
||||
@property
|
||||
def name(self):
|
||||
"""Return _name if set otherwise the class name"""
|
||||
if '_name' in self.__dict__:
|
||||
return self._name
|
||||
else:
|
||||
return self.__class__.__name__
|
||||
|
|
|
@ -3,3 +3,15 @@ from . import Plugin
|
|||
|
||||
class Nova(Plugin):
|
||||
"""Detect Nova daemons and setup configuration to monitor them."""
|
||||
pass
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection"""
|
||||
pass
|
||||
|
||||
def build_config(self):
|
||||
pass
|
||||
|
||||
def has_dependencies(self):
|
||||
pass
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
"""Classes implementing different methods for running mon-agent on startup as well as starting the process immediately
|
||||
"""
|
||||
import psutil
|
||||
|
||||
|
||||
class Service(object):
|
||||
"""Abstract base class implementing the interface for various service types."""
|
||||
def __init__(self, name='mon-agent'):
|
||||
def __init__(self, config_dir, name='mon-agent'):
|
||||
self.config_dir = config_dir
|
||||
self.name = name
|
||||
|
||||
def enable(self):
|
||||
|
@ -32,4 +34,9 @@ class Service(object):
|
|||
def is_running(self):
|
||||
"""Returns True if mon-agent is running, false otherwise
|
||||
"""
|
||||
raise NotImplementedError
|
||||
# Looking for the supervisor process not the individual components
|
||||
for process in psutil.pids():
|
||||
if '/etc/mon-agent/supervisor.conf' in process.cmdline():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -1,15 +1,81 @@
|
|||
"""System V style service.
|
||||
"""
|
||||
from glob import glob
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import subprocess
|
||||
|
||||
from . import Service
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SysV(Service):
|
||||
def __init__(self, init_template):
|
||||
def __init__(self, init_template, config_dir, name='mon-agent', username='mon-agent'):
|
||||
"""Setup this service with the given init template"""
|
||||
super(SysV, self).__init__()
|
||||
super(SysV, self).__init__(config_dir, name)
|
||||
self.init_script = '/etc/init.d/%s' % self.name
|
||||
self.init_template = init_template
|
||||
self.username = username
|
||||
|
||||
def enable(self):
|
||||
"""Sets mon-agent to start on boot.
|
||||
Generally this requires running as super user
|
||||
"""
|
||||
# Create mon-agent user/group if needed
|
||||
try:
|
||||
user = pwd.getpwnam(self.username)
|
||||
except KeyError:
|
||||
subprocess.check_call(['useradd', '-r', self.username])
|
||||
user = pwd.getpwnam(self.username)
|
||||
|
||||
# Create dirs
|
||||
# todo log dir is hardcoded
|
||||
for path in ('/var/log/mon-agent', self.config_dir, '%s/conf.d' % self.config_dir):
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path, 0775)
|
||||
os.chown(path, user.pw_uid, user.pw_gid)
|
||||
|
||||
# link the init script, then enable
|
||||
if not os.path.exists(self.init_script):
|
||||
os.symlink(self.init_template, self.init_script)
|
||||
os.chmod(self.init_script, 0755)
|
||||
|
||||
for runlevel in ['2', '3', '4', '5']:
|
||||
link_path = '/etc/rc%s.d/S10mon-agent' % runlevel
|
||||
if not os.path.exists(link_path):
|
||||
os.symlink(self.init_script, link_path)
|
||||
|
||||
def start(self, restart=True):
|
||||
"""Starts mon-agent
|
||||
If the agent is running and restart is True, restart
|
||||
"""
|
||||
if not self.is_enabled():
|
||||
log.error('The service is not enabled')
|
||||
return False
|
||||
|
||||
subprocess.check_call([self.init_script, 'start']) # Throws CalledProcessError on error
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
"""Stops mon-agent
|
||||
"""
|
||||
if not self.is_enabled():
|
||||
log.error('The service is not enabled')
|
||||
return False
|
||||
|
||||
subprocess.check_call([self.init_script, 'stop']) # Throws CalledProcessError on error
|
||||
return True
|
||||
|
||||
def is_enabled(self):
|
||||
"""Returns True if mon-agent is setup to start on boot, false otherwise
|
||||
"""
|
||||
if not os.path.exists(self.init_script):
|
||||
return False
|
||||
|
||||
if len(glob('/etc/rc?.d/S??mon-agent')) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# todo largely unimplemented, all needed files end up in /usr/local/share/mon/agent
|
||||
# todo don't forget to setup the proper users for the agent to run as
|
||||
# todo will need to setup supervisor.con
|
3
setup.py
3
setup.py
|
@ -24,6 +24,7 @@ install_requires=[
|
|||
'PyYAML',
|
||||
'redis',
|
||||
'simplejson',
|
||||
'supervisor',
|
||||
'tornado',
|
||||
'python-monclient',
|
||||
]
|
||||
|
@ -137,7 +138,7 @@ setup(
|
|||
],
|
||||
},
|
||||
include_package_data=True,
|
||||
data_files=[('share/mon/agent', ['agent.conf.example','packaging/supervisor.conf', 'packaging/mon-agent.init']),
|
||||
data_files=[('share/mon/agent', ['agent.conf.template', 'packaging/supervisor.conf', 'packaging/mon-agent.init']),
|
||||
('share/mon/agent/conf.d', glob('conf.d/*'))],
|
||||
test_suite='nose.collector'
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue