Merge lp:~doanac/lava-scheduler/configurable-dispatchers-v2 into lp:lava-scheduler

Proposed by Andy Doan
Status: Rejected
Rejected by: Michael Hudson-Doyle
Proposed branch: lp:~doanac/lava-scheduler/configurable-dispatchers-v2
Merge into: lp:lava-scheduler
Diff against target: 410 lines (+331/-11)
6 files modified
lava_scheduler_app/migrations/0023_auto__add_field_devicetype_dispatcher.py (+149/-0)
lava_scheduler_app/migrations/0024_auto__chg_field_devicetype_dispatcher.py (+147/-0)
lava_scheduler_app/models.py (+8/-0)
lava_scheduler_daemon/board.py (+5/-2)
lava_scheduler_daemon/dbjobsource.py (+2/-1)
lava_scheduler_daemon/service.py (+20/-8)
To merge this branch: bzr merge lp:~doanac/lava-scheduler/configurable-dispatchers-v2
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle (community) Approve
Review via email: mp+112433@code.launchpad.net

Description of the change

This is the second version of configurable dispatchers. This is a little more sensible than the first one.

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

190 + args = self.dispatcher.split() + [
191 + '--oob-fd', str(OOB_FD), self._json_file ]

That's a bit unsafe. If dispatcher field is not long enough to hold arbitrary arguments. Why don't you just return [self.dispatcher, '--oob-fd', ...] ?

Revision history for this message
Andy Doan (doanac) wrote :

> 190 + args = self.dispatcher.split() + [
> 191 + '--oob-fd', str(OOB_FD), self._json_file ]
>
> That's a bit unsafe. If dispatcher field is not long enough to hold arbitrary
> arguments. Why don't you just return [self.dispatcher, '--oob-fd', ...] ?

self.dispatcher can be something like "lava celery-dispatch" which is two args not one which was causing the spawnProcess call to fail.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :
Download full text (3.7 KiB)

Andy Doan <email address hidden> writes:

> Andy Doan has proposed merging lp:~doanac/lava-scheduler/configurable-dispatchers-v2 into lp:lava-scheduler.
>
> Requested reviews:
> Linaro Validation Team (linaro-validation)
>
> For more details, see:
> https://code.launchpad.net/~doanac/lava-scheduler/configurable-dispatchers-v2/+merge/112433
>
> This is the second version of configurable dispatchers. This is a
> little more sensible than the first one.
>

> === modified file 'lava_scheduler_app/models.py'
> --- lava_scheduler_app/models.py 2012-06-22 05:06:06 +0000
> +++ lava_scheduler_app/models.py 2012-06-27 21:07:19 +0000
> @@ -55,6 +55,11 @@
> health_check_job = models.TextField(
> null=True, blank=True, default=None, validators=[validate_job_json])
>
> + dispatcher = models.CharField(
> + max_length = 64, null=True, blank=True,
> + help_text = "Allows you to override the default lava-dispatcher for " \
> + "devices of this type")

I guess it's not really going to matter here, but having to specify
max_length is annoying and pointless with postgres. Can you set it to
something silly like 1024?

> @models.permalink
> def get_absolute_url(self):
> return ("lava.scheduler.device_type.detail", [self.pk])
> @@ -128,6 +133,9 @@
> def get_device_health_url(self):
> return ("lava.scheduler.labhealth.detail", [self.pk])
>
> + def get_dispatcher(self):
> + return self.device_type.dispatcher
> +
> def recent_jobs(self):
> return TestJob.objects.select_related(
> "actual_device",

> === modified file 'lava_scheduler_daemon/service.py'
> --- lava_scheduler_daemon/service.py 2012-03-29 04:56:44 +0000
> +++ lava_scheduler_daemon/service.py 2012-06-27 21:07:19 +0000
> @@ -24,20 +24,32 @@
> return self.source.getBoardList().addCallback(
> self._cbUpdateBoards).addErrback(catchall_errback(self.logger))
>
> - def _cbUpdateBoards(self, board_names):
> - if set(board_names) == set(self.boards):
> - return
> - self.logger.info("New board list %s", board_names)
> + def _cbUpdateBoards(self, board_cfgs):
> + '''board_cfgs is an array of dicts {hostname=name, dispatcher=...}
> + '''
> new_boards = {}
> - for board_name in board_names:
> - if board_name in self.boards:
> - new_boards[board_name] = self.boards.pop(board_name)
> + for board_cfg in board_cfgs:
> + board_name = board_cfg['hostname']
> +
> + dispatcher = board_cfg['dispatcher']
> + if dispatcher is None or len(dispatcher) == 0:
> + dispatcher = self.dispatcher
> +
> + if board_cfg['hostname'] in self.boards:
> + board = self.boards.pop(board_name)
> + if board.dispatcher != dispatcher:
> + board.dispatcher = dispatcher
> + self.logger.info("dispatcher changed for %s to '%s'" % \
> + (board_name, dispatcher))
> + new_boards[board_name] = board
> else:
> + self.logger.info("Adding ...

Read more...

Revision history for this message
Michael Hudson-Doyle (mwhudson) :
review: Approve
193. By Andy Doan

make dispatcher field larger

as per review comment a 64byte field like this in postgres is pointless

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I guess this branch is obsolete now?

Unmerged revisions

193. By Andy Doan

make dispatcher field larger

as per review comment a 64byte field like this in postgres is pointless

192. By Andy Doan

add support for custom dispatchers

This provides a way to configure different dispatchers for certain
device types. Notables:

models.py now has custom field that allows you to override the default
"lava-dispatch" command

dbjobsource.py now returns an array of dictionaries that includes the
board name as well as the dispatcher configured for it.

service.py logic was updated to handle the new format of data from
dbjobsource.py as well as adding some minor logging information

board.py was modified to handle the new dispatcher command which might
be more than one execve arg, ie "lava celery dispatch".

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'lava_scheduler_app/migrations/0023_auto__add_field_devicetype_dispatcher.py'
2--- lava_scheduler_app/migrations/0023_auto__add_field_devicetype_dispatcher.py 1970-01-01 00:00:00 +0000
3+++ lava_scheduler_app/migrations/0023_auto__add_field_devicetype_dispatcher.py 2012-07-01 19:09:19 +0000
4@@ -0,0 +1,149 @@
5+# -*- coding: utf-8 -*-
6+import datetime
7+from south.db import db
8+from south.v2 import SchemaMigration
9+from django.db import models
10+
11+
12+class Migration(SchemaMigration):
13+
14+ def forwards(self, orm):
15+ # Adding field 'DeviceType.dispatcher'
16+ db.add_column('lava_scheduler_app_devicetype', 'dispatcher',
17+ self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True),
18+ keep_default=False)
19+
20+
21+ def backwards(self, orm):
22+ # Deleting field 'DeviceType.dispatcher'
23+ db.delete_column('lava_scheduler_app_devicetype', 'dispatcher')
24+
25+
26+ models = {
27+ 'auth.group': {
28+ 'Meta': {'object_name': 'Group'},
29+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
30+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
31+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
32+ },
33+ 'auth.permission': {
34+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
35+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
36+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
37+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
38+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
39+ },
40+ 'auth.user': {
41+ 'Meta': {'object_name': 'User'},
42+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
43+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
44+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
45+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
46+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
47+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
48+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
49+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
50+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
51+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
52+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
53+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
54+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
55+ },
56+ 'contenttypes.contenttype': {
57+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
58+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
59+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
60+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
61+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
62+ },
63+ 'dashboard_app.bundle': {
64+ 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'},
65+ '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}),
66+ '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}),
67+ 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}),
68+ 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
69+ 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}),
70+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
71+ 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
72+ 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}),
73+ 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
74+ },
75+ 'dashboard_app.bundlestream': {
76+ 'Meta': {'object_name': 'BundleStream'},
77+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
78+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
79+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
80+ 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
81+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
82+ 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
83+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
84+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
85+ },
86+ 'lava_scheduler_app.device': {
87+ 'Meta': {'object_name': 'Device'},
88+ 'current_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}),
89+ 'device_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.DeviceType']"}),
90+ 'health_status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
91+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'primary_key': 'True'}),
92+ 'last_health_report_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}),
93+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
94+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'})
95+ },
96+ 'lava_scheduler_app.devicestatetransition': {
97+ 'Meta': {'object_name': 'DeviceStateTransition'},
98+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
99+ 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
100+ 'device': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['lava_scheduler_app.Device']"}),
101+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102+ 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.TestJob']", 'null': 'True', 'blank': 'True'}),
103+ 'message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
104+ 'new_state': ('django.db.models.fields.IntegerField', [], {}),
105+ 'old_state': ('django.db.models.fields.IntegerField', [], {})
106+ },
107+ 'lava_scheduler_app.devicetype': {
108+ 'Meta': {'object_name': 'DeviceType'},
109+ 'dispatcher': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
110+ 'health_check_job': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
111+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'primary_key': 'True'})
112+ },
113+ 'lava_scheduler_app.tag': {
114+ 'Meta': {'object_name': 'Tag'},
115+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
116+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
117+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
118+ },
119+ 'lava_scheduler_app.testjob': {
120+ 'Meta': {'object_name': 'TestJob'},
121+ '_results_bundle': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['dashboard_app.Bundle']", 'unique': 'True', 'null': 'True', 'db_column': "'results_bundle_id'", 'blank': 'True'}),
122+ '_results_link': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '400', 'null': 'True', 'db_column': "'results_link'", 'blank': 'True'}),
123+ 'actual_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}),
124+ 'definition': ('django.db.models.fields.TextField', [], {}),
125+ 'description': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}),
126+ 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
127+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
128+ 'health_check': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
129+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
130+ 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
131+ 'log_file': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}),
132+ 'requested_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}),
133+ 'requested_device_type': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.DeviceType']"}),
134+ 'start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
135+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
136+ 'submit_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
137+ 'submit_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['linaro_django_xmlrpc.AuthToken']", 'null': 'True', 'blank': 'True'}),
138+ 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}),
139+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}),
140+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
141+ },
142+ 'linaro_django_xmlrpc.authtoken': {
143+ 'Meta': {'object_name': 'AuthToken'},
144+ 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
145+ 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
146+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
147+ 'last_used_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
148+ 'secret': ('django.db.models.fields.CharField', [], {'default': "'um3n8ni3o7j3abma5m532j8xy72jis0f9p1mjgo4ii7m1hgo0qzmkpmsnkqwqjb04ht96tqdrqz1c610dycfpj6k7m25ajlk649f2xr975qm4w7jo0t9m6b805skxjqr'", 'unique': 'True', 'max_length': '128'}),
149+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': "orm['auth.User']"})
150+ }
151+ }
152+
153+ complete_apps = ['lava_scheduler_app']
154\ No newline at end of file
155
156=== added file 'lava_scheduler_app/migrations/0024_auto__chg_field_devicetype_dispatcher.py'
157--- lava_scheduler_app/migrations/0024_auto__chg_field_devicetype_dispatcher.py 1970-01-01 00:00:00 +0000
158+++ lava_scheduler_app/migrations/0024_auto__chg_field_devicetype_dispatcher.py 2012-07-01 19:09:19 +0000
159@@ -0,0 +1,147 @@
160+# -*- coding: utf-8 -*-
161+import datetime
162+from south.db import db
163+from south.v2 import SchemaMigration
164+from django.db import models
165+
166+
167+class Migration(SchemaMigration):
168+
169+ def forwards(self, orm):
170+
171+ # Changing field 'DeviceType.dispatcher'
172+ db.alter_column('lava_scheduler_app_devicetype', 'dispatcher', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
173+
174+ def backwards(self, orm):
175+
176+ # Changing field 'DeviceType.dispatcher'
177+ db.alter_column('lava_scheduler_app_devicetype', 'dispatcher', self.gf('django.db.models.fields.CharField')(max_length=64, null=True))
178+
179+ models = {
180+ 'auth.group': {
181+ 'Meta': {'object_name': 'Group'},
182+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
184+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
185+ },
186+ 'auth.permission': {
187+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
188+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
189+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
190+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
191+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
192+ },
193+ 'auth.user': {
194+ 'Meta': {'object_name': 'User'},
195+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
196+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
197+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
198+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
199+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
200+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
201+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
202+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
203+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
204+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
205+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
206+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
207+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
208+ },
209+ 'contenttypes.contenttype': {
210+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
211+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
212+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
213+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
214+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
215+ },
216+ 'dashboard_app.bundle': {
217+ 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'},
218+ '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}),
219+ '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}),
220+ 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}),
221+ 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
222+ 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}),
223+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
224+ 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
225+ 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}),
226+ 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
227+ },
228+ 'dashboard_app.bundlestream': {
229+ 'Meta': {'object_name': 'BundleStream'},
230+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
231+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
232+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
233+ 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
234+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
235+ 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
236+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
237+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
238+ },
239+ 'lava_scheduler_app.device': {
240+ 'Meta': {'object_name': 'Device'},
241+ 'current_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}),
242+ 'device_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.DeviceType']"}),
243+ 'health_status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
244+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'primary_key': 'True'}),
245+ 'last_health_report_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}),
246+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
247+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'})
248+ },
249+ 'lava_scheduler_app.devicestatetransition': {
250+ 'Meta': {'object_name': 'DeviceStateTransition'},
251+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
252+ 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
253+ 'device': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['lava_scheduler_app.Device']"}),
254+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
255+ 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.TestJob']", 'null': 'True', 'blank': 'True'}),
256+ 'message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
257+ 'new_state': ('django.db.models.fields.IntegerField', [], {}),
258+ 'old_state': ('django.db.models.fields.IntegerField', [], {})
259+ },
260+ 'lava_scheduler_app.devicetype': {
261+ 'Meta': {'object_name': 'DeviceType'},
262+ 'dispatcher': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
263+ 'health_check_job': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
264+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'primary_key': 'True'})
265+ },
266+ 'lava_scheduler_app.tag': {
267+ 'Meta': {'object_name': 'Tag'},
268+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
269+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
270+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
271+ },
272+ 'lava_scheduler_app.testjob': {
273+ 'Meta': {'object_name': 'TestJob'},
274+ '_results_bundle': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['dashboard_app.Bundle']", 'unique': 'True', 'null': 'True', 'db_column': "'results_bundle_id'", 'blank': 'True'}),
275+ '_results_link': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '400', 'null': 'True', 'db_column': "'results_link'", 'blank': 'True'}),
276+ 'actual_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}),
277+ 'definition': ('django.db.models.fields.TextField', [], {}),
278+ 'description': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}),
279+ 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
280+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
281+ 'health_check': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
282+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
283+ 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
284+ 'log_file': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}),
285+ 'requested_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}),
286+ 'requested_device_type': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.DeviceType']"}),
287+ 'start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
288+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
289+ 'submit_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
290+ 'submit_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['linaro_django_xmlrpc.AuthToken']", 'null': 'True', 'blank': 'True'}),
291+ 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}),
292+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}),
293+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
294+ },
295+ 'linaro_django_xmlrpc.authtoken': {
296+ 'Meta': {'object_name': 'AuthToken'},
297+ 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
298+ 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
299+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
300+ 'last_used_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
301+ 'secret': ('django.db.models.fields.CharField', [], {'default': "'yedas15tcil6fxzrmqrmpnd3qxnxt8wj3edjw5g8s5j6i00sey9rswrt38y53p9msb0i9k6qh7zzhmhprhnqfuw8qo1b76kqd3xhw58dnxqf02tw0jekosdj9fy0l64s'", 'unique': 'True', 'max_length': '128'}),
302+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': "orm['auth.User']"})
303+ }
304+ }
305+
306+ complete_apps = ['lava_scheduler_app']
307\ No newline at end of file
308
309=== modified file 'lava_scheduler_app/models.py'
310--- lava_scheduler_app/models.py 2012-06-22 05:06:06 +0000
311+++ lava_scheduler_app/models.py 2012-07-01 19:09:19 +0000
312@@ -55,6 +55,11 @@
313 health_check_job = models.TextField(
314 null=True, blank=True, default=None, validators=[validate_job_json])
315
316+ dispatcher = models.CharField(
317+ max_length = 1024, null=True, blank=True,
318+ help_text = "Allows you to override the default lava-dispatcher for " \
319+ "devices of this type")
320+
321 @models.permalink
322 def get_absolute_url(self):
323 return ("lava.scheduler.device_type.detail", [self.pk])
324@@ -128,6 +133,9 @@
325 def get_device_health_url(self):
326 return ("lava.scheduler.labhealth.detail", [self.pk])
327
328+ def get_dispatcher(self):
329+ return self.device_type.dispatcher
330+
331 def recent_jobs(self):
332 return TestJob.objects.select_related(
333 "actual_device",
334
335=== modified file 'lava_scheduler_daemon/board.py'
336--- lava_scheduler_daemon/board.py 2012-03-29 04:56:44 +0000
337+++ lava_scheduler_daemon/board.py 2012-07-01 19:09:19 +0000
338@@ -137,9 +137,12 @@
339 self._protocol = DispatcherProcessProtocol(
340 d, job_log_file, self)
341 self.job_log_file = job_log_file
342+
343+ args = self.dispatcher.split() + [
344+ '--oob-fd', str(OOB_FD), self._json_file ]
345+
346 self.reactor.spawnProcess(
347- self._protocol, self.dispatcher, args=[
348- self.dispatcher, self._json_file, '--oob-fd', str(OOB_FD)],
349+ self._protocol, args[0], args=args,
350 childFDs={0:0, 1:'r', 2:'r', OOB_FD:'r'}, env=None)
351 self._checkCancel_call.start(10)
352 timeout = max(
353
354=== modified file 'lava_scheduler_daemon/dbjobsource.py'
355--- lava_scheduler_daemon/dbjobsource.py 2012-06-22 05:07:18 +0000
356+++ lava_scheduler_daemon/dbjobsource.py 2012-07-01 19:09:19 +0000
357@@ -83,7 +83,8 @@
358 return self.deferToThread(wrapper, *args, **kw)
359
360 def getBoardList_impl(self):
361- return [d.hostname for d in Device.objects.all()]
362+ return [ {'hostname': d.hostname, 'dispatcher': d.get_dispatcher()}
363+ for d in Device.objects.all()]
364
365 def getBoardList(self):
366 return self.deferForDB(self.getBoardList_impl)
367
368=== modified file 'lava_scheduler_daemon/service.py'
369--- lava_scheduler_daemon/service.py 2012-03-29 04:56:44 +0000
370+++ lava_scheduler_daemon/service.py 2012-07-01 19:09:19 +0000
371@@ -24,20 +24,32 @@
372 return self.source.getBoardList().addCallback(
373 self._cbUpdateBoards).addErrback(catchall_errback(self.logger))
374
375- def _cbUpdateBoards(self, board_names):
376- if set(board_names) == set(self.boards):
377- return
378- self.logger.info("New board list %s", board_names)
379+ def _cbUpdateBoards(self, board_cfgs):
380+ '''board_cfgs is an array of dicts {hostname=name, dispatcher=...}
381+ '''
382 new_boards = {}
383- for board_name in board_names:
384- if board_name in self.boards:
385- new_boards[board_name] = self.boards.pop(board_name)
386+ for board_cfg in board_cfgs:
387+ board_name = board_cfg['hostname']
388+
389+ dispatcher = board_cfg['dispatcher']
390+ if dispatcher is None or len(dispatcher) == 0:
391+ dispatcher = self.dispatcher
392+
393+ if board_cfg['hostname'] in self.boards:
394+ board = self.boards.pop(board_name)
395+ if board.dispatcher != dispatcher:
396+ board.dispatcher = dispatcher
397+ self.logger.info("dispatcher changed for %s to '%s'" % \
398+ (board_name, dispatcher))
399+ new_boards[board_name] = board
400 else:
401+ self.logger.info("Adding board: %s" % board_name)
402 new_boards[board_name] = Board(
403- self.source, board_name, self.dispatcher, self.reactor,
404+ self.source, board_name, dispatcher, self.reactor,
405 self.daemon_options)
406 new_boards[board_name].start()
407 for board in self.boards.values():
408+ self.logger.info("Removing board: %s" % board.board_name)
409 board.stop()
410 self.boards = new_boards
411

Subscribers

People subscribed via source and target branches