#! /usr/bin/env python2.7 # # Start-stop script for a django apps # """ (c) 2009-2012 Jens Kasten """ import os import sys import signal from subprocess import Popen, PIPE import argparse from time import sleep # avaible option for runfcgi # for full option type python manage.py runfcgi help # to modify the folder where django exist is the variable pagefolder class DjangoInit(object): def __init__(self, args): base_path = os.getcwd() if not args.pidfile: self.pidfile = os.path.join(base_path, "pid" ,"django.pid") else: self.pidfile = args.pidfile if not args.errlog: self.errlog = os.path.join(base_path, "log", "django_err.log") else: self.errlog = args.errlog self.host = args.host self.port = str(args.port) if args.socket == "true": self.socket = os.path.join(base_path, "run", "django.socket") elif args.socket: self.socket = args.socket else: self.socket = False self.maxrequest = str(args.maxrequest) self.minspare = str(args.minspare) self.maxspare = str(args.maxspare) self.maxchildren = str(args.maxchildren) self.method = args.method self.daemonize = args.daemonize self.umask = args.umask if not args.pagefolder: self.pagefolder = os.path.join(base_path, args.document_root) else: self.pagefolder = args.pagefolder if args.environ: self.environ = args.environ else: self.environ = {} self.environ["PATH"] = "/bin:/usr/bin:/usr/local/bin" self.python = args.python self.manage = os.path.join(self.pagefolder, "manage.py") self._command_to_start = [] self.pid = None self._check_dirs(base_path) self.build_command() def _check_dirs(self, base_path): if self.socket: if not os.path.isdir(os.path.join(base_path, "run")): os.mkdir(os.path.join(base_path, "run")) if not os.path.isdir(os.path.join(base_path, "log")): os.mkdir(os.path.join(base_path, "log")) if not os.path.isdir(os.path.join(base_path, "pid")): os.mkdir(os.path.join(base_path, "pid")) def build_command(self): """Build a list for processing through subprocess.""" self.cmd_to_start = [self.python, self.manage, 'runfcgi', 'maxrequest=%s' % self.maxrequest, 'minspare=%s' % self.minspare, 'maxspare=%s' % self.maxspare, 'maxchildren=%s' % self.maxchildren, 'daemonize=%s' % self.daemonize, 'errlog=%s' % self.errlog, 'method=%s' % self.method, 'pidfile=%s' % self.pidfile] if self.socket: self.cmd_to_start.append("socket=%s" % self.socket) else: self.cmd_to_start.append("host=%s" % self.host) self.cmd_to_start.append("port=%s" % self.port) if self.daemonize: self.cmd_to_start.append("umask=%s" % self.umask) def is_running(self): """Check in two ways if django is running. Fist look for pidfile. Second find through name the pid. """ if os.path.isfile(self.pidfile) and os.access(self.pidfile, os.R_OK): with open(self.pidfile) as fd: self.pid = fd.readline().strip() if self.pid == '': self.pid = 0 else: process1 = Popen(["ps", "ax"], stdout=PIPE, stderr=PIPE) process2 = Popen(["grep", self.manage], stdin=process1.stdout, stdout=PIPE, stderr=PIPE) process1.stdout.flush() result = process2.communicate() if len(result[1]) > 0: print result[1] return False elif len(result[0]) > 0: # extract the pid from ps aux command, usually is on first place self.pid = int(result[0].split("\n")[0].lstrip().split(" ")[0]) else: return False try: if self.pid == 0: return False # test if process running to a given pid signale.SIG_DFL means 0 os.kill(self.pid, signal.SIG_DFL) return True except OSError: if os.path.isfile(self.pidfile) and os.access(self.pidfile, os.R_OK): print "Found pidfile but not a running procces." print "Delete stale pidfile '%s'." % self.pidfile os.remove(self.pidfile) return False def start(self): """Starting django apps.""" if self.is_running(): print "App is already running." return try: print "Starting django ..." process = Popen(self.cmd_to_start, env=self.environ, stdout=PIPE, stderr=PIPE) result = process.communicate() if len(result[1]) > 0: print result[1] else: print "Done." except OSError, error_msg: print str(error_msg) def stop(self): """Stopping django apps.""" if not self.is_running(): print "App is not running." return try: print "Stopping django ..." os.kill(self.pid, signal.SIGTERM) sleep(0.4) # cleanning if os.path.isfile(self.pidfile) and os.access(self.pidfile, os.W_OK): os.remove(self.pidfile) print "Done." except OSError, error_msg: print str(error_msg) def restart(self): self.stop() return self.start() def status(self): if self.is_running(): print "App is running." else: print "App is not running." def show(self): print " ".join(self.cmd_to_start) def main(): actions = ["start", "stop", "status", "restart", "show"] parser = argparse.ArgumentParser() parser.add_argument("action", choices=actions, help="Performe the given action.""") parser.add_argument("--host", type=str, default="localhost", help="The hostname to listen on (default is localhost).") parser.add_argument("--port", type=int, default="8080", help="The port to listen (default is port 8080).") parser.add_argument("--socket", help="UNIX socket to listen on. Or set 'true' then is using default " "socket path.") parser.add_argument("--pidfile", type=str, help="The absolute path for the pidfile. Default is storing in the" "current work directory and in subfolder pid.") parser.add_argument("--errlog", type=str, help="The absolute path for the errlog. Default is storing in the" "current work directroy and in subfolder log.") parser.add_argument("--maxrequest", type=int, default=2, help="Number of requests a child handles before it is" "killed and a new child is forked (0 = no limit) default is 2") parser.add_argument("--maxspare", type=int, default=1, help="man number of spare processes / threads") parser.add_argument("--minspare", type=int, default=1, help="min number of spare processes / threads default is 1") parser.add_argument("--maxchildren", type=int, default=2, help="hard limit number of processes / threads default is 2") parser.add_argument("--daemonize", type=bool, default=True, help="whether to detach from terminal. Default is True.") parser.add_argument("--method", type=str, choices=["prefork", "threaded"], default="threaded", help="prefork or threaded (default threaded).") parser.add_argument("--protocol", type=str, default="fcgi", choices=["fcgi", "scgi", "ajp"], help="Availables protocols, fcgi, scgi, ajp, ... (default fcgi)") parser.add_argument("--umask", type=str, default="022", help="umask to use when daemonizing, in octal notation (default 022).") parser.add_argument("--workdir", type=str, help="change to this directory when daemonizing.") parser.add_argument("--pagefolder", type=str, help="Folder where the manage.py is placed. So this script has not to" " placed into the Document Root") parser.add_argument("--environ", type=str, help="Set environ variables. Otherwise only path is used when " "starting the app. Example: \"PATH=/bin HOME=/home/my\"") parser.add_argument("--python", type=str, default="python", help="Set other then default python, usefull when using virtualenv. " "Example ./django_init.py --pyhton ~/my_virt/bin/python") parser.add_argument("-d", "--document-root", type=str, default="~/htdocs", help="Set document root.") args = parser.parse_args() # build environ dict if args.environ: environ = {} for i in args.environ.split(" "): temp = i.split("=", 1) if len(temp) == 1: parser.error("Missing in --environ '=', must be \"key=value key1=value1\"") environ[temp[0]] = temp[1] args.environ = environ d = DjangoInit(args) getattr(d, args.action)() if __name__ == "__main__": main()