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 Agent is a system for gathering metrics and sending them to
|
||||||
the Monitoring API. In its basic configuration, the Agent collects metrics
|
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
|
For information on deploying and using the Monitoring Agent, please see the
|
||||||
[Wiki](https://github.com/hpcloud-mon/mon-agent/wiki)
|
[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.
|
Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
|
|
@ -1,29 +1,24 @@
|
||||||
[Api]
|
[Api]
|
||||||
# Monitoring API URL: URL for the monitoring API
|
# Monitoring API URL: URL for the monitoring API
|
||||||
# Example: https://region-a.geo-1.monitoring.hpcloudsvc.com/v1.1/metrics
|
# Example: https://region-a.geo-1.monitoring.hpcloudsvc.com/v1.1/metrics
|
||||||
url: CHANGE_ME
|
url: {args.mon_url}
|
||||||
# Monitoring API Project Id: Project Id for the monitoring API
|
# Keystone Username
|
||||||
project_id: CHANGE_ME
|
username: {args.username}
|
||||||
# Monitoring API Username: Username for the monitoring API
|
# Keystone Password
|
||||||
username: CHANGE_ME
|
password: {args.password}
|
||||||
# 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
|
|
||||||
# Keystone API URL: URL for the Keystone server to use
|
# Keystone API URL: URL for the Keystone server to use
|
||||||
# Example: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v3/auth/tokens
|
# Example: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v3/auth/tokens
|
||||||
keystone_url: CHANGE_ME
|
keystone_url: {args.keystone_url}
|
||||||
|
|
||||||
[Main]
|
[Main]
|
||||||
|
|
||||||
# Force the hostname to whatever you want.
|
# Force the hostname to whatever you want.
|
||||||
#hostname: mymachine.mydomain
|
hostname: {hostname}
|
||||||
|
|
||||||
# Optional dimensions to be sent with every metric from this node
|
# Optional dimensions to be sent with every metric from this node
|
||||||
# They should be in the format name:value and seperated by a comma
|
# They should be in the format name:value and seperated by a comma
|
||||||
# Example (dimensions: service:nova, group:group_a, ...)
|
# Example (dimensions: service:nova, group:group_a, ...)
|
||||||
dimensions: service:mini-mon
|
dimensions: service:{args.service}
|
||||||
|
|
||||||
# Set the threshold for accepting points to allow anything
|
# Set the threshold for accepting points to allow anything
|
||||||
# with recent_point_threshold seconds
|
# with recent_point_threshold seconds
|
||||||
|
@ -119,7 +114,7 @@ monstatsd_port : 8125
|
||||||
# Logging
|
# Logging
|
||||||
# ========================================================================== #
|
# ========================================================================== #
|
||||||
|
|
||||||
log_level: DEBUG
|
log_level: INFO
|
||||||
|
|
||||||
collector_log_file: /var/log/mon-agent/collector.log
|
collector_log_file: /var/log/mon-agent/collector.log
|
||||||
forwarder_log_file: /var/log/mon-agent/forwarder.log
|
forwarder_log_file: /var/log/mon-agent/forwarder.log
|
|
@ -824,7 +824,7 @@ def get_mon_api_config(config):
|
||||||
'project_id': '',
|
'project_id': '',
|
||||||
'username': '',
|
'username': '',
|
||||||
'password': False,
|
'password': False,
|
||||||
'use_keystone': False,
|
'use_keystone': True,
|
||||||
'keystone_url': '',
|
'keystone_url': '',
|
||||||
'dimensions': None}
|
'dimensions': None}
|
||||||
|
|
||||||
|
|
|
@ -3,44 +3,77 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import detection
|
from detection import nova
|
||||||
import service
|
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]
|
log = logging.getLogger(__name__)
|
||||||
OS_SERVICE_MAP = {'linux': service.sysv.SysV}
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
parser = argparse.ArgumentParser(description='Detect running daemons then configure and start the agent.')
|
parser = argparse.ArgumentParser(description='Detect running daemons then configure and start the agent.')
|
||||||
# parser.add_argument('--foo')
|
parser.add_argument('-u', '--username', help="Keystone username used to post metrics", required=True)
|
||||||
# - It will need to be invoked with keystone credentials. Other options you can invoke with include:
|
parser.add_argument('-p', '--password', help="Keystone password used to post metrics", required=True)
|
||||||
# - non-interactive
|
parser.add_argument('-s', '--service', help="Service this node is associated with.", required=True)
|
||||||
# - force overwrite of existing config
|
parser.add_argument('--keystone_url', help="Keystone url", required=True)
|
||||||
# - alternative config output directory
|
parser.add_argument('--mon_url', help="Mon API url", required=True)
|
||||||
# - Optional active check config to include -
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# todo these are currently hardcoded
|
if args.verbose:
|
||||||
config_dir = '/etc/mon-agent'
|
log.setLevel(logging.DEBUG)
|
||||||
overwrite = True
|
else:
|
||||||
# todo add the ability to build the config in a temp dir then diff
|
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
|
# Detect os
|
||||||
detected_os = 'linux' # todo add detection
|
detected_os = 'linux' # todo add detection
|
||||||
|
|
||||||
# Run through detection and config building
|
# Service enable, includes setup of config directories so must be done before configuration
|
||||||
for detect_class in DETECTION_CLASSES:
|
# todo is there a better place for basic directories to be made then the service enabling?
|
||||||
detect = detect_class(config_dir, overwrite)
|
agent_service = OS_SERVICE_MAP[detected_os](os.path.join(args.template_dir, 'mon-agent.init'), args.config_dir)
|
||||||
detect.build_config()
|
|
||||||
|
|
||||||
# Service enable/start
|
|
||||||
agent_service = OS_SERVICE_MAP[detected_os]
|
|
||||||
# Todo add logic for situations where either enable or start is not needed or if not running as root isn't possible
|
# 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()
|
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)
|
agent_service.start(restart=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,30 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Base(object):
|
class Plugin(object):
|
||||||
"""Base detection class implementing the interface."""
|
"""Abstract class implemented by the mon-agent plugin detection classes"""
|
||||||
|
# todo these should include dependency detection
|
||||||
|
|
||||||
def __init__(self, config_dir, overwrite=True):
|
def __init__(self, config_dir, overwrite=True):
|
||||||
self.config_dir = config_dir
|
self.config_dir = config_dir
|
||||||
|
self.dependencies = ()
|
||||||
self.overwrite = overwrite
|
self.overwrite = overwrite
|
||||||
|
self._detect()
|
||||||
|
|
||||||
|
def _detect(self):
|
||||||
|
"""Run detection"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def build_config(self):
|
def build_config(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def has_dependencies(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
class Core(Base):
|
@property
|
||||||
"""Detect details related to the core mon-agent configuration."""
|
def name(self):
|
||||||
|
"""Return _name if set otherwise the class name"""
|
||||||
|
if '_name' in self.__dict__:
|
||||||
class Plugin(Base):
|
return self._name
|
||||||
"""Abstract class implemented by the mon-agent plugin detection classes"""
|
else:
|
||||||
# todo these should include dependency detection
|
return self.__class__.__name__
|
||||||
|
|
|
@ -3,3 +3,15 @@ from . import Plugin
|
||||||
|
|
||||||
class Nova(Plugin):
|
class Nova(Plugin):
|
||||||
"""Detect Nova daemons and setup configuration to monitor them."""
|
"""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
|
"""Classes implementing different methods for running mon-agent on startup as well as starting the process immediately
|
||||||
"""
|
"""
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
|
||||||
class Service(object):
|
class Service(object):
|
||||||
"""Abstract base class implementing the interface for various service types."""
|
"""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
|
self.name = name
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
|
@ -32,4 +34,9 @@ class Service(object):
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
"""Returns True if mon-agent is running, false otherwise
|
"""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.
|
"""System V style service.
|
||||||
"""
|
"""
|
||||||
|
from glob import glob
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from . import Service
|
from . import Service
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SysV(Service):
|
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"""
|
"""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.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',
|
'PyYAML',
|
||||||
'redis',
|
'redis',
|
||||||
'simplejson',
|
'simplejson',
|
||||||
|
'supervisor',
|
||||||
'tornado',
|
'tornado',
|
||||||
'python-monclient',
|
'python-monclient',
|
||||||
]
|
]
|
||||||
|
@ -137,7 +138,7 @@ setup(
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
include_package_data=True,
|
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/*'))],
|
('share/mon/agent/conf.d', glob('conf.d/*'))],
|
||||||
test_suite='nose.collector'
|
test_suite='nose.collector'
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue