runbuilds.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. from django.core.management.base import NoArgsCommand, CommandError
  2. from django.db import transaction
  3. from django.db.models import Q
  4. from bldcontrol.bbcontroller import getBuildEnvironmentController
  5. from bldcontrol.bbcontroller import ShellCmdException, BuildSetupException
  6. from bldcontrol.models import BuildRequest, BuildEnvironment
  7. from bldcontrol.models import BRError, BRVariable
  8. from orm.models import Build, ToasterSetting, LogMessage, Target
  9. import os
  10. import logging
  11. import time
  12. import sys
  13. import traceback
  14. logger = logging.getLogger("toaster")
  15. class Command(NoArgsCommand):
  16. args = ""
  17. help = "Schedules and executes build requests as possible."
  18. "Does not return (interrupt with Ctrl-C)"
  19. @transaction.atomic
  20. def _selectBuildEnvironment(self):
  21. bec = getBuildEnvironmentController(lock = BuildEnvironment.LOCK_FREE)
  22. bec.be.lock = BuildEnvironment.LOCK_LOCK
  23. bec.be.save()
  24. return bec
  25. @transaction.atomic
  26. def _selectBuildRequest(self):
  27. br = BuildRequest.objects.filter(state=BuildRequest.REQ_QUEUED).first()
  28. return br
  29. def schedule(self):
  30. try:
  31. # select the build environment and the request to build
  32. br = self._selectBuildRequest()
  33. if br:
  34. br.state = BuildRequest.REQ_INPROGRESS
  35. br.save()
  36. else:
  37. return
  38. try:
  39. bec = self._selectBuildEnvironment()
  40. except IndexError as e:
  41. # we could not find a BEC; postpone the BR
  42. br.state = BuildRequest.REQ_QUEUED
  43. br.save()
  44. logger.debug("runbuilds: No build env")
  45. return
  46. logger.debug("runbuilds: starting build %s, environment %s" % \
  47. (str(br).decode('utf-8'), bec.be))
  48. # let the build request know where it is being executed
  49. br.environment = bec.be
  50. br.save()
  51. # this triggers an async build
  52. bec.triggerBuild(br.brbitbake, br.brlayer_set.all(),
  53. br.brvariable_set.all(), br.brtarget_set.all(),
  54. "%d:%d" % (br.pk, bec.be.pk))
  55. except Exception as e:
  56. logger.error("runbuilds: Error launching build %s" % e)
  57. traceback.print_exc(e)
  58. if "[Errno 111] Connection refused" in str(e):
  59. # Connection refused, read toaster_server.out
  60. errmsg = bec.readServerLogFile()
  61. else:
  62. errmsg = str(e)
  63. BRError.objects.create(req = br,
  64. errtype = str(type(e)),
  65. errmsg = errmsg,
  66. traceback = traceback.format_exc(e))
  67. br.state = BuildRequest.REQ_FAILED
  68. br.save()
  69. bec.be.lock = BuildEnvironment.LOCK_FREE
  70. bec.be.save()
  71. def archive(self):
  72. for br in BuildRequest.objects.filter(state = BuildRequest.REQ_ARCHIVE):
  73. if br.build == None:
  74. br.state = BuildRequest.REQ_FAILED
  75. else:
  76. br.state = BuildRequest.REQ_COMPLETED
  77. br.save()
  78. def cleanup(self):
  79. from django.utils import timezone
  80. from datetime import timedelta
  81. # environments locked for more than 30 seconds
  82. # they should be unlocked
  83. BuildEnvironment.objects.filter(
  84. Q(buildrequest__state__in=[BuildRequest.REQ_FAILED,
  85. BuildRequest.REQ_COMPLETED,
  86. BuildRequest.REQ_CANCELLING]) &
  87. Q(lock=BuildEnvironment.LOCK_LOCK) &
  88. Q(updated__lt=timezone.now() - timedelta(seconds = 30))
  89. ).update(lock=BuildEnvironment.LOCK_FREE)
  90. # update all Builds that were in progress and failed to start
  91. for br in BuildRequest.objects.filter(
  92. state=BuildRequest.REQ_FAILED,
  93. build__outcome=Build.IN_PROGRESS):
  94. # transpose the launch errors in ToasterExceptions
  95. br.build.outcome = Build.FAILED
  96. for brerror in br.brerror_set.all():
  97. logger.debug("Saving error %s" % brerror)
  98. LogMessage.objects.create(build=br.build,
  99. level=LogMessage.EXCEPTION,
  100. message=brerror.errmsg)
  101. br.build.save()
  102. # we don't have a true build object here; hence, toasterui
  103. # didn't have a change to release the BE lock
  104. br.environment.lock = BuildEnvironment.LOCK_FREE
  105. br.environment.save()
  106. # update all BuildRequests without a build created
  107. for br in BuildRequest.objects.filter(build = None):
  108. br.build = Build.objects.create(project=br.project,
  109. completed_on=br.updated,
  110. started_on=br.created)
  111. br.build.outcome = Build.FAILED
  112. try:
  113. br.build.machine = br.brvariable_set.get(name='MACHINE').value
  114. except BRVariable.DoesNotExist:
  115. pass
  116. br.save()
  117. # transpose target information
  118. for brtarget in br.brtarget_set.all():
  119. Target.objects.create(build=br.build,
  120. target=brtarget.target,
  121. task=brtarget.task)
  122. # transpose the launch errors in ToasterExceptions
  123. for brerror in br.brerror_set.all():
  124. LogMessage.objects.create(build=br.build,
  125. level=LogMessage.EXCEPTION,
  126. message=brerror.errmsg)
  127. br.build.save()
  128. # Make sure the LOCK is removed for builds which have been fully
  129. # cancelled
  130. for br in BuildRequest.objects.filter(
  131. Q(build__outcome=Build.CANCELLED) &
  132. Q(state=BuildRequest.REQ_CANCELLING) &
  133. ~Q(environment=None)):
  134. br.environment.lock = BuildEnvironment.LOCK_FREE
  135. br.environment.save()
  136. def handle_noargs(self, **options):
  137. while True:
  138. try:
  139. self.cleanup()
  140. except Exception as e:
  141. logger.warning("runbuilds: cleanup exception %s" % str(e))
  142. try:
  143. self.archive()
  144. except Exception as e:
  145. logger.warning("runbuilds: archive exception %s" % str(e))
  146. try:
  147. self.schedule()
  148. except Exception as e:
  149. logger.warning("runbuilds: schedule exception %s" % str(e))
  150. time.sleep(1)