~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/buildmaster/model/packagebuild.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-12-09 09:23:38 UTC
  • mfrom: (14333.2.13 history-model)
  • Revision ID: launchpad@pqm.canonical.com-20111209092338-se7u5l0skqzaes1v
[r=jcsackett][bug=295214, 894836,
 898200] Keep sort button ob bug listing pages in sync with the
 displayed data

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2010 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
__metaclass__ = type
24
24
    Storm,
25
25
    Unicode,
26
26
    )
 
27
import transaction
27
28
from zope.component import getUtility
28
29
from zope.interface import (
29
30
    classProvides,
31
32
    )
32
33
from zope.security.proxy import removeSecurityProxy
33
34
 
 
35
from canonical.config import config
 
36
from canonical.database.enumcol import DBEnum
 
37
from canonical.launchpad.browser.librarian import ProxiedLibraryFileAlias
 
38
from canonical.launchpad.helpers import filenameToContentType
 
39
from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
 
40
from canonical.launchpad.interfaces.lpstorm import IMasterStore
 
41
from canonical.launchpad.webapp.interfaces import (
 
42
    DEFAULT_FLAVOR,
 
43
    IStoreSelector,
 
44
    MAIN_STORE,
 
45
    )
34
46
from lp.buildmaster.enums import (
35
47
    BuildFarmJobType,
36
48
    BuildStatus,
47
59
    )
48
60
from lp.buildmaster.model.buildqueue import BuildQueue
49
61
from lp.registry.interfaces.pocket import PackagePublishingPocket
50
 
from lp.services.config import config
51
 
from lp.services.database.enumcol import DBEnum
52
 
from lp.services.database.lpstorm import IMasterStore
53
 
from lp.services.helpers import filenameToContentType
54
 
from lp.services.librarian.browser import ProxiedLibraryFileAlias
55
 
from lp.services.librarian.interfaces import ILibraryFileAliasSet
56
 
from lp.services.webapp.interfaces import (
57
 
    DEFAULT_FLAVOR,
58
 
    IStoreSelector,
59
 
    MAIN_STORE,
60
 
    )
 
62
from lp.services.database.transaction_policy import DatabaseTransactionPolicy
61
63
from lp.soyuz.adapters.archivedependencies import (
62
64
    default_component_dependency_name,
63
65
    )
177
179
    def storeBuildInfo(build, librarian, slave_status):
178
180
        """See `IPackageBuild`."""
179
181
        def got_log(lfa_id):
 
182
            dependencies = slave_status.get('dependencies')
 
183
            if dependencies is not None:
 
184
                dependencies = unicode(dependencies)
 
185
 
180
186
            # log, builder and date_finished are read-only, so we must
181
187
            # currently remove the security proxy to set them.
182
188
            naked_build = removeSecurityProxy(build)
183
 
            naked_build.log = lfa_id
184
 
            naked_build.builder = build.buildqueue_record.builder
185
 
            # XXX cprov 20060615 bug=120584: Currently buildduration includes
186
 
            # the scanner latency, it should really be asking the slave for
187
 
            # the duration spent building locally.
188
 
            naked_build.date_finished = datetime.datetime.now(pytz.UTC)
189
 
            if slave_status.get('dependencies') is not None:
190
 
                build.dependencies = unicode(slave_status.get('dependencies'))
191
 
            else:
192
 
                build.dependencies = None
 
189
 
 
190
            transaction.commit()
 
191
            with DatabaseTransactionPolicy(read_only=False):
 
192
                naked_build.log = lfa_id
 
193
                naked_build.builder = build.buildqueue_record.builder
 
194
                # XXX cprov 20060615 bug=120584: Currently buildduration
 
195
                # includes the scanner latency.  It should really be asking
 
196
                # the slave for the duration spent building locally.
 
197
                naked_build.date_finished = datetime.datetime.now(pytz.UTC)
 
198
                build.dependencies = dependencies
 
199
                transaction.commit()
193
200
 
194
201
        d = build.getLogFromSlave(build)
195
202
        return d.addCallback(got_log)
290
297
 
291
298
    def handleStatus(self, status, librarian, slave_status):
292
299
        """See `IPackageBuild`."""
 
300
        # Avoid circular imports.
293
301
        from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME
 
302
 
294
303
        logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME)
295
304
        send_notification = status in self.ALLOWED_STATUS_NOTIFICATIONS
296
305
        method = getattr(self, '_handleStatus_' + status, None)
297
306
        if method is None:
298
 
            logger.critical("Unknown BuildStatus '%s' for builder '%s'"
299
 
                            % (status, self.buildqueue_record.builder.url))
300
 
            return
 
307
            logger.critical(
 
308
                "Unknown BuildStatus '%s' for builder '%s'",
 
309
                status, self.buildqueue_record.builder.url)
 
310
            return None
 
311
 
301
312
        d = method(librarian, slave_status, logger, send_notification)
302
313
        return d
303
314
 
 
315
    def _destroy_buildqueue_record(self, unused_arg):
 
316
        """Destroy this build's `BuildQueue` record."""
 
317
        transaction.commit()
 
318
        with DatabaseTransactionPolicy(read_only=False):
 
319
            self.buildqueue_record.destroySelf()
 
320
            transaction.commit()
 
321
 
304
322
    def _release_builder_and_remove_queue_item(self):
305
323
        # Release the builder for another job.
306
324
        d = self.buildqueue_record.builder.cleanSlave()
307
325
        # Remove BuildQueue record.
308
 
        return d.addCallback(lambda x: self.buildqueue_record.destroySelf())
 
326
        return d.addCallback(self._destroy_buildqueue_record)
 
327
 
 
328
    def _notify_if_appropriate(self, appropriate=True, extra_info=None):
 
329
        """If `appropriate`, call `self.notify` in a write transaction."""
 
330
        if appropriate:
 
331
            transaction.commit()
 
332
            with DatabaseTransactionPolicy(read_only=False):
 
333
                self.notify(extra_info=extra_info)
 
334
                transaction.commit()
309
335
 
310
336
    def _handleStatus_OK(self, librarian, slave_status, logger,
311
337
                         send_notification):
321
347
            self.buildqueue_record.specific_job.build.title,
322
348
            self.buildqueue_record.builder.name))
323
349
 
324
 
        # If this is a binary package build, discard it if its source is
325
 
        # no longer published.
 
350
        # If this is a binary package build for a source that is no
 
351
        # longer published, discard it.
326
352
        if self.build_farm_job_type == BuildFarmJobType.PACKAGEBUILD:
327
353
            build = self.buildqueue_record.specific_job.build
328
354
            if not build.current_source_publication:
329
 
                build.status = BuildStatus.SUPERSEDED
 
355
                transaction.commit()
 
356
                with DatabaseTransactionPolicy(read_only=False):
 
357
                    build.status = BuildStatus.SUPERSEDED
 
358
                    transaction.commit()
330
359
                return self._release_builder_and_remove_queue_item()
331
360
 
332
 
        # Explode before collect a binary that is denied in this
333
 
        # distroseries/pocket
 
361
        # Explode rather than collect a binary that is denied in this
 
362
        # distroseries/pocket.
334
363
        if not self.archive.allowUpdatesToReleasePocket():
335
364
            assert self.distro_series.canUploadToPocket(self.pocket), (
336
365
                "%s (%s) can not be built for pocket %s: illegal status"
375
404
            # files from the slave.
376
405
            if successful_copy_from_slave:
377
406
                logger.info(
378
 
                    "Gathered %s %d completely. Moving %s to uploader queue."
379
 
                    % (self.__class__.__name__, self.id, upload_leaf))
 
407
                    "Gathered %s %d completely. "
 
408
                    "Moving %s to uploader queue.",
 
409
                    self.__class__.__name__, self.id, upload_leaf)
380
410
                target_dir = os.path.join(root, "incoming")
381
 
                self.status = BuildStatus.UPLOADING
 
411
                resulting_status = BuildStatus.UPLOADING
382
412
            else:
383
413
                logger.warning(
384
 
                    "Copy from slave for build %s was unsuccessful.", self.id)
385
 
                self.status = BuildStatus.FAILEDTOUPLOAD
386
 
                if send_notification:
387
 
                    self.notify(
388
 
                        extra_info='Copy from slave was unsuccessful.')
 
414
                    "Copy from slave for build %s was unsuccessful.",
 
415
                    self.id)
389
416
                target_dir = os.path.join(root, "failed")
 
417
                resulting_status = BuildStatus.FAILEDTOUPLOAD
 
418
 
 
419
            transaction.commit()
 
420
            with DatabaseTransactionPolicy(read_only=False):
 
421
                self.status = resulting_status
 
422
                transaction.commit()
 
423
 
 
424
            if not successful_copy_from_slave:
 
425
                self._notify_if_appropriate(
 
426
                    send_notification, "Copy from slave was unsuccessful.")
390
427
 
391
428
            if not os.path.exists(target_dir):
392
429
                os.mkdir(target_dir)
394
431
            # Release the builder for another job.
395
432
            d = self._release_builder_and_remove_queue_item()
396
433
 
397
 
            # Commit so there are no race conditions with archiveuploader
398
 
            # about self.status.
399
 
            Store.of(self).commit()
400
 
 
401
434
            # Move the directory used to grab the binaries into
402
435
            # the incoming directory so the upload processor never
403
436
            # sees half-finished uploads.
421
454
        set the job status as FAILEDTOBUILD, store available info and
422
455
        remove Buildqueue entry.
423
456
        """
424
 
        self.status = BuildStatus.FAILEDTOBUILD
 
457
        transaction.commit()
 
458
        with DatabaseTransactionPolicy(read_only=False):
 
459
            self.status = BuildStatus.FAILEDTOBUILD
 
460
            transaction.commit()
425
461
 
426
462
        def build_info_stored(ignored):
427
 
            if send_notification:
428
 
                self.notify()
 
463
            self._notify_if_appropriate(send_notification)
429
464
            d = self.buildqueue_record.builder.cleanSlave()
430
 
            return d.addCallback(
431
 
                lambda x: self.buildqueue_record.destroySelf())
 
465
            return d.addCallback(self._destroy_buildqueue_record)
432
466
 
433
467
        d = self.storeBuildInfo(self, librarian, slave_status)
434
468
        return d.addCallback(build_info_stored)
446
480
        def build_info_stored(ignored):
447
481
            logger.critical("***** %s is MANUALDEPWAIT *****"
448
482
                            % self.buildqueue_record.builder.name)
449
 
            if send_notification:
450
 
                self.notify()
 
483
            self._notify_if_appropriate(send_notification)
451
484
            d = self.buildqueue_record.builder.cleanSlave()
452
 
            return d.addCallback(
453
 
                lambda x: self.buildqueue_record.destroySelf())
 
485
            return d.addCallback(self._destroy_buildqueue_record)
454
486
 
455
487
        d = self.storeBuildInfo(self, librarian, slave_status)
456
488
        return d.addCallback(build_info_stored)
466
498
        self.status = BuildStatus.CHROOTWAIT
467
499
 
468
500
        def build_info_stored(ignored):
469
 
            logger.critical("***** %s is CHROOTWAIT *****" %
470
 
                            self.buildqueue_record.builder.name)
471
 
            if send_notification:
472
 
                self.notify()
 
501
            logger.critical(
 
502
                "***** %s is CHROOTWAIT *****",
 
503
                self.buildqueue_record.builder.name)
 
504
 
 
505
            self._notify_if_appropriate(send_notification)
473
506
            d = self.buildqueue_record.builder.cleanSlave()
474
 
            return d.addCallback(
475
 
                lambda x: self.buildqueue_record.destroySelf())
 
507
            return d.addCallback(self._destroy_buildqueue_record)
476
508
 
477
509
        d = self.storeBuildInfo(self, librarian, slave_status)
478
510
        return d.addCallback(build_info_stored)
479
511
 
 
512
    def _reset_buildqueue_record(self, ignored_arg=None):
 
513
        """Reset the `BuildQueue` record, in a write transaction."""
 
514
        transaction.commit()
 
515
        with DatabaseTransactionPolicy(read_only=False):
 
516
            self.buildqueue_record.reset()
 
517
            transaction.commit()
 
518
 
480
519
    def _handleStatus_BUILDERFAIL(self, librarian, slave_status, logger,
481
520
                                  send_notification):
482
521
        """Handle builder failures.
490
529
        self.buildqueue_record.builder.failBuilder(
491
530
            "Builder returned BUILDERFAIL when asked for its status")
492
531
 
493
 
        def build_info_stored(ignored):
494
 
            # simply reset job
495
 
            self.buildqueue_record.reset()
496
532
        d = self.storeBuildInfo(self, librarian, slave_status)
497
 
        return d.addCallback(build_info_stored)
 
533
        return d.addCallback(self._reset_buildqueue_record)
498
534
 
499
535
    def _handleStatus_GIVENBACK(self, librarian, slave_status, logger,
500
536
                                send_notification):
514
550
            # the next Paris Summit, infinity has some ideas about how
515
551
            # to use this content. For now we just ensure it's stored.
516
552
            d = self.buildqueue_record.builder.cleanSlave()
517
 
            self.buildqueue_record.reset()
 
553
            self._reset_buildqueue_record()
518
554
            return d
519
555
 
520
556
        d = self.storeBuildInfo(self, librarian, slave_status)