~free.ekanayaka/landscape-client/lucid-1.4.4-0ubuntu0.10.04

« back to all changes in this revision

Viewing changes to landscape/package/taskhandler.py

  • Committer: Bazaar Package Importer
  • Author(s): Free Ekanayaka
  • Date: 2009-12-16 10:50:05 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20091216105005-svplwdorkgz6vja7
Tags: 1.4.0-0ubuntu0.10.04.0
* New upstream release with several bug fixes:
  - Fix landscape daemons fail to start when too many groups are
    available (LP: #456124)
  - Fix landscape programs wake up far too much. (LP: #340843)
  - Fix Package manager fails with 'no such table: task' (LP #465846)
  - Fix test suite leaving temporary files around (LP #476418)
  - Fix the 1hr long wait for user data to be uploaded following a
    resynchronisation (LP #369000)

* Add support for Ubuntu release upgrades:
  - Add helper function to fetch many files at once (LP: #450629)
  - Handle release-upgrade messages in the packagemanager
    plugin (LP: #455217)
  - Add a release-upgrader task handler (LP: #462543)
  - Support upgrade-tool environment variables (LP: #463321)

* Add initial support for Smart package locking:
  - Detect and report changes about Smart package locks (#488108)

* Packaging fixes:
  - Turn unnecessary Pre-Depends on python-gobject into a regular Depends
  - If it's empty, remove /etc/landscape upon purge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import os
 
2
import re
2
3
import logging
3
4
 
4
5
from twisted.internet.defer import Deferred, succeed
5
6
 
6
 
from landscape.lib.dbus_util import get_bus
7
7
from landscape.lib.lock import lock_path, LockError
8
8
from landscape.lib.log import log_failure
9
 
from landscape.lib.command import run_command, CommandError
 
9
from landscape.lib.lsb_release import LSB_RELEASE_FILENAME, parse_lsb_release
10
10
from landscape.deployment import Configuration, init_logging
11
11
from landscape.package.store import PackageStore, InvalidHashIdDb
12
12
from landscape.broker.remote import RemoteBroker
13
13
 
14
14
 
 
15
class PackageTaskHandlerConfiguration(Configuration):
 
16
    """Specialized configuration for L{PackageTaskHandler}s."""
 
17
 
 
18
    @property
 
19
    def package_directory(self):
 
20
        """Get the path to the package directory."""
 
21
        return os.path.join(self.data_path, "package")
 
22
 
 
23
    @property
 
24
    def store_filename(self):
 
25
        """Get the path to the SQlite file for the L{PackageStore}."""
 
26
        return os.path.join(self.package_directory, "database")
 
27
 
 
28
    @property
 
29
    def hash_id_directory(self):
 
30
        """Get the path to the directory holding the stock hash-id stores."""
 
31
        return os.path.join(self.package_directory, "hash-id")
 
32
 
 
33
 
 
34
class LazyRemoteBroker(object):
 
35
    """Wrapper class around L{RemoteBroker} providing lazy initialization.
 
36
 
 
37
    This class is a wrapper around a regular L{RemoteBroker}. It creates
 
38
    the remote broker object only when one of its attributes is first accessed.
 
39
 
 
40
    @note: This behaviour is needed in particular by the ReleaseUpgrader and
 
41
    the PackageChanger, because if the they connect early and DBus gets
 
42
    upgraded while they run, they might crash or not be able to communicate
 
43
    with the broker due to bugs in DBus.
 
44
    """
 
45
 
 
46
    def __init__(self, bus):
 
47
        self._remote = None
 
48
        self._bus = bus
 
49
 
 
50
    def __getattr__(self, name):
 
51
        if not self._remote:
 
52
            from landscape.lib.dbus_util import get_bus
 
53
            self._remote = RemoteBroker(get_bus(self._bus))
 
54
        return getattr(self._remote, name)
 
55
 
 
56
 
15
57
class PackageTaskHandler(object):
16
58
 
 
59
    config_factory = PackageTaskHandlerConfiguration
 
60
 
17
61
    queue_name = "default"
 
62
    lsb_release_filename = LSB_RELEASE_FILENAME
18
63
 
19
64
    def __init__(self, package_store, package_facade, remote_broker, config):
20
65
        self._store = package_store
22
67
        self._broker = remote_broker
23
68
        self._config = config
24
69
        self._channels_reloaded = False
25
 
        self._server_uuid = None
26
70
 
27
71
    def ensure_channels_reloaded(self):
28
72
        if not self._channels_reloaded:
33
77
        return self.handle_tasks()
34
78
 
35
79
    def handle_tasks(self):
36
 
        deferred = Deferred()
37
 
        self._handle_next_task(None, deferred)
38
 
        return deferred
 
80
        return self._handle_next_task(None)
39
81
 
40
 
    def _handle_next_task(self, result, deferred, last_task=None):
 
82
    def _handle_next_task(self, result, last_task=None):
41
83
        if last_task is not None:
42
84
            # Last task succeeded.  We can safely kill it now.
43
85
            last_task.remove()
47
89
        if task:
48
90
            # We have another task.  Let's handle it.
49
91
            result = self.handle_task(task)
50
 
            result.addCallback(self._handle_next_task, deferred, task)
51
 
            result.addErrback(deferred.errback)
 
92
            result.addCallback(self._handle_next_task, task)
 
93
            return result
52
94
 
53
95
        else:
54
96
            # No more tasks!  We're done!
55
 
            deferred.callback(None)
 
97
            return succeed(None)
56
98
 
57
99
    def handle_task(self, task):
58
100
        return succeed(None)
104
146
                return None
105
147
 
106
148
            try:
107
 
                # XXX replace this with a L{SmartFacade} method
108
 
                codename = run_command("lsb_release -cs")
109
 
            except CommandError, error:
 
149
                lsb_release_info = parse_lsb_release(self.lsb_release_filename)
 
150
            except IOError, error:
110
151
                logging.warning(warning % str(error))
111
152
                return None
 
153
            try:
 
154
                codename = lsb_release_info["code-name"]
 
155
            except KeyError:
 
156
                logging.warning(warning % "missing code-name key in %s" %
 
157
                                self.lsb_release_filename)
 
158
                return None
112
159
 
113
160
            arch = self._facade.get_arch()
114
161
            if arch is None:
118
165
                logging.warning(warning % "unknown dpkg architecture")
119
166
                return None
120
167
 
121
 
            package_directory = os.path.join(self._config.data_path, "package")
122
 
            hash_id_db_directory = os.path.join(package_directory, "hash-id")
123
 
 
124
 
            return os.path.join(hash_id_db_directory,
125
 
                                "%s_%s_%s" % (server_uuid,
126
 
                                              codename,
127
 
                                              arch))
 
168
            return os.path.join(self._config.hash_id_directory,
 
169
                                "%s_%s_%s" % (server_uuid, codename, arch))
128
170
 
129
171
        result = self._broker.get_server_uuid()
130
172
        result.addCallback(got_server_uuid)
132
174
 
133
175
 
134
176
def run_task_handler(cls, args, reactor=None):
135
 
    from twisted.internet.glib2reactor import install
 
177
    from landscape.reactor import install
136
178
    install()
137
179
 
138
180
    # please only pass reactor when you have totally mangled everything with
140
182
    if reactor is None:
141
183
        from twisted.internet import reactor
142
184
 
143
 
    program_name = cls.queue_name
144
 
 
145
 
    config = Configuration()
 
185
    config = cls.config_factory()
146
186
    config.load(args)
147
187
 
148
 
    package_directory = os.path.join(config.data_path, "package")
149
 
    hash_id_directory = os.path.join(package_directory, "hash-id")
150
 
    for directory in [package_directory, hash_id_directory]:
 
188
    for directory in [config.package_directory, config.hash_id_directory]:
151
189
        if not os.path.isdir(directory):
152
190
            os.mkdir(directory)
153
191
 
154
 
    lock_filename = os.path.join(package_directory, program_name + ".lock")
 
192
    program_name = cls.queue_name
 
193
    lock_filename = os.path.join(config.package_directory,
 
194
                                 program_name + ".lock")
155
195
    try:
156
196
        lock_path(lock_filename)
157
197
    except LockError:
161
201
                         % program_name)
162
202
 
163
203
 
164
 
    init_logging(config, "package-" + program_name)
165
 
 
166
 
    store_filename = os.path.join(package_directory, "database")
 
204
    words = re.findall("[A-Z][a-z]+", cls.__name__)
 
205
    init_logging(config, "-".join(word.lower() for word in words))
167
206
 
168
207
    # Setup our umask for Smart to use, this needs to setup file permissions to
169
208
    # 0644 so...
173
212
    # import Smart unless we need to.
174
213
    from landscape.package.facade import SmartFacade
175
214
 
176
 
    package_store = PackageStore(store_filename)
 
215
    package_store = PackageStore(config.store_filename)
177
216
    package_facade = SmartFacade()
178
 
    remote = RemoteBroker(get_bus(config.bus))
 
217
    remote = LazyRemoteBroker(config.bus)
179
218
 
180
219
    handler = cls(package_store, package_facade, remote, config)
181
220
 
182
221
    def got_err(failure):
183
222
        log_failure(failure)
184
223
 
185
 
    result = handler.run()
 
224
    result = Deferred()
 
225
    result.addCallback(lambda ignored: handler.run())
186
226
    result.addErrback(got_err)
187
227
    result.addBoth(lambda ignored: reactor.callLater(0, reactor.stop))
 
228
    reactor.callWhenRunning(lambda: result.callback(None))
188
229
 
189
230
    reactor.run()
190
231