~bjornt/landscape-client/fetch-progress-lucid-failure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import os
import logging
import signal

from twisted.application.service import Application, Service
from twisted.application.app import startApplication

from landscape.log import rotate_logs
from landscape.reactor import TwistedReactor
from landscape.deployment import get_versioned_persist, init_logging


class LandscapeService(Service, object):
    """Utility superclass for defining Landscape services.

    This sets up the reactor and L{Persist} object.

    @cvar service_name: The lower-case name of the service. This is used to
        generate the bpickle and the Unix socket filenames.
    @ivar config: A L{Configuration} object.
    @ivar reactor: A L{TwistedReactor} object.
    @ivar persist: A L{Persist} object, if C{persist_filename} is defined.
    @ivar factory: A L{LandscapeComponentProtocolFactory}, it must be provided
        by instances of sub-classes.
    """
    reactor_factory = TwistedReactor
    persist_filename = None

    def __init__(self, config):
        self.config = config
        try:
            from landscape.lib import bpickle_dbus
        except ImportError:
            pass
        else:
            bpickle_dbus.install()
        self.reactor = self.reactor_factory()
        if self.persist_filename:
            self.persist = get_versioned_persist(self)
        if not (self.config is not None and self.config.ignore_sigusr1):
            signal.signal(signal.SIGUSR1, lambda signal, frame: rotate_logs())
        self.socket = os.path.join(self.config.sockets_path,
                                   self.service_name + ".sock")

    def startService(self):
        Service.startService(self)
        logging.info("%s started with config %s" % (
            self.service_name.capitalize(), self.config.get_config_filename()))
        self.port = self.reactor.listen_unix(self.socket, self.factory)

    def stopService(self):
        # We don't need to call port.stopListening(), because the reactor
        # shutdown sequence will do that for us.
        Service.stopService(self)
        logging.info("%s stopped with config %s" % (
            self.service_name.capitalize(), self.config.get_config_filename()))


def run_landscape_service(configuration_class, service_class, args):
    """Run a Landscape service.

    This function instantiates the specified L{LandscapeService} subclass and
    attaches the resulting service object to a Twisted C{Application}.  After
    that it starts the Twisted L{Application} and calls the
    L{TwistedReactor.run} method of the L{LandscapeService}'s reactor.

    @param configuration_class: The service-specific subclass of
        L{Configuration} used to parse C{args} and build the C{service_class}
        object.
    @param service_class: The L{LandscapeService} subclass to create and start.
    @param args: Command line arguments used to initialize the configuration.
    """
    # Let's consider adding this:
    # from twisted.python.log import (
    #     startLoggingWithObserver, PythonLoggingObserver)
    # startLoggingWithObserver(PythonLoggingObserver().emit, setStdout=False)

    configuration = configuration_class()
    configuration.load(args)
    init_logging(configuration, service_class.service_name)
    application = Application("landscape-%s" % (service_class.service_name,))
    service = service_class(configuration)
    service.setServiceParent(application)

    if configuration.clones > 0:

        # Create clones here because TwistedReactor.__init__ would otherwise
        # cancel all scheduled delayed calls
        clones = []
        for i in range(configuration.clones):
            clone_config = configuration.clone()
            clone_config.computer_title += " Clone %d" % i
            clone_config.master_data_path = configuration.data_path
            clone_config.data_path += "-clone-%d" % i
            clone_config.log_dir += "-clone-%d" % i
            clone_config.is_clone = True
            clones.append(service_class(clone_config))

        configuration.is_clone = False

        def start_clones():
            # Spawn instances over 25-30 minutes.
            delay = configuration.start_clones_over / configuration.clones

            for i, clone in enumerate(clones):

                def start(clone):
                    clone.setServiceParent(application)
                    clone.reactor.fire("run")

                service.reactor.call_later(delay * (i + 1), start, clone=clone)

        service.reactor.call_when_running(start_clones)

    startApplication(application, False)
    if configuration.ignore_sigint:
        signal.signal(signal.SIGINT, signal.SIG_IGN)

    service.reactor.run()