Skip to contentSkip to conversation

Bugfixes for Toaster

PATCH New Alex DAMIAN 1 participant 9 messages Mar 11, 2014

lore
Share & tools
lore
#0 lore raw

cover Bugfixes for Toaster

From: Alexandru DAMIAN <[email protected]>

This is a set of bugfixes for Toaster.

Patches were reviewed on the [email protected] mailing list.

Can you please pull them into bitbake master ?

Thank you,
Alex

The following changes since commit 76029d08ad56a0a264ff9738a0336971a455b7f5:

runqueue: Use SIGCHLD instead of polling waitpid (2014-03-09 18:58:49 -0700)

are available in the git repository at:

git://git.yoctoproject.org/poky-contrib adamian/submission-11032014
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=adamian/submission-11032014

Alexandru DAMIAN (4):
toaster: update database schema
toaster: write files-in-image to the database
toaster: add commands to list and delete builds
toaster: erase checks for stop command

Belen Barros Pena (1):
toaster: Fix help text for task details outcome

David Reyna (3):
toaster: add empty outcome support to task page
toaster: add 'bblayers.conf' files to local config filter
toaster: fix 'show all' labels for tasks and variables

 bin/toaster                                        |  17 +-
 lib/bb/ui/buildinfohelper.py                       | 115 +++++++++-
 ...ge_size__add_field_target_license_manifest_p.py | 235 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |  32 +--
 .../toastergui/templates/basetable_top.html        |   2 +-
 lib/toaster/toastergui/templates/task.html         |   6 +-
 lib/toaster/toastergui/views.py                    |   6 +-
 lib/toaster/toastermain/management/__init__.py     |   0
 .../toastermain/management/commands/__init__.py    |   0
 .../toastermain/management/commands/builddelete.py |  33 +++
 .../toastermain/management/commands/buildslist.py  |  13 ++
 11 files changed, 423 insertions(+), 36 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0006_auto__add_field_target_image_size__add_field_target_license_manifest_p.py
 create mode 100644 lib/toaster/toastermain/management/__init__.py
 create mode 100644 lib/toaster/toastermain/management/commands/__init__.py
 create mode 100644 lib/toaster/toastermain/management/commands/builddelete.py
 create mode 100644 lib/toaster/toastermain/management/commands/buildslist.py
Signature · 1 line
1.8.3.2
#1 lore raw

PATCH 1/8 toaster: update database schema

From: Alexandru DAMIAN <[email protected]>

This is a schema update to account for

* relaxing constraints in Target_Image_File table
* modifying permission filed to string value
* adding license_manifest_path field
* adding image_size field

---
 ...ge_size__add_field_target_license_manifest_p.py | 235 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |  11 +-
 2 files changed, 241 insertions(+), 5 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0006_auto__add_field_target_image_size__add_field_target_license_manifest_p.py
lib/toaster/orm/migrations/0006_auto__add_field_target_image_size__add_field_target_license_manifest_p.py new file +235 -0
new file mode 100644
index 0000000..8195d09
@@ -0,0 +1,235 @@
+# -*- 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 'Target.image_size'
+        db.add_column(u'orm_target', 'image_size',
+                      self.gf('django.db.models.fields.IntegerField')(default=0),
+                      keep_default=False)
+
+        # Adding field 'Target.license_manifest_path'
+        db.add_column(u'orm_target', 'license_manifest_path',
+                      self.gf('django.db.models.fields.CharField')(max_length=500, null=True),
+                      keep_default=False)
+
+
+        # Changing field 'Target_File.permission'
+        db.alter_column(u'orm_target_file', 'permission', self.gf('django.db.models.fields.CharField')(max_length=16))
+
+        # Changing field 'Target_File.sym_target'
+        db.alter_column(u'orm_target_file', 'sym_target_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['orm.Target_File']))
+
+        # Changing field 'Target_File.directory'
+        db.alter_column(u'orm_target_file', 'directory_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['orm.Target_File']))
+
+    def backwards(self, orm):
+        # Deleting field 'Target.image_size'
+        db.delete_column(u'orm_target', 'image_size')
+
+        # Deleting field 'Target.license_manifest_path'
+        db.delete_column(u'orm_target', 'license_manifest_path')
+
+
+        # Changing field 'Target_File.permission'
+        db.alter_column(u'orm_target_file', 'permission', self.gf('django.db.models.fields.IntegerField')())
+
+        # User chose to not deal with backwards NULL issues for 'Target_File.sym_target'
+        raise RuntimeError("Cannot reverse this migration. 'Target_File.sym_target' and its values cannot be restored.")
+        
+        # The following code is provided here to aid in writing a correct migration
+        # Changing field 'Target_File.sym_target'
+        db.alter_column(u'orm_target_file', 'sym_target_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['orm.Target_File']))
+
+        # User chose to not deal with backwards NULL issues for 'Target_File.directory'
+        raise RuntimeError("Cannot reverse this migration. 'Target_File.directory' and its values cannot be restored.")
+        
+        # The following code is provided here to aid in writing a correct migration
+        # Changing field 'Target_File.directory'
+        db.alter_column(u'orm_target_file', 'directory_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['orm.Target_File']))
+
+    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'}),
+            '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.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.CharField', [], {'max_length': '200', '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.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '100', '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': '100'}),
+            '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': '16'}),
+            '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
lib/toaster/orm/models.py +6 -5
index ba3ade0..6706da8 100644
@@ -55,6 +55,8 @@ class Target(models.Model):
     build = models.ForeignKey(Build)
     target = models.CharField(max_length=100)
     is_image = models.BooleanField(default = False)
+    image_size = models.IntegerField(default=0)
+    license_manifest_path = models.CharField(max_length=500, null=True)
 
     def __str__(self):
         return self.target
@@ -72,7 +74,6 @@ class Target_File(models.Model):
     ITYPE_FIFO = 5
     ITYPE_CHARACTER = 6
     ITYPE_BLOCK = 7
-    ITYPE_SYMBLINK = 8
     ITYPES = ( (ITYPE_REGULAR ,'regular'),
         ( ITYPE_DIRECTORY ,'directory'),
         ( ITYPE_SYMLINK ,'symlink'),
@@ -80,17 +81,17 @@ class Target_File(models.Model):
         ( ITYPE_FIFO ,'fifo'),
         ( ITYPE_CHARACTER ,'character'),
         ( ITYPE_BLOCK ,'block'),
-        ( ITYPE_SYMLINK ,'symblink'))
+        )
 
     target = models.ForeignKey(Target)
     path = models.FilePathField()
     size = models.IntegerField()
     inodetype = models.IntegerField(choices = ITYPES)
-    permission = models.IntegerField()
+    permission = models.CharField(max_length=16)
     owner = models.CharField(max_length=128)
     group = models.CharField(max_length=128)
-    directory = models.ForeignKey('Target_File', related_name="directory_set")
-    sym_target = models.ForeignKey('Target_File', related_name="symlink_set", blank=True)
+    directory = models.ForeignKey('Target_File', related_name="directory_set", null=True)
+    sym_target = models.ForeignKey('Target_File', related_name="symlink_set", null=True)
 
 
 class TaskManager(models.Manager):
Signature · 1 line
1.8.3.2
#2 lore raw

PATCH 2/8 toaster: write files-in-image to the database

From: Alexandru DAMIAN <[email protected]>

Adding code to write files-in-image data from the metadata event to the database.

---
 lib/bb/ui/buildinfohelper.py | 115 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 113 insertions(+), 2 deletions(-)
lib/bb/ui/buildinfohelper.py +113 -2
index d7b526a..f9b067f 100644
@@ -27,7 +27,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toaster.toastermain.settings")
 import toaster.toastermain.settings as toaster_django_settings
 from toaster.orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage
 from toaster.orm.models import Variable, VariableHistory
-from toaster.orm.models import Package, Package_File, Target_Installed_Package
+from toaster.orm.models import Package, Package_File, Target_Installed_Package, Target_File
 from toaster.orm.models import Task_Dependency, Package_Dependency
 from toaster.orm.models import Recipe_Dependency
 from bb.msg import BBLogFormatter as format
@@ -181,6 +181,113 @@ class ORMWrapper(object):
 
         return layer_object
 
+    def save_target_file_information(self, build_obj, target_obj, filedata):
+        assert isinstance(build_obj, Build)
+        assert isinstance(target_obj, Target)
+        dirs = filedata['dirs']
+        files = filedata['files']
+        syms = filedata['syms']
+
+        # we insert directories, ordered by name depth
+        for d in sorted(dirs, key=lambda x:len(x[-1].split("/"))):
+            (user, group, size) = d[1:4]
+            permission = d[0][1:]
+            path = d[4].lstrip(".")
+            if len(path) == 0:
+                # we create the root directory as a special case
+                path = "/"
+                tf_obj = Target_File.objects.create(
+                        target = target_obj,
+                        path = path,
+                        size = size,
+                        inodetype = Target_File.ITYPE_DIRECTORY,
+                        permission = permission,
+                        owner = user,
+                        group = group,
+                        )
+                tf_obj.directory = tf_obj
+                tf_obj.save()
+                continue
+            parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
+            if len(parent_path) == 0:
+                parent_path = "/"
+            parent_obj = Target_File.objects.get(target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
+            tf_obj = Target_File.objects.create(
+                        target = target_obj,
+                        path = path,
+                        size = size,
+                        inodetype = Target_File.ITYPE_DIRECTORY,
+                        permission = permission,
+                        owner = user,
+                        group = group,
+                        directory = parent_obj)
+
+
+        # we insert files
+        for d in files:
+            (user, group, size) = d[1:4]
+            permission = d[0][1:]
+            path = d[4].lstrip(".")
+            parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
+            inodetype = Target_File.ITYPE_REGULAR
+            if permission.startswith('b'):
+                inodetype = Target_File.ITYPE_BLOCK
+            if permission.startswith('c'):
+                inodetype = Target_File.ITYPE_CHARACTER
+            if permission.startswith('p'):
+                inodetype = Target_File.ITYPE_FIFO
+
+            tf_obj = Target_File.objects.create(
+                        target = target_obj,
+                        path = path,
+                        size = size,
+                        inodetype = inodetype,
+                        permission = permission,
+                        owner = user,
+                        group = group)
+            parent_obj = Target_File.objects.get(target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
+            tf_obj.directory = parent_obj
+            tf_obj.save()
+
+        # we insert symlinks
+        for d in syms:
+            (user, group, size) = d[1:4]
+            permission = d[0][1:]
+            path = d[4].lstrip(".")
+            filetarget_path = d[6]
+
+            parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
+            if not filetarget_path.startswith("/"):
+                # we have a relative path, get a normalized absolute one
+                filetarget_path = parent_path + "/" + filetarget_path
+                fcp = filetarget_path.split("/")
+                fcpl = []
+                for i in fcp:
+                    if i == "..":
+                        fcpl.pop()
+                    else:
+                        fcpl.append(i)
+                filetarget_path = "/".join(fcpl)
+
+            try:
+                filetarget_obj = Target_File.objects.get(target = target_obj, path = filetarget_path)
+            except Exception as e:
+                # we might have an invalid link; no way to detect this. just set it to None
+                filetarget_obj = None
+
+            parent_obj = Target_File.objects.get(target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
+
+            tf_obj = Target_File.objects.create(
+                        target = target_obj,
+                        path = path,
+                        size = size,
+                        inodetype = Target_File.ITYPE_REGULAR,
+                        permission = permission,
+                        owner = user,
+                        group = group,
+                        directory = parent_obj,
+                        sym_target = filetarget_obj)
+
 
     def save_target_package_information(self, build_obj, target_obj, packagedict, pkgpnmap, recipes):
         assert isinstance(build_obj, Build)
@@ -613,6 +720,10 @@ class BuildInfoHelper(object):
                 pkgdata = event.data['pkgdata']
                 imgdata = event.data['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]
+                self.orm_wrapper.save_target_file_information(self.internal_state['build'], target, filedata)
+
+
 
     def store_dependency_information(self, event):
         assert '_depgraph' in vars(event)
@@ -692,7 +803,7 @@ class BuildInfoHelper(object):
             task_info['task_name'] = taskname
             task_obj = self.orm_wrapper.get_update_task_object(task_info)
             return task_obj
- 
+
         # create tasks
         tasks = {}
         for taskdesc in event._depgraph['tdepends']:
Signature · 1 line
1.8.3.2
#3 lore raw

PATCH 3/8 toaster: add commands to list and delete builds

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

We add Django commands for the manage.py to manage the database content.

The two commands added are:
* buildslist - produces a list of current builds
* builddelete - deletes a build and all associated data from the database

---
 lib/toaster/toastermain/management/__init__.py     |  0
 .../toastermain/management/commands/__init__.py    |  0
 .../toastermain/management/commands/builddelete.py | 33 ++++++++++++++++++++++
 .../toastermain/management/commands/buildslist.py  | 13 +++++++++
 4 files changed, 46 insertions(+)
 create mode 100644 lib/toaster/toastermain/management/__init__.py
 create mode 100644 lib/toaster/toastermain/management/commands/__init__.py
 create mode 100644 lib/toaster/toastermain/management/commands/builddelete.py
 create mode 100644 lib/toaster/toastermain/management/commands/buildslist.py
lib/toaster/toastermain/management/__init__.py new file +0 -0
new file mode 100644
index 0000000..e69de29
lib/toaster/toastermain/management/commands/__init__.py new file +0 -0
new file mode 100644
index 0000000..e69de29
lib/toaster/toastermain/management/commands/builddelete.py new file +33 -0
new file mode 100644
index 0000000..5cec436
@@ -0,0 +1,33 @@
+from django.core.management.base import BaseCommand, CommandError
+from orm.models import Build
+import os
+
+
+
+class Command(BaseCommand):
+    args    = "buildId"
+    help    = "Deletes selected build"
+
+    def handle(self, buildId, *args, **options):
+        b = Build.objects.get(pk = buildId)
+        # theoretically, just b.delete() would suffice
+        # however SQLite runs into problems when you try to
+        # delete too many rows at once, so we delete some direct
+        # relationships from Build manually.
+
+        for t in b.target_set.all():
+            t.delete()
+        for t in b.task_build.all():
+            t.delete()
+        for p in b.package_set.all():
+            p.delete()
+        for lv in b.layer_version_build.all():
+            lv.delete()
+        for v in b.variable_build.all():
+            v.delete()
+        for l in b.logmessage_set.all():
+            l.delete()
+
+        # this should take care of the rest
+        b.delete()
+
lib/toaster/toastermain/management/commands/buildslist.py new file +13 -0
new file mode 100644
index 0000000..cad987f
@@ -0,0 +1,13 @@
+from django.core.management.base import NoArgsCommand, CommandError
+from orm.models import Build
+import os
+
+
+
+class Command(NoArgsCommand):
+    args    = ""
+    help    = "Lists current builds"
+
+    def handle_noargs(self,**options):
+        for b in Build.objects.all():
+            print "%d: %s %s %s" % (b.pk, b.machine, b.distro, ",".join([x.target for x in b.target_set.all()]))
Signature · 1 line
1.8.3.2
#4 lore raw

PATCH 4/8 toaster: erase checks for stop command

In reply to Alex DAMIAN

From: Alexandru DAMIAN <[email protected]>

We remove the checks for the stop command, since we assume the user really means it wants to shutdown the system when he issues the command.

	[YOCTO #5376]
---
 bin/toaster | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)
bin/toaster +2 -15
index 38e71ac..9c0a15f 100755
@@ -143,25 +143,12 @@ if [ -e $BUILDDIR/bitbake.lock ]; then
 fi
 
 if [ ${CMD} == "start" ] && ( [ $lock -eq 0 ] || [ -e $BUILDDIR/.toastermain.pid ] ); then
-    echo "Error: bitbake lock state error. System is already on." 2>&1
-    return 3
-elif [ ${CMD} == "stop" ] && ( [ $lock -eq 1 ] || ! [ -e $BUILDDIR/.toastermain.pid ] ) ; then
-    echo "Error: bitbake lock state error. Trying to stop a stopped system ?
-If you think the system is hanged up, you can try to manually stop system with the commands
-
-# BBSERVER=localhost:8200 bitbake -m
-
-and
-
-# webserverKillAll
-" 2>&1
+    echo "Error: bitbake lock state error. File locks show that the system is on." 2>&1
+    echo "If you see problems, stop and then start the system again." 2>&1
     return 3
 fi
 
 
-
-
-
 # Execute the commands
 
 case $CMD in
Signature · 1 line
1.8.3.2
#5 lore raw

PATCH 5/8 toaster: Fix help text for task details outcome

In reply to Alex DAMIAN

From: Belen Barros Pena <[email protected]>

Changes made to models.py and task.html to make sure the help text for the task outcome is displayed.

[YOCTO #5917]

---
 lib/toaster/orm/models.py                  | 20 ++++++++++----------
 lib/toaster/toastergui/templates/task.html |  2 +-
 2 files changed, 11 insertions(+), 11 deletions(-)
lib/toaster/orm/models.py +10 -10
index 6706da8..0646813 100644
@@ -140,14 +140,14 @@ class Task(models.Model):
         (OUTCOME_EMPTY, 'Empty'),
     )
 
-    TASK_OUTCOME_HELP = {
-        OUTCOME_SUCCESS:'This task completed successfully',
-        OUTCOME_COVERED:'This task did not run because its output is provided by another task',
-        OUTCOME_CACHED:'This task restored output from the sstate-cache directory or mirrors',
-        OUTCOME_PREBUILT:'This task did not run because its outcome was reused from a previous build',
-        OUTCOME_FAILED:'This task did not complete',
-        OUTCOME_NA:''
-    }
+    TASK_OUTCOME_HELP = (
+        (OUTCOME_SUCCESS, 'This task completed successfully'),
+        (OUTCOME_COVERED, 'This task did not run because its output is provided by another task'),
+        (OUTCOME_CACHED, 'This task restored output from the sstate-cache directory or mirrors'),
+        (OUTCOME_PREBUILT, 'This task did not run because its outcome was reused from a previous build'),
+        (OUTCOME_FAILED, 'This task did not complete'),
+        (OUTCOME_NA, ''),
+    )
 
     search_allowed_fields = [ "recipe__name", "recipe__version", "task_name", "logfile" ]
 
@@ -156,8 +156,8 @@ class Task(models.Model):
     def get_related_setscene(self):
         return Task.objects.related_setscene(self)
 
-    def outcome_help(self):
-        return Task.TASK_OUTCOME_HELP[self.outcome]
+    def get_outcome_help(self):
+        return Task.TASK_OUTCOME_HELP[self.outcome][1]
 
     def get_executed_display(self):
         if self.task_executed:
lib/toaster/toastergui/templates/task.html +1 -1
index c5d6176..a8bcb52 100644
@@ -18,7 +18,7 @@
 {# Outcome section #}
 <h2 {{ task|task_color:True }}>
     {{task.get_outcome_display}}
-    <i class="icon-question-sign get-help heading-help" title="" data-original-title="{{task.outcome_help}}"></i>
+    <i class="icon-question-sign get-help heading-help" title="{{task.get_outcome_help}}"></i>
 </h2>
 {%if task.task_executed %}
     {# executed tasks outcome #}
Signature · 1 line
1.8.3.2
#6 lore raw

PATCH 6/8 toaster: add empty outcome support to task page

In reply to Alex DAMIAN

From: David Reyna <[email protected]>

Add the new "empty" outcome value to the task detail page, with help text, help notice, plus sorting for empty in the tasks page.

[YOCTO #5917]

---
 lib/toaster/orm/models.py                  | 1 +
 lib/toaster/toastergui/templates/task.html | 4 ++++
 lib/toaster/toastergui/views.py            | 2 +-
 3 files changed, 6 insertions(+), 1 deletion(-)
lib/toaster/orm/models.py +1 -0
index 0646813..93506d7 100644
@@ -146,6 +146,7 @@ class Task(models.Model):
         (OUTCOME_CACHED, 'This task restored output from the sstate-cache directory or mirrors'),
         (OUTCOME_PREBUILT, 'This task did not run because its outcome was reused from a previous build'),
         (OUTCOME_FAILED, 'This task did not complete'),
+        (OUTCOME_EMPTY, 'This task has no executable content'),
         (OUTCOME_NA, ''),
     )
 
lib/toaster/toastergui/templates/task.html +4 -0
index a8bcb52..5d79d44 100644
@@ -121,6 +121,10 @@
 		</code>
             </dd>
         </dl>
+    {%elif task.outcome == task.OUTCOME_EMPTY%}
+        <div class="alert alert-info details">
+            This task is empty because it has the <code>noexec</code> flag set to <code>1</code>, or the task function is empty
+        </div>
     {% endif %}
 {% endif %}
 
lib/toaster/toastergui/views.py +1 -1
index ccf9585..6273d4c 100644
@@ -558,7 +558,7 @@ def tasks_common(request, build_id, variant):
                                ('Cached Tasks', 'outcome:%d'%Task.OUTCOME_CACHED),
                                ('Prebuilt Tasks', 'outcome:%d'%Task.OUTCOME_PREBUILT),
                                ('Covered Tasks', 'outcome:%d'%Task.OUTCOME_COVERED),
-                               ('Empty Tasks', 'outcome:%d'%Task.OUTCOME_NA),
+                               ('Empty Tasks', 'outcome:%d'%Task.OUTCOME_EMPTY),
                                ]
                    }
 
Signature · 1 line
1.8.3.2
#7 lore raw

PATCH 7/8 toaster: add 'bblayers.conf' files to local config filter

In reply to Alex DAMIAN

From: David Reyna <[email protected]>

In the variables table, add to the local configuration filter the 'bblayers.conf' in addition to the 'local.conf' files.

[YOCTO #5912]

---
 lib/toaster/toastergui/views.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
lib/toaster/toastergui/views.py +3 -1
index 6273d4c..f8c8ccd 100644
@@ -795,6 +795,8 @@ def configvars(request, build_id):
     file_filter= search_term + ":"
     if filter_string.find('conf/local.conf') > 0:
         file_filter += 'conf/local.conf'
+    if filter_string.find('conf/bblayers.conf') > 0:
+        file_filter += 'conf/bblayers.conf'
     if filter_string.find('conf/machine/') > 0:
         file_filter += 'conf/machine/'
     if filter_string.find('conf/distro/') > 0:
@@ -833,7 +835,7 @@ def configvars(request, build_id):
                     'class' : 'vhistory__file_name',
                     'label': 'Show:',
                     'options' : [
-                               ('Local configuration variables', 'vhistory__file_name__contains:conf/local.conf'),
+                               ('Local configuration variables', 'vhistory__file_name__regex:conf/(local|bblayers).conf'),
                                ('Machine configuration variables', 'vhistory__file_name__contains:conf/machine/'),
                                ('Distro configuration variables', 'vhistory__file_name__contains:conf/distro/'),
                                ('Layer configuration variables', 'vhistory__file_name__contains:conf/layer.conf'),
Signature · 1 line
1.8.3.2
#8 lore raw

PATCH 8/8 toaster: fix 'show all' labels for tasks and variables

In reply to Alex DAMIAN

From: David Reyna <[email protected]>

Fix the 'Show all' button label to indicate 'variables' and 'tasks' instead of the internal page label for the variables page and the Disk I/O, CPU Usage, and Time pages.

[YOCTO #5915]

---
 lib/toaster/toastergui/templates/basetable_top.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
lib/toaster/toastergui/templates/basetable_top.html +1 -1
index 0cd948c..e2549f7 100644
@@ -79,7 +79,7 @@
                 {%if tc.orderfield%}<a {%if tc.ordericon%} class="sorted" {%endif%}href="javascript:reload_params({'page': 1, 'orderby' : '{{tc.orderfield}}' })" >{{tc.name}}</a>{%else%}<span class="muted">{{tc.name}}</span>{%endif%}
                 {%if tc.ordericon%} <i class="icon-caret-{{tc.ordericon}}"></i>{%endif%}
                 {%if tc.filter%}<div class="btn-group pull-right">
-                    <a href="#filter_{{tc.filter.class}}" role="button" class="btn btn-mini {%if request.GET.filter%}{{tc.filter.options|filtered_icon:request.GET.filter}} {%endif%}" {%if request.GET.filter%} title="<p>{{tc.filter.options|filtered_tooltip:request.GET.filter}}</p><p><a class='btn btn-small btn-primary' href=javascript:reload_params({'filter':''})>Show all {{objectname}}</a></p>" {%endif%} data-toggle="modal"> <i class="icon-filter filtered"></i> </a>
+                    <a href="#filter_{{tc.filter.class}}" role="button" class="btn btn-mini {%if request.GET.filter%}{{tc.filter.options|filtered_icon:request.GET.filter}} {%endif%}" {%if request.GET.filter%} title="<p>{{tc.filter.options|filtered_tooltip:request.GET.filter}}</p><p><a class='btn btn-small btn-primary' href=javascript:reload_params({'filter':''})>Show all {% if filter_search_display %}{{filter_search_display}}{% else %}{{objectname}}{% endif %}</a></p>" {%endif%} data-toggle="modal"> <i class="icon-filter filtered"></i> </a>
                 </div>{%endif%}
             </th>{% endfor %}
         </tr>
Signature · 1 line
1.8.3.2