Bugfixes for Toaster
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
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
@@ -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']
lib/toaster/orm/models.py +6 -5
@@ -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
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
@@ -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
PATCH 3/8 toaster: add commands to list and delete builds
In reply to Alex DAMIANFrom: 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
lib/toaster/toastermain/management/commands/__init__.py new file +0 -0
lib/toaster/toastermain/management/commands/builddelete.py new file +33 -0
@@ -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
@@ -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
PATCH 4/8 toaster: erase checks for stop command
In reply to Alex DAMIANFrom: 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
@@ -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
PATCH 5/8 toaster: Fix help text for task details outcome
In reply to Alex DAMIANFrom: 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
@@ -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
@@ -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
PATCH 6/8 toaster: add empty outcome support to task page
In reply to Alex DAMIANFrom: 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
@@ -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
@@ -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
@@ -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
PATCH 7/8 toaster: add 'bblayers.conf' files to local config filter
In reply to Alex DAMIANFrom: 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
@@ -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
PATCH 8/8 toaster: fix 'show all' labels for tasks and variables
In reply to Alex DAMIANFrom: 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
@@ -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