[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/winservice/ add
windows service base
Brian Lloyd
brian at zope.com
Wed Apr 27 11:25:50 EDT 2005
Log message for revision 30201:
add windows service base
Changed:
A Zope3/trunk/src/zope/app/winservice/
A Zope3/trunk/src/zope/app/winservice/__init__.py
A Zope3/trunk/src/zope/app/winservice/service.py
-=-
Added: Zope3/trunk/src/zope/app/winservice/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/winservice/__init__.py 2005-04-27 11:20:56 UTC (rev 30200)
+++ Zope3/trunk/src/zope/app/winservice/__init__.py 2005-04-27 15:25:49 UTC (rev 30201)
@@ -0,0 +1,13 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
Property changes on: Zope3/trunk/src/zope/app/winservice/__init__.py
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: Zope3/trunk/src/zope/app/winservice/service.py
===================================================================
--- Zope3/trunk/src/zope/app/winservice/service.py 2005-04-27 11:20:56 UTC (rev 30200)
+++ Zope3/trunk/src/zope/app/winservice/service.py 2005-04-27 15:25:49 UTC (rev 30201)
@@ -0,0 +1,180 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""
+Windows NT/2K service installer/controller for Zope/ZEO/ZRS instances.
+"""
+
+import win32serviceutil
+import win32service
+import win32event
+import win32process
+import pywintypes
+import time
+import os
+
+# the max seconds we're allowed to spend backing off
+BACKOFF_MAX = 300
+# if the process runs successfully for more than BACKOFF_CLEAR_TIME
+# seconds, we reset the backoff stats to their initial values
+BACKOFF_CLEAR_TIME = 30
+# the initial backoff interval (the amount of time we wait to restart
+# a dead process)
+BACKOFF_INITIAL_INTERVAL = 5
+
+class Service(win32serviceutil.ServiceFramework):
+ """ A class representing a Windows NT service that can manage an
+ instance-home-based Zope/ZEO/ZRS processes """
+
+ # The PythonService model requires that an actual on-disk class declaration
+ # represent a single service. Thus, the below definition of start_cmd,
+ # must be overridden in a subclass in a file within the instance home for
+ # each instance. The below-defined start_cmd (and _svc_display_name_
+ # and _svc_name_) are just examples.
+
+ _svc_name_ = r'Zope-Instance'
+ _svc_display_name_ = r'Zope instance at C:\Zope-Instance'
+
+ start_cmd = (
+ r'"C:\Program Files\Zope-2.7.0-a1\bin\python.exe" '
+ r'"C:\Program Files\Zope-2.7.0-a1\lib\python\Zope\Startup\run.py" '
+ r'-C "C:\Zope-Instance\etc\zope.conf"'
+ )
+
+ def __init__(self, args):
+ win32serviceutil.ServiceFramework.__init__(self, args)
+ # Create an event which we will use to wait on.
+ # The "service stop" request will set this event.
+ self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
+
+ def SvcStop(self):
+ # Before we do anything, tell the SCM we are starting the stop process.
+ self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
+ # stop the process if necessary
+ try:
+ win32process.TerminateProcess(self.hZope, 0)
+ except pywintypes.error:
+ # the process may already have been terminated
+ pass
+ # And set my event.
+ win32event.SetEvent(self.hWaitStop)
+
+ def createProcess(self, cmd):
+ return win32process.CreateProcess(
+ None, cmd, None, None, 0, 0, None, None,
+ win32process.STARTUPINFO())
+
+ def SvcDoRun(self):
+ # indicate to Zope that the process is daemon managed (restartable)
+ os.environ['ZMANAGED'] = '1'
+
+ # daemon behavior: we want to to restart the process if it
+ # dies, but if it dies too many times, we need to give up.
+
+ # we use a simple backoff algorithm to determine whether
+ # we should try to restart a dead process: for each
+ # time the process dies unexpectedly, we wait some number of
+ # seconds to restart it, as determined by the backoff interval,
+ # which doubles each time the process dies. if we exceed
+ # BACKOFF_MAX seconds in cumulative backoff time, we give up.
+ # at any time if we successfully run the process for more thab
+ # BACKOFF_CLEAR_TIME seconds, the backoff stats are reset.
+
+ # the initial number of seconds between process start attempts
+ backoff_interval = BACKOFF_INITIAL_INTERVAL
+ # the cumulative backoff seconds counter
+ backoff_cumulative = 0
+
+ import servicemanager
+
+ # log a service started message
+ servicemanager.LogMsg(
+ servicemanager.EVENTLOG_INFORMATION_TYPE,
+ servicemanager.PYS_SERVICE_STARTED,
+ (self._svc_name_, ' (%s)' % self._svc_display_name_))
+
+ while 1:
+ start_time = time.time()
+ info = self.createProcess(self.start_cmd)
+ self.hZope = info[0] # the pid
+ if backoff_interval > BACKOFF_INITIAL_INTERVAL:
+ # if we're in a backoff state, log a message about
+ # starting a new process
+ servicemanager.LogInfoMsg(
+ '%s (%s): recovering from died process, new process '
+ 'started' % (self._svc_name_, self._svc_display_name_)
+ )
+ rc = win32event.WaitForMultipleObjects(
+ (self.hWaitStop, self.hZope), 0, win32event.INFINITE)
+ if rc == win32event.WAIT_OBJECT_0:
+ # user sent a stop service request
+ self.SvcStop()
+ break
+ else:
+ # user did not send a service stop request, but
+ # the process died; this may be an error condition
+ status = win32process.GetExitCodeProcess(self.hZope)
+ if status == 0:
+ # the user shut the process down from the web
+ # interface (or it otherwise exited cleanly)
+ break
+ else:
+ # this was an abormal shutdown. if we can, we want to
+ # restart the process but if it seems hopeless,
+ # don't restart an infinite number of times.
+ if backoff_cumulative > BACKOFF_MAX:
+ # it's hopeless
+ servicemanager.LogErrorMsg(
+ '%s (%s): process could not be restarted due to max '
+ 'restart attempts exceeded' % (
+ self._svc_display_name_, self._svc_name_
+ ))
+ self.SvcStop()
+ break
+ servicemanager.LogWarningMsg(
+ '%s (%s): process died unexpectedly. Will attempt '
+ 'restart after %s seconds.' % (
+ self._svc_name_, self._svc_display_name_,
+ backoff_interval
+ )
+ )
+ # if BACKOFF_CLEAR_TIME seconds have elapsed since we last
+ # started the process, reset the backoff interval
+ # and the cumulative backoff time to their original
+ # states
+ if time.time() - start_time > BACKOFF_CLEAR_TIME:
+ backoff_interval = BACKOFF_INITIAL_INTERVAL
+ backoff_cumulative = 0
+ # we sleep for the backoff interval. since this is async
+ # code, it would be better done by sending and
+ # catching a timed event (a service
+ # stop request will need to wait for us to stop sleeping),
+ # but this works well enough for me.
+ time.sleep(backoff_interval)
+ # update backoff_cumulative with the time we spent
+ # backing off.
+ backoff_cumulative = backoff_cumulative + backoff_interval
+ # bump the backoff interval up by 2* the last interval
+ backoff_interval = backoff_interval * 2
+
+ # loop and try to restart the process
+
+ # log a service stopped message
+ servicemanager.LogMsg(
+ servicemanager.EVENTLOG_INFORMATION_TYPE,
+ servicemanager.PYS_SERVICE_STOPPED,
+ (self._svc_name_, ' (%s) ' % self._svc_display_name_))
+
+if __name__=='__main__':
+ win32serviceutil.HandleCommandLine(Service)
Property changes on: Zope3/trunk/src/zope/app/winservice/service.py
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
More information about the Zope3-Checkins
mailing list