diff options
Diffstat (limited to 'bitbake/lib/toaster/toastergui')
6 files changed, 118 insertions, 36 deletions
diff --git a/bitbake/lib/toaster/toastergui/templates/build.html b/bitbake/lib/toaster/toastergui/templates/build.html index e71e38feb9..684ec65884 100644 --- a/bitbake/lib/toaster/toastergui/templates/build.html +++ b/bitbake/lib/toaster/toastergui/templates/build.html | |||
@@ -40,7 +40,9 @@ | |||
40 | <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work --> | 40 | <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work --> |
41 | {% for build in objects %} | 41 | {% for build in objects %} |
42 | <tr class="data"> | 42 | <tr class="data"> |
43 | <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td> | 43 | <td class="outcome"> |
44 | <a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a> | ||
45 | </td> | ||
44 | <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> | 46 | <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> |
45 | <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td> | 47 | <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td> |
46 | <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td> | 48 | <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td> |
@@ -61,11 +63,6 @@ | |||
61 | <td class="errors_no"> | 63 | <td class="errors_no"> |
62 | {% if build.errors_no %} | 64 | {% if build.errors_no %} |
63 | <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a> | 65 | <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a> |
64 | {% if MANAGED and build.project %} | ||
65 | <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> | ||
66 | <i class="icon-download-alt" title="" data-original-title="Download build log"></i> | ||
67 | </a> | ||
68 | {% endif %} | ||
69 | {%endif%} | 66 | {%endif%} |
70 | </td> | 67 | </td> |
71 | <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td> | 68 | <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td> |
diff --git a/bitbake/lib/toaster/toastergui/templates/builddashboard.html b/bitbake/lib/toaster/toastergui/templates/builddashboard.html index 2458cdb6d1..c0898e291d 100644 --- a/bitbake/lib/toaster/toastergui/templates/builddashboard.html +++ b/bitbake/lib/toaster/toastergui/templates/builddashboard.html | |||
@@ -36,7 +36,11 @@ | |||
36 | {% endif %} | 36 | {% endif %} |
37 | <span > <i class="icon-warning-sign yellow"></i><strong><a href="#warnings" class="warning show-warnings"> {{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a></strong></span> | 37 | <span > <i class="icon-warning-sign yellow"></i><strong><a href="#warnings" class="warning show-warnings"> {{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a></strong></span> |
38 | {% endif %} | 38 | {% endif %} |
39 | <span class="pull-right">Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a></span> | 39 | <span class="pull-right">Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a> |
40 | {% if MANAGED and build.project %} | ||
41 | <a class="btn {%if build.outcome == build.SUCCEEDED%}btn-success{%else%}btn-danger{%endif%} pull-right" href="{% url 'build_artifact' build.id "cookerlog" build.id %}">Download build log</a> | ||
42 | {% endif %} | ||
43 | </span> | ||
40 | {%endif%} | 44 | {%endif%} |
41 | </div> | 45 | </div> |
42 | {% if build.toaster_exceptions.count > 0 %} | 46 | {% if build.toaster_exceptions.count > 0 %} |
@@ -54,10 +58,7 @@ | |||
54 | <div class="accordion span10 pull-right" id="errors"> | 58 | <div class="accordion span10 pull-right" id="errors"> |
55 | <div class="accordion-group"> | 59 | <div class="accordion-group"> |
56 | <div class="accordion-heading"> | 60 | <div class="accordion-heading"> |
57 | {% if MANAGED and build.project %} | 61 | <a class="accordion-toggle error toggle-errors"> |
58 | <a class="btn btn-large pull-right" href="{% url 'build_artifact' build.id "cookerlog" build.id %}" style="margin:15px;">Download build log</a> | ||
59 | {% endif %} | ||
60 | <a class="accordion-toggle error toggle-errors"> | ||
61 | <h2 id="error-toggle"> | 62 | <h2 id="error-toggle"> |
62 | <i class="icon-minus-sign"></i> | 63 | <i class="icon-minus-sign"></i> |
63 | {{build.errors_no}} error{{build.errors_no|pluralize}} | 64 | {{build.errors_no}} error{{build.errors_no|pluralize}} |
diff --git a/bitbake/lib/toaster/toastergui/templates/managed_builds.html b/bitbake/lib/toaster/toastergui/templates/managed_builds.html index 121ad07898..a4db55b967 100644 --- a/bitbake/lib/toaster/toastergui/templates/managed_builds.html +++ b/bitbake/lib/toaster/toastergui/templates/managed_builds.html | |||
@@ -46,7 +46,14 @@ | |||
46 | <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work --> | 46 | <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work --> |
47 | {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #} | 47 | {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #} |
48 | <tr class="data"> | 48 | <tr class="data"> |
49 | <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td> | 49 | <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a> |
50 | {% if build.project %} | ||
51 | <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> | ||
52 | <i class="icon-download-alt" title="" data-original-title="Download build log"></i> | ||
53 | </a> | ||
54 | {% endif %} | ||
55 | |||
56 | </td> | ||
50 | <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> | 57 | <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> |
51 | <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td> | 58 | <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td> |
52 | <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td> | 59 | <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td> |
diff --git a/bitbake/lib/toaster/toastergui/templates/projectbuilds.html b/bitbake/lib/toaster/toastergui/templates/projectbuilds.html index 02d166341f..afcf5191b5 100644 --- a/bitbake/lib/toaster/toastergui/templates/projectbuilds.html +++ b/bitbake/lib/toaster/toastergui/templates/projectbuilds.html | |||
@@ -49,7 +49,14 @@ | |||
49 | <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work --> | 49 | <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work --> |
50 | {% for br in objects %}{% if br.build %} {% with build=br.build %} {# if we have a build, just display it #} | 50 | {% for br in objects %}{% if br.build %} {% with build=br.build %} {# if we have a build, just display it #} |
51 | <tr class="data"> | 51 | <tr class="data"> |
52 | <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td> | 52 | <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a> |
53 | {% if build.project %} | ||
54 | <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> | ||
55 | <i class="icon-download-alt" title="" data-original-title="Download build log"></i> | ||
56 | </a> | ||
57 | {% endif %} | ||
58 | </td> | ||
59 | |||
53 | <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> | 60 | <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td> |
54 | <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td> | 61 | <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td> |
55 | <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td> | 62 | <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td> |
@@ -70,11 +77,6 @@ | |||
70 | <td class="errors_no"> | 77 | <td class="errors_no"> |
71 | {% if build.errors_no %} | 78 | {% if build.errors_no %} |
72 | <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a> | 79 | <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a> |
73 | {% if MANAGED and build.project %} | ||
74 | <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> | ||
75 | <i class="icon-download-alt" title="" data-original-title="Download build log"></i> | ||
76 | </a> | ||
77 | {% endif %} | ||
78 | {%endif%} | 80 | {%endif%} |
79 | </td> | 81 | </td> |
80 | <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td> | 82 | <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td> |
diff --git a/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html b/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html new file mode 100644 index 0000000000..c93d4257db --- /dev/null +++ b/bitbake/lib/toaster/toastergui/templates/unavailable_artifact.html | |||
@@ -0,0 +1,17 @@ | |||
1 | {% extends "base.html" %} | ||
2 | {% load projecttags %} | ||
3 | {% load humanize %} | ||
4 | {% load static %} | ||
5 | |||
6 | {% block pagecontent %} | ||
7 | <div class="section"> | ||
8 | </div> | ||
9 | <div class="row-fluid"> | ||
10 | |||
11 | <div class="alert alert-info"> | ||
12 | <p class="lead"> The artifact you are seeking to download is not available. We are sorry. You can <a href="javascript:window.history.back()">go back</a> | ||
13 | </p> | ||
14 | </div> | ||
15 | </div> | ||
16 | {% endblock %} | ||
17 | |||
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index b5ff9b1d53..eb323ec81d 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -2914,9 +2914,6 @@ if toastermain.settings.MANAGED: | |||
2914 | if artifact_type == "imagefile": | 2914 | if artifact_type == "imagefile": |
2915 | file_name = Target_Image_File.objects.get(target__build = b, pk = artifact_id).file_name | 2915 | file_name = Target_Image_File.objects.get(target__build = b, pk = artifact_id).file_name |
2916 | 2916 | ||
2917 | elif artifact_type == "cookerlog": | ||
2918 | file_name = b.cooker_log_path | ||
2919 | |||
2920 | elif artifact_type == "buildartifact": | 2917 | elif artifact_type == "buildartifact": |
2921 | file_name = BuildArtifact.objects.get(build = b, pk = artifact_id).file_name | 2918 | file_name = BuildArtifact.objects.get(build = b, pk = artifact_id).file_name |
2922 | 2919 | ||
@@ -2935,26 +2932,87 @@ if toastermain.settings.MANAGED: | |||
2935 | 2932 | ||
2936 | 2933 | ||
2937 | def build_artifact(request, build_id, artifact_type, artifact_id): | 2934 | def build_artifact(request, build_id, artifact_type, artifact_id): |
2938 | b = Build.objects.get(pk=build_id) | 2935 | if artifact_type in ["cookerlog"]: |
2939 | if b.buildrequest is None or b.buildrequest.environment is None: | 2936 | # these artifacts are saved after building, so they are on the server itself |
2940 | raise Exception("Artifact not available for download (missing build request or build environment)") | 2937 | def _mimetype_for_artifact(path): |
2938 | try: | ||
2939 | import magic | ||
2940 | |||
2941 | # fair warning: this is a mess; there are multiple competing and incompatible | ||
2942 | # magic modules floating around, so we try some of the most common combinations | ||
2943 | |||
2944 | try: # we try ubuntu's python-magic 5.4 | ||
2945 | m = magic.open(magic.MAGIC_MIME_TYPE) | ||
2946 | m.load() | ||
2947 | return m.file(path) | ||
2948 | except AttributeError: | ||
2949 | pass | ||
2950 | |||
2951 | try: # we try python-magic 0.4.6 | ||
2952 | m = magic.Magic(magic.MAGIC_MIME) | ||
2953 | return m.from_file(path) | ||
2954 | except AttributeError: | ||
2955 | pass | ||
2956 | |||
2957 | try: # we try pip filemagic 1.6 | ||
2958 | m = magic.Magic(flags=magic.MAGIC_MIME_TYPE) | ||
2959 | return m.id_filename(path) | ||
2960 | except AttributeError: | ||
2961 | pass | ||
2962 | |||
2963 | return "binary/octet-stream" | ||
2964 | except ImportError: | ||
2965 | return "binary/octet-stream" | ||
2966 | try: | ||
2967 | # match code with runbuilds.Command.archive() | ||
2968 | build_artifact_storage_dir = os.path.join(ToasterSetting.objects.get(name="ARTIFACTS_STORAGE_DIR").value, "%d" % int(build_id)) | ||
2969 | file_name = os.path.join(build_artifact_storage_dir, "cooker_log.txt") | ||
2970 | |||
2971 | fsock = open(file_name, "r") | ||
2972 | content_type=_mimetype_for_artifact(file_name) | ||
2941 | 2973 | ||
2942 | file_name = _file_name_for_artifact(b, artifact_type, artifact_id) | 2974 | response = HttpResponse(fsock, content_type = content_type) |
2943 | fsock = None | 2975 | |
2944 | content_type='application/force-download' | 2976 | response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_name) |
2977 | return response | ||
2978 | except IOError: | ||
2979 | context = { | ||
2980 | 'build' : Build.objects.get(pk = build_id), | ||
2981 | } | ||
2982 | return render(request, "unavailable_artifact.html", context) | ||
2945 | 2983 | ||
2946 | if file_name is None: | ||
2947 | raise Exception("Could not handle artifact %s id %s" % (artifact_type, artifact_id)) | ||
2948 | else: | 2984 | else: |
2949 | content_type = b.buildrequest.environment.get_artifact_type(file_name) | 2985 | # retrieve the artifact directly from the build environment |
2950 | fsock = b.buildrequest.environment.get_artifact(file_name) | 2986 | return _get_be_artifact(request, build_id, artifact_type, artifact_id) |
2951 | file_name = os.path.basename(file_name) # we assume that the build environment system has the same path conventions as host | ||
2952 | 2987 | ||
2953 | response = HttpResponse(fsock, content_type = content_type) | ||
2954 | 2988 | ||
2955 | # returns a file from the environment | 2989 | def _get_be_artifact(request, build_id, artifact_type, artifact_id): |
2956 | response['Content-Disposition'] = 'attachment; filename=' + file_name | 2990 | try: |
2957 | return response | 2991 | b = Build.objects.get(pk=build_id) |
2992 | if b.buildrequest is None or b.buildrequest.environment is None: | ||
2993 | raise Exception("Artifact not available for download (missing build request or build environment)") | ||
2994 | |||
2995 | file_name = _file_name_for_artifact(b, artifact_type, artifact_id) | ||
2996 | fsock = None | ||
2997 | content_type='application/force-download' | ||
2998 | |||
2999 | if file_name is None: | ||
3000 | raise Exception("Could not handle artifact %s id %s" % (artifact_type, artifact_id)) | ||
3001 | else: | ||
3002 | content_type = b.buildrequest.environment.get_artifact_type(file_name) | ||
3003 | fsock = b.buildrequest.environment.get_artifact(file_name) | ||
3004 | file_name = os.path.basename(file_name) # we assume that the build environment system has the same path conventions as host | ||
3005 | |||
3006 | response = HttpResponse(fsock, content_type = content_type) | ||
3007 | |||
3008 | # returns a file from the environment | ||
3009 | response['Content-Disposition'] = 'attachment; filename=' + file_name | ||
3010 | return response | ||
3011 | except IOError: | ||
3012 | context = { | ||
3013 | 'build' : Build.objects.get(pk = build_id), | ||
3014 | } | ||
3015 | return render(request, "unavailable_artifact.html", context) | ||
2958 | 3016 | ||
2959 | 3017 | ||
2960 | 3018 | ||