#!/usr/bin/env python
# -*- coding: utf-8 -*-
# $Id: backendctl 1634 2013-04-12 15:36:36Z amelung $
#
# Copyright (c) 2007-2009 Otto-von-Guericke-Universität, Magdeburg
#
# This file is part of ECSpooler.
#
"""
This module contains functions to start/stop a backend or get status
information from a backend. __main__ reads the command lines arguments and
processes them. A configuration file must be avaiable for each backend
with entries about spooler server's host and port as well as base port
for the backend. If a port is already in use, we will try another one.
"""
import os, sys, time, socket, xmlrpclib
import getopt
from getpass import getpass
# add parent directory to the system path
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), os.pardir))
# local imports
from lib.util import settings
from lib.util.servicecontrol import ServiceControl
from backends import *
# TODO: should be in config.py
MAX_TRIALS = 15
class BackendControl(ServiceControl):
"""
"""
@staticmethod
def usage():
"""
"""
print """
Usage: backendctl [OPTION ...] BACKEND {start|stop|restart|status}
OPTIONs are:
-P port Port to bind/connect backend to, i.e. where to listen for
new request. Default: First free port in the range
of 5060 .. 5074.
-S host Hostname or IP of the machine on which ECSpooler runs.
Needed for registering a backend. Default: localhost
-B port Port on which ECSpooler runs. Needed for registering a
backend. Default: 5050
-u user Name of the user required for authentication when starting
and registering to ECSpooler
-p password Password of the user required for authentication when
starting and registering to ECSpooler
BACKEND Name of the backend to start|stop|restart|status.
"""
def __init__(self, name, spooler_host, spooler_port, backend_port, username=None, password=None):
"""
"""
ServiceControl.__init__(self, name, backend_port, username=username, password=password)
self.spooler_host = spooler_host
self.spooler_port = spooler_port
def start(self):
"""
@see: ServiceControl.start()
"""
# Run backend as nobody
try:
os.setuid(settings.NOBODY_UID)
except os.error, e:
print >> sys.stderr, "Cannot change uid:", e
try:
exec 'from backends.%s import %s' % (self.name.lower(), self.name)
exec 'id = %s.%s.id' % (self.name, self.name,)
except NameError, ne:
print >> sys.stderr, "No such backend: '%s'" % self.name
raise ne
except AttributeError, ae:
print >> sys.stderr, "No such backend: '%s'. Did you mean '%s'?" \
% (self.name, self.name.capitalize(),)
raise ae
exec 'version = %s.%s.version' % (self.name, self.name,)
(backend, p)= self.__get_backend_instance()
if backend:
print >> sys.stdout, "Starting %s backend at %s" % (self.name, time.asctime())
print >> sys.stdout, " Hostname: %s" % self.host
print >> sys.stdout, " Port: %i" % p
print >> sys.stdout, "Registering to ECSpooler at '%s:%s'..." % (self.spooler_host, self.spooler_port)
try:
if sys.platform in ['unixware7']:
cpid = os.fork1()
else:
cpid = os.fork()
except AttributeError:
print >> sys.stdout, 'os.fork not defined - skipping.'
cpid = 0
if cpid == 0:
# child process
# blocks here 'til shutdown
err_msg = backend.start()
if err_msg:
print >> sys.stderr, err_msg
else:
# parent process
time.sleep(1)
print >> sys.stdout, 'pid=%d' % cpid
else:
#print 'Backend start failed. See log for more information.'
print >> sys.stderr, 'Backend start failed. See log for more information.'
def __get_backend_instance(self):
"""
Returns an instance of the backend class if one could be created.
"""
# put together the backend module and class name using backend_id
moduleName = '%s.%s' % (self.name, self.name,)
for port in range(self.port, self.port + MAX_TRIALS, 1):
# get a instance, e.g., Python-Backend
# FIXME: use spooler instead of spooler
instance_create_stmt = \
"backend = %s({ \
'host': '%s', \
'port': %d, \
'spooler': 'http://%s:%d', \
'auth': %s}, %s.__file__)" \
% (moduleName, self.host, port, \
self.spooler_host, self.spooler_port, \
repr(self._get_auth()), self.name)
retval = self.__try_get_backend_instance(instance_create_stmt)
if retval:
return (retval, port)
else:
time.sleep(0.1)
return (None, 0)
def __try_get_backend_instance(self, instance_create_stmt):
"""
Executes the import statement for the backend class, creates an
instance and returns this instance.
An exception is thrown if the port is already in use. In this case we
will return None.
@param: instance_create_stmt Statement which will be executed to create
a new instance of the backend class.
@return: The backend instance or None if the instance couldn't be created.
"""
try:
backend = None
#log.debug("Trying to create instance of backend '%s'" % self.name)
#log.debug(instance_create_stmt)
exec 'from backends.%s import %s' % (self.name.lower(), self.name)
exec(instance_create_stmt)
return backend
# FIXME: This doesn't work on Windows -> no socket.error is thrown if
# port is already in use!
except socket.error:
return None
def _process_stop(self):
"""
@see ServiceControl.stop
@see ServiceControl._process_stop
"""
backend = xmlrpclib.ServerProxy("http://%s:%d" % (self.host, self.port))
result = backend.shutdown(self._get_auth(), self.name)
print >> sys.stdout, result
def _process_status(self):
"""
@see ServiceControl.status
@see ServiceControl._process_status
"""
server = xmlrpclib.ServerProxy("http://%s:%d" % (self.host, self.port))
return server.getStatus(self._get_auth(), self.name)
# -----------------------------------------------------------------------------
def main():
"""
"""
try:
opts, args = getopt.getopt(sys.argv[1:], "S:B:P:u:p:h",
["spoolerhost=", "spoolerport=",
"backendport=", "user=", "password=",
"help"])
except getopt.GetoptError:
# print help information and exit:
BackendControl.usage()
sys.exit(2)
if len(args) != 2:
BackendControl.usage()
sys.exit(2)
else:
spooler_host = 'localhost'
spooler_port = 5050
backend_port = 5060
user = None
password = None
for o, a in opts:
if o in ("-h", "--help"):
BackendControl.usage()
sys.exit()
if o in ("-S", "--spoolerhost"):
spooler_host = a
if o in ("-B", "--spoolerport"):
try:
spooler_port = int(a)
except ValueError:
BackendControl.usage()
sys.exit(2)
if o in ("-u", "--user"):
user = a
if o in ("-p", "--password"):
password = a
if o in ("-P", "--backendport"):
try:
backend_port = int(a)
except ValueError:
BackendControl.usage()
sys.exit(2)
backend_id, cmd = args
settings.init_logging(backend_id.lower())
if not user:
user = raw_input("Username for ECSpooler: ")
if not password:
password = getpass("Password for ECSpooler: ")
if not user or not password:
print "%s requires username and password." % cmd
BackendControl.usage()
sys.exit(2)
backendctl = BackendControl(backend_id, spooler_host, spooler_port, backend_port, user, password)
backendctl.process(cmd)
# -- Main ----------------------------------------------------------------------
if __name__ == "__main__":
main()