From 164ab730ccb504b823f43b48de282de702ccf71b Mon Sep 17 00:00:00 2001 From: Alexandru DAMIAN Date: Fri, 11 Oct 2013 13:46:23 +0100 Subject: [PATCH] bitbake: 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 . (Bitbake rev: d24334a5e83d09b3ab227af485971bb768bf5412) Signed-off-by: Alexandru DAMIAN Signed-off-by: Paul Eggleton Signed-off-by: Richard Purdie --- bitbake/bin/toaster | 145 ++++++++++ bitbake/lib/toaster/__init__.py | 0 bitbake/lib/toaster/bldviewer/__init__.py | 0 bitbake/lib/toaster/bldviewer/api.py | 37 +++ .../lib/toaster/bldviewer/templates/base.html | 30 ++ .../bldviewer/templates/basebuildpage.html | 17 ++ .../bldviewer/templates/basetable.html | 46 ++++ .../toaster/bldviewer/templates/bfile.html | 24 ++ .../toaster/bldviewer/templates/bpackage.html | 44 +++ .../toaster/bldviewer/templates/build.html | 43 +++ .../bldviewer/templates/configuration.html | 20 ++ .../toaster/bldviewer/templates/layer.html | 34 +++ .../toaster/bldviewer/templates/package.html | 36 +++ .../toaster/bldviewer/templates/recipe.html | 54 ++++ .../lib/toaster/bldviewer/templates/task.html | 63 +++++ .../bldviewer/templatetags/__init__.py | 0 .../bldviewer/templatetags/projecttags.py | 26 ++ bitbake/lib/toaster/bldviewer/urls.py | 32 +++ bitbake/lib/toaster/bldviewer/views.py | 260 ++++++++++++++++++ bitbake/lib/toaster/manage.py | 10 + bitbake/lib/toaster/orm/__init__.py | 0 bitbake/lib/toaster/orm/models.py | 258 +++++++++++++++++ bitbake/lib/toaster/toastergui/__init__.py | 0 .../toastergui/static/images/yocto.jpg | Bin 0 -> 6582 bytes .../toaster/toastergui/templates/index.html | 13 + bitbake/lib/toaster/toastergui/urls.py | 27 ++ bitbake/lib/toaster/toastergui/views.py | 26 ++ bitbake/lib/toaster/toastermain/__init__.py | 0 bitbake/lib/toaster/toastermain/settings.py | 190 +++++++++++++ bitbake/lib/toaster/toastermain/urls.py | 41 +++ bitbake/lib/toaster/toastermain/wsgi.py | 32 +++ 31 files changed, 1508 insertions(+) create mode 100755 bitbake/bin/toaster create mode 100644 bitbake/lib/toaster/__init__.py create mode 100644 bitbake/lib/toaster/bldviewer/__init__.py create mode 100644 bitbake/lib/toaster/bldviewer/api.py create mode 100644 bitbake/lib/toaster/bldviewer/templates/base.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/basebuildpage.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/basetable.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/bfile.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/bpackage.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/build.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/configuration.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/layer.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/package.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/recipe.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/task.html create mode 100644 bitbake/lib/toaster/bldviewer/templatetags/__init__.py create mode 100644 bitbake/lib/toaster/bldviewer/templatetags/projecttags.py create mode 100644 bitbake/lib/toaster/bldviewer/urls.py create mode 100644 bitbake/lib/toaster/bldviewer/views.py create mode 100755 bitbake/lib/toaster/manage.py create mode 100644 bitbake/lib/toaster/orm/__init__.py create mode 100644 bitbake/lib/toaster/orm/models.py create mode 100644 bitbake/lib/toaster/toastergui/__init__.py create mode 100644 bitbake/lib/toaster/toastergui/static/images/yocto.jpg create mode 100644 bitbake/lib/toaster/toastergui/templates/index.html create mode 100644 bitbake/lib/toaster/toastergui/urls.py create mode 100644 bitbake/lib/toaster/toastergui/views.py create mode 100644 bitbake/lib/toaster/toastermain/__init__.py create mode 100644 bitbake/lib/toaster/toastermain/settings.py create mode 100644 bitbake/lib/toaster/toastermain/urls.py create mode 100644 bitbake/lib/toaster/toastermain/wsgi.py diff --git a/bitbake/bin/toaster b/bitbake/bin/toaster new file mode 100755 index 0000000000..16de52b115 --- /dev/null +++ b/bitbake/bin/toaster @@ -0,0 +1,145 @@ +#!/bin/bash +# (c) 2013 Intel Corp. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +# This script enables toaster event logging and +# starts bitbake resident server +# use as: source toaster [start|stop] + +# Helper function to kill a background toaster development server + +function webserverKillAll() +{ + local pidfile + for pidfile in ${BUILDDIR}/.toastermain.pid; do + if [ -f ${pidfile} ]; then + while kill -0 $(< ${pidfile}) 2>/dev/null; do + kill -SIGTERM -$(< ${pidfile}) 2>/dev/null + sleep 1; + done; + rm ${pidfile} + fi + done +} + + +function webserverStartAll() +{ + retval=0 + python $BBBASEDIR/lib/toaster/manage.py syncdb || retval=1 + if [ $retval -eq 1 ]; then + echo "Failed db sync, stopping system start" 1>&2 + else + python $BBBASEDIR/lib/toaster/manage.py runserver 0.0.0.0:8000 ${BUILDDIR}/toaster_web.log 2>&1 & echo $! >${BUILDDIR}/.toastermain.pid + fi + return $retval +} + + +# We make sure we're running in the current shell and in a good environment + +if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; then + echo "Error: This script needs to be sourced. Please run as 'source toaster [start|stop]'" 1>&2; + exit 1 +fi + +if [ -z "$BUILDDIR" ] || [ -z `which bitbake` ]; then + echo "Error: Build environment is not setup or bitbake is not in path." 1>&2; + return 2 +fi + +BBBASEDIR=`dirname ${BASH_SOURCE}`/.. + + +# Verify prerequisites + +if ! echo "import django; print (1,4,5) == django.VERSION[0:3]" | python 2>/dev/null | grep True >/dev/null; then + echo -e "This program needs Django 1.4.5. Please install with\n\nsudo pip install django==1.4.5" + return 2 +fi + + + +# Determine the action. If specified by arguments, fine, if not, toggle it +if [ "x$1" == "xstart" ] || [ "x$1" == "xstop" ]; then + CMD="$1" +else + if [ -z "$BBSERVER" ]; then + CMD="start" + else + CMD="stop" + fi; +fi + +NOTOASTERUI=0 +if [ "x$2" == "xnoui" ]; then + NOTOASTERUI=1 +fi + +echo "The system will $CMD." + +# Make sure it's safe to run by checking bitbake lock + +lock=1 +if [ -e $BUILDDIR/bitbake.lock ]; then + (flock -n 200 ) 200<$BUILDDIR/bitbake.lock || lock=0 +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 + return 3 +fi + + +# Execute the commands + +case $CMD in + start ) + webserverStartAll || return 4 + unset BBSERVER + bitbake --server-only -t xmlrpc -B localhost:8200 + export BBSERVER=localhost:8200 + if [ $NOTOASTERUI == 0 ]; then # we start the TOASTERUI only if not inhibited + bitbake --observe-only -u toasterui >${BUILDDIR}/toaster_ui.log 2>&1 & echo $! >${BUILDDIR}/.toasterui.pid + fi + ;; + stop ) + if [ -f ${BUILDDIR}/.toasterui.pid ]; then + kill $(< ${BUILDDIR}/.toasterui.pid ) + rm ${BUILDDIR}/.toasterui.pid + fi + bitbake -m + unset BBSERVER + webserverKillAll + # force stop any misbehaving bitbake server + lsof bitbake.lock | awk '{print $2}' | grep "[0-9]\+" | xargs -n1 -r kill + ;; +esac + +echo "Successful ${CMD}." + diff --git a/bitbake/lib/toaster/__init__.py b/bitbake/lib/toaster/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/bldviewer/__init__.py b/bitbake/lib/toaster/bldviewer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/bldviewer/api.py b/bitbake/lib/toaster/bldviewer/api.py new file mode 100644 index 0000000000..f761ba65a9 --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/bldviewer/templates/base.html b/bitbake/lib/toaster/bldviewer/templates/base.html new file mode 100644 index 0000000000..101880d3ea --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/base.html @@ -0,0 +1,30 @@ + +{% load static %} + + + Toaster Simple Explorer + + + + + + +
+ + +
+{% block pagecontent %} +{% endblock %} +
+ +
+ + + diff --git a/bitbake/lib/toaster/bldviewer/templates/basebuildpage.html b/bitbake/lib/toaster/bldviewer/templates/basebuildpage.html new file mode 100644 index 0000000000..873f271bab --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/basebuildpage.html @@ -0,0 +1,17 @@ +{% extends "basetable.html" %} + +{% block pagename %} + +

Toaster - Build {% block pagetitle %} {% endblock %}

+{% endblock %} + diff --git a/bitbake/lib/toaster/bldviewer/templates/basetable.html b/bitbake/lib/toaster/bldviewer/templates/basetable.html new file mode 100644 index 0000000000..083bcb82e9 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/basetable.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} + +{% block pagecontent %} + +
+ + {% block pagename %} + {% endblock %} +
Search: +
+ {% if hideshowcols %} +
Show/Hide columns: + {% for i in hideshowcols %} + {{i.name}} | + {% endfor %} +
+ {% endif %} +
+
+ +{% block pagetable %} +{% endblock %} +
+
+ +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/bfile.html b/bitbake/lib/toaster/bldviewer/templates/bfile.html new file mode 100644 index 0000000000..d90f4fbddb --- /dev/null +++ b/bitbake/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 %} +

No files were recorded for this package!

+ {% else %} + + + Name + Size (Bytes) + + + {% for file in files %} + + + {{file.path}} + {{file.size}} + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/bpackage.html b/bitbake/lib/toaster/bldviewer/templates/bpackage.html new file mode 100644 index 0000000000..2e254dbad6 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/bpackage.html @@ -0,0 +1,44 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Packages{% endblock %} +{% block pagetable %} + {% if not packages %} +

No packages were recorded for this target!

+ {% else %} + + + Name + Version + Recipe + Summary + Section + Description + Size on host disk (KBytes) + License + Dependencies List (all) + + + {% for package in packages %} + + + {{package.name}} ({{package.filelist_bpackage.count}} files) + {{package.version}}-{{package.revision}} + {{package.recipe.name}}{{package.package_name}} + + {{package.summary}} + {{package.section}} + {{package.description}} + {{package.size}} + {{package.license}} + +
+ {% for bpd in package.bpackage_dependencies_package.all %} + {{bpd.dep_type}}: {{bpd.depends_on}}
+ {% endfor %} +
+ + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/build.html b/bitbake/lib/toaster/bldviewer/templates/build.html new file mode 100644 index 0000000000..ab6e19643b --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/build.html @@ -0,0 +1,43 @@ +{% extends "basetable.html" %} + +{% block pagename %} +

Toaster - Builds

+{% endblock %} + +{% block pagetable %} + + {% load projecttags %} + + Outcome + Started On + Completed On + Target + Machine + Time + Errors + Warnings + Output + Log + Bitbake Version + Build Name + + {% for build in builds %} + + {{build.get_outcome_display}} + {{build.started_on}} + {{build.completed_on}} + {% for t in build.target_set.all %}{{t.target}}{% if t.is_image %} (Img){% endif %}
{% endfor %} + {{build.machine}} + {% time_difference build.started_on build.completed_on %} + {{build.errors_no}}:{% if build.errors_no %}{% for error in logs %}{% if error.build == build %}{% if error.level == 2 %}

{{error.message}}

{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %} + {{build.warnings_no}}:{% if build.warnings_no %}{% for warning in logs %}{% if warning.build == build %}{% if warning.level == 1 %}

{{warning.message}}

{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %} + {% if build.outcome == 0 %}{% for t in build.target_set.all %}{% if t.is_image %}{{build.image_fstypes}}{% endif %}{% endfor %}{% endif %} + {{build.cooker_log_path}} + {{build.bitbake_version}} + {{build.build_name}} + + + {% endfor %} +{% endblock %} + + diff --git a/bitbake/lib/toaster/bldviewer/templates/configuration.html b/bitbake/lib/toaster/bldviewer/templates/configuration.html new file mode 100644 index 0000000000..052c37c4e8 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/configuration.html @@ -0,0 +1,20 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Configuration{% endblock %} +{% block pagetable %} + + + Name + Value + Description + + + {% for variable in configuration %} + + + {{variable.variable_name}} + {{variable.variable_value}} + {% if variable.description %}{{variable.description}}{% endif %} + {% endfor %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/layer.html b/bitbake/lib/toaster/bldviewer/templates/layer.html new file mode 100644 index 0000000000..fa4fd9bde8 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/layer.html @@ -0,0 +1,34 @@ +{% extends "basetable.html" %} + +{% block pagename %} +

Toaster - Layers

+{% endblock %} + +{% block pagetable %} + {% load projecttags %} + + + Name + Local Path + Layer Index URL + Known Versions + + + {% for layer in layers %} + + + {{layer.name}} + {{layer.local_path}} + {{layer.layer_index_url}} + + {% for lv in layer.versions %} + + {% endfor %} +
+ ({{lv.priority}}){{lv.branch}}:{{lv.commit}} ({{lv.count}} recipes) +
+ + + {% endfor %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/package.html b/bitbake/lib/toaster/bldviewer/templates/package.html new file mode 100644 index 0000000000..642fcab9a1 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/package.html @@ -0,0 +1,36 @@ +{% extends "basebuildpage.html" %} + +{% block pagetable %} + {% if not packages %} +

No packages were recorded for this target!

+ {% else %} + + + Name + Version + Size (Bytes) + Recipe + Depends on + + + {% for package in packages %} + + + {{package.name}} + {{package.version}} + {{package.size}} + + {{package.recipe.name}}{{package.package_name}} + +
+ {% for d in package.depends_on %} + {{d.name}}
+ {% endfor %} +
+ + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/recipe.html b/bitbake/lib/toaster/bldviewer/templates/recipe.html new file mode 100644 index 0000000000..a62437066d --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/recipe.html @@ -0,0 +1,54 @@ +{% extends "basetable.html" %} + +{% block pagename %} + +

Toaster - Recipes for a Layer

+{% endblock %} + +{% block pagetable %} + {% load projecttags %} + + + + Name + Version + Summary + Description + Section + License + License file + Homepage + Bugtracker + Author + File_path + Recipe Dependency + + + {% for recipe in recipes %} + + + {{recipe.name}} + {{recipe.version}} + {{recipe.summary}} + {{recipe.description}} + {{recipe.section}} + {{recipe.license}} + {{recipe.licensing_info}} + {{recipe.homepage}} + {{recipe.bugtracker}} + {{recipe.author}} + {{recipe.file_path}} + +
+ {% for rr in recipe.r_dependencies_recipe.all %} + {{rr.depends_on.name}}
+ {% endfor %} +
+ + + + {% endfor %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/task.html b/bitbake/lib/toaster/bldviewer/templates/task.html new file mode 100644 index 0000000000..e7253698cd --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/task.html @@ -0,0 +1,63 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Tasks{% endblock %} +{% block pagetable %} + {% if not tasks %} +

No tasks were executed in this build!

+ {% else %} + + + Order + Task + Recipe Version + Task Type + Checksum + Outcome + Message + Logfile + Time + CPU usage + Disk I/O + Script type + File path + Depends + + + {% for task in tasks %} + + + {{task.order}} + + {{task.recipe.name}}.{{task.task_name}} + {{task.recipe.version}} + + {% if task.task_executed %} + Executed + {% else %} + Prebuilt + {% endif %} + + {{task.sstate_checksum}} + {{task.get_outcome_display}}{% if task.provider %}
(by {{task.provider.recipe.name}}.{{task.provider.task_name}}){% endif %} +

{{task.message}} + {{task.logfile}} + {{task.elapsed_time}} + {{task.cpu_usage}} + {{task.disk_io}} + {{task.get_script_type_display}} + {{task.recipe.file_path}} + +

+ {% for tt in task.task_dependencies_task.all %} + + {{tt.depends_on.recipe.name}}.{{tt.depends_on.task_name}}
+ {% endfor %} +
+ + + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templatetags/__init__.py b/bitbake/lib/toaster/bldviewer/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/bldviewer/templatetags/projecttags.py b/bitbake/lib/toaster/bldviewer/templatetags/projecttags.py new file mode 100644 index 0000000000..0c0d804c0c --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/bldviewer/urls.py b/bitbake/lib/toaster/bldviewer/urls.py new file mode 100644 index 0000000000..becc679203 --- /dev/null +++ b/bitbake/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\d+)/task/$', 'task', name='task'), + url(r'^build/(?P\d+)/packages/$', 'bpackage', name='bpackage'), + url(r'^build/(?P\d+)/package/(?P\d+)/files/$', 'bfile', name='bfile'), + url(r'^build/(?P\d+)/target/(?P\d+)/packages/$', 'tpackage', name='tpackage'), + url(r'^build/(?P\d+)/configuration/$', 'configuration', name='configuration'), + url(r'^layers/$', 'layer', name='all-layers'), + url(r'^layerversions/(?P\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'), + url(r'^$', redirect_to, {'url': 'builds/'}), +) diff --git a/bitbake/lib/toaster/bldviewer/views.py b/bitbake/lib/toaster/bldviewer/views.py new file mode 100644 index 0000000000..7be4d4b899 --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/manage.py b/bitbake/lib/toaster/manage.py new file mode 100755 index 0000000000..ceaa11bfc5 --- /dev/null +++ b/bitbake/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/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) diff --git a/bitbake/lib/toaster/toastergui/__init__.py b/bitbake/lib/toaster/toastergui/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/toastergui/static/images/yocto.jpg b/bitbake/lib/toaster/toastergui/static/images/yocto.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5de4b7693aa330bb8220baa81567bf82c307a8ab GIT binary patch literal 6582 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 aOU31^K>#@}(ZTG-?Z2-R|4z5g$NmTTvPqNx literal 0 HcmV?d00001 diff --git a/bitbake/lib/toaster/toastergui/templates/index.html b/bitbake/lib/toaster/toastergui/templates/index.html new file mode 100644 index 0000000000..80b02d94a9 --- /dev/null +++ b/bitbake/lib/toaster/toastergui/templates/index.html @@ -0,0 +1,13 @@ + + + GUI Page + + +{% load staticfiles %} + +Yocto + +This is your basic index page! + + + \ No newline at end of file diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py new file mode 100644 index 0000000000..6dbf0c5a56 --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py new file mode 100644 index 0000000000..ecb2f7e82f --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/toastermain/__init__.py b/bitbake/lib/toaster/toastermain/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/toastermain/settings.py b/bitbake/lib/toaster/toastermain/settings.py new file mode 100644 index 0000000000..dd1e25c087 --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/toastermain/urls.py b/bitbake/lib/toaster/toastermain/urls.py new file mode 100644 index 0000000000..d0606bce9e --- /dev/null +++ b/bitbake/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/bitbake/lib/toaster/toastermain/wsgi.py b/bitbake/lib/toaster/toastermain/wsgi.py new file mode 100644 index 0000000000..6277eb4d5e --- /dev/null +++ b/bitbake/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)