32
33
from zope.security.proxy import removeSecurityProxy
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 (
34
46
from lp.buildmaster.enums import (
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 (
62
from lp.services.database.transaction_policy import DatabaseTransactionPolicy
61
63
from lp.soyuz.adapters.archivedependencies import (
62
64
default_component_dependency_name,
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)
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'))
192
build.dependencies = None
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
194
201
d = build.getLogFromSlave(build)
195
202
return d.addCallback(got_log)
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
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))
308
"Unknown BuildStatus '%s' for builder '%s'",
309
status, self.buildqueue_record.builder.url)
301
312
d = method(librarian, slave_status, logger, send_notification)
315
def _destroy_buildqueue_record(self, unused_arg):
316
"""Destroy this build's `BuildQueue` record."""
318
with DatabaseTransactionPolicy(read_only=False):
319
self.buildqueue_record.destroySelf()
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)
328
def _notify_if_appropriate(self, appropriate=True, extra_info=None):
329
"""If `appropriate`, call `self.notify` in a write transaction."""
332
with DatabaseTransactionPolicy(read_only=False):
333
self.notify(extra_info=extra_info)
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))
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
356
with DatabaseTransactionPolicy(read_only=False):
357
build.status = BuildStatus.SUPERSEDED
330
359
return self._release_builder_and_remove_queue_item()
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:
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
384
"Copy from slave for build %s was unsuccessful.", self.id)
385
self.status = BuildStatus.FAILEDTOUPLOAD
386
if send_notification:
388
extra_info='Copy from slave was unsuccessful.')
414
"Copy from slave for build %s was unsuccessful.",
389
416
target_dir = os.path.join(root, "failed")
417
resulting_status = BuildStatus.FAILEDTOUPLOAD
420
with DatabaseTransactionPolicy(read_only=False):
421
self.status = resulting_status
424
if not successful_copy_from_slave:
425
self._notify_if_appropriate(
426
send_notification, "Copy from slave was unsuccessful.")
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()
397
# Commit so there are no race conditions with archiveuploader
399
Store.of(self).commit()
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.
424
self.status = BuildStatus.FAILEDTOBUILD
458
with DatabaseTransactionPolicy(read_only=False):
459
self.status = BuildStatus.FAILEDTOBUILD
426
462
def build_info_stored(ignored):
427
if send_notification:
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)
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:
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)
455
487
d = self.storeBuildInfo(self, librarian, slave_status)
456
488
return d.addCallback(build_info_stored)
466
498
self.status = BuildStatus.CHROOTWAIT
468
500
def build_info_stored(ignored):
469
logger.critical("***** %s is CHROOTWAIT *****" %
470
self.buildqueue_record.builder.name)
471
if send_notification:
502
"***** %s is CHROOTWAIT *****",
503
self.buildqueue_record.builder.name)
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)
477
509
d = self.storeBuildInfo(self, librarian, slave_status)
478
510
return d.addCallback(build_info_stored)
512
def _reset_buildqueue_record(self, ignored_arg=None):
513
"""Reset the `BuildQueue` record, in a write transaction."""
515
with DatabaseTransactionPolicy(read_only=False):
516
self.buildqueue_record.reset()
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")
493
def build_info_stored(ignored):
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)
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()
520
556
d = self.storeBuildInfo(self, librarian, slave_status)