Skip to content

Instantly share code, notes, and snippets.

@nopper
Created June 20, 2010 00:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nopper/445443 to your computer and use it in GitHub Desktop.
Save nopper/445443 to your computer and use it in GitHub Desktop.
Simple script to manage fcgi apps (useful for django applications)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2010 Francesco Piccinno
#
# Author: Francesco Piccinno <stack.box@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Simple script to manage fcgi apps (django apps)
import os
import os.path
import sys
import logging
import logging.handlers
from subprocess import call
log = logging.getLogger('spawner')
log.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
'/home/django/wsgi-apps.log', maxBytes=1024**2, backupCount=3)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
log.addHandler(handler)
PIDFILE_NAME = '/home/django/pids/'
LOGGING_DIR = '/home/django/logs/'
VIRTUALENV_PATH = '/home/django/pyve/'
DEFAULT_ENV = 'django'
APPLICATIONS_PATH = '/home/django/releases/'
KILL_BIN = '/bin/kill'
apps = (
{'app': 'test',
'vhost': 'your.vhost-here.org',
'virtualenv': 'django',
'release': 'current',
'port': 3034,
'enabled': False,
},
)
class Spawner(object):
def __init__(self, apps):
self.apps = apps
def enabled_apps(self):
return filter(
lambda x: x.get('enabled', False) and True or False,
self.apps
)
def get_app(self, name):
for app in self.apps:
if app['app'] == name:
return app
def croncheck(self):
log.info('Checking health of enabled apps')
for app in self.enabled_apps():
self.croncheck_app(app)
def croncheck_app(self, app):
pid = self.get_app_pid(app)
if pid == 0 or not self.is_running(pid):
log.info('%s app is not running. Restarting..' % app['app'])
try:
self.start_app(app)
except Exception, exc:
log.debug("Error in start_app(%s) -> %s" % \
(str(app), str(exc)))
def list(self):
for app in self.enabled_apps():
self.list_app(app)
print >>sys.stderr
def list_app(self, app):
print >>sys.stderr, "{0:20} {1:20}".format('Key', 'Value')
print >>sys.stderr, "="*20, "=" * 20
for k, v in sorted(app.items()):
print "{0:20} {1:20}".format(k, v)
def status(self):
print >>sys.stderr, "{0:20} {1:20}".format('Application', 'Status')
print >>sys.stderr, "="*20, "=" * 20
for app in self.enabled_apps():
self.status_app(app)
def status_app(self, app):
pid = self.get_app_pid(app)
print >>sys.stderr, "{0:20} {1:20}".format(
app['app'],
(pid != 0) and \
'RUNNING (%d)' % pid or \
'NOT RUNNING'
)
def get_app_pid(self, app):
try:
pidfile = open(os.path.join(
PIDFILE_NAME,
app.get('app') + '.pid',
), "r")
pid = pidfile.read().strip()
pidfile.close()
return int(pid)
except ValueError:
return 0
except IOError:
return 0
def stop(self):
for app in self.enabled_apps():
try:
self.stop_app(app)
except Exception, exc:
log.debug("Error in stop_app(%s) -> %s" % \
(str(app), str(exc)))
def is_running(self, pid):
execstr = '%s -0 %d' % (KILL_BIN, pid)
retcode = call(execstr, shell=True)
if retcode == 0:
return True
return False
def stop_app(self, app):
pid = self.get_app_pid(app)
if pid == 0:
log.warning('Cannot stop %s app. It is not running' % app['app'])
return False
if self.is_running(pid):
execstr = '%s -9 %d' % (KILL_BIN, pid)
retcode = call(execstr, shell=True)
if retcode == 0:
os.unlink(os.path.join(
PIDFILE_NAME,
app.get('app') + ".pid",
))
return True
else:
log.error('Killing the %s app (PID %d): failed.' % \
(app['app'], pid))
return False
else:
log.warning('Cannot stop %s app (PID %d): ' \
'Operation not permitted or ' \
'application is not running' % \
(app['app'], pid))
return False
def start(self):
for app in self.enabled_apps():
try:
self.start_app(app)
except Exception, exc:
log.debug("Error in start_app(%s) -> %s" % \
(str(app), str(exc)))
def start_app(self, app):
log.info('Spawning %s' % app['app'])
proj_dir = os.path.join(
APPLICATIONS_PATH,
app.get('vhost'),
app.get('release', 'current'),
app.get('app'),
)
env = app.get('virtualenv', DEFAULT_ENV)
pybin = os.path.join(VIRTUALENV_PATH, env, 'bin', 'python')
socket = os.path.join(SOCKET_DIR, app['app'] + '.socket')
pidfile = os.path.join(PIDFILE_NAME, app['app'] + '.pid')
errlog = os.path.join(LOGGING_DIR, app['app'] + '-errors.log')
outlog = os.path.join(LOGGING_DIR, app['app'] + '-output.log')
#'-v 0 outlog=%(outlog)s errlog=%(errlog)s ' \
execstr = '%(python)s manage.py runfcgi host=127.0.0.1 port=%(port)s ' \
'method=%(method)s daemonize=true maxrequests=%(maxrequests)s ' \
'minspare=%(minspare)s maxspare=%(maxspare)s ' \
'protocol=scgi pidfile=%(pidfile)s' % ({ \
'port': app['port'],
'python': pybin,
'socket': socket,
'outlog': outlog,
'errlog': errlog,
'method': app.get('method', 'prefork'),
'maxrequests': app.get('maxrequests', 20),
'minspare': app.get('maxspare', 2),
'maxspare': app.get('maxspare', 4),
'pidfile': pidfile})
log.debug('Switching to "%s"' % proj_dir)
os.chdir(proj_dir)
log.debug('Executing "%s"' % execstr)
retcode = call(execstr, shell=True)
if retcode != 0:
log.error('Error while spawning %s (Return code: %d') % \
(app.get('app'), retcode)
return False
return True
def restart(self):
self.stop()
self.start()
def restart_app(self, app):
log.info('Restarting %s app' % app['app'])
try:
self.stop_app(app)
except Exception, exc:
log.debug("Error in stop_app(%s) -> %s" % \
(str(app), str(exc)))
try:
self.start_app(app)
except Exception, exc:
log.debug("Error in start_app(%s) -> %s" % \
(str(app), str(exc)))
if len(sys.argv) < 2 or len(sys.argv) > 3:
print("Usage: %s <start>|<stop>|<restart>|<list>|<croncheck> [app]" % sys.argv[0])
sys.exit(-1)
spawn = Spawner(apps)
if sys.argv[1] in ('start', 'stop', 'restart', 'status', 'croncheck', 'list'):
app = None
if len(sys.argv) == 3:
app = spawn.get_app(sys.argv[2])
if app:
getattr(spawn, sys.argv[1] + '_app')(app)
else:
getattr(spawn, sys.argv[1])()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment