blob: 52ac751d0d0349a87be29f42be1d600296ccf7e5 [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
7class EntryWithInvalid(resource_lib.Entry):
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):
21 @classmethod
showardf46ad4c2010-02-03 20:28:59 +000022 def from_uri_args(cls, request, name):
23 return cls(request, models.AtomicGroup.objects.get(name=name))
showardf828c772010-01-25 21:49:42 +000024
25
26 def _uri_args(self):
27 return (self.instance.name,), {}
28
29
30 def short_representation(self):
31 rep = super(AtomicGroupClass, self).short_representation()
32 rep['name'] = self.instance.name
33 return rep
34
35
36 def full_representation(self):
37 rep = super(AtomicGroupClass, self).full_representation()
38 rep.update({'max_number_of_machines':
39 self.instance.max_number_of_machines,
40 'labels': AtomicGroupLabels(self).link()})
41 return rep
42
43
44 @classmethod
45 def create_instance(cls, input_dict, containing_collection):
46 cls._check_for_required_fields(input_dict, ('name',))
47 return models.AtomicGroup.add_object(name=input_dict['name'])
48
49
50 def update(self, input_dict):
51 data = {'max_number_of_machines':
52 input_dict.get('max_number_of_machines')}
53 data = input_dict.remove_unspecified_fields(data)
54 self.instance.update_object(**data)
55
56
57class AtomicGroupClassCollection(resource_lib.Collection):
58 queryset = models.AtomicGroup.valid_objects.all()
59 entry_class = AtomicGroupClass
60
61
62class AtomicGroupLabels(resource_lib.Relationship):
63 base_entry_class = AtomicGroupClass
64 entry_class = 'autotest_lib.frontend.afe.resources.Label'
65
66 def _fresh_queryset(self):
67 return self.base_entry.instance.label_set.all()
68
69
70 def _update_relationship(self, related_instances):
71 self.base_entry.instance.label_set = related_instances
72
73
74class Label(EntryWithInvalid):
75 class QueryProcessor(query_lib.BaseQueryProcessor):
76 @classmethod
77 def _add_all_selectors(cls):
78 cls._add_field_selector('name')
79 cls._add_field_selector('is_platform', field='platform',
80 value_transform=cls.read_boolean)
81
82
83 @classmethod
showardf46ad4c2010-02-03 20:28:59 +000084 def from_uri_args(cls, request, name):
85 return cls(request, models.Label.objects.get(name=name))
showardf828c772010-01-25 21:49:42 +000086
87
88 def _uri_args(self):
89 return (self.instance.name,), {}
90
91
92 def short_representation(self):
93 rep = super(Label, self).short_representation()
94 rep.update({'name': self.instance.name,
95 'is_platform': bool(self.instance.platform)})
96 return rep
97
98
99 def full_representation(self):
100 rep = super(Label, self).full_representation()
showardf46ad4c2010-02-03 20:28:59 +0000101 atomic_group_class = AtomicGroupClass.from_optional_instance(
102 self._request, self.instance.atomic_group)
103 rep.update({'atomic_group_class':
104 atomic_group_class.short_representation(),
showardf828c772010-01-25 21:49:42 +0000105 'hosts': LabelHosts(self).link()})
106 return rep
107
108
109 @classmethod
110 def create_instance(cls, input_dict, containing_collection):
111 cls._check_for_required_fields(input_dict, ('name',))
112 return models.Label.add_object(name=input_dict['name'])
113
114
115 def update(self, input_dict):
116 # TODO update atomic group
117 raise NotImplementedError
118
119
120class LabelCollection(resource_lib.Collection):
121 queryset = models.Label.valid_objects.all()
122 entry_class = Label
123
124
125class LabelHosts(resource_lib.Relationship):
126 base_entry_class = Label
127 entry_class = 'autotest_lib.frontend.afe.resources.Host'
128
129
130 def _fresh_queryset(self):
131 return self.base_entry.instance.host_set.all()
132
133
134 def _update_relationship(self, related_instances):
135 self.base_entry.instance.host_set = related_instances
136
137
138class User(resource_lib.Entry):
139 _permitted_methods = ('GET,')
140
141
142 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000143 def from_uri_args(cls, request, username):
showardf828c772010-01-25 21:49:42 +0000144 if username == '@me':
showardf46ad4c2010-02-03 20:28:59 +0000145 username = models.User.current_user().login
146 return cls(request, models.User.objects.get(login=username))
showardf828c772010-01-25 21:49:42 +0000147
148
149 def _uri_args(self):
150 return (self.instance.login,), {}
151
152
153 def short_representation(self):
154 rep = super(User, self).short_representation()
155 rep['username'] = self.instance.login
156 return rep
157
158
159 def full_representation(self):
160 rep = super(User, self).full_representation()
161 rep.update({'jobs': 'TODO',
162 'recurring_runs': 'TODO',
163 'accessible_hosts': UserAccessibleHosts(self).link(),
164 'acls': UserAcls(self).link()})
165 return rep
166
167
168class UserCollection(resource_lib.Collection):
169 _permitted_methods = ('GET',)
170 queryset = models.User.objects.all()
171 entry_class = User
172
173
174class UserAcls(resource_lib.Relationship):
175 base_entry_class = User
176 entry_class = 'autotest_lib.frontend.afe.resources.Acl'
177
178
179 def _fresh_queryset(self):
180 return self.base_entry.instance.aclgroup_set.all()
181
182
183 def _update_relationship(self, related_instances):
184 # TODO check for and add/remove "everyone"
185 self.base_entry.instance.aclgroup_set = related_instances
186
187
188class UserAccessibleHosts(resource_lib.Relationship):
189 _permitted_methods = ('GET',)
190 base_entry_class = User
191 entry_class = 'autotest_lib.frontend.afe.resources.Host'
192
193
194 def _fresh_queryset(self):
195 return models.Host.objects.filter(
196 aclgroup__users=self.base_entry.instance)
197
198
199class Acl(resource_lib.Entry):
200 _permitted_methods = ('GET',)
201
202 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000203 def from_uri_args(cls, request, name):
204 return cls(request, models.AclGroup.objects.get(name=name))
showardf828c772010-01-25 21:49:42 +0000205
206
207 def _uri_args(self):
208 return (self.instance.name,), {}
209
210
211 def short_representation(self):
212 rep = super(Acl, self).short_representation()
213 rep['name'] = self.instance.name
214 return rep
215
216
217 def full_representation(self):
218 rep = super(Acl, self).full_representation()
219 rep.update({'users': AclUsers(self).link(),
220 'hosts': AclHosts(self).link()})
221 return rep
222
223
224 @classmethod
225 def create_instance(cls, input_dict, containing_collection):
226 cls._check_for_required_fields(input_dict, ('name',))
227 return models.AclGroup.add_object(name=input_dict['name'])
228
229
230 def update(self, input_dict):
231 pass
232
233
234class AclCollection(resource_lib.Collection):
235 queryset = models.AclGroup.objects.all()
236 entry_class = Acl
237
238
239class AclUsers(resource_lib.Relationship):
240 base_entry_class = Acl
241 entry_class = User
242
243
244 def _fresh_queryset(self):
245 return self.base_entry.instance.users.all()
246
247
248 def _update_relationship(self, related_instances):
249 self.base_entry.instance.users = related_instances
250
251
252class AclHosts(resource_lib.Relationship):
253 base_entry_class = Acl
254 entry_class = 'autotest_lib.frontend.afe.resources.Host'
255
256
257 def _fresh_queryset(self):
258 return self.base_entry.instance.hosts.all()
259
260
261 def _update_relationship(self, related_instances):
262 self.base_entry.instance.hosts = related_instances
263
264
265class Host(EntryWithInvalid):
266 class QueryProcessor(query_lib.BaseQueryProcessor):
267 @classmethod
268 def _add_all_selectors(cls):
269 cls._add_field_selector('hostname')
270 cls._add_field_selector('locked', value_transform=cls.read_boolean)
271 cls._add_field_selector(
272 'locked_by', field='locked_by__login',
273 doc='Username of user who locked this host, if locked')
274 cls._add_field_selector('status')
275 cls._add_field_selector('protection_level', field='protection',
276 doc='Verify/repair protection level',
277 value_transform=Host._read_protection)
278 cls._add_related_existence_selector('has_label', models.Label,
279 'name')
280
281
282 @classmethod
283 def _read_protection(cls, protection_input):
284 return host_protections.Protection.get_value(protection_input)
285
286
287 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000288 def from_uri_args(cls, request, hostname):
289 return cls(request, models.Host.objects.get(hostname=hostname))
showardf828c772010-01-25 21:49:42 +0000290
291
292 def _uri_args(self):
293 return (self.instance.hostname,), {}
294
295
296 def short_representation(self):
297 rep = super(Host, self).short_representation()
298 # TODO calling platform() over and over is inefficient
showardf46ad4c2010-02-03 20:28:59 +0000299 platform_rep = (Label.from_optional_instance(self._request,
300 self.instance.platform())
showardf828c772010-01-25 21:49:42 +0000301 .short_representation())
302 rep.update({'hostname': self.instance.hostname,
303 'locked': bool(self.instance.locked),
304 'status': self.instance.status,
305 'platform': platform_rep})
306 return rep
307
308
309 def full_representation(self):
310 rep = super(Host, self).full_representation()
311 protection = host_protections.Protection.get_string(
312 self.instance.protection)
showardf46ad4c2010-02-03 20:28:59 +0000313 locked_by = (User.from_optional_instance(self._request,
314 self.instance.locked_by)
showardf828c772010-01-25 21:49:42 +0000315 .short_representation())
316 rep.update({'locked_by': locked_by,
317 'locked_on': self._format_datetime(self.instance.lock_time),
318 'invalid': self.instance.invalid,
319 'protection_level': protection,
320 # TODO make these efficient
321 'labels': HostLabels(self).full_representation(),
322 'acls': HostAcls(self).full_representation(),
323 'queue_entries': HostQueueEntries(self).link(),
324 'health_tasks': HostHealthTasks(self).link()})
325 return rep
326
327
328 @classmethod
329 def create_instance(cls, input_dict, containing_collection):
330 cls._check_for_required_fields(input_dict, ('hostname',))
331
332 # always create locked, so we can set up ACLs safely
333 instance = models.Host.add_object(hostname=input_dict['hostname'],
334 locked=True)
335
336 if 'acls' in input_dict:
showardf46ad4c2010-02-03 20:28:59 +0000337 entry = Host(containing_collection._request, instance)
showardf828c772010-01-25 21:49:42 +0000338 HostAcls(entry).update(input_dict['acls'])
339
340 instance.locked = False # restore default
341 instance.save()
342 return instance
343
344
345 def update(self, input_dict):
346 data = {'locked': input_dict.get('locked'),
347 'protection': input_dict.get('protection_level')}
348 data = input_dict.remove_unspecified_fields(data)
349
350 if 'protection' in data:
351 data['protection'] = self._read_protection(data['protection'])
352
353 self.instance.update_object(**data)
354
355 if 'platform' in input_dict:
showardf46ad4c2010-02-03 20:28:59 +0000356 label = self.resolve_link(input_dict['platform']) .instance
showardf828c772010-01-25 21:49:42 +0000357 if not label.platform:
358 raise BadRequest('Label %s is not a platform' % label.name)
359 for label in self.instance.labels.filter(platform=True):
360 self.instance.labels.remove(label)
361 self.instance.labels.add(label)
362
363
364class HostCollection(resource_lib.Collection):
365 queryset = models.Host.valid_objects.all()
366 entry_class = Host
367
368
369class HostLabels(resource_lib.Relationship):
370 base_entry_class = Host
371 entry_class = Label
372
373
374 def _fresh_queryset(self):
375 return self.base_entry.instance.labels.all()
376
377
378 def _update_relationship(self, related_instances):
379 self.base_entry.instance.labels = related_instances
380
381
382class HostAcls(resource_lib.Relationship):
383 base_entry_class = Host
384 entry_class = Acl
385
386
387 def _fresh_queryset(self):
388 return self.base_entry.instance.aclgroup_set.all()
389
390
391 def _update_relationship(self, related_instances):
392 for acl in related_instances:
393 acl.check_for_acl_violation_acl_group()
394 self.base_entry.instance.aclgroup_set = related_instances
395 models.AclGroup.on_host_membership_change()
396
397
398class HostQueueEntries(resource_lib.Relationship):
399 _permitted_methods = ('GET',)
400 base_entry_class = Host
401 entry_class = 'autotest_lib.frontend.afe.resources.QueueEntry'
402
403
404 def _fresh_queryset(self):
405 return self.base_entry.instance.hostqueueentry_set.order_by('-id')
406
407
408class HostHealthTasks(resource_lib.Relationship):
409 _permitted_methods = ('GET', 'POST')
410 base_entry_class = Host
411 entry_class = 'autotest_lib.frontend.afe.resources.HealthTask'
412
413
414 def _fresh_queryset(self):
415 return self.base_entry.instance.specialtask_set.order_by('-id')
416
417
418class Test(resource_lib.Entry):
419 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000420 def from_uri_args(cls, request, name):
421 return cls(request, models.Test.objects.get(name=name))
showardf828c772010-01-25 21:49:42 +0000422
423
424 def _uri_args(self):
425 return (self.instance.name,), {}
426
427
428 def short_representation(self):
429 rep = super(Test, self).short_representation()
430 rep['name'] = self.instance.name
431 return rep
432
433
434 def full_representation(self):
435 rep = super(Test, self).full_representation()
436 rep.update({'author': self.instance.author,
437 'class': self.instance.test_class,
438 'control_file_type':
439 models.Test.Types.get_string(self.instance.test_type),
440 'control_file_path': self.instance.path,
441 'dependencies': TestDependencies(self).link(),
442 })
443 return rep
444
445
446 @classmethod
447 def create_instance(cls, input_dict, containing_collection):
448 cls._check_for_required_fields(input_dict,
449 ('name', 'control_file_type',
450 'control_file_path'))
451 test_type = models.Test.Type.get_value(input['control_file_type'])
452 return models.Test.add_object(name=input_dict['name'],
453 test_type=test_type,
454 path=input_dict['control_file_path'])
455
456
457 def update(self, input_dict):
458 data = {'test_type': input_dict.get('control_file_type'),
459 'path': input_dict.get('control_file_path'),
460 'class': input_dict.get('class'),
461 }
462 data = input_dict.remove_unspecified_fields(data)
463 self.instance.update_object(**data)
464
465
466class TestCollection(resource_lib.Collection):
467 queryset = models.Test.objects.all()
468 entry_class = Test
469
470
471class TestDependencies(resource_lib.Relationship):
472 base_entry_class = Test
473 entry_class = Label
474
475 def _fresh_queryset(self):
476 return self.base_entry.instance.dependency_labels.all()
477
478
479 def _update_relationship(self, related_instances):
480 self.base_entry.instance.dependency_labels = related_instances
481
482
483# TODO profilers
484
485
486class ExecutionInfo(resource_lib.Resource):
487 _permitted_methods = ('GET','POST')
488 _job_fields = models.Job.get_field_dict()
489 _DEFAULTS = {
490 'control_file': '',
491 'is_server': True,
492 'dependencies': [],
493 'machines_per_execution': 1,
494 'run_verify': bool(_job_fields['run_verify'].default),
495 'timeout_hrs': _job_fields['timeout'].default,
496 'maximum_runtime_hrs': _job_fields['max_runtime_hrs'].default,
497 'cleanup_before_job':
498 models.RebootBefore.get_string(models.DEFAULT_REBOOT_BEFORE),
499 'cleanup_after_job':
500 models.RebootAfter.get_string(models.DEFAULT_REBOOT_AFTER),
501 }
502
503
504 def _query_parameters(self):
505 return (('tests', 'Comma-separated list of test names to run'),
506 ('kernels', 'TODO'),
507 ('client_control_file',
508 'Client control file segment to run after all specified '
509 'tests'),
510 ('profilers',
511 'Comma-separated list of profilers to activate during the '
512 'job'),
513 ('use_container', 'TODO'),
514 ('profile_only',
515 'If true, run only profiled iterations; otherwise, always run '
516 'at least one non-profiled iteration in addition to a '
517 'profiled iteration'),
518 ('upload_kernel_config',
519 'If true, generate a server control file code that uploads '
520 'the kernel config file to the client and tells the client of '
521 'the new (local) path when compiling the kernel; the tests '
522 'must be server side tests'))
523
524
525 @classmethod
526 def execution_info_from_job(cls, job):
527 return {'control_file': job.control_file,
528 'is_server': job.control_type == models.Job.ControlType.SERVER,
529 'dependencies': [label.name for label
530 in job.dependency_labels.all()],
531 'machines_per_execution': job.synch_count,
532 'run_verify': bool(job.run_verify),
533 'timeout_hrs': job.timeout,
534 'maximum_runtime_hrs': job.max_runtime_hrs,
535 'cleanup_before_job':
536 models.RebootBefore.get_string(job.reboot_before),
537 'cleanup_after_job':
538 models.RebootAfter.get_string(job.reboot_after),
539 }
540
541
542 def _get_execution_info(self, input_dict):
543 tests = input_dict.get('tests', '')
544 client_control_file = input_dict.get('client_control_file', None)
545 if not tests and not client_control_file:
546 return self._DEFAULTS
547
548 test_list = tests.split(',')
549 if 'profilers' in input_dict:
550 profilers_list = input_dict['profilers'].split(',')
551 else:
552 profilers_list = []
553 kernels = input_dict.get('kernels', '') # TODO
554 if kernels:
555 kernels = [dict(version=kernel) for kernel in kernels.split(',')]
556
557 cf_info, test_objects, profiler_objects, label = (
558 rpc_utils.prepare_generate_control_file(
559 test_list, kernels, None, profilers_list))
560 control_file_contents = control_file.generate_control(
561 tests=test_objects, kernels=kernels,
562 profilers=profiler_objects, is_server=cf_info['is_server'],
563 client_control_file=client_control_file,
564 profile_only=input_dict.get('profile_only', None),
565 upload_kernel_config=input_dict.get(
566 'upload_kernel_config', None))
567 return dict(self._DEFAULTS,
568 control_file=control_file_contents,
569 is_server=cf_info['is_server'],
570 dependencies=cf_info['dependencies'],
571 machines_per_execution=cf_info['synch_count'])
572
573
showardf46ad4c2010-02-03 20:28:59 +0000574 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000575 result = self.link()
showardf46ad4c2010-02-03 20:28:59 +0000576 result['execution_info'] = self._get_execution_info(
577 self._request.REQUEST)
showardf828c772010-01-25 21:49:42 +0000578 return self._basic_response(result)
579
580
581class QueueEntriesRequest(resource_lib.Resource):
582 _permitted_methods = ('GET',)
583
584
585 def _query_parameters(self):
586 return (('hosts', 'Comma-separated list of hostnames'),
587 ('one_time_hosts',
588 'Comma-separated list of hostnames not already in the '
589 'Autotest system'),
590 ('meta_hosts',
591 'Comma-separated list of label names; for each one, an entry '
592 'will be created and assigned at runtime to an available host '
593 'with that label'),
594 ('atomic_group_class', 'TODO'))
595
596
597 def _read_list(self, list_string):
598 if list_string:
599 return list_string.split(',')
600 return []
601
602
showardf46ad4c2010-02-03 20:28:59 +0000603 def handle_request(self):
604 request_dict = self._request.REQUEST
showardf828c772010-01-25 21:49:42 +0000605 hosts = self._read_list(request_dict.get('hosts'))
606 one_time_hosts = self._read_list(request_dict.get('one_time_hosts'))
607 meta_hosts = self._read_list(request_dict.get('meta_hosts'))
608 atomic_group_class = request_dict.get('atomic_group_class')
609
610 # TODO: bring in all the atomic groups magic from create_job()
611
612 entries = []
613 for hostname in one_time_hosts:
614 models.Host.create_one_time_host(hostname)
615 for hostname in hosts:
showardf46ad4c2010-02-03 20:28:59 +0000616 entry = Host.from_uri_args(self._request, hostname)
showardf828c772010-01-25 21:49:42 +0000617 entries.append({'host': entry.link()})
618 for label_name in meta_hosts:
showardf46ad4c2010-02-03 20:28:59 +0000619 entry = Label.from_uri_args(self._request, label_name)
showardf828c772010-01-25 21:49:42 +0000620 entries.append({'meta_host': entry.link()})
621
622 result = self.link()
623 result['queue_entries'] = entries
624 return self._basic_response(result)
625
626
627class Job(resource_lib.Entry):
628 _permitted_methods = ('GET',)
629
630 class _StatusConstraint(query_lib.Constraint):
631 @classmethod
632 def apply_constraint(cls, queryset, value, comparison_type, is_inverse):
633 if comparison_type != 'equals' or is_inverse:
634 raise query_lib.ConstraintError('Can only use this selector '
635 'with equals')
636 non_queued_statuses = [
637 status for status, _
638 in models.HostQueueEntry.Status.choices()
639 if status != models.HostQueueEntry.Status.QUEUED]
640 if value == 'queued':
641 return queryset.exclude(
642 hostqueueentry__status__in=non_queued_statuses)
643 elif value == 'active':
644 return queryset.filter(
645 hostqueueentry__status__in=non_queued_statuses).filter(
646 hostqueueentry__complete=False).distinct()
647 elif value == 'complete':
648 return queryset.exclude(hostqueueentry__complete=False)
649 else:
650 raise query_lib.ConstraintError('Value must be one of queued, '
651 'active or complete')
652
653
654 class QueryProcessor(query_lib.BaseQueryProcessor):
655 @classmethod
656 def _add_all_selectors(cls):
657 cls._add_field_selector('id')
658 cls._add_selector(
659 query_lib.Selector('status',
660 doc='One of queued, active or complete'),
661 Job._StatusConstraint())
662
663
664 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000665 def from_uri_args(cls, request, job_id):
666 return cls(request, models.Job.objects.get(id=job_id))
showardf828c772010-01-25 21:49:42 +0000667
668
669 def _uri_args(self):
670 return (self.instance.id,), {}
671
672
673 def short_representation(self):
674 rep = super(Job, self).short_representation()
675 rep.update({'id': self.instance.id,
676 'owner': self.instance.owner,
677 'name': self.instance.name,
678 'priority':
679 models.Job.Priority.get_string(self.instance.priority),
680 'created_on':
681 self._format_datetime(self.instance.created_on),
682 })
683 return rep
684
685
686 def full_representation(self):
687 rep = super(Job, self).full_representation()
688 rep.update({'email_list': self.instance.email_list,
689 'parse_failed_repair':
690 bool(self.instance.parse_failed_repair),
691 'execution_info':
692 ExecutionInfo.execution_info_from_job(self.instance),
693 'queue_entries': JobQueueEntries(self).link(),
694 })
695 return rep
696
697
698 @classmethod
699 def create_instance(cls, input_dict, containing_collection):
700 cls._check_for_required_fields(input_dict, ('name', 'execution_info',
701 'queue_entries'))
702 execution_info = input_dict['execution_info']
703 cls._check_for_required_fields(execution_info, ('control_file',
704 'is_server'))
705
706 if execution_info['is_server']:
707 control_type = models.Job.ControlType.SERVER
708 else:
709 control_type = models.Job.ControlType.CLIENT
710 options = dict(
711 name=input_dict['name'],
712 priority=input_dict.get('priority', None),
713 control_file=execution_info['control_file'],
714 control_type=control_type,
715 is_template=input_dict.get('is_template', None),
716 timeout=execution_info.get('timeout_hrs'),
717 max_runtime_hrs=execution_info.get('maximum_runtime_hrs'),
718 synch_count=execution_info.get('machines_per_execution'),
719 run_verify=execution_info.get('run_verify'),
720 email_list=input_dict.get('email_list', None),
721 dependencies=execution_info.get('dependencies', ()),
722 reboot_before=execution_info.get('cleanup_before_job'),
723 reboot_after=execution_info.get('cleanup_after_job'),
724 parse_failed_repair=input_dict.get('parse_failed_repair', None),
725 keyvals=input_dict.get('keyvals', None))
726
727 host_objects, metahost_label_objects, atomic_group = [], [], None
728 for queue_entry in input_dict['queue_entries']:
729 if 'host' in queue_entry:
730 host = queue_entry['host']
731 if host: # can be None, indicated a hostless job
showardf46ad4c2010-02-03 20:28:59 +0000732 host_entry = containing_collection.resolve_link(host)
showardf828c772010-01-25 21:49:42 +0000733 host_objects.append(host_entry.instance)
734 elif 'meta_host' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000735 label_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000736 queue_entry['meta_host'])
737 metahost_label_objects.append(label_entry.instance)
738 if 'atomic_group' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000739 atomic_group_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000740 queue_entry['atomic_group'])
741 if atomic_group:
742 assert atomic_group_entry.instance.id == atomic_group.id
743 else:
744 atomic_group = atomic_group_entry.instance
745
746 job_id = rpc_utils.create_new_job(
747 owner=models.User.current_user().login,
748 options=options,
749 host_objects=host_objects,
750 metahost_objects=metahost_label_objects,
751 atomic_group=atomic_group)
752 return models.Job.objects.get(id=job_id)
753
754
755 def update(self, input_dict):
756 # Required for POST, doesn't actually support PUT
757 pass
758
759
760class JobCollection(resource_lib.Collection):
761 queryset = models.Job.objects.order_by('-id')
762 entry_class = Job
763
764
765class JobQueueEntries(resource_lib.Relationship):
766 _permitted_methods = ('GET',)
767
768 base_entry_class = Job
769 entry_class = 'autotest_lib.frontend.afe.resources.QueueEntry'
770
771 def _fresh_queryset(self):
772 return self.base_entry.instance.hostqueueentry_set.all()
773
774
775class QueueEntry(resource_lib.Entry):
776 _permitted_methods = ('GET', 'PUT')
777
778 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000779 def from_uri_args(cls, request, job_id, queue_entry_id):
showardf828c772010-01-25 21:49:42 +0000780 instance = models.HostQueueEntry.objects.get(id=queue_entry_id)
781 if instance.job.id != int(job_id):
782 raise http.Http404('Incorrect job ID %r (expected %r)'
783 % (job_id, instance.job.id))
showardf46ad4c2010-02-03 20:28:59 +0000784 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000785
786
787 def _uri_args(self):
788 return (self.instance.job_id, self.instance.id), {}
789
790
791 def short_representation(self):
792 rep = super(QueueEntry, self).short_representation()
793 if self.instance.host:
showardf46ad4c2010-02-03 20:28:59 +0000794 host = (Host(self._request, self.instance.host)
795 .short_representation())
showardf828c772010-01-25 21:49:42 +0000796 else:
797 host = None
showardf46ad4c2010-02-03 20:28:59 +0000798 job = Job(self._request, self.instance.job)
799 host = Host.from_optional_instance(self._request, self.instance.host)
800 label = Label.from_optional_instance(self._request,
801 self.instance.meta_host)
802 atomic_group_class = AtomicGroupClass.from_optional_instance(
803 self._request, self.instance.atomic_group)
showardf828c772010-01-25 21:49:42 +0000804 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000805 {'job': job.short_representation(),
806 'host': host.short_representation(),
807 'label': label.short_representation(),
showardf828c772010-01-25 21:49:42 +0000808 'atomic_group_class':
showardf46ad4c2010-02-03 20:28:59 +0000809 atomic_group_class.short_representation(),
showardf828c772010-01-25 21:49:42 +0000810 'status': self.instance.status,
811 'execution_path': self.instance.execution_subdir,
812 'started_on': self._format_datetime(self.instance.started_on),
813 'aborted': bool(self.instance.aborted)})
814 return rep
815
816
817 def update(self, input_dict):
818 if 'aborted' in input_dict:
819 if input_dict['aborted'] != True:
820 raise BadRequest('"aborted" can only be set to true')
821 query = models.HostQueueEntry.objects.filter(pk=self.instance.pk)
822 models.AclGroup.check_abort_permissions(query)
823 rpc_utils.check_abort_synchronous_jobs(query)
824 self.instance.abort(thread_local.get_user())
825
826
827class HealthTask(resource_lib.Entry):
828 _permitted_methods = ('GET',)
829
830 @classmethod
showardf46ad4c2010-02-03 20:28:59 +0000831 def from_uri_args(cls, request, hostname, task_id):
showardf828c772010-01-25 21:49:42 +0000832 instance = models.SpecialTask.objects.get(id=task_id)
833 if instance.host.hostname != hostname:
834 raise http.Http404('Incorrect hostname %r (expected %r)'
835 % (hostname, instance.host.hostname))
showardf46ad4c2010-02-03 20:28:59 +0000836 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000837
838
839 def _uri_args(self):
840 return (self.instance.host.hostname, self.instance.id), {}
841
842
843 def short_representation(self):
844 rep = super(HealthTask, self).short_representation()
showardf46ad4c2010-02-03 20:28:59 +0000845 host = Host(self._request, self.instance.host)
846 queue_entry = QueueEntry.from_optional_instance(
847 self._request, self.instance.queue_entry)
showardf828c772010-01-25 21:49:42 +0000848 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000849 {'host': host.short_representation(),
showardf828c772010-01-25 21:49:42 +0000850 'task_type': self.instance.task,
851 'started_on':
852 self._format_datetime(self.instance.time_started),
853 'status': self.instance.status,
showardf46ad4c2010-02-03 20:28:59 +0000854 'queue_entry': queue_entry.short_representation()
showardf828c772010-01-25 21:49:42 +0000855 })
856 return rep
857
858
859 @classmethod
860 def create_instance(cls, input_dict, containing_collection):
861 cls._check_for_required_fields(input_dict, ('task_type',))
862 host = containing_collection.base_entry.instance
863 models.AclGroup.check_for_acl_violation_hosts((host,))
864 return models.SpecialTask.schedule_special_task(host,
865 input_dict['task_type'])
866
867
868 def update(self, input_dict):
869 # Required for POST, doesn't actually support PUT
870 pass
871
872
873class ResourceDirectory(resource_lib.Resource):
874 _permitted_methods = ('GET',)
875
showardf46ad4c2010-02-03 20:28:59 +0000876 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000877 result = self.link()
878 result.update({
showardf46ad4c2010-02-03 20:28:59 +0000879 'atomic_group_classes':
880 AtomicGroupClassCollection(self._request).link(),
881 'labels': LabelCollection(self._request).link(),
882 'users': UserCollection(self._request).link(),
883 'acl_groups': AclCollection(self._request).link(),
884 'hosts': HostCollection(self._request).link(),
885 'tests': TestCollection(self._request).link(),
886 'execution_info': ExecutionInfo(self._request).link(),
887 'queue_entries_request':
888 QueueEntriesRequest(self._request).link(),
889 'jobs': JobCollection(self._request).link(),
showardf828c772010-01-25 21:49:42 +0000890 })
891 return self._basic_response(result)