Added a systemd service provider

Change-Id: I2e98f120bd937131b739778f5737f068976a0a14
This commit is contained in:
Tim Kuhlman 2015-04-21 14:00:05 -06:00
parent 48b1c4bc5f
commit 5f5c77847d
6 changed files with 160 additions and 72 deletions

View File

@ -5,7 +5,6 @@
import argparse
import logging
import os
import platform
import pwd
import socket
import subprocess
@ -14,13 +13,8 @@ import yaml
import agent_config
from detection.plugins import DETECTION_PLUGINS
import service.sysv as sysv
from service.detection import detect_init
from detection.utils import check_output
# List of all detection plugins to run
# Map OS to service type
OS_SERVICE_MAP = {'Linux': sysv.SysV}
log = logging.getLogger(__name__)
@ -101,41 +95,8 @@ def main(argv=None):
else:
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
# Detect os
detected_os = platform.system()
if detected_os == 'Linux':
supported_linux_flavors = ['Ubuntu', 'debian']
this_linux_flavor = platform.linux_distribution()[0]
if this_linux_flavor in supported_linux_flavors:
for package in ['coreutils']:
# Check for required dependencies for system checks
try:
output = check_output('dpkg -s {0}'.format(package),
stderr=subprocess.STDOUT,
shell=True)
except subprocess.CalledProcessError:
log.warn("*** {0} package is not installed! ***".format(package) +
"\nNOTE: If you do not install the {0} ".format(package) +
"package, you will not receive all of the standard " +
"operating system type metrics!")
else:
pass
elif detected_os == 'Darwin':
print("Mac OS is not currently supported by the Monasca Agent")
sys.exit()
elif detected_os == 'Windows':
print("Windows is not currently supported by the Monasca Agent")
sys.exit()
else:
print("{0} is not currently supported by the Monasca Agent".format(detected_os))
# Service enable, includes setup of users/config directories so must be
# done before configuration
agent_service = OS_SERVICE_MAP[detected_os](PREFIX_DIR,
args.config_dir,
args.log_dir,
args.template_dir,
username=args.user)
# 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)
if not args.skip_enable:
agent_service.enable()
@ -152,7 +113,7 @@ def main(argv=None):
args.dimensions = dict((name, value) for (name, value) in dimensions.iteritems())
write_template(os.path.join(args.template_dir, 'agent.yaml.template'),
os.path.join(args.config_dir, 'agent.yaml'),
{'args': args, 'hostname': socket.getfqdn() },
{'args': args, 'hostname': socket.getfqdn()},
gid,
is_yaml=True)

View File

@ -0,0 +1,40 @@
import logging
import platform
import sys
import linux
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 apropriate Service object for this system
"""
detected_os = platform.system()
if detected_os == 'Linux':
supported_linux_flavors = ['Ubuntu', 'debian']
flavor = platform.linux_distribution()[0]
if flavor not in supported_linux_flavors:
log.warn('{0} is not a support 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
def detect_linux_init(*args, **kwargs):
""" Detect which of the linux inits is running
:return: Return a valid Linux service manager object
"""
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)

View File

@ -1,5 +1,4 @@
"""System V style service.
""" Systemd based service
"""
import glob
import logging
@ -9,24 +8,15 @@ import subprocess
import service
log = logging.getLogger(__name__)
class SysV(service.Service):
def __init__(self, prefix_dir, config_dir, log_dir, template_dir, name='monasca-agent', username='monasca-agent'):
"""Setup this service with the given init template.
"""
super(SysV, self).__init__(prefix_dir, config_dir, log_dir, name)
self.init_script = '/etc/init.d/%s' % self.name
self.init_template = os.path.join(template_dir, 'monasca-agent.init.template')
self.username = username
class LinuxInit(service.Service):
""" Parent class for all Linux based init systems.
"""
def enable(self):
"""Sets monasca-agent to start on boot.
Generally this requires running as super user
""" Does user/group directory creation.
"""
# Create monasca-agent user/group if needed
try:
@ -44,6 +34,94 @@ class SysV(service.Service):
# the 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
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, 0644)
# 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))
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 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)])
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.
"""
try:
subprocess.check_output(['systemctl', 'is-enabled', '{0}.service'.format(self.name)])
except subprocess.CalledProcessError:
return False
return True
class SysV(LinuxInit):
def __init__(self, prefix_dir, config_dir, log_dir, template_dir, name='monasca-agent', username='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:
@ -63,9 +141,7 @@ class SysV(service.Service):
If the agent is running and restart is True, restart
"""
if not self.is_enabled():
log.error('The service is not enabled')
return False
LinuxInit.start(self)
log.info('Starting {0} service via SysV init script'.format(self.name))
if restart:
@ -78,9 +154,7 @@ class SysV(service.Service):
"""Stops monasca-agent.
"""
if not self.is_enabled():
log.error('The service is not enabled')
return False
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
@ -96,4 +170,4 @@ class SysV(service.Service):
if len(glob.glob('/etc/rc?.d/S??monasca-agent')) > 0:
return True
else:
return False
return False

View File

@ -1,20 +1,21 @@
"""Classes implementing different methods for running monasca-agent on startup as well as starting the process immediately.
"""Code to handle various service managers used on different OS
"""
import psutil
class Service(object):
"""Abstract base class implementing the interface for various service types.
"""
def __init__(self, prefix_dir, config_dir, log_dir, name='monasca-agent'):
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.
@ -42,14 +43,13 @@ class Service(object):
"""
raise NotImplementedError
@staticmethod
def is_running():
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 '/etc/monasca/agent/supervisor.conf' in process.cmdline():
if '{0}/supervisor.conf'.format(self.config_dir) in process.cmdline():
return True
return False

View File

@ -0,0 +1,12 @@
[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

View File

@ -24,6 +24,7 @@ data_files=
agent.yaml.template
packaging/supervisor.conf.template
packaging/monasca-agent.init.template
packaging/monasca-agent.service.template
share/monasca/agent/conf.d = conf.d/*
[entry_points]