From 42e619c585de45e5e4e16ae3746efb7db9f90b1e Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 5 Jun 2012 17:34:29 -0400 Subject: re-rationalized service modes to make them more consistent and granular --- doc/server/plugins/generators/rules.txt | 59 +++++++++++++++++++-------------- schemas/servicetype.xsd | 14 ++++++-- src/lib/Bcfg2/Client/Tools/Chkconfig.py | 46 +++++++++---------------- src/lib/Bcfg2/Client/Tools/DebInit.py | 5 --- src/lib/Bcfg2/Client/Tools/RcUpdate.py | 40 ++++++++-------------- src/lib/Bcfg2/Client/Tools/SMF.py | 5 --- src/lib/Bcfg2/Client/Tools/Systemd.py | 17 +++------- src/lib/Bcfg2/Client/Tools/Upstart.py | 5 --- src/lib/Bcfg2/Client/Tools/__init__.py | 28 ++++++++++++---- src/lib/Bcfg2/Client/Tools/launchd.py | 5 --- tools/upgrade/1.3/service_modes.py | 46 +++++++++++++++++++++++++ 11 files changed, 147 insertions(+), 123 deletions(-) create mode 100755 tools/upgrade/1.3/service_modes.py diff --git a/doc/server/plugins/generators/rules.txt b/doc/server/plugins/generators/rules.txt index 03372b120..3b1d94480 100644 --- a/doc/server/plugins/generators/rules.txt +++ b/doc/server/plugins/generators/rules.txt @@ -118,7 +118,14 @@ Service Tag +------------+-------------------------------+---------------------------------------------------------+ | Name | Description | Values | +============+===============================+=========================================================+ -| mode | Per Service Mode (New in 1.0) | (manual | default | supervised | interactive_only ) | +| restart | Whether to restart the | ( true | false | interactive ) | +| | service when the bundle | | +| | changes (new in 1.3; replaces | | +| | "mode" attribute) | | ++------------+-------------------------------+---------------------------------------------------------+ +| install | Whether to install the | ( true | false ) | +| | service (new in 1.3; replaces | | +| | "mode" attribute) | | +------------+-------------------------------+---------------------------------------------------------+ | name | Service name or regular | String or regex | | | expression | | @@ -139,29 +146,33 @@ Service Tag | | (Upstart services only) | | +------------+-------------------------------+---------------------------------------------------------+ -Service mode descriptions -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. versionadded:: 1.0.0 - -* manual - - * do not start/stop/restart this service - * service installation is not performed - -* interactive_only - - * only attempt to start/stop/restart this service if the client is run interactively - * service installation is performed - -* default - - * perform appropriate service operations - -* supervised - - * default and ensure service is running (or stopped) when verification is performed - * deprecates supervised='true' +Service mode specification +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 1.3.0 + +In the 1.3.0 release, the "mode" attribute has been replaced by a pair +of attributes, "restart" and "install," which control how a service is +handled more granularly than the old "mode" attribute. The old "mode" +attribute values are equivalent as follows: + ++-----------------------------+------------------------------------------+ +| Mode attribute | Equivalent | ++=============================+==========================================+ +| ``mode="default"`` | ``restart="true" install="true"`` | ++-----------------------------+------------------------------------------+ +| ``mode="interactive_only"`` | ``restart="interactive" install="true"`` | ++-----------------------------+------------------------------------------+ +| ``mode="supervised"`` | ``restart="true" install="true"`` | ++-----------------------------+------------------------------------------+ +| ``mode="manual"`` | ``restart="false" install="false"`` | ++-----------------------------+------------------------------------------+ + +The default is ``restart="true" install="true"`` + +Previously, "supervised" could be used to start a service during the +verification phase; this is no longer supported. Services that have +been stopped on a client will be started during the install phase. Service status descriptions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/schemas/servicetype.xsd b/schemas/servicetype.xsd index af5bc64a6..4cab3716c 100644 --- a/schemas/servicetype.xsd +++ b/schemas/servicetype.xsd @@ -12,6 +12,16 @@ + + + + + + + + + + @@ -24,13 +34,13 @@ + + - - diff --git a/src/lib/Bcfg2/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py index 12ea5f132..0169b12da 100644 --- a/src/lib/Bcfg2/Client/Tools/Chkconfig.py +++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py @@ -45,30 +45,14 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool): except IndexError: onlevels = [] + pstatus = self.check_service(entry) if entry.get('status') == 'on': - status = (len(onlevels) > 0) + status = (len(onlevels) > 0 and pstatus) command = 'start' else: - status = (len(onlevels) == 0) + status = (len(onlevels) == 0 and not pstatus) command = 'stop' - if entry.get('mode', 'default') == 'supervised': - # turn on or off the service in supervised mode - pstatus = self.cmd.run('/sbin/service %s status' % \ - entry.get('name'))[0] - needs_modification = ((command == 'start' and pstatus) or \ - (command == 'stop' and not pstatus)) - if (not self.setup.get('dryrun') and - self.setup['servicemode'] != 'disabled' and - needs_modification): - self.cmd.run(self.get_svc_command(entry, command)) - # service was modified, so it failed - pstatus = False - - # chkconfig/init.d service - if entry.get('status') == 'on': - status = status and not pstatus - if not status: if entry.get('status') == 'on': entry.set('current_status', 'off') @@ -78,22 +62,22 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool): def InstallService(self, entry): """Install Service entry.""" - # don't take any actions for mode='manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return False rcmd = "/sbin/chkconfig %s %s" self.cmd.run("/sbin/chkconfig --add %s" % (entry.attrib['name'])) self.logger.info("Installing Service %s" % (entry.get('name'))) - pass1 = True + rv = True if entry.get('status') == 'off': - rc = self.cmd.run(rcmd % (entry.get('name'), - entry.get('status')) + \ - " --level 0123456")[0] - pass1 = rc == 0 - rc = self.cmd.run(rcmd % (entry.get('name'), entry.get('status')))[0] - return pass1 and rc == 0 + rv &= self.cmd.run(rcmd + " --level 0123456" % + (entry.get('name'), + entry.get('status')))[0] == 0 + if entry.get("current_status") == "on": + rv &= self.stop_service(entry) + else: + rv &= self.cmd.run(rcmd % (entry.get('name'), + entry.get('status')))[0] == 0 + if entry.get("current_status") == "off": + rv &= self.start_service(entry) + return rv def FindExtra(self): """Locate extra chkconfig Services.""" diff --git a/src/lib/Bcfg2/Client/Tools/DebInit.py b/src/lib/Bcfg2/Client/Tools/DebInit.py index ca6fc439e..7d5af1127 100644 --- a/src/lib/Bcfg2/Client/Tools/DebInit.py +++ b/src/lib/Bcfg2/Client/Tools/DebInit.py @@ -76,11 +76,6 @@ class DebInit(Bcfg2.Client.Tools.SvcTool): def InstallService(self, entry): """Install Service for entry.""" - # don't take any actions for mode='manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return False self.logger.info("Installing Service %s" % (entry.get('name'))) try: os.stat('/etc/init.d/%s' % entry.get('name')) diff --git a/src/lib/Bcfg2/Client/Tools/RcUpdate.py b/src/lib/Bcfg2/Client/Tools/RcUpdate.py index 1b9a29478..ddf9c1f2d 100644 --- a/src/lib/Bcfg2/Client/Tools/RcUpdate.py +++ b/src/lib/Bcfg2/Client/Tools/RcUpdate.py @@ -23,22 +23,18 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool): rc = self.cmd.run(cmd % entry.get('name'))[0] is_enabled = (rc == 0) - if entry.get('mode', 'default') == 'supervised': - # check if init script exists - try: - os.stat('/etc/init.d/%s' % entry.get('name')) - except OSError: - self.logger.debug('Init script for service %s does not exist' % - entry.get('name')) - return False + # check if init script exists + try: + os.stat('/etc/init.d/%s' % entry.get('name')) + except OSError: + self.logger.debug('Init script for service %s does not exist' % + entry.get('name')) + return False - # check if service is enabled - cmd = '/etc/init.d/%s status | grep started' - rc = self.cmd.run(cmd % entry.attrib['name'])[0] - is_running = (rc == 0) - else: - # we don't care - is_running = is_enabled + # check if service is enabled + cmd = '/etc/init.d/%s status | grep started' + rc = self.cmd.run(cmd % entry.attrib['name'])[0] + is_running = (rc == 0) if entry.get('status') == 'on' and not (is_enabled and is_running): entry.set('current_status', 'off') @@ -53,19 +49,11 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool): def InstallService(self, entry): """ Install Service entry - In supervised mode we also take care it's (not) running. """ - # don't take any actions for mode='manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return False self.logger.info('Installing Service %s' % entry.get('name')) if entry.get('status') == 'on': - # make sure it's running if in supervised mode - if entry.get('mode', 'default') == 'supervised' \ - and entry.get('current_status') == 'off': + if entry.get('current_status') == 'off': self.start_service(entry) # make sure it's enabled cmd = '/sbin/rc-update add %s default' @@ -73,9 +61,7 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool): return (rc == 0) elif entry.get('status') == 'off': - # make sure it's not running if in supervised mode - if entry.get('mode', 'default') == 'supervised' \ - and entry.get('current_status') == 'on': + if entry.get('current_status') == 'on': self.stop_service(entry) # make sure it's disabled cmd = '/sbin/rc-update del %s default' diff --git a/src/lib/Bcfg2/Client/Tools/SMF.py b/src/lib/Bcfg2/Client/Tools/SMF.py index f824410ad..3e0a9da13 100644 --- a/src/lib/Bcfg2/Client/Tools/SMF.py +++ b/src/lib/Bcfg2/Client/Tools/SMF.py @@ -73,11 +73,6 @@ class SMF(Bcfg2.Client.Tools.SvcTool): def InstallService(self, entry): """Install SMF Service entry.""" - # don't take any actions for mode='manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return False self.logger.info("Installing Service %s" % (entry.get('name'))) if entry.get('status') == 'off': if entry.get("FMRI").startswith('lrc'): diff --git a/src/lib/Bcfg2/Client/Tools/Systemd.py b/src/lib/Bcfg2/Client/Tools/Systemd.py index e3f6a4169..a295bc608 100644 --- a/src/lib/Bcfg2/Client/Tools/Systemd.py +++ b/src/lib/Bcfg2/Client/Tools/Systemd.py @@ -42,18 +42,11 @@ class Systemd(Bcfg2.Client.Tools.SvcTool): def InstallService(self, entry): """Install Service entry.""" - # don't take any actions for mode = 'manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return True - if entry.get('status') == 'on': - pstatus = self.cmd.run(self.get_svc_command(entry, 'enable'))[0] - pstatus = self.cmd.run(self.get_svc_command(entry, 'start'))[0] - + rv = self.cmd.run(self.get_svc_command(entry, 'enable'))[0] == 0 + rv &= self.cmd.run(self.get_svc_command(entry, 'start'))[0] == 0 else: - pstatus = self.cmd.run(self.get_svc_command(entry, 'stop'))[0] - pstatus = self.cmd.run(self.get_svc_command(entry, 'disable'))[0] + rv = self.cmd.run(self.get_svc_command(entry, 'stop'))[0] == 0 + rv &= self.cmd.run(self.get_svc_command(entry, 'disable'))[0] == 0 - return not pstatus + return rv diff --git a/src/lib/Bcfg2/Client/Tools/Upstart.py b/src/lib/Bcfg2/Client/Tools/Upstart.py index 7afc8edd7..aa5a921a6 100644 --- a/src/lib/Bcfg2/Client/Tools/Upstart.py +++ b/src/lib/Bcfg2/Client/Tools/Upstart.py @@ -69,11 +69,6 @@ class Upstart(Bcfg2.Client.Tools.SvcTool): def InstallService(self, entry): """Install Service for entry.""" - # don't take any actions for mode='manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return False if entry.get('status') == 'on': pstatus = self.cmd.run(self.get_svc_command(entry, 'start'))[0] elif entry.get('status') == 'off': diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py index c6cb6e239..e741b5e23 100644 --- a/src/lib/Bcfg2/Client/Tools/__init__.py +++ b/src/lib/Bcfg2/Client/Tools/__init__.py @@ -305,8 +305,7 @@ class SvcTool(Tool): return self.cmd.run(self.get_svc_command(service, restart_target))[0] def check_service(self, service): - # not supported for this driver - return 0 + return self.cmd.run(self.get_svc_command(service, 'status'))[0] == 0 def Remove(self, services): """ Dummy implementation of service removal method """ @@ -321,13 +320,12 @@ class SvcTool(Tool): return for entry in [ent for ent in bundle if self.handlesEntry(ent)]: - mode = entry.get('mode', 'default') - if (mode == 'manual' or - (mode == 'interactive_only' and + restart = entry.get("restart", "true") + if (restart.lower() == "false" or + (restart.lower == "interactive" and not self.setup['interactive'])): continue - # need to handle servicemode = (build|default) - # need to handle mode = (default|supervised) + rc = None if entry.get('status') == 'on': if self.setup['servicemode'] == 'build': @@ -351,3 +349,19 @@ class SvcTool(Tool): if rc: self.logger.error("Failed to manipulate service %s" % (entry.get('name'))) + + def Install(self, entries, states): + """Install all entries in sublist.""" + for entry in entries: + if entry.get('install', 'true').lower() == 'false': + self.logger.info("Service %s installation is false. Skipping " + "installation." % (entry.get('name'))) + continue + try: + func = getattr(self, "Install%s" % (entry.tag)) + states[entry] = func(entry) + if states[entry]: + self.modified.append(entry) + except: + self.logger.error("Unexpected failure of install method for entry type %s" + % (entry.tag), exc_info=1) diff --git a/src/lib/Bcfg2/Client/Tools/launchd.py b/src/lib/Bcfg2/Client/Tools/launchd.py index c022d32ae..6f08559a2 100644 --- a/src/lib/Bcfg2/Client/Tools/launchd.py +++ b/src/lib/Bcfg2/Client/Tools/launchd.py @@ -88,11 +88,6 @@ class launchd(Bcfg2.Client.Tools.Tool): def InstallService(self, entry): """Enable or disable launchd item.""" - # don't take any actions for mode='manual' - if entry.get('mode', 'default') == 'manual': - self.logger.info("Service %s mode set to manual. Skipping " - "installation." % (entry.get('name'))) - return False name = entry.get('name') if entry.get('status') == 'on': self.logger.error("Installing service %s" % name) diff --git a/tools/upgrade/1.3/service_modes.py b/tools/upgrade/1.3/service_modes.py new file mode 100755 index 000000000..b658fb51e --- /dev/null +++ b/tools/upgrade/1.3/service_modes.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +import os +import sys +import glob +import lxml.etree +import Bcfg2.Options + +def main(): + opts = dict(repo=Bcfg2.Options.SERVER_REPOSITORY) + setup = Bcfg2.Options.OptionParser(opts) + setup.parse(sys.argv[1:]) + + for bfile in glob.glob(os.path.join(setup['repo'], "Bundler", "*")): + bdata = lxml.etree.parse(bfile) + changed = False + for svc in bdata.xpath("//Service|//BoundService"): + if "mode" not in svc.attrib: + continue + mode = svc.get("mode") + del svc.attrib["mode"] + if mode not in ["default", "supervised", "interactive_only", + "manual"]: + print("Unrecognized mode on Service:%s: %s. Assuming default" % + (svc.get("name"), mode)) + mode = "default" + if mode == "default" or mode == "supervised": + svc.set("restart", "true") + svc.set("install", "true") + elif mode == "interactive_only": + svc.set("restart", "interactive") + svc.set("install", "true") + elif mode == "manual": + svc.set("restart", "false") + svc.set("install", "false") + changed = True + if changed: + print("Writing %s" % bfile) + try: + open(bfile, "w").write(lxml.etree.tostring(bdata)) + except IOError: + err = sys.exc_info()[1] + print("Could not write %s: %s" % (bfile, err)) + +if __name__ == '__main__': + sys.exit(main()) -- cgit v1.2.3-1-g7c22