diff options
Diffstat (limited to 'bitbake/lib/toaster/bldcontrol')
19 files changed, 0 insertions, 1568 deletions
diff --git a/bitbake/lib/toaster/bldcontrol/__init__.py b/bitbake/lib/toaster/bldcontrol/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/bitbake/lib/toaster/bldcontrol/__init__.py +++ /dev/null | |||
diff --git a/bitbake/lib/toaster/bldcontrol/admin.py b/bitbake/lib/toaster/bldcontrol/admin.py deleted file mode 100644 index 1754bc11cc..0000000000 --- a/bitbake/lib/toaster/bldcontrol/admin.py +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | # | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 3 | # | ||
| 4 | |||
| 5 | from django.contrib import admin | ||
| 6 | from .models import BuildEnvironment | ||
| 7 | |||
| 8 | class BuildEnvironmentAdmin(admin.ModelAdmin): | ||
| 9 | pass | ||
| 10 | |||
| 11 | admin.site.register(BuildEnvironment, BuildEnvironmentAdmin) | ||
diff --git a/bitbake/lib/toaster/bldcontrol/bbcontroller.py b/bitbake/lib/toaster/bldcontrol/bbcontroller.py deleted file mode 100644 index 71c288df34..0000000000 --- a/bitbake/lib/toaster/bldcontrol/bbcontroller.py +++ /dev/null | |||
| @@ -1,126 +0,0 @@ | |||
| 1 | # | ||
| 2 | # BitBake Toaster Implementation | ||
| 3 | # | ||
| 4 | # Copyright (C) 2014 Intel Corporation | ||
| 5 | # | ||
| 6 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 7 | # | ||
| 8 | |||
| 9 | import os | ||
| 10 | import sys | ||
| 11 | from django.db.models import Q | ||
| 12 | from bldcontrol.models import BuildEnvironment, BRLayer, BRBitbake | ||
| 13 | |||
| 14 | # load Bitbake components | ||
| 15 | path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | ||
| 16 | sys.path.insert(0, path) | ||
| 17 | |||
| 18 | class BitbakeController(object): | ||
| 19 | """ This is the basic class that controlls a bitbake server. | ||
| 20 | It is outside the scope of this class on how the server is started and aquired | ||
| 21 | """ | ||
| 22 | |||
| 23 | def __init__(self, be): | ||
| 24 | import bb.server.xmlrpcclient | ||
| 25 | self.connection = bb.server.xmlrpcclient._create_server(be.bbaddress, | ||
| 26 | int(be.bbport))[0] | ||
| 27 | |||
| 28 | def _runCommand(self, command): | ||
| 29 | result, error = self.connection.runCommand(command) | ||
| 30 | if error: | ||
| 31 | raise Exception(error) | ||
| 32 | return result | ||
| 33 | |||
| 34 | def disconnect(self): | ||
| 35 | return self.connection.removeClient() | ||
| 36 | |||
| 37 | def setVariable(self, name, value): | ||
| 38 | return self._runCommand(["setVariable", name, value]) | ||
| 39 | |||
| 40 | def getVariable(self, name): | ||
| 41 | return self._runCommand(["getVariable", name]) | ||
| 42 | |||
| 43 | def triggerEvent(self, event): | ||
| 44 | return self._runCommand(["triggerEvent", event]) | ||
| 45 | |||
| 46 | def build(self, targets, task = None): | ||
| 47 | if task is None: | ||
| 48 | task = "build" | ||
| 49 | return self._runCommand(["buildTargets", targets, task]) | ||
| 50 | |||
| 51 | def forceShutDown(self): | ||
| 52 | return self._runCommand(["stateForceShutdown"]) | ||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | def getBuildEnvironmentController(**kwargs): | ||
| 57 | """ Gets you a BuildEnvironmentController that encapsulates a build environment, | ||
| 58 | based on the query dictionary sent in. | ||
| 59 | |||
| 60 | This is used to retrieve, for example, the currently running BE from inside | ||
| 61 | the toaster UI, or find a new BE to start a new build in it. | ||
| 62 | |||
| 63 | The return object MUST always be a BuildEnvironmentController. | ||
| 64 | """ | ||
| 65 | |||
| 66 | from bldcontrol.localhostbecontroller import LocalhostBEController | ||
| 67 | |||
| 68 | be = BuildEnvironment.objects.filter(Q(**kwargs))[0] | ||
| 69 | if be.betype == BuildEnvironment.TYPE_LOCAL: | ||
| 70 | return LocalhostBEController(be) | ||
| 71 | else: | ||
| 72 | raise Exception("FIXME: Implement BEC for type %s" % str(be.betype)) | ||
| 73 | |||
| 74 | |||
| 75 | class BuildEnvironmentController(object): | ||
| 76 | """ BuildEnvironmentController (BEC) is the abstract class that defines the operations that MUST | ||
| 77 | or SHOULD be supported by a Build Environment. It is used to establish the framework, and must | ||
| 78 | not be instantiated directly by the user. | ||
| 79 | |||
| 80 | Use the "getBuildEnvironmentController()" function to get a working BEC for your remote. | ||
| 81 | |||
| 82 | How the BuildEnvironments are discovered is outside the scope of this class. | ||
| 83 | |||
| 84 | You must derive this class to teach Toaster how to operate in your own infrastructure. | ||
| 85 | We provide some specific BuildEnvironmentController classes that can be used either to | ||
| 86 | directly set-up Toaster infrastructure, or as a model for your own infrastructure set: | ||
| 87 | |||
| 88 | * Localhost controller will run the Toaster BE on the same account as the web server | ||
| 89 | (current user if you are using the the Django development web server) | ||
| 90 | on the local machine, with the "build/" directory under the "poky/" source checkout directory. | ||
| 91 | Bash is expected to be available. | ||
| 92 | |||
| 93 | """ | ||
| 94 | def __init__(self, be): | ||
| 95 | """ Takes a BuildEnvironment object as parameter that points to the settings of the BE. | ||
| 96 | """ | ||
| 97 | self.be = be | ||
| 98 | self.connection = None | ||
| 99 | |||
| 100 | def setLayers(self, bitbake, ls): | ||
| 101 | """ Checks-out bitbake executor and layers from git repositories. | ||
| 102 | Sets the layer variables in the config file, after validating local layer paths. | ||
| 103 | bitbake must be a single BRBitbake instance | ||
| 104 | The layer paths must be in a list of BRLayer object | ||
| 105 | |||
| 106 | a word of attention: by convention, the first layer for any build will be poky! | ||
| 107 | """ | ||
| 108 | raise NotImplementedError("FIXME: Must override setLayers") | ||
| 109 | |||
| 110 | def getArtifact(self, path): | ||
| 111 | """ This call returns an artifact identified by the 'path'. How 'path' is interpreted as | ||
| 112 | up to the implementing BEC. The return MUST be a REST URL where a GET will actually return | ||
| 113 | the content of the artifact, e.g. for use as a "download link" in a web UI. | ||
| 114 | """ | ||
| 115 | raise NotImplementedError("Must return the REST URL of the artifact") | ||
| 116 | |||
| 117 | def triggerBuild(self, bitbake, layers, variables, targets): | ||
| 118 | raise NotImplementedError("Must override BE release") | ||
| 119 | |||
| 120 | class ShellCmdException(Exception): | ||
| 121 | pass | ||
| 122 | |||
| 123 | |||
| 124 | class BuildSetupException(Exception): | ||
| 125 | pass | ||
| 126 | |||
diff --git a/bitbake/lib/toaster/bldcontrol/localhostbecontroller.py b/bitbake/lib/toaster/bldcontrol/localhostbecontroller.py deleted file mode 100644 index 577e765f11..0000000000 --- a/bitbake/lib/toaster/bldcontrol/localhostbecontroller.py +++ /dev/null | |||
| @@ -1,516 +0,0 @@ | |||
| 1 | # | ||
| 2 | # BitBake Toaster Implementation | ||
| 3 | # | ||
| 4 | # Copyright (C) 2014 Intel Corporation | ||
| 5 | # | ||
| 6 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 7 | # | ||
| 8 | |||
| 9 | import os | ||
| 10 | import re | ||
| 11 | import shutil | ||
| 12 | import time | ||
| 13 | from bldcontrol.models import BuildEnvironment, BuildRequest, Build | ||
| 14 | from orm.models import CustomImageRecipe, Layer, Layer_Version, Project, ToasterSetting | ||
| 15 | from orm.models import signal_runbuilds | ||
| 16 | import subprocess | ||
| 17 | |||
| 18 | from toastermain import settings | ||
| 19 | |||
| 20 | from bldcontrol.bbcontroller import BuildEnvironmentController, ShellCmdException, BuildSetupException | ||
| 21 | |||
| 22 | import logging | ||
| 23 | logger = logging.getLogger("toaster") | ||
| 24 | |||
| 25 | install_dir = os.environ.get('TOASTER_DIR') | ||
| 26 | |||
| 27 | from pprint import pformat | ||
| 28 | |||
| 29 | class LocalhostBEController(BuildEnvironmentController): | ||
| 30 | """ Implementation of the BuildEnvironmentController for the localhost; | ||
| 31 | this controller manages the default build directory, | ||
| 32 | the server setup and system start and stop for the localhost-type build environment | ||
| 33 | |||
| 34 | """ | ||
| 35 | |||
| 36 | def __init__(self, be): | ||
| 37 | super(LocalhostBEController, self).__init__(be) | ||
| 38 | self.pokydirname = None | ||
| 39 | self.islayerset = False | ||
| 40 | |||
| 41 | def _shellcmd(self, command, cwd=None, nowait=False,env=None): | ||
| 42 | if cwd is None: | ||
| 43 | cwd = self.be.sourcedir | ||
| 44 | if env is None: | ||
| 45 | env=os.environ.copy() | ||
| 46 | |||
| 47 | logger.debug("lbc_shellcmd: (%s) %s" % (cwd, command)) | ||
| 48 | p = subprocess.Popen(command, cwd = cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) | ||
| 49 | if nowait: | ||
| 50 | return | ||
| 51 | (out,err) = p.communicate() | ||
| 52 | p.wait() | ||
| 53 | if p.returncode: | ||
| 54 | if len(err) == 0: | ||
| 55 | err = "command: %s \n%s" % (command, out) | ||
| 56 | else: | ||
| 57 | err = "command: %s \n%s" % (command, err) | ||
| 58 | logger.warning("localhostbecontroller: shellcmd error %s" % err) | ||
| 59 | raise ShellCmdException(err) | ||
| 60 | else: | ||
| 61 | logger.debug("localhostbecontroller: shellcmd success") | ||
| 62 | return out.decode('utf-8') | ||
| 63 | |||
| 64 | def getGitCloneDirectory(self, url, branch): | ||
| 65 | """Construct unique clone directory name out of url and branch.""" | ||
| 66 | if branch != "HEAD": | ||
| 67 | return "_toaster_clones/_%s_%s" % (re.sub('[:/@+%]', '_', url), branch) | ||
| 68 | |||
| 69 | # word of attention; this is a localhost-specific issue; only on the localhost we expect to have "HEAD" releases | ||
| 70 | # which _ALWAYS_ means the current poky checkout | ||
| 71 | from os.path import dirname as DN | ||
| 72 | local_checkout_path = DN(DN(DN(DN(DN(os.path.abspath(__file__)))))) | ||
| 73 | #logger.debug("localhostbecontroller: using HEAD checkout in %s" % local_checkout_path) | ||
| 74 | return local_checkout_path | ||
| 75 | |||
| 76 | def setCloneStatus(self,bitbake,status,total,current,repo_name): | ||
| 77 | bitbake.req.build.repos_cloned=current | ||
| 78 | bitbake.req.build.repos_to_clone=total | ||
| 79 | bitbake.req.build.progress_item=repo_name | ||
| 80 | bitbake.req.build.save() | ||
| 81 | |||
| 82 | def setLayers(self, bitbake, layers, targets): | ||
| 83 | """ a word of attention: by convention, the first layer for any build will be poky! """ | ||
| 84 | |||
| 85 | assert self.be.sourcedir is not None | ||
| 86 | |||
| 87 | layerlist = [] | ||
| 88 | nongitlayerlist = [] | ||
| 89 | layer_index = 0 | ||
| 90 | git_env = os.environ.copy() | ||
| 91 | # (note: add custom environment settings here) | ||
| 92 | |||
| 93 | # set layers in the layersource | ||
| 94 | |||
| 95 | # 1. get a list of repos with branches, and map dirpaths for each layer | ||
| 96 | gitrepos = {} | ||
| 97 | |||
| 98 | # if we're using a remotely fetched version of bitbake add its git | ||
| 99 | # details to the list of repos to clone | ||
| 100 | if bitbake.giturl and bitbake.commit: | ||
| 101 | gitrepos[(bitbake.giturl, bitbake.commit)] = [] | ||
| 102 | gitrepos[(bitbake.giturl, bitbake.commit)].append( | ||
| 103 | ("bitbake", bitbake.dirpath, 0)) | ||
| 104 | |||
| 105 | for layer in layers: | ||
| 106 | # We don't need to git clone the layer for the CustomImageRecipe | ||
| 107 | # as it's generated by us layer on if needed | ||
| 108 | if CustomImageRecipe.LAYER_NAME in layer.name: | ||
| 109 | continue | ||
| 110 | |||
| 111 | # If we have local layers then we don't need clone them | ||
| 112 | # For local layers giturl will be empty | ||
| 113 | if not layer.giturl: | ||
| 114 | nongitlayerlist.append( "%03d:%s" % (layer_index,layer.local_source_dir) ) | ||
| 115 | continue | ||
| 116 | |||
| 117 | if not (layer.giturl, layer.commit) in gitrepos: | ||
| 118 | gitrepos[(layer.giturl, layer.commit)] = [] | ||
| 119 | gitrepos[(layer.giturl, layer.commit)].append( (layer.name,layer.dirpath,layer_index) ) | ||
| 120 | layer_index += 1 | ||
| 121 | |||
| 122 | |||
| 123 | logger.debug("localhostbecontroller, our git repos are %s" % pformat(gitrepos)) | ||
| 124 | |||
| 125 | |||
| 126 | # 2. Note for future use if the current source directory is a | ||
| 127 | # checked-out git repos that could match a layer's vcs_url and therefore | ||
| 128 | # be used to speed up cloning (rather than fetching it again). | ||
| 129 | |||
| 130 | cached_layers = {} | ||
| 131 | |||
| 132 | try: | ||
| 133 | for remotes in self._shellcmd("git remote -v", self.be.sourcedir,env=git_env).split("\n"): | ||
| 134 | try: | ||
| 135 | remote = remotes.split("\t")[1].split(" ")[0] | ||
| 136 | if remote not in cached_layers: | ||
| 137 | cached_layers[remote] = self.be.sourcedir | ||
| 138 | except IndexError: | ||
| 139 | pass | ||
| 140 | except ShellCmdException: | ||
| 141 | # ignore any errors in collecting git remotes this is an optional | ||
| 142 | # step | ||
| 143 | pass | ||
| 144 | |||
| 145 | logger.info("Using pre-checked out source for layer %s", cached_layers) | ||
| 146 | |||
| 147 | # 3. checkout the repositories | ||
| 148 | clone_count=0 | ||
| 149 | clone_total=len(gitrepos.keys()) | ||
| 150 | self.setCloneStatus(bitbake,'Started',clone_total,clone_count,'') | ||
| 151 | for giturl, commit in gitrepos.keys(): | ||
| 152 | self.setCloneStatus(bitbake,'progress',clone_total,clone_count,gitrepos[(giturl, commit)][0][0]) | ||
| 153 | clone_count += 1 | ||
| 154 | |||
| 155 | localdirname = os.path.join(self.be.sourcedir, self.getGitCloneDirectory(giturl, commit)) | ||
| 156 | logger.debug("localhostbecontroller: giturl %s:%s checking out in current directory %s" % (giturl, commit, localdirname)) | ||
| 157 | |||
| 158 | # see if our directory is a git repository | ||
| 159 | if os.path.exists(localdirname): | ||
| 160 | try: | ||
| 161 | localremotes = self._shellcmd("git remote -v", | ||
| 162 | localdirname,env=git_env) | ||
| 163 | # NOTE: this nice-to-have check breaks when using git remaping to get past firewall | ||
| 164 | # Re-enable later with .gitconfig remapping checks | ||
| 165 | #if not giturl in localremotes and commit != 'HEAD': | ||
| 166 | # raise BuildSetupException("Existing git repository at %s, but with different remotes ('%s', expected '%s'). Toaster will not continue out of fear of damaging something." % (localdirname, ", ".join(localremotes.split("\n")), giturl)) | ||
| 167 | pass | ||
| 168 | except ShellCmdException: | ||
| 169 | # our localdirname might not be a git repository | ||
| 170 | #- that's fine | ||
| 171 | pass | ||
| 172 | else: | ||
| 173 | if giturl in cached_layers: | ||
| 174 | logger.debug("localhostbecontroller git-copying %s to %s" % (cached_layers[giturl], localdirname)) | ||
| 175 | self._shellcmd("git clone \"%s\" \"%s\"" % (cached_layers[giturl], localdirname),env=git_env) | ||
| 176 | self._shellcmd("git remote remove origin", localdirname,env=git_env) | ||
| 177 | self._shellcmd("git remote add origin \"%s\"" % giturl, localdirname,env=git_env) | ||
| 178 | else: | ||
| 179 | logger.debug("localhostbecontroller: cloning %s in %s" % (giturl, localdirname)) | ||
| 180 | self._shellcmd('git clone "%s" "%s"' % (giturl, localdirname),env=git_env) | ||
| 181 | |||
| 182 | # branch magic name "HEAD" will inhibit checkout | ||
| 183 | if commit != "HEAD": | ||
| 184 | logger.debug("localhostbecontroller: checking out commit %s to %s " % (commit, localdirname)) | ||
| 185 | ref = commit if re.match('^[a-fA-F0-9]+$', commit) else 'origin/%s' % commit | ||
| 186 | self._shellcmd('git fetch && git reset --hard "%s"' % ref, localdirname,env=git_env) | ||
| 187 | |||
| 188 | # take the localdirname as poky dir if we can find the oe-init-build-env | ||
| 189 | if self.pokydirname is None and os.path.exists(os.path.join(localdirname, "oe-init-build-env")): | ||
| 190 | logger.debug("localhostbecontroller: selected poky dir name %s" % localdirname) | ||
| 191 | self.pokydirname = localdirname | ||
| 192 | |||
| 193 | # make sure we have a working bitbake | ||
| 194 | if not os.path.exists(os.path.join(self.pokydirname, 'bitbake')): | ||
| 195 | logger.debug("localhostbecontroller: checking bitbake into the poky dirname %s " % self.pokydirname) | ||
| 196 | self._shellcmd("git clone -b \"%s\" \"%s\" \"%s\" " % (bitbake.commit, bitbake.giturl, os.path.join(self.pokydirname, 'bitbake')),env=git_env) | ||
| 197 | |||
| 198 | # verify our repositories | ||
| 199 | for name, dirpath, index in gitrepos[(giturl, commit)]: | ||
| 200 | localdirpath = os.path.join(localdirname, dirpath) | ||
| 201 | logger.debug("localhostbecontroller: localdirpath expects '%s'" % localdirpath) | ||
| 202 | if not os.path.exists(localdirpath): | ||
| 203 | raise BuildSetupException("Cannot find layer git path '%s' in checked out repository '%s:%s'. Exiting." % (localdirpath, giturl, commit)) | ||
| 204 | |||
| 205 | if name != "bitbake": | ||
| 206 | layerlist.append("%03d:%s" % (index,localdirpath.rstrip("/"))) | ||
| 207 | |||
| 208 | self.setCloneStatus(bitbake,'complete',clone_total,clone_count,'') | ||
| 209 | logger.debug("localhostbecontroller: current layer list %s " % pformat(layerlist)) | ||
| 210 | |||
| 211 | # Resolve self.pokydirname if not resolved yet, consider the scenario | ||
| 212 | # where all layers are local, that's the else clause | ||
| 213 | if self.pokydirname is None: | ||
| 214 | if os.path.exists(os.path.join(self.be.sourcedir, "oe-init-build-env")): | ||
| 215 | logger.debug("localhostbecontroller: selected poky dir name %s" % self.be.sourcedir) | ||
| 216 | self.pokydirname = self.be.sourcedir | ||
| 217 | else: | ||
| 218 | # Alternatively, scan local layers for relative "oe-init-build-env" location | ||
| 219 | for layer in layers: | ||
| 220 | if os.path.exists(os.path.join(layer.layer_version.layer.local_source_dir,"..","oe-init-build-env")): | ||
| 221 | logger.debug("localhostbecontroller, setting pokydirname to %s" % (layer.layer_version.layer.local_source_dir)) | ||
| 222 | self.pokydirname = os.path.join(layer.layer_version.layer.local_source_dir,"..") | ||
| 223 | break | ||
| 224 | else: | ||
| 225 | logger.error("pokydirname is not set, you will run into trouble!") | ||
| 226 | |||
| 227 | # 5. create custom layer and add custom recipes to it | ||
| 228 | for target in targets: | ||
| 229 | try: | ||
| 230 | customrecipe = CustomImageRecipe.objects.get( | ||
| 231 | name=target.target, | ||
| 232 | project=bitbake.req.project) | ||
| 233 | |||
| 234 | custom_layer_path = self.setup_custom_image_recipe( | ||
| 235 | customrecipe, layers) | ||
| 236 | |||
| 237 | if os.path.isdir(custom_layer_path): | ||
| 238 | layerlist.append("%03d:%s" % (layer_index,custom_layer_path)) | ||
| 239 | |||
| 240 | except CustomImageRecipe.DoesNotExist: | ||
| 241 | continue # not a custom recipe, skip | ||
| 242 | |||
| 243 | layerlist.extend(nongitlayerlist) | ||
| 244 | logger.debug("\n\nset layers gives this list %s" % pformat(layerlist)) | ||
| 245 | self.islayerset = True | ||
| 246 | |||
| 247 | # restore the order of layer list for bblayers.conf | ||
| 248 | layerlist.sort() | ||
| 249 | sorted_layerlist = [l[4:] for l in layerlist] | ||
| 250 | return sorted_layerlist | ||
| 251 | |||
| 252 | def setup_custom_image_recipe(self, customrecipe, layers): | ||
| 253 | """ Set up toaster-custom-images layer and recipe files """ | ||
| 254 | layerpath = os.path.join(self.be.builddir, | ||
| 255 | CustomImageRecipe.LAYER_NAME) | ||
| 256 | |||
| 257 | # create directory structure | ||
| 258 | for name in ("conf", "recipes"): | ||
| 259 | path = os.path.join(layerpath, name) | ||
| 260 | if not os.path.isdir(path): | ||
| 261 | os.makedirs(path) | ||
| 262 | |||
| 263 | # create layer.conf | ||
| 264 | config = os.path.join(layerpath, "conf", "layer.conf") | ||
| 265 | if not os.path.isfile(config): | ||
| 266 | with open(config, "w") as conf: | ||
| 267 | conf.write('BBPATH .= ":${LAYERDIR}"\nBBFILES += "${LAYERDIR}/recipes/*.bb"\n') | ||
| 268 | |||
| 269 | # Update the Layer_Version dirpath that has our base_recipe in | ||
| 270 | # to be able to read the base recipe to then generate the | ||
| 271 | # custom recipe. | ||
| 272 | br_layer_base_recipe = layers.get( | ||
| 273 | layer_version=customrecipe.base_recipe.layer_version) | ||
| 274 | |||
| 275 | # If the layer is one that we've cloned we know where it lives | ||
| 276 | if br_layer_base_recipe.giturl and br_layer_base_recipe.commit: | ||
| 277 | layer_path = self.getGitCloneDirectory( | ||
| 278 | br_layer_base_recipe.giturl, | ||
| 279 | br_layer_base_recipe.commit) | ||
| 280 | # Otherwise it's a local layer | ||
| 281 | elif br_layer_base_recipe.local_source_dir: | ||
| 282 | layer_path = br_layer_base_recipe.local_source_dir | ||
| 283 | else: | ||
| 284 | logger.error("Unable to workout the dir path for the custom" | ||
| 285 | " image recipe") | ||
| 286 | |||
| 287 | br_layer_base_dirpath = os.path.join( | ||
| 288 | self.be.sourcedir, | ||
| 289 | layer_path, | ||
| 290 | customrecipe.base_recipe.layer_version.dirpath) | ||
| 291 | |||
| 292 | customrecipe.base_recipe.layer_version.dirpath = br_layer_base_dirpath | ||
| 293 | |||
| 294 | customrecipe.base_recipe.layer_version.save() | ||
| 295 | |||
| 296 | # create recipe | ||
| 297 | recipe_path = os.path.join(layerpath, "recipes", "%s.bb" % | ||
| 298 | customrecipe.name) | ||
| 299 | with open(recipe_path, "w") as recipef: | ||
| 300 | recipef.write(customrecipe.generate_recipe_file_contents()) | ||
| 301 | |||
| 302 | # Update the layer and recipe objects | ||
| 303 | customrecipe.layer_version.dirpath = layerpath | ||
| 304 | customrecipe.layer_version.layer.local_source_dir = layerpath | ||
| 305 | customrecipe.layer_version.layer.save() | ||
| 306 | customrecipe.layer_version.save() | ||
| 307 | |||
| 308 | customrecipe.file_path = recipe_path | ||
| 309 | customrecipe.save() | ||
| 310 | |||
| 311 | return layerpath | ||
| 312 | |||
| 313 | |||
| 314 | def readServerLogFile(self): | ||
| 315 | return open(os.path.join(self.be.builddir, "toaster_server.log"), "r").read() | ||
| 316 | |||
| 317 | |||
| 318 | def triggerBuild(self, bitbake, layers, variables, targets, brbe): | ||
| 319 | layers = self.setLayers(bitbake, layers, targets) | ||
| 320 | is_merged_attr = bitbake.req.project.merged_attr | ||
| 321 | |||
| 322 | git_env = os.environ.copy() | ||
| 323 | # (note: add custom environment settings here) | ||
| 324 | try: | ||
| 325 | # insure that the project init/build uses the selected bitbake, and not Toaster's | ||
| 326 | del git_env['TEMPLATECONF'] | ||
| 327 | del git_env['BBBASEDIR'] | ||
| 328 | del git_env['BUILDDIR'] | ||
| 329 | except KeyError: | ||
| 330 | pass | ||
| 331 | |||
| 332 | # init build environment from the clone | ||
| 333 | if bitbake.req.project.builddir: | ||
| 334 | builddir = bitbake.req.project.builddir | ||
| 335 | else: | ||
| 336 | builddir = '%s-toaster-%d' % (self.be.builddir, bitbake.req.project.id) | ||
| 337 | oe_init = os.path.join(self.pokydirname, 'oe-init-build-env') | ||
| 338 | # init build environment | ||
| 339 | try: | ||
| 340 | custom_script = ToasterSetting.objects.get(name="CUSTOM_BUILD_INIT_SCRIPT").value | ||
| 341 | custom_script = custom_script.replace("%BUILDDIR%" ,builddir) | ||
| 342 | self._shellcmd("bash -c 'source %s'" % (custom_script),env=git_env) | ||
| 343 | except ToasterSetting.DoesNotExist: | ||
| 344 | self._shellcmd("bash -c 'source %s %s'" % (oe_init, builddir), | ||
| 345 | self.be.sourcedir,env=git_env) | ||
| 346 | |||
| 347 | # update bblayers.conf | ||
| 348 | if not is_merged_attr: | ||
| 349 | bblconfpath = os.path.join(builddir, "conf/toaster-bblayers.conf") | ||
| 350 | with open(bblconfpath, 'w') as bblayers: | ||
| 351 | bblayers.write('# line added by toaster build control\n' | ||
| 352 | 'BBLAYERS = "%s"' % ' '.join(layers)) | ||
| 353 | |||
| 354 | # write configuration file | ||
| 355 | confpath = os.path.join(builddir, 'conf/toaster.conf') | ||
| 356 | with open(confpath, 'w') as conf: | ||
| 357 | for var in variables: | ||
| 358 | conf.write('%s="%s"\n' % (var.name, var.value)) | ||
| 359 | conf.write('INHERIT+="toaster buildhistory"') | ||
| 360 | else: | ||
| 361 | # Append the Toaster-specific values directly to the bblayers.conf | ||
| 362 | bblconfpath = os.path.join(builddir, "conf/bblayers.conf") | ||
| 363 | bblconfpath_save = os.path.join(builddir, "conf/bblayers.conf.save") | ||
| 364 | shutil.copyfile(bblconfpath, bblconfpath_save) | ||
| 365 | with open(bblconfpath) as bblayers: | ||
| 366 | content = bblayers.readlines() | ||
| 367 | do_write = True | ||
| 368 | was_toaster = False | ||
| 369 | with open(bblconfpath,'w') as bblayers: | ||
| 370 | for line in content: | ||
| 371 | #line = line.strip('\n') | ||
| 372 | if 'TOASTER_CONFIG_PROLOG' in line: | ||
| 373 | do_write = False | ||
| 374 | was_toaster = True | ||
| 375 | elif 'TOASTER_CONFIG_EPILOG' in line: | ||
| 376 | do_write = True | ||
| 377 | elif do_write: | ||
| 378 | bblayers.write(line) | ||
| 379 | if not was_toaster: | ||
| 380 | bblayers.write('\n') | ||
| 381 | bblayers.write('#=== TOASTER_CONFIG_PROLOG ===\n') | ||
| 382 | bblayers.write('BBLAYERS = "\\\n') | ||
| 383 | for layer in layers: | ||
| 384 | bblayers.write(' %s \\\n' % layer) | ||
| 385 | bblayers.write(' "\n') | ||
| 386 | bblayers.write('#=== TOASTER_CONFIG_EPILOG ===\n') | ||
| 387 | # Append the Toaster-specific values directly to the local.conf | ||
| 388 | bbconfpath = os.path.join(builddir, "conf/local.conf") | ||
| 389 | bbconfpath_save = os.path.join(builddir, "conf/local.conf.save") | ||
| 390 | shutil.copyfile(bbconfpath, bbconfpath_save) | ||
| 391 | with open(bbconfpath) as f: | ||
| 392 | content = f.readlines() | ||
| 393 | do_write = True | ||
| 394 | was_toaster = False | ||
| 395 | with open(bbconfpath,'w') as conf: | ||
| 396 | for line in content: | ||
| 397 | #line = line.strip('\n') | ||
| 398 | if 'TOASTER_CONFIG_PROLOG' in line: | ||
| 399 | do_write = False | ||
| 400 | was_toaster = True | ||
| 401 | elif 'TOASTER_CONFIG_EPILOG' in line: | ||
| 402 | do_write = True | ||
| 403 | elif do_write: | ||
| 404 | conf.write(line) | ||
| 405 | if not was_toaster: | ||
| 406 | conf.write('\n') | ||
| 407 | conf.write('#=== TOASTER_CONFIG_PROLOG ===\n') | ||
| 408 | for var in variables: | ||
| 409 | if (not var.name.startswith("INTERNAL_")) and (not var.name == "BBLAYERS"): | ||
| 410 | conf.write('%s="%s"\n' % (var.name, var.value)) | ||
| 411 | conf.write('#=== TOASTER_CONFIG_EPILOG ===\n') | ||
| 412 | |||
| 413 | # If 'target' is just the project preparation target, then we are done | ||
| 414 | for target in targets: | ||
| 415 | if "_PROJECT_PREPARE_" == target.target: | ||
| 416 | logger.debug('localhostbecontroller: Project has been prepared. Done.') | ||
| 417 | # Update the Build Request and release the build environment | ||
| 418 | bitbake.req.state = BuildRequest.REQ_COMPLETED | ||
| 419 | bitbake.req.save() | ||
| 420 | self.be.lock = BuildEnvironment.LOCK_FREE | ||
| 421 | self.be.save() | ||
| 422 | # Close the project build and progress bar | ||
| 423 | bitbake.req.build.outcome = Build.SUCCEEDED | ||
| 424 | bitbake.req.build.save() | ||
| 425 | # Update the project status | ||
| 426 | bitbake.req.project.set_variable(Project.PROJECT_SPECIFIC_STATUS,Project.PROJECT_SPECIFIC_CLONING_SUCCESS) | ||
| 427 | signal_runbuilds() | ||
| 428 | return | ||
| 429 | |||
| 430 | # clean the Toaster to build environment | ||
| 431 | env_clean = 'unset BBPATH;' # clean BBPATH for <= YP-2.4.0 | ||
| 432 | |||
| 433 | # run bitbake server from the clone if available | ||
| 434 | # otherwise pick it from the PATH | ||
| 435 | bitbake = os.path.join(self.pokydirname, 'bitbake', 'bin', 'bitbake') | ||
| 436 | if not os.path.exists(bitbake): | ||
| 437 | logger.info("Bitbake not available under %s, will try to use it from PATH" % | ||
| 438 | self.pokydirname) | ||
| 439 | for path in os.environ["PATH"].split(os.pathsep): | ||
| 440 | if os.path.exists(os.path.join(path, 'bitbake')): | ||
| 441 | bitbake = os.path.join(path, 'bitbake') | ||
| 442 | break | ||
| 443 | else: | ||
| 444 | logger.error("Looks like Bitbake is not available, please fix your environment") | ||
| 445 | |||
| 446 | toasterlayers = os.path.join(builddir,"conf/toaster-bblayers.conf") | ||
| 447 | if not is_merged_attr: | ||
| 448 | self._shellcmd('%s bash -c \"source %s %s; BITBAKE_UI="knotty" %s --read %s --read %s ' | ||
| 449 | '--server-only -B 0.0.0.0:0\"' % (env_clean, oe_init, | ||
| 450 | builddir, bitbake, confpath, toasterlayers), self.be.sourcedir) | ||
| 451 | else: | ||
| 452 | self._shellcmd('%s bash -c \"source %s %s; BITBAKE_UI="knotty" %s ' | ||
| 453 | '--server-only -B 0.0.0.0:0\"' % (env_clean, oe_init, | ||
| 454 | builddir, bitbake), self.be.sourcedir) | ||
| 455 | |||
| 456 | # read port number from bitbake.lock | ||
| 457 | self.be.bbport = -1 | ||
| 458 | bblock = os.path.join(builddir, 'bitbake.lock') | ||
| 459 | # allow 10 seconds for bb lock file to appear but also be populated | ||
| 460 | for lock_check in range(10): | ||
| 461 | if not os.path.exists(bblock): | ||
| 462 | logger.debug("localhostbecontroller: waiting for bblock file to appear") | ||
| 463 | time.sleep(1) | ||
| 464 | continue | ||
| 465 | if 10 < os.stat(bblock).st_size: | ||
| 466 | break | ||
| 467 | logger.debug("localhostbecontroller: waiting for bblock content to appear") | ||
| 468 | time.sleep(1) | ||
| 469 | else: | ||
| 470 | raise BuildSetupException("Cannot find bitbake server lock file '%s'. Exiting." % bblock) | ||
| 471 | |||
| 472 | with open(bblock) as fplock: | ||
| 473 | for line in fplock: | ||
| 474 | if ":" in line: | ||
| 475 | self.be.bbport = line.split(":")[-1].strip() | ||
| 476 | logger.debug("localhostbecontroller: bitbake port %s", self.be.bbport) | ||
| 477 | break | ||
| 478 | |||
| 479 | if -1 == self.be.bbport: | ||
| 480 | raise BuildSetupException("localhostbecontroller: can't read bitbake port from %s" % bblock) | ||
| 481 | |||
| 482 | self.be.bbaddress = "localhost" | ||
| 483 | self.be.bbstate = BuildEnvironment.SERVER_STARTED | ||
| 484 | self.be.lock = BuildEnvironment.LOCK_RUNNING | ||
| 485 | self.be.save() | ||
| 486 | |||
| 487 | bbtargets = '' | ||
| 488 | for target in targets: | ||
| 489 | task = target.task | ||
| 490 | if task: | ||
| 491 | if not task.startswith('do_'): | ||
| 492 | task = 'do_' + task | ||
| 493 | task = ':%s' % task | ||
| 494 | bbtargets += '%s%s ' % (target.target, task) | ||
| 495 | |||
| 496 | # run build with local bitbake. stop the server after the build. | ||
| 497 | log = os.path.join(builddir, 'toaster_ui.log') | ||
| 498 | local_bitbake = os.path.join(os.path.dirname(os.getenv('BBBASEDIR')), | ||
| 499 | 'bitbake') | ||
| 500 | if not is_merged_attr: | ||
| 501 | self._shellcmd(['%s bash -c \"(TOASTER_BRBE="%s" BBSERVER="0.0.0.0:%s" ' | ||
| 502 | '%s %s -u toasterui --read %s --read %s --token="" >>%s 2>&1;' | ||
| 503 | 'BITBAKE_UI="knotty" BBSERVER=0.0.0.0:%s %s -m)&\"' \ | ||
| 504 | % (env_clean, brbe, self.be.bbport, local_bitbake, bbtargets, confpath, toasterlayers, log, | ||
| 505 | self.be.bbport, bitbake,)], | ||
| 506 | builddir, nowait=True) | ||
| 507 | else: | ||
| 508 | self._shellcmd(['%s bash -c \"(TOASTER_BRBE="%s" BBSERVER="0.0.0.0:%s" ' | ||
| 509 | '%s %s -u toasterui --token="" >>%s 2>&1;' | ||
| 510 | 'BITBAKE_UI="knotty" BBSERVER=0.0.0.0:%s %s -m)&\"' \ | ||
| 511 | % (env_clean, brbe, self.be.bbport, local_bitbake, bbtargets, log, | ||
| 512 | self.be.bbport, bitbake,)], | ||
| 513 | builddir, nowait=True) | ||
| 514 | |||
| 515 | logger.debug('localhostbecontroller: Build launched, exiting. ' | ||
| 516 | 'Follow build logs at %s' % log) | ||
diff --git a/bitbake/lib/toaster/bldcontrol/management/__init__.py b/bitbake/lib/toaster/bldcontrol/management/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/bitbake/lib/toaster/bldcontrol/management/__init__.py +++ /dev/null | |||
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 | |||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0001_initial.py b/bitbake/lib/toaster/bldcontrol/migrations/0001_initial.py deleted file mode 100644 index 7ee19f4745..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0001_initial.py +++ /dev/null | |||
| @@ -1,113 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('orm', '0001_initial'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.CreateModel( | ||
| 15 | name='BRBitbake', | ||
| 16 | fields=[ | ||
| 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 18 | ('giturl', models.CharField(max_length=254)), | ||
| 19 | ('commit', models.CharField(max_length=254)), | ||
| 20 | ('dirpath', models.CharField(max_length=254)), | ||
| 21 | ], | ||
| 22 | ), | ||
| 23 | migrations.CreateModel( | ||
| 24 | name='BRError', | ||
| 25 | fields=[ | ||
| 26 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 27 | ('errtype', models.CharField(max_length=100)), | ||
| 28 | ('errmsg', models.TextField()), | ||
| 29 | ('traceback', models.TextField()), | ||
| 30 | ], | ||
| 31 | ), | ||
| 32 | migrations.CreateModel( | ||
| 33 | name='BRLayer', | ||
| 34 | fields=[ | ||
| 35 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 36 | ('name', models.CharField(max_length=100)), | ||
| 37 | ('giturl', models.CharField(max_length=254)), | ||
| 38 | ('commit', models.CharField(max_length=254)), | ||
| 39 | ('dirpath', models.CharField(max_length=254)), | ||
| 40 | ('layer_version', models.ForeignKey(to='orm.Layer_Version', null=True, on_delete=models.CASCADE)), | ||
| 41 | ], | ||
| 42 | ), | ||
| 43 | migrations.CreateModel( | ||
| 44 | name='BRTarget', | ||
| 45 | fields=[ | ||
| 46 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 47 | ('target', models.CharField(max_length=100)), | ||
| 48 | ('task', models.CharField(max_length=100, null=True)), | ||
| 49 | ], | ||
| 50 | ), | ||
| 51 | migrations.CreateModel( | ||
| 52 | name='BRVariable', | ||
| 53 | fields=[ | ||
| 54 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 55 | ('name', models.CharField(max_length=100)), | ||
| 56 | ('value', models.TextField(blank=True)), | ||
| 57 | ], | ||
| 58 | ), | ||
| 59 | migrations.CreateModel( | ||
| 60 | name='BuildEnvironment', | ||
| 61 | fields=[ | ||
| 62 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 63 | ('address', models.CharField(max_length=254)), | ||
| 64 | ('betype', models.IntegerField(choices=[(0, b'local'), (1, b'ssh')])), | ||
| 65 | ('bbaddress', models.CharField(max_length=254, blank=True)), | ||
| 66 | ('bbport', models.IntegerField(default=-1)), | ||
| 67 | ('bbtoken', models.CharField(max_length=126, blank=True)), | ||
| 68 | ('bbstate', models.IntegerField(default=0, choices=[(0, b'stopped'), (1, b'started')])), | ||
| 69 | ('sourcedir', models.CharField(max_length=512, blank=True)), | ||
| 70 | ('builddir', models.CharField(max_length=512, blank=True)), | ||
| 71 | ('lock', models.IntegerField(default=0, choices=[(0, b'free'), (1, b'lock'), (2, b'running')])), | ||
| 72 | ('created', models.DateTimeField(auto_now_add=True)), | ||
| 73 | ('updated', models.DateTimeField(auto_now=True)), | ||
| 74 | ], | ||
| 75 | ), | ||
| 76 | migrations.CreateModel( | ||
| 77 | name='BuildRequest', | ||
| 78 | fields=[ | ||
| 79 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
| 80 | ('state', models.IntegerField(default=0, choices=[(0, b'created'), (1, b'queued'), (2, b'in progress'), (3, b'completed'), (4, b'failed'), (5, b'deleted'), (6, b'archive')])), | ||
| 81 | ('created', models.DateTimeField(auto_now_add=True)), | ||
| 82 | ('updated', models.DateTimeField(auto_now=True)), | ||
| 83 | ('build', models.OneToOneField(null=True, to='orm.Build', on_delete=models.CASCADE)), | ||
| 84 | ('environment', models.ForeignKey(to='bldcontrol.BuildEnvironment', null=True, on_delete=models.CASCADE)), | ||
| 85 | ('project', models.ForeignKey(to='orm.Project', on_delete=models.CASCADE)), | ||
| 86 | ], | ||
| 87 | ), | ||
| 88 | migrations.AddField( | ||
| 89 | model_name='brvariable', | ||
| 90 | name='req', | ||
| 91 | field=models.ForeignKey(to='bldcontrol.BuildRequest', on_delete=models.CASCADE), | ||
| 92 | ), | ||
| 93 | migrations.AddField( | ||
| 94 | model_name='brtarget', | ||
| 95 | name='req', | ||
| 96 | field=models.ForeignKey(to='bldcontrol.BuildRequest', on_delete=models.CASCADE), | ||
| 97 | ), | ||
| 98 | migrations.AddField( | ||
| 99 | model_name='brlayer', | ||
| 100 | name='req', | ||
| 101 | field=models.ForeignKey(to='bldcontrol.BuildRequest', on_delete=models.CASCADE), | ||
| 102 | ), | ||
| 103 | migrations.AddField( | ||
| 104 | model_name='brerror', | ||
| 105 | name='req', | ||
| 106 | field=models.ForeignKey(to='bldcontrol.BuildRequest', on_delete=models.CASCADE), | ||
| 107 | ), | ||
| 108 | migrations.AddField( | ||
| 109 | model_name='brbitbake', | ||
| 110 | name='req', | ||
| 111 | field=models.OneToOneField(to='bldcontrol.BuildRequest', on_delete=models.CASCADE), | ||
| 112 | ), | ||
| 113 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0002_auto_20160120_1250.py b/bitbake/lib/toaster/bldcontrol/migrations/0002_auto_20160120_1250.py deleted file mode 100644 index 0c2475aba5..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0002_auto_20160120_1250.py +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('bldcontrol', '0001_initial'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.AlterField( | ||
| 15 | model_name='buildenvironment', | ||
| 16 | name='betype', | ||
| 17 | field=models.IntegerField(choices=[(0, b'local')]), | ||
| 18 | ), | ||
| 19 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0003_add_cancelling_state.py b/bitbake/lib/toaster/bldcontrol/migrations/0003_add_cancelling_state.py deleted file mode 100644 index eec9216ca3..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0003_add_cancelling_state.py +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('bldcontrol', '0002_auto_20160120_1250'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.AlterField( | ||
| 15 | model_name='buildrequest', | ||
| 16 | name='state', | ||
| 17 | field=models.IntegerField(default=0, choices=[(0, b'created'), (1, b'queued'), (2, b'in progress'), (3, b'completed'), (4, b'failed'), (5, b'deleted'), (6, b'cancelling'), (7, b'archive')]), | ||
| 18 | ), | ||
| 19 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160523_1446.py b/bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160523_1446.py deleted file mode 100644 index 3d90629541..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160523_1446.py +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('bldcontrol', '0003_add_cancelling_state'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.AlterField( | ||
| 15 | model_name='buildenvironment', | ||
| 16 | name='bbstate', | ||
| 17 | field=models.IntegerField(default=0, choices=[(0, 'stopped'), (1, 'started')]), | ||
| 18 | ), | ||
| 19 | migrations.AlterField( | ||
| 20 | model_name='buildenvironment', | ||
| 21 | name='betype', | ||
| 22 | field=models.IntegerField(choices=[(0, 'local')]), | ||
| 23 | ), | ||
| 24 | migrations.AlterField( | ||
| 25 | model_name='buildenvironment', | ||
| 26 | name='lock', | ||
| 27 | field=models.IntegerField(default=0, choices=[(0, 'free'), (1, 'lock'), (2, 'running')]), | ||
| 28 | ), | ||
| 29 | migrations.AlterField( | ||
| 30 | model_name='buildrequest', | ||
| 31 | name='state', | ||
| 32 | field=models.IntegerField(default=0, choices=[(0, 'created'), (1, 'queued'), (2, 'in progress'), (3, 'completed'), (4, 'failed'), (5, 'deleted'), (6, 'cancelling'), (7, 'archive')]), | ||
| 33 | ), | ||
| 34 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0005_reorder_buildrequest_states.py b/bitbake/lib/toaster/bldcontrol/migrations/0005_reorder_buildrequest_states.py deleted file mode 100644 index 4bb9517768..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0005_reorder_buildrequest_states.py +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('bldcontrol', '0004_auto_20160523_1446'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.AlterField( | ||
| 15 | model_name='buildrequest', | ||
| 16 | name='state', | ||
| 17 | field=models.IntegerField(choices=[(0, 'created'), (1, 'queued'), (2, 'in progress'), (3, 'failed'), (4, 'deleted'), (5, 'cancelling'), (6, 'completed'), (7, 'archive')], default=0), | ||
| 18 | ), | ||
| 19 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0006_brlayer_local_source_dir.py b/bitbake/lib/toaster/bldcontrol/migrations/0006_brlayer_local_source_dir.py deleted file mode 100644 index 2460002f07..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0006_brlayer_local_source_dir.py +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('bldcontrol', '0005_reorder_buildrequest_states'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.AddField( | ||
| 15 | model_name='brlayer', | ||
| 16 | name='local_source_dir', | ||
| 17 | field=models.CharField(max_length=254, null=True), | ||
| 18 | ), | ||
| 19 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0007_brlayers_optional_gitinfo.py b/bitbake/lib/toaster/bldcontrol/migrations/0007_brlayers_optional_gitinfo.py deleted file mode 100644 index 4be42a4cf9..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0007_brlayers_optional_gitinfo.py +++ /dev/null | |||
| @@ -1,29 +0,0 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from __future__ import unicode_literals | ||
| 3 | |||
| 4 | from django.db import migrations, models | ||
| 5 | |||
| 6 | |||
| 7 | class Migration(migrations.Migration): | ||
| 8 | |||
| 9 | dependencies = [ | ||
| 10 | ('bldcontrol', '0006_brlayer_local_source_dir'), | ||
| 11 | ] | ||
| 12 | |||
| 13 | operations = [ | ||
| 14 | migrations.AlterField( | ||
| 15 | model_name='brlayer', | ||
| 16 | name='commit', | ||
| 17 | field=models.CharField(max_length=254, null=True), | ||
| 18 | ), | ||
| 19 | migrations.AlterField( | ||
| 20 | model_name='brlayer', | ||
| 21 | name='dirpath', | ||
| 22 | field=models.CharField(max_length=254, null=True), | ||
| 23 | ), | ||
| 24 | migrations.AlterField( | ||
| 25 | model_name='brlayer', | ||
| 26 | name='giturl', | ||
| 27 | field=models.CharField(max_length=254, null=True), | ||
| 28 | ), | ||
| 29 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0008_models_bigautofield.py b/bitbake/lib/toaster/bldcontrol/migrations/0008_models_bigautofield.py deleted file mode 100644 index 45b477d02c..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/0008_models_bigautofield.py +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | # Generated by Django 3.2.12 on 2022-03-06 03:28 | ||
| 2 | |||
| 3 | from django.db import migrations, models | ||
| 4 | |||
| 5 | |||
| 6 | class Migration(migrations.Migration): | ||
| 7 | |||
| 8 | dependencies = [ | ||
| 9 | ('bldcontrol', '0007_brlayers_optional_gitinfo'), | ||
| 10 | ] | ||
| 11 | |||
| 12 | operations = [ | ||
| 13 | migrations.AlterField( | ||
| 14 | model_name='brbitbake', | ||
| 15 | name='id', | ||
| 16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 17 | ), | ||
| 18 | migrations.AlterField( | ||
| 19 | model_name='brerror', | ||
| 20 | name='id', | ||
| 21 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 22 | ), | ||
| 23 | migrations.AlterField( | ||
| 24 | model_name='brlayer', | ||
| 25 | name='id', | ||
| 26 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 27 | ), | ||
| 28 | migrations.AlterField( | ||
| 29 | model_name='brtarget', | ||
| 30 | name='id', | ||
| 31 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 32 | ), | ||
| 33 | migrations.AlterField( | ||
| 34 | model_name='brvariable', | ||
| 35 | name='id', | ||
| 36 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 37 | ), | ||
| 38 | migrations.AlterField( | ||
| 39 | model_name='buildenvironment', | ||
| 40 | name='id', | ||
| 41 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 42 | ), | ||
| 43 | migrations.AlterField( | ||
| 44 | model_name='buildrequest', | ||
| 45 | name='id', | ||
| 46 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), | ||
| 47 | ), | ||
| 48 | ] | ||
diff --git a/bitbake/lib/toaster/bldcontrol/migrations/__init__.py b/bitbake/lib/toaster/bldcontrol/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/bitbake/lib/toaster/bldcontrol/migrations/__init__.py +++ /dev/null | |||
diff --git a/bitbake/lib/toaster/bldcontrol/models.py b/bitbake/lib/toaster/bldcontrol/models.py deleted file mode 100644 index 42750e7180..0000000000 --- a/bitbake/lib/toaster/bldcontrol/models.py +++ /dev/null | |||
| @@ -1,165 +0,0 @@ | |||
| 1 | # | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 3 | # | ||
| 4 | |||
| 5 | from __future__ import unicode_literals | ||
| 6 | from django.db import models | ||
| 7 | from django.utils.encoding import force_str | ||
| 8 | from orm.models import Project, Build, Layer_Version | ||
| 9 | |||
| 10 | import logging | ||
| 11 | logger = logging.getLogger("toaster") | ||
| 12 | # a BuildEnvironment is the equivalent of the "build/" directory on the localhost | ||
| 13 | class BuildEnvironment(models.Model): | ||
| 14 | SERVER_STOPPED = 0 | ||
| 15 | SERVER_STARTED = 1 | ||
| 16 | SERVER_STATE = ( | ||
| 17 | (SERVER_STOPPED, "stopped"), | ||
| 18 | (SERVER_STARTED, "started"), | ||
| 19 | ) | ||
| 20 | |||
| 21 | TYPE_LOCAL = 0 | ||
| 22 | TYPE = ( | ||
| 23 | (TYPE_LOCAL, "local"), | ||
| 24 | ) | ||
| 25 | |||
| 26 | LOCK_FREE = 0 | ||
| 27 | LOCK_LOCK = 1 | ||
| 28 | LOCK_RUNNING = 2 | ||
| 29 | LOCK_STATE = ( | ||
| 30 | (LOCK_FREE, "free"), | ||
| 31 | (LOCK_LOCK, "lock"), | ||
| 32 | (LOCK_RUNNING, "running"), | ||
| 33 | ) | ||
| 34 | |||
| 35 | address = models.CharField(max_length = 254) | ||
| 36 | betype = models.IntegerField(choices = TYPE) | ||
| 37 | bbaddress = models.CharField(max_length = 254, blank = True) | ||
| 38 | bbport = models.IntegerField(default = -1) | ||
| 39 | bbtoken = models.CharField(max_length = 126, blank = True) | ||
| 40 | bbstate = models.IntegerField(choices = SERVER_STATE, default = SERVER_STOPPED) | ||
| 41 | sourcedir = models.CharField(max_length = 512, blank = True) | ||
| 42 | builddir = models.CharField(max_length = 512, blank = True) | ||
| 43 | lock = models.IntegerField(choices = LOCK_STATE, default = LOCK_FREE) | ||
| 44 | created = models.DateTimeField(auto_now_add = True) | ||
| 45 | updated = models.DateTimeField(auto_now = True) | ||
| 46 | |||
| 47 | def get_artifact(self, path): | ||
| 48 | if self.betype == BuildEnvironment.TYPE_LOCAL: | ||
| 49 | return open(path, "r") | ||
| 50 | raise NotImplementedError("FIXME: artifact download not implemented "\ | ||
| 51 | "for build environment type %s" % \ | ||
| 52 | self.get_betype_display()) | ||
| 53 | |||
| 54 | def has_artifact(self, path): | ||
| 55 | import os | ||
| 56 | if self.betype == BuildEnvironment.TYPE_LOCAL: | ||
| 57 | return os.path.exists(path) | ||
| 58 | raise NotImplementedError("FIXME: has artifact not implemented for "\ | ||
| 59 | "build environment type %s" % \ | ||
| 60 | self.get_betype_display()) | ||
| 61 | |||
| 62 | # a BuildRequest is a request that the scheduler will build using a BuildEnvironment | ||
| 63 | # the build request queue is the table itself, ordered by state | ||
| 64 | |||
| 65 | class BuildRequest(models.Model): | ||
| 66 | REQ_CREATED = 0 | ||
| 67 | REQ_QUEUED = 1 | ||
| 68 | REQ_INPROGRESS = 2 | ||
| 69 | REQ_FAILED = 3 | ||
| 70 | REQ_DELETED = 4 | ||
| 71 | REQ_CANCELLING = 5 | ||
| 72 | REQ_COMPLETED = 6 | ||
| 73 | REQ_ARCHIVE = 7 | ||
| 74 | |||
| 75 | REQUEST_STATE = ( | ||
| 76 | (REQ_CREATED, "created"), | ||
| 77 | (REQ_QUEUED, "queued"), | ||
| 78 | (REQ_INPROGRESS, "in progress"), | ||
| 79 | (REQ_FAILED, "failed"), | ||
| 80 | (REQ_DELETED, "deleted"), | ||
| 81 | (REQ_CANCELLING, "cancelling"), | ||
| 82 | (REQ_COMPLETED, "completed"), | ||
| 83 | (REQ_ARCHIVE, "archive"), | ||
| 84 | ) | ||
| 85 | |||
| 86 | search_allowed_fields = ("brtarget__target", "build__project__name") | ||
| 87 | |||
| 88 | project = models.ForeignKey(Project, on_delete=models.CASCADE) | ||
| 89 | build = models.OneToOneField(Build, on_delete=models.CASCADE, null = True) # TODO: toasterui should set this when Build is created | ||
| 90 | environment = models.ForeignKey(BuildEnvironment, on_delete=models.CASCADE, null = True) | ||
| 91 | state = models.IntegerField(choices = REQUEST_STATE, default = REQ_CREATED) | ||
| 92 | created = models.DateTimeField(auto_now_add = True) | ||
| 93 | updated = models.DateTimeField(auto_now = True) | ||
| 94 | |||
| 95 | def __init__(self, *args, **kwargs): | ||
| 96 | super(BuildRequest, self).__init__(*args, **kwargs) | ||
| 97 | # Save the old state in case it's about to be modified | ||
| 98 | self.old_state = self.state | ||
| 99 | |||
| 100 | def save(self, *args, **kwargs): | ||
| 101 | # Check that the state we're trying to set is not going backwards | ||
| 102 | # e.g. from REQ_FAILED to REQ_INPROGRESS | ||
| 103 | if self.old_state != self.state and self.old_state > self.state: | ||
| 104 | logger.warning("Invalid state change requested: " | ||
| 105 | "Cannot go from %s to %s - ignoring request" % | ||
| 106 | (BuildRequest.REQUEST_STATE[self.old_state][1], | ||
| 107 | BuildRequest.REQUEST_STATE[self.state][1]) | ||
| 108 | ) | ||
| 109 | # Set property back to the old value | ||
| 110 | self.state = self.old_state | ||
| 111 | return | ||
| 112 | |||
| 113 | super(BuildRequest, self).save(*args, **kwargs) | ||
| 114 | |||
| 115 | |||
| 116 | def get_duration(self): | ||
| 117 | return (self.updated - self.created).total_seconds() | ||
| 118 | |||
| 119 | def get_sorted_target_list(self): | ||
| 120 | tgts = self.brtarget_set.order_by( 'target' ); | ||
| 121 | return( tgts ); | ||
| 122 | |||
| 123 | def get_machine(self): | ||
| 124 | return self.brvariable_set.get(name="MACHINE").value | ||
| 125 | |||
| 126 | def __str__(self): | ||
| 127 | return force_str('%s %s' % (self.project, self.get_state_display())) | ||
| 128 | |||
| 129 | # These tables specify the settings for running an actual build. | ||
| 130 | # They MUST be kept in sync with the tables in orm.models.Project* | ||
| 131 | |||
| 132 | |||
| 133 | class BRLayer(models.Model): | ||
| 134 | req = models.ForeignKey(BuildRequest, on_delete=models.CASCADE) | ||
| 135 | name = models.CharField(max_length=100) | ||
| 136 | giturl = models.CharField(max_length=254, null=True) | ||
| 137 | local_source_dir = models.CharField(max_length=254, null=True) | ||
| 138 | commit = models.CharField(max_length=254, null=True) | ||
| 139 | dirpath = models.CharField(max_length=254, null=True) | ||
| 140 | layer_version = models.ForeignKey(Layer_Version, on_delete=models.CASCADE, null=True) | ||
| 141 | |||
| 142 | class BRBitbake(models.Model): | ||
| 143 | req = models.OneToOneField(BuildRequest, on_delete=models.CASCADE) # only one bitbake for a request | ||
| 144 | giturl = models.CharField(max_length =254) | ||
| 145 | commit = models.CharField(max_length = 254) | ||
| 146 | dirpath = models.CharField(max_length = 254) | ||
| 147 | |||
| 148 | class BRVariable(models.Model): | ||
| 149 | req = models.ForeignKey(BuildRequest, on_delete=models.CASCADE) | ||
| 150 | name = models.CharField(max_length=100) | ||
| 151 | value = models.TextField(blank = True) | ||
| 152 | |||
| 153 | class BRTarget(models.Model): | ||
| 154 | req = models.ForeignKey(BuildRequest, on_delete=models.CASCADE) | ||
| 155 | target = models.CharField(max_length=100) | ||
| 156 | task = models.CharField(max_length=100, null=True) | ||
| 157 | |||
| 158 | class BRError(models.Model): | ||
| 159 | req = models.ForeignKey(BuildRequest, on_delete=models.CASCADE) | ||
| 160 | errtype = models.CharField(max_length=100) | ||
| 161 | errmsg = models.TextField() | ||
| 162 | traceback = models.TextField() | ||
| 163 | |||
| 164 | def __str__(self): | ||
| 165 | return "%s (%s)" % (self.errmsg, self.req) | ||
diff --git a/bitbake/lib/toaster/bldcontrol/views.py b/bitbake/lib/toaster/bldcontrol/views.py deleted file mode 100644 index 286d88b45a..0000000000 --- a/bitbake/lib/toaster/bldcontrol/views.py +++ /dev/null | |||
| @@ -1,5 +0,0 @@ | |||
| 1 | # | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 3 | # | ||
| 4 | |||
| 5 | # Create your views here. | ||
