bitbake: toaster: enable remote HTTP API for status aggregation

Add support for Toaster aggregators with a set of api links that
return JSON data for (a) builds in progress, (b) builds completed,
(c) specific build data, and (d) an is-alive health ping link.

[YOCTO #11794]

(Bitbake rev: d8e79661c69671dd424dca5cc3f7f2f855b0afed)

Signed-off-by: David Reyna <David.Reyna@windriver.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
David Reyna 2017-07-28 17:14:13 -07:00 committed by Richard Purdie
parent 3f71378fde
commit 3bc3d26b46
4 changed files with 100 additions and 4 deletions

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head><title>Toaster Health</title></head>
<body>Ok</body>
</html>

View File

@ -244,6 +244,11 @@ urlpatterns = [
url(r'^mostrecentbuilds$', widgets.MostRecentBuildsView.as_view(),
name='most_recent_builds'),
# default redirection
# JSON data for aggregators
url(r'^api/builds$', views.json_builds, name='json_builds'),
url(r'^api/building$', views.json_building, name='json_building'),
url(r'^api/build/(?P<build_id>\d+)$', views.json_build, name='json_build'),
# default redirection
url(r'^$', RedirectView.as_view(url='landing', permanent=True)),
]

View File

@ -35,7 +35,7 @@ from orm.models import BitbakeVersion, CustomImageRecipe
from django.core.urlresolvers import reverse, resolve
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponseNotFound
from django.http import HttpResponseNotFound, JsonResponse
from django.utils import timezone
from datetime import timedelta, datetime
from toastergui.templatetags.projecttags import json as jsonfilter
@ -1256,6 +1256,89 @@ def managedcontextprocessor(request):
}
return ret
# REST-based API calls to return build/building status to external Toaster
# managers and aggregators via JSON
def _json_build_status(build_id,extend):
build_stat = None
try:
build = Build.objects.get( pk = build_id )
build_stat = {}
build_stat['id'] = build.id
build_stat['name'] = build.build_name
build_stat['machine'] = build.machine
build_stat['distro'] = build.distro
build_stat['start'] = build.started_on
# look up target name
target= Target.objects.get( build = build )
if target:
if target.task:
build_stat['target'] = '%s:%s' % (target.target,target.task)
else:
build_stat['target'] = '%s' % (target.target)
else:
build_stat['target'] = ''
# look up project name
project = Project.objects.get( build = build )
if project:
build_stat['project'] = project.name
else:
build_stat['project'] = ''
if Build.IN_PROGRESS == build.outcome:
now = timezone.now()
timediff = now - build.started_on
build_stat['seconds']='%.3f' % timediff.total_seconds()
build_stat['clone']='%d:%d' % (build.repos_cloned,build.repos_to_clone)
build_stat['parse']='%d:%d' % (build.recipes_parsed,build.recipes_to_parse)
tf = Task.objects.filter(build = build)
tfc = tf.count()
if tfc > 0:
tfd = tf.exclude(order__isnull=True).count()
else:
tfd = 0
build_stat['task']='%d:%d' % (tfd,tfc)
else:
build_stat['outcome'] = build.get_outcome_text()
timediff = build.completed_on - build.started_on
build_stat['seconds']='%.3f' % timediff.total_seconds()
build_stat['stop'] = build.completed_on
messages = LogMessage.objects.all().filter(build = build)
errors = len(messages.filter(level=LogMessage.ERROR) |
messages.filter(level=LogMessage.EXCEPTION) |
messages.filter(level=LogMessage.CRITICAL))
build_stat['errors'] = errors
warnings = len(messages.filter(level=LogMessage.WARNING))
build_stat['warnings'] = warnings
if extend:
build_stat['cooker_log'] = build.cooker_log_path
except Exception as e:
build_state = str(e)
return build_stat
def json_builds(request):
build_table = []
builds = []
try:
builds = Build.objects.exclude(outcome=Build.IN_PROGRESS).order_by("-started_on")
for build in builds:
build_table.append(_json_build_status(build.id,False))
except Exception as e:
build_table = str(e)
return JsonResponse({'builds' : build_table, 'count' : len(builds)})
def json_building(request):
build_table = []
builds = []
try:
builds = Build.objects.filter(outcome=Build.IN_PROGRESS).order_by("-started_on")
for build in builds:
build_table.append(_json_build_status(build.id,False))
except Exception as e:
build_table = str(e)
return JsonResponse({'building' : build_table, 'count' : len(builds)})
def json_build(request,build_id):
return JsonResponse({'build' : _json_build_status(build_id,True)})
import toastermain.settings
@ -1694,3 +1777,4 @@ if True:
return render(request, "unavailable_artifact.html")
except (ObjectDoesNotExist, IOError):
return render(request, "unavailable_artifact.html")

View File

@ -20,9 +20,8 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import patterns, include, url
from django.views.generic import RedirectView
from django.views.generic import RedirectView, TemplateView
from django.views.decorators.cache import never_cache
import bldcollector.views
import logging
@ -46,6 +45,8 @@ urlpatterns = [
# in the future.
url(r'^orm/eventfile$', bldcollector.views.eventfile),
url(r'^health$', TemplateView.as_view(template_name="health.html"), name='Toaster Health'),
# if no application is selected, we have the magic toastergui app here
url(r'^$', never_cache(RedirectView.as_view(url='/toastergui/', permanent=True))),
]