blob: 835cc83c820287df13e0dbf483450986f0531389 [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
4from autotest_lib.frontend import thread_local
5from autotest_lib.client.common_lib import host_protections
6
jamesren3981f442010-02-16 19:27:59 +00007class EntryWithInvalid(resource_lib.InstanceEntry):
showardf46ad4c2010-02-03 20:28:59 +00008 def put(self):
showardf828c772010-01-25 21:49:42 +00009 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000010 raise http.Http404('%s has been deleted' % self.instance)
11 return super(EntryWithInvalid, self).put()
showardf828c772010-01-25 21:49:42 +000012
13
showardf46ad4c2010-02-03 20:28:59 +000014 def delete(self):
showardf828c772010-01-25 21:49:42 +000015 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000016 raise http.Http404('%s has already been deleted' % self.instance)
17 return super(EntryWithInvalid, self).delete()
showardf828c772010-01-25 21:49:42 +000018
19
20class AtomicGroupClass(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000021 model = models.AtomicGroup
22
23
showardf828c772010-01-25 21:49:42 +000024 @classmethod
jamesren3981f442010-02-16 19:27:59 +000025 def from_uri_args(cls, request, ag_name, **kwargs):
26 return cls(request, models.AtomicGroup.objects.get(name=ag_name))
showardf828c772010-01-25 21:49:42 +000027
28
29 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000030 return {'ag_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000031
32
33 def short_representation(self):
34 rep = super(AtomicGroupClass, self).short_representation()
35 rep['name'] = self.instance.name
36 return rep
37
38
39 def full_representation(self):
40 rep = super(AtomicGroupClass, self).full_representation()
41 rep.update({'max_number_of_machines':
42 self.instance.max_number_of_machines,
jamesren3981f442010-02-16 19:27:59 +000043 'labels':
44 AtomicLabelTaggingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +000045 return rep
46
47
48 @classmethod
49 def create_instance(cls, input_dict, containing_collection):
50 cls._check_for_required_fields(input_dict, ('name',))
51 return models.AtomicGroup.add_object(name=input_dict['name'])
52
53
54 def update(self, input_dict):
55 data = {'max_number_of_machines':
56 input_dict.get('max_number_of_machines')}
57 data = input_dict.remove_unspecified_fields(data)
58 self.instance.update_object(**data)
59
60
61class AtomicGroupClassCollection(resource_lib.Collection):
62 queryset = models.AtomicGroup.valid_objects.all()
63 entry_class = AtomicGroupClass
64
65
showardf828c772010-01-25 21:49:42 +000066class Label(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000067 model = models.Label
68
69 @classmethod
70 def add_query_selectors(cls, query_processor):
71 query_processor.add_field_selector('name')
72 query_processor.add_field_selector(
73 'is_platform', field='platform',
74 value_transform=query_processor.read_boolean)
showardf828c772010-01-25 21:49:42 +000075
76
77 @classmethod
jamesren3981f442010-02-16 19:27:59 +000078 def from_uri_args(cls, request, label_name, **kwargs):
79 return cls(request, models.Label.objects.get(name=label_name))
showardf828c772010-01-25 21:49:42 +000080
81
82 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000083 return {'label_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000084
85
86 def short_representation(self):
87 rep = super(Label, self).short_representation()
88 rep.update({'name': self.instance.name,
89 'is_platform': bool(self.instance.platform)})
90 return rep
91
92
93 def full_representation(self):
94 rep = super(Label, self).full_representation()
showardf46ad4c2010-02-03 20:28:59 +000095 atomic_group_class = AtomicGroupClass.from_optional_instance(
96 self._request, self.instance.atomic_group)
97 rep.update({'atomic_group_class':
98 atomic_group_class.short_representation(),
jamesren3981f442010-02-16 19:27:59 +000099 'hosts': HostLabelingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000100 return rep
101
102
103 @classmethod
104 def create_instance(cls, input_dict, containing_collection):
105 cls._check_for_required_fields(input_dict, ('name',))
106 return models.Label.add_object(name=input_dict['name'])
107
108
109 def update(self, input_dict):
110 # TODO update atomic group
111 raise NotImplementedError
112
113
114class LabelCollection(resource_lib.Collection):
115 queryset = models.Label.valid_objects.all()
116 entry_class = Label
117
118
jamesren3981f442010-02-16 19:27:59 +0000119class AtomicLabelTagging(resource_lib.Relationship):
120 related_classes = {'label': Label, 'atomic_group_class': AtomicGroupClass}
showardf828c772010-01-25 21:49:42 +0000121
122
jamesren3981f442010-02-16 19:27:59 +0000123class AtomicLabelTaggingCollection(resource_lib.RelationshipCollection):
124 entry_class = AtomicLabelTagging
showardf828c772010-01-25 21:49:42 +0000125
126
jamesren3981f442010-02-16 19:27:59 +0000127class User(resource_lib.InstanceEntry):
128 model = models.User
showardf828c772010-01-25 21:49:42 +0000129 _permitted_methods = ('GET,')
130
131
132 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000133 def from_uri_args(cls, request, username, **kwargs):
showardf828c772010-01-25 21:49:42 +0000134 if username == '@me':
showardf46ad4c2010-02-03 20:28:59 +0000135 username = models.User.current_user().login
136 return cls(request, models.User.objects.get(login=username))
showardf828c772010-01-25 21:49:42 +0000137
138
139 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000140 return {'username': self.instance.login}
showardf828c772010-01-25 21:49:42 +0000141
142
143 def short_representation(self):
144 rep = super(User, self).short_representation()
145 rep['username'] = self.instance.login
146 return rep
147
148
149 def full_representation(self):
150 rep = super(User, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000151 accessible_hosts = HostCollection(self._request)
152 accessible_hosts.set_query_parameters(accessible_by=self.instance.login)
showardf828c772010-01-25 21:49:42 +0000153 rep.update({'jobs': 'TODO',
154 'recurring_runs': 'TODO',
jamesren3981f442010-02-16 19:27:59 +0000155 'acls':
156 UserAclMembershipCollection(fixed_entry=self).link(),
157 'accessible_hosts': accessible_hosts.link()})
showardf828c772010-01-25 21:49:42 +0000158 return rep
159
160
161class UserCollection(resource_lib.Collection):
162 _permitted_methods = ('GET',)
163 queryset = models.User.objects.all()
164 entry_class = User
165
166
jamesren3981f442010-02-16 19:27:59 +0000167class Acl(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000168 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000169 model = models.AclGroup
showardf828c772010-01-25 21:49:42 +0000170
171 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000172 def from_uri_args(cls, request, acl_name, **kwargs):
173 return cls(request, models.AclGroup.objects.get(name=acl_name))
showardf828c772010-01-25 21:49:42 +0000174
175
176 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000177 return {'acl_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000178
179
180 def short_representation(self):
181 rep = super(Acl, self).short_representation()
182 rep['name'] = self.instance.name
183 return rep
184
185
186 def full_representation(self):
187 rep = super(Acl, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000188 rep.update({'users':
189 UserAclMembershipCollection(fixed_entry=self).link(),
190 'hosts':
191 HostAclMembershipCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000192 return rep
193
194
195 @classmethod
196 def create_instance(cls, input_dict, containing_collection):
197 cls._check_for_required_fields(input_dict, ('name',))
198 return models.AclGroup.add_object(name=input_dict['name'])
199
200
201 def update(self, input_dict):
202 pass
203
204
205class AclCollection(resource_lib.Collection):
206 queryset = models.AclGroup.objects.all()
207 entry_class = Acl
208
209
jamesren3981f442010-02-16 19:27:59 +0000210class UserAclMembership(resource_lib.Relationship):
211 related_classes = {'user': User, 'acl': Acl}
212
213 # TODO: check permissions
214 # TODO: check for and add/remove "Everyone"
showardf828c772010-01-25 21:49:42 +0000215
216
jamesren3981f442010-02-16 19:27:59 +0000217class UserAclMembershipCollection(resource_lib.RelationshipCollection):
218 entry_class = UserAclMembership
showardf828c772010-01-25 21:49:42 +0000219
220
221class Host(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +0000222 model = models.Host
223
224 @classmethod
225 def add_query_selectors(cls, query_processor):
226 query_processor.add_field_selector('hostname')
227 query_processor.add_field_selector(
228 'locked', value_transform=query_processor.read_boolean)
229 query_processor.add_field_selector(
showardf828c772010-01-25 21:49:42 +0000230 'locked_by', field='locked_by__login',
231 doc='Username of user who locked this host, if locked')
jamesren3981f442010-02-16 19:27:59 +0000232 query_processor.add_field_selector('status')
233 query_processor.add_field_selector(
234 'protection_level', field='protection',
235 doc='Verify/repair protection level',
236 value_transform=cls._read_protection)
237 query_processor.add_field_selector(
238 'accessible_by', field='aclgroup__users__login',
239 doc='Username of user with access to this host')
240 query_processor.add_related_existence_selector(
241 'has_label', models.Label, 'name')
showardf828c772010-01-25 21:49:42 +0000242
243
244 @classmethod
245 def _read_protection(cls, protection_input):
246 return host_protections.Protection.get_value(protection_input)
247
248
249 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000250 def from_uri_args(cls, request, hostname, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000251 return cls(request, models.Host.objects.get(hostname=hostname))
showardf828c772010-01-25 21:49:42 +0000252
253
254 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000255 return {'hostname': self.instance.hostname}
showardf828c772010-01-25 21:49:42 +0000256
257
258 def short_representation(self):
259 rep = super(Host, self).short_representation()
260 # TODO calling platform() over and over is inefficient
showardf46ad4c2010-02-03 20:28:59 +0000261 platform_rep = (Label.from_optional_instance(self._request,
262 self.instance.platform())
showardf828c772010-01-25 21:49:42 +0000263 .short_representation())
264 rep.update({'hostname': self.instance.hostname,
265 'locked': bool(self.instance.locked),
266 'status': self.instance.status,
267 'platform': platform_rep})
268 return rep
269
270
271 def full_representation(self):
272 rep = super(Host, self).full_representation()
273 protection = host_protections.Protection.get_string(
274 self.instance.protection)
showardf46ad4c2010-02-03 20:28:59 +0000275 locked_by = (User.from_optional_instance(self._request,
276 self.instance.locked_by)
showardf828c772010-01-25 21:49:42 +0000277 .short_representation())
jamesren3981f442010-02-16 19:27:59 +0000278 labels = HostLabelingCollection(fixed_entry=self)
279 acls = HostAclMembershipCollection(fixed_entry=self)
280 queue_entries = QueueEntryCollection(self._request)
281 queue_entries.set_query_parameters(host=self.instance.hostname)
282 health_tasks = HealthTaskCollection(self._request)
283 health_tasks.set_query_parameters(host=self.instance.hostname)
284
showardf828c772010-01-25 21:49:42 +0000285 rep.update({'locked_by': locked_by,
286 'locked_on': self._format_datetime(self.instance.lock_time),
287 'invalid': self.instance.invalid,
288 'protection_level': protection,
289 # TODO make these efficient
jamesren3981f442010-02-16 19:27:59 +0000290 'labels': labels.full_representation(),
291 'acls': acls.full_representation(),
292 'queue_entries': queue_entries.link(),
293 'health_tasks': health_tasks.link()})
showardf828c772010-01-25 21:49:42 +0000294 return rep
295
296
297 @classmethod
298 def create_instance(cls, input_dict, containing_collection):
299 cls._check_for_required_fields(input_dict, ('hostname',))
jamesren3981f442010-02-16 19:27:59 +0000300 # include locked here, rather than waiting for update(), to avoid race
301 # conditions
302 host = models.Host.add_object(hostname=input_dict['hostname'],
303 locked=input_dict.get('locked', False))
304 return host
showardf828c772010-01-25 21:49:42 +0000305
306 def update(self, input_dict):
307 data = {'locked': input_dict.get('locked'),
308 'protection': input_dict.get('protection_level')}
309 data = input_dict.remove_unspecified_fields(data)
310
311 if 'protection' in data:
312 data['protection'] = self._read_protection(data['protection'])
313
314 self.instance.update_object(**data)
315
316 if 'platform' in input_dict:
showardf46ad4c2010-02-03 20:28:59 +0000317 label = self.resolve_link(input_dict['platform']) .instance
showardf828c772010-01-25 21:49:42 +0000318 if not label.platform:
319 raise BadRequest('Label %s is not a platform' % label.name)
320 for label in self.instance.labels.filter(platform=True):
321 self.instance.labels.remove(label)
322 self.instance.labels.add(label)
323
324
325class HostCollection(resource_lib.Collection):
326 queryset = models.Host.valid_objects.all()
327 entry_class = Host
328
329
jamesren3981f442010-02-16 19:27:59 +0000330class HostLabeling(resource_lib.Relationship):
331 related_classes = {'host': Host, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000332
333
jamesren3981f442010-02-16 19:27:59 +0000334class HostLabelingCollection(resource_lib.RelationshipCollection):
335 entry_class = HostLabeling
showardf828c772010-01-25 21:49:42 +0000336
337
jamesren3981f442010-02-16 19:27:59 +0000338class HostAclMembership(resource_lib.Relationship):
339 related_classes = {'host': Host, 'acl': Acl}
340
341 # TODO: acl.check_for_acl_violation_acl_group()
342 # TODO: models.AclGroup.on_host_membership_change()
showardf828c772010-01-25 21:49:42 +0000343
344
jamesren3981f442010-02-16 19:27:59 +0000345class HostAclMembershipCollection(resource_lib.RelationshipCollection):
346 entry_class = HostAclMembership
showardf828c772010-01-25 21:49:42 +0000347
348
jamesren3981f442010-02-16 19:27:59 +0000349class Test(resource_lib.InstanceEntry):
350 model = models.Test
showardf828c772010-01-25 21:49:42 +0000351
352
showardf828c772010-01-25 21:49:42 +0000353 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000354 def from_uri_args(cls, request, test_name, **kwargs):
355 return cls(request, models.Test.objects.get(name=test_name))
showardf828c772010-01-25 21:49:42 +0000356
357
358 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000359 return {'test_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000360
361
362 def short_representation(self):
363 rep = super(Test, self).short_representation()
364 rep['name'] = self.instance.name
365 return rep
366
367
368 def full_representation(self):
369 rep = super(Test, self).full_representation()
370 rep.update({'author': self.instance.author,
371 'class': self.instance.test_class,
372 'control_file_type':
373 models.Test.Types.get_string(self.instance.test_type),
374 'control_file_path': self.instance.path,
jamesren3981f442010-02-16 19:27:59 +0000375 'dependencies':
376 TestDependencyCollection(fixed_entry=self).link(),
showardf828c772010-01-25 21:49:42 +0000377 })
378 return rep
379
380
381 @classmethod
382 def create_instance(cls, input_dict, containing_collection):
383 cls._check_for_required_fields(input_dict,
384 ('name', 'control_file_type',
385 'control_file_path'))
386 test_type = models.Test.Type.get_value(input['control_file_type'])
387 return models.Test.add_object(name=input_dict['name'],
388 test_type=test_type,
389 path=input_dict['control_file_path'])
390
391
392 def update(self, input_dict):
393 data = {'test_type': input_dict.get('control_file_type'),
394 'path': input_dict.get('control_file_path'),
395 'class': input_dict.get('class'),
396 }
397 data = input_dict.remove_unspecified_fields(data)
398 self.instance.update_object(**data)
399
400
401class TestCollection(resource_lib.Collection):
402 queryset = models.Test.objects.all()
403 entry_class = Test
404
405
jamesren3981f442010-02-16 19:27:59 +0000406class TestDependency(resource_lib.Relationship):
407 related_classes = {'test': Test, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000408
409
jamesren3981f442010-02-16 19:27:59 +0000410class TestDependencyCollection(resource_lib.RelationshipCollection):
411 entry_class = TestDependency
showardf828c772010-01-25 21:49:42 +0000412
413
414# TODO profilers
415
416
417class ExecutionInfo(resource_lib.Resource):
418 _permitted_methods = ('GET','POST')
419 _job_fields = models.Job.get_field_dict()
420 _DEFAULTS = {
421 'control_file': '',
422 'is_server': True,
423 'dependencies': [],
424 'machines_per_execution': 1,
425 'run_verify': bool(_job_fields['run_verify'].default),
426 'timeout_hrs': _job_fields['timeout'].default,
427 'maximum_runtime_hrs': _job_fields['max_runtime_hrs'].default,
428 'cleanup_before_job':
429 models.RebootBefore.get_string(models.DEFAULT_REBOOT_BEFORE),
430 'cleanup_after_job':
431 models.RebootAfter.get_string(models.DEFAULT_REBOOT_AFTER),
432 }
433
434
jamesren3981f442010-02-16 19:27:59 +0000435 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000436 return (('tests', 'Comma-separated list of test names to run'),
437 ('kernels', 'TODO'),
438 ('client_control_file',
439 'Client control file segment to run after all specified '
440 'tests'),
441 ('profilers',
442 'Comma-separated list of profilers to activate during the '
443 'job'),
444 ('use_container', 'TODO'),
445 ('profile_only',
446 'If true, run only profiled iterations; otherwise, always run '
447 'at least one non-profiled iteration in addition to a '
448 'profiled iteration'),
449 ('upload_kernel_config',
450 'If true, generate a server control file code that uploads '
451 'the kernel config file to the client and tells the client of '
452 'the new (local) path when compiling the kernel; the tests '
453 'must be server side tests'))
454
455
456 @classmethod
457 def execution_info_from_job(cls, job):
458 return {'control_file': job.control_file,
459 'is_server': job.control_type == models.Job.ControlType.SERVER,
460 'dependencies': [label.name for label
461 in job.dependency_labels.all()],
462 'machines_per_execution': job.synch_count,
463 'run_verify': bool(job.run_verify),
464 'timeout_hrs': job.timeout,
465 'maximum_runtime_hrs': job.max_runtime_hrs,
466 'cleanup_before_job':
467 models.RebootBefore.get_string(job.reboot_before),
468 'cleanup_after_job':
469 models.RebootAfter.get_string(job.reboot_after),
470 }
471
472
473 def _get_execution_info(self, input_dict):
474 tests = input_dict.get('tests', '')
475 client_control_file = input_dict.get('client_control_file', None)
476 if not tests and not client_control_file:
477 return self._DEFAULTS
478
479 test_list = tests.split(',')
480 if 'profilers' in input_dict:
481 profilers_list = input_dict['profilers'].split(',')
482 else:
483 profilers_list = []
484 kernels = input_dict.get('kernels', '') # TODO
485 if kernels:
486 kernels = [dict(version=kernel) for kernel in kernels.split(',')]
487
488 cf_info, test_objects, profiler_objects, label = (
489 rpc_utils.prepare_generate_control_file(
490 test_list, kernels, None, profilers_list))
491 control_file_contents = control_file.generate_control(
492 tests=test_objects, kernels=kernels,
493 profilers=profiler_objects, is_server=cf_info['is_server'],
494 client_control_file=client_control_file,
495 profile_only=input_dict.get('profile_only', None),
496 upload_kernel_config=input_dict.get(
497 'upload_kernel_config', None))
498 return dict(self._DEFAULTS,
499 control_file=control_file_contents,
500 is_server=cf_info['is_server'],
501 dependencies=cf_info['dependencies'],
502 machines_per_execution=cf_info['synch_count'])
503
504
showardf46ad4c2010-02-03 20:28:59 +0000505 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000506 result = self.link()
showardf46ad4c2010-02-03 20:28:59 +0000507 result['execution_info'] = self._get_execution_info(
508 self._request.REQUEST)
showardf828c772010-01-25 21:49:42 +0000509 return self._basic_response(result)
510
511
512class QueueEntriesRequest(resource_lib.Resource):
513 _permitted_methods = ('GET',)
514
515
jamesren3981f442010-02-16 19:27:59 +0000516 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000517 return (('hosts', 'Comma-separated list of hostnames'),
518 ('one_time_hosts',
519 'Comma-separated list of hostnames not already in the '
520 'Autotest system'),
521 ('meta_hosts',
522 'Comma-separated list of label names; for each one, an entry '
523 'will be created and assigned at runtime to an available host '
524 'with that label'),
525 ('atomic_group_class', 'TODO'))
526
527
528 def _read_list(self, list_string):
529 if list_string:
530 return list_string.split(',')
531 return []
532
533
showardf46ad4c2010-02-03 20:28:59 +0000534 def handle_request(self):
535 request_dict = self._request.REQUEST
showardf828c772010-01-25 21:49:42 +0000536 hosts = self._read_list(request_dict.get('hosts'))
537 one_time_hosts = self._read_list(request_dict.get('one_time_hosts'))
538 meta_hosts = self._read_list(request_dict.get('meta_hosts'))
539 atomic_group_class = request_dict.get('atomic_group_class')
540
541 # TODO: bring in all the atomic groups magic from create_job()
542
543 entries = []
544 for hostname in one_time_hosts:
545 models.Host.create_one_time_host(hostname)
546 for hostname in hosts:
showardf46ad4c2010-02-03 20:28:59 +0000547 entry = Host.from_uri_args(self._request, hostname)
showardf828c772010-01-25 21:49:42 +0000548 entries.append({'host': entry.link()})
549 for label_name in meta_hosts:
showardf46ad4c2010-02-03 20:28:59 +0000550 entry = Label.from_uri_args(self._request, label_name)
showardf828c772010-01-25 21:49:42 +0000551 entries.append({'meta_host': entry.link()})
552
553 result = self.link()
554 result['queue_entries'] = entries
555 return self._basic_response(result)
556
557
jamesren3981f442010-02-16 19:27:59 +0000558class Job(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000559 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000560 model = models.Job
561
showardf828c772010-01-25 21:49:42 +0000562
563 class _StatusConstraint(query_lib.Constraint):
jamesren3981f442010-02-16 19:27:59 +0000564 def apply_constraint(self, queryset, value, comparison_type,
565 is_inverse):
showardf828c772010-01-25 21:49:42 +0000566 if comparison_type != 'equals' or is_inverse:
567 raise query_lib.ConstraintError('Can only use this selector '
568 'with equals')
569 non_queued_statuses = [
570 status for status, _
571 in models.HostQueueEntry.Status.choices()
572 if status != models.HostQueueEntry.Status.QUEUED]
573 if value == 'queued':
574 return queryset.exclude(
575 hostqueueentry__status__in=non_queued_statuses)
576 elif value == 'active':
577 return queryset.filter(
578 hostqueueentry__status__in=non_queued_statuses).filter(
579 hostqueueentry__complete=False).distinct()
580 elif value == 'complete':
581 return queryset.exclude(hostqueueentry__complete=False)
582 else:
583 raise query_lib.ConstraintError('Value must be one of queued, '
584 'active or complete')
585
586
jamesren3981f442010-02-16 19:27:59 +0000587 @classmethod
588 def add_query_selectors(cls, query_processor):
589 query_processor.add_field_selector('id')
590 query_processor.add_selector(
591 query_lib.Selector('status',
592 doc='One of queued, active or complete'),
593 Job._StatusConstraint())
showardf828c772010-01-25 21:49:42 +0000594
595
596 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000597 def from_uri_args(cls, request, job_id, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000598 return cls(request, models.Job.objects.get(id=job_id))
showardf828c772010-01-25 21:49:42 +0000599
600
601 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000602 return {'job_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000603
604
605 def short_representation(self):
606 rep = super(Job, self).short_representation()
607 rep.update({'id': self.instance.id,
608 'owner': self.instance.owner,
609 'name': self.instance.name,
610 'priority':
611 models.Job.Priority.get_string(self.instance.priority),
612 'created_on':
613 self._format_datetime(self.instance.created_on),
614 })
615 return rep
616
617
618 def full_representation(self):
619 rep = super(Job, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000620 queue_entries = QueueEntryCollection(self._request)
621 queue_entries.set_query_parameters(job=self.instance.id)
showardf828c772010-01-25 21:49:42 +0000622 rep.update({'email_list': self.instance.email_list,
623 'parse_failed_repair':
624 bool(self.instance.parse_failed_repair),
625 'execution_info':
626 ExecutionInfo.execution_info_from_job(self.instance),
jamesren3981f442010-02-16 19:27:59 +0000627 'queue_entries': queue_entries.link(),
showardf828c772010-01-25 21:49:42 +0000628 })
629 return rep
630
631
632 @classmethod
633 def create_instance(cls, input_dict, containing_collection):
634 cls._check_for_required_fields(input_dict, ('name', 'execution_info',
635 'queue_entries'))
636 execution_info = input_dict['execution_info']
637 cls._check_for_required_fields(execution_info, ('control_file',
638 'is_server'))
639
640 if execution_info['is_server']:
641 control_type = models.Job.ControlType.SERVER
642 else:
643 control_type = models.Job.ControlType.CLIENT
644 options = dict(
645 name=input_dict['name'],
646 priority=input_dict.get('priority', None),
647 control_file=execution_info['control_file'],
648 control_type=control_type,
649 is_template=input_dict.get('is_template', None),
650 timeout=execution_info.get('timeout_hrs'),
651 max_runtime_hrs=execution_info.get('maximum_runtime_hrs'),
652 synch_count=execution_info.get('machines_per_execution'),
653 run_verify=execution_info.get('run_verify'),
654 email_list=input_dict.get('email_list', None),
655 dependencies=execution_info.get('dependencies', ()),
656 reboot_before=execution_info.get('cleanup_before_job'),
657 reboot_after=execution_info.get('cleanup_after_job'),
658 parse_failed_repair=input_dict.get('parse_failed_repair', None),
659 keyvals=input_dict.get('keyvals', None))
660
661 host_objects, metahost_label_objects, atomic_group = [], [], None
662 for queue_entry in input_dict['queue_entries']:
663 if 'host' in queue_entry:
664 host = queue_entry['host']
665 if host: # can be None, indicated a hostless job
showardf46ad4c2010-02-03 20:28:59 +0000666 host_entry = containing_collection.resolve_link(host)
showardf828c772010-01-25 21:49:42 +0000667 host_objects.append(host_entry.instance)
668 elif 'meta_host' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000669 label_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000670 queue_entry['meta_host'])
671 metahost_label_objects.append(label_entry.instance)
672 if 'atomic_group' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000673 atomic_group_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000674 queue_entry['atomic_group'])
675 if atomic_group:
676 assert atomic_group_entry.instance.id == atomic_group.id
677 else:
678 atomic_group = atomic_group_entry.instance
679
680 job_id = rpc_utils.create_new_job(
681 owner=models.User.current_user().login,
682 options=options,
683 host_objects=host_objects,
684 metahost_objects=metahost_label_objects,
685 atomic_group=atomic_group)
686 return models.Job.objects.get(id=job_id)
687
688
689 def update(self, input_dict):
690 # Required for POST, doesn't actually support PUT
691 pass
692
693
694class JobCollection(resource_lib.Collection):
695 queryset = models.Job.objects.order_by('-id')
696 entry_class = Job
697
698
jamesren3981f442010-02-16 19:27:59 +0000699class QueueEntry(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000700 _permitted_methods = ('GET', 'PUT')
jamesren3981f442010-02-16 19:27:59 +0000701 model = models.HostQueueEntry
702
showardf828c772010-01-25 21:49:42 +0000703
704 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000705 def add_query_selectors(cls, query_processor):
706 query_processor.add_field_selector('host', field='host__hostname')
707 query_processor.add_field_selector('job', field='job__id')
708
709
710 @classmethod
711 def from_uri_args(cls, request, queue_entry_id):
showardf828c772010-01-25 21:49:42 +0000712 instance = models.HostQueueEntry.objects.get(id=queue_entry_id)
showardf46ad4c2010-02-03 20:28:59 +0000713 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000714
715
716 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000717 return {'queue_entry_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000718
719
720 def short_representation(self):
721 rep = super(QueueEntry, self).short_representation()
722 if self.instance.host:
showardf46ad4c2010-02-03 20:28:59 +0000723 host = (Host(self._request, self.instance.host)
724 .short_representation())
showardf828c772010-01-25 21:49:42 +0000725 else:
726 host = None
showardf46ad4c2010-02-03 20:28:59 +0000727 job = Job(self._request, self.instance.job)
728 host = Host.from_optional_instance(self._request, self.instance.host)
729 label = Label.from_optional_instance(self._request,
730 self.instance.meta_host)
731 atomic_group_class = AtomicGroupClass.from_optional_instance(
732 self._request, self.instance.atomic_group)
showardf828c772010-01-25 21:49:42 +0000733 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000734 {'job': job.short_representation(),
735 'host': host.short_representation(),
736 'label': label.short_representation(),
showardf828c772010-01-25 21:49:42 +0000737 'atomic_group_class':
showardf46ad4c2010-02-03 20:28:59 +0000738 atomic_group_class.short_representation(),
showardf828c772010-01-25 21:49:42 +0000739 'status': self.instance.status,
740 'execution_path': self.instance.execution_subdir,
741 'started_on': self._format_datetime(self.instance.started_on),
742 'aborted': bool(self.instance.aborted)})
743 return rep
744
745
746 def update(self, input_dict):
747 if 'aborted' in input_dict:
748 if input_dict['aborted'] != True:
749 raise BadRequest('"aborted" can only be set to true')
750 query = models.HostQueueEntry.objects.filter(pk=self.instance.pk)
751 models.AclGroup.check_abort_permissions(query)
752 rpc_utils.check_abort_synchronous_jobs(query)
753 self.instance.abort(thread_local.get_user())
754
755
jamesren3981f442010-02-16 19:27:59 +0000756class QueueEntryCollection(resource_lib.Collection):
757 queryset = models.HostQueueEntry.objects.order_by('-id')
758 entry_class = QueueEntry
759
760
761class HealthTask(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000762 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000763 model = models.SpecialTask
764
showardf828c772010-01-25 21:49:42 +0000765
766 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000767 def add_query_selectors(cls, query_processor):
768 query_processor.add_field_selector('host', field='host__hostname')
769
770
771 @classmethod
772 def from_uri_args(cls, request, task_id):
showardf828c772010-01-25 21:49:42 +0000773 instance = models.SpecialTask.objects.get(id=task_id)
showardf46ad4c2010-02-03 20:28:59 +0000774 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000775
776
777 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000778 return {'task_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000779
780
781 def short_representation(self):
782 rep = super(HealthTask, self).short_representation()
showardf46ad4c2010-02-03 20:28:59 +0000783 host = Host(self._request, self.instance.host)
784 queue_entry = QueueEntry.from_optional_instance(
785 self._request, self.instance.queue_entry)
showardf828c772010-01-25 21:49:42 +0000786 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000787 {'host': host.short_representation(),
showardf828c772010-01-25 21:49:42 +0000788 'task_type': self.instance.task,
789 'started_on':
790 self._format_datetime(self.instance.time_started),
791 'status': self.instance.status,
showardf46ad4c2010-02-03 20:28:59 +0000792 'queue_entry': queue_entry.short_representation()
showardf828c772010-01-25 21:49:42 +0000793 })
794 return rep
795
796
797 @classmethod
798 def create_instance(cls, input_dict, containing_collection):
799 cls._check_for_required_fields(input_dict, ('task_type',))
800 host = containing_collection.base_entry.instance
801 models.AclGroup.check_for_acl_violation_hosts((host,))
802 return models.SpecialTask.schedule_special_task(host,
803 input_dict['task_type'])
804
805
806 def update(self, input_dict):
807 # Required for POST, doesn't actually support PUT
808 pass
809
810
jamesren3981f442010-02-16 19:27:59 +0000811class HealthTaskCollection(resource_lib.Collection):
812 entry_class = HealthTask
813
814
815 def _fresh_queryset(self):
816 return models.SpecialTask.objects.order_by('-id')
817
818
showardf828c772010-01-25 21:49:42 +0000819class ResourceDirectory(resource_lib.Resource):
820 _permitted_methods = ('GET',)
821
showardf46ad4c2010-02-03 20:28:59 +0000822 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000823 result = self.link()
824 result.update({
showardf46ad4c2010-02-03 20:28:59 +0000825 'atomic_group_classes':
826 AtomicGroupClassCollection(self._request).link(),
827 'labels': LabelCollection(self._request).link(),
828 'users': UserCollection(self._request).link(),
829 'acl_groups': AclCollection(self._request).link(),
830 'hosts': HostCollection(self._request).link(),
831 'tests': TestCollection(self._request).link(),
832 'execution_info': ExecutionInfo(self._request).link(),
833 'queue_entries_request':
834 QueueEntriesRequest(self._request).link(),
835 'jobs': JobCollection(self._request).link(),
showardf828c772010-01-25 21:49:42 +0000836 })
837 return self._basic_response(result)