4
from twisted.application.service import Application, Service
5
from twisted.application.app import startApplication
7
from landscape.log import rotate_logs
8
from landscape.reactor import LandscapeReactor
9
from landscape.deployment import get_versioned_persist, init_logging
12
class LandscapeService(Service, object):
13
"""Utility superclass for defining Landscape services.
15
This sets up the reactor and L{Persist} object.
17
@cvar service_name: The lower-case name of the service. This is used to
18
generate the bpickle and the Unix socket filenames.
19
@ivar config: A L{Configuration} object.
20
@ivar reactor: A L{LandscapeReactor} object.
21
@ivar persist: A L{Persist} object, if C{persist_filename} is defined.
22
@ivar factory: A L{LandscapeComponentProtocolFactory}, it must be provided
23
by instances of sub-classes.
25
reactor_factory = LandscapeReactor
26
persist_filename = None
28
def __init__(self, config):
30
self.reactor = self.reactor_factory()
31
if self.persist_filename:
32
self.persist = get_versioned_persist(self)
33
if not (self.config is not None and self.config.ignore_sigusr1):
34
from twisted.internet import reactor
37
lambda signal, frame: reactor.callFromThread(rotate_logs))
39
def startService(self):
40
Service.startService(self)
41
logging.info("%s started with config %s" % (
42
self.service_name.capitalize(), self.config.get_config_filename()))
44
def stopService(self):
45
# We don't need to call port.stopListening(), because the reactor
46
# shutdown sequence will do that for us.
47
Service.stopService(self)
48
logging.info("%s stopped with config %s" % (
49
self.service_name.capitalize(), self.config.get_config_filename()))
52
def run_landscape_service(configuration_class, service_class, args):
53
"""Run a Landscape service.
55
This function instantiates the specified L{LandscapeService} subclass and
56
attaches the resulting service object to a Twisted C{Application}. After
57
that it starts the Twisted L{Application} and calls the
58
L{LandscapeReactor.run} method of the L{LandscapeService}'s reactor.
60
@param configuration_class: The service-specific subclass of
61
L{Configuration} used to parse C{args} and build the C{service_class}
63
@param service_class: The L{LandscapeService} subclass to create and start.
64
@param args: Command line arguments used to initialize the configuration.
66
# Let's consider adding this:
67
# from twisted.python.log import (
68
# startLoggingWithObserver, PythonLoggingObserver)
69
# startLoggingWithObserver(PythonLoggingObserver().emit, setStdout=False)
71
configuration = configuration_class()
72
configuration.load(args)
73
init_logging(configuration, service_class.service_name)
74
application = Application("landscape-%s" % (service_class.service_name,))
75
service = service_class(configuration)
76
service.setServiceParent(application)
78
if configuration.clones > 0:
80
# Increase the timeout of AMP's MethodCalls
81
# XXX: we should find a better way to expose this knot, and
82
# not set it globally on the class
83
from landscape.lib.amp import MethodCallSender
84
MethodCallSender.timeout = 300
86
# Create clones here because LandscapeReactor.__init__ would otherwise
87
# cancel all scheduled delayed calls
89
for i in range(configuration.clones):
90
clone_config = configuration.clone()
91
clone_config.computer_title += " Clone %d" % i
92
clone_config.master_data_path = configuration.data_path
93
clone_config.data_path += "-clone-%d" % i
94
clone_config.log_dir += "-clone-%d" % i
95
clone_config.is_clone = True
96
clones.append(service_class(clone_config))
98
configuration.is_clone = False
101
# Spawn instances over the given time window
102
start_clones_over = float(configuration.start_clones_over)
103
delay = start_clones_over / configuration.clones
105
for i, clone in enumerate(clones):
108
clone.setServiceParent(application)
109
clone.reactor.fire("run")
111
service.reactor.call_later(delay * (i + 1), start, clone=clone)
113
service.reactor.call_when_running(start_clones)
115
startApplication(application, False)
116
if configuration.ignore_sigint:
117
signal.signal(signal.SIGINT, signal.SIG_IGN)
119
service.reactor.run()