Merge lp:~milo/linaro-ci-dashboard/lava-data-model into lp:linaro-ci-dashboard
- lava-data-model
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 67 |
Proposed branch: | lp:~milo/linaro-ci-dashboard/lava-data-model |
Merge into: | lp:linaro-ci-dashboard |
Diff against target: |
1083 lines (+865/-7) (has conflicts) 22 files modified
Makefile (+2/-0) dashboard/frontend/forms/loop_form.py (+6/-3) dashboard/frontend/migrations/0007_auto__add_field_loopbuild_lava_test_status.py (+52/-0) dashboard/frontend/migrations/0008_auto__add_field_loop_lava_tests.py (+53/-0) dashboard/frontend/models/loop.py (+1/-0) dashboard/frontend/models/loop_build.py (+3/-0) dashboard/frontend/templates/loop_detail.html (+2/-2) dashboard/js/lava/lava_test_select.js (+28/-0) dashboard/lava/lib/lavalib.py (+249/-0) dashboard/lava/migrations/0001_initial.py (+65/-0) dashboard/lava/models/__init__.py (+1/-0) dashboard/lava/models/lava.py (+37/-0) dashboard/lava/templates/lava_select.html (+66/-0) dashboard/lava/templates/tests_list.html (+7/-0) dashboard/lava/tests/__init__.py (+23/-0) dashboard/lava/tests/test_lavalib.py (+70/-0) dashboard/lava/urls.py (+29/-0) dashboard/lava/views/lava_select_test_names.py (+58/-0) dashboard/lava/views/lava_select_view.py (+46/-0) dashboard/lava/widgets/lava_test_select_widget.py (+54/-0) dashboard/settings.py (+8/-0) dashboard/urls.py (+5/-2) Text conflict in dashboard/settings.py |
To merge this branch: | bzr merge lp:~milo/linaro-ci-dashboard/lava-data-model |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stevan Radaković | Pending | ||
Review via email:
|
Commit message
Description of the change
Merge proposal to keep track and discuss the LAVA model and other implementation details.
Discussin with Danilo we thought about creating a separate app for the ci-dashboard, more or less as jenkinsserver.
In this first implmentation there is the structure of the app, initial implementation for the model, and a lib subdirectory ofr a library to interact with LAVA XML-RPC.
- 53. By Milo Casagrande
-
Merged from trunk.

Stevan Radaković (stevanr) wrote : | # |

Milo Casagrande (milo) wrote : | # |
On Wed, Sep 19, 2012 at 3:46 PM, Stevan Radaković
<email address hidden> wrote:
> This looks OK for now.
> I guess you already have these things in mind, but I'm going to list additional things to be extended here:
> 1. Extend the test status to match all available status results as in LAVA and move the status choices list to i.e. settings so it can be used in both loop_build and lavatest classes.
Yeah, I was thinking where to move those values, and settings.py is a
good place to store the all for both of them.
> 2. Add the unit tests for lava library.
Sure, that is way needed. Didn't insert it at the moment, need to
clear some things out first.
> 3. I noticed in LAVA that each test contain multiple entries (i don't know what entity is this you can see in https:/
I think we might not cover that at the moment. That is the total
number of runs for that test. Indipendently of the
board/build/
test has been used, not really relevant to what we are doing.
The other thing I'm looking into, is how to best retrieve information
out from LAVA, even server side in an asynchronous way, without having
to deal with cron job. I was thinking about using RabbitMQ/Celery for
this: schedule the necessary tasks, and let the queue handle them. But
those are two new dependencies we have to deal with (maybe good to
start discussion on the mailing list).
Thanks for taking a look!
--
Milo Casagrande
Infrastructure Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs
- 54. By Milo Casagrande
-
Added initial tests selection view.
- 55. By Milo Casagrande
-
Added field to store list of tests for each loop, needs custom widget.
- 56. By Milo Casagrande
-
Added LAVA widget for test selection.
- 57. By Milo Casagrande
-
Merged from trunk.
- 58. By Milo Casagrande
-
Added OK button to dialog.
- 59. By Milo Casagrande
-
Clean up and fixes.
- 60. By Milo Casagrande
-
Added hard-coded test names.
- 61. By Milo Casagrande
-
Added lavalib tests, started porting of linaro-
android- tools. - 62. By Milo Casagrande
-
Added placeholder for new method.
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2012-09-14 13:44:53 +0000 |
3 | +++ Makefile 2012-09-21 15:30:40 +0000 |
4 | @@ -12,6 +12,8 @@ |
5 | echo "No changes to frontend models." |
6 | python dashboard/manage.py schemamigration jenkinsserver --auto || \ |
7 | echo "No changes to jenkins models." |
8 | + python dashboard/manage.py schemamigration lava --auto || \ |
9 | + echo "No changes to lava models." |
10 | python dashboard/manage.py schemamigration android_build --auto || \ |
11 | echo "No changes to android_build models." |
12 | python dashboard/manage.py schemamigration kernel_build --auto || \ |
13 | |
14 | === modified file 'dashboard/frontend/forms/loop_form.py' |
15 | --- dashboard/frontend/forms/loop_form.py 2012-09-10 14:27:16 +0000 |
16 | +++ dashboard/frontend/forms/loop_form.py 2012-09-21 15:30:40 +0000 |
17 | @@ -23,6 +23,7 @@ |
18 | ) |
19 | from frontend.models.loop import Loop |
20 | from frontend.widgets.chain_loop_widget import ChainLoopWidget |
21 | +from lava.widgets.lava_test_select_widget import LavaTestSelectWidget |
22 | |
23 | |
24 | class LoopForm(ModelForm): |
25 | @@ -31,18 +32,20 @@ |
26 | """ |
27 | class Meta: |
28 | model = Loop |
29 | - exclude = ('server') |
30 | + exclude = ('server', 'lava_tests') |
31 | |
32 | next_loop = IntegerField(widget=ChainLoopWidget, required=False) |
33 | type = CharField(widget=HiddenInput, initial=Loop.__name__, |
34 | required=False) |
35 | + lava_tests = CharField(widget=LavaTestSelectWidget, required=False) |
36 | |
37 | def save(self, *args, **kwargs): |
38 | instance = super(LoopForm, self).save(commit=False) |
39 | - if self.data["next_loop_0"]: |
40 | + if self.data.get("next_loop_0", None): |
41 | next_loop = Loop.objects.get(id=self.data["next_loop_0"]) |
42 | instance.next_loop = next_loop |
43 | - |
44 | + if self.data.get("lava_tests_0", None): |
45 | + instance.lava_tests = self.data["lava_tests_0"] |
46 | instance.save() |
47 | |
48 | return instance |
49 | |
50 | === added file 'dashboard/frontend/migrations/0007_auto__add_field_loopbuild_lava_test_status.py' |
51 | --- dashboard/frontend/migrations/0007_auto__add_field_loopbuild_lava_test_status.py 1970-01-01 00:00:00 +0000 |
52 | +++ dashboard/frontend/migrations/0007_auto__add_field_loopbuild_lava_test_status.py 2012-09-21 15:30:40 +0000 |
53 | @@ -0,0 +1,52 @@ |
54 | +# encoding: utf-8 |
55 | +import datetime |
56 | +from south.db import db |
57 | +from south.v2 import SchemaMigration |
58 | +from django.db import models |
59 | + |
60 | +class Migration(SchemaMigration): |
61 | + |
62 | + def forwards(self, orm): |
63 | + |
64 | + # Adding field 'LoopBuild.lava_test_status' |
65 | + db.add_column('frontend_loopbuild', 'lava_test_status', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True), keep_default=False) |
66 | + |
67 | + |
68 | + def backwards(self, orm): |
69 | + |
70 | + # Deleting field 'LoopBuild.lava_test_status' |
71 | + db.delete_column('frontend_loopbuild', 'lava_test_status') |
72 | + |
73 | + |
74 | + models = { |
75 | + 'frontend.loop': { |
76 | + 'Meta': {'object_name': 'Loop'}, |
77 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
78 | + 'is_official': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
79 | + 'is_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
80 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), |
81 | + 'next_loop': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'previous_loop'", 'unique': 'True', 'null': 'True', 'to': "orm['frontend.Loop']"}), |
82 | + 'server': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['jenkinsserver.JenkinsServer']", 'null': 'True'}), |
83 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}) |
84 | + }, |
85 | + 'frontend.loopbuild': { |
86 | + 'Meta': {'ordering': "['-id']", 'unique_together': "(('build_number', 'loop'),)", 'object_name': 'LoopBuild'}, |
87 | + 'build_number': ('django.db.models.fields.IntegerField', [], {'default': 'None'}), |
88 | + 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}), |
89 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
90 | + 'lava_test_status': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), |
91 | + 'loop': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Loop']"}), |
92 | + 'remote_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
93 | + 'result_xml': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
94 | + 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'}) |
95 | + }, |
96 | + 'jenkinsserver.jenkinsserver': { |
97 | + 'Meta': {'object_name': 'JenkinsServer'}, |
98 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
99 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
100 | + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
101 | + 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
102 | + } |
103 | + } |
104 | + |
105 | + complete_apps = ['frontend'] |
106 | |
107 | === added file 'dashboard/frontend/migrations/0008_auto__add_field_loop_lava_tests.py' |
108 | --- dashboard/frontend/migrations/0008_auto__add_field_loop_lava_tests.py 1970-01-01 00:00:00 +0000 |
109 | +++ dashboard/frontend/migrations/0008_auto__add_field_loop_lava_tests.py 2012-09-21 15:30:40 +0000 |
110 | @@ -0,0 +1,53 @@ |
111 | +# encoding: utf-8 |
112 | +import datetime |
113 | +from south.db import db |
114 | +from south.v2 import SchemaMigration |
115 | +from django.db import models |
116 | + |
117 | +class Migration(SchemaMigration): |
118 | + |
119 | + def forwards(self, orm): |
120 | + |
121 | + # Adding field 'Loop.lava_tests' |
122 | + db.add_column('frontend_loop', 'lava_tests', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) |
123 | + |
124 | + |
125 | + def backwards(self, orm): |
126 | + |
127 | + # Deleting field 'Loop.lava_tests' |
128 | + db.delete_column('frontend_loop', 'lava_tests') |
129 | + |
130 | + |
131 | + models = { |
132 | + 'frontend.loop': { |
133 | + 'Meta': {'object_name': 'Loop'}, |
134 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
135 | + 'is_official': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
136 | + 'is_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
137 | + 'lava_tests': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
138 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), |
139 | + 'next_loop': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'previous_loop'", 'unique': 'True', 'null': 'True', 'to': "orm['frontend.Loop']"}), |
140 | + 'server': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['jenkinsserver.JenkinsServer']", 'null': 'True'}), |
141 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}) |
142 | + }, |
143 | + 'frontend.loopbuild': { |
144 | + 'Meta': {'ordering': "['-id']", 'unique_together': "(('build_number', 'loop'),)", 'object_name': 'LoopBuild'}, |
145 | + 'build_number': ('django.db.models.fields.IntegerField', [], {'default': 'None'}), |
146 | + 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}), |
147 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
148 | + 'lava_test_status': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), |
149 | + 'loop': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Loop']"}), |
150 | + 'remote_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
151 | + 'result_xml': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
152 | + 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'}) |
153 | + }, |
154 | + 'jenkinsserver.jenkinsserver': { |
155 | + 'Meta': {'object_name': 'JenkinsServer'}, |
156 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
157 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
158 | + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
159 | + 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
160 | + } |
161 | + } |
162 | + |
163 | + complete_apps = ['frontend'] |
164 | |
165 | === modified file 'dashboard/frontend/models/loop.py' |
166 | --- dashboard/frontend/models/loop.py 2012-09-14 11:36:54 +0000 |
167 | +++ dashboard/frontend/models/loop.py 2012-09-21 15:30:40 +0000 |
168 | @@ -40,6 +40,7 @@ |
169 | unique=True, on_delete=models.SET_NULL, |
170 | related_name='previous_loop', |
171 | verbose_name='Next loop') |
172 | + lava_tests = models.CharField(max_length=255, blank=True, null=True) |
173 | |
174 | def __init__(self, *args, **kwargs): |
175 | self.log = Logger.getClassLogger(self) |
176 | |
177 | === modified file 'dashboard/frontend/models/loop_build.py' |
178 | --- dashboard/frontend/models/loop_build.py 2012-09-13 13:56:36 +0000 |
179 | +++ dashboard/frontend/models/loop_build.py 2012-09-21 15:30:40 +0000 |
180 | @@ -40,6 +40,7 @@ |
181 | (SUCCESS, 'SUCCESS'), |
182 | (ABORTED, 'ABORTED'), |
183 | ) |
184 | + |
185 | FINISHED_STATUSES = [FAILURE, SUCCESS] |
186 | NON_FINISHED_STATUSES = [SCHEDULED, RUNNING] |
187 | |
188 | @@ -49,6 +50,8 @@ |
189 | status = models.CharField(max_length=20, choices=BUILD_STATUS) |
190 | duration = models.DecimalField(max_digits=8, decimal_places=2) |
191 | result_xml = models.TextField(null=True, blank=True) |
192 | + # General status of the LAVA tests for this build. |
193 | + lava_test_status = models.CharField(max_length=20, blank=True, null=True) |
194 | |
195 | def save(self, *args, **kwargs): |
196 | loop_builds = LoopBuild.objects.filter(loop=self.loop).order_by("-id") |
197 | |
198 | === modified file 'dashboard/frontend/templates/loop_detail.html' |
199 | --- dashboard/frontend/templates/loop_detail.html 2012-09-13 09:13:00 +0000 |
200 | +++ dashboard/frontend/templates/loop_detail.html 2012-09-21 15:30:40 +0000 |
201 | @@ -21,8 +21,8 @@ |
202 | </div> |
203 | {% block values %} |
204 | {% comment %} |
205 | - This is used in the textfield detail views, to give a spece |
206 | - to represent the key<>value values inserte by the user in a better |
207 | + This is used in the textfield detail views, to give a space |
208 | + to represent the key<>value values inserted by the user in a better |
209 | way. |
210 | {% endcomment %} |
211 | {% endblock values %} |
212 | |
213 | === added directory 'dashboard/img' |
214 | === added file 'dashboard/img/loading.gif' |
215 | Binary files dashboard/img/loading.gif 1970-01-01 00:00:00 +0000 and dashboard/img/loading.gif 2012-09-21 15:30:40 +0000 differ |
216 | === added directory 'dashboard/js/lava' |
217 | === added file 'dashboard/js/lava/lava_test_select.js' |
218 | --- dashboard/js/lava/lava_test_select.js 1970-01-01 00:00:00 +0000 |
219 | +++ dashboard/js/lava/lava_test_select.js 2012-09-21 15:30:40 +0000 |
220 | @@ -0,0 +1,28 @@ |
221 | +$().ready(function () { |
222 | + "use strict"; |
223 | + init_lava_dialog(); |
224 | + $('#lava_select_link').click(function () { |
225 | + populate_dialog(); |
226 | + $('#lava_select_dialog').dialog('open'); |
227 | + }); |
228 | +}); |
229 | + |
230 | +function init_lava_dialog() { |
231 | + "use strict"; |
232 | + $('<div id="lava_select_dialog"></div>').dialog({ |
233 | + autoOpen: false, |
234 | + title: 'Select LAVA Tests', |
235 | + draggable: false, |
236 | + height: 480, |
237 | + width: 640, |
238 | + modal: true, |
239 | + resizable: false |
240 | + }); |
241 | +} |
242 | + |
243 | +function populate_dialog() { |
244 | + "use strict"; |
245 | + $.get('/lava/select/', function (data) { |
246 | + $('#lava_select_dialog').html(data); |
247 | + }); |
248 | +} |
249 | |
250 | === added directory 'dashboard/lava' |
251 | === added file 'dashboard/lava/__init__.py' |
252 | === added directory 'dashboard/lava/forms' |
253 | === added file 'dashboard/lava/forms/__init__.py' |
254 | === added directory 'dashboard/lava/lib' |
255 | === added file 'dashboard/lava/lib/__init__.py' |
256 | === added file 'dashboard/lava/lib/lavalib.py' |
257 | --- dashboard/lava/lib/lavalib.py 1970-01-01 00:00:00 +0000 |
258 | +++ dashboard/lava/lib/lavalib.py 2012-09-21 15:30:40 +0000 |
259 | @@ -0,0 +1,249 @@ |
260 | +# Copyright (C) 2012 Linaro |
261 | +# |
262 | +# This file is part of linaro-ci-dashboard. |
263 | +# |
264 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
265 | +# it under the terms of the GNU Affero General Public License as published by |
266 | +# the Free Software Foundation, either version 3 of the License, or |
267 | +# (at your option) any later version. |
268 | +# |
269 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
270 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
271 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
272 | +# GNU Affero General Public License for more details. |
273 | +# |
274 | +# You should have received a copy of the GNU Affero General Public License |
275 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
276 | + |
277 | +import xmlrpclib |
278 | +from lib.logger import Logger |
279 | +from dashboard.settings import LAVA_URL |
280 | + |
281 | + |
282 | +# The default delimiter used to separate test names to be executed. |
283 | +TEST_PLAN_DELIMITER = ';' |
284 | + |
285 | +# Map a TARGET_PRODUCT to LAVA parameters. |
286 | +PRODUCT_MAP = { |
287 | + "pandaboard": { |
288 | + "test_device_type": "panda", |
289 | + "image_path": "%s%s" % ( |
290 | + "target/product/", |
291 | + "pandaboard")}, |
292 | + "full_panda": { |
293 | + "test_device_type": "panda", |
294 | + "image_path": "%s%s" % ( |
295 | + "target/product/", |
296 | + "panda")}, |
297 | + "beagleboard": { |
298 | + "test_device_type": "beaglexm", |
299 | + "image_path": "%s%s" % ( |
300 | + "target/product/", |
301 | + "beagleboard")}, |
302 | + "snowball": { |
303 | + "test_device_type": "snowball_sd", |
304 | + "image_path": "%s%s" % ( |
305 | + "target/product/", |
306 | + "snowball")}, |
307 | + "iMX53": { |
308 | + "test_device_type": "mx53loco", |
309 | + "image_path": "%s%s" % ( |
310 | + "target/product/", |
311 | + "iMX53")}, |
312 | + "origen": { |
313 | + "test_device_type": "origen", |
314 | + "image_path": "%s%s" % ( |
315 | + "target/product/", |
316 | + "origen")}, |
317 | + "vexpress": { |
318 | + "test_device_type": "vexpress", |
319 | + "image_path": "%s%s" % ( |
320 | + "target/product/", |
321 | + "vexpress")}, |
322 | + } |
323 | + |
324 | +OPTION_SUFFIX = "_OPTION" |
325 | +TIMEOUT_SUFFIX = "_TIMEOUT" |
326 | + |
327 | +# Special token for LAVA_TEST_PLAN that allows the build owner to encode a |
328 | +# reboot in between test actions. |
329 | +REBOOT_TOKEN = '[system-reboot]' |
330 | + |
331 | + |
332 | +class LavaLibException(Exception): |
333 | + """ |
334 | + Base class for exception in LAVA lib. |
335 | + """ |
336 | + |
337 | + |
338 | +class LavaLib(object): |
339 | + """ |
340 | + Library to interact with LAVA XML-RPC. |
341 | + """ |
342 | + |
343 | + def __init__(self, user=None, token=None, secure=True): |
344 | + """ |
345 | + Initialize the LavaLib library to interact with LAVA. |
346 | + |
347 | + :param user: The user name to use for interacting with LAVA. |
348 | + :type user str |
349 | + :param token: The token associated with the user. |
350 | + :type token str |
351 | + :param secure: If the connection should be secure or not. Default to |
352 | + True. |
353 | + :type secure bool |
354 | + """ |
355 | + super(LavaLib, self).__init__() |
356 | + self.log = Logger.getClassLogger(self) |
357 | + self.user = user |
358 | + self.token = token |
359 | + self.secure = secure |
360 | + |
361 | + def _lava_auth(self): |
362 | + """ |
363 | + Creates the LAVA authorization string used to talk to LAVA. |
364 | + |
365 | + :return A string with user name and token, if they have been specified. |
366 | + """ |
367 | + auth_url = '' |
368 | + if self.user is not None and self.token is not None: |
369 | + auth_url = '%(user)s:%(token)s@' % dict(user=self.user, |
370 | + token=self.token) |
371 | + return auth_url |
372 | + |
373 | + def _create_server_url(self): |
374 | + """ |
375 | + Creates the server URL necessary to interact with the LAVA server. |
376 | + |
377 | + :return A string with the URL for the LAVA server. |
378 | + """ |
379 | + protocol = 'https' |
380 | + if not self.secure: |
381 | + protocol = 'http' |
382 | + |
383 | + lava_auth = self._lava_auth() |
384 | + |
385 | + if lava_auth is not '' and not self.secure: |
386 | + self.log.warning('Sending sensible information through an ' |
387 | + 'insecure channel. Consider using HTTPS.') |
388 | + |
389 | + params_dict = dict(protocol=protocol, |
390 | + auth_url=lava_auth, |
391 | + lava_url=LAVA_URL) |
392 | + server_url = ('%(protocol)s://%(auth_url)s%(lava_url)s' % params_dict) |
393 | + return server_url |
394 | + |
395 | + def _get_server(self): |
396 | + """ |
397 | + Gets a connection to the LAVA server. |
398 | + |
399 | + :return A xmlrpclib.ServerProxy object. |
400 | + """ |
401 | + return xmlrpclib.ServerProxy(self._create_server_url()) |
402 | + |
403 | + def get_test_names(self, sort=True): |
404 | + """ |
405 | + Get the name of the tests it is possible to run. |
406 | + |
407 | + :param sort: If the results should be sorted alphabetically. Default to |
408 | + True. |
409 | + :type sort bool |
410 | + :return A list with all the test names. |
411 | + """ |
412 | + # TODO we are faking some data here, since the LAVA server at |
413 | + # validation.linaro.org does not include now the function to get |
414 | + # out the test names. |
415 | + test_names = ['0xbench', 'bluetooth', 'bootchart', 'busybox', |
416 | + 'glmark2', 'skia', 'v8', 'mmtest', 'monkey', |
417 | + 'monkey_long_run'] |
418 | + if sort: |
419 | + test_names.sort() |
420 | + return test_names |
421 | + |
422 | + def send_job(self, test_plan): |
423 | + """ |
424 | + Sends the test plan to be executed to the LAVA server. |
425 | + |
426 | + :param test_plan: The string of tests to be executed. |
427 | + :type test_plan str |
428 | + """ |
429 | + config = self._prepare_config(self._split_test_plan(test_plan)) |
430 | + try: |
431 | + self._get_server().scheduler.submit_job(config) |
432 | + except: |
433 | + self.log.exception('Impossible to submit LAVA requests.') |
434 | + |
435 | + def _split_test_plan(self, test_plan): |
436 | + """ |
437 | + Splits the string of tests into single test names. The split happens |
438 | + on the default delimiter set to the variable TEST_PLAN_DELIMITER. |
439 | + |
440 | + :param test_plan: The string of tests to be split. |
441 | + :type test_plan str |
442 | + :return A list of test names. |
443 | + """ |
444 | + if TEST_PLAN_DELIMITER in test_plan: |
445 | + # Exclude the last element, the test plan we get should have also |
446 | + # the delimiter appended on the last element. |
447 | + tests = test_plan.split(TEST_PLAN_DELIMITER)[:-1] |
448 | + |
449 | + # XXX need to check if this test still exists. |
450 | + if "test_android_0xbench" in tests: |
451 | + i = tests.index("test_android_0xbench") |
452 | + tests[i] = "0xbench" |
453 | + else: |
454 | + self.log.exception('Found wrong delimiter in test plan ' |
455 | + 'definition.') |
456 | + raise LavaLibException('Wrong delimiter in test plan string' |
457 | + 'definition.') |
458 | + return tests |
459 | + |
460 | + def _prepare_config(self, tests): |
461 | + """ |
462 | + :return A JSON representation of the config to be run. |
463 | + """ |
464 | + # TODO here should go the biggest part of the main() in |
465 | + # post-build-lava inside linaro-android-build-tools. |
466 | + return {} |
467 | + |
468 | + def gen_android_test_actions(self, tests): |
469 | + # TODO this needs to be reworked, need to eliminate env var lookup |
470 | + # Method taken from linaro-android-build-tools. |
471 | + import os |
472 | + actions = [] |
473 | + if len(tests) > 0: |
474 | + test_actions = [test for test in tests if test != REBOOT_TOKEN] |
475 | + |
476 | + if len(test_actions) > 0: |
477 | + inst_action = { |
478 | + "command": "lava_android_test_install", |
479 | + "parameters": { |
480 | + # ensure only unique test names |
481 | + "tests": list(set(test_actions)) |
482 | + } |
483 | + } |
484 | + actions.append(inst_action) |
485 | + |
486 | + for test in tests: |
487 | + parameters = {'test_name': test} |
488 | + |
489 | + test_option = os.environ.get('%s%s' % (test.upper(), |
490 | + OPTION_SUFFIX)) |
491 | + if test_option: |
492 | + parameters['option'] = test_option |
493 | + |
494 | + timeout_option = os.environ.get('%s%s' % (test.upper(), |
495 | + TIMEOUT_SUFFIX)) |
496 | + if timeout_option: |
497 | + parameters['timeout'] = int(timeout_option) |
498 | + |
499 | + if test != REBOOT_TOKEN: |
500 | + run_action = { |
501 | + "command": "lava_android_test_run", |
502 | + "parameters": parameters |
503 | + } |
504 | + actions.append(run_action) |
505 | + else: |
506 | + actions.append({"command": "boot_linaro_android_image"}) |
507 | + |
508 | + return actions |
509 | |
510 | === added directory 'dashboard/lava/migrations' |
511 | === added file 'dashboard/lava/migrations/0001_initial.py' |
512 | --- dashboard/lava/migrations/0001_initial.py 1970-01-01 00:00:00 +0000 |
513 | +++ dashboard/lava/migrations/0001_initial.py 2012-09-21 15:30:40 +0000 |
514 | @@ -0,0 +1,65 @@ |
515 | +# encoding: utf-8 |
516 | +import datetime |
517 | +from south.db import db |
518 | +from south.v2 import SchemaMigration |
519 | +from django.db import models |
520 | + |
521 | +class Migration(SchemaMigration): |
522 | + |
523 | + def forwards(self, orm): |
524 | + |
525 | + # Adding model 'LavaTest' |
526 | + db.create_table('lava_lavatest', ( |
527 | + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
528 | + ('build', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['frontend.LoopBuild'])), |
529 | + ('test_name', self.gf('django.db.models.fields.CharField')(max_length=64)), |
530 | + ('test_status', self.gf('django.db.models.fields.CharField')(max_length=20)), |
531 | + )) |
532 | + db.send_create_signal('lava', ['LavaTest']) |
533 | + |
534 | + |
535 | + def backwards(self, orm): |
536 | + |
537 | + # Deleting model 'LavaTest' |
538 | + db.delete_table('lava_lavatest') |
539 | + |
540 | + |
541 | + models = { |
542 | + 'frontend.loop': { |
543 | + 'Meta': {'object_name': 'Loop'}, |
544 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
545 | + 'is_official': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
546 | + 'is_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
547 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}), |
548 | + 'next_loop': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'previous_loop'", 'unique': 'True', 'null': 'True', 'to': "orm['frontend.Loop']"}), |
549 | + 'server': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['jenkinsserver.JenkinsServer']", 'null': 'True'}), |
550 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}) |
551 | + }, |
552 | + 'frontend.loopbuild': { |
553 | + 'Meta': {'ordering': "['-id']", 'unique_together': "(('build_number', 'loop'),)", 'object_name': 'LoopBuild'}, |
554 | + 'build_number': ('django.db.models.fields.IntegerField', [], {'default': 'None'}), |
555 | + 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}), |
556 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
557 | + 'lava_test_status': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), |
558 | + 'loop': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Loop']"}), |
559 | + 'remote_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
560 | + 'result_xml': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
561 | + 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'}) |
562 | + }, |
563 | + 'jenkinsserver.jenkinsserver': { |
564 | + 'Meta': {'object_name': 'JenkinsServer'}, |
565 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
566 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
567 | + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
568 | + 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
569 | + }, |
570 | + 'lava.lavatest': { |
571 | + 'Meta': {'object_name': 'LavaTest'}, |
572 | + 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.LoopBuild']"}), |
573 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
574 | + 'test_name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
575 | + 'test_status': ('django.db.models.fields.CharField', [], {'max_length': '20'}) |
576 | + } |
577 | + } |
578 | + |
579 | + complete_apps = ['lava'] |
580 | |
581 | === added file 'dashboard/lava/migrations/__init__.py' |
582 | === added directory 'dashboard/lava/models' |
583 | === added file 'dashboard/lava/models/__init__.py' |
584 | --- dashboard/lava/models/__init__.py 1970-01-01 00:00:00 +0000 |
585 | +++ dashboard/lava/models/__init__.py 2012-09-21 15:30:40 +0000 |
586 | @@ -0,0 +1,1 @@ |
587 | +from dashboard.lava.models.lava import LavaTest |
588 | |
589 | === added file 'dashboard/lava/models/lava.py' |
590 | --- dashboard/lava/models/lava.py 1970-01-01 00:00:00 +0000 |
591 | +++ dashboard/lava/models/lava.py 2012-09-21 15:30:40 +0000 |
592 | @@ -0,0 +1,37 @@ |
593 | +# Copyright (C) 2012 Linaro |
594 | +# |
595 | +# This file is part of linaro-ci-dashboard. |
596 | +# |
597 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
598 | +# it under the terms of the GNU Affero General Public License as published by |
599 | +# the Free Software Foundation, either version 3 of the License, or |
600 | +# (at your option) any later version. |
601 | +# |
602 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
603 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
604 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
605 | +# GNU Affero General Public License for more details. |
606 | +# |
607 | +# You should have received a copy of the GNU Affero General Public License |
608 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
609 | + |
610 | +from django.db import models |
611 | +from lib.logger import Logger |
612 | +from frontend.models.loop_build import LoopBuild |
613 | + |
614 | + |
615 | +class LavaTest(models.Model): |
616 | + class Meta: |
617 | + app_label = 'lava' |
618 | + |
619 | + def __init__(self, *args, **kwargs): |
620 | + self.log = Logger.getClassLogger(self) |
621 | + super(LavaTest, self).__init__(*args, **kwargs) |
622 | + |
623 | + PENDING = 'pending' |
624 | + RUNNING = 'running' |
625 | + FINISHED = 'finished' |
626 | + |
627 | + build = models.ForeignKey(LoopBuild) |
628 | + test_name = models.CharField(max_length=64) |
629 | + test_status = models.CharField(max_length=20) |
630 | |
631 | === added directory 'dashboard/lava/templates' |
632 | === added file 'dashboard/lava/templates/__init__.py' |
633 | === added file 'dashboard/lava/templates/lava_select.html' |
634 | --- dashboard/lava/templates/lava_select.html 1970-01-01 00:00:00 +0000 |
635 | +++ dashboard/lava/templates/lava_select.html 2012-09-21 15:30:40 +0000 |
636 | @@ -0,0 +1,66 @@ |
637 | + <div id="tests_selection"> |
638 | + <div id="left_div"> |
639 | + <div id="loading_div" class="loading"> |
640 | + <img src="/img/loading.gif" alt="Loading test names..." /> |
641 | + </div> |
642 | + <select id="available" name="name_available_select" multiple="multiple"> |
643 | + {% include "tests_list.html" %} |
644 | + </select> |
645 | + </div> |
646 | + <div id="center_div"> |
647 | + <input type="button" id="add_button" value="→" /> |
648 | + <input type="button" id="remove_button" value="←" /> |
649 | + </div> |
650 | + <div id="right_div"> |
651 | + <select id="chosen" multiple="multiple"> |
652 | + {% comment %} |
653 | + This will be populated moving elements from the previous select |
654 | + element.. |
655 | + {% endcomment %} |
656 | + </select> |
657 | + </div> |
658 | + </div> |
659 | + <div><input type="button" id="id_select_button" value="OK"/></div> |
660 | +<script type="text/javascript"> |
661 | + "use strict"; |
662 | + $.ajax({ |
663 | + url: "{% if request.is_secure %}https{% else %}http{% endif %}://{{ request.get_host }}{% url LavaSelectTestsNames %}", |
664 | + beforeSend: function () { |
665 | + // Do not show the select element if it is empty, just show the |
666 | + // spinner. |
667 | + $('#available').hide(); |
668 | + }, |
669 | + success: function (data) { |
670 | + $(data).prependTo($('#available')); |
671 | + $('#available').show(); |
672 | + }, |
673 | + error: function(data, status, error) { |
674 | + $('#left_div').html('<span class="error"><strong>Oops!</strong> The LAVA server is not responding.</span>') |
675 | + } |
676 | + }); |
677 | + $('#loading_div').show().ajaxStop(function () { |
678 | + // Disable the spinner once AJAX is done. |
679 | + $(this).hide(); |
680 | + }); |
681 | + $('#add_button').click(function () { |
682 | + return !$('#available option:selected').remove().appendTo('#chosen'); |
683 | + }); |
684 | + $('#remove_button').click(function () { |
685 | + return !$('#chosen option:selected').remove().appendTo('#available'); |
686 | + }); |
687 | + $('#id_select_button').click(function () { |
688 | + var values = ""; |
689 | + $("#chosen > option").each(function() { |
690 | + // We store the values as a semi-colon separated list of names. |
691 | + values += $(this).val() + ";"; |
692 | + }); |
693 | + $('#id_lava_tests_0').val(values); |
694 | + // Ellipsize the visual representation if it exceeds a prefixed amount |
695 | + // of chars in length, and append an ellipsis. |
696 | + if (values.length > 30) { |
697 | + values = values.substring(0, 27) + "…"; |
698 | + } |
699 | + $('#lava_select_link').html(values); |
700 | + $('#lava_select_dialog').dialog('close'); |
701 | + }); |
702 | +</script> |
703 | |
704 | === added file 'dashboard/lava/templates/tests_list.html' |
705 | --- dashboard/lava/templates/tests_list.html 1970-01-01 00:00:00 +0000 |
706 | +++ dashboard/lava/templates/tests_list.html 2012-09-21 15:30:40 +0000 |
707 | @@ -0,0 +1,7 @@ |
708 | +{% comment %} |
709 | +This is a basic template used to populate a select element in another view |
710 | +through an asynchronous call. |
711 | +{% endcomment %} |
712 | +{% for test in tests %} |
713 | + <option id="id_{{ test }}" value="{{ test }}">{{ test }}</option> |
714 | +{% endfor %} |
715 | \ No newline at end of file |
716 | |
717 | === added directory 'dashboard/lava/tests' |
718 | === added file 'dashboard/lava/tests/__init__.py' |
719 | --- dashboard/lava/tests/__init__.py 1970-01-01 00:00:00 +0000 |
720 | +++ dashboard/lava/tests/__init__.py 2012-09-21 15:30:40 +0000 |
721 | @@ -0,0 +1,23 @@ |
722 | +# Copyright (C) 2012 Linaro |
723 | +# |
724 | +# This file is part of linaro-ci-dashboard. |
725 | +# |
726 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
727 | +# it under the terms of the GNU Affero General Public License as published by |
728 | +# the Free Software Foundation, either version 3 of the License, or |
729 | +# (at your option) any later version. |
730 | +# |
731 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
732 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
733 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
734 | +# GNU Affero General Public License for more details. |
735 | +# |
736 | +# You should have received a copy of the GNU Affero General Public License |
737 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
738 | + |
739 | +from dashboard.lava.tests.test_lavalib import * |
740 | + |
741 | + |
742 | +__test__ = { |
743 | + 'LavalibTest': LavalibTest, |
744 | + } |
745 | |
746 | === added file 'dashboard/lava/tests/test_lavalib.py' |
747 | --- dashboard/lava/tests/test_lavalib.py 1970-01-01 00:00:00 +0000 |
748 | +++ dashboard/lava/tests/test_lavalib.py 2012-09-21 15:30:40 +0000 |
749 | @@ -0,0 +1,70 @@ |
750 | +# Copyright (C) 2012 Linaro |
751 | +# |
752 | +# This file is part of linaro-ci-dashboard. |
753 | +# |
754 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
755 | +# it under the terms of the GNU Affero General Public License as published by |
756 | +# the Free Software Foundation, either version 3 of the License, or |
757 | +# (at your option) any later version. |
758 | +# |
759 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
760 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
761 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
762 | +# GNU Affero General Public License for more details. |
763 | +# |
764 | +# You should have received a copy of the GNU Affero General Public License |
765 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
766 | + |
767 | +from django.test import TestCase |
768 | +from dashboard.lava.lib.lavalib import ( |
769 | + LavaLib, |
770 | + LavaLibException, |
771 | + LAVA_URL, |
772 | +) |
773 | + |
774 | + |
775 | +class LavalibTest(TestCase): |
776 | + |
777 | + def setUp(self): |
778 | + self.user = 'auser' |
779 | + self.token = 'atoken' |
780 | + self.lava_lib = LavaLib(self.user, self.token) |
781 | + |
782 | + def test_split_test_plan(self): |
783 | + test_plan = 'a;b;c;d;' |
784 | + expected_out = ['a', 'b', 'c', 'd'] |
785 | + self.assertEqual(expected_out, |
786 | + self.lava_lib._split_test_plan(test_plan)) |
787 | + |
788 | + def test_split_test_plan_wrong(self): |
789 | + test_plan = 'a,b,c' |
790 | + self.assertRaises(LavaLibException, |
791 | + self.lava_lib._split_test_plan, test_plan) |
792 | + |
793 | + def test_create_lava_auth(self): |
794 | + expected_out = ('%s:%s@' % (self.user, self.token)) |
795 | + self.assertEqual(expected_out, self.lava_lib._lava_auth()) |
796 | + |
797 | + def test_create_lava_auth_empty(self): |
798 | + lava_lib = LavaLib() |
799 | + expected_out = '' |
800 | + self.assertEqual(expected_out, lava_lib._lava_auth()) |
801 | + |
802 | + def test_create_server_url(self): |
803 | + expected_out = ('https://%s:%s@' + LAVA_URL) % (self.user, self.token) |
804 | + self.assertEqual(expected_out, self.lava_lib._create_server_url()) |
805 | + |
806 | + def test_create_server_url_no_user(self): |
807 | + expected_out = 'https://' + LAVA_URL |
808 | + lava_lib = LavaLib() |
809 | + self.assertEqual(expected_out, lava_lib._create_server_url()) |
810 | + |
811 | + def test_create_server_url_not_secure_no_user(self): |
812 | + expected_out = 'http://' + LAVA_URL |
813 | + lava_lib = LavaLib(secure=False) |
814 | + self.assertEqual(expected_out, lava_lib._create_server_url()) |
815 | + |
816 | + def test_create_server_url_not_secure_with_user(self): |
817 | + expected_out = ('http://%s:%s@' + LAVA_URL) % (self.user, self.token) |
818 | + lava_lib = LavaLib(user=self.user, token=self.token, secure=False) |
819 | + self.assertEqual(expected_out, lava_lib._create_server_url()) |
820 | |
821 | === added file 'dashboard/lava/urls.py' |
822 | --- dashboard/lava/urls.py 1970-01-01 00:00:00 +0000 |
823 | +++ dashboard/lava/urls.py 2012-09-21 15:30:40 +0000 |
824 | @@ -0,0 +1,29 @@ |
825 | +#!/usr/bin/env python |
826 | +# Copyright (C) 2012 Linaro |
827 | +# |
828 | +# This file is part of linaro-ci-dashboard. |
829 | +# |
830 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
831 | +# it under the terms of the GNU Affero General Public License as published by |
832 | +# the Free Software Foundation, either version 3 of the License, or |
833 | +# (at your option) any later version. |
834 | +# |
835 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
836 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
837 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
838 | +# GNU Affero General Public License for more details. |
839 | +# |
840 | +# You should have received a copy of the GNU Affero General Public License |
841 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
842 | + |
843 | +from django.conf.urls.defaults import patterns, url |
844 | +from dashboard.lava.views.lava_select_view import LavaSelectView |
845 | +from dashboard.lava.views.lava_select_test_names import LavaSelectTestNames |
846 | + |
847 | + |
848 | +urlpatterns = patterns('', |
849 | + url(r'^lava/select/$', |
850 | + LavaSelectView.as_view(), name='LavaSelectView'), |
851 | + url(r'^lava/ajax/get-test-names/$', |
852 | + LavaSelectTestNames.as_view(), name='LavaSelectTestsNames'), |
853 | + ) |
854 | \ No newline at end of file |
855 | |
856 | === added directory 'dashboard/lava/views' |
857 | === added file 'dashboard/lava/views/__init__.py' |
858 | === added file 'dashboard/lava/views/lava_select_test_names.py' |
859 | --- dashboard/lava/views/lava_select_test_names.py 1970-01-01 00:00:00 +0000 |
860 | +++ dashboard/lava/views/lava_select_test_names.py 2012-09-21 15:30:40 +0000 |
861 | @@ -0,0 +1,58 @@ |
862 | +# Copyright (C) 2012 Linaro |
863 | +# |
864 | +# This file is part of linaro-ci-dashboard. |
865 | +# |
866 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
867 | +# it under the terms of the GNU Affero General Public License as published by |
868 | +# the Free Software Foundation, either version 3 of the License, or |
869 | +# (at your option) any later version. |
870 | +# |
871 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
872 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
873 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
874 | +# GNU Affero General Public License for more details. |
875 | +# |
876 | +# You should have received a copy of the GNU Affero General Public License |
877 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
878 | + |
879 | +from django.template import RequestContext |
880 | +from django.shortcuts import render_to_response |
881 | +from django.contrib.auth.decorators import login_required |
882 | +from django.views.generic.base import TemplateView |
883 | +from django.utils.decorators import method_decorator |
884 | +from lava.lib.lavalib import LavaLib |
885 | + |
886 | + |
887 | +class LavaSelectTestNames(TemplateView): |
888 | + """ |
889 | + View class used to make asynchronous calls in order to retrieve the list of |
890 | + available test names from a LAVA server through the LAVA library. |
891 | + |
892 | + This is not a real view, it is used only to make async call into the |
893 | + ci-dashboard server. |
894 | + """ |
895 | + template_name = "tests_list.html" |
896 | + |
897 | + @method_decorator(login_required) |
898 | + def dispatch(self, *args, **kwargs): |
899 | + return super(LavaSelectTestNames, self).dispatch(*args, **kwargs) |
900 | + |
901 | + def get_lava_tests(self): |
902 | + """ |
903 | + Retrieves all the test names from the LAVA server. |
904 | + |
905 | + :return A list with all the test names. |
906 | + """ |
907 | + lava_server = LavaLib(secure=False) |
908 | + return lava_server.get_test_names() |
909 | + |
910 | + def render_to_response(self, context, **response_kwargs): |
911 | + if self.request.is_ajax(): |
912 | + data = { |
913 | + "tests": self.get_lava_tests() |
914 | + } |
915 | + return render_to_response(self.template_name, data, |
916 | + context_instance=RequestContext( |
917 | + self.request)) |
918 | + else: |
919 | + raise NotImplementedError |
920 | |
921 | === added file 'dashboard/lava/views/lava_select_view.py' |
922 | --- dashboard/lava/views/lava_select_view.py 1970-01-01 00:00:00 +0000 |
923 | +++ dashboard/lava/views/lava_select_view.py 2012-09-21 15:30:40 +0000 |
924 | @@ -0,0 +1,46 @@ |
925 | +# Copyright (C) 2012 Linaro |
926 | +# |
927 | +# This file is part of linaro-ci-dashboard. |
928 | +# |
929 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
930 | +# it under the terms of the GNU Affero General Public License as published by |
931 | +# the Free Software Foundation, either version 3 of the License, or |
932 | +# (at your option) any later version. |
933 | +# |
934 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
935 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
936 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
937 | +# GNU Affero General Public License for more details. |
938 | +# |
939 | +# You should have received a copy of the GNU Affero General Public License |
940 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
941 | + |
942 | +from django.contrib.auth.decorators import login_required |
943 | +from django.views.generic.base import TemplateView |
944 | +from django.utils.decorators import method_decorator |
945 | +from django.shortcuts import render_to_response |
946 | +from django.template import RequestContext |
947 | + |
948 | + |
949 | +class LavaSelectView(TemplateView): |
950 | + template_name = 'lava_select.html' |
951 | + |
952 | + @method_decorator(login_required) |
953 | + def dispatch(self, *args, **kwargs): |
954 | + return super(LavaSelectView, self).dispatch(*args, **kwargs) |
955 | + |
956 | + def get_context_data(self, **kwargs): |
957 | + context = super(LavaSelectView, self).get_context_data(**kwargs) |
958 | + context['request'] = self.request |
959 | + return context |
960 | + |
961 | + def render_to_response(self, context, **response_kwargs): |
962 | + data = { |
963 | + "request": self.request |
964 | + } |
965 | + if self.request.is_ajax(): |
966 | + return render_to_response(self.template_name, data, |
967 | + context_instance=RequestContext( |
968 | + self.request)) |
969 | + else: |
970 | + raise NotImplementedError |
971 | |
972 | === added directory 'dashboard/lava/widgets' |
973 | === added file 'dashboard/lava/widgets/__init__.py' |
974 | === added file 'dashboard/lava/widgets/lava_test_select_widget.py' |
975 | --- dashboard/lava/widgets/lava_test_select_widget.py 1970-01-01 00:00:00 +0000 |
976 | +++ dashboard/lava/widgets/lava_test_select_widget.py 2012-09-21 15:30:40 +0000 |
977 | @@ -0,0 +1,54 @@ |
978 | +# Copyright (C) 2012 Linaro |
979 | +# |
980 | +# This file is part of linaro-ci-dashboard. |
981 | +# |
982 | +# linaro-ci-dashboard is free software: you can redistribute it and/or modify |
983 | +# it under the terms of the GNU Affero General Public License as published by |
984 | +# the Free Software Foundation, either version 3 of the License, or |
985 | +# (at your option) any later version. |
986 | +# |
987 | +# linaro-ci-dashboard is distributed in the hope that it will be useful, |
988 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
989 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
990 | +# GNU Affero General Public License for more details. |
991 | +# |
992 | +# You should have received a copy of the GNU Affero General Public License |
993 | +# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>. |
994 | + |
995 | +from django.forms.widgets import ( |
996 | + HiddenInput, |
997 | + MultiWidget, |
998 | + Widget |
999 | +) |
1000 | + |
1001 | + |
1002 | +class LavaLinkWidget(Widget): |
1003 | + def render(self, name, value, attrs=None): |
1004 | + from django.utils.safestring import mark_safe |
1005 | + if not value: |
1006 | + value = "Select Tests" |
1007 | + output = ['<span><a href="#" id="lava_select_link">%s</a>' |
1008 | + '</span>' % value] |
1009 | + return mark_safe(u' '.join(output)) |
1010 | + |
1011 | + |
1012 | +class LavaTestSelectWidget(MultiWidget): |
1013 | + class Media: |
1014 | + js = ("/js/lava/lava_test_select.js",) |
1015 | + |
1016 | + def __init__(self, attrs=None): |
1017 | + _widgets = ( |
1018 | + HiddenInput(attrs=attrs), |
1019 | + LavaLinkWidget(attrs=attrs), |
1020 | + ) |
1021 | + super(LavaTestSelectWidget, self).__init__(_widgets, attrs) |
1022 | + |
1023 | + def decompress(self, value): |
1024 | + rtn_list = [None, None] |
1025 | + return rtn_list |
1026 | + |
1027 | + def format_output(self, rendered_widgets): |
1028 | + return u''.join(rendered_widgets) |
1029 | + |
1030 | + def value_from_datadict(self, data, files, name): |
1031 | + return data.get("lava_tests_0", None) |
1032 | |
1033 | === modified file 'dashboard/settings.py' |
1034 | --- dashboard/settings.py 2012-09-21 09:25:19 +0000 |
1035 | +++ dashboard/settings.py 2012-09-21 15:30:40 +0000 |
1036 | @@ -29,6 +29,7 @@ |
1037 | DEPS_INSTALL_PATH = os.path.join(ROOT_PATH, "..", "lib") |
1038 | JS_PATH = os.path.join(ROOT_PATH, "js") |
1039 | CSS_PATH = os.path.join(ROOT_PATH, "css") |
1040 | +IMG_PATH = os.path.join(ROOT_PATH, 'img') |
1041 | |
1042 | ADMINS = ( |
1043 | # ('Your Name', 'your_email@example.com'), |
1044 | @@ -142,6 +143,7 @@ |
1045 | 'django.contrib.messages', |
1046 | 'django.contrib.staticfiles', |
1047 | 'django_openid_auth', |
1048 | + 'lava', |
1049 | 'jenkinsserver', |
1050 | 'frontend', |
1051 | 'frontend.android_build', |
1052 | @@ -224,6 +226,12 @@ |
1053 | JENKINS_HOME = os.path.join(os.getenv('HOME'), ".jenkins_test") |
1054 | JENKINS_LOG_NAME = 'jenkins.log' |
1055 | JENKINS_DB_ID = 1 |
1056 | +<<<<<<< TREE |
1057 | |
1058 | LAVA_ACCEPTING_ADDRESS = '0.0.0.0' |
1059 | LAVA_PORT = '8001' |
1060 | +======= |
1061 | + |
1062 | +# LAVA specific values |
1063 | +LAVA_URL = 'validation.linaro.org/lava-server/RPC2/' |
1064 | +>>>>>>> MERGE-SOURCE |
1065 | |
1066 | === modified file 'dashboard/urls.py' |
1067 | --- dashboard/urls.py 2012-09-13 10:51:03 +0000 |
1068 | +++ dashboard/urls.py 2012-09-21 15:30:40 +0000 |
1069 | @@ -28,9 +28,12 @@ |
1070 | url(r'^admin/', include(admin.site.urls)), |
1071 | url(r'^logout/$', 'django.contrib.auth.views.logout'), |
1072 | url(r'^js/(?P<path>.*)$', 'django.views.static.serve', |
1073 | - {'document_root': settings.JS_PATH}), |
1074 | + {'document_root': settings.JS_PATH}), |
1075 | url(r'^css/(?P<path>.*)$', 'django.views.static.serve', |
1076 | - {'document_root': settings.CSS_PATH}), |
1077 | + {'document_root': settings.CSS_PATH}), |
1078 | + url(r'^img/(?P<path>.*)$', 'django.views.static.serve', |
1079 | + {'document_root': settings.IMG_PATH}), |
1080 | + url(r'^', include('dashboard.lava.urls')), |
1081 | url(r'^', include('dashboard.frontend.urls')), |
1082 | url(r'^', include('dashboard.frontend.android_build.urls')), |
1083 | url(r'^', include('dashboard.frontend.kernel_build.urls')), |
This looks OK for now. /validation. linaro. org/lava- server/ dashboard/ tests/lava/ ) which that have Total results and total failures and stuff like that. So how do we plan to cover this, or do we want to do it at all?
I guess you already have these things in mind, but I'm going to list additional things to be extended here:
1. Extend the test status to match all available status results as in LAVA and move the status choices list to i.e. settings so it can be used in both loop_build and lavatest classes.
2. Add the unit tests for lava library.
3. I noticed in LAVA that each test contain multiple entries (i don't know what entity is this you can see in https:/