Merge lp:~milo/linaro-ci-dashboard/lava-data-model into lp:linaro-ci-dashboard

Proposed by Milo Casagrande
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
Reviewer Review Type Date Requested Status
Stevan Radaković Pending
Review via email: mp+125162@code.launchpad.net

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.

To post a comment you must log in.
53. By Milo Casagrande

Merged from trunk.

Revision history for this message
Stevan Radaković (stevanr) 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.
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://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?

Revision history for this message
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://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 think we might not cover that at the moment. That is the total
number of runs for that test. Indipendently of the
board/build/whatever. It is just a big total of how many times that
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'
215Binary 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="&#8594;" />
648+ <input type="button" id="remove_button" value="&#8592;" />
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) + "&#8230;";
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')),

Subscribers

People subscribed via source and target branches