50
51
from lp.services.config import config
51
52
from lp.services.database.enumcol import DBEnum
52
53
from lp.services.database.lpstorm import IMasterStore
54
from lp.services.database.transaction_policy import DatabaseTransactionPolicy
53
55
from lp.services.helpers import filenameToContentType
54
56
from lp.services.librarian.browser import ProxiedLibraryFileAlias
55
57
from lp.services.librarian.interfaces import ILibraryFileAliasSet
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)
286
293
job=job, processor=processor,
287
294
virtualized=specific_job.virtualized)
288
295
Store.of(self).add(queue_entry)
289
297
return queue_entry
291
299
def handleStatus(self, status, librarian, slave_status):
292
300
"""See `IPackageBuild`."""
301
# Avoid circular imports.
293
302
from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME
294
304
logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME)
295
305
send_notification = status in self.ALLOWED_STATUS_NOTIFICATIONS
296
306
method = getattr(self, '_handleStatus_' + status, None)
297
307
if method is None:
298
logger.critical("Unknown BuildStatus '%s' for builder '%s'"
299
% (status, self.buildqueue_record.builder.url))
309
"Unknown BuildStatus '%s' for builder '%s'",
310
status, self.buildqueue_record.builder.url)
301
313
d = method(librarian, slave_status, logger, send_notification)
316
def _destroy_buildqueue_record(self, unused_arg):
317
"""Destroy this build's `BuildQueue` record."""
319
with DatabaseTransactionPolicy(read_only=False):
320
self.buildqueue_record.destroySelf()
304
323
def _release_builder_and_remove_queue_item(self):
305
324
# Release the builder for another job.
306
325
d = self.buildqueue_record.builder.cleanSlave()
307
326
# Remove BuildQueue record.
308
return d.addCallback(lambda x: self.buildqueue_record.destroySelf())
327
return d.addCallback(self._destroy_buildqueue_record)
329
def _notify_if_appropriate(self, appropriate=True, extra_info=None):
330
"""If `appropriate`, call `self.notify` in a write transaction."""
333
with DatabaseTransactionPolicy(read_only=False):
334
self.notify(extra_info=extra_info)
310
337
def _handleStatus_OK(self, librarian, slave_status, logger,
311
338
send_notification):
321
348
self.buildqueue_record.specific_job.build.title,
322
349
self.buildqueue_record.builder.name))
324
# If this is a binary package build, discard it if its source is
325
# no longer published.
351
# If this is a binary package build for a source that is no
352
# longer published, discard it.
326
353
if self.build_farm_job_type == BuildFarmJobType.PACKAGEBUILD:
327
354
build = self.buildqueue_record.specific_job.build
328
355
if not build.current_source_publication:
329
build.status = BuildStatus.SUPERSEDED
357
with DatabaseTransactionPolicy(read_only=False):
358
build.status = BuildStatus.SUPERSEDED
330
360
return self._release_builder_and_remove_queue_item()
332
# Explode before collect a binary that is denied in this
333
# distroseries/pocket
362
# Explode rather than collect a binary that is denied in this
363
# distroseries/pocket.
334
364
if not self.archive.allowUpdatesToReleasePocket():
335
365
assert self.distro_series.canUploadToPocket(self.pocket), (
336
366
"%s (%s) can not be built for pocket %s: illegal status"
375
405
# files from the slave.
376
406
if successful_copy_from_slave:
378
"Gathered %s %d completely. Moving %s to uploader queue."
379
% (self.__class__.__name__, self.id, upload_leaf))
408
"Gathered %s %d completely. "
409
"Moving %s to uploader queue.",
410
self.__class__.__name__, self.id, upload_leaf)
380
411
target_dir = os.path.join(root, "incoming")
381
self.status = BuildStatus.UPLOADING
412
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.')
415
"Copy from slave for build %s was unsuccessful.",
389
417
target_dir = os.path.join(root, "failed")
418
resulting_status = BuildStatus.FAILEDTOUPLOAD
421
with DatabaseTransactionPolicy(read_only=False):
422
self.status = resulting_status
425
if not successful_copy_from_slave:
426
self._notify_if_appropriate(
427
send_notification, "Copy from slave was unsuccessful.")
391
429
if not os.path.exists(target_dir):
392
430
os.mkdir(target_dir)
394
432
# Release the builder for another job.
395
433
d = self._release_builder_and_remove_queue_item()
397
# Commit so there are no race conditions with archiveuploader
399
Store.of(self).commit()
401
435
# Move the directory used to grab the binaries into
402
436
# the incoming directory so the upload processor never
403
437
# sees half-finished uploads.
421
455
set the job status as FAILEDTOBUILD, store available info and
422
456
remove Buildqueue entry.
424
self.status = BuildStatus.FAILEDTOBUILD
459
with DatabaseTransactionPolicy(read_only=False):
460
self.status = BuildStatus.FAILEDTOBUILD
426
463
def build_info_stored(ignored):
427
if send_notification:
464
self._notify_if_appropriate(send_notification)
429
465
d = self.buildqueue_record.builder.cleanSlave()
430
return d.addCallback(
431
lambda x: self.buildqueue_record.destroySelf())
466
return d.addCallback(self._destroy_buildqueue_record)
433
468
d = self.storeBuildInfo(self, librarian, slave_status)
434
469
return d.addCallback(build_info_stored)
441
476
MANUALDEPWAIT, store available information, remove BuildQueue
442
477
entry and release builder slave for another job.
444
self.status = BuildStatus.MANUALDEPWAIT
479
with DatabaseTransactionPolicy(read_only=False):
480
self.status = BuildStatus.MANUALDEPWAIT
446
483
def build_info_stored(ignored):
447
484
logger.critical("***** %s is MANUALDEPWAIT *****"
448
485
% self.buildqueue_record.builder.name)
449
if send_notification:
486
self._notify_if_appropriate(send_notification)
451
487
d = self.buildqueue_record.builder.cleanSlave()
452
return d.addCallback(
453
lambda x: self.buildqueue_record.destroySelf())
488
return d.addCallback(self._destroy_buildqueue_record)
455
490
d = self.storeBuildInfo(self, librarian, slave_status)
456
491
return d.addCallback(build_info_stored)
463
498
job as CHROOTFAIL, store available information, remove BuildQueue
464
499
and release the builder.
466
self.status = BuildStatus.CHROOTWAIT
501
with DatabaseTransactionPolicy(read_only=False):
502
self.status = BuildStatus.CHROOTWAIT
468
505
def build_info_stored(ignored):
469
logger.critical("***** %s is CHROOTWAIT *****" %
470
self.buildqueue_record.builder.name)
471
if send_notification:
507
"***** %s is CHROOTWAIT *****",
508
self.buildqueue_record.builder.name)
510
self._notify_if_appropriate(send_notification)
473
511
d = self.buildqueue_record.builder.cleanSlave()
474
return d.addCallback(
475
lambda x: self.buildqueue_record.destroySelf())
512
return d.addCallback(self._destroy_buildqueue_record)
477
514
d = self.storeBuildInfo(self, librarian, slave_status)
478
515
return d.addCallback(build_info_stored)
517
def _reset_buildqueue_record(self, ignored_arg=None):
518
"""Reset the `BuildQueue` record, in a write transaction."""
520
with DatabaseTransactionPolicy(read_only=False):
521
self.buildqueue_record.reset()
480
524
def _handleStatus_BUILDERFAIL(self, librarian, slave_status, logger,
481
525
send_notification):
482
526
"""Handle builder failures.
490
534
self.buildqueue_record.builder.failBuilder(
491
535
"Builder returned BUILDERFAIL when asked for its status")
493
def build_info_stored(ignored):
495
self.buildqueue_record.reset()
496
537
d = self.storeBuildInfo(self, librarian, slave_status)
497
return d.addCallback(build_info_stored)
538
return d.addCallback(self._reset_buildqueue_record)
499
540
def _handleStatus_GIVENBACK(self, librarian, slave_status, logger,
500
541
send_notification):
514
555
# the next Paris Summit, infinity has some ideas about how
515
556
# to use this content. For now we just ensure it's stored.
516
557
d = self.buildqueue_record.builder.cleanSlave()
517
self.buildqueue_record.reset()
558
self._reset_buildqueue_record()
520
561
d = self.storeBuildInfo(self, librarian, slave_status)