blob: 9527acb551141cc0dfad79516c21336fb8f1ede5 [file] [log] [blame]
showardf828c772010-01-25 21:49:42 +00001from django import http
2from autotest_lib.frontend.shared import query_lib, resource_lib
3from 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
jamesren3981f442010-02-16 19:27:59 +00008class EntryWithInvalid(resource_lib.InstanceEntry):
showardf46ad4c2010-02-03 20:28:59 +00009 def put(self):
showardf828c772010-01-25 21:49:42 +000010 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000011 raise http.Http404('%s has been deleted' % self.instance)
12 return super(EntryWithInvalid, self).put()
showardf828c772010-01-25 21:49:42 +000013
14
showardf46ad4c2010-02-03 20:28:59 +000015 def delete(self):
showardf828c772010-01-25 21:49:42 +000016 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000017 raise http.Http404('%s has already been deleted' % self.instance)
18 return super(EntryWithInvalid, self).delete()
showardf828c772010-01-25 21:49:42 +000019
20
21class AtomicGroupClass(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000022 model = models.AtomicGroup
23
24
showardf828c772010-01-25 21:49:42 +000025 @classmethod
jamesren3981f442010-02-16 19:27:59 +000026 def from_uri_args(cls, request, ag_name, **kwargs):
27 return cls(request, models.AtomicGroup.objects.get(name=ag_name))
showardf828c772010-01-25 21:49:42 +000028
29
30 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000031 return {'ag_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000032
33
34 def short_representation(self):
35 rep = super(AtomicGroupClass, self).short_representation()
36 rep['name'] = self.instance.name
37 return rep
38
39
40 def full_representation(self):
41 rep = super(AtomicGroupClass, self).full_representation()
42 rep.update({'max_number_of_machines':
43 self.instance.max_number_of_machines,
jamesren3981f442010-02-16 19:27:59 +000044 'labels':
45 AtomicLabelTaggingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +000046 return rep
47
48
49 @classmethod
50 def create_instance(cls, input_dict, containing_collection):
51 cls._check_for_required_fields(input_dict, ('name',))
52 return models.AtomicGroup.add_object(name=input_dict['name'])
53
54
55 def update(self, input_dict):
56 data = {'max_number_of_machines':
57 input_dict.get('max_number_of_machines')}
58 data = input_dict.remove_unspecified_fields(data)
59 self.instance.update_object(**data)
60
61
62class AtomicGroupClassCollection(resource_lib.Collection):
63 queryset = models.AtomicGroup.valid_objects.all()
64 entry_class = AtomicGroupClass
65
66
showardf828c772010-01-25 21:49:42 +000067class Label(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000068 model = models.Label
69
70 @classmethod
71 def add_query_selectors(cls, query_processor):
72 query_processor.add_field_selector('name')
73 query_processor.add_field_selector(
74 'is_platform', field='platform',
75 value_transform=query_processor.read_boolean)
showardf828c772010-01-25 21:49:42 +000076
77
78 @classmethod
jamesren3981f442010-02-16 19:27:59 +000079 def from_uri_args(cls, request, label_name, **kwargs):
80 return cls(request, models.Label.objects.get(name=label_name))
showardf828c772010-01-25 21:49:42 +000081
82
83 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000084 return {'label_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000085
86
87 def short_representation(self):
88 rep = super(Label, self).short_representation()
89 rep.update({'name': self.instance.name,
90 'is_platform': bool(self.instance.platform)})
91 return rep
92
93
94 def full_representation(self):
95 rep = super(Label, self).full_representation()
showardf46ad4c2010-02-03 20:28:59 +000096 atomic_group_class = AtomicGroupClass.from_optional_instance(
97 self._request, self.instance.atomic_group)
98 rep.update({'atomic_group_class':
99 atomic_group_class.short_representation(),
jamesren3981f442010-02-16 19:27:59 +0000100 'hosts': HostLabelingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000101 return rep
102
103
104 @classmethod
105 def create_instance(cls, input_dict, containing_collection):
106 cls._check_for_required_fields(input_dict, ('name',))
107 return models.Label.add_object(name=input_dict['name'])
108
109
110 def update(self, input_dict):
111 # TODO update atomic group
112 raise NotImplementedError
113
114
115class LabelCollection(resource_lib.Collection):
116 queryset = models.Label.valid_objects.all()
117 entry_class = Label
118
119
jamesren3981f442010-02-16 19:27:59 +0000120class AtomicLabelTagging(resource_lib.Relationship):
121 related_classes = {'label': Label, 'atomic_group_class': AtomicGroupClass}
showardf828c772010-01-25 21:49:42 +0000122
123
jamesren3981f442010-02-16 19:27:59 +0000124class AtomicLabelTaggingCollection(resource_lib.RelationshipCollection):
125 entry_class = AtomicLabelTagging
showardf828c772010-01-25 21:49:42 +0000126
127
jamesren3981f442010-02-16 19:27:59 +0000128class User(resource_lib.InstanceEntry):
129 model = models.User
showardf828c772010-01-25 21:49:42 +0000130 _permitted_methods = ('GET,')
131
132
133 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000134 def from_uri_args(cls, request, username, **kwargs):
showardf828c772010-01-25 21:49:42 +0000135 if username == '@me':
showardf46ad4c2010-02-03 20:28:59 +0000136 username = models.User.current_user().login
137 return cls(request, models.User.objects.get(login=username))
showardf828c772010-01-25 21:49:42 +0000138
139
140 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000141 return {'username': self.instance.login}
showardf828c772010-01-25 21:49:42 +0000142
143
144 def short_representation(self):
145 rep = super(User, self).short_representation()
146 rep['username'] = self.instance.login
147 return rep
148
149
150 def full_representation(self):
151 rep = super(User, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000152 accessible_hosts = HostCollection(self._request)
153 accessible_hosts.set_query_parameters(accessible_by=self.instance.login)
showardf828c772010-01-25 21:49:42 +0000154 rep.update({'jobs': 'TODO',
155 'recurring_runs': 'TODO',
jamesren3981f442010-02-16 19:27:59 +0000156 'acls':
157 UserAclMembershipCollection(fixed_entry=self).link(),
158 'accessible_hosts': accessible_hosts.link()})
showardf828c772010-01-25 21:49:42 +0000159 return rep
160
161
162class UserCollection(resource_lib.Collection):
163 _permitted_methods = ('GET',)
164 queryset = models.User.objects.all()
165 entry_class = User
166
167
jamesren3981f442010-02-16 19:27:59 +0000168class Acl(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000169 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000170 model = models.AclGroup
showardf828c772010-01-25 21:49:42 +0000171
172 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000173 def from_uri_args(cls, request, acl_name, **kwargs):
174 return cls(request, models.AclGroup.objects.get(name=acl_name))
showardf828c772010-01-25 21:49:42 +0000175
176
177 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000178 return {'acl_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000179
180
181 def short_representation(self):
182 rep = super(Acl, self).short_representation()
183 rep['name'] = self.instance.name
184 return rep
185
186
187 def full_representation(self):
188 rep = super(Acl, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000189 rep.update({'users':
190 UserAclMembershipCollection(fixed_entry=self).link(),
191 'hosts':
192 HostAclMembershipCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000193 return rep
194
195
196 @classmethod
197 def create_instance(cls, input_dict, containing_collection):
198 cls._check_for_required_fields(input_dict, ('name',))
199 return models.AclGroup.add_object(name=input_dict['name'])
200
201
202 def update(self, input_dict):
203 pass
204
205
206class AclCollection(resource_lib.Collection):
207 queryset = models.AclGroup.objects.all()
208 entry_class = Acl
209
210
jamesren3981f442010-02-16 19:27:59 +0000211class UserAclMembership(resource_lib.Relationship):
212 related_classes = {'user': User, 'acl': Acl}
213
214 # TODO: check permissions
215 # TODO: check for and add/remove "Everyone"
showardf828c772010-01-25 21:49:42 +0000216
217
jamesren3981f442010-02-16 19:27:59 +0000218class UserAclMembershipCollection(resource_lib.RelationshipCollection):
219 entry_class = UserAclMembership
showardf828c772010-01-25 21:49:42 +0000220
221
222class Host(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +0000223 model = models.Host
224
225 @classmethod
226 def add_query_selectors(cls, query_processor):
227 query_processor.add_field_selector('hostname')
228 query_processor.add_field_selector(
229 'locked', value_transform=query_processor.read_boolean)
230 query_processor.add_field_selector(
showardf828c772010-01-25 21:49:42 +0000231 'locked_by', field='locked_by__login',
232 doc='Username of user who locked this host, if locked')
jamesren3981f442010-02-16 19:27:59 +0000233 query_processor.add_field_selector('status')
234 query_processor.add_field_selector(
235 'protection_level', field='protection',
236 doc='Verify/repair protection level',
237 value_transform=cls._read_protection)
238 query_processor.add_field_selector(
239 'accessible_by', field='aclgroup__users__login',
240 doc='Username of user with access to this host')
241 query_processor.add_related_existence_selector(
242 'has_label', models.Label, 'name')
showardf828c772010-01-25 21:49:42 +0000243
244
245 @classmethod
246 def _read_protection(cls, protection_input):
247 return host_protections.Protection.get_value(protection_input)
248
249
250 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000251 def from_uri_args(cls, request, hostname, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000252 return cls(request, models.Host.objects.get(hostname=hostname))
showardf828c772010-01-25 21:49:42 +0000253
254
255 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000256 return {'hostname': self.instance.hostname}
showardf828c772010-01-25 21:49:42 +0000257
258
259 def short_representation(self):
260 rep = super(Host, self).short_representation()
261 # TODO calling platform() over and over is inefficient
showardf46ad4c2010-02-03 20:28:59 +0000262 platform_rep = (Label.from_optional_instance(self._request,
263 self.instance.platform())
showardf828c772010-01-25 21:49:42 +0000264 .short_representation())
265 rep.update({'hostname': self.instance.hostname,
266 'locked': bool(self.instance.locked),
267 'status': self.instance.status,
268 'platform': platform_rep})
269 return rep
270
271
272 def full_representation(self):
273 rep = super(Host, self).full_representation()
274 protection = host_protections.Protection.get_string(
275 self.instance.protection)
showardf46ad4c2010-02-03 20:28:59 +0000276 locked_by = (User.from_optional_instance(self._request,
277 self.instance.locked_by)
showardf828c772010-01-25 21:49:42 +0000278 .short_representation())
jamesren3981f442010-02-16 19:27:59 +0000279 labels = HostLabelingCollection(fixed_entry=self)
280 acls = HostAclMembershipCollection(fixed_entry=self)
281 queue_entries = QueueEntryCollection(self._request)
282 queue_entries.set_query_parameters(host=self.instance.hostname)
283 health_tasks = HealthTaskCollection(self._request)
284 health_tasks.set_query_parameters(host=self.instance.hostname)
285
showardf828c772010-01-25 21:49:42 +0000286 rep.update({'locked_by': locked_by,
287 'locked_on': self._format_datetime(self.instance.lock_time),
288 'invalid': self.instance.invalid,
289 'protection_level': protection,
290 # TODO make these efficient
jamesren3981f442010-02-16 19:27:59 +0000291 'labels': labels.full_representation(),
292 'acls': acls.full_representation(),
293 'queue_entries': queue_entries.link(),
294 'health_tasks': health_tasks.link()})
showardf828c772010-01-25 21:49:42 +0000295 return rep
296
297
298 @classmethod
299 def create_instance(cls, input_dict, containing_collection):
300 cls._check_for_required_fields(input_dict, ('hostname',))
jamesren3981f442010-02-16 19:27:59 +0000301 # include locked here, rather than waiting for update(), to avoid race
302 # conditions
303 host = models.Host.add_object(hostname=input_dict['hostname'],
304 locked=input_dict.get('locked', False))
305 return host
showardf828c772010-01-25 21:49:42 +0000306
307 def update(self, input_dict):
308 data = {'locked': input_dict.get('locked'),
309 'protection': input_dict.get('protection_level')}
310 data = input_dict.remove_unspecified_fields(data)
311
312 if 'protection' in data:
313 data['protection'] = self._read_protection(data['protection'])
314
315 self.instance.update_object(**data)
316
317 if 'platform' in input_dict:
showardf46ad4c2010-02-03 20:28:59 +0000318 label = self.resolve_link(input_dict['platform']) .instance
showardf828c772010-01-25 21:49:42 +0000319 if not label.platform:
320 raise BadRequest('Label %s is not a platform' % label.name)
321 for label in self.instance.labels.filter(platform=True):
322 self.instance.labels.remove(label)
323 self.instance.labels.add(label)
324
325
326class HostCollection(resource_lib.Collection):
327 queryset = models.Host.valid_objects.all()
328 entry_class = Host
329
330
jamesren3981f442010-02-16 19:27:59 +0000331class HostLabeling(resource_lib.Relationship):
332 related_classes = {'host': Host, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000333
334
jamesren3981f442010-02-16 19:27:59 +0000335class HostLabelingCollection(resource_lib.RelationshipCollection):
336 entry_class = HostLabeling
showardf828c772010-01-25 21:49:42 +0000337
338
jamesren3981f442010-02-16 19:27:59 +0000339class HostAclMembership(resource_lib.Relationship):
340 related_classes = {'host': Host, 'acl': Acl}
341
342 # TODO: acl.check_for_acl_violation_acl_group()
343 # TODO: models.AclGroup.on_host_membership_change()
showardf828c772010-01-25 21:49:42 +0000344
345
jamesren3981f442010-02-16 19:27:59 +0000346class HostAclMembershipCollection(resource_lib.RelationshipCollection):
347 entry_class = HostAclMembership
showardf828c772010-01-25 21:49:42 +0000348
349
jamesren3981f442010-02-16 19:27:59 +0000350class Test(resource_lib.InstanceEntry):
351 model = models.Test
showardf828c772010-01-25 21:49:42 +0000352
353
showardf828c772010-01-25 21:49:42 +0000354 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000355 def from_uri_args(cls, request, test_name, **kwargs):
356 return cls(request, models.Test.objects.get(name=test_name))
showardf828c772010-01-25 21:49:42 +0000357
358
359 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000360 return {'test_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000361
362
363 def short_representation(self):
364 rep = super(Test, self).short_representation()
365 rep['name'] = self.instance.name
366 return rep
367
368
369 def full_representation(self):
370 rep = super(Test, self).full_representation()
371 rep.update({'author': self.instance.author,
372 'class': self.instance.test_class,
373 'control_file_type':
jamesrendd855242010-03-02 22:23:44 +0000374 model_attributes.TestTypes.get_string(
375 self.instance.test_type),
showardf828c772010-01-25 21:49:42 +0000376 'control_file_path': self.instance.path,
jamesren3981f442010-02-16 19:27:59 +0000377 'dependencies':
378 TestDependencyCollection(fixed_entry=self).link(),
showardf828c772010-01-25 21:49:42 +0000379 })
380 return rep
381
382
383 @classmethod
384 def create_instance(cls, input_dict, containing_collection):
385 cls._check_for_required_fields(input_dict,
386 ('name', 'control_file_type',
387 'control_file_path'))
jamesrendd855242010-03-02 22:23:44 +0000388 test_type = model_attributes.TestTypes.get_value(
389 input['control_file_type'])
showardf828c772010-01-25 21:49:42 +0000390 return models.Test.add_object(name=input_dict['name'],
391 test_type=test_type,
392 path=input_dict['control_file_path'])
393
394
395 def update(self, input_dict):
396 data = {'test_type': input_dict.get('control_file_type'),
397 'path': input_dict.get('control_file_path'),
398 'class': input_dict.get('class'),
399 }
400 data = input_dict.remove_unspecified_fields(data)
401 self.instance.update_object(**data)
402
403
404class TestCollection(resource_lib.Collection):
405 queryset = models.Test.objects.all()
406 entry_class = Test
407
408
jamesren3981f442010-02-16 19:27:59 +0000409class TestDependency(resource_lib.Relationship):
410 related_classes = {'test': Test, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000411
412
jamesren3981f442010-02-16 19:27:59 +0000413class TestDependencyCollection(resource_lib.RelationshipCollection):
414 entry_class = TestDependency
showardf828c772010-01-25 21:49:42 +0000415
416
417# TODO profilers
418
419
420class ExecutionInfo(resource_lib.Resource):
421 _permitted_methods = ('GET','POST')
422 _job_fields = models.Job.get_field_dict()
423 _DEFAULTS = {
424 'control_file': '',
425 'is_server': True,
426 'dependencies': [],
427 'machines_per_execution': 1,
428 'run_verify': bool(_job_fields['run_verify'].default),
429 'timeout_hrs': _job_fields['timeout'].default,
430 'maximum_runtime_hrs': _job_fields['max_runtime_hrs'].default,
431 'cleanup_before_job':
jamesrendd855242010-03-02 22:23:44 +0000432 model_attributes.RebootBefore.get_string(
433 models.DEFAULT_REBOOT_BEFORE),
showardf828c772010-01-25 21:49:42 +0000434 'cleanup_after_job':
jamesrendd855242010-03-02 22:23:44 +0000435 model_attributes.RebootAfter.get_string(
436 models.DEFAULT_REBOOT_AFTER),
showardf828c772010-01-25 21:49:42 +0000437 }
438
439
jamesren3981f442010-02-16 19:27:59 +0000440 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000441 return (('tests', 'Comma-separated list of test names to run'),
442 ('kernels', 'TODO'),
443 ('client_control_file',
444 'Client control file segment to run after all specified '
445 'tests'),
446 ('profilers',
447 'Comma-separated list of profilers to activate during the '
448 'job'),
449 ('use_container', 'TODO'),
450 ('profile_only',
451 'If true, run only profiled iterations; otherwise, always run '
452 'at least one non-profiled iteration in addition to a '
453 'profiled iteration'),
454 ('upload_kernel_config',
455 'If true, generate a server control file code that uploads '
456 'the kernel config file to the client and tells the client of '
457 'the new (local) path when compiling the kernel; the tests '
458 'must be server side tests'))
459
460
461 @classmethod
462 def execution_info_from_job(cls, job):
463 return {'control_file': job.control_file,
464 'is_server': job.control_type == models.Job.ControlType.SERVER,
465 'dependencies': [label.name for label
466 in job.dependency_labels.all()],
467 'machines_per_execution': job.synch_count,
468 'run_verify': bool(job.run_verify),
469 'timeout_hrs': job.timeout,
470 'maximum_runtime_hrs': job.max_runtime_hrs,
471 'cleanup_before_job':
jamesrendd855242010-03-02 22:23:44 +0000472 model_attributes.RebootBefore.get_string(job.reboot_before),
showardf828c772010-01-25 21:49:42 +0000473 'cleanup_after_job':
jamesrendd855242010-03-02 22:23:44 +0000474 model_attributes.RebootAfter.get_string(job.reboot_after),
showardf828c772010-01-25 21:49:42 +0000475 }
476
477
478 def _get_execution_info(self, input_dict):
479 tests = input_dict.get('tests', '')
480 client_control_file = input_dict.get('client_control_file', None)
481 if not tests and not client_control_file:
482 return self._DEFAULTS
483
484 test_list = tests.split(',')
485 if 'profilers' in input_dict:
486 profilers_list = input_dict['profilers'].split(',')
487 else:
488 profilers_list = []
489 kernels = input_dict.get('kernels', '') # TODO
490 if kernels:
491 kernels = [dict(version=kernel) for kernel in kernels.split(',')]
492
493 cf_info, test_objects, profiler_objects, label = (
494 rpc_utils.prepare_generate_control_file(
495 test_list, kernels, None, profilers_list))
496 control_file_contents = control_file.generate_control(
497 tests=test_objects, kernels=kernels,
498 profilers=profiler_objects, is_server=cf_info['is_server'],
499 client_control_file=client_control_file,
500 profile_only=input_dict.get('profile_only', None),
501 upload_kernel_config=input_dict.get(
502 'upload_kernel_config', None))
503 return dict(self._DEFAULTS,
504 control_file=control_file_contents,
505 is_server=cf_info['is_server'],
506 dependencies=cf_info['dependencies'],
507 machines_per_execution=cf_info['synch_count'])
508
509
showardf46ad4c2010-02-03 20:28:59 +0000510 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000511 result = self.link()
showardf46ad4c2010-02-03 20:28:59 +0000512 result['execution_info'] = self._get_execution_info(
513 self._request.REQUEST)
showardf828c772010-01-25 21:49:42 +0000514 return self._basic_response(result)
515
516
517class QueueEntriesRequest(resource_lib.Resource):
518 _permitted_methods = ('GET',)
519
520
jamesren3981f442010-02-16 19:27:59 +0000521 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000522 return (('hosts', 'Comma-separated list of hostnames'),
523 ('one_time_hosts',
524 'Comma-separated list of hostnames not already in the '
525 'Autotest system'),
526 ('meta_hosts',
527 'Comma-separated list of label names; for each one, an entry '
528 'will be created and assigned at runtime to an available host '
529 'with that label'),
530 ('atomic_group_class', 'TODO'))
531
532
533 def _read_list(self, list_string):
534 if list_string:
535 return list_string.split(',')
536 return []
537
538
showardf46ad4c2010-02-03 20:28:59 +0000539 def handle_request(self):
540 request_dict = self._request.REQUEST
showardf828c772010-01-25 21:49:42 +0000541 hosts = self._read_list(request_dict.get('hosts'))
542 one_time_hosts = self._read_list(request_dict.get('one_time_hosts'))
543 meta_hosts = self._read_list(request_dict.get('meta_hosts'))
544 atomic_group_class = request_dict.get('atomic_group_class')
545
546 # TODO: bring in all the atomic groups magic from create_job()
547
548 entries = []
549 for hostname in one_time_hosts:
550 models.Host.create_one_time_host(hostname)
551 for hostname in hosts:
showardf46ad4c2010-02-03 20:28:59 +0000552 entry = Host.from_uri_args(self._request, hostname)
showardf828c772010-01-25 21:49:42 +0000553 entries.append({'host': entry.link()})
554 for label_name in meta_hosts:
showardf46ad4c2010-02-03 20:28:59 +0000555 entry = Label.from_uri_args(self._request, label_name)
showardf828c772010-01-25 21:49:42 +0000556 entries.append({'meta_host': entry.link()})
jamesren3e9f6092010-03-11 21:32:10 +0000557 if atomic_group_class:
558 entries.append({'atomic_group_class': atomic_group_class})
showardf828c772010-01-25 21:49:42 +0000559
560 result = self.link()
561 result['queue_entries'] = entries
562 return self._basic_response(result)
563
564
jamesren3981f442010-02-16 19:27:59 +0000565class Job(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000566 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000567 model = models.Job
568
showardf828c772010-01-25 21:49:42 +0000569
570 class _StatusConstraint(query_lib.Constraint):
jamesren3981f442010-02-16 19:27:59 +0000571 def apply_constraint(self, queryset, value, comparison_type,
572 is_inverse):
showardf828c772010-01-25 21:49:42 +0000573 if comparison_type != 'equals' or is_inverse:
574 raise query_lib.ConstraintError('Can only use this selector '
575 'with equals')
576 non_queued_statuses = [
577 status for status, _
578 in models.HostQueueEntry.Status.choices()
579 if status != models.HostQueueEntry.Status.QUEUED]
580 if value == 'queued':
581 return queryset.exclude(
582 hostqueueentry__status__in=non_queued_statuses)
583 elif value == 'active':
584 return queryset.filter(
585 hostqueueentry__status__in=non_queued_statuses).filter(
586 hostqueueentry__complete=False).distinct()
587 elif value == 'complete':
588 return queryset.exclude(hostqueueentry__complete=False)
589 else:
590 raise query_lib.ConstraintError('Value must be one of queued, '
591 'active or complete')
592
593
jamesren3981f442010-02-16 19:27:59 +0000594 @classmethod
595 def add_query_selectors(cls, query_processor):
596 query_processor.add_field_selector('id')
jamesrenc3940222010-02-19 21:57:37 +0000597 query_processor.add_field_selector('name')
jamesren3981f442010-02-16 19:27:59 +0000598 query_processor.add_selector(
599 query_lib.Selector('status',
600 doc='One of queued, active or complete'),
601 Job._StatusConstraint())
showardf828c772010-01-25 21:49:42 +0000602
603
604 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000605 def from_uri_args(cls, request, job_id, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000606 return cls(request, models.Job.objects.get(id=job_id))
showardf828c772010-01-25 21:49:42 +0000607
608
609 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000610 return {'job_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000611
612
613 def short_representation(self):
614 rep = super(Job, self).short_representation()
615 rep.update({'id': self.instance.id,
616 'owner': self.instance.owner,
617 'name': self.instance.name,
618 'priority':
619 models.Job.Priority.get_string(self.instance.priority),
620 'created_on':
621 self._format_datetime(self.instance.created_on),
622 })
623 return rep
624
625
626 def full_representation(self):
627 rep = super(Job, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000628 queue_entries = QueueEntryCollection(self._request)
629 queue_entries.set_query_parameters(job=self.instance.id)
showardf828c772010-01-25 21:49:42 +0000630 rep.update({'email_list': self.instance.email_list,
631 'parse_failed_repair':
632 bool(self.instance.parse_failed_repair),
633 'execution_info':
634 ExecutionInfo.execution_info_from_job(self.instance),
jamesren3981f442010-02-16 19:27:59 +0000635 'queue_entries': queue_entries.link(),
showardf828c772010-01-25 21:49:42 +0000636 })
637 return rep
638
639
640 @classmethod
641 def create_instance(cls, input_dict, containing_collection):
642 cls._check_for_required_fields(input_dict, ('name', 'execution_info',
643 'queue_entries'))
644 execution_info = input_dict['execution_info']
645 cls._check_for_required_fields(execution_info, ('control_file',
646 'is_server'))
647
648 if execution_info['is_server']:
649 control_type = models.Job.ControlType.SERVER
650 else:
651 control_type = models.Job.ControlType.CLIENT
652 options = dict(
653 name=input_dict['name'],
654 priority=input_dict.get('priority', None),
655 control_file=execution_info['control_file'],
656 control_type=control_type,
657 is_template=input_dict.get('is_template', None),
658 timeout=execution_info.get('timeout_hrs'),
659 max_runtime_hrs=execution_info.get('maximum_runtime_hrs'),
660 synch_count=execution_info.get('machines_per_execution'),
661 run_verify=execution_info.get('run_verify'),
662 email_list=input_dict.get('email_list', None),
663 dependencies=execution_info.get('dependencies', ()),
664 reboot_before=execution_info.get('cleanup_before_job'),
665 reboot_after=execution_info.get('cleanup_after_job'),
666 parse_failed_repair=input_dict.get('parse_failed_repair', None),
667 keyvals=input_dict.get('keyvals', None))
668
669 host_objects, metahost_label_objects, atomic_group = [], [], None
670 for queue_entry in input_dict['queue_entries']:
671 if 'host' in queue_entry:
672 host = queue_entry['host']
673 if host: # can be None, indicated a hostless job
showardf46ad4c2010-02-03 20:28:59 +0000674 host_entry = containing_collection.resolve_link(host)
showardf828c772010-01-25 21:49:42 +0000675 host_objects.append(host_entry.instance)
676 elif 'meta_host' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000677 label_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000678 queue_entry['meta_host'])
679 metahost_label_objects.append(label_entry.instance)
jamesren3e9f6092010-03-11 21:32:10 +0000680 if 'atomic_group_class' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000681 atomic_group_entry = containing_collection.resolve_link(
jamesren3e9f6092010-03-11 21:32:10 +0000682 queue_entry['atomic_group_class'])
showardf828c772010-01-25 21:49:42 +0000683 if atomic_group:
684 assert atomic_group_entry.instance.id == atomic_group.id
685 else:
686 atomic_group = atomic_group_entry.instance
687
688 job_id = rpc_utils.create_new_job(
689 owner=models.User.current_user().login,
690 options=options,
691 host_objects=host_objects,
692 metahost_objects=metahost_label_objects,
693 atomic_group=atomic_group)
694 return models.Job.objects.get(id=job_id)
695
696
697 def update(self, input_dict):
698 # Required for POST, doesn't actually support PUT
699 pass
700
701
702class JobCollection(resource_lib.Collection):
703 queryset = models.Job.objects.order_by('-id')
704 entry_class = Job
705
706
jamesren3981f442010-02-16 19:27:59 +0000707class QueueEntry(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000708 _permitted_methods = ('GET', 'PUT')
jamesren3981f442010-02-16 19:27:59 +0000709 model = models.HostQueueEntry
710
showardf828c772010-01-25 21:49:42 +0000711
712 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000713 def add_query_selectors(cls, query_processor):
714 query_processor.add_field_selector('host', field='host__hostname')
715 query_processor.add_field_selector('job', field='job__id')
716
717
718 @classmethod
719 def from_uri_args(cls, request, queue_entry_id):
showardf828c772010-01-25 21:49:42 +0000720 instance = models.HostQueueEntry.objects.get(id=queue_entry_id)
showardf46ad4c2010-02-03 20:28:59 +0000721 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000722
723
724 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000725 return {'queue_entry_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000726
727
728 def short_representation(self):
729 rep = super(QueueEntry, self).short_representation()
730 if self.instance.host:
showardf46ad4c2010-02-03 20:28:59 +0000731 host = (Host(self._request, self.instance.host)
732 .short_representation())
showardf828c772010-01-25 21:49:42 +0000733 else:
734 host = None
showardf46ad4c2010-02-03 20:28:59 +0000735 job = Job(self._request, self.instance.job)
736 host = Host.from_optional_instance(self._request, self.instance.host)
737 label = Label.from_optional_instance(self._request,
738 self.instance.meta_host)
739 atomic_group_class = AtomicGroupClass.from_optional_instance(
740 self._request, self.instance.atomic_group)
showardf828c772010-01-25 21:49:42 +0000741 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000742 {'job': job.short_representation(),
743 'host': host.short_representation(),
744 'label': label.short_representation(),
showardf828c772010-01-25 21:49:42 +0000745 'atomic_group_class':
showardf46ad4c2010-02-03 20:28:59 +0000746 atomic_group_class.short_representation(),
showardf828c772010-01-25 21:49:42 +0000747 'status': self.instance.status,
748 'execution_path': self.instance.execution_subdir,
749 'started_on': self._format_datetime(self.instance.started_on),
750 'aborted': bool(self.instance.aborted)})
751 return rep
752
753
754 def update(self, input_dict):
755 if 'aborted' in input_dict:
756 if input_dict['aborted'] != True:
757 raise BadRequest('"aborted" can only be set to true')
758 query = models.HostQueueEntry.objects.filter(pk=self.instance.pk)
759 models.AclGroup.check_abort_permissions(query)
760 rpc_utils.check_abort_synchronous_jobs(query)
761 self.instance.abort(thread_local.get_user())
762
763
jamesren3981f442010-02-16 19:27:59 +0000764class QueueEntryCollection(resource_lib.Collection):
765 queryset = models.HostQueueEntry.objects.order_by('-id')
766 entry_class = QueueEntry
767
768
769class HealthTask(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000770 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000771 model = models.SpecialTask
772
showardf828c772010-01-25 21:49:42 +0000773
774 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000775 def add_query_selectors(cls, query_processor):
776 query_processor.add_field_selector('host', field='host__hostname')
777
778
779 @classmethod
780 def from_uri_args(cls, request, task_id):
showardf828c772010-01-25 21:49:42 +0000781 instance = models.SpecialTask.objects.get(id=task_id)
showardf46ad4c2010-02-03 20:28:59 +0000782 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000783
784
785 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000786 return {'task_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000787
788
789 def short_representation(self):
790 rep = super(HealthTask, self).short_representation()
showardf46ad4c2010-02-03 20:28:59 +0000791 host = Host(self._request, self.instance.host)
792 queue_entry = QueueEntry.from_optional_instance(
793 self._request, self.instance.queue_entry)
showardf828c772010-01-25 21:49:42 +0000794 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000795 {'host': host.short_representation(),
showardf828c772010-01-25 21:49:42 +0000796 'task_type': self.instance.task,
797 'started_on':
798 self._format_datetime(self.instance.time_started),
799 'status': self.instance.status,
showardf46ad4c2010-02-03 20:28:59 +0000800 'queue_entry': queue_entry.short_representation()
showardf828c772010-01-25 21:49:42 +0000801 })
802 return rep
803
804
805 @classmethod
806 def create_instance(cls, input_dict, containing_collection):
807 cls._check_for_required_fields(input_dict, ('task_type',))
808 host = containing_collection.base_entry.instance
809 models.AclGroup.check_for_acl_violation_hosts((host,))
810 return models.SpecialTask.schedule_special_task(host,
811 input_dict['task_type'])
812
813
814 def update(self, input_dict):
815 # Required for POST, doesn't actually support PUT
816 pass
817
818
jamesren3981f442010-02-16 19:27:59 +0000819class HealthTaskCollection(resource_lib.Collection):
820 entry_class = HealthTask
821
822
823 def _fresh_queryset(self):
824 return models.SpecialTask.objects.order_by('-id')
825
826
showardf828c772010-01-25 21:49:42 +0000827class ResourceDirectory(resource_lib.Resource):
828 _permitted_methods = ('GET',)
829
showardf46ad4c2010-02-03 20:28:59 +0000830 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000831 result = self.link()
832 result.update({
showardf46ad4c2010-02-03 20:28:59 +0000833 'atomic_group_classes':
834 AtomicGroupClassCollection(self._request).link(),
835 'labels': LabelCollection(self._request).link(),
836 'users': UserCollection(self._request).link(),
837 'acl_groups': AclCollection(self._request).link(),
838 'hosts': HostCollection(self._request).link(),
839 'tests': TestCollection(self._request).link(),
840 'execution_info': ExecutionInfo(self._request).link(),
841 'queue_entries_request':
842 QueueEntriesRequest(self._request).link(),
843 'jobs': JobCollection(self._request).link(),
showardf828c772010-01-25 21:49:42 +0000844 })
845 return self._basic_response(result)