aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/toaster/bldcontrol/bbcontroller.py108
-rw-r--r--lib/toaster/bldcontrol/management/commands/runbuilds.py23
-rw-r--r--lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py112
-rw-r--r--lib/toaster/bldcontrol/models.py8
4 files changed, 226 insertions, 25 deletions
diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index c3beba96f..1e58c67fc 100644
--- a/lib/toaster/bldcontrol/bbcontroller.py
+++ b/lib/toaster/bldcontrol/bbcontroller.py
@@ -126,6 +126,8 @@ class BuildEnvironmentController(object):
def setLayers(self,ls):
""" Sets the layer variables in the config file, after validating local layer paths.
The layer paths must be in a list of BRLayer object
+
+ a word of attention: by convention, the first layer for any build will be poky!
"""
raise Exception("Must override setLayers")
@@ -165,25 +167,31 @@ class BuildEnvironmentController(object):
class ShellCmdException(Exception):
pass
+
+class BuildSetupException(Exception):
+ pass
+
class LocalhostBEController(BuildEnvironmentController):
""" Implementation of the BuildEnvironmentController for the localhost;
this controller manages the default build directory,
the server setup and system start and stop for the localhost-type build environment
"""
- from os.path import dirname as DN
def __init__(self, be):
super(LocalhostBEController, self).__init__(be)
- from os.path import dirname as DN
self.dburl = settings.getDATABASE_URL()
+ self.pokydirname = None
+
+ def _shellcmd(self, command, cwd = None):
+ if cwd is None:
+ cwd = self.be.sourcedir
- def _shellcmd(self, command):
- p = subprocess.Popen(command, cwd=self.be.sourcedir, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p = subprocess.Popen(command, cwd = cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out,err) = p.communicate()
if p.returncode:
if len(err) == 0:
- err = "command: %s" % command
+ err = "command: %s \n%s" % (command, out)
else:
err = "command: %s \n%s" % (command, err)
raise ShellCmdException(err)
@@ -191,22 +199,20 @@ class LocalhostBEController(BuildEnvironmentController):
return out
def _createdirpath(self, path):
+ from os.path import dirname as DN
if not os.path.exists(DN(path)):
self._createdirpath(DN(path))
if not os.path.exists(path):
os.mkdir(path, 0755)
def _startBE(self):
- assert self.be.sourcedir and os.path.exists(self.be.sourcedir)
+ assert self.pokydirname and os.path.exists(self.pokydirname)
self._createdirpath(self.be.builddir)
- self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.be.sourcedir, self.be.builddir))
+ self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.pokydirname, self.be.builddir))
def startBBServer(self):
- assert self.be.sourcedir and os.path.exists(self.be.sourcedir)
-
- self._startBE()
-
- print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && DATABASE_URL=%s source toaster start noweb && sleep 1\"" % (self.be.sourcedir, self.be.builddir, self.dburl))
+ assert self.pokydirname and os.path.exists(self.pokydirname)
+ print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && DATABASE_URL=%s source toaster start noweb && sleep 1\"" % (self.pokydirname, self.be.builddir, self.dburl))
# FIXME unfortunate sleep 1 - we need to make sure that bbserver is started and the toaster ui is connected
# but since they start async without any return, we just wait a bit
print "Started server"
@@ -225,10 +231,82 @@ class LocalhostBEController(BuildEnvironmentController):
print "Stopped server"
def setLayers(self, layers):
+ """ a word of attention: by convention, the first layer for any build will be poky! """
+
assert self.be.sourcedir is not None
- layerconf = os.path.join(self.be.builddir, "conf/bblayers.conf")
- if not os.path.exists(layerconf):
- raise Exception("BE is not consistent: bblayers.conf file missing at ", layerconf)
+ # set layers in the layersource
+
+ # 1. get a list of repos, and map dirpaths for each layer
+ gitrepos = {}
+ for layer in layers:
+ if not layer.giturl in gitrepos:
+ gitrepos[layer.giturl] = []
+ gitrepos[layer.giturl].append( (layer.name, layer.dirpath, layer.commit))
+ for giturl in gitrepos.keys():
+ commitid = gitrepos[giturl][0][2]
+ for e in gitrepos[giturl]:
+ if commitid != e[2]:
+ raise BuildSetupException("More than one commit per git url, unsupported configuration")
+
+ def _getgitdirectoryname(url):
+ import re
+ components = re.split(r'[\.\/]', url)
+ return components[-2] if components[-1] == "git" else components[-1]
+
+ layerlist = []
+
+ # 2. checkout the repositories
+ for giturl in gitrepos.keys():
+ localdirname = os.path.join(self.be.sourcedir, _getgitdirectoryname(giturl))
+ print "DEBUG: giturl checking out in current directory", localdirname
+
+ # make sure our directory is a git repository
+ if os.path.exists(localdirname):
+ if not giturl in self._shellcmd("git remote -v", localdirname):
+ raise BuildSetupException("Existing git repository at %s, but with different remotes (not '%s'). Aborting." % (localdirname, giturl))
+ else:
+ self._shellcmd("git clone \"%s\" \"%s\"" % (giturl, localdirname))
+ # checkout the needed commit
+ commit = gitrepos[giturl][0][2]
+ self._shellcmd("git fetch --all && git checkout \"%s\"" % commit , localdirname)
+ print "DEBUG: checked out commit ", commit, "to", localdirname
+
+ # if this is the first checkout, take the localdirname as poky dir
+ if self.pokydirname is None:
+ print "DEBUG: selected poky dir name", localdirname
+ self.pokydirname = localdirname
+
+ # verify our repositories
+ for name, dirpath, commit in gitrepos[giturl]:
+ localdirpath = os.path.join(localdirname, dirpath)
+ if not os.path.exists(localdirpath):
+ raise BuildSetupException("Cannot find layer git path '%s' in checked out repository '%s:%s'. Aborting." % (localdirpath, giturl, commit))
+
+ layerlist.append(localdirpath)
+
+ print "DEBUG: current layer list ", layerlist
+
+ # 3. configure the build environment, so we have a conf/bblayers.conf
+ assert self.pokydirname is not None
+ self._startBE()
+
+ # 4. update the bblayers.conf
+ bblayerconf = os.path.join(self.be.builddir, "conf/bblayers.conf")
+ if not os.path.exists(bblayerconf):
+ raise BuildSetupException("BE is not consistent: bblayers.conf file missing at %s" % bblayerconf)
+
+ conflines = open(bblayerconf, "r").readlines()
+
+ bblayerconffile = open(bblayerconf, "w")
+ for i in xrange(len(conflines)):
+ if conflines[i].startswith("# line added by toaster"):
+ i += 2
+ else:
+ bblayerconffile.write(conflines[i])
+
+ bblayerconffile.write("\n# line added by toaster build control\nBBLAYERS = \"" + " ".join(layerlist) + "\"")
+ bblayerconffile.close()
+
return True
def release(self):
diff --git a/lib/toaster/bldcontrol/management/commands/runbuilds.py b/lib/toaster/bldcontrol/management/commands/runbuilds.py
index dd8f84b07..fa8c1a990 100644
--- a/lib/toaster/bldcontrol/management/commands/runbuilds.py
+++ b/lib/toaster/bldcontrol/management/commands/runbuilds.py
@@ -1,8 +1,8 @@
from django.core.management.base import NoArgsCommand, CommandError
from django.db import transaction
from orm.models import Build
-from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException
-from bldcontrol.models import BuildRequest, BuildEnvironment
+from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException, BuildSetupException
+from bldcontrol.models import BuildRequest, BuildEnvironment, BRError
import os
class Command(NoArgsCommand):
@@ -25,6 +25,7 @@ class Command(NoArgsCommand):
return br
def schedule(self):
+ import traceback
try:
br = None
try:
@@ -63,15 +64,19 @@ class Command(NoArgsCommand):
# cleanup to be performed by toaster when the deed is done
- except ShellCmdException as e:
- import traceback
- print " EE Error executing shell command\n", e
- traceback.format_exc(e)
except Exception as e:
- import traceback
- traceback.print_exc()
- raise e
+ print " EE Error executing shell command\n", e
+ traceback.print_exc(e)
+ BRError.objects.create(req = br,
+ errtype = str(type(e)),
+ errmsg = str(e),
+ traceback = traceback.format_exc(e))
+ br.state = BuildRequest.REQ_FAILED
+ br.save()
+ bec.be.lock = BuildEnvironment.LOCK_FREE
+ bec.be.save()
+
def cleanup(self):
from django.utils import timezone
diff --git a/lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py b/lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py
new file mode 100644
index 000000000..98aeb41ce
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'BRError'
+ db.create_table(u'bldcontrol_brerror', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('req', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['bldcontrol.BuildRequest'])),
+ ('errtype', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ('errmsg', self.gf('django.db.models.fields.TextField')()),
+ ('traceback', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal(u'bldcontrol', ['BRError'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'BRError'
+ db.delete_table(u'bldcontrol_brerror')
+
+
+ models = {
+ u'bldcontrol.brerror': {
+ 'Meta': {'object_name': 'BRError'},
+ 'errmsg': ('django.db.models.fields.TextField', [], {}),
+ 'errtype': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"}),
+ 'traceback': ('django.db.models.fields.TextField', [], {})
+ },
+ u'bldcontrol.brlayer': {
+ 'Meta': {'object_name': 'BRLayer'},
+ 'commit': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+ 'dirpath': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+ 'giturl': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"})
+ },
+ u'bldcontrol.brtarget': {
+ 'Meta': {'object_name': 'BRTarget'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+ },
+ u'bldcontrol.brvariable': {
+ 'Meta': {'object_name': 'BRVariable'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'req': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['bldcontrol.BuildRequest']"}),
+ 'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ u'bldcontrol.buildenvironment': {
+ 'Meta': {'object_name': 'BuildEnvironment'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+ 'bbaddress': ('django.db.models.fields.CharField', [], {'max_length': '254', 'blank': 'True'}),
+ 'bbport': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+ 'bbstate': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bbtoken': ('django.db.models.fields.CharField', [], {'max_length': '126', 'blank': 'True'}),
+ 'betype': ('django.db.models.fields.IntegerField', [], {}),
+ 'builddir': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'lock': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'sourcedir': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ u'bldcontrol.buildrequest': {
+ 'Meta': {'object_name': 'BuildRequest'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']", 'null': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'state': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ u'orm.build': {
+ 'Meta': {'object_name': 'Build'},
+ 'bitbake_version': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'build_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'completed_on': ('django.db.models.fields.DateTimeField', [], {}),
+ 'cooker_log_path': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'distro': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'distro_version': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'errors_no': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'machine': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'outcome': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']", 'null': 'True'}),
+ 'started_on': ('django.db.models.fields.DateTimeField', [], {}),
+ 'timespent': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'warnings_no': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+ },
+ u'orm.project': {
+ 'Meta': {'object_name': 'Project'},
+ 'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+ }
+ }
+
+ complete_apps = ['bldcontrol'] \ No newline at end of file
diff --git a/lib/toaster/bldcontrol/models.py b/lib/toaster/bldcontrol/models.py
index 1f253ffb2..8c271ffa9 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -48,12 +48,14 @@ class BuildRequest(models.Model):
REQ_QUEUED = 1
REQ_INPROGRESS = 2
REQ_COMPLETED = 3
+ REQ_FAILED = 4
REQUEST_STATE = (
(REQ_CREATED, "created"),
(REQ_QUEUED, "queued"),
(REQ_INPROGRESS, "in progress"),
(REQ_COMPLETED, "completed"),
+ (REQ_FAILED, "failed"),
)
project = models.ForeignKey(Project)
@@ -84,4 +86,8 @@ class BRTarget(models.Model):
target = models.CharField(max_length=100)
task = models.CharField(max_length=100, null=True)
-
+class BRError(models.Model):
+ req = models.ForeignKey(BuildRequest)
+ errtype = models.CharField(max_length=100)
+ errmsg = models.TextField()
+ traceback = models.TextField()