summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliot Smith <elliot.smith@intel.com>2016-01-18 15:45:10 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-01-18 22:06:46 +0000
commitf98e11c80951d39401d7caef8f8324969547b246 (patch)
tree1682c919c002f0940c4c4dfdb499dc633071a692
parent68f3e1e0674e5ae8df3d2401b64aa86d782f14c6 (diff)
downloadpoky-f98e11c80951d39401d7caef8f8324969547b246.tar.gz
bitbake: toastergui: make artifact download more robust
When an artifact download is requested, Toaster goes through a convoluted series of conditions to decide which file to push to the response. In the case of build artifact downloads for command line builds, this caused an ugly exception, as command line builds don't have a build request. To simplify and catch more corner cases, remove the code which fetches files via the build environment (we only support the local build environment anyway). Then push all requests along a single path, catching any missing file errors, missing object errors or poorly-formed URLs in a single except clause which always returns a valid response. Also modify the text on the "unavailable artifact" page so it says that the artifact doesn't exist, rather than it "no longer" exists (exceptions may occur because an invalid artifact was requested, rather than an artifact which was removed). [YOCTO #7603] (Bitbake rev: 24e20db55c2933de5e58ca754b8fd5b624f47820) Signed-off-by: Elliot Smith <elliot.smith@intel.com> Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html13
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py97
2 files changed, 47 insertions, 63 deletions
diff --git a/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html b/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html
index 0301a6c608..2d3d02c2e1 100644
--- a/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html
+++ b/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html
@@ -3,15 +3,14 @@
3{% load humanize %} 3{% load humanize %}
4{% load static %} 4{% load static %}
5 5
6{% block title %} Build artifact no longer exists - Toaster {% endblock %} 6{% block title %} Build artifact does not exist - Toaster {% endblock %}
7 7
8{% block pagecontent %} 8{% block pagecontent %}
9 9 <div class="row-fluid air">
10<div class="row-fluid air"> 10 <div class="alert alert-info span8 lead">
11 <div class="alert alert-info span8 lead"> 11 <p>The build artifact you are trying to download does not exist.</p>
12 <p"> The build artifact you are trying to download no longer exists.</p> 12 <p><a href="javascript:window.history.back()">Back to previous page</a></p>
13 <p><a href="javascript:window.history.back()">Back to previous page</a></p> 13 </div>
14 </div> 14 </div>
15</div>
16{% endblock %} 15{% endblock %}
17 16
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 59e16b27b2..995937a077 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -35,7 +35,7 @@ from orm.models import BitbakeVersion, CustomImageRecipe
35from bldcontrol import bbcontroller 35from bldcontrol import bbcontroller
36from django.views.decorators.cache import cache_control 36from django.views.decorators.cache import cache_control
37from django.core.urlresolvers import reverse, resolve 37from django.core.urlresolvers import reverse, resolve
38from django.core.exceptions import MultipleObjectsReturned 38from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
39from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 39from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
40from django.http import HttpResponseBadRequest, HttpResponseNotFound 40from django.http import HttpResponseBadRequest, HttpResponseNotFound
41from django.utils import timezone 41from django.utils import timezone
@@ -2575,78 +2575,63 @@ if True:
2575 2575
2576 return context 2576 return context
2577 2577
2578 def _file_name_for_artifact(b, artifact_type, artifact_id): 2578 def _file_names_for_artifact(build, artifact_type, artifact_id):
2579 """
2580 Return a tuple (file path, file name for the download response) for an
2581 artifact of type artifact_type with ID artifact_id for build; if
2582 artifact type is not supported, returns (None, None)
2583 """
2579 file_name = None 2584 file_name = None
2580 # Target_Image_File file_name 2585 response_file_name = None
2581 if artifact_type == "imagefile": 2586
2582 file_name = Target_Image_File.objects.get(target__build = b, pk = artifact_id).file_name 2587 if artifact_type == "cookerlog":
2588 file_name = build.cooker_log_path
2589 response_file_name = "cooker.log"
2590
2591 elif artifact_type == "imagefile":
2592 file_name = Target_Image_File.objects.get(target__build = build, pk = artifact_id).file_name
2583 2593
2584 elif artifact_type == "buildartifact": 2594 elif artifact_type == "buildartifact":
2585 file_name = BuildArtifact.objects.get(build = b, pk = artifact_id).file_name 2595 file_name = BuildArtifact.objects.get(build = build, pk = artifact_id).file_name
2586 2596
2587 elif artifact_type == "licensemanifest": 2597 elif artifact_type == "licensemanifest":
2588 file_name = Target.objects.get(build = b, pk = artifact_id).license_manifest_path 2598 file_name = Target.objects.get(build = build, pk = artifact_id).license_manifest_path
2589 2599
2590 elif artifact_type == "tasklogfile": 2600 elif artifact_type == "tasklogfile":
2591 file_name = Task.objects.get(build = b, pk = artifact_id).logfile 2601 file_name = Task.objects.get(build = build, pk = artifact_id).logfile
2592 2602
2593 elif artifact_type == "logmessagefile": 2603 elif artifact_type == "logmessagefile":
2594 file_name = LogMessage.objects.get(build = b, pk = artifact_id).pathname 2604 file_name = LogMessage.objects.get(build = build, pk = artifact_id).pathname
2595 else:
2596 raise Exception("FIXME: artifact type %s not implemented" % (artifact_type))
2597 2605
2598 return file_name 2606 if file_name and not response_file_name:
2607 response_file_name = os.path.basename(file_name)
2599 2608
2609 return (file_name, response_file_name)
2600 2610
2601 def build_artifact(request, build_id, artifact_type, artifact_id): 2611 def build_artifact(request, build_id, artifact_type, artifact_id):
2602 if artifact_type in ["cookerlog"]: 2612 """
2603 try: 2613 View which returns a build artifact file as a response
2604 build = Build.objects.get(pk = build_id) 2614 """
2605 file_name = build.cooker_log_path 2615 file_name = None
2616 response_file_name = None
2617
2618 try:
2619 build = Build.objects.get(pk = build_id)
2620 file_name, response_file_name = _file_names_for_artifact(
2621 build, artifact_type, artifact_id
2622 )
2623
2624 if file_name and response_file_name:
2606 fsock = open(file_name, "r") 2625 fsock = open(file_name, "r")
2607 content_type = MimeTypeFinder.get_mimetype(file_name) 2626 content_type = MimeTypeFinder.get_mimetype(file_name)
2608 2627
2609 response = HttpResponse(fsock, content_type = content_type) 2628 response = HttpResponse(fsock, content_type = content_type)
2610 2629
2611 disposition = 'attachment; filename=cooker.log' 2630 disposition = "attachment; filename=" + response_file_name
2612 response['Content-Disposition'] = disposition 2631 response["Content-Disposition"] = disposition
2613 2632
2614 return response 2633 return response
2615 except IOError:
2616 context = {
2617 'build' : Build.objects.get(pk = build_id),
2618 }
2619 return render(request, "unavailable_artifact.html", context)
2620
2621 else:
2622 # retrieve the artifact directly from the build environment
2623 return _get_be_artifact(request, build_id, artifact_type, artifact_id)
2624
2625
2626 def _get_be_artifact(request, build_id, artifact_type, artifact_id):
2627 try:
2628 b = Build.objects.get(pk=build_id)
2629 if b.buildrequest is None or b.buildrequest.environment is None:
2630 raise Exception("Artifact not available for download (missing build request or build environment)")
2631
2632 file_name = _file_name_for_artifact(b, artifact_type, artifact_id)
2633 fsock = None
2634 content_type='application/force-download'
2635
2636 if file_name is None:
2637 raise Exception("Could not handle artifact %s id %s" % (artifact_type, artifact_id))
2638 else: 2634 else:
2639 content_type = MimeTypeFinder.get_mimetype(file_name) 2635 return render(request, "unavailable_artifact.html")
2640 fsock = b.buildrequest.environment.get_artifact(file_name) 2636 except ObjectDoesNotExist, IOError:
2641 file_name = os.path.basename(file_name) # we assume that the build environment system has the same path conventions as host 2637 return render(request, "unavailable_artifact.html")
2642
2643 response = HttpResponse(fsock, content_type = content_type)
2644
2645 # returns a file from the environment
2646 response['Content-Disposition'] = 'attachment; filename=' + file_name
2647 return response
2648 except IOError:
2649 context = {
2650 'build' : Build.objects.get(pk = build_id),
2651 }
2652 return render(request, "unavailable_artifact.html", context)