1# 2# SPDX-License-Identifier: GPL-2.0-only 3# 4 5from __future__ import unicode_literals 6from django.db import models 7from django.utils.encoding import force_str 8from orm.models import Project, Build, Layer_Version 9 10import logging 11logger = logging.getLogger("toaster") 12# a BuildEnvironment is the equivalent of the "build/" directory on the localhost 13class 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 65class 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 133class 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 142class 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 148class 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 153class 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 158class 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) 166