Skip to contentSkip to conversation

Toaster project main page and other bugfixes and enhancements

PATCH New Alex DAMIAN 1 participant 9 messages Jul 18, 2014

lore
Share & tools
lore
#0 lore raw

cover Toaster project main page and other bugfixes and enhancements

From: Alexandru DAMIAN <[email protected]>

Hello,

This patchset adds the main project edit page, where you can edit project settings, and trigger builds. Currently, only the target add/delete setting is working.

There are two patches that add provisions for running Toaster inside a virtualenv environment, and Django 1.6 compatibility support.

A fix for 6332 requested by WR is included.

This patchset HAS NOT BEEN REVIEWED independently on toaster mailing list, so this submission is also CCed there for review.

Can you please pull on your convenience ?

Thank you,
Alex

The following changes since commit af1d3373706d365f9138caec110fcb20a5966b7b:

toasterui: fix build - project identification (2014-07-09 17:33:35 +0100)

are available in the git repository at:

git://git.yoctoproject.org/poky-contrib adamian/bitbake-fixes-build-enhancements
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=adamian/bitbake-fixes-build-enhancements

Alexandru DAMIAN (8):
toaster: add fields for sourcedir and builddir paths
toaster: added subdirectory entry for build layers
toaster: move bldcontrol initial_data fixture to code
toaster: add project main edit page
toaster: properly set layers when running a build document requirements for the python environment toaster start script allows for Django 1.6
toasterui: fixing event.data clash

 .gitignore                                         |   2 +
 bin/toaster                                        |  18 +-
 lib/bb/event.py                                    |   2 +-
 lib/bb/ui/buildinfohelper.py                       |  30 +-
 lib/toaster/bldcontrol/bbcontroller.py             | 137 ++++++--
 lib/toaster/bldcontrol/fixtures/initial_data.json  |   1 -
 .../management/commands/checksettings.py           |  38 +++
 .../bldcontrol/management/commands/runbuilds.py    |  23 +-
 ...onment_sourcedir__add_field_buildenvironment.py | 106 ++++++
 .../0003_auto__add_field_brlayer_dirpath.py        |  99 ++++++
 .../bldcontrol/migrations/0004_loadinitialdata.py  | 104 ++++++
 .../migrations/0005_auto__add_brerror.py           | 112 +++++++
 lib/toaster/bldcontrol/models.py                   |  12 +-
 .../0011_auto__add_field_projectlayer_dirpath.py   | 242 ++++++++++++++
 ...layer_optional__add_field_projecttarget_task.py | 252 +++++++++++++++
 lib/toaster/orm/models.py                          |  25 +-
 lib/toaster/toastergui/static/css/default.css      |  42 ++-
 lib/toaster/toastergui/templates/base.html         |   4 +
 lib/toaster/toastergui/templates/project.html      | 356 +++++++++++++++++++++
 lib/toaster/toastergui/urls.py                     |   3 +
 lib/toaster/toastergui/views.py                    | 105 +++++-
 requirements.txt                                   |   4 +
 22 files changed, 1632 insertions(+), 85 deletions(-)
 delete mode 100644 lib/toaster/bldcontrol/fixtures/initial_data.json
 create mode 100644 lib/toaster/bldcontrol/management/commands/checksettings.py
 create mode 100644 lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
 create mode 100644 lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py
 create mode 100644 lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
 create mode 100644 lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py
 create mode 100644 lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
 create mode 100644 lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
 create mode 100644 requirements.txt
Signature · 1 line
1.9.1
#1 lore raw

PATCH 1/8 toaster: add fields for sourcedir and builddir paths

From: Alexandru DAMIAN <[email protected]>

We add explicit absolute paths for a directory where the layer sources will be checked out (sourcedir) and where the build activities will take place.

Adding minimal checking when starting the application in order to make sure that BuildEnvironment (BE) settings are usable.

Modify the localhost bbcontroller to use the BE settings instead of trying to self-configure on checked out sources.

---
 bin/toaster                                        |   1 +
 lib/toaster/bldcontrol/bbcontroller.py             |  51 +++++-----
 .../management/commands/checksettings.py           |  38 ++++++++
 ...onment_sourcedir__add_field_buildenvironment.py | 106 +++++++++++++++++++++
 lib/toaster/bldcontrol/models.py                   |   2 +
 5 files changed, 172 insertions(+), 26 deletions(-)
 create mode 100644 lib/toaster/bldcontrol/management/commands/checksettings.py
 create mode 100644 lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
bin/toaster +1 -0
index 90cd982..1f90362 100755
@@ -64,6 +64,7 @@ function webserverStartAll()
             python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=1
         fi
         python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1
+	python $BBBASEDIR/lib/toaster/manage.py checksettings  || retval=1
 
         if [ $retval -eq 0 ]; then
             python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
lib/toaster/bldcontrol/bbcontroller.py +25 -26

91 lines not on this page · raw message

lib/toaster/bldcontrol/management/commands/checksettings.py new file +38 -0
new file mode 100644
index 0000000..da1c3b7
@@ -0,0 +1,38 @@
+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
+import os
+
+class Command(NoArgsCommand):
+    args = ""
+    help = "Verifies thid %dthe configured settings are valid and usable, or prompts the user to fix the settings."
+
+    def handle(self, **options):
+        # we make sure we have builddir and sourcedir for all defined build envionments
+        for be in BuildEnvironment.objects.all():
+            def _verify_be():
+                is_changed = False
+                print("Verifying the Build Environment type %s id %d." % (be.get_betype_display(), be.pk))
+                if len(be.sourcedir) == 0:
+                    be.sourcedir = raw_input(" -- sourcedir may not be empty:")
+                    is_changed = True
+                if not be.sourcedir.startswith("/"):
+                    be.sourcedir = raw_input(" -- sourcedir must be an absolute path:")
+                    is_changed = True
+                if len(be.builddir) == 0:
+                    be.builddir = raw_input(" -- builddir may not be empty:")
+                    is_changed = True
+                if not be.builddir.startswith("/"):
+                    be.builddir = raw_input(" -- builddir must be an absolute path:")
+                    is_changed = True
+                return is_changed
+
+            while (_verify_be()):
+                pass
+        
+
+            
+            
+
lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py new file +106 -0

107 lines not on this page · raw message

lib/toaster/bldcontrol/models.py +2 -0
index 158874f..8285257 100644
@@ -33,6 +33,8 @@ class BuildEnvironment(models.Model):
     bbport      = models.IntegerField(default = -1)
     bbtoken     = models.CharField(max_length = 126, blank = True)
     bbstate     = models.IntegerField(choices = SERVER_STATE, default = SERVER_STOPPED)
+    sourcedir   = models.CharField(max_length = 512, blank = True)
+    builddir    = models.CharField(max_length = 512, blank = True)
     lock        = models.IntegerField(choices = LOCK_STATE, default = LOCK_FREE)
     created     = models.DateTimeField(auto_now_add = True)
     updated     = models.DateTimeField(auto_now = True)
Signature · 1 line
1.9.1
#2 lore raw

PATCH 2/8 toaster: added subdirectory entry for build layers

From: Alexandru DAMIAN <[email protected]>

A layer may live in a subdirectory of a git repository, so we add a field to track this setting.

We add the Project schedule_build function, which creates a build request from the current project configuration.

We also fix an import problem with Projects in views.

---
 .../0003_auto__add_field_brlayer_dirpath.py        |  99 +++++++++
 lib/toaster/bldcontrol/models.py                   |   2 +
 .../0011_auto__add_field_projectlayer_dirpath.py   | 242 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |  21 +-
 lib/toaster/toastergui/views.py                    |   2 +
 5 files changed, 364 insertions(+), 2 deletions(-)
 create mode 100644 lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py
 create mode 100644 lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py new file +99 -0
new file mode 100644
index 0000000..b9ba838
@@ -0,0 +1,99 @@
+# -*- 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 field 'BRLayer.dirpath'
+        db.add_column(u'bldcontrol_brlayer', 'dirpath',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=254),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'BRLayer.dirpath'
+        db.delete_column(u'bldcontrol_brlayer', 'dirpath')
+
+
+    models = {
+        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
lib/toaster/bldcontrol/models.py +2 -0
index 8285257..1f253ff 100644
@@ -71,6 +71,8 @@ class BRLayer(models.Model):
     name        = models.CharField(max_length = 100)
     giturl      = models.CharField(max_length = 254)
     commit      = models.CharField(max_length = 254)
+    dirpath     = models.CharField(max_length = 254)
+
 
 class BRVariable(models.Model):
     req         = models.ForeignKey(BuildRequest)
lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py new file +242 -0

243 lines not on this page · raw message

lib/toaster/orm/models.py +19 -2
index f406429..9b7387a 100644
@@ -37,13 +37,13 @@ class ProjectManager(models.Manager):
             name = "meta",
             giturl = "git://git.yoctoproject.org/poky",
             commit = branch,
-            treepath = "meta")
+            dirpath = "meta")
 
         ProjectLayer.objects.create(project = prj,
             name = "meta-yocto",
             giturl = "git://git.yoctoproject.org/poky",
             commit = branch,
-            treepath = "meta-yocto")
+            dirpath = "meta-yocto")
 
         return prj
 
@@ -66,6 +66,22 @@ class Project(models.Model):
     user_id     = models.IntegerField(null = True)
     objects     = ProjectManager()
 
+
+    def schedule_build(self):
+        from bldcontrol.models import BuildRequest, BRTarget, BRLayer, BRVariable
+        br = BuildRequest.objects.create(project = self)
+        for l in self.projectlayer_set.all():
+            BRLayer.objects.create(req = br, name = l.name, giturl = l.giturl, commit = l.commit, dirpath = l.dirpath)
+        for t in self.projecttarget_set.all():
+            BRTarget.objects.create(req = br, target = t.target, task = t.task)
+        for v in self.projectvariable_set.all():
+            BRVariable.objects.create(req = br, name = v.name, value = v.value)
+
+        br.state = BuildRequest.REQ_QUEUED
+        br.save()
+        return br
+
+
 class Build(models.Model):
     SUCCEEDED = 0
     FAILED = 1
@@ -375,6 +391,7 @@ class ProjectLayer(models.Model):
     name = models.CharField(max_length = 100)
     giturl = models.CharField(max_length = 254)
     commit = models.CharField(max_length = 254)
+    dirpath = models.CharField(max_length = 254)
 
 class Layer(models.Model):
     name = models.CharField(max_length=100)
lib/toaster/toastergui/views.py +2 -0
index 8fbe8a3..f9e8e51 100755
@@ -1773,6 +1773,8 @@ if toastermain.settings.MANAGED:
     from django.contrib.auth import authenticate, login
     from django.contrib.auth.decorators import login_required
 
+    from orm.models import Project
+
     import traceback
 
     class BadParameterException(Exception): pass        # error thrown on invalid POST requests
Signature · 1 line
1.9.1
#3 lore raw

PATCH 3/8 toaster: move bldcontrol initial_data fixture to code

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

We move the initial_data fixture to a code migration, in order to make sure that we don't overwrite modified data.

---
 bin/toaster                                        |   2 +-
 lib/toaster/bldcontrol/fixtures/initial_data.json  |   1 -
 .../management/commands/checksettings.py           |   8 +-
 .../bldcontrol/migrations/0004_loadinitialdata.py  | 104 +++++++++++++++++++++
 4 files changed, 109 insertions(+), 6 deletions(-)
 delete mode 100644 lib/toaster/bldcontrol/fixtures/initial_data.json
 create mode 100644 lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
bin/toaster +1 -1
index 1f90362..78fbfa5 100755
@@ -65,7 +65,7 @@ function webserverStartAll()
         fi
         python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1
 	python $BBBASEDIR/lib/toaster/manage.py checksettings  || retval=1
-
+	echo "Starting webserver"
         if [ $retval -eq 0 ]; then
             python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
             sleep 1
lib/toaster/bldcontrol/fixtures/initial_data.json deleted +0 -1
deleted file mode 100644
index b2f12b3..0000000
@@ -1 +0,0 @@
-[{"pk": 1, "model": "bldcontrol.buildenvironment", "fields": {"updated": "2014-05-20T12:17:30Z", "created": "2014-05-20T12:17:30Z", "lock": 0, "bbstate": 0, "bbaddress": "", "betype": 0, "bbtoken": "", "address": "", "bbport": -1}}]
lib/toaster/bldcontrol/management/commands/checksettings.py +4 -4
index da1c3b7..a91dd15 100644
@@ -27,12 +27,12 @@ class Command(NoArgsCommand):
                 if not be.builddir.startswith("/"):
                     be.builddir = raw_input(" -- builddir must be an absolute path:")
                     is_changed = True
+                if is_changed:
+                    print "saved"
+                    be.save()
                 return is_changed
 
             while (_verify_be()):
                 pass
-        
-
-            
-            
 
+            return 0
lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py new file +104 -0

104 lines not on this page · raw message

Signature · 1 line
1.9.1
#4 lore raw

PATCH 4/8 toaster: add project main edit page

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

This is the first commit on the project main edit page.

At this point we have:
* the default settings for a newly created project
* the ability to add targets
* the ability to trigger a build command, and have the build executed

---
 bin/toaster                                        |   4 +-
 ...layer_optional__add_field_projecttarget_task.py | 252 +++++++++++++++
 lib/toaster/orm/models.py                          |   8 +-
 lib/toaster/toastergui/static/css/default.css      |  42 ++-
 lib/toaster/toastergui/templates/base.html         |   4 +
 lib/toaster/toastergui/templates/project.html      | 356 +++++++++++++++++++++
 lib/toaster/toastergui/urls.py                     |   3 +
 lib/toaster/toastergui/views.py                    | 105 +++++-
 8 files changed, 750 insertions(+), 24 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
bin/toaster +2 -2
index 78fbfa5..93d75b0 100755
@@ -128,8 +128,8 @@ if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; th
     # Start just the web server, point the web browser to the interface, and start any Django services.
 
     if [ -n "$BUILDDIR" ]; then
-        echo "Error: build/ directory detected. Standalone Toaster will not start in a build environment." 1>&2;
-        return 1;
+        echo -e "Error: build/ directory detected. Toaster will not start in managed mode if a build environment is detected.\nUse a clean terminal to start Toaster." 1>&2;
+        exit 1;
     fi
 
     # Define a fake builddir where only the pid files are actually created. No real builds will take place here.
lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py new file +252 -0

253 lines not on this page · raw message

lib/toaster/orm/models.py +6 -2
index 9b7387a..f19a437 100644
@@ -37,13 +37,15 @@ class ProjectManager(models.Manager):
             name = "meta",
             giturl = "git://git.yoctoproject.org/poky",
             commit = branch,
-            dirpath = "meta")
+            dirpath = "meta",
+            optional = False)
 
         ProjectLayer.objects.create(project = prj,
             name = "meta-yocto",
             giturl = "git://git.yoctoproject.org/poky",
             commit = branch,
-            dirpath = "meta-yocto")
+            dirpath = "meta-yocto",
+            optional = False)
 
         return prj
 
@@ -116,6 +118,7 @@ class Build(models.Model):
 class ProjectTarget(models.Model):
     project = models.ForeignKey(Project)
     target = models.CharField(max_length=100)
+    task = models.CharField(max_length=100, null=True)
 
 @python_2_unicode_compatible
 class Target(models.Model):
@@ -392,6 +395,7 @@ class ProjectLayer(models.Model):
     giturl = models.CharField(max_length = 254)
     commit = models.CharField(max_length = 254)
     dirpath = models.CharField(max_length = 254)
+    optional = models.BooleanField(default = True)
 
 class Layer(models.Model):
     name = models.CharField(max_length=100)
lib/toaster/toastergui/static/css/default.css +28 -14

45 lines not on this page · raw message

lib/toaster/toastergui/templates/base.html +4 -0
index 1407d64..9ef249a 100644
@@ -65,6 +65,10 @@ function reload_params(params) {
 							  <i class="icon-caret-down"></i>
 						  </button>
 						  <ul class="dropdown-menu">
+{% for prj in projects %}
+								<li><a href="{% url 'project' prj.id %}">{{prj.name}}</a></li>
+{% endfor %}
+							  <li><hr/></li>
 							  <li><a href="#">Clone project</a></li>
 							  <li><a href="#">Export project</a></li>
 							  <li><a href="#">Import project</a></li>
lib/toaster/toastergui/templates/project.html +356 -0

360 lines not on this page · raw message

lib/toaster/toastergui/urls.py +3 -0
index 0d7a4c3..7c4f894 100644
@@ -69,6 +69,9 @@ urlpatterns = patterns('toastergui.views',
         # project URLs
         url(r'^newproject/$', 'newproject', name='newproject'),
         url(r'^project/(?P<pid>\d+)/$', 'project', name='project'),
+        url(r'^xhr_projectbuild/(?P<pid>\d+)/$', 'xhr_projectbuild', name='xhr_projectbuild'),
+        url(r'^xhr_projectedit/(?P<pid>\d+)/$', 'xhr_projectedit', name='xhr_projectedit'),
+
 
         # default redirection
         url(r'^$', RedirectView.as_view( url= 'builds/')),
lib/toaster/toastergui/views.py +99 -6

140 lines not on this page · raw message

Signature · 1 line
1.9.1
#5 lore raw

PATCH 5/8 toaster: properly set layers when running a build

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

This patch enables the localhost build controller to properly set the layers before the build runs.

It creates the checkout directories under BuildEnvironment sourcedir directory, and runs the build in the buildir directory.

Build launch errors are tracked in the newly added BRError table. These are different from build errors, in the sense that the build can't start due to these errors.

---
 lib/toaster/bldcontrol/bbcontroller.py             | 108 +++++++++++++++++---
 .../bldcontrol/management/commands/runbuilds.py    |  23 +++--
 .../migrations/0005_auto__add_brerror.py           | 112 +++++++++++++++++++++
 lib/toaster/bldcontrol/models.py                   |   8 +-
 4 files changed, 226 insertions(+), 25 deletions(-)
 create mode 100644 lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py
lib/toaster/bldcontrol/bbcontroller.py +93 -15
index c3beba9..1e58c67 100644
@@ -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):
lib/toaster/bldcontrol/management/commands/runbuilds.py +14 -9
index dd8f84b..fa8c1a9 100644
@@ -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
lib/toaster/bldcontrol/migrations/0005_auto__add_brerror.py new file +112 -0

113 lines not on this page · raw message

lib/toaster/bldcontrol/models.py +7 -1
index 1f253ff..8c271ff 100644
@@ -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()
Signature · 1 line
1.9.1
#6 lore raw

PATCH 6/8 document requirements for the python environment

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

Since Toaster adds a number of specific requirements managed by pip, we document these requirements in order to be able to run bitbake and toaster under virtualenv. The target here is to break the dependency on distro packages for specific python libraries.

In order to start bitbake/Toaster in a distro-independent manner, we use virtualenv and pip. We add venv to the .gitignore list to make room for the virtualenv deployment in this directory.

Use this command sequence to setup the virtualenv:

$ virtualenv venv
$ . venv/bin/activate
$ cat requirements.txt | xargs pip install

bitbake and Toaster will then run normally under virtualenv.

---
 .gitignore       | 2 ++
 requirements.txt | 4 ++++
 2 files changed, 6 insertions(+)
 create mode 100644 requirements.txt
.gitignore +2 -0
index 5d9bc50..5adf4d1 100644
@@ -7,6 +7,8 @@ pyshtables.py
 /bin/bitbakec
 *.swp
 tags
+*.sqlite
+venv/
 doc/bitbake-user-manual/bitbake-user-manual.html
 doc/bitbake-user-manual/bitbake-user-manual.pdf
 doc/bitbake-user-manual/bitbake-user-manual.tgz
requirements.txt new file +4 -0
new file mode 100644
index 0000000..2bb07e6
@@ -0,0 +1,4 @@
+Django==1.5
+South==0.8.4
+argparse==1.2.1
+wsgiref==0.1.2
Signature · 1 line
1.9.1
#7 lore raw

PATCH 7/8 toaster start script allows for Django 1.6

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

We perform upgrade to Django 1.6 in order to keep using a current Django version. In practice, this means allowing for both 1.5 and 1.6 version in the toaster start script. We also modify the suggestion installation commands to remove "sudo" since now pip can be used in a virtualenv.

Update requirements.txt to install the 1.6 Django release.

---
 bin/toaster      | 13 ++++++++-----
 requirements.txt |  2 +-
 2 files changed, 9 insertions(+), 6 deletions(-)
bin/toaster +8 -5
index 93d75b0..ce16de6 100755
@@ -63,8 +63,10 @@ function webserverStartAll()
             retval=0
             python $BBBASEDIR/lib/toaster/manage.py migrate orm || retval=1
         fi
-        python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1
-	python $BBBASEDIR/lib/toaster/manage.py checksettings  || retval=1
+	if [ "x$TOASTER_MANAGED" == "x1" ]; then
+	        python $BBBASEDIR/lib/toaster/manage.py migrate bldcontrol || retval=1
+		python $BBBASEDIR/lib/toaster/manage.py checksettings  || retval=1
+	fi
 	echo "Starting webserver"
         if [ $retval -eq 0 ]; then
             python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 </dev/null >${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid
@@ -140,6 +142,7 @@ if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; th
         webserverKillAll
         RUNNING=0
     }
+    TOASTER_MANAGED=1
     export TOASTER_MANAGED=1
     webserverStartAll || (echo "Fail to start the web server, stopping" 1>&2 && exit 1)
     xdg-open http://0.0.0.0:8000/ >/dev/null 2>&1 &
@@ -163,13 +166,13 @@ fi
 
 # Verify prerequisites
 
-if ! echo "import django; print (1,5) == django.VERSION[0:2]" | python 2>/dev/null | grep True >/dev/null; then
-    echo -e "This program needs Django 1.5. Please install with\n\nsudo pip install django==1.5"
+if ! echo "import django; print (1,) == django.VERSION[0:1] and django.VERSION[1:2][0] in (5,6)" | python 2>/dev/null | grep True >/dev/null; then
+    echo -e "This program needs Django 1.5 or 1.6. Please install with\n\npip install django==1.6"
     return 2
 fi
 
 if ! echo "import south; print [0,8,4] ==  map(int,south.__version__.split(\".\"))" | python 2>/dev/null | grep True >/dev/null; then
-    echo -e "This program needs South 0.8.4. Please install with\n\nsudo pip install south==0.8.4"
+    echo -e "This program needs South 0.8.4. Please install with\n\npip install south==0.8.4"
     return 2
 fi
 
requirements.txt +1 -1
index 2bb07e6..19b5293 100644
@@ -1,4 +1,4 @@
-Django==1.5
+Django==1.6
 South==0.8.4
 argparse==1.2.1
 wsgiref==0.1.2
Signature · 1 line
1.9.1
#8 lore raw

PATCH 8/8 toasterui: fixing event.data clash

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

This patch fixes a name collision on the event.data in the MetadataEvent class. event.data is used in the event system as a copy of "d" in certain situations, and this collision triggered a bug leading to data loss.

[YOCTO #6332]

---
 lib/bb/event.py              |  2 +-
 lib/bb/ui/buildinfohelper.py | 30 +++++++++++++++---------------
 2 files changed, 16 insertions(+), 16 deletions(-)
lib/bb/event.py +1 -1
index 8b47ae5..e2f6b9c 100644
@@ -594,7 +594,7 @@ class MetadataEvent(Event):
     def __init__(self, eventtype, eventdata):
         Event.__init__(self)
         self.type = eventtype
-        self.data = eventdata
+        self._localdata = eventdata
 
 class SanityCheck(Event):
     """
lib/bb/ui/buildinfohelper.py +15 -15
index 29cfc81..f64423b 100644
@@ -596,8 +596,8 @@ class BuildInfoHelper(object):
     ## external available methods to store information
 
     def store_layer_info(self, event):
-        assert 'data' in vars(event)
-        layerinfos = event.data
+        assert '_localdata' in vars(event)
+        layerinfos = event._localdata
         self.internal_state['lvs'] = {}
         for layer in layerinfos:
             self.internal_state['lvs'][self.orm_wrapper.get_update_layer_object(layerinfos[layer])] = layerinfos[layer]['version']
@@ -636,10 +636,10 @@ class BuildInfoHelper(object):
         image_fstypes = self.server.runCommand(["getVariable", "IMAGE_FSTYPES"])[0]
         for t in self.internal_state['targets']:
             if t.is_image == True:
-                output_files = list(event.data.viewkeys())
+                output_files = list(event._localdata.viewkeys())
                 for output in output_files:
                     if t.target in output and output.split('.rootfs.')[1] in image_fstypes:
-                        self.orm_wrapper.save_target_image_file_information(t, output, event.data[output])
+                        self.orm_wrapper.save_target_image_file_information(t, output, event._localdata[output])
 
     def update_build_information(self, event, errors, warnings, taskfailures):
         if 'build' in self.internal_state:
@@ -647,8 +647,8 @@ class BuildInfoHelper(object):
 
 
     def store_license_manifest_path(self, event):
-        deploy_dir = event.data['deploy_dir']
-        image_name =  event.data['image_name']
+        deploy_dir = event._localdata['deploy_dir']
+        image_name =  event._localdata['image_name']
         path = deploy_dir + "/licenses/" + image_name + "/"
         for target in self.internal_state['targets']:
             if target.target in image_name:
@@ -696,7 +696,7 @@ class BuildInfoHelper(object):
 
 
     def store_tasks_stats(self, event):
-        for (taskfile, taskname, taskstats, recipename) in event.data:
+        for (taskfile, taskname, taskstats, recipename) in event._localdata:
             localfilepath = taskfile.split(":")[-1]
             assert localfilepath.startswith("/")
 
@@ -768,7 +768,7 @@ class BuildInfoHelper(object):
 
 
     def store_missed_state_tasks(self, event):
-        for (fn, taskname, taskhash, sstatefile) in event.data['missed']:
+        for (fn, taskname, taskhash, sstatefile) in event._localdata['missed']:
 
             identifier = fn + taskname + "_setscene"
             recipe_information = self._get_recipe_information_from_taskfile(fn)
@@ -787,7 +787,7 @@ class BuildInfoHelper(object):
 
             self.orm_wrapper.get_update_task_object(task_information)
 
-        for (fn, taskname, taskhash, sstatefile) in event.data['found']:
+        for (fn, taskname, taskhash, sstatefile) in event._localdata['found']:
 
             identifier = fn + taskname + "_setscene"
             recipe_information = self._get_recipe_information_from_taskfile(fn)
@@ -804,15 +804,15 @@ class BuildInfoHelper(object):
 
 
     def store_target_package_data(self, event):
-        assert 'data' in vars(event)
+        assert '_localdata' in vars(event)
         # for all image targets
         for target in self.internal_state['targets']:
             if target.is_image:
                 try:
-                    pkgdata = event.data['pkgdata']
-                    imgdata = event.data['imgdata'][target.target]
+                    pkgdata = event._localdata['pkgdata']
+                    imgdata = event._localdata['imgdata'][target.target]
                     self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata, pkgdata, self.internal_state['recipes'])
-                    filedata = event.data['filedata'][target.target]
+                    filedata = event._localdata['filedata'][target.target]
                     self.orm_wrapper.save_target_file_information(self.internal_state['build'], target, filedata)
                 except KeyError:
                     # we must have not got the data for this image, nothing to save
@@ -922,8 +922,8 @@ class BuildInfoHelper(object):
 
 
     def store_build_package_information(self, event):
-        assert 'data' in vars(event)
-        package_info = event.data
+        assert '_localdata' in vars(event)
+        package_info = event._localdata
         self.orm_wrapper.save_build_package_information(self.internal_state['build'],
                             package_info,
                             self.internal_state['recipes'],
Signature · 1 line
1.9.1