From cb1338dedc3873ac81488df9dd3aa6f048e6b02f Mon Sep 17 00:00:00 2001 From: Alexandru DAMIAN Date: Fri, 11 Oct 2013 13:46:23 +0100 Subject: bitbake: add toaster code to bitbake This patch adds the Toaster component to Bitbake. Toaster is a module designed to record the progress of a Bitbake build, and data about the resultant artifacts. It contains a web-based interface and a REST API allowing post-facto inspection of the build process and artifacts. Features present in this build: * toaster start script * relational data model * Django boilerplate code * the REST API * the Simple UI web interface This patch has all the development history squashed together. Code portions contributed by Calin Dragomir . Signed-off-by: Alexandru DAMIAN --- bitbake/lib/toaster/orm/__init__.py | 0 bitbake/lib/toaster/orm/models.py | 258 ++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 bitbake/lib/toaster/orm/__init__.py create mode 100644 bitbake/lib/toaster/orm/models.py (limited to 'bitbake/lib/toaster/orm') diff --git a/bitbake/lib/toaster/orm/__init__.py b/bitbake/lib/toaster/orm/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py new file mode 100644 index 0000000000..cb6581c9e1 --- /dev/null +++ b/bitbake/lib/toaster/orm/models.py @@ -0,0 +1,258 @@ +# +# BitBake Toaster Implementation +# +# Copyright (C) 2013 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from django.db import models +from django.utils.encoding import python_2_unicode_compatible + + +class Build(models.Model): + SUCCEEDED = 0 + FAILED = 1 + IN_PROGRESS = 2 + + BUILD_OUTCOME = ( + (SUCCEEDED, 'Succeeded'), + (FAILED, 'Failed'), + (IN_PROGRESS, 'In Progress'), + ) + + search_allowed_fields = ['machine', + 'cooker_log_path'] + + machine = models.CharField(max_length=100) + image_fstypes = models.CharField(max_length=100) + distro = models.CharField(max_length=100) + distro_version = models.CharField(max_length=100) + started_on = models.DateTimeField() + completed_on = models.DateTimeField() + outcome = models.IntegerField(choices=BUILD_OUTCOME, default=IN_PROGRESS) + errors_no = models.IntegerField(default=0) + warnings_no = models.IntegerField(default=0) + cooker_log_path = models.CharField(max_length=500) + build_name = models.CharField(max_length=100) + bitbake_version = models.CharField(max_length=50) + +@python_2_unicode_compatible +class Target(models.Model): + search_allowed_fields = ['target', 'image_fstypes', 'file_name'] + build = models.ForeignKey(Build) + target = models.CharField(max_length=100) + is_image = models.BooleanField(default = False) + file_name = models.CharField(max_length=100) + file_size = models.IntegerField() + + def __str__(self): + return self.target + + +class Task(models.Model): + + SSTATE_NA = 0 + SSTATE_MISS = 1 + SSTATE_FAILED = 2 + SSTATE_RESTORED = 3 + + SSTATE_RESULT = ( + (SSTATE_NA, 'Not Applicable'), # For rest of tasks, but they still need checking. + (SSTATE_MISS, 'Missing'), # it is a miss + (SSTATE_FAILED, 'Failed'), # there was a pkg, but the script failed + (SSTATE_RESTORED, 'Restored'), # succesfully restored + ) + + CODING_PYTHON = 0 + CODING_SHELL = 1 + + TASK_CODING = ( + (CODING_PYTHON, 'Python'), + (CODING_SHELL, 'Shell'), + ) + + OUTCOME_SUCCESS = 0 + OUTCOME_COVERED = 1 + OUTCOME_SSTATE = 2 + OUTCOME_EXISTING = 3 + OUTCOME_FAILED = 4 + OUTCOME_NA = 5 + + TASK_OUTCOME = ( + (OUTCOME_SUCCESS, 'Succeeded'), + (OUTCOME_COVERED, 'Covered'), + (OUTCOME_SSTATE, 'Sstate'), + (OUTCOME_EXISTING, 'Existing'), + (OUTCOME_FAILED, 'Failed'), + (OUTCOME_NA, 'Not Available'), + ) + + build = models.ForeignKey(Build, related_name='task_build') + order = models.IntegerField(null=True) + task_executed = models.BooleanField(default=False) # True means Executed, False means Prebuilt + outcome = models.IntegerField(choices=TASK_OUTCOME, default=OUTCOME_NA) + sstate_checksum = models.CharField(max_length=100, blank=True) + path_to_sstate_obj = models.FilePathField(max_length=500, blank=True) + recipe = models.ForeignKey('Recipe', related_name='build_recipe') + task_name = models.CharField(max_length=100) + source_url = models.FilePathField(max_length=255, blank=True) + work_directory = models.FilePathField(max_length=255, blank=True) + script_type = models.IntegerField(choices=TASK_CODING, default=CODING_PYTHON) + line_number = models.IntegerField(default=0) + disk_io = models.IntegerField(null=True) + cpu_usage = models.DecimalField(max_digits=6, decimal_places=2, null=True) + elapsed_time = models.CharField(max_length=50, default=0) + sstate_result = models.IntegerField(choices=SSTATE_RESULT, default=SSTATE_NA) + message = models.CharField(max_length=240) + logfile = models.FilePathField(max_length=255, blank=True) + + class Meta: + ordering = ('order', 'recipe' ,) + + +class Task_Dependency(models.Model): + task = models.ForeignKey(Task, related_name='task_dependencies_task') + depends_on = models.ForeignKey(Task, related_name='task_dependencies_depends') + + +class Build_Package(models.Model): + build = models.ForeignKey('Build') + recipe = models.ForeignKey('Recipe', null=True) + name = models.CharField(max_length=100) + version = models.CharField(max_length=100, blank=True) + revision = models.CharField(max_length=32, blank=True) + summary = models.CharField(max_length=200, blank=True) + description = models.CharField(max_length=200, blank=True) + size = models.IntegerField(default=0) + section = models.CharField(max_length=80, blank=True) + license = models.CharField(max_length=80, blank=True) + +class Build_Package_Dependency(models.Model): + TYPE_RDEPENDS = 0 + TYPE_RPROVIDES = 1 + TYPE_RRECOMMENDS = 2 + TYPE_RSUGGESTS = 3 + TYPE_RREPLACES = 4 + TYPE_RCONFLICTS = 5 + DEPENDS_TYPE = ( + (TYPE_RDEPENDS, "rdepends"), + (TYPE_RPROVIDES, "rprovides"), + (TYPE_RRECOMMENDS, "rrecommends"), + (TYPE_RSUGGESTS, "rsuggests"), + (TYPE_RREPLACES, "rreplaces"), + (TYPE_RCONFLICTS, "rconflicts"), + ) + package = models.ForeignKey(Build_Package, related_name='bpackage_dependencies_package') + depends_on = models.CharField(max_length=100) # soft dependency + dep_type = models.IntegerField(choices=DEPENDS_TYPE) + + +class Target_Package(models.Model): + target = models.ForeignKey('Target') + recipe = models.ForeignKey('Recipe', null=True) + name = models.CharField(max_length=100) + version = models.CharField(max_length=100, blank=True) + size = models.IntegerField() + + +class Target_Package_Dependency(models.Model): + TYPE_DEPENDS = 0 + TYPE_RDEPENDS = 1 + TYPE_RECOMMENDS = 2 + + DEPENDS_TYPE = ( + (TYPE_DEPENDS, "depends"), + (TYPE_RDEPENDS, "rdepends"), + (TYPE_RECOMMENDS, "recommends"), + ) + package = models.ForeignKey(Target_Package, related_name='tpackage_dependencies_package') + depends_on = models.ForeignKey(Target_Package, related_name='tpackage_dependencies_depends') + dep_type = models.IntegerField(choices=DEPENDS_TYPE) + + +class Build_File(models.Model): + bpackage = models.ForeignKey(Build_Package, related_name='filelist_bpackage') + path = models.FilePathField(max_length=255, blank=True) + size = models.IntegerField() + +class Target_File(models.Model): + tpackage = models.ForeignKey(Target_Package, related_name='filelist_tpackage') + path = models.FilePathField(max_length=255, blank=True) + size = models.IntegerField() + + +class Recipe(models.Model): + name = models.CharField(max_length=100, blank=True) + version = models.CharField(max_length=100, blank=True) + layer_version = models.ForeignKey('Layer_Version', related_name='recipe_layer_version') + summary = models.CharField(max_length=100, blank=True) + description = models.CharField(max_length=100, blank=True) + section = models.CharField(max_length=100, blank=True) + license = models.CharField(max_length=200, blank=True) + licensing_info = models.TextField(blank=True) + homepage = models.URLField(blank=True) + bugtracker = models.URLField(blank=True) + author = models.CharField(max_length=100, blank=True) + file_path = models.FilePathField(max_length=255) + + +class Recipe_Dependency(models.Model): + TYPE_DEPENDS = 0 + TYPE_RDEPENDS = 1 + + DEPENDS_TYPE = ( + (TYPE_DEPENDS, "depends"), + (TYPE_RDEPENDS, "rdepends"), + ) + recipe = models.ForeignKey(Recipe, related_name='r_dependencies_recipe') + depends_on = models.ForeignKey(Recipe, related_name='r_dependencies_depends') + dep_type = models.IntegerField(choices=DEPENDS_TYPE) + +class Layer(models.Model): + name = models.CharField(max_length=100) + local_path = models.FilePathField(max_length=255) + layer_index_url = models.URLField() + + +class Layer_Version(models.Model): + layer = models.ForeignKey(Layer, related_name='layer_version_layer') + branch = models.CharField(max_length=50) + commit = models.CharField(max_length=100) + priority = models.IntegerField() + + +class Variable(models.Model): + build = models.ForeignKey(Build, related_name='variable_build') + variable_name = models.CharField(max_length=100) + variable_value = models.TextField(blank=True) + file = models.FilePathField(max_length=255) + changed = models.BooleanField(default=False) + human_readable_name = models.CharField(max_length=200) + description = models.TextField(blank=True) + + +class LogMessage(models.Model): + INFO = 0 + WARNING = 1 + ERROR = 2 + + LOG_LEVEL = ( (INFO, "info"), + (WARNING, "warn"), + (ERROR, "error") ) + + build = models.ForeignKey(Build) + level = models.IntegerField(choices=LOG_LEVEL, default=INFO) + message=models.CharField(max_length=240) + pathname = models.FilePathField(max_length=255, blank=True) + lineno = models.IntegerField(null=True) -- cgit v1.2.3-54-g00ecf