[Zodb-checkins] SVN: ZODB/trunk/ Merge rev 30120 from 3.4 branch.

Tim Peters tim.one at comcast.net
Fri Apr 22 18:35:42 EDT 2005


Log message for revision 30121:
  Merge rev 30120 from 3.4 branch.
  
  Port ZEO changes for Windows from ZODB 3.2.
  
  In addition, repaired the useless default logging in runzeo.py.
  
  Here's Sidnei's Zope 2.7 checkin comment:
  
  * Borrow Zope's 'Signal' mechanism for Windows, if available, to
    implement clean shutdown and log rotation handlers for Windows.
  
  * Back to creating a .PID for ZEO, so external programs that wish to set
    the 'signal' can get the PID and therefore derive the signal name.
    Currently only necessary on Windows but created on all platforms which
    implement os.getpid(), as long as the 'pid-filename' option is set,
    or the 'INSTANCE_HOME' environment variable can be found.
  

Changed:
  U   ZODB/trunk/NEWS.txt
  U   ZODB/trunk/src/ZEO/component.xml
  U   ZODB/trunk/src/ZEO/mkzeoinst.py
  U   ZODB/trunk/src/ZEO/runzeo.py

-=-
Modified: ZODB/trunk/NEWS.txt
===================================================================
--- ZODB/trunk/NEWS.txt	2005-04-22 22:34:30 UTC (rev 30120)
+++ ZODB/trunk/NEWS.txt	2005-04-22 22:35:42 UTC (rev 30121)
@@ -7,6 +7,33 @@
 ==========================
 Release date: DD-MMM-YYYY
 
+ZEO
+---
+
+The default logging setup in ``runzeo.py`` was broken.  It was changed
+so that running ``runzeo.py`` from a command line now, and without using
+a config file, prints output to the console much as ZODB 3.2 did.
+
+ZEO on Windows
+--------------
+
+Thanks to Mark Hammond for these ``runzeo.py`` enhancements on Windows:
+
+- A pid file (containing the process id as a decimal string) is created now
+  for a ZEO server started via ``runzeo.py``.  External programs can
+  read the pid from this file and derive a "signal name" used in a new
+  signal-emulation scheme for Windows.  This is only necessary on Windows,
+  but the pid file is created on all platforms that implement
+  ``os.getpid()``, as long as the ``pid-filename`` option is set, or
+  environment variable ``INSTANCE_HOME`` is defined.  The ``pid-filename``
+  option can be set in a ZEO config file, or passed as the new ``--pid-file``
+  argument to ``runzeo.py``.
+
+- If available, ``runzeo.py`` now uses Zope's new 'Signal' mechanism for
+  Windows, to implement clean shutdown and log rotation handlers for Windows.
+  Note that the Python in use on the ZEO server must also have the Python
+  Win32 extensions installed for this to be useful.
+
 DemoStorage
 -----------
 

Modified: ZODB/trunk/src/ZEO/component.xml
===================================================================
--- ZODB/trunk/src/ZEO/component.xml	2005-04-22 22:34:30 UTC (rev 30120)
+++ ZODB/trunk/src/ZEO/component.xml	2005-04-22 22:35:42 UTC (rev 30121)
@@ -93,6 +93,15 @@
       </description>
     </key>
 
+    <key name="pid-filename" datatype="existing-dirpath"
+         required="no">
+      <description>
+        The full path to the file in which to write the ZEO server's Process ID
+        at startup. If omitted, $INSTANCE/var/ZEO.pid is used.
+      </description>
+      <metadefault>$INSTANCE/var/ZEO.pid (or $clienthome/ZEO.pid)</metadefault>
+    </key>
+
   </sectiontype>
 
 </component>

Modified: ZODB/trunk/src/ZEO/mkzeoinst.py
===================================================================
--- ZODB/trunk/src/ZEO/mkzeoinst.py	2005-04-22 22:34:30 UTC (rev 30120)
+++ ZODB/trunk/src/ZEO/mkzeoinst.py	2005-04-22 22:35:42 UTC (rev 30121)
@@ -47,6 +47,7 @@
   address %(port)d
   read-only false
   invalidation-queue-size 100
+  # pid-filename $INSTANCE/var/ZEO.pid
   # monitor-address PORT
   # transaction-timeout SECONDS
 </zeo>

Modified: ZODB/trunk/src/ZEO/runzeo.py
===================================================================
--- ZODB/trunk/src/ZEO/runzeo.py	2005-04-22 22:34:30 UTC (rev 30120)
+++ ZODB/trunk/src/ZEO/runzeo.py	2005-04-22 22:35:42 UTC (rev 30121)
@@ -24,6 +24,9 @@
 -t/--timeout TIMEOUT -- transaction timeout in seconds (default no timeout)
 -h/--help -- print this usage message and exit
 -m/--monitor ADDRESS -- address of monitor server ([HOST:]PORT or PATH)
+--pid-file PATH -- relative path to output file containing this process's pid;
+                   default $(INSTANCE_HOME)/var/ZEO.pid but only if envar
+                   INSTANCE_HOME is defined
 
 Unless -C is specified, -a and -f are required.
 """
@@ -50,12 +53,15 @@
     message = "(%s) %s" % (_pid, msg)
     logger.log(level, message, exc_info=exc_info)
 
-
 def parse_address(arg):
     # Caution:  Not part of the official ZConfig API.
     obj = ZConfig.datatypes.SocketAddress(arg)
     return obj.family, obj.address
 
+def windows_shutdown_handler():
+    # Called by the signal mechanism on Windows to perform shutdown.
+    import asyncore
+    asyncore.close_all()
 
 class ZEOOptionsMixin:
 
@@ -104,6 +110,8 @@
                  None, 'auth-database=')
         self.add('auth_realm', 'zeo.authentication_realm',
                  None, 'auth-realm=')
+        self.add('pid_file', 'zeo.pid_filename',
+                 None, 'pid-file=')
 
 class ZEOOptions(ZDOptions, ZEOOptionsMixin):
 
@@ -126,6 +134,7 @@
         self.setup_default_logging()
         self.check_socket()
         self.clear_socket()
+        self.make_pidfile()
         try:
             self.open_storages()
             self.setup_signals()
@@ -134,15 +143,20 @@
         finally:
             self.close_storages()
             self.clear_socket()
+            self.remove_pidfile()
 
     def setup_default_logging(self):
         if self.options.config_logger is not None:
             return
         # No log file is configured; default to stderr.
-        logger = logging.getLogger()
+        root = logging.getLogger()
+        root.setLevel(logging.INFO)
+        fmt = logging.Formatter(
+            "------\n%(asctime)s %(levelname)s %(name)s %(message)s",
+            "%Y-%m-%dT%H:%M:%S")
         handler = logging.StreamHandler()
-        handler.setLevel(logging.INFO)
-        logger.addHandler(handler)
+        handler.setFormatter(fmt)
+        root.addHandler(handler)
 
     def check_socket(self):
         if self.can_connect(self.options.family, self.options.address):
@@ -182,6 +196,8 @@
         method is called without additional arguments.
         """
         if os.name != "posix":
+            if os.name == "nt":
+                self.setup_win32_signals()
             return
         if hasattr(signal, 'SIGXFSZ'):
             signal.signal(signal.SIGXFSZ, signal.SIG_IGN) # Special case
@@ -193,6 +209,27 @@
                     method()
                 signal.signal(sig, wrapper)
 
+    def setup_win32_signals(self):
+        # Borrow the Zope Signals package win32 support, if available.
+        # Signals does a check/log for the availability of pywin32.
+        try:
+            import Signals.Signals
+        except ImportError:
+            logger.debug("Signals package not found. "
+                         "Windows-specific signal handler "
+                         "will *not* be installed.")
+            return
+        SignalHandler = Signals.Signals.SignalHandler
+        if SignalHandler is not None: # may be None if no pywin32.
+            SignalHandler.registerHandler(signal.SIGTERM,
+                                          windows_shutdown_handler)
+            SignalHandler.registerHandler(signal.SIGINT,
+                                          windows_shutdown_handler)
+            # Can use the log rotate handler too.
+            from Signals.Signals import logfileRotateHandler
+            SIGUSR2 = 12 # not in signal module on Windows.
+            SignalHandler.registerHandler(SIGUSR2, logfileRotateHandler)
+
     def create_server(self):
         from ZEO.StorageServer import StorageServer
         self.server = StorageServer(
@@ -237,7 +274,53 @@
                 log("failed to close storage %r" % name,
                     level=logging.EXCEPTION, exc_info=True)
 
+    def _get_pidfile(self):
+        pidfile = self.options.pid_file
+        # 'pidfile' is marked as not required.
+        if not pidfile:
+            # Try to find a reasonable location if the pidfile is not
+            # set. If we are running in a Zope environment, we can
+            # safely assume INSTANCE_HOME.
+            instance_home = os.environ.get("INSTANCE_HOME")
+            if not instance_home:
+                # If all our attempts failed, just log a message and
+                # proceed.
+                logger.debug("'pidfile' option not set, and 'INSTANCE_HOME' "
+                             "environment variable could not be found. "
+                             "Cannot guess pidfile location.")
+                return
+            self.options.pid_file = os.path.join(instance_home,
+                                                 "var", "ZEO.pid")
 
+    def make_pidfile(self):
+        if not self.options.read_only:
+            self._get_pidfile()
+            pidfile = self.options.pid_file
+            if pidfile is None:
+                return
+            pid = os.getpid()
+            try:
+                if os.path.exists(pidfile):
+                    os.unlink(pidfile)
+                f = open(pidfile, 'w')
+                print >> f, pid
+                f.close()
+                log("created PID file '%s'" % pidfile)
+            except IOError:
+                logger.error("PID file '%s' cannot be opened" % pidfile)
+
+    def remove_pidfile(self):
+        if not self.options.read_only:
+            pidfile = self.options.pid_file
+            if pidfile is None:
+                return
+            try:
+                if os.path.exists(pidfile):
+                    os.unlink(pidfile)
+                    log("removed PID file '%s'" % pidfile)
+            except IOError:
+                logger.error("PID file '%s' could not be removed" % pidfile)
+
 # Signal names
 
 signames = None



More information about the Zodb-checkins mailing list