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