diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2013-10-11 13:46:23 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2013-10-18 11:13:13 +0100 |
commit | d24334a5e83d09b3ab227af485971bb768bf5412 (patch) | |
tree | 2c7682da9abed81fd2478c869e0324ab051679d9 /lib/toaster | |
parent | 8a8bafc8ded98364a31878b23c64503a53affcd1 (diff) | |
download | bitbake-contrib-d24334a5e83d09b3ab227af485971bb768bf5412.tar.gz |
toaster: 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 <calindragomir@gmail.com>.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Diffstat (limited to 'lib/toaster')
30 files changed, 1363 insertions, 0 deletions
diff --git a/lib/toaster/__init__.py b/lib/toaster/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/toaster/__init__.py diff --git a/lib/toaster/bldviewer/__init__.py b/lib/toaster/bldviewer/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/toaster/bldviewer/__init__.py diff --git a/lib/toaster/bldviewer/api.py b/lib/toaster/bldviewer/api.py new file mode 100644 index 000000000..f761ba65a --- /dev/null +++ b/lib/toaster/bldviewer/api.py @@ -0,0 +1,37 @@ +# +# 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.conf.urls import patterns, include, url + + +urlpatterns = patterns('bldviewer.views', + url(r'^builds$', 'model_explorer', {'model_name':'build'}, name='builds'), + url(r'^targets$', 'model_explorer', {'model_name':'target'}, name='targets'), + url(r'^tasks$', 'model_explorer', {'model_name':'task'}, name='task'), + url(r'^task_dependencies$', 'model_explorer', {'model_name':'task_dependency'}, name='task_dependencies'), + url(r'^packages$', 'model_explorer', {'model_name':'build_package'}, name='build_packages'), + url(r'^package_dependencies$', 'model_explorer', {'model_name':'build_package_dependency'}, name='build_package_dependencies'), + url(r'^target_packages$', 'model_explorer', {'model_name':'target_package'}, name='target_packages'), + url(r'^package_files$', 'model_explorer', {'model_name':'build_file'}, name='build_files'), + url(r'^layers$', 'model_explorer', {'model_name':'layer'}, name='layer'), + url(r'^layerversions$', 'model_explorer', {'model_name':'layerversion'}, name='layerversion'), + url(r'^recipes$', 'model_explorer', {'model_name':'recipe'}, name='recipe'), + url(r'^recipe_dependencies$', 'model_explorer', {'model_name':'recipe_dependency'}, name='recipe_dependencies'), + url(r'^variables$', 'model_explorer', {'model_name':'variable'}, name='variables'), + url(r'^logmessages$', 'model_explorer', {'model_name':'logmessage'}, name='logmessages'), +) diff --git a/lib/toaster/bldviewer/templates/base.html b/lib/toaster/bldviewer/templates/base.html new file mode 100644 index 000000000..101880d3e --- /dev/null +++ b/lib/toaster/bldviewer/templates/base.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +{% load static %} +<html> + <head> + <title>Toaster Simple Explorer</title> +<script src="{% static 'js/jquery-2.0.3.js' %}"> +</script> +<script src="{% static 'js/bootstrap.js' %}"> +</script> +<link href="{% static 'css/bootstrap.css' %}" rel="stylesheet" type="text/css"> + </head> + +<body style="height: 100%"> +<div style="width:100%; height: 100%; position:absolute"> +<div style="width: 100%; height: 3em" class="nav"> + <ul class="nav nav-tabs"> + <li><a href="{% url all-builds %}">All Builds</a></li> + <li><a href="{% url all-layers %}">All Layers</a></li> + </ul> +</div> + +<div style="overflow-y:scroll; width: 100%; position: absolute; top: 3em; bottom:70px "> +{% block pagecontent %} +{% endblock %} +</div> +<div class="navbar" style="position: absolute; bottom: 0; width:100%"><br/>About Toaster | Yocto Project </div> +</div> +</body> +</html> + diff --git a/lib/toaster/bldviewer/templates/basebuildpage.html b/lib/toaster/bldviewer/templates/basebuildpage.html new file mode 100644 index 000000000..873f271ba --- /dev/null +++ b/lib/toaster/bldviewer/templates/basebuildpage.html @@ -0,0 +1,17 @@ +{% extends "basetable.html" %} + +{% block pagename %} +<ul class="nav nav-tabs" style="display: inline-block"> + <li><a>Build {{build.target_set.all|join:" "}} at {{build.started_on}} : </a></li> + <li><a href="{% url task build.id %}"> Tasks </a></li> + <li><a href="{% url bpackage build.id %}"> Build Packages </a></li> + {% for t in build.target_set.all %} + {% if t.is_image %} + <li><a href="{% url tpackage build.id t.pk %}"> Packages for {{t.target}} </a> </li> + {% endif %} + {% endfor %} + <li><a href="{% url configuration build.id %}"> Configuration </a> </li> +</ul> + <h1>Toaster - Build {% block pagetitle %} {% endblock %}</h1> +{% endblock %} + diff --git a/lib/toaster/bldviewer/templates/basetable.html b/lib/toaster/bldviewer/templates/basetable.html new file mode 100644 index 000000000..083bcb82e --- /dev/null +++ b/lib/toaster/bldviewer/templates/basetable.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} + +{% block pagecontent %} +<script> +function showhideTableColumn(i, sh) { + if (sh) + $('td:nth-child('+i+'),th:nth-child('+i+')').show(); + else + $('td:nth-child('+i+'),th:nth-child('+i+')').hide(); +} + + +function filterTableRows(test) { + if (test.length > 0) { + var r = test.split(/[ ,]+/).map(function (e) { return new RegExp(e, 'i') }); + $('tr.data').map( function (i, el) { + (! r.map(function (j) { return j.test($(el).html())}).reduce(function (c, p) { return c && p;} )) ? $(el).hide() : $(el).show(); + }); + } else + { + $('tr.data').show(); + } +} +</script> +<div style="margin-bottom: 0.5em"> + + {% block pagename %} + {% endblock %} + <div align="left" style="display:inline-block; width: 40%; margin-left: 2em"> Search: <input type="search" id="filterstring" style="width: 80%" onkeyup="filterTableRows($('#filterstring').val())" autocomplete="off"> + </div> + {% if hideshowcols %} + <div align="right" style="display: inline-block; width: 40%">Show/Hide columns: + {% for i in hideshowcols %} + <span>{{i.name}} <input type="checkbox" id="ct{{i.name}}" onchange="showhideTableColumn({{i.order}}, $('#ct{{i.name}}').is(':checked'))" checked autocomplete="off"></span> | + {% endfor %} + </div> + {% endif %} +</div> +<div> + <table class="table table-striped table-condensed" style="width:95%"> +{% block pagetable %} +{% endblock %} + </table> +</div> + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/bfile.html b/lib/toaster/bldviewer/templates/bfile.html new file mode 100644 index 000000000..d90f4fbdd --- /dev/null +++ b/lib/toaster/bldviewer/templates/bfile.html @@ -0,0 +1,24 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Files for package {{files.0.bpackage.name}} {% endblock %} +{% block pagetable %} + {% if not files %} + <p>No files were recorded for this package!</p> + {% else %} + + <tr> + <th>Name</th> + <th>Size (Bytes)</th> + </tr> + + {% for file in files %} + + <tr class="data"> + <td>{{file.path}}</td> + <td>{{file.size}}</td> + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/bpackage.html b/lib/toaster/bldviewer/templates/bpackage.html new file mode 100644 index 000000000..2e254dbad --- /dev/null +++ b/lib/toaster/bldviewer/templates/bpackage.html @@ -0,0 +1,44 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Packages{% endblock %} +{% block pagetable %} + {% if not packages %} + <p>No packages were recorded for this target!</p> + {% else %} + + <tr> + <th>Name</th> + <th>Version</th> + <th>Recipe</th> + <th>Summary</th> + <th>Section</th> + <th>Description</th> + <th>Size on host disk (KBytes)</th> + <th>License</th> + <th>Dependencies List (all)</th> + </tr> + + {% for package in packages %} + + <tr class="data"> + <td><a name="#{{package.name}}" href="{% url bfile build.pk package.pk %}">{{package.name}} ({{package.filelist_bpackage.count}} files)</a></td> + <td>{{package.version}}-{{package.revision}}</td> + <td><a href="{% url layer_versions_recipes package.recipe.layer_version_id %}#{{package.recipe.name}}">{{package.recipe.name}}</a>{{package.package_name}}</a></td> + + <td>{{package.summary}}</td> + <td>{{package.section}}</td> + <td>{{package.description}}</td> + <td>{{package.size}}</td> + <td>{{package.license}}</td> + <td> + <div style="height: 3em; overflow:auto"> + {% for bpd in package.bpackage_dependencies_package.all %} + {{bpd.dep_type}}: {{bpd.depends_on}} <br/> + {% endfor %} + </div> + </td> + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/build.html b/lib/toaster/bldviewer/templates/build.html new file mode 100644 index 000000000..ab6e19643 --- /dev/null +++ b/lib/toaster/bldviewer/templates/build.html @@ -0,0 +1,43 @@ +{% extends "basetable.html" %} + +{% block pagename %} + <h1>Toaster - Builds</h1> +{% endblock %} + +{% block pagetable %} + + {% load projecttags %} + <tr> + <th>Outcome</th> + <th>Started On</th> + <th>Completed On</th> + <th>Target</th> + <th>Machine</th> + <th>Time</th> + <th>Errors</th> + <th>Warnings</th> + <th>Output</th> + <th>Log</th> + <th>Bitbake Version</th> + <th>Build Name</th> + </tr> + {% for build in builds %} + <tr class="data"> + <td><a href="{% url configuration build.id %}">{{build.get_outcome_display}}</a></td> + <td>{{build.started_on}}</td> + <td>{{build.completed_on}}</td> + <td>{% for t in build.target_set.all %}<a href="{% url tpackage build.id t.id %}">{{t.target}}</a>{% if t.is_image %} (Img){% endif %}<br/>{% endfor %}</td> + <td>{{build.machine}}</td> + <td>{% time_difference build.started_on build.completed_on %}</td> + <td>{{build.errors_no}}:{% if build.errors_no %}{% for error in logs %}{% if error.build == build %}{% if error.level == 2 %}<p>{{error.message}}</p>{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %}</td> + <td>{{build.warnings_no}}:{% if build.warnings_no %}{% for warning in logs %}{% if warning.build == build %}{% if warning.level == 1 %}<p>{{warning.message}}</p>{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %}</td> + <td>{% if build.outcome == 0 %}{% for t in build.target_set.all %}{% if t.is_image %}{{build.image_fstypes}}{% endif %}{% endfor %}{% endif %}</td> + <td>{{build.cooker_log_path}}</td> + <td>{{build.bitbake_version}}</td> + <td>{{build.build_name}}</td> + </tr> + + {% endfor %} +{% endblock %} + + diff --git a/lib/toaster/bldviewer/templates/configuration.html b/lib/toaster/bldviewer/templates/configuration.html new file mode 100644 index 000000000..052c37c4e --- /dev/null +++ b/lib/toaster/bldviewer/templates/configuration.html @@ -0,0 +1,20 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Configuration{% endblock %} +{% block pagetable %} + + <tr> + <th>Name</th> + <th>Value</th> + <th>Description</th> + </tr> + + {% for variable in configuration %} + + <tr class="data"> + <td>{{variable.variable_name}}</td> + <td>{{variable.variable_value}}</td> + <td>{% if variable.description %}{{variable.description}}{% endif %}</td> + {% endfor %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/layer.html b/lib/toaster/bldviewer/templates/layer.html new file mode 100644 index 000000000..fa4fd9bde --- /dev/null +++ b/lib/toaster/bldviewer/templates/layer.html @@ -0,0 +1,34 @@ +{% extends "basetable.html" %} + +{% block pagename %} + <h1>Toaster - Layers</h1> +{% endblock %} + +{% block pagetable %} + {% load projecttags %} + + <tr> + <th>Name</th> + <th>Local Path</th> + <th>Layer Index URL</th> + <th>Known Versions</th> + </tr> + + {% for layer in layers %} + + <tr class="data"> + <td>{{layer.name}}</td> + <td>{{layer.local_path}}</td> + <td><a href='{{layer.layer_index_url}}'>{{layer.layer_index_url}}</a></td> + <td><table> + {% for lv in layer.versions %} + <tr><td> + <a href="{% url layer_versions_recipes lv.id %}">({{lv.priority}}){{lv.branch}}:{{lv.commit}} ({{lv.count}} recipes)</a> + </td></tr> + {% endfor %} + </table></td> + </tr> + + {% endfor %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/package.html b/lib/toaster/bldviewer/templates/package.html new file mode 100644 index 000000000..642fcab9a --- /dev/null +++ b/lib/toaster/bldviewer/templates/package.html @@ -0,0 +1,36 @@ +{% extends "basebuildpage.html" %} + +{% block pagetable %} + {% if not packages %} + <p>No packages were recorded for this target!</p> + {% else %} + + <tr> + <th>Name</th> + <th>Version</th> + <th>Size (Bytes)</th> + <th>Recipe</th> + <th>Depends on</th> + </tr> + + {% for package in packages %} + + <tr class="data"> + <td><a name="#{{package.name}}">{{package.name}}</a></td> + <td>{{package.version}}</td> + <td>{{package.size}}</td> + <td><a name="{{package.recipe.name}}.{{package.package_name}}"> + <a href="{% url layer_versions_recipes package.recipe.layer_version_id %}#{{package.recipe.name}}">{{package.recipe.name}}</a>{{package.package_name}}</a></td> + <td> + <div style="height: 3em; overflow:auto"> + {% for d in package.depends_on %} + <a href="#{{d.name}}">{{d.name}}</a><br/> + {% endfor %} + </div> + </td> + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/recipe.html b/lib/toaster/bldviewer/templates/recipe.html new file mode 100644 index 000000000..a62437066 --- /dev/null +++ b/lib/toaster/bldviewer/templates/recipe.html @@ -0,0 +1,54 @@ +{% extends "basetable.html" %} + +{% block pagename %} +<ul class="nav nav-tabs" style="display: inline-block"> + <li><a>Layer {{layer_version.layer.name}} : {{layer_version.branch}} : {{layer_version.commit}} : {{layer_version.priority}}</a></li> +</ul> + <h1>Toaster - Recipes for a Layer</h1> +{% endblock %} + +{% block pagetable %} + {% load projecttags %} + + <tr> + </tr> + <th>Name</th> + <th>Version</th> + <th>Summary</th> + <th>Description</th> + <th>Section</th> + <th>License</th> + <th>License file</th> + <th>Homepage</th> + <th>Bugtracker</th> + <th>Author</th> + <th>File_path</th> + <th style="width: 30em">Recipe Dependency</th> + + + {% for recipe in recipes %} + + <tr class="data"> + <td><a name="{{recipe.name}}">{{recipe.name}}</a></td> + <td>{{recipe.version}}</td> + <td>{{recipe.summary}}</td> + <td>{{recipe.description}}</td> + <td>{{recipe.section}}</td> + <td>{{recipe.license}}</td> + <td>{{recipe.licensing_info}}</td> + <td>{{recipe.homepage}}</td> + <td>{{recipe.bugtracker}}</td> + <td>{{recipe.author}}</td> + <td>{{recipe.file_path}}</td> + <td> + <div style="height: 5em; overflow:auto"> + {% for rr in recipe.r_dependencies_recipe.all %} + <a href="#{{rr.depends_on.name}}">{{rr.depends_on.name}}</a><br/> + {% endfor %} + </div> + </td> + </tr> + + {% endfor %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templates/task.html b/lib/toaster/bldviewer/templates/task.html new file mode 100644 index 000000000..e7253698c --- /dev/null +++ b/lib/toaster/bldviewer/templates/task.html @@ -0,0 +1,63 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Tasks{% endblock %} +{% block pagetable %} + {% if not tasks %} + <p>No tasks were executed in this build!</p> + {% else %} + + <tr> + <th>Order</th> + <th>Task</th> + <th>Recipe Version</th> + <th>Task Type</th> + <th>Checksum</th> + <th>Outcome</th> + <th>Message</th> + <th>Logfile</th> + <th>Time</th> + <th>CPU usage</th> + <th>Disk I/O</th> + <th>Script type</th> + <th>File path</th> + <th>Depends</th> + </tr> + + {% for task in tasks %} + + <tr class="data"> + <td>{{task.order}}</td> + <td><a name="{{task.recipe.name}}.{{task.task_name}}"> + <a href="{% url layer_versions_recipes task.recipe.layer_version_id %}#{{task.recipe.name}}">{{task.recipe.name}}</a>.{{task.task_name}}</a></td> + <td>{{task.recipe.version}}</td> + + {% if task.task_executed %} + <td>Executed</td> + {% else %} + <td>Prebuilt</td> + {% endif %} + + <td>{{task.sstate_checksum}}</td> + <td>{{task.get_outcome_display}}{% if task.provider %}</br>(by <a href="#{{task.provider.recipe.name}}.{{task.provider.task_name}}">{{task.provider.recipe.name}}.{{task.provider.task_name}}</a>){% endif %}</td> + <td><p>{{task.message}}</td> + <td><a target="_fileview" href="file:///{{task.logfile}}">{{task.logfile}}</a></td> + <td>{{task.elapsed_time}}</td> + <td>{{task.cpu_usage}}</td> + <td>{{task.disk_io}}</td> + <td>{{task.get_script_type_display}}</td> + <td><a target="_fileview" href="file:///{{task.recipe.file_path}}">{{task.recipe.file_path}}</a></td> + <td> + <div style="height: 3em; overflow:auto"> + {% for tt in task.task_dependencies_task.all %} + <a href="#{{tt.depends_on.recipe.name}}.{{tt.depends_on.task_name}}"> + {{tt.depends_on.recipe.name}}.{{tt.depends_on.task_name}}</a><br/> + {% endfor %} + </div> + </td> + </tr> + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/lib/toaster/bldviewer/templatetags/__init__.py b/lib/toaster/bldviewer/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/toaster/bldviewer/templatetags/__init__.py diff --git a/lib/toaster/bldviewer/templatetags/projecttags.py b/lib/toaster/bldviewer/templatetags/projecttags.py new file mode 100644 index 000000000..0c0d804c0 --- /dev/null +++ b/lib/toaster/bldviewer/templatetags/projecttags.py @@ -0,0 +1,26 @@ +# +# 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 datetime import datetime +from django import template + +register = template.Library() + +@register.simple_tag +def time_difference(start_time, end_time): + return end_time - start_time diff --git a/lib/toaster/bldviewer/urls.py b/lib/toaster/bldviewer/urls.py new file mode 100644 index 000000000..becc67920 --- /dev/null +++ b/lib/toaster/bldviewer/urls.py @@ -0,0 +1,32 @@ +# +# 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.conf.urls import patterns, include, url +from django.views.generic.simple import redirect_to + +urlpatterns = patterns('bldviewer.views', + url(r'^builds/$', 'build', name='all-builds'), + url(r'^build/(?P<build_id>\d+)/task/$', 'task', name='task'), + url(r'^build/(?P<build_id>\d+)/packages/$', 'bpackage', name='bpackage'), + url(r'^build/(?P<build_id>\d+)/package/(?P<package_id>\d+)/files/$', 'bfile', name='bfile'), + url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/packages/$', 'tpackage', name='tpackage'), + url(r'^build/(?P<build_id>\d+)/configuration/$', 'configuration', name='configuration'), + url(r'^layers/$', 'layer', name='all-layers'), + url(r'^layerversions/(?P<layerversion_id>\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'), + url(r'^$', redirect_to, {'url': 'builds/'}), +) diff --git a/lib/toaster/bldviewer/views.py b/lib/toaster/bldviewer/views.py new file mode 100644 index 000000000..7be4d4b89 --- /dev/null +++ b/lib/toaster/bldviewer/views.py @@ -0,0 +1,260 @@ +# +# 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. + +import operator + +from django.db.models import Q +from django.shortcuts import render +from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, Target_Package, LogMessage, Variable +from orm.models import Task_Dependency, Recipe_Dependency, Build_Package, Build_File, Build_Package_Dependency +from django.views.decorators.cache import cache_control + +@cache_control(no_store=True) +def build(request): + template = 'build.html' + build_info = Build.objects.all() + + logs = LogMessage.objects.all() + + context = {'builds': build_info, 'logs': logs , + 'hideshowcols' : [ + {'name': 'Output', 'order':10}, + {'name': 'Log', 'order':11}, + ]} + + return render(request, template, context) + + +def _find_task_revdep(task): + tp = [] + for p in Task_Dependency.objects.filter(depends_on=task): + tp.append(p.task); + return tp + +def _find_task_provider(task): + task_revdeps = _find_task_revdep(task) + for tr in task_revdeps: + if tr.outcome != Task.OUTCOME_COVERED: + return tr + for tr in task_revdeps: + trc = _find_task_provider(tr) + if trc is not None: + return trc + return None + +def task(request, build_id): + template = 'task.html' + + tasks = Task.objects.filter(build=build_id) + + for t in tasks: + if t.outcome == Task.OUTCOME_COVERED: + t.provider = _find_task_provider(t) + + context = {'build': Build.objects.filter(pk=build_id)[0], 'tasks': tasks} + + return render(request, template, context) + +def configuration(request, build_id): + template = 'configuration.html' + variables = Variable.objects.filter(build=build_id) + context = {'build': Build.objects.filter(pk=build_id)[0], 'configuration' : variables} + return render(request, template, context) + +def bpackage(request, build_id): + template = 'bpackage.html' + packages = Build_Package.objects.filter(build = build_id) + context = {'build': Build.objects.filter(pk=build_id)[0], 'packages' : packages} + return render(request, template, context) + +def bfile(request, build_id, package_id): + template = 'bfile.html' + files = Build_File.objects.filter(bpackage = package_id) + context = {'build': Build.objects.filter(pk=build_id)[0], 'files' : files} + return render(request, template, context) + +def tpackage(request, build_id, target_id): + template = 'package.html' + + packages = Target_Package.objects.filter(target=target_id) + + context = {'build' : Build.objects.filter(pk=build_id)[0],'packages': packages} + + return render(request, template, context) + +def layer(request): + template = 'layer.html' + layer_info = Layer.objects.all() + + for li in layer_info: + li.versions = Layer_Version.objects.filter(layer = li) + for liv in li.versions: + liv.count = Recipe.objects.filter(layer_version__id = liv.id).count() + + context = {'layers': layer_info} + + return render(request, template, context) + + +def layer_versions_recipes(request, layerversion_id): + template = 'recipe.html' + recipes = Recipe.objects.filter(layer_version__id = layerversion_id) + + context = {'recipes': recipes, + 'layer_version' : Layer_Version.objects.filter( id = layerversion_id )[0] + } + + return render(request, template, context) + +#### API + +import json +from django.core import serializers +from django.http import HttpResponse, HttpResponseBadRequest + + +def model_explorer(request, model_name): + + DESCENDING = 'desc' + response_data = {} + model_mapping = { + 'build': Build, + 'target': Target, + 'target_package': Target_Package, + 'task': Task, + 'task_dependency': Task_Dependency, + 'package': Build_Package, + 'layer': Layer, + 'layerversion': Layer_Version, + 'recipe': Recipe, + 'recipe_dependency': Recipe_Dependency, + 'build_package': Build_Package, + 'build_package_dependency': Build_Package_Dependency, + 'build_file': Build_File, + 'variable': Variable, + 'logmessage': LogMessage, + } + + if model_name not in model_mapping.keys(): + return HttpResponseBadRequest() + + model = model_mapping[model_name] + + try: + limit = int(request.GET.get('limit', 0)) + except ValueError: + limit = 0 + + try: + offset = int(request.GET.get('offset', 0)) + except ValueError: + offset = 0 + + ordering_string, invalid = _validate_input(request.GET.get('orderby', ''), + model) + if invalid: + return HttpResponseBadRequest() + + filter_string, invalid = _validate_input(request.GET.get('filter', ''), + model) + if invalid: + return HttpResponseBadRequest() + + search_term = request.GET.get('search', '') + + if filter_string: + filter_terms = _get_filtering_terms(filter_string) + try: + queryset = model.objects.filter(**filter_terms) + except ValueError: + queryset = [] + else: + queryset = model.objects.all() + + if search_term: + queryset = _get_search_results(search_term, queryset, model) + + if ordering_string and queryset: + column, order = ordering_string.split(':') + if order.lower() == DESCENDING: + queryset = queryset.order_by('-' + column) + else: + queryset = queryset.order_by(column) + + if offset and limit: + queryset = queryset[offset:(offset+limit)] + elif offset: + queryset = queryset[offset:] + elif limit: + queryset = queryset[:limit] + + if queryset: + response_data['count'] = queryset.count() + else: + response_data['count'] = 0 + + response_data['list'] = serializers.serialize('json', queryset) + + return HttpResponse(json.dumps(response_data), + content_type='application/json') + +def _get_filtering_terms(filter_string): + + search_terms = filter_string.split(":") + keys = search_terms[0].split(',') + values = search_terms[1].split(',') + + return dict(zip(keys, values)) + +def _validate_input(input, model): + + invalid = 0 + + if input: + input_list = input.split(":") + + # Check we have only one colon + if len(input_list) != 2: + invalid = 1 + return None, invalid + + # Check we have an equal number of terms both sides of the colon + if len(input_list[0].split(',')) != len(input_list[1].split(',')): + invalid = 1 + return None, invalid + + # Check we are looking for a valid field + valid_fields = model._meta.get_all_field_names() + for field in input_list[0].split(','): + if field not in valid_fields: + invalid = 1 + return None, invalid + + return input, invalid + +def _get_search_results(search_term, queryset, model): + search_objects = [] + for st in search_term.split(" "): + q_map = map(lambda x: Q(**{x+'__icontains': st}), + model.search_allowed_fields) + + search_objects.append(reduce(operator.or_, q_map)) + search_object = reduce(operator.and_, search_objects) + queryset = queryset.filter(search_object) + + return queryset diff --git a/lib/toaster/manage.py b/lib/toaster/manage.py new file mode 100755 index 000000000..ceaa11bfc --- /dev/null +++ b/lib/toaster/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/lib/toaster/orm/__init__.py b/lib/toaster/orm/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/toaster/orm/__init__.py diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py new file mode 100644 index 000000000..cb6581c9e --- /dev/null +++ b/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) diff --git a/lib/toaster/toastergui/__init__.py b/lib/toaster/toastergui/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/toaster/toastergui/__init__.py diff --git a/lib/toaster/toastergui/static/images/yocto.jpg b/lib/toaster/toastergui/static/images/yocto.jpg Binary files differnew file mode 100644 index 000000000..5de4b7693 --- /dev/null +++ b/lib/toaster/toastergui/static/images/yocto.jpg diff --git a/lib/toaster/toastergui/templates/index.html b/lib/toaster/toastergui/templates/index.html new file mode 100644 index 000000000..80b02d94a --- /dev/null +++ b/lib/toaster/toastergui/templates/index.html @@ -0,0 +1,13 @@ +<html> +<head> + <title>GUI Page</title> +</head> +<body> +{% load staticfiles %} + +<img src="{% static "/static/images/yocto.jpg" %}" alt="Yocto"/> + +This is your basic index page! +</body> + +</html>
\ No newline at end of file diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py new file mode 100644 index 000000000..6dbf0c5a5 --- /dev/null +++ b/lib/toaster/toastergui/urls.py @@ -0,0 +1,27 @@ +# +# 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. + +import os + +from django.conf import settings +from django.conf.urls import patterns, include, url + + +urlpatterns = patterns('toastergui.views', + url(r'^$', 'guihome', name='guihome'), +) diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py new file mode 100644 index 000000000..ecb2f7e82 --- /dev/null +++ b/lib/toaster/toastergui/views.py @@ -0,0 +1,26 @@ +# +# 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.shortcuts import render +from orm.models import Build, Task + + +def guihome(request): + template = 'index.html' + + return render(request, template) diff --git a/lib/toaster/toastermain/__init__.py b/lib/toaster/toastermain/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/toaster/toastermain/__init__.py diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py new file mode 100644 index 000000000..dd1e25c08 --- /dev/null +++ b/lib/toaster/toastermain/settings.py @@ -0,0 +1,190 @@ +# +# 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. + +# Django settings for Toaster project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@example.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'toaster.sqlite', # Or path to database file if using sqlite3. + 'USER': '', + 'PASSWORD': '', + 'HOST': '127.0.0.1', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. + 'PORT': '3306', # Set to empty string for default. + } +} + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [] + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# In a Windows environment this must be set to your system time zone. + +# Always use local computer's time zone +#TIME_ZONE = '' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale. +USE_L10N = True + +# If you set this to False, Django will not use timezone-aware datetimes. +USE_TZ = False + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/var/www/example.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://example.com/media/", "http://media.example.com/" +MEDIA_URL = '' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/var/www/example.com/static/" +STATIC_ROOT = '' + +# URL prefix for static files. +# Example: "http://example.com/static/", "http://static.example.com/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'NOT_SUITABLE_FOR_HOSTED_DEPLOYMENT' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + # Uncomment the next line for simple clickjacking protection: + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', +) + +ROOT_URLCONF = 'toastermain.urls' + +# Python dotted path to the WSGI application used by Django's runserver. +WSGI_APPLICATION = 'toastermain.wsgi.application' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +INSTALLED_APPS = ( + #'django.contrib.auth', + #'django.contrib.contenttypes', + #'django.contrib.sessions', + #'django.contrib.sites', + #'django.contrib.messages', + 'django.contrib.staticfiles', + # Uncomment the next line to enable the admin: + # 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', + 'orm', + 'toastermain', + 'bldviewer', + 'toastergui', +) + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} + +# If we're using sqlite, we need to tweak the performance a bit +from django.db.backends.signals import connection_created +def activate_synchronous_off(sender, connection, **kwargs): + if connection.vendor == 'sqlite': + cursor = connection.cursor() + cursor.execute('PRAGMA synchronous = 0;') +connection_created.connect(activate_synchronous_off) +# + + diff --git a/lib/toaster/toastermain/urls.py b/lib/toaster/toastermain/urls.py new file mode 100644 index 000000000..d0606bce9 --- /dev/null +++ b/lib/toaster/toastermain/urls.py @@ -0,0 +1,41 @@ +# +# 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.conf.urls import patterns, include, url +from django.views.generic.simple import redirect_to +from django.views.decorators.cache import never_cache + + +# Uncomment the next two lines to enable the admin: +# from django.contrib import admin +# admin.autodiscover() + +urlpatterns = patterns('', + url(r'^simple/', include('bldviewer.urls')), + url(r'^api/1.0/', include('bldviewer.api')), + url(r'^gui/', include('toastergui.urls')), + url(r'^$', never_cache(redirect_to), {'url': '/simple/'}), + # Examples: + # url(r'^toaster/', include('toaster.foo.urls')), + + # Uncomment the admin/doc line below to enable admin documentation: + # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), + + # Uncomment the next line to enable the admin: + # url(r'^admin/', include(admin.site.urls)), +) diff --git a/lib/toaster/toastermain/wsgi.py b/lib/toaster/toastermain/wsgi.py new file mode 100644 index 000000000..6277eb4d5 --- /dev/null +++ b/lib/toaster/toastermain/wsgi.py @@ -0,0 +1,32 @@ +""" +WSGI config for Toaster project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks +# if running multiple sites in the same mod_wsgi process. To fix this, use +# mod_wsgi daemon mode with each site in its own daemon process, or use +# os.environ["DJANGO_SETTINGS_MODULE"] = "Toaster.settings" +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application) |