All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/8] toaster: add fields for sourcedir and builddir paths
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:14   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/bin/toaster b/bin/toaster
index 90cd982..1f90362 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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
diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index d2b2a23..c3beba9 100644
--- a/lib/toaster/bldcontrol/bbcontroller.py
+++ b/lib/toaster/bldcontrol/bbcontroller.py
@@ -170,27 +170,16 @@ class LocalhostBEController(BuildEnvironmentController):
         this controller manages the default build directory,
         the server setup and system start and stop for the localhost-type build environment
 
-        The address field is used as working directory; if not set, the build/ directory
-        is created
     """
+    from os.path import dirname as DN
 
     def __init__(self, be):
         super(LocalhostBEController, self).__init__(be)
         from os.path import dirname as DN
-        self.cwd = DN(DN(DN(DN(DN(os.path.realpath(__file__))))))
-        if self.be.address is None or len(self.be.address) == 0:
-            self.be.address = "build"
-            self.be.save()
-        self.bwd = self.be.address
         self.dburl = settings.getDATABASE_URL()
 
-        # transform relative paths to absolute ones
-        if not self.bwd.startswith("/"):
-            self.bwd = os.path.join(self.cwd, self.bwd)
-        self._createBE()
-
     def _shellcmd(self, command):
-        p = subprocess.Popen(command, cwd=self.cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        p = subprocess.Popen(command, cwd=self.be.sourcedir, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         (out,err) = p.communicate()
         if p.returncode:
             if len(err) == 0:
@@ -201,39 +190,49 @@ class LocalhostBEController(BuildEnvironmentController):
         else:
             return out
 
-    def _createBE(self):
-        assert self.cwd and os.path.exists(self.cwd)
-        self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.cwd, self.bwd))
+    def _createdirpath(self, path):
+        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)
+        self._createdirpath(self.be.builddir)
+        self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.be.sourcedir, self.be.builddir))
 
     def startBBServer(self):
-        assert self.cwd and os.path.exists(self.cwd)
-        print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && DATABASE_URL=%s source toaster start noweb && sleep 1\"" % (self.cwd, self.bwd, self.dburl))
+        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))
         # 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"
-        assert self.cwd and os.path.exists(self.bwd)
+        assert self.be.sourcedir and os.path.exists(self.be.builddir)
         self.be.bbaddress = "localhost"
         self.be.bbport = "8200"
         self.be.bbstate = BuildEnvironment.SERVER_STARTED
         self.be.save()
 
     def stopBBServer(self):
-        assert self.cwd
+        assert self.be.sourcedir
         print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && %s source toaster stop\"" %
-            (self.cwd, self.bwd, (lambda: "" if self.be.bbtoken is None else "BBTOKEN=%s" % self.be.bbtoken)()))
+            (self.be.sourcedir, self.be.builddir, (lambda: "" if self.be.bbtoken is None else "BBTOKEN=%s" % self.be.bbtoken)()))
         self.be.bbstate = BuildEnvironment.SERVER_STOPPED
         self.be.save()
         print "Stopped server"
 
     def setLayers(self, layers):
-        assert self.cwd is not None
-        layerconf = os.path.join(self.bwd, "conf/bblayers.conf")
+        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)
         return True
 
     def release(self):
-        assert self.cwd and os.path.exists(self.bwd)
+        assert self.be.sourcedir and os.path.exists(self.be.builddir)
         import shutil
-        shutil.rmtree(os.path.join(self.cwd, "build"))
-        assert not os.path.exists(self.bwd)
+        shutil.rmtree(os.path.join(self.be.sourcedir, "build"))
+        assert not os.path.exists(self.be.builddir)
diff --git a/lib/toaster/bldcontrol/management/commands/checksettings.py b/lib/toaster/bldcontrol/management/commands/checksettings.py
new file mode 100644
index 0000000..da1c3b7
--- /dev/null
+++ b/lib/toaster/bldcontrol/management/commands/checksettings.py
@@ -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
+        
+
+            
+            
+
diff --git a/lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py b/lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
new file mode 100644
index 0000000..f522a50
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
@@ -0,0 +1,106 @@
+# -*- 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 'BuildEnvironment.sourcedir'
+        db.add_column(u'bldcontrol_buildenvironment', 'sourcedir',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=512, blank=True),
+                      keep_default=False)
+
+        # Adding field 'BuildEnvironment.builddir'
+        db.add_column(u'bldcontrol_buildenvironment', 'builddir',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=512, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'BuildEnvironment.sourcedir'
+        db.delete_column(u'bldcontrol_buildenvironment', 'sourcedir')
+
+        # Deleting field 'BuildEnvironment.builddir'
+        db.delete_column(u'bldcontrol_buildenvironment', 'builddir')
+
+
+    models = {
+        u'bldcontrol.brlayer': {
+            'Meta': {'object_name': 'BRLayer'},
+            'commit': ('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 158874f..8285257 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -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)
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 1/8] toaster: add fields for sourcedir and builddir paths
@ 2014-07-18 12:14   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/bin/toaster b/bin/toaster
index 90cd982..1f90362 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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
diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index d2b2a23..c3beba9 100644
--- a/lib/toaster/bldcontrol/bbcontroller.py
+++ b/lib/toaster/bldcontrol/bbcontroller.py
@@ -170,27 +170,16 @@ class LocalhostBEController(BuildEnvironmentController):
         this controller manages the default build directory,
         the server setup and system start and stop for the localhost-type build environment
 
-        The address field is used as working directory; if not set, the build/ directory
-        is created
     """
+    from os.path import dirname as DN
 
     def __init__(self, be):
         super(LocalhostBEController, self).__init__(be)
         from os.path import dirname as DN
-        self.cwd = DN(DN(DN(DN(DN(os.path.realpath(__file__))))))
-        if self.be.address is None or len(self.be.address) == 0:
-            self.be.address = "build"
-            self.be.save()
-        self.bwd = self.be.address
         self.dburl = settings.getDATABASE_URL()
 
-        # transform relative paths to absolute ones
-        if not self.bwd.startswith("/"):
-            self.bwd = os.path.join(self.cwd, self.bwd)
-        self._createBE()
-
     def _shellcmd(self, command):
-        p = subprocess.Popen(command, cwd=self.cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        p = subprocess.Popen(command, cwd=self.be.sourcedir, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         (out,err) = p.communicate()
         if p.returncode:
             if len(err) == 0:
@@ -201,39 +190,49 @@ class LocalhostBEController(BuildEnvironmentController):
         else:
             return out
 
-    def _createBE(self):
-        assert self.cwd and os.path.exists(self.cwd)
-        self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.cwd, self.bwd))
+    def _createdirpath(self, path):
+        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)
+        self._createdirpath(self.be.builddir)
+        self._shellcmd("bash -c \"source %s/oe-init-build-env %s\"" % (self.be.sourcedir, self.be.builddir))
 
     def startBBServer(self):
-        assert self.cwd and os.path.exists(self.cwd)
-        print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && DATABASE_URL=%s source toaster start noweb && sleep 1\"" % (self.cwd, self.bwd, self.dburl))
+        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))
         # 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"
-        assert self.cwd and os.path.exists(self.bwd)
+        assert self.be.sourcedir and os.path.exists(self.be.builddir)
         self.be.bbaddress = "localhost"
         self.be.bbport = "8200"
         self.be.bbstate = BuildEnvironment.SERVER_STARTED
         self.be.save()
 
     def stopBBServer(self):
-        assert self.cwd
+        assert self.be.sourcedir
         print self._shellcmd("bash -c \"source %s/oe-init-build-env %s && %s source toaster stop\"" %
-            (self.cwd, self.bwd, (lambda: "" if self.be.bbtoken is None else "BBTOKEN=%s" % self.be.bbtoken)()))
+            (self.be.sourcedir, self.be.builddir, (lambda: "" if self.be.bbtoken is None else "BBTOKEN=%s" % self.be.bbtoken)()))
         self.be.bbstate = BuildEnvironment.SERVER_STOPPED
         self.be.save()
         print "Stopped server"
 
     def setLayers(self, layers):
-        assert self.cwd is not None
-        layerconf = os.path.join(self.bwd, "conf/bblayers.conf")
+        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)
         return True
 
     def release(self):
-        assert self.cwd and os.path.exists(self.bwd)
+        assert self.be.sourcedir and os.path.exists(self.be.builddir)
         import shutil
-        shutil.rmtree(os.path.join(self.cwd, "build"))
-        assert not os.path.exists(self.bwd)
+        shutil.rmtree(os.path.join(self.be.sourcedir, "build"))
+        assert not os.path.exists(self.be.builddir)
diff --git a/lib/toaster/bldcontrol/management/commands/checksettings.py b/lib/toaster/bldcontrol/management/commands/checksettings.py
new file mode 100644
index 0000000..da1c3b7
--- /dev/null
+++ b/lib/toaster/bldcontrol/management/commands/checksettings.py
@@ -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
+        
+
+            
+            
+
diff --git a/lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py b/lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
new file mode 100644
index 0000000..f522a50
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0002_auto__add_field_buildenvironment_sourcedir__add_field_buildenvironment.py
@@ -0,0 +1,106 @@
+# -*- 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 'BuildEnvironment.sourcedir'
+        db.add_column(u'bldcontrol_buildenvironment', 'sourcedir',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=512, blank=True),
+                      keep_default=False)
+
+        # Adding field 'BuildEnvironment.builddir'
+        db.add_column(u'bldcontrol_buildenvironment', 'builddir',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=512, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'BuildEnvironment.sourcedir'
+        db.delete_column(u'bldcontrol_buildenvironment', 'sourcedir')
+
+        # Deleting field 'BuildEnvironment.builddir'
+        db.delete_column(u'bldcontrol_buildenvironment', 'builddir')
+
+
+    models = {
+        u'bldcontrol.brlayer': {
+            'Meta': {'object_name': 'BRLayer'},
+            'commit': ('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 158874f..8285257 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -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)
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/8] toaster: added subdirectory entry for build layers
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:14   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 .../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

diff --git a/lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py b/lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py
new file mode 100644
index 0000000..b9ba838
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py
@@ -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
diff --git a/lib/toaster/bldcontrol/models.py b/lib/toaster/bldcontrol/models.py
index 8285257..1f253ff 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -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)
diff --git a/lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py b/lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
new file mode 100644
index 0000000..8a65221
--- /dev/null
+++ b/lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
@@ -0,0 +1,242 @@
+# -*- 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 'ProjectLayer.dirpath'
+        db.add_column(u'orm_projectlayer', 'dirpath',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=254),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'ProjectLayer.dirpath'
+        db.delete_column(u'orm_projectlayer', 'dirpath')
+
+
+    models = {
+        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.helptext': {
+            'Meta': {'object_name': 'HelpText'},
+            'area': ('django.db.models.fields.IntegerField', [], {}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        u'orm.layer': {
+            'Meta': {'object_name': 'Layer'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'local_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.layer_version': {
+            'Meta': {'object_name': 'Layer_Version'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_build'", 'to': u"orm['orm.Build']"}),
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+            'priority': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.logmessage': {
+            'Meta': {'object_name': 'LogMessage'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+        },
+        u'orm.package': {
+            'Meta': {'object_name': 'Package'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+            'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.package_dependency': {
+            'Meta': {'object_name': 'Package_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+        },
+        u'orm.package_file': {
+            'Meta': {'object_name': 'Package_File'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        },
+        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'})
+        },
+        u'orm.projectlayer': {
+            'Meta': {'object_name': 'ProjectLayer'},
+            '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'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+        },
+        u'orm.projecttarget': {
+            'Meta': {'object_name': 'ProjectTarget'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.projectvariable': {
+            'Meta': {'object_name': 'ProjectVariable'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.recipe_dependency': {
+            'Meta': {'object_name': 'Recipe_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+        },
+        u'orm.target': {
+            'Meta': {'object_name': 'Target'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.target_file': {
+            'Meta': {'object_name': 'Target_File'},
+            'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+            'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+            'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'size': ('django.db.models.fields.IntegerField', [], {}),
+            'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_image_file': {
+            'Meta': {'object_name': 'Target_Image_File'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+            'file_size': ('django.db.models.fields.IntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_installed_package': {
+            'Meta': {'object_name': 'Target_Installed_Package'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.task': {
+            'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+            'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+            'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'build_recipe'", 'to': u"orm['orm.Recipe']"}),
+            'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        u'orm.task_dependency': {
+            'Meta': {'object_name': 'Task_Dependency'},
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+        },
+        u'orm.variable': {
+            'Meta': {'object_name': 'Variable'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+            'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.variablehistory': {
+            'Meta': {'object_name': 'VariableHistory'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+        }
+    }
+
+    complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index f406429..9b7387a 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -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)
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 8fbe8a3..f9e8e51 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -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
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/8] toaster: added subdirectory entry for build layers
@ 2014-07-18 12:14   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 .../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

diff --git a/lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py b/lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py
new file mode 100644
index 0000000..b9ba838
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0003_auto__add_field_brlayer_dirpath.py
@@ -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
diff --git a/lib/toaster/bldcontrol/models.py b/lib/toaster/bldcontrol/models.py
index 8285257..1f253ff 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -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)
diff --git a/lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py b/lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
new file mode 100644
index 0000000..8a65221
--- /dev/null
+++ b/lib/toaster/orm/migrations/0011_auto__add_field_projectlayer_dirpath.py
@@ -0,0 +1,242 @@
+# -*- 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 'ProjectLayer.dirpath'
+        db.add_column(u'orm_projectlayer', 'dirpath',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=254),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'ProjectLayer.dirpath'
+        db.delete_column(u'orm_projectlayer', 'dirpath')
+
+
+    models = {
+        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.helptext': {
+            'Meta': {'object_name': 'HelpText'},
+            'area': ('django.db.models.fields.IntegerField', [], {}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        u'orm.layer': {
+            'Meta': {'object_name': 'Layer'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'local_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.layer_version': {
+            'Meta': {'object_name': 'Layer_Version'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_build'", 'to': u"orm['orm.Build']"}),
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+            'priority': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.logmessage': {
+            'Meta': {'object_name': 'LogMessage'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+        },
+        u'orm.package': {
+            'Meta': {'object_name': 'Package'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+            'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.package_dependency': {
+            'Meta': {'object_name': 'Package_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+        },
+        u'orm.package_file': {
+            'Meta': {'object_name': 'Package_File'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        },
+        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'})
+        },
+        u'orm.projectlayer': {
+            'Meta': {'object_name': 'ProjectLayer'},
+            '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'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+        },
+        u'orm.projecttarget': {
+            'Meta': {'object_name': 'ProjectTarget'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.projectvariable': {
+            'Meta': {'object_name': 'ProjectVariable'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.recipe_dependency': {
+            'Meta': {'object_name': 'Recipe_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+        },
+        u'orm.target': {
+            'Meta': {'object_name': 'Target'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.target_file': {
+            'Meta': {'object_name': 'Target_File'},
+            'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+            'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+            'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'size': ('django.db.models.fields.IntegerField', [], {}),
+            'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_image_file': {
+            'Meta': {'object_name': 'Target_Image_File'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+            'file_size': ('django.db.models.fields.IntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_installed_package': {
+            'Meta': {'object_name': 'Target_Installed_Package'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.task': {
+            'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+            'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+            'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'build_recipe'", 'to': u"orm['orm.Recipe']"}),
+            'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        u'orm.task_dependency': {
+            'Meta': {'object_name': 'Task_Dependency'},
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+        },
+        u'orm.variable': {
+            'Meta': {'object_name': 'Variable'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+            'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.variablehistory': {
+            'Meta': {'object_name': 'VariableHistory'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+        }
+    }
+
+    complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index f406429..9b7387a 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -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)
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 8fbe8a3..f9e8e51 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -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
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 0/8] Toaster project main page and other bugfixes and enhancements
@ 2014-07-18 12:14 ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>


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

-- 
1.9.1



^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 3/8] toaster: move bldcontrol initial_data fixture to code
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:14   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/bin/toaster b/bin/toaster
index 1f90362..78fbfa5 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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
diff --git a/lib/toaster/bldcontrol/fixtures/initial_data.json b/lib/toaster/bldcontrol/fixtures/initial_data.json
deleted file mode 100644
index b2f12b3..0000000
--- a/lib/toaster/bldcontrol/fixtures/initial_data.json
+++ /dev/null
@@ -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}}]
diff --git a/lib/toaster/bldcontrol/management/commands/checksettings.py b/lib/toaster/bldcontrol/management/commands/checksettings.py
index da1c3b7..a91dd15 100644
--- a/lib/toaster/bldcontrol/management/commands/checksettings.py
+++ b/lib/toaster/bldcontrol/management/commands/checksettings.py
@@ -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
diff --git a/lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py b/lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
new file mode 100644
index 0000000..d908578
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Write your forwards methods here."
+        # Note: Don't use "from appname.models import ModelName".
+        # Use orm.ModelName to refer to models in this application,
+        # and orm['appname.ModelName'] for models in other applications.
+        try:
+            orm.BuildEnvironment.objects.get(pk = 1)
+        except:
+            from django.utils import timezone
+            orm.BuildEnvironment.objects.create(pk = 1,
+                created = timezone.now(),
+                updated = timezone.now(),
+                betype = 0)
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+
+    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']
+    symmetrical = True
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 0/8] Toaster project main page and other bugfixes and enhancements
@ 2014-07-18 12:14 ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>


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

-- 
1.9.1



^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 3/8] toaster: move bldcontrol initial_data fixture to code
@ 2014-07-18 12:14   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/bin/toaster b/bin/toaster
index 1f90362..78fbfa5 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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
diff --git a/lib/toaster/bldcontrol/fixtures/initial_data.json b/lib/toaster/bldcontrol/fixtures/initial_data.json
deleted file mode 100644
index b2f12b3..0000000
--- a/lib/toaster/bldcontrol/fixtures/initial_data.json
+++ /dev/null
@@ -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}}]
diff --git a/lib/toaster/bldcontrol/management/commands/checksettings.py b/lib/toaster/bldcontrol/management/commands/checksettings.py
index da1c3b7..a91dd15 100644
--- a/lib/toaster/bldcontrol/management/commands/checksettings.py
+++ b/lib/toaster/bldcontrol/management/commands/checksettings.py
@@ -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
diff --git a/lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py b/lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
new file mode 100644
index 0000000..d908578
--- /dev/null
+++ b/lib/toaster/bldcontrol/migrations/0004_loadinitialdata.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Write your forwards methods here."
+        # Note: Don't use "from appname.models import ModelName".
+        # Use orm.ModelName to refer to models in this application,
+        # and orm['appname.ModelName'] for models in other applications.
+        try:
+            orm.BuildEnvironment.objects.get(pk = 1)
+        except:
+            from django.utils import timezone
+            orm.BuildEnvironment.objects.create(pk = 1,
+                created = timezone.now(),
+                updated = timezone.now(),
+                betype = 0)
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+
+    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']
+    symmetrical = True
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 4/8] toaster: add project main edit page
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:14   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/bin/toaster b/bin/toaster
index 78fbfa5..93d75b0 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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.
diff --git a/lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py b/lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
new file mode 100644
index 0000000..9e483f5
--- /dev/null
+++ b/lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
@@ -0,0 +1,252 @@
+# -*- 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 'ProjectLayer.optional'
+        db.add_column(u'orm_projectlayer', 'optional',
+                      self.gf('django.db.models.fields.BooleanField')(default=True),
+                      keep_default=False)
+
+        # Adding field 'ProjectTarget.task'
+        db.add_column(u'orm_projecttarget', 'task',
+                      self.gf('django.db.models.fields.CharField')(max_length=100, null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'ProjectLayer.optional'
+        db.delete_column(u'orm_projectlayer', 'optional')
+
+        # Deleting field 'ProjectTarget.task'
+        db.delete_column(u'orm_projecttarget', 'task')
+
+
+    models = {
+        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.helptext': {
+            'Meta': {'object_name': 'HelpText'},
+            'area': ('django.db.models.fields.IntegerField', [], {}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        u'orm.layer': {
+            'Meta': {'object_name': 'Layer'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'local_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.layer_version': {
+            'Meta': {'object_name': 'Layer_Version'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_build'", 'to': u"orm['orm.Build']"}),
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+            'priority': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.logmessage': {
+            'Meta': {'object_name': 'LogMessage'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+        },
+        u'orm.package': {
+            'Meta': {'object_name': 'Package'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+            'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.package_dependency': {
+            'Meta': {'object_name': 'Package_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+        },
+        u'orm.package_file': {
+            'Meta': {'object_name': 'Package_File'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        },
+        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'})
+        },
+        u'orm.projectlayer': {
+            'Meta': {'object_name': 'ProjectLayer'},
+            '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'}),
+            'optional': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+        },
+        u'orm.projecttarget': {
+            'Meta': {'object_name': 'ProjectTarget'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+        },
+        u'orm.projectvariable': {
+            'Meta': {'object_name': 'ProjectVariable'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.recipe_dependency': {
+            'Meta': {'object_name': 'Recipe_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+        },
+        u'orm.target': {
+            'Meta': {'object_name': 'Target'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.target_file': {
+            'Meta': {'object_name': 'Target_File'},
+            'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+            'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+            'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'size': ('django.db.models.fields.IntegerField', [], {}),
+            'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_image_file': {
+            'Meta': {'object_name': 'Target_Image_File'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+            'file_size': ('django.db.models.fields.IntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_installed_package': {
+            'Meta': {'object_name': 'Target_Installed_Package'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.task': {
+            'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+            'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+            'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'build_recipe'", 'to': u"orm['orm.Recipe']"}),
+            'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        u'orm.task_dependency': {
+            'Meta': {'object_name': 'Task_Dependency'},
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+        },
+        u'orm.variable': {
+            'Meta': {'object_name': 'Variable'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+            'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.variablehistory': {
+            'Meta': {'object_name': 'VariableHistory'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+        }
+    }
+
+    complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 9b7387a..f19a437 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -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)
diff --git a/lib/toaster/toastergui/static/css/default.css b/lib/toaster/toastergui/static/css/default.css
index 2c283fe..778d8b8 100644
--- a/lib/toaster/toastergui/static/css/default.css
+++ b/lib/toaster/toastergui/static/css/default.css
@@ -116,17 +116,31 @@ select { width: auto; }
 /* make tables Chrome-happy (me, not so much) */
 #otable { table-layout: fixed; word-wrap: break-word; }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+/* Configuration styles */
+.icon-trash { color: #B94A48; font-size: 16px; padding-left: 2px; }
+.icon-trash:hover { color: #943A38; text-decoration: none; cursor: pointer; }
+.icon-pencil, .icon-download-alt { font-size: 16px; color: #0088CC; padding-left: 2px; }
+.icon-pencil:hover, .icon-download-alt:hover { color: #005580; text-decoration: none; cursor: pointer; }
+.configuration-list li { line-height: 35px; font-size: 21px; font-weight: 200; }
+.configuration-list { font-size: 16px; margin-bottom: 1.5em; }
+.configuration-list i { font-size: 16px; }
+/*.configuration-layers { height: 135px; overflow: scroll; }*/
+.counter { font-weight: normal; }
+.well-alert { background-color: #FCF8E3; border: 1px solid #FBEED5; border-radius: 4px; }
+.well-alert > .lead { color: #C09853; padding-bottom: .75em; }
+.configuration-alert { margin-bottom: 0px; padding: 8px 14px; }
+.configuration-alert p { margin-bottom: 0px; }
+fieldset { padding-left: 19px; }
+.project-form { margin-top: 10px; }
+.add-layers .btn-block + .btn-block { margin-top: 0px; }
+input.huge { font-size: 17.5px; padding: 11px 19px; }
+.build-form { margin-bottom: 0px; padding-left: 20px; }
+a code { color: #0088CC; }
+a code:hover { color: #005580; }
+.localconf { font-size: 17.5px; margin-top: 40px; }
+.localconf code { font-size: 17.5px; }
+#add-layer-dependencies {  margin-top: 5px; }
+.artifact { width: 9em; }
+.control-group { margin-bottom: 0px; }
+#project-details form { margin: 0px; }
+dd form { margin: 10px 0 0 0; }
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index 1407d64..9ef249a 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -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>
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
index 71adb54..c859f6b 100644
--- a/lib/toaster/toastergui/templates/project.html
+++ b/lib/toaster/toastergui/templates/project.html
@@ -3,4 +3,360 @@
 {% load humanize %}
 {% block pagecontent %}
 
+<script>
+
+var buildrequests = [];
+
+function targetInPage(targetname) {
+    return targetname in $("ul#target-list > li > a").map(function (i, x) {return x.text});
+}
+
+function setEventHandlers() {
+    $("i#del-target-icon").unbind().click(function (evt) {
+        console.log("del target", evt.target.attributes["x-data"].value);
+        postEditAjaxRequest({"targetDel": evt.target.attributes["x-data"].value});
+    });
+    $("button#add-target-button").unbind().click( function (evt) {
+        if ( $("input#target")[0].value.length == 0) {
+            alert("cannot add empty target");
+            return;
+        }
+        postEditAjaxRequest({"targetAdd" : $("input#target")[0].value});
+    });
+}
+
+function onEditPageUpdate(data) {
+    // update targets
+    var i; var orightml = "";
+
+    $("span#target-count").html(data.targets.length);
+    for (i = 0; i < data.targets.length; i++) {
+        if (! targetInPage(data.targets[i].target)) {
+            orightml += '<li><a href="#">'+data.targets[i].target;
+            if (data.targets[i].task != "" && data.targets[i].task !== null) {
+                orightml += " ("+data.targets[i].task+")";
+            }
+            orightml += '</a><i title="" data-original-title="" class="icon-trash" id="del-target-icon" x-data="'+data.targets[i].pk+'"></i></li>';
+        }
+    }
+
+    $("ul#target-list").html(orightml);
+
+    // update recent builds
+
+    setEventHandlers();
+}
+
+function onEditAjaxSuccess(data, textstatus) {
+    console.log("XHR returned:", data, "(" + textstatus + ")");
+    if (data.error != "ok") {
+        alert("error on request:\n" + data.error);
+        return;
+    }
+    onEditPageUpdate(data);
+}
+
+function onEditAjaxError(jqXHR, textstatus, error) {
+    alert("XHR errored:\n" + error + "\n(" + textstatus + ")");
+}
+
+function postEditAjaxRequest(reqdata) {
+    var ajax = $.ajax({
+            type:"POST",
+            data: $.param(reqdata),
+            url:"{% url 'xhr_projectedit' project.id%}",
+            headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+            success: onEditAjaxSuccess,
+            error: onEditAjaxError,
+        })
+}
+
+$(document).ready(function () {
+    setEventHandlers();
+});
+
+</script>
+
+
+            <div class="page-header">
+                <h1>
+                    {{project.name}}
+                    {% if project.build_set.all.count == 0 %}
+                    <small>No builds yet</small>
+                    {% else %}
+                    <small><a href="#">{{project.build_set.all.count}} builds</a></small>
+                    {% endif %}
+                </h1>
+            </div>
+
+
+            <div class="well">
+                <!--div class="control-group error"-->
+                    <button id="build-all-button" class="btn btn-primary btn-large">Build all added targets</button>
+                    <div class="input-append build-form controls">
+                        <input class="huge input-xxlarge" placeholder="Or enter the target you want to build" autocomplete="off" data-minlength="1" data-autocomplete="off" data-provide="typeahead" data-source="" type="text">
+                        <button id="build-button" class="btn btn-large" disabled="">Build</button>
+                    </div>
+                    <script>
+/* Provide XHR calls for the "build" buttons.*/
+$("button#build-all-button").click( function (evt) {
+    var ajax = $.ajax({
+            type:"POST",
+            url:"{% url 'xhr_projectbuild' project.id %}",
+            headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+            success: function (data, textstatus) {
+                if (data.error != "ok") {
+                    alert("XHR fail: " + data.error );
+                }
+            },
+            error: function (jqXHR, textstatus, error) { alert("XHR errored:" + error + "(" + textstatus + ")"); },
+        })
+});
+
+                    </script>
+                    <!--span class="help-inline">This target is not provided <br />by any of your added layers
+                        <i class="icon-question-sign get-help get-help-red" title="Review your list of added layers to make sure one of them provides core-image-xyz. Clicking on a layer name will give you all the information Toaster has about the layer"></i>
+                    </span>
+                </div-->
+            </div>
+
+            <div id="meta-tizen-alert" class="alert alert-info lead air" style="display:none;">
+                 <button type="button" class="close" data-dismiss="alert">?</button>
+                You have added <strong>6</strong> layers: <a href="#">meta-tizen</a> and its dependencies (<a href="#">meta-efl</a>, <a href="#">meta-intel</a>, <a href="#">meta-multimedia</a>, <a href="#">meta-oe</a> and <a href="#">meta-ruby</a>).
+            </div>
+
+
+
+
+
+        {% if builds|length > 0 or buildrequests|length > 0 %}
+            <h2 class="air">Recent Builds</h2>
+
+    <div id="scheduled-builds">
+          {% for br in buildrequests %}
+<div class="alert {% if br.0.state == br.0.REQ_FAILED%}alert-error{%else%}alert-info{%endif%}" id="build-request">
+        <div class="row-fluid">
+            <div class="lead span4">
+                <span>
+                {{br.0.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} {{br.1.machine.value}} (Created {{br.0.created}})
+                </span>
+            </div>
+            <div class="span2">
+	 {{br.0.get_state_display}}
+            </div>
+	    <div class="span8">
+{% if br.state == br.REQ_FAILED%}
+	{% for bre in br.0.brerror_set.all %} {{bre.errmsg}} ({{bre.errtype}}) <br/><hr/><code>{{bre.traceback}}</code>{%endfor%}
+{%endif%}
+            </div>
+
+        </div>
+</div>
+
+          {% endfor %}
+
+    </div>
+
+
+
+<!-- Lifted from build.html -->
+          {% for build in builds %}
+<div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}">
+        <div class="row-fluid">
+            <div class="lead span5">
+                {%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}
+    {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
+                <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
+    {% endif %}
+                <span data-toggle="tooltip" {%if build.target_set.all.count > 1%}title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} {{build.machine}} ({{build.completed_on|naturaltime}})</span>
+    {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
+                </a>
+    {% endif %}
+            </div>
+    {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
+            <div class="span2 lead">
+    {% if  build.errors_no %}
+                <i class="icon-minus-sign red"></i> <a href="{%url 'builddashboard' build.pk%}#errors" class="error">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
+    {% endif %}
+            </div>
+            <div class="span2 lead">
+    {% if  build.warnings_no %}
+                <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}#warnings" class="warning">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>
+    {% endif %}
+            </div >
+            <div class="lead pull-right">
+                Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
+            </div>
+    {%endif%}{%if build.outcome == build.IN_PROGRESS %}
+            <div class="span4">
+                <div class="progress" style="margin-top:5px;" data-toggle="tooltip" title="{{build.completeper}}% of tasks complete">
+                    <div style="width: {{build.completeper}}%;" class="bar"></div>
+                </div>
+            </div>
+            <div class="lead pull-right">ETA: in {{build.eta|naturaltime}}</div>
+    {%endif%}
+        </div>
+    </div>
+          {% endfor %}
+<!-- end of lift-->
+        {%endif%}
+
+            <h2 class="air">Project configuration</h2>
+
+            <div class="row-fluid">
+
+                <div id="layer-container" class="well well-transparent span4">
+                    <h3>
+                        Add layers
+                        <i data-original-title="OpenEmbedded organises metadata into modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/1.6.1/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <form style="margin-top:20px;">
+                        <div class="input-append">
+                            <input class="input-xlarge" id="layer" autocomplete="off" placeholder="Type a layer name" data-provide="typeahead" data-source="" data-minlength="1" data-autocomplete="off" type="text">
+                            <button id="add-layer" class="btn" disabled="">Add</button>
+                        </div>
+                        <div id="import-alert" class="alert alert-info" style="display:none;">
+                            Toaster does not know about this layer. Please <a href="#">import it</a>
+                        </div>
+                        <div id="dependency-alert" class="alert alert-info" style="display:none;">
+                            <p><strong>meta-tizen</strong> depends on the layers below. Check the ones you want to add: </p>
+                            <ul class="unstyled">
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-efl
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-intel
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-multimedia
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-oe
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-ruby
+                                    </label>
+                                </li>
+                            </ul>
+                            <button id="add-layer-dependencies" class="btn btn-info add-layer">Add layers</button>
+                        </div>
+
+                        <p><a href="#">Import your layer</a> | <a href="#">View all layers</a></p>
+                    </form>
+
+                    <h4 class="air">
+                        Added layers
+                        <span class="muted counter">{{project.projectlayer_set.count}}</span>
+                        <i data-original-title="Your added layers will be listed in this same order in your <code>bblayers.conf</code> file" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h4>
+                    <ul class="unstyled configuration-list">
+            {% for pl in project.projectlayer_set.all %}
+                        <li>
+                            <a href="#">{{pl.name}} (<span class="layer-version">{{pl.giturl}}</span>)</a>
+                {% if pl.optional %}
+                            <i title="" data-original-title="" class="icon-trash" id="del-layer-icon" x-data="{{pl.pk}}"></i>
+                {% endif %}
+                        </li>
+            {% endfor %}
+                    </ul>
+                </div>
+
+                <div id="target-container" class="well well-transparent span4">
+                    <h3>
+                        Add targets
+                        <i data-original-title="A target is what you want to build, usually an image recipe that produces a root file system" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <form style="margin-top:20px;">
+                        <div class="input-append">
+                            <input id="target" class="input-xlarge" autocomplete="off" placeholder="Type a target name" data-provide="typeahead" data-source="" data-minlength="1" data-autocomplete="off" type="text">
+                            <button id="add-target-button" class="btn" type="button">Add</button>
+                        </div>
+
+                        <p><a href="#" class="link">View all targets</a></p>
+                    </form>
+                    <h4 class="air">
+                        Added targets
+                        <span id="target-count" class="muted counter">{{project.projecttarget_set.count}}</span>
+                    </h4>
+                    <ul class="unstyled configuration-list" id="target-list">
+        {% for target in project.projecttarget_set.all %}
+            {% if target %}
+                        <li>
+                            <a href="#">{{target.target}}{% if target.task%} (target.task){%endif%}</a>
+			    {% if target.notprovided %}
+                            <i title="" data-original-title="" id="msg1" class="icon-exclamation-sign get-help-yellow" data-title="<strong>Target may not be provided</strong>" data-content="From the layer information it currently has, Toaster thinks this target is not provided by any of your added layers. If a target is not provided by one of your added layers, the build will fail.<h5>What Toaster suggests</h5><p>The <a href='#'>meta-abc</a> and <a href='#'>meta-efg</a> layers provide core-image-notprovided. You could add one of them to your project.</p><button class='btn btn-block'>Add meta-abc</button><button class='btn btn-block'>Add meta-efg</button><button id='dismiss1' class='btn btn-block btn-info'>Stop showing this message</button>"></i>
+		            {% elif target.notknown %}
+                            <i title="" data-original-title="" id="msg2" class="icon-exclamation-sign get-help-yellow" data-title="<strong>Target may not be provided</strong>" data-content="From the layer information it currently has, Toaster thinks this target is not provided by any of your added layers. If a target is not provided by one of your added layers, the build will fail.<h5>What Toaster suggests</h5><p>Review your added layers to make sure one of them provides core-image-unknown. Clicking on a layer name will give you all the information Toaster has about the layer. </p> <button class='btn btn-block btn-info'>Stop showing this message</button>"></i>
+                            {% endif %}
+                            <i title="" data-original-title="" class="icon-trash" id="del-target-icon" x-data="{{target.pk}}"></i>
+                        </li>
+            {% endif %}
+        {% endfor %}
+
+
+                    </ul>
+                </div>
+
+                <div class="well well-transparent span4">
+                    <h3>
+                        Set machine
+                        <i data-original-title="The machine is the hardware for which you want to build. You can only set one machine per project" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <p class="lead">
+                        {{machine}}
+                        <i title="" data-original-title="" class="icon-pencil"></i>
+                    </p>
+                    <h3>
+                        Set distro
+                        <i data-original-title="When you build an image using the Yocto Project and do not alter the distro, you are creating a Poky distribution" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <p class="lead">
+                        {{distro}}
+                        <i title="" data-original-title="" class="icon-pencil"></i>
+                    </p>
+                    <p class="localconf">
+                        <a href="#" class="link">Edit the <code>local.conf</code> file</a>
+                        <i data-original-title="The <code>local.conf</code> file is where other project configuration options are set. Pretty much any configuration option can be set in this file. Each option, like everything else in the build system, is a variable - value pair" class="icon-question-sign get-help heading-help" title=""></i>
+                    </p>
+                </div>
+            </div>
+
+            <h2>Project details</h2>
+
+            <div class="well well-transparent">
+                <h3>Project name</h3>
+                <p class="lead">
+                    {{project.name}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+                <h3>Project owner</h3>
+                <p class="lead">
+                    {{puser.username}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+                <h3>Owner's email</h3>
+                <p class="lead">
+            {{puser.email}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+                <h3>Yocto Project version</h3>
+                <p class="lead">
+                    {{project.branch}} - {{project.short_description}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+            </div>
 {% endblock %}
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index 0d7a4c3..7c4f894 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -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/')),
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index f9e8e51..bd65c08 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -30,7 +30,7 @@ from orm.models import Target_Installed_Package, Target_File, Target_Image_File
 from django.views.decorators.cache import cache_control
 from django.core.urlresolvers import reverse
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
-from django.http import HttpResponseBadRequest
+from django.http import HttpResponseBadRequest, HttpResponseNotFound
 from django.utils import timezone
 from datetime import timedelta
 from django.utils import formats
@@ -1761,8 +1761,6 @@ def image_information_dir(request, build_id, target_id, packagefile_id):
 
 
 import toastermain.settings
-def managedcontextprocessor(request):
-    return { "MANAGED" : toastermain.settings.MANAGED }
 
 
 # we have a set of functions if we're in managed mode, or
@@ -1773,7 +1771,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
+    from orm.models import Project, ProjectLayer, ProjectTarget, ProjectVariable
+    from bldcontrol.models import BuildRequest
 
     import traceback
 
@@ -1831,19 +1830,113 @@ if toastermain.settings.MANAGED:
                 else:
                     context['alert'] = str(e)
                 return render(request, template, context)
+
         raise Exception("Invalid HTTP method for this page")
 
     # Shows the edit project page
     def project(request, pid):
         template = "project.html"
-        context = {}
+        try:
+            prj = Project.objects.get(id = pid)
+        except Project.DoesNotExist:
+            return HttpResponseNotFound("<h1>Project id " + pid + " is unavailable</h1>")
+
+        try:
+            puser = User.objects.get(id = prj.user_id)
+        except User.DoesNotExist:
+            puser = None
+
+        context = {
+            "project" : prj,
+            #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
+            "buildrequests" : map(lambda x: (x, {"machine" : x.brvariable_set.filter(name="MACHINE")[0]}), prj.buildrequest_set.order_by("-pk")),
+            "builds" : prj.build_set.all(),
+            "puser": puser,
+        }
+        try:
+            context["machine"] = prj.projectvariable_set.get(name="MACHINE").value
+        except ProjectVariable.DoesNotExist:
+            context["machine"] = "-- not set yet"
+
+        try:
+            context["distro"] = prj.projectvariable_set.get(name="DISTRO").value
+        except ProjectVariable.DoesNotExist:
+            context["distro"] = "-- not set yet"
+
+
         return render(request, template, context)
 
+    import json
+
+    def xhr_projectbuild(request, pid):
+        try:
+            if request.method != "POST":
+                raise BadParameterException("invalid method")
+            prj = Project.objects.get(id = pid)
+
+            if prj.projecttarget_set.count() == 0:
+                raise BadParameterException("no targets selected")
+
+            br = prj.schedule_build()
+            return HttpResponse(json.dumps({"error":"ok",
+                "brtarget" : map(lambda x: x.target, br.brtarget_set.all()),
+                "machine" : br.brvariable_set.get(name="MACHINE").value,
+
+            }), content_type = "application/json")
+        except Exception as e:
+            return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
+
+    def xhr_projectedit(request, pid):
+        try:
+            prj = Project.objects.get(id = pid)
+            # add targets
+            if 'targetAdd' in request.POST:
+                for t in request.POST['targetAdd'].strip().split(" "):
+                    if ":" in t:
+                        target, task = t.split(":")
+                    else:
+                        target = t
+                        task = ""
+
+                    pt, created = ProjectTarget.objects.get_or_create(project = prj, target = target, task = task)
+            # remove targets
+            if 'targetDel' in request.POST:
+                for t in request.POST['targetDel'].strip().split(" "):
+                    pt = ProjectTarget.objects.get(pk = int(t)).delete()
+
+            # add layers
+
+            # remove layers
+
+            # return all project settings
+            return HttpResponse(json.dumps( {
+                "error": "ok",
+                "layers": map(lambda x: (x.name, x.giturl), prj.projectlayer_set.all()),
+                "targets" : map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all()),
+                "variables": map(lambda x: (x.name, x.value), prj.projectvariable_set.all()),
+                }), content_type = "application/json")
+
+        except Exception as e:
+            return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
+
 
 else:
     # these are pages that are NOT available in interactive mode
+    def managedcontextprocessor(request):
+        return {
+            "projects": [],
+            "MANAGED" : toastermain.settings.MANAGED
+        }
+
     def newproject(request):
         raise Exception("page not available in interactive mode")
 
-    def project(request):
+    def project(request, pid):
+        raise Exception("page not available in interactive mode")
+
+    def xhr_projectbuild(request, pid):
         raise Exception("page not available in interactive mode")
+
+    def xhr_projectedit(request, pid):
+        raise Exception("page not available in interactive mode")
+
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 4/8] toaster: add project main edit page
@ 2014-07-18 12:14   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/bin/toaster b/bin/toaster
index 78fbfa5..93d75b0 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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.
diff --git a/lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py b/lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
new file mode 100644
index 0000000..9e483f5
--- /dev/null
+++ b/lib/toaster/orm/migrations/0012_auto__add_field_projectlayer_optional__add_field_projecttarget_task.py
@@ -0,0 +1,252 @@
+# -*- 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 'ProjectLayer.optional'
+        db.add_column(u'orm_projectlayer', 'optional',
+                      self.gf('django.db.models.fields.BooleanField')(default=True),
+                      keep_default=False)
+
+        # Adding field 'ProjectTarget.task'
+        db.add_column(u'orm_projecttarget', 'task',
+                      self.gf('django.db.models.fields.CharField')(max_length=100, null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'ProjectLayer.optional'
+        db.delete_column(u'orm_projectlayer', 'optional')
+
+        # Deleting field 'ProjectTarget.task'
+        db.delete_column(u'orm_projecttarget', 'task')
+
+
+    models = {
+        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.helptext': {
+            'Meta': {'object_name': 'HelpText'},
+            'area': ('django.db.models.fields.IntegerField', [], {}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        u'orm.layer': {
+            'Meta': {'object_name': 'Layer'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'local_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.layer_version': {
+            'Meta': {'object_name': 'Layer_Version'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_build'", 'to': u"orm['orm.Build']"}),
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+            'priority': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.logmessage': {
+            'Meta': {'object_name': 'LogMessage'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+        },
+        u'orm.package': {
+            'Meta': {'object_name': 'Package'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+            'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.package_dependency': {
+            'Meta': {'object_name': 'Package_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+        },
+        u'orm.package_file': {
+            'Meta': {'object_name': 'Package_File'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        },
+        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'})
+        },
+        u'orm.projectlayer': {
+            'Meta': {'object_name': 'ProjectLayer'},
+            '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'}),
+            'optional': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+        },
+        u'orm.projecttarget': {
+            'Meta': {'object_name': 'ProjectTarget'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+        },
+        u'orm.projectvariable': {
+            'Meta': {'object_name': 'ProjectVariable'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.recipe_dependency': {
+            'Meta': {'object_name': 'Recipe_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+        },
+        u'orm.target': {
+            'Meta': {'object_name': 'Target'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.target_file': {
+            'Meta': {'object_name': 'Target_File'},
+            'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+            'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+            'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'size': ('django.db.models.fields.IntegerField', [], {}),
+            'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_image_file': {
+            'Meta': {'object_name': 'Target_Image_File'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+            'file_size': ('django.db.models.fields.IntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_installed_package': {
+            'Meta': {'object_name': 'Target_Installed_Package'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.task': {
+            'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+            'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+            'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'build_recipe'", 'to': u"orm['orm.Recipe']"}),
+            'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        u'orm.task_dependency': {
+            'Meta': {'object_name': 'Task_Dependency'},
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+        },
+        u'orm.variable': {
+            'Meta': {'object_name': 'Variable'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+            'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.variablehistory': {
+            'Meta': {'object_name': 'VariableHistory'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+        }
+    }
+
+    complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 9b7387a..f19a437 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -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)
diff --git a/lib/toaster/toastergui/static/css/default.css b/lib/toaster/toastergui/static/css/default.css
index 2c283fe..778d8b8 100644
--- a/lib/toaster/toastergui/static/css/default.css
+++ b/lib/toaster/toastergui/static/css/default.css
@@ -116,17 +116,31 @@ select { width: auto; }
 /* make tables Chrome-happy (me, not so much) */
 #otable { table-layout: fixed; word-wrap: break-word; }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+/* Configuration styles */
+.icon-trash { color: #B94A48; font-size: 16px; padding-left: 2px; }
+.icon-trash:hover { color: #943A38; text-decoration: none; cursor: pointer; }
+.icon-pencil, .icon-download-alt { font-size: 16px; color: #0088CC; padding-left: 2px; }
+.icon-pencil:hover, .icon-download-alt:hover { color: #005580; text-decoration: none; cursor: pointer; }
+.configuration-list li { line-height: 35px; font-size: 21px; font-weight: 200; }
+.configuration-list { font-size: 16px; margin-bottom: 1.5em; }
+.configuration-list i { font-size: 16px; }
+/*.configuration-layers { height: 135px; overflow: scroll; }*/
+.counter { font-weight: normal; }
+.well-alert { background-color: #FCF8E3; border: 1px solid #FBEED5; border-radius: 4px; }
+.well-alert > .lead { color: #C09853; padding-bottom: .75em; }
+.configuration-alert { margin-bottom: 0px; padding: 8px 14px; }
+.configuration-alert p { margin-bottom: 0px; }
+fieldset { padding-left: 19px; }
+.project-form { margin-top: 10px; }
+.add-layers .btn-block + .btn-block { margin-top: 0px; }
+input.huge { font-size: 17.5px; padding: 11px 19px; }
+.build-form { margin-bottom: 0px; padding-left: 20px; }
+a code { color: #0088CC; }
+a code:hover { color: #005580; }
+.localconf { font-size: 17.5px; margin-top: 40px; }
+.localconf code { font-size: 17.5px; }
+#add-layer-dependencies {  margin-top: 5px; }
+.artifact { width: 9em; }
+.control-group { margin-bottom: 0px; }
+#project-details form { margin: 0px; }
+dd form { margin: 10px 0 0 0; }
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index 1407d64..9ef249a 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -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>
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
index 71adb54..c859f6b 100644
--- a/lib/toaster/toastergui/templates/project.html
+++ b/lib/toaster/toastergui/templates/project.html
@@ -3,4 +3,360 @@
 {% load humanize %}
 {% block pagecontent %}
 
+<script>
+
+var buildrequests = [];
+
+function targetInPage(targetname) {
+    return targetname in $("ul#target-list > li > a").map(function (i, x) {return x.text});
+}
+
+function setEventHandlers() {
+    $("i#del-target-icon").unbind().click(function (evt) {
+        console.log("del target", evt.target.attributes["x-data"].value);
+        postEditAjaxRequest({"targetDel": evt.target.attributes["x-data"].value});
+    });
+    $("button#add-target-button").unbind().click( function (evt) {
+        if ( $("input#target")[0].value.length == 0) {
+            alert("cannot add empty target");
+            return;
+        }
+        postEditAjaxRequest({"targetAdd" : $("input#target")[0].value});
+    });
+}
+
+function onEditPageUpdate(data) {
+    // update targets
+    var i; var orightml = "";
+
+    $("span#target-count").html(data.targets.length);
+    for (i = 0; i < data.targets.length; i++) {
+        if (! targetInPage(data.targets[i].target)) {
+            orightml += '<li><a href="#">'+data.targets[i].target;
+            if (data.targets[i].task != "" && data.targets[i].task !== null) {
+                orightml += " ("+data.targets[i].task+")";
+            }
+            orightml += '</a><i title="" data-original-title="" class="icon-trash" id="del-target-icon" x-data="'+data.targets[i].pk+'"></i></li>';
+        }
+    }
+
+    $("ul#target-list").html(orightml);
+
+    // update recent builds
+
+    setEventHandlers();
+}
+
+function onEditAjaxSuccess(data, textstatus) {
+    console.log("XHR returned:", data, "(" + textstatus + ")");
+    if (data.error != "ok") {
+        alert("error on request:\n" + data.error);
+        return;
+    }
+    onEditPageUpdate(data);
+}
+
+function onEditAjaxError(jqXHR, textstatus, error) {
+    alert("XHR errored:\n" + error + "\n(" + textstatus + ")");
+}
+
+function postEditAjaxRequest(reqdata) {
+    var ajax = $.ajax({
+            type:"POST",
+            data: $.param(reqdata),
+            url:"{% url 'xhr_projectedit' project.id%}",
+            headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+            success: onEditAjaxSuccess,
+            error: onEditAjaxError,
+        })
+}
+
+$(document).ready(function () {
+    setEventHandlers();
+});
+
+</script>
+
+
+            <div class="page-header">
+                <h1>
+                    {{project.name}}
+                    {% if project.build_set.all.count == 0 %}
+                    <small>No builds yet</small>
+                    {% else %}
+                    <small><a href="#">{{project.build_set.all.count}} builds</a></small>
+                    {% endif %}
+                </h1>
+            </div>
+
+
+            <div class="well">
+                <!--div class="control-group error"-->
+                    <button id="build-all-button" class="btn btn-primary btn-large">Build all added targets</button>
+                    <div class="input-append build-form controls">
+                        <input class="huge input-xxlarge" placeholder="Or enter the target you want to build" autocomplete="off" data-minlength="1" data-autocomplete="off" data-provide="typeahead" data-source="" type="text">
+                        <button id="build-button" class="btn btn-large" disabled="">Build</button>
+                    </div>
+                    <script>
+/* Provide XHR calls for the "build" buttons.*/
+$("button#build-all-button").click( function (evt) {
+    var ajax = $.ajax({
+            type:"POST",
+            url:"{% url 'xhr_projectbuild' project.id %}",
+            headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+            success: function (data, textstatus) {
+                if (data.error != "ok") {
+                    alert("XHR fail: " + data.error );
+                }
+            },
+            error: function (jqXHR, textstatus, error) { alert("XHR errored:" + error + "(" + textstatus + ")"); },
+        })
+});
+
+                    </script>
+                    <!--span class="help-inline">This target is not provided <br />by any of your added layers
+                        <i class="icon-question-sign get-help get-help-red" title="Review your list of added layers to make sure one of them provides core-image-xyz. Clicking on a layer name will give you all the information Toaster has about the layer"></i>
+                    </span>
+                </div-->
+            </div>
+
+            <div id="meta-tizen-alert" class="alert alert-info lead air" style="display:none;">
+                 <button type="button" class="close" data-dismiss="alert">?</button>
+                You have added <strong>6</strong> layers: <a href="#">meta-tizen</a> and its dependencies (<a href="#">meta-efl</a>, <a href="#">meta-intel</a>, <a href="#">meta-multimedia</a>, <a href="#">meta-oe</a> and <a href="#">meta-ruby</a>).
+            </div>
+
+
+
+
+
+        {% if builds|length > 0 or buildrequests|length > 0 %}
+            <h2 class="air">Recent Builds</h2>
+
+    <div id="scheduled-builds">
+          {% for br in buildrequests %}
+<div class="alert {% if br.0.state == br.0.REQ_FAILED%}alert-error{%else%}alert-info{%endif%}" id="build-request">
+        <div class="row-fluid">
+            <div class="lead span4">
+                <span>
+                {{br.0.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} {{br.1.machine.value}} (Created {{br.0.created}})
+                </span>
+            </div>
+            <div class="span2">
+	 {{br.0.get_state_display}}
+            </div>
+	    <div class="span8">
+{% if br.state == br.REQ_FAILED%}
+	{% for bre in br.0.brerror_set.all %} {{bre.errmsg}} ({{bre.errtype}}) <br/><hr/><code>{{bre.traceback}}</code>{%endfor%}
+{%endif%}
+            </div>
+
+        </div>
+</div>
+
+          {% endfor %}
+
+    </div>
+
+
+
+<!-- Lifted from build.html -->
+          {% for build in builds %}
+<div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}">
+        <div class="row-fluid">
+            <div class="lead span5">
+                {%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}
+    {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
+                <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
+    {% endif %}
+                <span data-toggle="tooltip" {%if build.target_set.all.count > 1%}title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} {{build.machine}} ({{build.completed_on|naturaltime}})</span>
+    {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
+                </a>
+    {% endif %}
+            </div>
+    {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
+            <div class="span2 lead">
+    {% if  build.errors_no %}
+                <i class="icon-minus-sign red"></i> <a href="{%url 'builddashboard' build.pk%}#errors" class="error">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
+    {% endif %}
+            </div>
+            <div class="span2 lead">
+    {% if  build.warnings_no %}
+                <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}#warnings" class="warning">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>
+    {% endif %}
+            </div >
+            <div class="lead pull-right">
+                Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
+            </div>
+    {%endif%}{%if build.outcome == build.IN_PROGRESS %}
+            <div class="span4">
+                <div class="progress" style="margin-top:5px;" data-toggle="tooltip" title="{{build.completeper}}% of tasks complete">
+                    <div style="width: {{build.completeper}}%;" class="bar"></div>
+                </div>
+            </div>
+            <div class="lead pull-right">ETA: in {{build.eta|naturaltime}}</div>
+    {%endif%}
+        </div>
+    </div>
+          {% endfor %}
+<!-- end of lift-->
+        {%endif%}
+
+            <h2 class="air">Project configuration</h2>
+
+            <div class="row-fluid">
+
+                <div id="layer-container" class="well well-transparent span4">
+                    <h3>
+                        Add layers
+                        <i data-original-title="OpenEmbedded organises metadata into modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/1.6.1/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <form style="margin-top:20px;">
+                        <div class="input-append">
+                            <input class="input-xlarge" id="layer" autocomplete="off" placeholder="Type a layer name" data-provide="typeahead" data-source="" data-minlength="1" data-autocomplete="off" type="text">
+                            <button id="add-layer" class="btn" disabled="">Add</button>
+                        </div>
+                        <div id="import-alert" class="alert alert-info" style="display:none;">
+                            Toaster does not know about this layer. Please <a href="#">import it</a>
+                        </div>
+                        <div id="dependency-alert" class="alert alert-info" style="display:none;">
+                            <p><strong>meta-tizen</strong> depends on the layers below. Check the ones you want to add: </p>
+                            <ul class="unstyled">
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-efl
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-intel
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-multimedia
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-oe
+                                    </label>
+                                </li>
+                                <li>
+                                    <label class="checkbox">
+                                        <input checked="checked" type="checkbox">
+                                        meta-ruby
+                                    </label>
+                                </li>
+                            </ul>
+                            <button id="add-layer-dependencies" class="btn btn-info add-layer">Add layers</button>
+                        </div>
+
+                        <p><a href="#">Import your layer</a> | <a href="#">View all layers</a></p>
+                    </form>
+
+                    <h4 class="air">
+                        Added layers
+                        <span class="muted counter">{{project.projectlayer_set.count}}</span>
+                        <i data-original-title="Your added layers will be listed in this same order in your <code>bblayers.conf</code> file" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h4>
+                    <ul class="unstyled configuration-list">
+            {% for pl in project.projectlayer_set.all %}
+                        <li>
+                            <a href="#">{{pl.name}} (<span class="layer-version">{{pl.giturl}}</span>)</a>
+                {% if pl.optional %}
+                            <i title="" data-original-title="" class="icon-trash" id="del-layer-icon" x-data="{{pl.pk}}"></i>
+                {% endif %}
+                        </li>
+            {% endfor %}
+                    </ul>
+                </div>
+
+                <div id="target-container" class="well well-transparent span4">
+                    <h3>
+                        Add targets
+                        <i data-original-title="A target is what you want to build, usually an image recipe that produces a root file system" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <form style="margin-top:20px;">
+                        <div class="input-append">
+                            <input id="target" class="input-xlarge" autocomplete="off" placeholder="Type a target name" data-provide="typeahead" data-source="" data-minlength="1" data-autocomplete="off" type="text">
+                            <button id="add-target-button" class="btn" type="button">Add</button>
+                        </div>
+
+                        <p><a href="#" class="link">View all targets</a></p>
+                    </form>
+                    <h4 class="air">
+                        Added targets
+                        <span id="target-count" class="muted counter">{{project.projecttarget_set.count}}</span>
+                    </h4>
+                    <ul class="unstyled configuration-list" id="target-list">
+        {% for target in project.projecttarget_set.all %}
+            {% if target %}
+                        <li>
+                            <a href="#">{{target.target}}{% if target.task%} (target.task){%endif%}</a>
+			    {% if target.notprovided %}
+                            <i title="" data-original-title="" id="msg1" class="icon-exclamation-sign get-help-yellow" data-title="<strong>Target may not be provided</strong>" data-content="From the layer information it currently has, Toaster thinks this target is not provided by any of your added layers. If a target is not provided by one of your added layers, the build will fail.<h5>What Toaster suggests</h5><p>The <a href='#'>meta-abc</a> and <a href='#'>meta-efg</a> layers provide core-image-notprovided. You could add one of them to your project.</p><button class='btn btn-block'>Add meta-abc</button><button class='btn btn-block'>Add meta-efg</button><button id='dismiss1' class='btn btn-block btn-info'>Stop showing this message</button>"></i>
+		            {% elif target.notknown %}
+                            <i title="" data-original-title="" id="msg2" class="icon-exclamation-sign get-help-yellow" data-title="<strong>Target may not be provided</strong>" data-content="From the layer information it currently has, Toaster thinks this target is not provided by any of your added layers. If a target is not provided by one of your added layers, the build will fail.<h5>What Toaster suggests</h5><p>Review your added layers to make sure one of them provides core-image-unknown. Clicking on a layer name will give you all the information Toaster has about the layer. </p> <button class='btn btn-block btn-info'>Stop showing this message</button>"></i>
+                            {% endif %}
+                            <i title="" data-original-title="" class="icon-trash" id="del-target-icon" x-data="{{target.pk}}"></i>
+                        </li>
+            {% endif %}
+        {% endfor %}
+
+
+                    </ul>
+                </div>
+
+                <div class="well well-transparent span4">
+                    <h3>
+                        Set machine
+                        <i data-original-title="The machine is the hardware for which you want to build. You can only set one machine per project" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <p class="lead">
+                        {{machine}}
+                        <i title="" data-original-title="" class="icon-pencil"></i>
+                    </p>
+                    <h3>
+                        Set distro
+                        <i data-original-title="When you build an image using the Yocto Project and do not alter the distro, you are creating a Poky distribution" class="icon-question-sign get-help heading-help" title=""></i>
+                    </h3>
+                    <p class="lead">
+                        {{distro}}
+                        <i title="" data-original-title="" class="icon-pencil"></i>
+                    </p>
+                    <p class="localconf">
+                        <a href="#" class="link">Edit the <code>local.conf</code> file</a>
+                        <i data-original-title="The <code>local.conf</code> file is where other project configuration options are set. Pretty much any configuration option can be set in this file. Each option, like everything else in the build system, is a variable - value pair" class="icon-question-sign get-help heading-help" title=""></i>
+                    </p>
+                </div>
+            </div>
+
+            <h2>Project details</h2>
+
+            <div class="well well-transparent">
+                <h3>Project name</h3>
+                <p class="lead">
+                    {{project.name}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+                <h3>Project owner</h3>
+                <p class="lead">
+                    {{puser.username}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+                <h3>Owner's email</h3>
+                <p class="lead">
+            {{puser.email}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+                <h3>Yocto Project version</h3>
+                <p class="lead">
+                    {{project.branch}} - {{project.short_description}}
+                    <i title="" data-original-title="" class="icon-pencil"></i>
+                </p>
+            </div>
 {% endblock %}
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index 0d7a4c3..7c4f894 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -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/')),
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index f9e8e51..bd65c08 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -30,7 +30,7 @@ from orm.models import Target_Installed_Package, Target_File, Target_Image_File
 from django.views.decorators.cache import cache_control
 from django.core.urlresolvers import reverse
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
-from django.http import HttpResponseBadRequest
+from django.http import HttpResponseBadRequest, HttpResponseNotFound
 from django.utils import timezone
 from datetime import timedelta
 from django.utils import formats
@@ -1761,8 +1761,6 @@ def image_information_dir(request, build_id, target_id, packagefile_id):
 
 
 import toastermain.settings
-def managedcontextprocessor(request):
-    return { "MANAGED" : toastermain.settings.MANAGED }
 
 
 # we have a set of functions if we're in managed mode, or
@@ -1773,7 +1771,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
+    from orm.models import Project, ProjectLayer, ProjectTarget, ProjectVariable
+    from bldcontrol.models import BuildRequest
 
     import traceback
 
@@ -1831,19 +1830,113 @@ if toastermain.settings.MANAGED:
                 else:
                     context['alert'] = str(e)
                 return render(request, template, context)
+
         raise Exception("Invalid HTTP method for this page")
 
     # Shows the edit project page
     def project(request, pid):
         template = "project.html"
-        context = {}
+        try:
+            prj = Project.objects.get(id = pid)
+        except Project.DoesNotExist:
+            return HttpResponseNotFound("<h1>Project id " + pid + " is unavailable</h1>")
+
+        try:
+            puser = User.objects.get(id = prj.user_id)
+        except User.DoesNotExist:
+            puser = None
+
+        context = {
+            "project" : prj,
+            #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
+            "buildrequests" : map(lambda x: (x, {"machine" : x.brvariable_set.filter(name="MACHINE")[0]}), prj.buildrequest_set.order_by("-pk")),
+            "builds" : prj.build_set.all(),
+            "puser": puser,
+        }
+        try:
+            context["machine"] = prj.projectvariable_set.get(name="MACHINE").value
+        except ProjectVariable.DoesNotExist:
+            context["machine"] = "-- not set yet"
+
+        try:
+            context["distro"] = prj.projectvariable_set.get(name="DISTRO").value
+        except ProjectVariable.DoesNotExist:
+            context["distro"] = "-- not set yet"
+
+
         return render(request, template, context)
 
+    import json
+
+    def xhr_projectbuild(request, pid):
+        try:
+            if request.method != "POST":
+                raise BadParameterException("invalid method")
+            prj = Project.objects.get(id = pid)
+
+            if prj.projecttarget_set.count() == 0:
+                raise BadParameterException("no targets selected")
+
+            br = prj.schedule_build()
+            return HttpResponse(json.dumps({"error":"ok",
+                "brtarget" : map(lambda x: x.target, br.brtarget_set.all()),
+                "machine" : br.brvariable_set.get(name="MACHINE").value,
+
+            }), content_type = "application/json")
+        except Exception as e:
+            return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
+
+    def xhr_projectedit(request, pid):
+        try:
+            prj = Project.objects.get(id = pid)
+            # add targets
+            if 'targetAdd' in request.POST:
+                for t in request.POST['targetAdd'].strip().split(" "):
+                    if ":" in t:
+                        target, task = t.split(":")
+                    else:
+                        target = t
+                        task = ""
+
+                    pt, created = ProjectTarget.objects.get_or_create(project = prj, target = target, task = task)
+            # remove targets
+            if 'targetDel' in request.POST:
+                for t in request.POST['targetDel'].strip().split(" "):
+                    pt = ProjectTarget.objects.get(pk = int(t)).delete()
+
+            # add layers
+
+            # remove layers
+
+            # return all project settings
+            return HttpResponse(json.dumps( {
+                "error": "ok",
+                "layers": map(lambda x: (x.name, x.giturl), prj.projectlayer_set.all()),
+                "targets" : map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all()),
+                "variables": map(lambda x: (x.name, x.value), prj.projectvariable_set.all()),
+                }), content_type = "application/json")
+
+        except Exception as e:
+            return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
+
 
 else:
     # these are pages that are NOT available in interactive mode
+    def managedcontextprocessor(request):
+        return {
+            "projects": [],
+            "MANAGED" : toastermain.settings.MANAGED
+        }
+
     def newproject(request):
         raise Exception("page not available in interactive mode")
 
-    def project(request):
+    def project(request, pid):
+        raise Exception("page not available in interactive mode")
+
+    def xhr_projectbuild(request, pid):
         raise Exception("page not available in interactive mode")
+
+    def xhr_projectedit(request, pid):
+        raise Exception("page not available in interactive mode")
+
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 5/8] toaster: properly set layers when running a build
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:14   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index c3beba9..1e58c67 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 dd8f84b..fa8c1a9 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 0000000..98aeb41
--- /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 1f253ff..8c271ff 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()
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 5/8] toaster: properly set layers when running a build
@ 2014-07-18 12:14   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:14 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 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

diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index c3beba9..1e58c67 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 dd8f84b..fa8c1a9 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 0000000..98aeb41
--- /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 1f253ff..8c271ff 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()
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 6/8] document requirements for the python environment
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:15   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:15 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 .gitignore       | 2 ++
 requirements.txt | 4 ++++
 2 files changed, 6 insertions(+)
 create mode 100644 requirements.txt

diff --git a/.gitignore b/.gitignore
index 5d9bc50..5adf4d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..2bb07e6
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+Django==1.5
+South==0.8.4
+argparse==1.2.1
+wsgiref==0.1.2
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 6/8] document requirements for the python environment
@ 2014-07-18 12:15   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:15 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 .gitignore       | 2 ++
 requirements.txt | 4 ++++
 2 files changed, 6 insertions(+)
 create mode 100644 requirements.txt

diff --git a/.gitignore b/.gitignore
index 5d9bc50..5adf4d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..2bb07e6
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+Django==1.5
+South==0.8.4
+argparse==1.2.1
+wsgiref==0.1.2
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 7/8] toaster start script allows for Django 1.6
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:15   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:15 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 bin/toaster      | 13 ++++++++-----
 requirements.txt |  2 +-
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/bin/toaster b/bin/toaster
index 93d75b0..ce16de6 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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
 
diff --git a/requirements.txt b/requirements.txt
index 2bb07e6..19b5293 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-Django==1.5
+Django==1.6
 South==0.8.4
 argparse==1.2.1
 wsgiref==0.1.2
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 7/8] toaster start script allows for Django 1.6
@ 2014-07-18 12:15   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:15 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 bin/toaster      | 13 ++++++++-----
 requirements.txt |  2 +-
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/bin/toaster b/bin/toaster
index 93d75b0..ce16de6 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -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
 
diff --git a/requirements.txt b/requirements.txt
index 2bb07e6..19b5293 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-Django==1.5
+Django==1.6
 South==0.8.4
 argparse==1.2.1
 wsgiref==0.1.2
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 8/8] toasterui: fixing event.data clash
  2014-07-18 12:14 ` Alex DAMIAN
@ 2014-07-18 12:15   ` Alex DAMIAN
  -1 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:15 UTC (permalink / raw)
  To: bitbake-devel, toaster

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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]

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/bb/event.py              |  2 +-
 lib/bb/ui/buildinfohelper.py | 30 +++++++++++++++---------------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/lib/bb/event.py b/lib/bb/event.py
index 8b47ae5..e2f6b9c 100644
--- a/lib/bb/event.py
+++ b/lib/bb/event.py
@@ -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):
     """
diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 29cfc81..f64423b 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -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'],
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 8/8] toasterui: fixing event.data clash
@ 2014-07-18 12:15   ` Alex DAMIAN
  0 siblings, 0 replies; 18+ messages in thread
From: Alex DAMIAN @ 2014-07-18 12:15 UTC (permalink / raw)
  To: bitbake-devel, toaster; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

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]

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/bb/event.py              |  2 +-
 lib/bb/ui/buildinfohelper.py | 30 +++++++++++++++---------------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/lib/bb/event.py b/lib/bb/event.py
index 8b47ae5..e2f6b9c 100644
--- a/lib/bb/event.py
+++ b/lib/bb/event.py
@@ -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):
     """
diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 29cfc81..f64423b 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -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'],
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2014-07-18 12:15 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-18 12:14 [PATCH 0/8] Toaster project main page and other bugfixes and enhancements Alex DAMIAN
2014-07-18 12:14 ` Alex DAMIAN
2014-07-18 12:14 ` [PATCH 1/8] toaster: add fields for sourcedir and builddir paths Alex DAMIAN
2014-07-18 12:14   ` Alex DAMIAN
2014-07-18 12:14 ` [PATCH 2/8] toaster: added subdirectory entry for build layers Alex DAMIAN
2014-07-18 12:14   ` Alex DAMIAN
2014-07-18 12:14 ` [PATCH 3/8] toaster: move bldcontrol initial_data fixture to code Alex DAMIAN
2014-07-18 12:14   ` Alex DAMIAN
2014-07-18 12:14 ` [PATCH 4/8] toaster: add project main edit page Alex DAMIAN
2014-07-18 12:14   ` Alex DAMIAN
2014-07-18 12:14 ` [PATCH 5/8] toaster: properly set layers when running a build Alex DAMIAN
2014-07-18 12:14   ` Alex DAMIAN
2014-07-18 12:15 ` [PATCH 6/8] document requirements for the python environment Alex DAMIAN
2014-07-18 12:15   ` Alex DAMIAN
2014-07-18 12:15 ` [PATCH 7/8] toaster start script allows for Django 1.6 Alex DAMIAN
2014-07-18 12:15   ` Alex DAMIAN
2014-07-18 12:15 ` [PATCH 8/8] toasterui: fixing event.data clash Alex DAMIAN
2014-07-18 12:15   ` Alex DAMIAN

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.