diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2014-12-05 15:19:55 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2014-12-18 10:24:06 +0000 |
commit | aa9816ad79ca34000c1cb0cdc4d35b5fc09a9ba3 (patch) | |
tree | 0c902d4bdf2bab05b1740c63cbe3735707b5394f /bitbake/lib/toaster/toastergui/views.py | |
parent | 85a17f86ea2edf24b54aa62bd25e10ff522cb6e7 (diff) | |
download | poky-aa9816ad79ca34000c1cb0cdc4d35b5fc09a9ba3.tar.gz |
bitbake: toastergui: implement UI changes to allow file download
This patchset adds download links in the build analisys pages
if toaster runs in managed mode. This allows the user to access
data directly from the web interface.
[YOCTO #6837]
(Bitbake rev: 6000e1ae5c846e51932ecd0dc21e0fa02eb10357)
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/views.py')
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 168 |
1 files changed, 101 insertions, 67 deletions
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index 8301f6ce66..736de784a3 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -27,7 +27,7 @@ from django.db import IntegrityError | |||
27 | from django.shortcuts import render, redirect | 27 | from django.shortcuts import render, redirect |
28 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable | 28 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable |
29 | from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency | 29 | from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency |
30 | from orm.models import Target_Installed_Package, Target_File, Target_Image_File | 30 | from orm.models import Target_Installed_Package, Target_File, Target_Image_File, BuildArtifact |
31 | from django.views.decorators.cache import cache_control | 31 | from django.views.decorators.cache import cache_control |
32 | from django.core.urlresolvers import reverse | 32 | from django.core.urlresolvers import reverse |
33 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger | 33 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger |
@@ -421,30 +421,36 @@ def builds(request): | |||
421 | 'ordericon':_get_toggle_order_icon(request, "timespent"), | 421 | 'ordericon':_get_toggle_order_icon(request, "timespent"), |
422 | 'orderkey' : 'timespent', | 422 | 'orderkey' : 'timespent', |
423 | }, | 423 | }, |
424 | {'name': 'Log', | 424 | {'name': 'Image files', 'clclass': 'output', |
425 | 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory", | ||
426 | # TODO: compute image fstypes from Target_Image_File | ||
427 | }, | ||
428 | ] | ||
429 | } | ||
430 | |||
431 | if not toastermain.settings.MANAGED: | ||
432 | context['tablecols'].insert(-2, | ||
433 | {'name': 'Log1', | ||
425 | 'dclass': "span4", | 434 | 'dclass': "span4", |
426 | 'qhelp': "Path to the build main log file", | 435 | 'qhelp': "Path to the build main log file", |
427 | 'clclass': 'log', 'hidden': 1, | 436 | 'clclass': 'log', 'hidden': 1, |
428 | 'orderfield': _get_toggle_order(request, "cooker_log_path"), | 437 | 'orderfield': _get_toggle_order(request, "cooker_log_path"), |
429 | 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"), | 438 | 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"), |
430 | 'orderkey' : 'cooker_log_path', | 439 | 'orderkey' : 'cooker_log_path', |
431 | }, | 440 | } |
432 | {'name': 'Output', 'clclass': 'output', | 441 | ) |
433 | 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory", | 442 | |
434 | # TODO: compute image fstypes from Target_Image_File | ||
435 | }, | ||
436 | ] | ||
437 | } | ||
438 | 443 | ||
439 | if toastermain.settings.MANAGED: | 444 | if toastermain.settings.MANAGED: |
440 | context['tablecols'].append( | 445 | context['tablecols'].append( |
441 | {'name': 'Project', 'clclass': 'project', | 446 | {'name': 'Project', 'clclass': 'project', |
442 | 'filter': {'class': 'project', | 447 | 'filter': {'class': 'project', |
443 | 'label': 'Project:', | 448 | 'label': 'Project:', |
444 | 'options': map(lambda x: (x.name,'',x.build_set.filter(outcome__lt=Build.IN_PROGRESS).count()), Project.objects.all()), | 449 | 'options': map(lambda x: (x.name,'',x.build_set.filter(outcome__lt=Build.IN_PROGRESS).count()), Project.objects.all()), |
445 | 450 | ||
446 | } | 451 | } |
447 | }) | 452 | } |
453 | ) | ||
448 | 454 | ||
449 | 455 | ||
450 | response = render(request, template, context) | 456 | response = render(request, template, context) |
@@ -481,12 +487,8 @@ def builddashboard( request, build_id ): | |||
481 | hasImages = True | 487 | hasImages = True |
482 | npkg = 0 | 488 | npkg = 0 |
483 | pkgsz = 0 | 489 | pkgsz = 0 |
484 | pid= 0 | ||
485 | tp = Target_Installed_Package.objects.filter( target_id = t.id ) | ||
486 | package = None | 490 | package = None |
487 | for p in tp: | 491 | for package in Package.objects.filter(id__in = [x.package_id for x in t.target_installed_package_set.all()]): |
488 | pid = p.package_id | ||
489 | package = Package.objects.get( pk = p.package_id ) | ||
490 | pkgsz = pkgsz + package.size | 492 | pkgsz = pkgsz + package.size |
491 | if ( package.installed_name ): | 493 | if ( package.installed_name ): |
492 | npkg = npkg + 1 | 494 | npkg = npkg + 1 |
@@ -499,7 +501,7 @@ def builddashboard( request, build_id ): | |||
499 | if ( ndx < 0 ): | 501 | if ( ndx < 0 ): |
500 | ndx = 0; | 502 | ndx = 0; |
501 | f = i.file_name[ ndx + 1: ] | 503 | f = i.file_name[ ndx + 1: ] |
502 | imageFiles.append({ 'path': f, 'size' : i.file_size }) | 504 | imageFiles.append({ 'id': i.id, 'path': f, 'size' : i.file_size }) |
503 | if ( t.is_image and | 505 | if ( t.is_image and |
504 | (( len( imageFiles ) <= 0 ) or ( len( t.license_manifest_path ) <= 0 ))): | 506 | (( len( imageFiles ) <= 0 ) or ( len( t.license_manifest_path ) <= 0 ))): |
505 | targetHasNoImages = True | 507 | targetHasNoImages = True |
@@ -517,6 +519,8 @@ def builddashboard( request, build_id ): | |||
517 | if ( p.installed_name ): | 519 | if ( p.installed_name ): |
518 | packageCount = packageCount + 1 | 520 | packageCount = packageCount + 1 |
519 | 521 | ||
522 | logmessages = list(LogMessage.objects.filter( build = build_id )) | ||
523 | |||
520 | context = { | 524 | context = { |
521 | 'build' : build, | 525 | 'build' : build, |
522 | 'hasImages' : hasImages, | 526 | 'hasImages' : hasImages, |
@@ -524,7 +528,7 @@ def builddashboard( request, build_id ): | |||
524 | 'targets' : targets, | 528 | 'targets' : targets, |
525 | 'recipecount' : recipeCount, | 529 | 'recipecount' : recipeCount, |
526 | 'packagecount' : packageCount, | 530 | 'packagecount' : packageCount, |
527 | 'logmessages' : LogMessage.objects.filter( build = build_id ), | 531 | 'logmessages' : logmessages, |
528 | } | 532 | } |
529 | return render( request, template, context ) | 533 | return render( request, template, context ) |
530 | 534 | ||
@@ -637,6 +641,9 @@ def target_common( request, build_id, target_id, variant ): | |||
637 | Package, queryset, filter_string, search_term, ordering_string, 'name' ) | 641 | Package, queryset, filter_string, search_term, ordering_string, 'name' ) |
638 | packages = _build_page_range( Paginator(queryset, pagesize), request.GET.get( 'page', 1 )) | 642 | packages = _build_page_range( Paginator(queryset, pagesize), request.GET.get( 'page', 1 )) |
639 | 643 | ||
644 | |||
645 | build = Build.objects.get( pk = build_id ) | ||
646 | |||
640 | # bring in package dependencies | 647 | # bring in package dependencies |
641 | for p in packages.object_list: | 648 | for p in packages.object_list: |
642 | p.runtime_dependencies = p.package_dependencies_source.filter( | 649 | p.runtime_dependencies = p.package_dependencies_source.filter( |
@@ -697,8 +704,7 @@ eans multiple licenses exist that cover different parts of the source', | |||
697 | tc_dependencies[ "hidden" ] = 1 | 704 | tc_dependencies[ "hidden" ] = 1 |
698 | tc_rdependencies = { | 705 | tc_rdependencies = { |
699 | 'name' : 'Reverse dependencies', | 706 | 'name' : 'Reverse dependencies', |
700 | 'qhelp' : 'Package run-time reverse dependencies (i.e. which other packages depend on t\ | 707 | 'qhelp' : 'Package run-time reverse dependencies (i.e. which other packages depend on this package', |
701 | his package', | ||
702 | 'clclass' : 'brought_in_by', | 708 | 'clclass' : 'brought_in_by', |
703 | } | 709 | } |
704 | if ( variant == 'target' ): | 710 | if ( variant == 'target' ): |
@@ -741,18 +747,10 @@ his package', | |||
741 | 'clclass' : 'layer_commit', | 747 | 'clclass' : 'layer_commit', |
742 | 'hidden' : 1, | 748 | 'hidden' : 1, |
743 | } | 749 | } |
744 | tc_layerDir = { | 750 | |
745 | 'name':'Layer directory', | ||
746 | 'qhelp':'Location in disk of the layer providing the recipe that builds the package', | ||
747 | 'orderfield' : _get_toggle_order( request, "recipe__layer_version__layer__local_path" ), | ||
748 | 'ordericon' : _get_toggle_order_icon( request, "recipe__layer_version__layer__local_path" )\ | ||
749 | , | ||
750 | 'clclass' : 'layer_directory', | ||
751 | 'hidden' : 1, | ||
752 | } | ||
753 | context = { | 751 | context = { |
754 | 'objectname': variant, | 752 | 'objectname': variant, |
755 | 'build' : Build.objects.filter( pk = build_id )[ 0 ], | 753 | 'build' : build, |
756 | 'target' : Target.objects.filter( pk = target_id )[ 0 ], | 754 | 'target' : Target.objects.filter( pk = target_id )[ 0 ], |
757 | 'objects' : packages, | 755 | 'objects' : packages, |
758 | 'packages_sum' : packages_sum[ 'installed_size__sum' ], | 756 | 'packages_sum' : packages_sum[ 'installed_size__sum' ], |
@@ -771,10 +769,21 @@ his package', | |||
771 | tc_layer, | 769 | tc_layer, |
772 | tc_layerBranch, | 770 | tc_layerBranch, |
773 | tc_layerCommit, | 771 | tc_layerCommit, |
774 | tc_layerDir, | ||
775 | ] | 772 | ] |
776 | } | 773 | } |
777 | 774 | ||
775 | if not toastermain.settings.MANAGED or build.project is None: | ||
776 | |||
777 | tc_layerDir = { | ||
778 | 'name':'Layer directory', | ||
779 | 'qhelp':'Location in disk of the layer providing the recipe that builds the package', | ||
780 | 'orderfield' : _get_toggle_order( request, "recipe__layer_version__layer__local_path" ), | ||
781 | 'ordericon' : _get_toggle_order_icon( request, "recipe__layer_version__layer__local_path" ), | ||
782 | 'clclass' : 'layer_directory', | ||
783 | 'hidden' : 1, | ||
784 | } | ||
785 | context['tablecols'].append(tc_layerDir) | ||
786 | |||
778 | response = render(request, template, context) | 787 | response = render(request, template, context) |
779 | _save_parameters_cookies(response, pagesize, orderby, request) | 788 | _save_parameters_cookies(response, pagesize, orderby, request) |
780 | return response | 789 | return response |
@@ -1136,12 +1145,13 @@ def tasks_common(request, build_id, variant, task_anchor): | |||
1136 | } | 1145 | } |
1137 | if 'diskio' == variant: tc_diskio['hidden']='0'; del tc_diskio['clclass']; tc_cache['hidden']='1'; | 1146 | if 'diskio' == variant: tc_diskio['hidden']='0'; del tc_diskio['clclass']; tc_cache['hidden']='1'; |
1138 | 1147 | ||
1148 | build = Build.objects.get(pk=build_id) | ||
1139 | 1149 | ||
1140 | context = { 'objectname': variant, | 1150 | context = { 'objectname': variant, |
1141 | 'object_search_display': object_search_display, | 1151 | 'object_search_display': object_search_display, |
1142 | 'filter_search_display': filter_search_display, | 1152 | 'filter_search_display': filter_search_display, |
1143 | 'title': title_variant, | 1153 | 'title': title_variant, |
1144 | 'build': Build.objects.get(pk=build_id), | 1154 | 'build': build, |
1145 | 'objects': tasks, | 1155 | 'objects': tasks, |
1146 | 'default_orderby' : orderby, | 1156 | 'default_orderby' : orderby, |
1147 | 'search_term': search_term, | 1157 | 'search_term': search_term, |
@@ -1157,9 +1167,12 @@ def tasks_common(request, build_id, variant, task_anchor): | |||
1157 | tc_time, | 1167 | tc_time, |
1158 | tc_cpu, | 1168 | tc_cpu, |
1159 | tc_diskio, | 1169 | tc_diskio, |
1160 | tc_log, | ||
1161 | ]} | 1170 | ]} |
1162 | 1171 | ||
1172 | |||
1173 | if not toastermain.settings.MANAGED or build.project is None: | ||
1174 | context['tablecols'].append(tc_log) | ||
1175 | |||
1163 | response = render(request, template, context) | 1176 | response = render(request, template, context) |
1164 | _save_parameters_cookies(response, pagesize, orderby, request) | 1177 | _save_parameters_cookies(response, pagesize, orderby, request) |
1165 | return response | 1178 | return response |
@@ -1206,9 +1219,11 @@ def recipes(request, build_id): | |||
1206 | revlist.append(recipe_dep) | 1219 | revlist.append(recipe_dep) |
1207 | revs[recipe.id] = revlist | 1220 | revs[recipe.id] = revlist |
1208 | 1221 | ||
1222 | build = Build.objects.get(pk=build_id) | ||
1223 | |||
1209 | context = { | 1224 | context = { |
1210 | 'objectname': 'recipes', | 1225 | 'objectname': 'recipes', |
1211 | 'build': Build.objects.get(pk=build_id), | 1226 | 'build': build, |
1212 | 'objects': recipes, | 1227 | 'objects': recipes, |
1213 | 'default_orderby' : 'name:+', | 1228 | 'default_orderby' : 'name:+', |
1214 | 'recipe_deps' : deps, | 1229 | 'recipe_deps' : deps, |
@@ -1279,6 +1294,11 @@ def recipes(request, build_id): | |||
1279 | 'qhelp':'The Git commit of the layer providing the recipe', | 1294 | 'qhelp':'The Git commit of the layer providing the recipe', |
1280 | 'clclass': 'layer_version__layer__commit', 'hidden': 1, | 1295 | 'clclass': 'layer_version__layer__commit', 'hidden': 1, |
1281 | }, | 1296 | }, |
1297 | ] | ||
1298 | } | ||
1299 | |||
1300 | if not toastermain.settings.MANAGED or build.project is None: | ||
1301 | context['tablecols'].append( | ||
1282 | { | 1302 | { |
1283 | 'name':'Layer directory', | 1303 | 'name':'Layer directory', |
1284 | 'qhelp':'Path to the layer prodiving the recipe', | 1304 | 'qhelp':'Path to the layer prodiving the recipe', |
@@ -1286,9 +1306,8 @@ def recipes(request, build_id): | |||
1286 | 'ordericon':_get_toggle_order_icon(request, "layer_version__layer__local_path"), | 1306 | 'ordericon':_get_toggle_order_icon(request, "layer_version__layer__local_path"), |
1287 | 'orderkey' : 'layer_version__layer__local_path', | 1307 | 'orderkey' : 'layer_version__layer__local_path', |
1288 | 'clclass': 'layer_version__layer__local_path', 'hidden': 1, | 1308 | 'clclass': 'layer_version__layer__local_path', 'hidden': 1, |
1289 | }, | 1309 | }) |
1290 | ] | 1310 | |
1291 | } | ||
1292 | 1311 | ||
1293 | response = render(request, template, context) | 1312 | response = render(request, template, context) |
1294 | _save_parameters_cookies(response, pagesize, orderby, request) | 1313 | _save_parameters_cookies(response, pagesize, orderby, request) |
@@ -2685,41 +2704,53 @@ if toastermain.settings.MANAGED: | |||
2685 | return render(request, template, context) | 2704 | return render(request, template, context) |
2686 | 2705 | ||
2687 | 2706 | ||
2707 | def _file_name_for_artifact(b, artifact_type, artifact_id): | ||
2708 | file_name = None | ||
2709 | # Target_Image_File file_name | ||
2710 | if artifact_type == "imagefile": | ||
2711 | file_name = Target_Image_File.objects.get(target__build = b, pk = artifact_id).file_name | ||
2688 | 2712 | ||
2689 | def build_artifact(request, build_id, artifact_type, artifact_id): | 2713 | elif artifact_type == "cookerlog": |
2690 | try: | 2714 | file_name = b.cooker_log_path |
2691 | b = Build.objects.get(pk=build_id) | 2715 | |
2692 | if b.buildrequest is None or b.buildrequest.environment is None: | 2716 | elif artifact_type == "buildartifact": |
2693 | raise Exception("Cannot download file") | 2717 | file_name = BuildArtifact.objects.get(build = b, pk = artifact_id).file_name |
2694 | 2718 | ||
2695 | file_name = None | 2719 | elif artifact_type == "licensemanifest": |
2696 | fsock = None | 2720 | file_name = Target.objects.get(build = b, pk = artifact_id).license_manifest_path |
2697 | content_type='application/force-download' | ||
2698 | # Target_Image_File file_name | ||
2699 | # Task logfile | ||
2700 | if artifact_type == "tasklogfile": | ||
2701 | file_name = Task.objects.get(build = b, pk = artifact_id).logfile | ||
2702 | 2721 | ||
2703 | # Task path_to_sstate_obj | 2722 | elif artifact_type == "tasklogfile": |
2704 | # Package_File path | 2723 | file_name = Task.objects.get(build = b, pk = artifact_id).logfile |
2705 | # Recipe file_path | ||
2706 | # VariableHistory file_name | ||
2707 | # LogMessage pathname | ||
2708 | if artifact_type == "logmessagefile": | ||
2709 | file_name = LogMessage.objects.get(build = b, pk = artifact_id).pathname | ||
2710 | 2724 | ||
2711 | if file_name is not None: | 2725 | elif artifact_type == "logmessagefile": |
2712 | content_type = b.buildrequest.environment.get_artifact_type(file_name) | 2726 | file_name = LogMessage.objects.get(build = b, pk = artifact_id).pathname |
2713 | fsock = b.buildrequest.environment.get_artifact(file_name) | 2727 | else: |
2714 | file_name = os.path.basename(file_name) | 2728 | raise Exception("FIXME: artifact type %s not implemented" % (artifact_type)) |
2715 | 2729 | ||
2716 | response = HttpResponse(fsock, content_type = content_type) | 2730 | return file_name |
2717 | 2731 | ||
2718 | # returns a file from the environment | 2732 | |
2719 | response['Content-Disposition'] = 'attachment; filename=' + file_name | 2733 | def build_artifact(request, build_id, artifact_type, artifact_id): |
2720 | return response | 2734 | b = Build.objects.get(pk=build_id) |
2721 | except: | 2735 | if b.buildrequest is None or b.buildrequest.environment is None: |
2722 | raise | 2736 | raise Exception("Artifact not available for download (missing build request or build environment)") |
2737 | |||
2738 | file_name = _file_name_for_artifact(b, artifact_type, artifact_id) | ||
2739 | fsock = None | ||
2740 | content_type='application/force-download' | ||
2741 | |||
2742 | if file_name is None: | ||
2743 | raise Exception("Could not handle artifact %s id %s" % (artifact_type, artifact_id)) | ||
2744 | else: | ||
2745 | content_type = b.buildrequest.environment.get_artifact_type(file_name) | ||
2746 | fsock = b.buildrequest.environment.get_artifact(file_name) | ||
2747 | file_name = os.path.basename(file_name) # we assume that the build environment system has the same path conventions as host | ||
2748 | |||
2749 | response = HttpResponse(fsock, content_type = content_type) | ||
2750 | |||
2751 | # returns a file from the environment | ||
2752 | response['Content-Disposition'] = 'attachment; filename=' + file_name | ||
2753 | return response | ||
2723 | 2754 | ||
2724 | 2755 | ||
2725 | 2756 | ||
@@ -2856,3 +2887,6 @@ else: | |||
2856 | 2887 | ||
2857 | def projects(request): | 2888 | def projects(request): |
2858 | raise Exception("page not available in interactive mode") | 2889 | raise Exception("page not available in interactive mode") |
2890 | |||
2891 | def xhr_importlayer(request): | ||
2892 | raise Exception("page not available in interactive mode") | ||