Merge lp:~doanac/lava-scheduler/celery-support into lp:lava-scheduler
- celery-support
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michael Hudson-Doyle |
Approved revision: | 197 |
Merged at revision: | 196 |
Proposed branch: | lp:~doanac/lava-scheduler/celery-support |
Merge into: | lp:lava-scheduler |
Diff against target: |
378 lines (+206/-21) 6 files modified
lava_scheduler_app/management/commands/schedulermonitor.py (+6/-1) lava_scheduler_app/migrations/0023_auto__add_field_devicetype_use_celery.py (+149/-0) lava_scheduler_app/models.py (+7/-0) lava_scheduler_daemon/board.py (+26/-11) lava_scheduler_daemon/dbjobsource.py (+2/-1) lava_scheduler_daemon/service.py (+16/-8) |
To merge this branch: | bzr merge lp:~doanac/lava-scheduler/celery-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Hudson-Doyle (community) | Approve | ||
Review via email: mp+114092@code.launchpad.net |
Commit message
Description of the change
Changes required for the scheduler to properly run jobs via celery remotely
Andy Doan (doanac) wrote : | # |
On 07/10/2012 12:17 AM, Michael Hudson-Doyle wrote:
> Review: Approve
>
> Yay, looks good.
>
>> + log_to_stdout = os.getenv(
>
> That is quite the hack :-)
>
>> - if self.log_
>> + if self.log_file != sys.stdout and \
>> + self.log_
>
> I would rather switch to having DispatcherProce
>
> I would really like to get the code I sent you to help debug this landed -- can you file a bug about that?
>
I will try and fix both things before merging this.
Preview Diff
1 | === modified file 'lava_scheduler_app/management/commands/schedulermonitor.py' |
2 | --- lava_scheduler_app/management/commands/schedulermonitor.py 2012-03-27 05:41:09 +0000 |
3 | +++ lava_scheduler_app/management/commands/schedulermonitor.py 2012-07-10 04:16:21 +0000 |
4 | @@ -16,6 +16,7 @@ |
5 | # You should have received a copy of the GNU Affero General Public License |
6 | # along with LAVA Scheduler. If not, see <http://www.gnu.org/licenses/>. |
7 | |
8 | +import os |
9 | import simplejson |
10 | |
11 | |
12 | @@ -35,9 +36,13 @@ |
13 | daemon_options = self._configure(options) |
14 | source = DatabaseJobSource() |
15 | dispatcher, board_name, json_file = args |
16 | + |
17 | + log_to_stdout = os.getenv("CELERY_CONFIG_MODULE", False) |
18 | + |
19 | job = Job( |
20 | simplejson.load(open(json_file)), dispatcher, |
21 | - source, board_name, reactor, daemon_options=daemon_options) |
22 | + source, board_name, reactor, daemon_options=daemon_options, |
23 | + log_to_stdout=log_to_stdout) |
24 | def run(): |
25 | job.run().addCallback(lambda result: reactor.stop()) |
26 | reactor.callWhenRunning(run) |
27 | |
28 | === added file 'lava_scheduler_app/migrations/0023_auto__add_field_devicetype_use_celery.py' |
29 | --- lava_scheduler_app/migrations/0023_auto__add_field_devicetype_use_celery.py 1970-01-01 00:00:00 +0000 |
30 | +++ lava_scheduler_app/migrations/0023_auto__add_field_devicetype_use_celery.py 2012-07-10 04:16:21 +0000 |
31 | @@ -0,0 +1,149 @@ |
32 | +# -*- coding: utf-8 -*- |
33 | +import datetime |
34 | +from south.db import db |
35 | +from south.v2 import SchemaMigration |
36 | +from django.db import models |
37 | + |
38 | + |
39 | +class Migration(SchemaMigration): |
40 | + |
41 | + def forwards(self, orm): |
42 | + # Adding field 'DeviceType.use_celery' |
43 | + db.add_column('lava_scheduler_app_devicetype', 'use_celery', |
44 | + self.gf('django.db.models.fields.BooleanField')(default=False), |
45 | + keep_default=False) |
46 | + |
47 | + |
48 | + def backwards(self, orm): |
49 | + # Deleting field 'DeviceType.use_celery' |
50 | + db.delete_column('lava_scheduler_app_devicetype', 'use_celery') |
51 | + |
52 | + |
53 | + models = { |
54 | + 'auth.group': { |
55 | + 'Meta': {'object_name': 'Group'}, |
56 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
57 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
58 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
59 | + }, |
60 | + 'auth.permission': { |
61 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
62 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
63 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
64 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
65 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
66 | + }, |
67 | + 'auth.user': { |
68 | + 'Meta': {'object_name': 'User'}, |
69 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
70 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
71 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
72 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
73 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
74 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
75 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
76 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
77 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
78 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
79 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
80 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
81 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
82 | + }, |
83 | + 'contenttypes.contenttype': { |
84 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
85 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
86 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
87 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
88 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
89 | + }, |
90 | + 'dashboard_app.bundle': { |
91 | + 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'}, |
92 | + '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}), |
93 | + '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}), |
94 | + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), |
95 | + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
96 | + 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}), |
97 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
98 | + 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
99 | + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}), |
100 | + 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
101 | + }, |
102 | + 'dashboard_app.bundlestream': { |
103 | + 'Meta': {'object_name': 'BundleStream'}, |
104 | + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), |
105 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
106 | + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
107 | + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
108 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), |
109 | + 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), |
110 | + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), |
111 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
112 | + }, |
113 | + 'lava_scheduler_app.device': { |
114 | + 'Meta': {'object_name': 'Device'}, |
115 | + 'current_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}), |
116 | + 'device_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.DeviceType']"}), |
117 | + 'health_status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
118 | + 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'primary_key': 'True'}), |
119 | + 'last_health_report_job': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['lava_scheduler_app.TestJob']"}), |
120 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}), |
121 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}) |
122 | + }, |
123 | + 'lava_scheduler_app.devicestatetransition': { |
124 | + 'Meta': {'object_name': 'DeviceStateTransition'}, |
125 | + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
126 | + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
127 | + 'device': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['lava_scheduler_app.Device']"}), |
128 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
129 | + 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['lava_scheduler_app.TestJob']", 'null': 'True', 'blank': 'True'}), |
130 | + 'message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
131 | + 'new_state': ('django.db.models.fields.IntegerField', [], {}), |
132 | + 'old_state': ('django.db.models.fields.IntegerField', [], {}) |
133 | + }, |
134 | + 'lava_scheduler_app.devicetype': { |
135 | + 'Meta': {'object_name': 'DeviceType'}, |
136 | + 'health_check_job': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), |
137 | + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'primary_key': 'True'}), |
138 | + 'use_celery': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) |
139 | + }, |
140 | + 'lava_scheduler_app.tag': { |
141 | + 'Meta': {'object_name': 'Tag'}, |
142 | + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
143 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
144 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}) |
145 | + }, |
146 | + 'lava_scheduler_app.testjob': { |
147 | + 'Meta': {'object_name': 'TestJob'}, |
148 | + '_results_bundle': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['dashboard_app.Bundle']", 'unique': 'True', 'null': 'True', 'db_column': "'results_bundle_id'", 'blank': 'True'}), |
149 | + '_results_link': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '400', 'null': 'True', 'db_column': "'results_link'", 'blank': 'True'}), |
150 | + 'actual_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}), |
151 | + 'definition': ('django.db.models.fields.TextField', [], {}), |
152 | + 'description': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '200', 'null': 'True', 'blank': 'True'}), |
153 | + 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
154 | + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), |
155 | + 'health_check': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
156 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
157 | + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
158 | + 'log_file': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}), |
159 | + 'requested_device': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.Device']"}), |
160 | + 'requested_device_type': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'+'", 'null': 'True', 'blank': 'True', 'to': "orm['lava_scheduler_app.DeviceType']"}), |
161 | + 'start_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
162 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
163 | + 'submit_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
164 | + 'submit_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['linaro_django_xmlrpc.AuthToken']", 'null': 'True', 'blank': 'True'}), |
165 | + 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), |
166 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['lava_scheduler_app.Tag']", 'symmetrical': 'False', 'blank': 'True'}), |
167 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
168 | + }, |
169 | + 'linaro_django_xmlrpc.authtoken': { |
170 | + 'Meta': {'object_name': 'AuthToken'}, |
171 | + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
172 | + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), |
173 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
174 | + 'last_used_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
175 | + 'secret': ('django.db.models.fields.CharField', [], {'default': "'fxxmj9mnu0ox1a771w9c53gxjh07mnlc5bj5gthygd6jraelz0wpac744ls1ucrz0dyb5s9sbrojbk00lcw7tx4iczlu3le1qi63aejomaiuc4bnr7e3uhesli16em4r'", 'unique': 'True', 'max_length': '128'}), |
176 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': "orm['auth.User']"}) |
177 | + } |
178 | + } |
179 | + |
180 | + complete_apps = ['lava_scheduler_app'] |
181 | \ No newline at end of file |
182 | |
183 | === modified file 'lava_scheduler_app/models.py' |
184 | --- lava_scheduler_app/models.py 2012-06-29 03:26:14 +0000 |
185 | +++ lava_scheduler_app/models.py 2012-07-10 04:16:21 +0000 |
186 | @@ -55,6 +55,10 @@ |
187 | health_check_job = models.TextField( |
188 | null=True, blank=True, default=None, validators=[validate_job_json]) |
189 | |
190 | + use_celery = models.BooleanField(default=False, |
191 | + help_text=("Denotes the job should be run via the celery "\ |
192 | + "schedulermonitor rather than the local one")) |
193 | + |
194 | @models.permalink |
195 | def get_absolute_url(self): |
196 | return ("lava.scheduler.device_type.detail", [self.pk]) |
197 | @@ -128,6 +132,9 @@ |
198 | def get_device_health_url(self): |
199 | return ("lava.scheduler.labhealth.detail", [self.pk]) |
200 | |
201 | + def use_celery(self): |
202 | + return self.device_type.use_celery |
203 | + |
204 | def recent_jobs(self): |
205 | return TestJob.objects.select_related( |
206 | "actual_device", |
207 | |
208 | === modified file 'lava_scheduler_daemon/board.py' |
209 | --- lava_scheduler_daemon/board.py 2012-03-29 04:56:44 +0000 |
210 | +++ lava_scheduler_daemon/board.py 2012-07-10 04:16:21 +0000 |
211 | @@ -1,6 +1,7 @@ |
212 | import json |
213 | import os |
214 | import signal |
215 | +import sys |
216 | import tempfile |
217 | import logging |
218 | |
219 | @@ -56,7 +57,8 @@ |
220 | if childFD == OOB_FD: |
221 | self.oob_data.dataReceived(data) |
222 | self.log_file.write(data) |
223 | - if self.log_file.tell() > self.job.daemon_options['LOG_FILE_SIZE_LIMIT']: |
224 | + if self.log_file != sys.stdout and \ |
225 | + self.log_file.tell() > self.job.daemon_options['LOG_FILE_SIZE_LIMIT']: |
226 | if not self.job._killing: |
227 | self.job.cancel("exceeded log size limit") |
228 | self.log_file.flush() |
229 | @@ -70,7 +72,7 @@ |
230 | |
231 | |
232 | def __init__(self, job_data, dispatcher, source, board_name, reactor, |
233 | - daemon_options): |
234 | + daemon_options, log_to_stdout=False): |
235 | self.job_data = job_data |
236 | self.dispatcher = dispatcher |
237 | self.source = source |
238 | @@ -85,6 +87,7 @@ |
239 | self._time_limit_call = None |
240 | self._killing = False |
241 | self.job_log_file = None |
242 | + self._log_to_stdout = log_to_stdout |
243 | |
244 | def _checkCancel(self): |
245 | if self._killing: |
246 | @@ -124,6 +127,9 @@ |
247 | self.cancel("killing job for exceeding timeout") |
248 | |
249 | def run(self): |
250 | + if self._log_to_stdout: |
251 | + return self._run(sys.stdout) |
252 | + |
253 | d = self.source.getLogFileForJobOnBoard(self.board_name) |
254 | return d.addCallback(self._run).addErrback( |
255 | catchall_errback(self.logger)) |
256 | @@ -174,7 +180,7 @@ |
257 | |
258 | |
259 | def __init__(self, job_data, dispatcher, source, board_name, reactor, |
260 | - daemon_options): |
261 | + daemon_options, use_celery=False): |
262 | self.logger = logging.getLogger(__name__ + '.MonitorJob') |
263 | self.job_data = job_data |
264 | self.dispatcher = dispatcher |
265 | @@ -182,6 +188,7 @@ |
266 | self.board_name = board_name |
267 | self.reactor = reactor |
268 | self.daemon_options = daemon_options |
269 | + self.use_celery = use_celery |
270 | self._json_file = None |
271 | |
272 | def run(self): |
273 | @@ -190,12 +197,18 @@ |
274 | fd, self._json_file = tempfile.mkstemp() |
275 | with os.fdopen(fd, 'wb') as f: |
276 | json.dump(json_data, f) |
277 | - args = [ |
278 | - 'setsid', 'lava-server', 'manage', 'schedulermonitor', |
279 | - self.dispatcher, str(self.board_name), self._json_file, |
280 | - '-l', self.daemon_options['LOG_LEVEL']] |
281 | - if self.daemon_options['LOG_FILE_PATH']: |
282 | - args.extend(['-f', self.daemon_options['LOG_FILE_PATH']]) |
283 | + |
284 | + if self.use_celery: |
285 | + args = [ |
286 | + 'setsid', 'lava', 'celery-schedulermonitor', |
287 | + self.dispatcher, str(self.board_name), self._json_file] |
288 | + else: |
289 | + args = [ |
290 | + 'setsid', 'lava-server', 'manage', 'schedulermonitor', |
291 | + self.dispatcher, str(self.board_name), self._json_file, |
292 | + '-l', self.daemon_options['LOG_LEVEL']] |
293 | + if self.daemon_options['LOG_FILE_PATH']: |
294 | + args.extend(['-f', self.daemon_options['LOG_FILE_PATH']]) |
295 | self.logger.info('executing "%s"', ' '.join(args)) |
296 | self.reactor.spawnProcess( |
297 | SimplePP(d), 'setsid', childFDs={0:0, 1:1, 2:2}, |
298 | @@ -259,7 +272,8 @@ |
299 | |
300 | job_cls = MonitorJob |
301 | |
302 | - def __init__(self, source, board_name, dispatcher, reactor, daemon_options, job_cls=None): |
303 | + def __init__(self, source, board_name, dispatcher, reactor, daemon_options, |
304 | + use_celery=False, job_cls=None): |
305 | self.source = source |
306 | self.board_name = board_name |
307 | self.dispatcher = dispatcher |
308 | @@ -272,6 +286,7 @@ |
309 | self._stopping_deferreds = [] |
310 | self.logger = logging.getLogger(__name__ + '.Board.' + board_name) |
311 | self.checking = False |
312 | + self.use_celery = use_celery |
313 | |
314 | def _state_name(self): |
315 | if self.running_job: |
316 | @@ -345,7 +360,7 @@ |
317 | self.logger.info("starting job %r", job_data) |
318 | self.running_job = self.job_cls( |
319 | job_data, self.dispatcher, self.source, self.board_name, |
320 | - self.reactor, self.daemon_options) |
321 | + self.reactor, self.daemon_options, self.use_celery) |
322 | d = self.running_job.run() |
323 | d.addCallbacks(self._cbJobFinished, self._ebJobFinished) |
324 | |
325 | |
326 | === modified file 'lava_scheduler_daemon/dbjobsource.py' |
327 | --- lava_scheduler_daemon/dbjobsource.py 2012-06-29 03:14:32 +0000 |
328 | +++ lava_scheduler_daemon/dbjobsource.py 2012-07-10 04:16:21 +0000 |
329 | @@ -88,7 +88,8 @@ |
330 | return self.deferToThread(wrapper, *args, **kw) |
331 | |
332 | def getBoardList_impl(self): |
333 | - return [d.hostname for d in Device.objects.all()] |
334 | + return [ {'hostname': d.hostname, 'use_celery': d.use_celery()} |
335 | + for d in Device.objects.all()] |
336 | |
337 | def getBoardList(self): |
338 | return self.deferForDB(self.getBoardList_impl) |
339 | |
340 | === modified file 'lava_scheduler_daemon/service.py' |
341 | --- lava_scheduler_daemon/service.py 2012-03-29 04:56:44 +0000 |
342 | +++ lava_scheduler_daemon/service.py 2012-07-10 04:16:21 +0000 |
343 | @@ -24,20 +24,28 @@ |
344 | return self.source.getBoardList().addCallback( |
345 | self._cbUpdateBoards).addErrback(catchall_errback(self.logger)) |
346 | |
347 | - def _cbUpdateBoards(self, board_names): |
348 | - if set(board_names) == set(self.boards): |
349 | - return |
350 | - self.logger.info("New board list %s", board_names) |
351 | + def _cbUpdateBoards(self, board_cfgs): |
352 | + '''board_cfgs is an array of dicts {hostname=name, use_celery=...} ''' |
353 | new_boards = {} |
354 | - for board_name in board_names: |
355 | - if board_name in self.boards: |
356 | - new_boards[board_name] = self.boards.pop(board_name) |
357 | + for board_cfg in board_cfgs: |
358 | + board_name = board_cfg['hostname'] |
359 | + use_celery = board_cfg['use_celery'] |
360 | + |
361 | + if board_cfg['hostname'] in self.boards: |
362 | + board = self.boards.pop(board_name) |
363 | + if use_celery != board.use_celery: |
364 | + board.use_celery = use_celery |
365 | + self.logger.info("use_celery changed for %s to '%s'" % \ |
366 | + (board_name, use_celery)) |
367 | + new_boards[board_name] = board |
368 | else: |
369 | + self.logger.info("Adding board: %s" % board_name) |
370 | new_boards[board_name] = Board( |
371 | self.source, board_name, self.dispatcher, self.reactor, |
372 | - self.daemon_options) |
373 | + self.daemon_options, use_celery) |
374 | new_boards[board_name].start() |
375 | for board in self.boards.values(): |
376 | + self.logger.info("Removing board: %s" % board.board_name) |
377 | board.stop() |
378 | self.boards = new_boards |
379 |
Yay, looks good.
> + log_to_stdout = os.getenv( "CELERY_ CONFIG_ MODULE" , False)
That is quite the hack :-)
> - if self.log_ file.tell( ) > self.job. daemon_ options[ 'LOG_FILE_ SIZE_LIMIT' ]: file.tell( ) > self.job. daemon_ options[ 'LOG_FILE_ SIZE_LIMIT' ]:
> + if self.log_file != sys.stdout and \
> + self.log_
I would rather switch to having DispatcherProce ssProtocol keep track of how big the log is (self._log_size = 0 in __init__, self._log_size += len(data) in childDataReceived, then check that rather than log_file.tell()) than lose this auto-killing.
I would really like to get the code I sent you to help debug this landed -- can you file a bug about that?