wiki:DeployAsWindowsService
Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Version 3 (modified by sdelatorre, 14 years ago) (diff)

--

Introduction

This page describes the steps needed to run a TurboGears application as a Windows service.

Prerequisites

The win32all package (downloadable from  http://starship.python.net/crew/mhammond/win32/) must be installed before the Windows service functionality can be used.

Installation Instructions

  1. Copy the code at the end of this page and create a file called 'service.py' with its contents.
    • Note: this file does not have to be in the same directory as the TG application code.
  2. Edit service.py's "USER EDIT SECTION" with the appropriate information. A description of each variable follows:
    • _svc_name_ (Required): The name of the service (used in the Windows registry).
    • _svc_display_name_ (Required): The name that will be displayed in the Windows Services management console.
    • code_dir (Required): The base directory of the TG application code. This should be the same directory where the <project-name>-start.py, dev.cfg, and prod.cfg files are located.
    • root_class (Required): The fully qualified name of the main TG class. For example, if you were enabling the  20 Minute Wiki tutorial as a Windows service, then the fully qualified class name would be 'wiki20.controllers.Root'.
    • log_dir (Optional): The location for the app's stdout and stderr log files. If no location is provided, then the value of code_dir will be used.
  3. Open a command prompt and navigate to the directory where service.py is located. Use the following commands to install/start/stop/remove your service:
service.py                        (Lists all available options)
service.py install                (Installs the service with a manual startup )
service.py --startup auto install (Installs the service with auto startup)    
service.py start                  (Starts the service)
service.py stop                   (Stops the service)
service.py remove                 (Removes the service)
  1. The service should now be accessible and controllable from the Window Services management console.

Notes

  • The autoreload functionality does not work when a Cherrypy application is run as a Windows service (it crashes the service). Therefore, the code will disable the autoreload functionality before the server is started.
  • When the Python code runs as a Windows service, the current directory is automatically set to C:\<python-install-dir>\lib\site-packages\win32. The code below uses the code_dir value to reset the current directory to the base directory of the TG application.
  • The stdout and stderr output is redirected to two files (stdout.log and stderr.log). stdout and stderr must be redirected because when running as a service, they don't have a valid file to write to. So, when Windows attempts to flush stdout or stderr, the service crashes and the following entry is found in the Windows Event Application Logs:
The instance's SvcRun() method failed 
  File "C:\Python24\lib\site-packages\win32\lib\win32serviceutil.py", line 742, in SvcRun
    self.SvcDoRun()
  File "C:\Documents and Settings\<user>\Desktop\service.py", line 80, in SvcDoRun
    self.tg_init()
  File "C:\Documents and Settings\<user>\Desktop\service.py", line 115, in tg_init
    print 'try to crash the buffer over and over again...' 
exceptions.IOError: (9, 'Bad file descriptor')
  • The Cherrpy site also lists a  similar method of creating a Cherrypy Windows service.

Troubleshooting

Use the Windows Event Viewer, the stdout, and the stderr log files to locate problems with your application.

Code

# File name: service.py
#
# The service module defines a single class (TGWindowsService) that contains
# all of the necessary functionality for running a TurboGears application as
# a Windows Service.
# 
# To use this class, users must do the following:
# 1. Download and install the win32all package
#    (http://starship.python.net/crew/mhammond/win32/)
# 2. Edit the "USER EDIT SECTION with the proper information.
# 3. Open a command prompt and navigate to the directory where this file
#    is located.  Use one of the following commands to
#    install/start/stop/remove the service:
#    > service.py install
#    > service.py start
#    > service.py stop
#    > service.py remove
#    Additionally, typing "service.py" will present the user with all of the
#    available options.
#
# Once installed, the service will be accessible through the Services
# management console just like any other Windows Service.  All exceptions
# encountered by the TGWindowsService class will be viewable in the
# Windows event viewer; all application specific exceptions should appear
# in the stdout/stderr logs.
#
# This module has been tested on Windows Server 2000, 2003, and Windows
# XP Professional.
#
# Note 1: This file does not need to exist in the same directory as the code for
# the TurbGears application.  The TGWindowsService class will find the
# application's code using the "code_dir" class attribute as defined
# by the user.
#
# Note 2: The cherrypy autoreload functionality will not function when cherrypy
# is run as a Windows service, so the TGWindowsService class will automatically
# disable autoreloading before starting the server.

import cherrypy
import pkg_resources
pkg_resources.require("TurboGears")

import sys
import os
from os.path import *

import win32serviceutil
import win32service
from win32com.client import constants

class TGWindowsService(win32serviceutil.ServiceFramework):
    """TurboGears Windows Service helper class.

    The TGWindowsService class contains all the functionality required
    for running a TurboGears application as a Windows Service.  The only
    user edits required for this class are located in the following class
    variables:
    
    _svc_name_:         The name of the service (used in the Windows registry).
    _svc_display_name_: The name that will appear in the Windows Service Manager.
    code_dir:           The full path to the base directory of the user's
                        TG app code (usually where <project_name>-start.py
                        and the *.cfg files are located).
    root_class:         The fully qualified Root class name
                        (e.g. wiki20.controllers.Root)
    log_dir:            The desired location of the stdout and stderr
                        log files.

    For information on installing the application, please refer to the
    documentation at the end of this module or navigate to the directory
    where this module is located and type "service.py" from the command
    prompt.
    """

    # -- START USER EDIT SECTION
    # -- Users must edit this section before installing the service.
    _svc_name_ = ''          # (Required) The name of the service.
    _svc_display_name_ = ''  # (Required) The Service Manager display name.
    code_dir = r''           # (Required) The base directory of the TG app code.
    root_class = ''          # (Required) The fully qualified Root class name.
    log_dir = r''            # (Optional) The log directory. Default = code_dir.
    # -- END USER EDIT SECTION
    
    def SvcDoRun(self):
        """ Called when the Windows Service runs. """

        self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
        self.tg_init()
        cherrypy.root = self.root()
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
        cherrypy.server.start()
    
    def SvcStop(self):
        """Called when Windows receives a service stop request."""
        
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        cherrypy.server.stop()
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

    def tg_init(self):
        """ Checks for the required data and initializes the application. """

        if TGWindowsService.code_dir:
            os.chdir(TGWindowsService.code_dir)
            sys.path.append(TGWindowsService.code_dir)
            # Redirect stdout and stderr to avoid buffer crashes.            
            sys.stdout = open(join(TGWindowsService.log_dir, 'stdout.log'),'a')
            sys.stderr = open(join(TGWindowsService.log_dir, 'stderr.log'),'a')
        else:
            raise ValueError("""The code directory setting is missing.
                                The Windows Service will not run
                                without this setting.""")

        if not TGWindowsService.root_class:
            raise ValueError("""The fully qualified root class name must
                                be provided.""")

        if not TGWindowsService.log_dir:
            TGWindowsService.log_dir = '.'

        if exists(join(TGWindowsService.code_dir, "setup.py")):
            cherrypy.config.update(file="dev.cfg")
        else:
            cherrypy.config.update(file="prod.cfg")

        # Set environment to production to disable auto-reload.
        cherrypy.config.update({'global': {'server.environment': 'production'},})

        # Parse out the root class information and set it to self.root
        full_class_name = TGWindowsService.root_class
        last_mark = full_class_name.rfind('.')
        
        if (last_mark < 1) or (last_mark + 1) == len(full_class_name):
            raise ValueError("""The user-defined class name is invalid.
                                Please make sure to include a fully
                                qualified class name for the root_class
                                value (e.g. wiki20.controllers.Root).""")
        
        package_name = full_class_name[:last_mark]
        class_name = full_class_name[last_mark+1:]
        exec('from %s import %s as Root' % (package_name, class_name))
        self.root = Root
     
if __name__ == '__main__':
    # The following are the most common command-line arguments that are used
    # with this module:
    #  service.py install (Installs the service with manual startup)
    #  service.py --startup auto install (Installs the service with auto startup)    
    #  service.py start (Starts the service)
    #  service.py stop (Stops the service)
    #  service.py remove (Removes the service)
    #
    # For a full list of arguments, simply type "service.py".
    win32serviceutil.HandleCommandLine(TGWindowsService)