blob: 6a55dd46c8b6662699fff54842b79f97ddb01b38 [file] [log] [blame]
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001from django.core.management.base import BaseCommand
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002from django.db import transaction
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05003from django.db.models import Q
4
5from bldcontrol.bbcontroller import getBuildEnvironmentController
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05006from bldcontrol.models import BuildRequest, BuildEnvironment
7from bldcontrol.models import BRError, BRVariable
8
Patrick Williamsc0f7c042017-02-23 20:41:17 -06009from orm.models import Build, LogMessage, Target
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050010
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011import logging
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050012import traceback
Patrick Williamsc0f7c042017-02-23 20:41:17 -060013import signal
Brad Bishop6e60e8b2018-02-01 10:27:11 -050014import os
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050016logger = logging.getLogger("toaster")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017
Brad Bishop6e60e8b2018-02-01 10:27:11 -050018
Brad Bishopd7bf8c12018-02-25 22:55:05 -050019class Command(BaseCommand):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060020 args = ""
21 help = "Schedules and executes build requests as possible. "\
22 "Does not return (interrupt with Ctrl-C)"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050024 @transaction.atomic
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025 def _selectBuildEnvironment(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026 bec = getBuildEnvironmentController(lock=BuildEnvironment.LOCK_FREE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050027 bec.be.lock = BuildEnvironment.LOCK_LOCK
28 bec.be.save()
29 return bec
30
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050031 @transaction.atomic
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032 def _selectBuildRequest(self):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050033 br = BuildRequest.objects.filter(state=BuildRequest.REQ_QUEUED).first()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034 return br
35
36 def schedule(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050038 # select the build environment and the request to build
39 br = self._selectBuildRequest()
40 if br:
41 br.state = BuildRequest.REQ_INPROGRESS
42 br.save()
43 else:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050044 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050045
Patrick Williamsc124f4f2015-09-15 14:41:29 -050046 try:
47 bec = self._selectBuildEnvironment()
48 except IndexError as e:
49 # we could not find a BEC; postpone the BR
50 br.state = BuildRequest.REQ_QUEUED
51 br.save()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080052 logger.debug("runbuilds: No build env (%s)" % e)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053 return
54
Brad Bishop6e60e8b2018-02-01 10:27:11 -050055 logger.info("runbuilds: starting build %s, environment %s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056 (br, bec.be))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057
58 # let the build request know where it is being executed
59 br.environment = bec.be
60 br.save()
61
62 # this triggers an async build
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050063 bec.triggerBuild(br.brbitbake, br.brlayer_set.all(),
64 br.brvariable_set.all(), br.brtarget_set.all(),
65 "%d:%d" % (br.pk, bec.be.pk))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066
67 except Exception as e:
68 logger.error("runbuilds: Error launching build %s" % e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060069 traceback.print_exc()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070 if "[Errno 111] Connection refused" in str(e):
71 # Connection refused, read toaster_server.out
72 errmsg = bec.readServerLogFile()
73 else:
74 errmsg = str(e)
75
Patrick Williamsc0f7c042017-02-23 20:41:17 -060076 BRError.objects.create(req=br, errtype=str(type(e)), errmsg=errmsg,
77 traceback=traceback.format_exc())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078 br.state = BuildRequest.REQ_FAILED
79 br.save()
80 bec.be.lock = BuildEnvironment.LOCK_FREE
81 bec.be.save()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050082 # Cancel the pending build and report the exception to the UI
83 log_object = LogMessage.objects.create(
84 build = br.build,
85 level = LogMessage.EXCEPTION,
86 message = errmsg)
87 log_object.save()
88 br.build.outcome = Build.FAILED
89 br.build.save()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090
91 def archive(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060092 for br in BuildRequest.objects.filter(state=BuildRequest.REQ_ARCHIVE):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050093 if br.build is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094 br.state = BuildRequest.REQ_FAILED
Patrick Williamsd7e96312015-09-22 08:09:05 -050095 else:
96 br.state = BuildRequest.REQ_COMPLETED
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097 br.save()
98
99 def cleanup(self):
100 from django.utils import timezone
101 from datetime import timedelta
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500102 # environments locked for more than 30 seconds
103 # they should be unlocked
104 BuildEnvironment.objects.filter(
105 Q(buildrequest__state__in=[BuildRequest.REQ_FAILED,
106 BuildRequest.REQ_COMPLETED,
107 BuildRequest.REQ_CANCELLING]) &
108 Q(lock=BuildEnvironment.LOCK_LOCK) &
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600109 Q(updated__lt=timezone.now() - timedelta(seconds=30))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500110 ).update(lock=BuildEnvironment.LOCK_FREE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500112 # update all Builds that were in progress and failed to start
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500113 for br in BuildRequest.objects.filter(
114 state=BuildRequest.REQ_FAILED,
115 build__outcome=Build.IN_PROGRESS):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116 # transpose the launch errors in ToasterExceptions
117 br.build.outcome = Build.FAILED
118 for brerror in br.brerror_set.all():
119 logger.debug("Saving error %s" % brerror)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500120 LogMessage.objects.create(build=br.build,
121 level=LogMessage.EXCEPTION,
122 message=brerror.errmsg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123 br.build.save()
124
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500125 # we don't have a true build object here; hence, toasterui
126 # didn't have a change to release the BE lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127 br.environment.lock = BuildEnvironment.LOCK_FREE
128 br.environment.save()
129
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130 # update all BuildRequests without a build created
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600131 for br in BuildRequest.objects.filter(build=None):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500132 br.build = Build.objects.create(project=br.project,
133 completed_on=br.updated,
134 started_on=br.created)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135 br.build.outcome = Build.FAILED
136 try:
137 br.build.machine = br.brvariable_set.get(name='MACHINE').value
138 except BRVariable.DoesNotExist:
139 pass
140 br.save()
141 # transpose target information
142 for brtarget in br.brtarget_set.all():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500143 Target.objects.create(build=br.build,
144 target=brtarget.target,
145 task=brtarget.task)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146 # transpose the launch errors in ToasterExceptions
147 for brerror in br.brerror_set.all():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500148 LogMessage.objects.create(build=br.build,
149 level=LogMessage.EXCEPTION,
150 message=brerror.errmsg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151
152 br.build.save()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500153
154 # Make sure the LOCK is removed for builds which have been fully
155 # cancelled
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500156 for br in BuildRequest.objects.filter(
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600157 Q(build__outcome=Build.CANCELLED) &
158 Q(state=BuildRequest.REQ_CANCELLING) &
159 ~Q(environment=None)):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500160 br.environment.lock = BuildEnvironment.LOCK_FREE
161 br.environment.save()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600163 def runbuild(self):
164 try:
165 self.cleanup()
166 except Exception as e:
167 logger.warn("runbuilds: cleanup exception %s" % str(e))
168
169 try:
170 self.archive()
171 except Exception as e:
172 logger.warn("runbuilds: archive exception %s" % str(e))
173
174 try:
175 self.schedule()
176 except Exception as e:
177 logger.warn("runbuilds: schedule exception %s" % str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500179 def handle(self, **options):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500180 pidfile_path = os.path.join(os.environ.get("BUILDDIR", "."),
181 ".runbuilds.pid")
182
183 with open(pidfile_path, 'w') as pidfile:
184 pidfile.write("%s" % os.getpid())
185
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600186 self.runbuild()
187
188 signal.signal(signal.SIGUSR1, lambda sig, frame: None)
189
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500190 while True:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600191 signal.pause()
192 self.runbuild()