Structural changes to allow a single detection plugin to setup multiple plugins
Added a Plugins object to allow merging of various configs before writing the file.
This commit is contained in:
parent
40d1cb7bee
commit
9853526cfb
|
@ -1,85 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
""" Detect running daemons then configure and start the agent.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from detection import network, nova
|
||||
from service import sysv
|
||||
|
||||
# List of all detection plugins to run
|
||||
DETECTION_PLUGINS = [network.Network, nova.Nova]
|
||||
# Map OS to service type
|
||||
OS_SERVICE_MAP = {'linux': 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('-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")
|
||||
#todo provide an option to exclude certain detection plugins
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s")
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
log.info('Configuring base Agent settings.')
|
||||
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.template_dir, args.overwrite)
|
||||
if detect.available:
|
||||
log.info('Configuring {0}'.format(detect.name))
|
||||
detect.build_config()
|
||||
#todo add option to install dependencies
|
||||
|
||||
# Now that the config is build start the service
|
||||
try:
|
||||
agent_service.start(restart=True)
|
||||
except subprocess.CalledProcessError:
|
||||
log.error('The service did not startup correctly see /var/log/mon-agent')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
|
@ -0,0 +1,20 @@
|
|||
"""Classes to aid in configuration of the agent."""
|
||||
|
||||
import collections
|
||||
|
||||
|
||||
class Plugins(collections.defaultdict):
|
||||
"""A container for the plugin configurations used by the mon-agent.
|
||||
This is essentially a defaultdict(dict) but put into a class primarily to make the interface clear, also
|
||||
to add a couple of helper methods.
|
||||
Each plugin config is stored with the key being its config name (excluding .yaml).
|
||||
The value a dict which will convert to yaml.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(Plugins, self).__init__(dict)
|
||||
|
||||
# todo Possibly enforce the key being a string without .yaml in it.
|
||||
|
||||
def diff(self, other_plugins):
|
||||
raise NotImplementedError
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
|
||||
class Plugin(object):
|
||||
"""Abstract class implemented by the mon-agent plugin detection classes"""
|
||||
"""Abstract class implemented by the mon-agent plugin detection classes
|
||||
"""
|
||||
# todo these should include dependency detection
|
||||
|
||||
def __init__(self, config_dir, template_dir, overwrite=True):
|
||||
def __init__(self, template_dir, overwrite=True):
|
||||
self.available = False
|
||||
self.config_dir = config_dir
|
||||
self.template_dir = template_dir
|
||||
self.dependencies = ()
|
||||
self.overwrite = overwrite
|
||||
|
@ -20,6 +20,8 @@ class Plugin(object):
|
|||
raise NotImplementedError
|
||||
|
||||
def build_config(self):
|
||||
"""Build the config as a Plugins object and return.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def dependencies_installed(self):
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
from . import Plugin
|
||||
from monsetup import agent_config
|
||||
|
||||
|
||||
class MySQL(Plugin):
|
||||
"""Detect MySQL daemons and setup configuration to monitor them."""
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection, set self.available True if the service is detected."""
|
||||
self.available = True
|
||||
|
||||
def build_config(self):
|
||||
"""Build the config as a Plugins object and return.
|
||||
"""
|
||||
return agent_config.Plugins()
|
||||
|
||||
def dependencies_installed(self):
|
||||
pass
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import shutil
|
||||
import yaml
|
||||
|
||||
from . import Plugin
|
||||
from monsetup import agent_config
|
||||
|
||||
|
||||
class Network(Plugin):
|
||||
|
@ -9,13 +10,18 @@ class Network(Plugin):
|
|||
"""
|
||||
|
||||
def _detect(self):
|
||||
"""Run detection"""
|
||||
"""Run detection, set self.available True if the service is detected."""
|
||||
self.available = True
|
||||
|
||||
def build_config(self):
|
||||
"""No detection just copy the config"""
|
||||
shutil.copyfile(os.path.join(self.template_dir, 'conf.d/network.yaml'),
|
||||
os.path.join(self.config_dir, 'conf.d/network.yaml'))
|
||||
"""Build the config as a Plugins object and return.
|
||||
"""
|
||||
# A bit silly to parse the yaml only for it to be converted back but this plugin is the exception not the rule
|
||||
with open(os.path.join(self.template_dir, 'conf.d/network.yaml'), 'r') as network_template:
|
||||
default_net_config = yaml.load(network_template.read())
|
||||
config = agent_config.Plugins()
|
||||
config['network'] = default_net_config
|
||||
return config
|
||||
|
||||
def dependencies_installed(self):
|
||||
return True
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from . import Plugin
|
||||
from monsetup import agent_config
|
||||
|
||||
|
||||
class Nova(Plugin):
|
||||
|
@ -9,7 +10,9 @@ class Nova(Plugin):
|
|||
self.available = True
|
||||
|
||||
def build_config(self):
|
||||
pass
|
||||
"""Build the config as a Plugins object and return.
|
||||
"""
|
||||
return agent_config.Plugins()
|
||||
|
||||
def dependencies_installed(self):
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env python
|
||||
""" Detect running daemons then configure and start the agent.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
import agent_config
|
||||
from detection import mysql, network, nova
|
||||
from service import sysv
|
||||
|
||||
# List of all detection plugins to run
|
||||
DETECTION_PLUGINS = [mysql.MySQL, network.Network, nova.Nova]
|
||||
# Map OS to service type
|
||||
OS_SERVICE_MAP = {'linux': 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('-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 plugin configuration." +
|
||||
"The default is to merge. Agent.conf is always overwritten.",
|
||||
action="store_true")
|
||||
parser.add_argument('-v', '--verbose', help="Verbose Output", action="store_true")
|
||||
#todo provide an option to exclude certain detection plugins
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s")
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
||||
|
||||
# todo implement a way to include some predefined configuration, useful for active checks
|
||||
|
||||
# Detect os
|
||||
detected_os = 'linux' # todo add detection
|
||||
|
||||
# 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 - Note this is always overwritten
|
||||
log.info('Configuring base Agent settings.')
|
||||
with open(os.path.join(args.template_dir, 'agent.conf.template'), 'r') as agent_template:
|
||||
with open(os.path.join(args.config_dir, 'agent.conf'), 'w') as agent_conf:
|
||||
agent_conf.write(agent_template.read().format(args=args, hostname=socket.gethostname()))
|
||||
# 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
|
||||
plugin_config = agent_config.Plugins()
|
||||
for detect_class in DETECTION_PLUGINS:
|
||||
detect = detect_class(args.template_dir, args.overwrite)
|
||||
if detect.available:
|
||||
log.info('Configuring {0}'.format(detect.name))
|
||||
new_config = detect.build_config()
|
||||
plugin_config.update(new_config)
|
||||
|
||||
#todo add option to install dependencies
|
||||
|
||||
# Write out the plugin config
|
||||
for key, value in plugin_config.iteritems():
|
||||
# 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 show a diff before overwriting or merging config
|
||||
config_path = os.path.join(args.config_dir, 'conf.d', key + '.yaml')
|
||||
if (not args.overwrite) and os.path.exists(config_path): # merge old and new config, new has precedence
|
||||
with open(config_path, 'r') as config_file:
|
||||
old_config = yaml.load(config_file.read())
|
||||
if old_config is not None:
|
||||
value = old_config.update(value)
|
||||
with open(config_path, 'w') as config_file:
|
||||
config_file.write(yaml.dump(value))
|
||||
|
||||
# Now that the config is build start the service
|
||||
try:
|
||||
agent_service.start(restart=True)
|
||||
except subprocess.CalledProcessError:
|
||||
log.error('The service did not startup correctly see /var/log/mon-agent')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue