diff options
Diffstat (limited to 'bitbake/lib/toaster/bldcontrol/management/commands')
3 files changed, 0 insertions, 445 deletions
diff --git a/bitbake/lib/toaster/bldcontrol/management/commands/__init__.py b/bitbake/lib/toaster/bldcontrol/management/commands/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/bitbake/lib/toaster/bldcontrol/management/commands/__init__.py +++ /dev/null | |||
diff --git a/bitbake/lib/toaster/bldcontrol/management/commands/checksettings.py b/bitbake/lib/toaster/bldcontrol/management/commands/checksettings.py deleted file mode 100644 index 20f9dce569..0000000000 --- a/bitbake/lib/toaster/bldcontrol/management/commands/checksettings.py +++ /dev/null | |||
| @@ -1,170 +0,0 @@ | |||
| 1 | # | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 3 | # | ||
| 4 | |||
| 5 | from django.core.management.base import BaseCommand | ||
| 6 | |||
| 7 | from django.core.management import call_command | ||
| 8 | from bldcontrol.models import BuildRequest, BuildEnvironment, BRError | ||
| 9 | from orm.models import ToasterSetting, Build, Layer | ||
| 10 | |||
| 11 | import os | ||
| 12 | import traceback | ||
| 13 | import warnings | ||
| 14 | |||
| 15 | |||
| 16 | def DN(path): | ||
| 17 | if path is None: | ||
| 18 | return "" | ||
| 19 | else: | ||
| 20 | return os.path.dirname(path) | ||
| 21 | |||
| 22 | |||
| 23 | class Command(BaseCommand): | ||
| 24 | args = "" | ||
| 25 | help = "Verifies that the configured settings are valid and usable, or prompts the user to fix the settings." | ||
| 26 | |||
| 27 | def __init__(self, *args, **kwargs): | ||
| 28 | super(Command, self).__init__(*args, **kwargs) | ||
| 29 | self.guesspath = DN(DN(DN(DN(DN(DN(DN(__file__))))))) | ||
| 30 | |||
| 31 | def _verify_build_environment(self): | ||
| 32 | # provide a local build env. This will be extended later to include non local | ||
| 33 | if BuildEnvironment.objects.count() == 0: | ||
| 34 | BuildEnvironment.objects.create(betype=BuildEnvironment.TYPE_LOCAL) | ||
| 35 | |||
| 36 | # we make sure we have builddir and sourcedir for all defined build envionments | ||
| 37 | for be in BuildEnvironment.objects.all(): | ||
| 38 | be.needs_import = False | ||
| 39 | def _verify_be(): | ||
| 40 | is_changed = False | ||
| 41 | |||
| 42 | def _update_sourcedir(): | ||
| 43 | be.sourcedir = os.environ.get('TOASTER_DIR') | ||
| 44 | return True | ||
| 45 | |||
| 46 | if len(be.sourcedir) == 0: | ||
| 47 | is_changed = _update_sourcedir() | ||
| 48 | |||
| 49 | if not be.sourcedir.startswith("/"): | ||
| 50 | print("\n -- Validation: The layers checkout directory must be set to an absolute path.") | ||
| 51 | is_changed = _update_sourcedir() | ||
| 52 | |||
| 53 | if is_changed: | ||
| 54 | if be.betype == BuildEnvironment.TYPE_LOCAL: | ||
| 55 | be.needs_import = True | ||
| 56 | return True | ||
| 57 | |||
| 58 | def _update_builddir(): | ||
| 59 | be.builddir = os.environ.get('TOASTER_DIR')+"/build" | ||
| 60 | return True | ||
| 61 | |||
| 62 | if len(be.builddir) == 0: | ||
| 63 | is_changed = _update_builddir() | ||
| 64 | |||
| 65 | if not be.builddir.startswith("/"): | ||
| 66 | print("\n -- Validation: The build directory must to be set to an absolute path.") | ||
| 67 | is_changed = _update_builddir() | ||
| 68 | |||
| 69 | if is_changed: | ||
| 70 | print("\nBuild configuration saved") | ||
| 71 | be.save() | ||
| 72 | return True | ||
| 73 | |||
| 74 | if be.needs_import: | ||
| 75 | try: | ||
| 76 | print("Loading default settings") | ||
| 77 | call_command("loaddata", "settings") | ||
| 78 | template_conf = os.environ.get("TEMPLATECONF", "") | ||
| 79 | custom_xml_only = os.environ.get("CUSTOM_XML_ONLY") | ||
| 80 | |||
| 81 | if ToasterSetting.objects.filter(name='CUSTOM_XML_ONLY').count() > 0 or custom_xml_only is not None: | ||
| 82 | # only use the custom settings | ||
| 83 | pass | ||
| 84 | elif "poky" in template_conf: | ||
| 85 | print("Loading poky configuration") | ||
| 86 | call_command("loaddata", "poky") | ||
| 87 | else: | ||
| 88 | print("Loading OE-Core configuration") | ||
| 89 | call_command("loaddata", "oe-core") | ||
| 90 | if template_conf: | ||
| 91 | oe_core_path = os.path.realpath( | ||
| 92 | template_conf + | ||
| 93 | "/../") | ||
| 94 | else: | ||
| 95 | print("TEMPLATECONF not found. You may have to" | ||
| 96 | " manually configure layer paths") | ||
| 97 | oe_core_path = input("Please enter the path of" | ||
| 98 | " your openembedded-core " | ||
| 99 | "layer: ") | ||
| 100 | # Update the layer instances of openemebedded-core | ||
| 101 | for layer in Layer.objects.filter( | ||
| 102 | name="openembedded-core", | ||
| 103 | local_source_dir="OE-CORE-LAYER-DIR"): | ||
| 104 | layer.local_path = oe_core_path | ||
| 105 | layer.save() | ||
| 106 | |||
| 107 | # Import the custom fixture if it's present | ||
| 108 | with warnings.catch_warnings(): | ||
| 109 | warnings.filterwarnings( | ||
| 110 | action="ignore", | ||
| 111 | message="^.*No fixture named.*$") | ||
| 112 | print("Importing custom settings if present") | ||
| 113 | try: | ||
| 114 | call_command("loaddata", "custom") | ||
| 115 | except: | ||
| 116 | print("NOTE: optional fixture 'custom' not found") | ||
| 117 | |||
| 118 | # we run lsupdates after config update | ||
| 119 | print("\nFetching information from the layer index, " | ||
| 120 | "please wait.\nYou can re-update any time later " | ||
| 121 | "by running bitbake/lib/toaster/manage.py " | ||
| 122 | "lsupdates\n") | ||
| 123 | call_command("lsupdates") | ||
| 124 | |||
| 125 | # we don't look for any other config files | ||
| 126 | return is_changed | ||
| 127 | except Exception as e: | ||
| 128 | print("Failure while trying to setup toaster: %s" | ||
| 129 | % e) | ||
| 130 | traceback.print_exc() | ||
| 131 | |||
| 132 | return is_changed | ||
| 133 | |||
| 134 | while _verify_be(): | ||
| 135 | pass | ||
| 136 | return 0 | ||
| 137 | |||
| 138 | def _verify_default_settings(self): | ||
| 139 | # verify that default settings are there | ||
| 140 | if ToasterSetting.objects.filter(name='DEFAULT_RELEASE').count() != 1: | ||
| 141 | ToasterSetting.objects.filter(name='DEFAULT_RELEASE').delete() | ||
| 142 | ToasterSetting.objects.get_or_create(name='DEFAULT_RELEASE', value='') | ||
| 143 | return 0 | ||
| 144 | |||
| 145 | def _verify_builds_in_progress(self): | ||
| 146 | # we are just starting up. we must not have any builds in progress, or build environments taken | ||
| 147 | for b in BuildRequest.objects.filter(state=BuildRequest.REQ_INPROGRESS): | ||
| 148 | BRError.objects.create(req=b, errtype="toaster", | ||
| 149 | errmsg= | ||
| 150 | "Toaster found this build IN PROGRESS while Toaster started up. This is an inconsistent state, and the build was marked as failed") | ||
| 151 | |||
| 152 | BuildRequest.objects.filter(state=BuildRequest.REQ_INPROGRESS).update(state=BuildRequest.REQ_FAILED) | ||
| 153 | |||
| 154 | BuildEnvironment.objects.update(lock=BuildEnvironment.LOCK_FREE) | ||
| 155 | |||
| 156 | # also mark "In Progress builds as failures" | ||
| 157 | from django.utils import timezone | ||
| 158 | Build.objects.filter(outcome=Build.IN_PROGRESS).update(outcome=Build.FAILED, completed_on=timezone.now()) | ||
| 159 | |||
| 160 | return 0 | ||
| 161 | |||
| 162 | |||
| 163 | |||
| 164 | def handle(self, **options): | ||
| 165 | retval = 0 | ||
| 166 | retval += self._verify_build_environment() | ||
| 167 | retval += self._verify_default_settings() | ||
| 168 | retval += self._verify_builds_in_progress() | ||
| 169 | |||
| 170 | return retval | ||
diff --git a/bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py b/bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py deleted file mode 100644 index 834e32b36f..0000000000 --- a/bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py +++ /dev/null | |||
| @@ -1,275 +0,0 @@ | |||
| 1 | # | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 3 | # | ||
| 4 | |||
| 5 | from django.core.management.base import BaseCommand | ||
| 6 | from django.db import transaction | ||
| 7 | from django.db.models import Q | ||
| 8 | |||
| 9 | from bldcontrol.bbcontroller import getBuildEnvironmentController | ||
| 10 | from bldcontrol.models import BuildRequest, BuildEnvironment | ||
| 11 | from bldcontrol.models import BRError, BRVariable | ||
| 12 | |||
| 13 | from orm.models import Build, LogMessage, Target | ||
| 14 | |||
| 15 | import logging | ||
| 16 | import traceback | ||
| 17 | import signal | ||
| 18 | import os | ||
| 19 | |||
| 20 | logger = logging.getLogger("toaster") | ||
| 21 | |||
| 22 | |||
| 23 | class Command(BaseCommand): | ||
| 24 | args = "" | ||
| 25 | help = "Schedules and executes build requests as possible. "\ | ||
| 26 | "Does not return (interrupt with Ctrl-C)" | ||
| 27 | |||
| 28 | @transaction.atomic | ||
| 29 | def _selectBuildEnvironment(self): | ||
| 30 | bec = getBuildEnvironmentController(lock=BuildEnvironment.LOCK_FREE) | ||
| 31 | bec.be.lock = BuildEnvironment.LOCK_LOCK | ||
| 32 | bec.be.save() | ||
| 33 | return bec | ||
| 34 | |||
| 35 | @transaction.atomic | ||
| 36 | def _selectBuildRequest(self): | ||
| 37 | br = BuildRequest.objects.filter(state=BuildRequest.REQ_QUEUED).first() | ||
| 38 | return br | ||
| 39 | |||
| 40 | def schedule(self): | ||
| 41 | try: | ||
| 42 | # select the build environment and the request to build | ||
| 43 | br = self._selectBuildRequest() | ||
| 44 | if br: | ||
| 45 | br.state = BuildRequest.REQ_INPROGRESS | ||
| 46 | br.save() | ||
| 47 | else: | ||
| 48 | return | ||
| 49 | |||
| 50 | try: | ||
| 51 | bec = self._selectBuildEnvironment() | ||
| 52 | except IndexError as e: | ||
| 53 | # we could not find a BEC; postpone the BR | ||
| 54 | br.state = BuildRequest.REQ_QUEUED | ||
| 55 | br.save() | ||
| 56 | logger.debug("runbuilds: No build env (%s)" % e) | ||
| 57 | return | ||
| 58 | |||
| 59 | logger.info("runbuilds: starting build %s, environment %s" % | ||
| 60 | (br, bec.be)) | ||
| 61 | |||
| 62 | # let the build request know where it is being executed | ||
| 63 | br.environment = bec.be | ||
| 64 | br.save() | ||
| 65 | |||
| 66 | # this triggers an async build | ||
| 67 | bec.triggerBuild(br.brbitbake, br.brlayer_set.all(), | ||
| 68 | br.brvariable_set.all(), br.brtarget_set.all(), | ||
| 69 | "%d:%d" % (br.pk, bec.be.pk)) | ||
| 70 | |||
| 71 | except Exception as e: | ||
| 72 | logger.error("runbuilds: Error launching build %s" % e) | ||
| 73 | traceback.print_exc() | ||
| 74 | if "[Errno 111] Connection refused" in str(e): | ||
| 75 | # Connection refused, read toaster_server.out | ||
| 76 | errmsg = bec.readServerLogFile() | ||
| 77 | else: | ||
| 78 | errmsg = str(e) | ||
| 79 | |||
| 80 | BRError.objects.create(req=br, errtype=str(type(e)), errmsg=errmsg, | ||
| 81 | traceback=traceback.format_exc()) | ||
| 82 | br.state = BuildRequest.REQ_FAILED | ||
| 83 | br.save() | ||
| 84 | bec.be.lock = BuildEnvironment.LOCK_FREE | ||
| 85 | bec.be.save() | ||
| 86 | # Cancel the pending build and report the exception to the UI | ||
| 87 | log_object = LogMessage.objects.create( | ||
| 88 | build = br.build, | ||
| 89 | level = LogMessage.EXCEPTION, | ||
| 90 | message = errmsg) | ||
| 91 | log_object.save() | ||
| 92 | br.build.outcome = Build.FAILED | ||
| 93 | br.build.save() | ||
| 94 | |||
| 95 | def archive(self): | ||
| 96 | for br in BuildRequest.objects.filter(state=BuildRequest.REQ_ARCHIVE): | ||
| 97 | if br.build is None: | ||
| 98 | br.state = BuildRequest.REQ_FAILED | ||
| 99 | else: | ||
| 100 | br.state = BuildRequest.REQ_COMPLETED | ||
| 101 | br.save() | ||
| 102 | |||
| 103 | def cleanup(self): | ||
| 104 | from django.utils import timezone | ||
| 105 | from datetime import timedelta | ||
| 106 | # environments locked for more than 30 seconds | ||
| 107 | # they should be unlocked | ||
| 108 | BuildEnvironment.objects.filter( | ||
| 109 | Q(buildrequest__state__in=[BuildRequest.REQ_FAILED, | ||
| 110 | BuildRequest.REQ_COMPLETED, | ||
| 111 | BuildRequest.REQ_CANCELLING]) & | ||
| 112 | Q(lock=BuildEnvironment.LOCK_LOCK) & | ||
| 113 | Q(updated__lt=timezone.now() - timedelta(seconds=30)) | ||
| 114 | ).update(lock=BuildEnvironment.LOCK_FREE) | ||
| 115 | |||
| 116 | # update all Builds that were in progress and failed to start | ||
| 117 | for br in BuildRequest.objects.filter( | ||
| 118 | state=BuildRequest.REQ_FAILED, | ||
| 119 | build__outcome=Build.IN_PROGRESS): | ||
| 120 | # transpose the launch errors in ToasterExceptions | ||
| 121 | br.build.outcome = Build.FAILED | ||
| 122 | for brerror in br.brerror_set.all(): | ||
| 123 | logger.debug("Saving error %s" % brerror) | ||
| 124 | LogMessage.objects.create(build=br.build, | ||
| 125 | level=LogMessage.EXCEPTION, | ||
| 126 | message=brerror.errmsg) | ||
| 127 | br.build.save() | ||
| 128 | |||
| 129 | # we don't have a true build object here; hence, toasterui | ||
| 130 | # didn't have a change to release the BE lock | ||
| 131 | br.environment.lock = BuildEnvironment.LOCK_FREE | ||
| 132 | br.environment.save() | ||
| 133 | |||
| 134 | # update all BuildRequests without a build created | ||
| 135 | for br in BuildRequest.objects.filter(build=None): | ||
| 136 | br.build = Build.objects.create(project=br.project, | ||
| 137 | completed_on=br.updated, | ||
| 138 | started_on=br.created) | ||
| 139 | br.build.outcome = Build.FAILED | ||
| 140 | try: | ||
| 141 | br.build.machine = br.brvariable_set.get(name='MACHINE').value | ||
| 142 | except BRVariable.DoesNotExist: | ||
| 143 | pass | ||
| 144 | br.save() | ||
| 145 | # transpose target information | ||
| 146 | for brtarget in br.brtarget_set.all(): | ||
| 147 | Target.objects.create(build=br.build, | ||
| 148 | target=brtarget.target, | ||
| 149 | task=brtarget.task) | ||
| 150 | # transpose the launch errors in ToasterExceptions | ||
| 151 | for brerror in br.brerror_set.all(): | ||
| 152 | LogMessage.objects.create(build=br.build, | ||
| 153 | level=LogMessage.EXCEPTION, | ||
| 154 | message=brerror.errmsg) | ||
| 155 | |||
| 156 | br.build.save() | ||
| 157 | |||
| 158 | # Make sure the LOCK is removed for builds which have been fully | ||
| 159 | # cancelled | ||
| 160 | for br in BuildRequest.objects.filter( | ||
| 161 | Q(build__outcome=Build.CANCELLED) & | ||
| 162 | Q(state=BuildRequest.REQ_CANCELLING) & | ||
| 163 | ~Q(environment=None)): | ||
| 164 | br.environment.lock = BuildEnvironment.LOCK_FREE | ||
| 165 | br.environment.save() | ||
| 166 | |||
| 167 | def runbuild(self): | ||
| 168 | try: | ||
| 169 | self.cleanup() | ||
| 170 | except Exception as e: | ||
| 171 | logger.warning("runbuilds: cleanup exception %s" % str(e)) | ||
| 172 | |||
| 173 | try: | ||
| 174 | self.archive() | ||
| 175 | except Exception as e: | ||
| 176 | logger.warning("runbuilds: archive exception %s" % str(e)) | ||
| 177 | |||
| 178 | try: | ||
| 179 | self.schedule() | ||
| 180 | except Exception as e: | ||
| 181 | logger.warning("runbuilds: schedule exception %s" % str(e)) | ||
| 182 | |||
| 183 | # Test to see if a build pre-maturely died due to a bitbake crash | ||
| 184 | def check_dead_builds(self): | ||
| 185 | do_cleanup = False | ||
| 186 | try: | ||
| 187 | for br in BuildRequest.objects.filter(state=BuildRequest.REQ_INPROGRESS): | ||
| 188 | # Get the build directory | ||
| 189 | if br.project.builddir: | ||
| 190 | builddir = br.project.builddir | ||
| 191 | else: | ||
| 192 | builddir = '%s-toaster-%d' % (br.environment.builddir,br.project.id) | ||
| 193 | # Check log to see if there is a recent traceback | ||
| 194 | toaster_ui_log = os.path.join(builddir, 'toaster_ui.log') | ||
| 195 | test_file = os.path.join(builddir, '._toaster_check.txt') | ||
| 196 | os.system("tail -n 50 %s > %s" % (os.path.join(builddir, 'toaster_ui.log'),test_file)) | ||
| 197 | traceback_text = '' | ||
| 198 | is_traceback = False | ||
| 199 | with open(test_file,'r') as test_file_fd: | ||
| 200 | test_file_tail = test_file_fd.readlines() | ||
| 201 | for line in test_file_tail: | ||
| 202 | if line.startswith('Traceback (most recent call last):'): | ||
| 203 | traceback_text = line | ||
| 204 | is_traceback = True | ||
| 205 | elif line.startswith('NOTE: ToasterUI waiting for events'): | ||
| 206 | # Ignore any traceback before new build start | ||
| 207 | traceback_text = '' | ||
| 208 | is_traceback = False | ||
| 209 | elif line.startswith('Note: Toaster traceback auto-stop'): | ||
| 210 | # Ignore any traceback before this previous traceback catch | ||
| 211 | traceback_text = '' | ||
| 212 | is_traceback = False | ||
| 213 | elif is_traceback: | ||
| 214 | traceback_text += line | ||
| 215 | # Test the results | ||
| 216 | is_stop = False | ||
| 217 | if is_traceback: | ||
| 218 | # Found a traceback | ||
| 219 | errtype = 'Bitbake crash' | ||
| 220 | errmsg = 'Bitbake crash\n' + traceback_text | ||
| 221 | state = BuildRequest.REQ_FAILED | ||
| 222 | # Clean up bitbake files | ||
| 223 | bitbake_lock = os.path.join(builddir, 'bitbake.lock') | ||
| 224 | if os.path.isfile(bitbake_lock): | ||
| 225 | os.remove(bitbake_lock) | ||
| 226 | bitbake_sock = os.path.join(builddir, 'bitbake.sock') | ||
| 227 | if os.path.isfile(bitbake_sock): | ||
| 228 | os.remove(bitbake_sock) | ||
| 229 | if os.path.isfile(test_file): | ||
| 230 | os.remove(test_file) | ||
| 231 | # Add note to ignore this traceback on next check | ||
| 232 | os.system('echo "Note: Toaster traceback auto-stop" >> %s' % toaster_ui_log) | ||
| 233 | is_stop = True | ||
| 234 | # Add more tests here | ||
| 235 | #elif ... | ||
| 236 | # Stop the build request? | ||
| 237 | if is_stop: | ||
| 238 | brerror = BRError( | ||
| 239 | req = br, | ||
| 240 | errtype = errtype, | ||
| 241 | errmsg = errmsg, | ||
| 242 | traceback = traceback_text, | ||
| 243 | ) | ||
| 244 | brerror.save() | ||
| 245 | br.state = state | ||
| 246 | br.save() | ||
| 247 | do_cleanup = True | ||
| 248 | # Do cleanup | ||
| 249 | if do_cleanup: | ||
| 250 | self.cleanup() | ||
| 251 | except Exception as e: | ||
| 252 | logger.error("runbuilds: Error in check_dead_builds %s" % e) | ||
| 253 | |||
| 254 | def handle(self, **options): | ||
| 255 | pidfile_path = os.path.join(os.environ.get("BUILDDIR", "."), | ||
| 256 | ".runbuilds.pid") | ||
| 257 | |||
| 258 | with open(pidfile_path, 'w') as pidfile: | ||
| 259 | pidfile.write("%s" % os.getpid()) | ||
| 260 | |||
| 261 | # Clean up any stale/failed builds from previous Toaster run | ||
| 262 | self.runbuild() | ||
| 263 | |||
| 264 | signal.signal(signal.SIGUSR1, lambda sig, frame: None) | ||
| 265 | |||
| 266 | while True: | ||
| 267 | sigset = signal.sigtimedwait([signal.SIGUSR1], 5) | ||
| 268 | if sigset: | ||
| 269 | for sig in sigset: | ||
| 270 | # Consume each captured pending event | ||
| 271 | self.runbuild() | ||
| 272 | else: | ||
| 273 | # Check for build exceptions | ||
| 274 | self.check_dead_builds() | ||
| 275 | |||
