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

Changes between Version 17 and Version 18 of DeployAsWindowsService


Ignore:
Timestamp:
06/24/07 18:21:25 (12 years ago)
Author:
Chris Arndt
Comment:

migration notice

Legend:

Unmodified
Added
Removed
Modified
  • DeployAsWindowsService

    v17 v18  
    1 = Introduction = 
    2  
    3 This page describes the steps needed to run a TurboGears application as a Windows service.  The code below has been tested with TG v 0.9a6. 
    4  
    5 = Prerequisites = 
    6  
    7 The win32all package  
    8 - docs at http://starship.python.net/crew/mhammond/win32/ 
    9 - Download from https://sourceforge.net/projects/pywin32/ 
    10  
    11 This must be installed before the Windows service functionality can be used. 
    12  
    13 = Installation Instructions = 
    14  
    15 1. Copy the code at the end of this page and create a file called 'service.py' with its contents. 
    16   * '''Note''': this file __does not__ have to be in the same directory as the TG application code. 
    17 2. Edit service.py's "USER EDIT SECTION" where appropriate.  If service.py is located in the TG application's base directory, then the default values do not need to be changed.  A description of each variable follows: 
    18   * '''_svc_name_'''  
    19     * '''Description''': The name of the service (used in the Windows registry). 
    20     * '''Default''': The capitalized name of the directory where the service.py file is located. 
    21   * '''_svc_display_name_''' 
    22     * '''Description''': The name that will be displayed in the Windows Services management console. 
    23     * '''Default''': The capitalized name of the directory where the service.py file is located. 
    24   * '''code_dir''' 
    25     * '''Description''': The base directory of  the TG application code.  This should be the same directory where the <project-name>-start.py, dev.cfg, or prod.cfg files are located. 
    26     * '''Default''': The directory where the service.py file is located. 
    27   * '''root_class''' 
    28     * '''Description''': The fully qualified name of the main TG class.  For example, if you were enabling the [http://www.turbogears.org/preview/docs/tutorials/wiki20/ 20 Minute Wiki] tutorial as a Windows service, then the fully qualified class name would be 'wiki20.controllers.Root'.   
    29     * '''Default''': <current_dir>.controllers.Root 
    30   * '''config_module''' 
    31     * '''Description''': The name of the configuration module. 
    32     * '''Default''': <current_dir>.config 
    33   * '''log_dir''' 
    34     * '''Description''': The location for the app's stdout and stderr log files.   
    35     * '''Default''': code_dir 
    36 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: 
    37  
    381{{{ 
    39 service.py                        (Lists all available options) 
    40 service.py install                (Installs the service with a manual startup ) 
    41 service.py --startup auto install (Installs the service with auto startup)     
    42  
    43 service.py start                  (Starts the service) 
    44 service.py stop                   (Stops the service) 
    45 service.py remove                 (Removes the service) 
     2#!rst 
     3.. note:: This page has been migrated to http://docs.turbogears.org/1.0/WindowsService. 
    464}}} 
    47 4. The service should now be accessible and controllable from the Window Services management console. 
    48  
    49 = Notes = 
    50  * 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. 
    51  * 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. 
    52  * 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: 
    53  
    54 {{{ 
    55 The instance's SvcRun() method failed  
    56   File "C:\Python24\lib\site-packages\win32\lib\win32serviceutil.py", line 742, in SvcRun 
    57     self.SvcDoRun() 
    58   File "C:\Documents and Settings\<user>\Desktop\service.py", line 80, in SvcDoRun 
    59     self.tg_init() 
    60   File "C:\Documents and Settings\<user>\Desktop\service.py", line 115, in tg_init 
    61     print 'try to crash the buffer over and over again...'  
    62 exceptions.IOError: (9, 'Bad file descriptor') 
    63 }}} 
    64  
    65  * The Cherrpy site also lists a [http://docs.cherrypy.org/cherrypy22-as-windows-service similar method] of creating a Cherrypy Windows service. 
    66  
    67 = Troubleshooting = 
    68  
    69 Use the Windows Event Viewer, the stdout, and the stderr log files to locate problems with your application. 
    70  
    71 = Code = 
    72 {{{ 
    73 # File name: service.py 
    74 # 
    75 # The service module defines a single class (TGWindowsService) that contains 
    76 # the functionality for running a TurboGears application as a Windows Service. 
    77 #  
    78 # To use this class, users must do the following: 
    79 # 1. Download and install the win32all package 
    80 #    (http://starship.python.net/crew/mhammond/win32/) 
    81 # 2. Edit the "USER EDIT SECTION" with the proper information. 
    82 #    If the standard TurboGears structure is being used (e.g. the one generated by  
    83 #    quickstart), and this file is located in the base directory of the TG  
    84 #    application, then no edits are required. 
    85 # 3. Open a command prompt and navigate to the directory where this file 
    86 #    is located.  Use one of the following commands to 
    87 #    install/start/stop/remove the service: 
    88 #    > service.py install 
    89 #    > service.py start 
    90 #    > service.py stop 
    91 #    > service.py remove 
    92 #    Additionally, typing "service.py" will present the user with all of the 
    93 #    available options. 
    94 # 
    95 # Once installed, the service will be accessible through the Services 
    96 # management console just like any other Windows Service.  All service  
    97 # startup exceptions encountered by the TGWindowsService class will be  
    98 # viewable in the Windows event viewer (this is useful for debugging 
    99 # service startup errors); all application specific output or exceptions that 
    100 # are not captured by the standard TG logging mechanism should  
    101 # appear in the stdout/stderr logs. 
    102 # 
    103 # This module has been tested on Windows Server 2000, 2003, and Windows 
    104 # XP Professional. 
    105 # 
    106 # Note 1: If this file is not located in the application's base directory,  
    107 # then make sure to edit the USER EDIT SECTION with the appropriate 
    108 # values. 
    109 # 
    110 # Note 2: The cherrypy autoreload functionality will not function when cherrypy 
    111 # is run as a Windows service, so the TGWindowsService class will automatically 
    112 # disable autoreloading before starting the server. 
    113  
    114 import pkg_resources 
    115 pkg_resources.require("TurboGears") 
    116  
    117 import turbogears 
    118 import cherrypy 
    119 cherrypy.lowercase_api = True 
    120  
    121 import sys 
    122 import os 
    123 from os.path import * 
    124  
    125 import win32serviceutil 
    126 import win32service 
    127 from win32com.client import constants 
    128  
    129 class TGWindowsService(win32serviceutil.ServiceFramework): 
    130     """TurboGears Windows Service helper class. 
    131  
    132     The TGWindowsService class contains all the functionality required 
    133     for running a TurboGears application as a Windows Service.  The only 
    134     user edits required for this class are located in the following class 
    135     variables: 
    136      
    137     _svc_name_:         The name of the service (used in the Windows registry). 
    138                         DEFAULT: The capitalized name of the current directory. 
    139     _svc_display_name_: The name that will appear in the Windows Service Manager. 
    140                         DEFAULT: The capitalized name of the current directory.     
    141     code_dir:           The full path to the base directory of the user's 
    142                         TG app code (usually where <project_name>-start.py 
    143                         and the *.cfg files are located). 
    144                         DEFAULT: The directory where this file is located. 
    145     root_class:         The fully qualified Root class name 
    146                         (e.g. wiki20.controllers.Root) 
    147                         DEFAULT: <current_dir_name>.controllers.Root 
    148     config_module:      The name of the configuration module. 
    149                         DEFAULT: <current_dir_name>.config 
    150     log_dir:            The desired location of the stdout and stderr 
    151                         log files. 
    152                         DEFAULT: code_dir 
    153            
    154     For information on installing the application, please refer to the 
    155     documentation at the end of this module or navigate to the directory 
    156     where this module is located and type "service.py" from the command 
    157     prompt. 
    158     """ 
    159     current_dir = os.path.split(__file__)[0] 
    160     default_project_name = os.path.split(current_dir)[1] 
    161  
    162     # -- START USER EDIT SECTION 
    163     # -- Users must edit this section before installing the service. 
    164     _svc_name_ = '%s' % default_project_name.capitalize()           # The name of the service. 
    165     _svc_display_name_ = '%s' % default_project_name.capitalize()   # The Service Manager display name. 
    166     code_dir = current_dir                                          # The base directory of the TG app code.         
    167     root_class = '%s.controllers.Root' % default_project_name       # The fully qualified Root class name. 
    168     config_module = '%s.config' % default_project_name              # The name of the config module 
    169     log_dir = r''                                                   # The log directory for the stderr and  
    170                                                                     # stdout logs. Default = code_dir. 
    171     # -- END USER EDIT SECTION 
    172      
    173     def SvcDoRun(self): 
    174         """ Called when the Windows Service runs. """ 
    175  
    176         self.ReportServiceStatus(win32service.SERVICE_START_PENDING) 
    177         self.tg_init() 
    178         self.ReportServiceStatus(win32service.SERVICE_RUNNING) 
    179         turbogears.start_server(self.root()) 
    180      
    181     def SvcStop(self): 
    182         """Called when Windows receives a service stop request.""" 
    183          
    184         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 
    185         cherrypy.server.stop() 
    186         self.ReportServiceStatus(win32service.SERVICE_STOPPED) 
    187  
    188     def tg_init(self): 
    189         """ Checks for the required data and initializes the application. """ 
    190  
    191         if TGWindowsService.code_dir: 
    192             os.chdir(TGWindowsService.code_dir) 
    193             sys.path.append(TGWindowsService.code_dir) 
    194             # Redirect stdout and stderr to avoid buffer crashes.             
    195             sys.stdout = open(join(TGWindowsService.log_dir, 'stdout.log'),'a') 
    196             sys.stderr = open(join(TGWindowsService.log_dir, 'stderr.log'),'a') 
    197         else: 
    198             raise ValueError("""The code directory setting is missing. 
    199                                 The Windows Service will not run 
    200                                 without this setting.""") 
    201  
    202         if not TGWindowsService.root_class: 
    203             raise ValueError("""The fully qualified root class name must 
    204                                 be provided.""") 
    205  
    206         if not TGWindowsService.log_dir: 
    207             TGWindowsService.log_dir = '.' 
    208  
    209         if exists(join(dirname(__file__), "setup.py")): 
    210             turbogears.update_config(configfile="dev.cfg", 
    211                 modulename=TGWindowsService.config_module) 
    212         else: 
    213             turbogears.update_config(configfile="prod.cfg", 
    214                 modulename=TGWindowsService.config_module) 
    215  
    216         # Set environment to production to disable auto-reload. 
    217         cherrypy.config.update({'global': {'server.environment': 'production'},}) 
    218  
    219         # Parse out the root class information and set it to self.root 
    220         full_class_name = TGWindowsService.root_class 
    221         last_mark = full_class_name.rfind('.') 
    222          
    223         if (last_mark < 1) or (last_mark + 1) == len(full_class_name): 
    224             raise ValueError("""The user-defined class name is invalid. 
    225                                 Please make sure to include a fully 
    226                                 qualified class name for the root_class 
    227                                 value (e.g. wiki20.controllers.Root).""") 
    228          
    229         package_name = full_class_name[:last_mark] 
    230         class_name = full_class_name[last_mark+1:] 
    231         exec('from %s import %s as Root' % (package_name, class_name)) 
    232         self.root = Root 
    233       
    234 if __name__ == '__main__': 
    235     # The following are the most common command-line arguments that are used 
    236     # with this module: 
    237     #  service.py install (Installs the service with manual startup) 
    238     #  service.py --startup auto install (Installs the service with auto startup)     
    239     #  service.py start (Starts the service) 
    240     #  service.py stop (Stops the service) 
    241     #  service.py remove (Removes the service) 
    242     # 
    243     # For a full list of arguments, simply type "service.py". 
    244     win32serviceutil.HandleCommandLine(TGWindowsService) 
    245 }}} 
    246  
    247 If your service stops when you log off Windows, try the following modification to the code: 
    248  
    249 {{{ 
    250     ...snip... 
    251  
    252     # -- END USER EDIT SECTION 
    253     stop_event = win32event.CreateEvent(None, 0, 0, None) 
    254      
    255     def SvcDoRun(self): 
    256         """ Called when the Windows Service runs. """ 
    257  
    258         self.ReportServiceStatus(win32service.SERVICE_START_PENDING) 
    259         self.tg_init() 
    260         cherrypy.root = self.root() 
    261         self.ReportServiceStatus(win32service.SERVICE_RUNNING) 
    262         cherrypy.server.start(init_only=True) 
    263         win32event.WaitForSingleObject(TGWindowsService.stop_event, win32event.INFINITE) 
    264  
    265     ...snip... 
    266 }}}