blob: b783115f8de47a407c4e1ee974a9411a9c7d9222 [file] [log] [blame]
showardf828c772010-01-25 21:49:42 +00001from django import http
Eric Li0a993912011-05-17 12:56:25 -07002from autotest_lib.frontend.shared import query_lib, resource_lib, exceptions
showardf828c772010-01-25 21:49:42 +00003from autotest_lib.frontend.afe import control_file, models, rpc_utils
jamesrendd855242010-03-02 22:23:44 +00004from autotest_lib.frontend.afe import model_attributes
showardf828c772010-01-25 21:49:42 +00005from autotest_lib.frontend import thread_local
6from autotest_lib.client.common_lib import host_protections
7
Eric Li0a993912011-05-17 12:56:25 -07008
jamesren3981f442010-02-16 19:27:59 +00009class EntryWithInvalid(resource_lib.InstanceEntry):
showardf46ad4c2010-02-03 20:28:59 +000010 def put(self):
showardf828c772010-01-25 21:49:42 +000011 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000012 raise http.Http404('%s has been deleted' % self.instance)
13 return super(EntryWithInvalid, self).put()
showardf828c772010-01-25 21:49:42 +000014
15
showardf46ad4c2010-02-03 20:28:59 +000016 def delete(self):
showardf828c772010-01-25 21:49:42 +000017 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000018 raise http.Http404('%s has already been deleted' % self.instance)
19 return super(EntryWithInvalid, self).delete()
showardf828c772010-01-25 21:49:42 +000020
21
22class AtomicGroupClass(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000023 model = models.AtomicGroup
24
25
showardf828c772010-01-25 21:49:42 +000026 @classmethod
jamesren3981f442010-02-16 19:27:59 +000027 def from_uri_args(cls, request, ag_name, **kwargs):
28 return cls(request, models.AtomicGroup.objects.get(name=ag_name))
showardf828c772010-01-25 21:49:42 +000029
30
31 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000032 return {'ag_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000033
34
35 def short_representation(self):
36 rep = super(AtomicGroupClass, self).short_representation()
37 rep['name'] = self.instance.name
38 return rep
39
40
41 def full_representation(self):
42 rep = super(AtomicGroupClass, self).full_representation()
43 rep.update({'max_number_of_machines':
44 self.instance.max_number_of_machines,
jamesren3981f442010-02-16 19:27:59 +000045 'labels':
46 AtomicLabelTaggingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +000047 return rep
48
49
50 @classmethod
51 def create_instance(cls, input_dict, containing_collection):
52 cls._check_for_required_fields(input_dict, ('name',))
53 return models.AtomicGroup.add_object(name=input_dict['name'])
54
55
56 def update(self, input_dict):
57 data = {'max_number_of_machines':
58 input_dict.get('max_number_of_machines')}
59 data = input_dict.remove_unspecified_fields(data)
60 self.instance.update_object(**data)
61
62
63class AtomicGroupClassCollection(resource_lib.Collection):
64 queryset = models.AtomicGroup.valid_objects.all()
65 entry_class = AtomicGroupClass
66
67
showardf828c772010-01-25 21:49:42 +000068class Label(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000069 model = models.Label
70
71 @classmethod
72 def add_query_selectors(cls, query_processor):
73 query_processor.add_field_selector('name')
74 query_processor.add_field_selector(
75 'is_platform', field='platform',
76 value_transform=query_processor.read_boolean)
showardf828c772010-01-25 21:49:42 +000077
78
79 @classmethod
jamesren3981f442010-02-16 19:27:59 +000080 def from_uri_args(cls, request, label_name, **kwargs):
81 return cls(request, models.Label.objects.get(name=label_name))
showardf828c772010-01-25 21:49:42 +000082
83
84 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000085 return {'label_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000086
87
88 def short_representation(self):
89 rep = super(Label, self).short_representation()
90 rep.update({'name': self.instance.name,
91 'is_platform': bool(self.instance.platform)})
92 return rep
93
94
95 def full_representation(self):
96 rep = super(Label, self).full_representation()
showardf46ad4c2010-02-03 20:28:59 +000097 atomic_group_class = AtomicGroupClass.from_optional_instance(
98 self._request, self.instance.atomic_group)
99 rep.update({'atomic_group_class':
100 atomic_group_class.short_representation(),
jamesren3981f442010-02-16 19:27:59 +0000101 'hosts': HostLabelingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000102 return rep
103
104
105 @classmethod
106 def create_instance(cls, input_dict, containing_collection):
107 cls._check_for_required_fields(input_dict, ('name',))
108 return models.Label.add_object(name=input_dict['name'])
109
110
111 def update(self, input_dict):
112 # TODO update atomic group
jamesrencd7a81a2010-04-21 20:39:08 +0000113 if 'is_platform' in input_dict:
114 self.instance.platform = input_dict['is_platform']
115 self.instance.save()
showardf828c772010-01-25 21:49:42 +0000116
117
118class LabelCollection(resource_lib.Collection):
119 queryset = models.Label.valid_objects.all()
120 entry_class = Label
121
122
jamesren3981f442010-02-16 19:27:59 +0000123class AtomicLabelTagging(resource_lib.Relationship):
124 related_classes = {'label': Label, 'atomic_group_class': AtomicGroupClass}
showardf828c772010-01-25 21:49:42 +0000125
126
jamesren3981f442010-02-16 19:27:59 +0000127class AtomicLabelTaggingCollection(resource_lib.RelationshipCollection):
128 entry_class = AtomicLabelTagging
showardf828c772010-01-25 21:49:42 +0000129
130
jamesren3981f442010-02-16 19:27:59 +0000131class User(resource_lib.InstanceEntry):
132 model = models.User
showardf828c772010-01-25 21:49:42 +0000133 _permitted_methods = ('GET,')
134
135
136 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000137 def from_uri_args(cls, request, username, **kwargs):
showardf828c772010-01-25 21:49:42 +0000138 if username == '@me':
showardf46ad4c2010-02-03 20:28:59 +0000139 username = models.User.current_user().login
140 return cls(request, models.User.objects.get(login=username))
showardf828c772010-01-25 21:49:42 +0000141
142
143 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000144 return {'username': self.instance.login}
showardf828c772010-01-25 21:49:42 +0000145
146
147 def short_representation(self):
148 rep = super(User, self).short_representation()
149 rep['username'] = self.instance.login
150 return rep
151
152
153 def full_representation(self):
154 rep = super(User, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000155 accessible_hosts = HostCollection(self._request)
156 accessible_hosts.set_query_parameters(accessible_by=self.instance.login)
showardf828c772010-01-25 21:49:42 +0000157 rep.update({'jobs': 'TODO',
158 'recurring_runs': 'TODO',
jamesren3981f442010-02-16 19:27:59 +0000159 'acls':
160 UserAclMembershipCollection(fixed_entry=self).link(),
161 'accessible_hosts': accessible_hosts.link()})
showardf828c772010-01-25 21:49:42 +0000162 return rep
163
164
165class UserCollection(resource_lib.Collection):
166 _permitted_methods = ('GET',)
167 queryset = models.User.objects.all()
168 entry_class = User
169
170
jamesren3981f442010-02-16 19:27:59 +0000171class Acl(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000172 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000173 model = models.AclGroup
showardf828c772010-01-25 21:49:42 +0000174
175 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000176 def from_uri_args(cls, request, acl_name, **kwargs):
177 return cls(request, models.AclGroup.objects.get(name=acl_name))
showardf828c772010-01-25 21:49:42 +0000178
179
180 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000181 return {'acl_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000182
183
184 def short_representation(self):
185 rep = super(Acl, self).short_representation()
186 rep['name'] = self.instance.name
187 return rep
188
189
190 def full_representation(self):
191 rep = super(Acl, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000192 rep.update({'users':
193 UserAclMembershipCollection(fixed_entry=self).link(),
194 'hosts':
195 HostAclMembershipCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000196 return rep
197
198
199 @classmethod
200 def create_instance(cls, input_dict, containing_collection):
201 cls._check_for_required_fields(input_dict, ('name',))
202 return models.AclGroup.add_object(name=input_dict['name'])
203
204
205 def update(self, input_dict):
206 pass
207
208
209class AclCollection(resource_lib.Collection):
210 queryset = models.AclGroup.objects.all()
211 entry_class = Acl
212
213
jamesren3981f442010-02-16 19:27:59 +0000214class UserAclMembership(resource_lib.Relationship):
215 related_classes = {'user': User, 'acl': Acl}
216
217 # TODO: check permissions
218 # TODO: check for and add/remove "Everyone"
showardf828c772010-01-25 21:49:42 +0000219
220
jamesren3981f442010-02-16 19:27:59 +0000221class UserAclMembershipCollection(resource_lib.RelationshipCollection):
222 entry_class = UserAclMembership
showardf828c772010-01-25 21:49:42 +0000223
224
225class Host(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +0000226 model = models.Host
227
228 @classmethod
229 def add_query_selectors(cls, query_processor):
230 query_processor.add_field_selector('hostname')
231 query_processor.add_field_selector(
232 'locked', value_transform=query_processor.read_boolean)
233 query_processor.add_field_selector(
showardf828c772010-01-25 21:49:42 +0000234 'locked_by', field='locked_by__login',
235 doc='Username of user who locked this host, if locked')
jamesren3981f442010-02-16 19:27:59 +0000236 query_processor.add_field_selector('status')
237 query_processor.add_field_selector(
238 'protection_level', field='protection',
239 doc='Verify/repair protection level',
240 value_transform=cls._read_protection)
241 query_processor.add_field_selector(
242 'accessible_by', field='aclgroup__users__login',
243 doc='Username of user with access to this host')
244 query_processor.add_related_existence_selector(
245 'has_label', models.Label, 'name')
showardf828c772010-01-25 21:49:42 +0000246
247
248 @classmethod
249 def _read_protection(cls, protection_input):
250 return host_protections.Protection.get_value(protection_input)
251
252
253 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000254 def from_uri_args(cls, request, hostname, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000255 return cls(request, models.Host.objects.get(hostname=hostname))
showardf828c772010-01-25 21:49:42 +0000256
257
258 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000259 return {'hostname': self.instance.hostname}
showardf828c772010-01-25 21:49:42 +0000260
261
262 def short_representation(self):
263 rep = super(Host, self).short_representation()
264 # TODO calling platform() over and over is inefficient
showardf46ad4c2010-02-03 20:28:59 +0000265 platform_rep = (Label.from_optional_instance(self._request,
266 self.instance.platform())
showardf828c772010-01-25 21:49:42 +0000267 .short_representation())
268 rep.update({'hostname': self.instance.hostname,
269 'locked': bool(self.instance.locked),
270 'status': self.instance.status,
271 'platform': platform_rep})
272 return rep
273
274
275 def full_representation(self):
276 rep = super(Host, self).full_representation()
277 protection = host_protections.Protection.get_string(
278 self.instance.protection)
showardf46ad4c2010-02-03 20:28:59 +0000279 locked_by = (User.from_optional_instance(self._request,
280 self.instance.locked_by)
showardf828c772010-01-25 21:49:42 +0000281 .short_representation())
jamesren3981f442010-02-16 19:27:59 +0000282 labels = HostLabelingCollection(fixed_entry=self)
283 acls = HostAclMembershipCollection(fixed_entry=self)
284 queue_entries = QueueEntryCollection(self._request)
285 queue_entries.set_query_parameters(host=self.instance.hostname)
286 health_tasks = HealthTaskCollection(self._request)
287 health_tasks.set_query_parameters(host=self.instance.hostname)
288
showardf828c772010-01-25 21:49:42 +0000289 rep.update({'locked_by': locked_by,
290 'locked_on': self._format_datetime(self.instance.lock_time),
291 'invalid': self.instance.invalid,
292 'protection_level': protection,
293 # TODO make these efficient
jamesren3981f442010-02-16 19:27:59 +0000294 'labels': labels.full_representation(),
295 'acls': acls.full_representation(),
296 'queue_entries': queue_entries.link(),
297 'health_tasks': health_tasks.link()})
showardf828c772010-01-25 21:49:42 +0000298 return rep
299
300
301 @classmethod
302 def create_instance(cls, input_dict, containing_collection):
303 cls._check_for_required_fields(input_dict, ('hostname',))
jamesren3981f442010-02-16 19:27:59 +0000304 # include locked here, rather than waiting for update(), to avoid race
305 # conditions
306 host = models.Host.add_object(hostname=input_dict['hostname'],
307 locked=input_dict.get('locked', False))
308 return host
showardf828c772010-01-25 21:49:42 +0000309
310 def update(self, input_dict):
311 data = {'locked': input_dict.get('locked'),
312 'protection': input_dict.get('protection_level')}
313 data = input_dict.remove_unspecified_fields(data)
314
315 if 'protection' in data:
316 data['protection'] = self._read_protection(data['protection'])
317
318 self.instance.update_object(**data)
319
320 if 'platform' in input_dict:
showardf46ad4c2010-02-03 20:28:59 +0000321 label = self.resolve_link(input_dict['platform']) .instance
showardf828c772010-01-25 21:49:42 +0000322 if not label.platform:
Eric Li0a993912011-05-17 12:56:25 -0700323 raise exceptions.BadRequest('Label %s is not a platform' % label.name)
showardf828c772010-01-25 21:49:42 +0000324 for label in self.instance.labels.filter(platform=True):
325 self.instance.labels.remove(label)
326 self.instance.labels.add(label)
327
328
329class HostCollection(resource_lib.Collection):
330 queryset = models.Host.valid_objects.all()
331 entry_class = Host
332
333
jamesren3981f442010-02-16 19:27:59 +0000334class HostLabeling(resource_lib.Relationship):
335 related_classes = {'host': Host, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000336
337
jamesren3981f442010-02-16 19:27:59 +0000338class HostLabelingCollection(resource_lib.RelationshipCollection):
339 entry_class = HostLabeling
showardf828c772010-01-25 21:49:42 +0000340
341
jamesren3981f442010-02-16 19:27:59 +0000342class HostAclMembership(resource_lib.Relationship):
343 related_classes = {'host': Host, 'acl': Acl}
344
345 # TODO: acl.check_for_acl_violation_acl_group()
346 # TODO: models.AclGroup.on_host_membership_change()
showardf828c772010-01-25 21:49:42 +0000347
348
jamesren3981f442010-02-16 19:27:59 +0000349class HostAclMembershipCollection(resource_lib.RelationshipCollection):
350 entry_class = HostAclMembership
showardf828c772010-01-25 21:49:42 +0000351
352
jamesren3981f442010-02-16 19:27:59 +0000353class Test(resource_lib.InstanceEntry):
354 model = models.Test
showardf828c772010-01-25 21:49:42 +0000355
356
showardf828c772010-01-25 21:49:42 +0000357 @classmethod
jamesrencd7a81a2010-04-21 20:39:08 +0000358 def add_query_selectors(cls, query_processor):
359 query_processor.add_field_selector('name')
360
361
362 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000363 def from_uri_args(cls, request, test_name, **kwargs):
364 return cls(request, models.Test.objects.get(name=test_name))
showardf828c772010-01-25 21:49:42 +0000365
366
367 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000368 return {'test_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000369
370
371 def short_representation(self):
372 rep = super(Test, self).short_representation()
373 rep['name'] = self.instance.name
374 return rep
375
376
377 def full_representation(self):
378 rep = super(Test, self).full_representation()
379 rep.update({'author': self.instance.author,
380 'class': self.instance.test_class,
381 'control_file_type':
jamesrendd855242010-03-02 22:23:44 +0000382 model_attributes.TestTypes.get_string(
383 self.instance.test_type),
showardf828c772010-01-25 21:49:42 +0000384 'control_file_path': self.instance.path,
jamesrencd7a81a2010-04-21 20:39:08 +0000385 'sync_count': self.instance.sync_count,
jamesren3981f442010-02-16 19:27:59 +0000386 'dependencies':
387 TestDependencyCollection(fixed_entry=self).link(),
showardf828c772010-01-25 21:49:42 +0000388 })
389 return rep
390
391
392 @classmethod
393 def create_instance(cls, input_dict, containing_collection):
394 cls._check_for_required_fields(input_dict,
395 ('name', 'control_file_type',
396 'control_file_path'))
jamesrendd855242010-03-02 22:23:44 +0000397 test_type = model_attributes.TestTypes.get_value(
398 input['control_file_type'])
showardf828c772010-01-25 21:49:42 +0000399 return models.Test.add_object(name=input_dict['name'],
400 test_type=test_type,
401 path=input_dict['control_file_path'])
402
403
404 def update(self, input_dict):
405 data = {'test_type': input_dict.get('control_file_type'),
406 'path': input_dict.get('control_file_path'),
407 'class': input_dict.get('class'),
408 }
409 data = input_dict.remove_unspecified_fields(data)
410 self.instance.update_object(**data)
411
412
413class TestCollection(resource_lib.Collection):
414 queryset = models.Test.objects.all()
415 entry_class = Test
416
417
jamesren3981f442010-02-16 19:27:59 +0000418class TestDependency(resource_lib.Relationship):
419 related_classes = {'test': Test, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000420
421
jamesren3981f442010-02-16 19:27:59 +0000422class TestDependencyCollection(resource_lib.RelationshipCollection):
423 entry_class = TestDependency
showardf828c772010-01-25 21:49:42 +0000424
425
426# TODO profilers
427
428
429class ExecutionInfo(resource_lib.Resource):
430 _permitted_methods = ('GET','POST')
431 _job_fields = models.Job.get_field_dict()
432 _DEFAULTS = {
433 'control_file': '',
434 'is_server': True,
435 'dependencies': [],
436 'machines_per_execution': 1,
437 'run_verify': bool(_job_fields['run_verify'].default),
438 'timeout_hrs': _job_fields['timeout'].default,
439 'maximum_runtime_hrs': _job_fields['max_runtime_hrs'].default,
440 'cleanup_before_job':
jamesrendd855242010-03-02 22:23:44 +0000441 model_attributes.RebootBefore.get_string(
442 models.DEFAULT_REBOOT_BEFORE),
showardf828c772010-01-25 21:49:42 +0000443 'cleanup_after_job':
jamesrendd855242010-03-02 22:23:44 +0000444 model_attributes.RebootAfter.get_string(
445 models.DEFAULT_REBOOT_AFTER),
showardf828c772010-01-25 21:49:42 +0000446 }
447
448
jamesren3981f442010-02-16 19:27:59 +0000449 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000450 return (('tests', 'Comma-separated list of test names to run'),
451 ('kernels', 'TODO'),
452 ('client_control_file',
453 'Client control file segment to run after all specified '
454 'tests'),
455 ('profilers',
456 'Comma-separated list of profilers to activate during the '
457 'job'),
458 ('use_container', 'TODO'),
459 ('profile_only',
460 'If true, run only profiled iterations; otherwise, always run '
461 'at least one non-profiled iteration in addition to a '
462 'profiled iteration'),
463 ('upload_kernel_config',
464 'If true, generate a server control file code that uploads '
465 'the kernel config file to the client and tells the client of '
466 'the new (local) path when compiling the kernel; the tests '
467 'must be server side tests'))
468
469
470 @classmethod
471 def execution_info_from_job(cls, job):
472 return {'control_file': job.control_file,
473 'is_server': job.control_type == models.Job.ControlType.SERVER,
474 'dependencies': [label.name for label
475 in job.dependency_labels.all()],
476 'machines_per_execution': job.synch_count,
477 'run_verify': bool(job.run_verify),
478 'timeout_hrs': job.timeout,
479 'maximum_runtime_hrs': job.max_runtime_hrs,
480 'cleanup_before_job':
jamesrendd855242010-03-02 22:23:44 +0000481 model_attributes.RebootBefore.get_string(job.reboot_before),
showardf828c772010-01-25 21:49:42 +0000482 'cleanup_after_job':
jamesrendd855242010-03-02 22:23:44 +0000483 model_attributes.RebootAfter.get_string(job.reboot_after),
showardf828c772010-01-25 21:49:42 +0000484 }
485
486
487 def _get_execution_info(self, input_dict):
488 tests = input_dict.get('tests', '')
489 client_control_file = input_dict.get('client_control_file', None)
490 if not tests and not client_control_file:
491 return self._DEFAULTS
492
493 test_list = tests.split(',')
494 if 'profilers' in input_dict:
495 profilers_list = input_dict['profilers'].split(',')
496 else:
497 profilers_list = []
498 kernels = input_dict.get('kernels', '') # TODO
499 if kernels:
500 kernels = [dict(version=kernel) for kernel in kernels.split(',')]
501
502 cf_info, test_objects, profiler_objects, label = (
503 rpc_utils.prepare_generate_control_file(
504 test_list, kernels, None, profilers_list))
505 control_file_contents = control_file.generate_control(
506 tests=test_objects, kernels=kernels,
507 profilers=profiler_objects, is_server=cf_info['is_server'],
508 client_control_file=client_control_file,
509 profile_only=input_dict.get('profile_only', None),
510 upload_kernel_config=input_dict.get(
511 'upload_kernel_config', None))
512 return dict(self._DEFAULTS,
513 control_file=control_file_contents,
514 is_server=cf_info['is_server'],
515 dependencies=cf_info['dependencies'],
516 machines_per_execution=cf_info['synch_count'])
517
518
showardf46ad4c2010-02-03 20:28:59 +0000519 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000520 result = self.link()
showardf46ad4c2010-02-03 20:28:59 +0000521 result['execution_info'] = self._get_execution_info(
522 self._request.REQUEST)
showardf828c772010-01-25 21:49:42 +0000523 return self._basic_response(result)
524
525
526class QueueEntriesRequest(resource_lib.Resource):
527 _permitted_methods = ('GET',)
528
529
jamesren3981f442010-02-16 19:27:59 +0000530 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000531 return (('hosts', 'Comma-separated list of hostnames'),
532 ('one_time_hosts',
533 'Comma-separated list of hostnames not already in the '
534 'Autotest system'),
535 ('meta_hosts',
536 'Comma-separated list of label names; for each one, an entry '
537 'will be created and assigned at runtime to an available host '
538 'with that label'),
539 ('atomic_group_class', 'TODO'))
540
541
542 def _read_list(self, list_string):
543 if list_string:
544 return list_string.split(',')
545 return []
546
547
showardf46ad4c2010-02-03 20:28:59 +0000548 def handle_request(self):
549 request_dict = self._request.REQUEST
showardf828c772010-01-25 21:49:42 +0000550 hosts = self._read_list(request_dict.get('hosts'))
551 one_time_hosts = self._read_list(request_dict.get('one_time_hosts'))
552 meta_hosts = self._read_list(request_dict.get('meta_hosts'))
553 atomic_group_class = request_dict.get('atomic_group_class')
554
555 # TODO: bring in all the atomic groups magic from create_job()
556
557 entries = []
558 for hostname in one_time_hosts:
559 models.Host.create_one_time_host(hostname)
560 for hostname in hosts:
showardf46ad4c2010-02-03 20:28:59 +0000561 entry = Host.from_uri_args(self._request, hostname)
showardf828c772010-01-25 21:49:42 +0000562 entries.append({'host': entry.link()})
563 for label_name in meta_hosts:
showardf46ad4c2010-02-03 20:28:59 +0000564 entry = Label.from_uri_args(self._request, label_name)
showardf828c772010-01-25 21:49:42 +0000565 entries.append({'meta_host': entry.link()})
jamesren3e9f6092010-03-11 21:32:10 +0000566 if atomic_group_class:
567 entries.append({'atomic_group_class': atomic_group_class})
showardf828c772010-01-25 21:49:42 +0000568
569 result = self.link()
570 result['queue_entries'] = entries
571 return self._basic_response(result)
572
573
jamesren3981f442010-02-16 19:27:59 +0000574class Job(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000575 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000576 model = models.Job
577
showardf828c772010-01-25 21:49:42 +0000578
579 class _StatusConstraint(query_lib.Constraint):
jamesren3981f442010-02-16 19:27:59 +0000580 def apply_constraint(self, queryset, value, comparison_type,
581 is_inverse):
showardf828c772010-01-25 21:49:42 +0000582 if comparison_type != 'equals' or is_inverse:
583 raise query_lib.ConstraintError('Can only use this selector '
584 'with equals')
585 non_queued_statuses = [
586 status for status, _
587 in models.HostQueueEntry.Status.choices()
588 if status != models.HostQueueEntry.Status.QUEUED]
589 if value == 'queued':
590 return queryset.exclude(
591 hostqueueentry__status__in=non_queued_statuses)
592 elif value == 'active':
593 return queryset.filter(
594 hostqueueentry__status__in=non_queued_statuses).filter(
595 hostqueueentry__complete=False).distinct()
596 elif value == 'complete':
597 return queryset.exclude(hostqueueentry__complete=False)
598 else:
599 raise query_lib.ConstraintError('Value must be one of queued, '
600 'active or complete')
601
602
jamesren3981f442010-02-16 19:27:59 +0000603 @classmethod
604 def add_query_selectors(cls, query_processor):
605 query_processor.add_field_selector('id')
jamesrenc3940222010-02-19 21:57:37 +0000606 query_processor.add_field_selector('name')
jamesren3981f442010-02-16 19:27:59 +0000607 query_processor.add_selector(
608 query_lib.Selector('status',
609 doc='One of queued, active or complete'),
610 Job._StatusConstraint())
jamesrencd7a81a2010-04-21 20:39:08 +0000611 query_processor.add_keyval_selector('has_keyval', models.JobKeyval,
612 'key', 'value')
showardf828c772010-01-25 21:49:42 +0000613
614
615 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000616 def from_uri_args(cls, request, job_id, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000617 return cls(request, models.Job.objects.get(id=job_id))
showardf828c772010-01-25 21:49:42 +0000618
619
620 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000621 return {'job_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000622
623
jamesrencd7a81a2010-04-21 20:39:08 +0000624 @classmethod
625 def _do_prepare_for_full_representation(cls, instances):
626 models.Job.objects.populate_relationships(instances, models.JobKeyval,
627 'keyvals')
628
629
showardf828c772010-01-25 21:49:42 +0000630 def short_representation(self):
631 rep = super(Job, self).short_representation()
632 rep.update({'id': self.instance.id,
633 'owner': self.instance.owner,
634 'name': self.instance.name,
635 'priority':
636 models.Job.Priority.get_string(self.instance.priority),
637 'created_on':
638 self._format_datetime(self.instance.created_on),
639 })
640 return rep
641
642
643 def full_representation(self):
644 rep = super(Job, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000645 queue_entries = QueueEntryCollection(self._request)
646 queue_entries.set_query_parameters(job=self.instance.id)
jamesren76fcf192010-04-21 20:39:50 +0000647 drone_set = self.instance.drone_set and self.instance.drone_set.name
showardf828c772010-01-25 21:49:42 +0000648 rep.update({'email_list': self.instance.email_list,
649 'parse_failed_repair':
650 bool(self.instance.parse_failed_repair),
jamesren76fcf192010-04-21 20:39:50 +0000651 'drone_set': drone_set,
showardf828c772010-01-25 21:49:42 +0000652 'execution_info':
653 ExecutionInfo.execution_info_from_job(self.instance),
jamesren3981f442010-02-16 19:27:59 +0000654 'queue_entries': queue_entries.link(),
jamesrencd7a81a2010-04-21 20:39:08 +0000655 'keyvals': dict((keyval.key, keyval.value)
656 for keyval in self.instance.keyvals)
showardf828c772010-01-25 21:49:42 +0000657 })
658 return rep
659
660
661 @classmethod
662 def create_instance(cls, input_dict, containing_collection):
jamesrene38a0a72010-04-19 18:05:31 +0000663 owner = input_dict.get('owner')
664 if not owner:
665 owner = models.User.current_user().login
666
showardf828c772010-01-25 21:49:42 +0000667 cls._check_for_required_fields(input_dict, ('name', 'execution_info',
668 'queue_entries'))
669 execution_info = input_dict['execution_info']
670 cls._check_for_required_fields(execution_info, ('control_file',
671 'is_server'))
672
673 if execution_info['is_server']:
674 control_type = models.Job.ControlType.SERVER
675 else:
676 control_type = models.Job.ControlType.CLIENT
677 options = dict(
678 name=input_dict['name'],
679 priority=input_dict.get('priority', None),
680 control_file=execution_info['control_file'],
681 control_type=control_type,
682 is_template=input_dict.get('is_template', None),
683 timeout=execution_info.get('timeout_hrs'),
684 max_runtime_hrs=execution_info.get('maximum_runtime_hrs'),
685 synch_count=execution_info.get('machines_per_execution'),
686 run_verify=execution_info.get('run_verify'),
687 email_list=input_dict.get('email_list', None),
688 dependencies=execution_info.get('dependencies', ()),
689 reboot_before=execution_info.get('cleanup_before_job'),
690 reboot_after=execution_info.get('cleanup_after_job'),
691 parse_failed_repair=input_dict.get('parse_failed_repair', None),
jamesren76fcf192010-04-21 20:39:50 +0000692 drone_set=input_dict.get('drone_set', None),
showardf828c772010-01-25 21:49:42 +0000693 keyvals=input_dict.get('keyvals', None))
694
695 host_objects, metahost_label_objects, atomic_group = [], [], None
696 for queue_entry in input_dict['queue_entries']:
697 if 'host' in queue_entry:
698 host = queue_entry['host']
699 if host: # can be None, indicated a hostless job
showardf46ad4c2010-02-03 20:28:59 +0000700 host_entry = containing_collection.resolve_link(host)
showardf828c772010-01-25 21:49:42 +0000701 host_objects.append(host_entry.instance)
702 elif 'meta_host' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000703 label_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000704 queue_entry['meta_host'])
705 metahost_label_objects.append(label_entry.instance)
jamesren3e9f6092010-03-11 21:32:10 +0000706 if 'atomic_group_class' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000707 atomic_group_entry = containing_collection.resolve_link(
jamesren3e9f6092010-03-11 21:32:10 +0000708 queue_entry['atomic_group_class'])
showardf828c772010-01-25 21:49:42 +0000709 if atomic_group:
710 assert atomic_group_entry.instance.id == atomic_group.id
711 else:
712 atomic_group = atomic_group_entry.instance
713
714 job_id = rpc_utils.create_new_job(
jamesrene38a0a72010-04-19 18:05:31 +0000715 owner=owner,
showardf828c772010-01-25 21:49:42 +0000716 options=options,
717 host_objects=host_objects,
718 metahost_objects=metahost_label_objects,
719 atomic_group=atomic_group)
720 return models.Job.objects.get(id=job_id)
721
722
723 def update(self, input_dict):
724 # Required for POST, doesn't actually support PUT
725 pass
726
727
728class JobCollection(resource_lib.Collection):
729 queryset = models.Job.objects.order_by('-id')
730 entry_class = Job
731
732
jamesren3981f442010-02-16 19:27:59 +0000733class QueueEntry(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000734 _permitted_methods = ('GET', 'PUT')
jamesren3981f442010-02-16 19:27:59 +0000735 model = models.HostQueueEntry
736
showardf828c772010-01-25 21:49:42 +0000737
738 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000739 def add_query_selectors(cls, query_processor):
740 query_processor.add_field_selector('host', field='host__hostname')
741 query_processor.add_field_selector('job', field='job__id')
742
743
744 @classmethod
745 def from_uri_args(cls, request, queue_entry_id):
showardf828c772010-01-25 21:49:42 +0000746 instance = models.HostQueueEntry.objects.get(id=queue_entry_id)
showardf46ad4c2010-02-03 20:28:59 +0000747 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000748
749
750 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000751 return {'queue_entry_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000752
753
754 def short_representation(self):
755 rep = super(QueueEntry, self).short_representation()
756 if self.instance.host:
showardf46ad4c2010-02-03 20:28:59 +0000757 host = (Host(self._request, self.instance.host)
758 .short_representation())
showardf828c772010-01-25 21:49:42 +0000759 else:
760 host = None
showardf46ad4c2010-02-03 20:28:59 +0000761 job = Job(self._request, self.instance.job)
762 host = Host.from_optional_instance(self._request, self.instance.host)
763 label = Label.from_optional_instance(self._request,
764 self.instance.meta_host)
765 atomic_group_class = AtomicGroupClass.from_optional_instance(
766 self._request, self.instance.atomic_group)
showardf828c772010-01-25 21:49:42 +0000767 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000768 {'job': job.short_representation(),
769 'host': host.short_representation(),
770 'label': label.short_representation(),
showardf828c772010-01-25 21:49:42 +0000771 'atomic_group_class':
showardf46ad4c2010-02-03 20:28:59 +0000772 atomic_group_class.short_representation(),
showardf828c772010-01-25 21:49:42 +0000773 'status': self.instance.status,
774 'execution_path': self.instance.execution_subdir,
775 'started_on': self._format_datetime(self.instance.started_on),
776 'aborted': bool(self.instance.aborted)})
777 return rep
778
779
780 def update(self, input_dict):
781 if 'aborted' in input_dict:
782 if input_dict['aborted'] != True:
Eric Li0a993912011-05-17 12:56:25 -0700783 raise exceptions.BadRequest('"aborted" can only be set to true')
showardf828c772010-01-25 21:49:42 +0000784 query = models.HostQueueEntry.objects.filter(pk=self.instance.pk)
785 models.AclGroup.check_abort_permissions(query)
786 rpc_utils.check_abort_synchronous_jobs(query)
787 self.instance.abort(thread_local.get_user())
788
789
jamesren3981f442010-02-16 19:27:59 +0000790class QueueEntryCollection(resource_lib.Collection):
791 queryset = models.HostQueueEntry.objects.order_by('-id')
792 entry_class = QueueEntry
793
794
795class HealthTask(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000796 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000797 model = models.SpecialTask
798
showardf828c772010-01-25 21:49:42 +0000799
800 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000801 def add_query_selectors(cls, query_processor):
802 query_processor.add_field_selector('host', field='host__hostname')
803
804
805 @classmethod
806 def from_uri_args(cls, request, task_id):
showardf828c772010-01-25 21:49:42 +0000807 instance = models.SpecialTask.objects.get(id=task_id)
showardf46ad4c2010-02-03 20:28:59 +0000808 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000809
810
811 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000812 return {'task_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000813
814
815 def short_representation(self):
816 rep = super(HealthTask, self).short_representation()
showardf46ad4c2010-02-03 20:28:59 +0000817 host = Host(self._request, self.instance.host)
818 queue_entry = QueueEntry.from_optional_instance(
819 self._request, self.instance.queue_entry)
showardf828c772010-01-25 21:49:42 +0000820 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000821 {'host': host.short_representation(),
showardf828c772010-01-25 21:49:42 +0000822 'task_type': self.instance.task,
823 'started_on':
824 self._format_datetime(self.instance.time_started),
825 'status': self.instance.status,
showardf46ad4c2010-02-03 20:28:59 +0000826 'queue_entry': queue_entry.short_representation()
showardf828c772010-01-25 21:49:42 +0000827 })
828 return rep
829
830
831 @classmethod
832 def create_instance(cls, input_dict, containing_collection):
833 cls._check_for_required_fields(input_dict, ('task_type',))
834 host = containing_collection.base_entry.instance
835 models.AclGroup.check_for_acl_violation_hosts((host,))
836 return models.SpecialTask.schedule_special_task(host,
837 input_dict['task_type'])
838
839
840 def update(self, input_dict):
841 # Required for POST, doesn't actually support PUT
842 pass
843
844
jamesren3981f442010-02-16 19:27:59 +0000845class HealthTaskCollection(resource_lib.Collection):
846 entry_class = HealthTask
847
848
849 def _fresh_queryset(self):
850 return models.SpecialTask.objects.order_by('-id')
851
852
showardf828c772010-01-25 21:49:42 +0000853class ResourceDirectory(resource_lib.Resource):
854 _permitted_methods = ('GET',)
855
showardf46ad4c2010-02-03 20:28:59 +0000856 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000857 result = self.link()
858 result.update({
showardf46ad4c2010-02-03 20:28:59 +0000859 'atomic_group_classes':
860 AtomicGroupClassCollection(self._request).link(),
861 'labels': LabelCollection(self._request).link(),
862 'users': UserCollection(self._request).link(),
863 'acl_groups': AclCollection(self._request).link(),
864 'hosts': HostCollection(self._request).link(),
865 'tests': TestCollection(self._request).link(),
866 'execution_info': ExecutionInfo(self._request).link(),
867 'queue_entries_request':
868 QueueEntriesRequest(self._request).link(),
869 'jobs': JobCollection(self._request).link(),
showardf828c772010-01-25 21:49:42 +0000870 })
871 return self._basic_response(result)