blob: 04171b1559da6b8a2605c05e0e19c8c7f364fb13 [file] [log] [blame]
showardf828c772010-01-25 21:49:42 +00001from django import http
Eric Li0a993912011-05-17 12:56:25 -07002from autotest_lib.frontend.shared import query_lib, resource_lib, exceptions
showardf828c772010-01-25 21:49:42 +00003from autotest_lib.frontend.afe import control_file, models, rpc_utils
jamesrendd855242010-03-02 22:23:44 +00004from autotest_lib.frontend.afe import model_attributes
showardf828c772010-01-25 21:49:42 +00005from autotest_lib.frontend import thread_local
6from autotest_lib.client.common_lib import host_protections
Aviv Keshet3dd8beb2013-05-13 17:36:04 -07007from autotest_lib.client.common_lib import control_data
showardf828c772010-01-25 21:49:42 +00008
Eric Li0a993912011-05-17 12:56:25 -07009
jamesren3981f442010-02-16 19:27:59 +000010class EntryWithInvalid(resource_lib.InstanceEntry):
showardf46ad4c2010-02-03 20:28:59 +000011 def put(self):
showardf828c772010-01-25 21:49:42 +000012 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000013 raise http.Http404('%s has been deleted' % self.instance)
14 return super(EntryWithInvalid, self).put()
showardf828c772010-01-25 21:49:42 +000015
16
showardf46ad4c2010-02-03 20:28:59 +000017 def delete(self):
showardf828c772010-01-25 21:49:42 +000018 if self.instance.invalid:
showardf46ad4c2010-02-03 20:28:59 +000019 raise http.Http404('%s has already been deleted' % self.instance)
20 return super(EntryWithInvalid, self).delete()
showardf828c772010-01-25 21:49:42 +000021
22
23class AtomicGroupClass(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000024 model = models.AtomicGroup
25
26
showardf828c772010-01-25 21:49:42 +000027 @classmethod
jamesren3981f442010-02-16 19:27:59 +000028 def from_uri_args(cls, request, ag_name, **kwargs):
29 return cls(request, models.AtomicGroup.objects.get(name=ag_name))
showardf828c772010-01-25 21:49:42 +000030
31
32 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000033 return {'ag_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000034
35
36 def short_representation(self):
37 rep = super(AtomicGroupClass, self).short_representation()
38 rep['name'] = self.instance.name
39 return rep
40
41
42 def full_representation(self):
43 rep = super(AtomicGroupClass, self).full_representation()
44 rep.update({'max_number_of_machines':
45 self.instance.max_number_of_machines,
jamesren3981f442010-02-16 19:27:59 +000046 'labels':
47 AtomicLabelTaggingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +000048 return rep
49
50
51 @classmethod
52 def create_instance(cls, input_dict, containing_collection):
53 cls._check_for_required_fields(input_dict, ('name',))
54 return models.AtomicGroup.add_object(name=input_dict['name'])
55
56
57 def update(self, input_dict):
58 data = {'max_number_of_machines':
59 input_dict.get('max_number_of_machines')}
60 data = input_dict.remove_unspecified_fields(data)
61 self.instance.update_object(**data)
62
63
64class AtomicGroupClassCollection(resource_lib.Collection):
65 queryset = models.AtomicGroup.valid_objects.all()
66 entry_class = AtomicGroupClass
67
68
showardf828c772010-01-25 21:49:42 +000069class Label(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +000070 model = models.Label
71
72 @classmethod
73 def add_query_selectors(cls, query_processor):
74 query_processor.add_field_selector('name')
75 query_processor.add_field_selector(
76 'is_platform', field='platform',
77 value_transform=query_processor.read_boolean)
showardf828c772010-01-25 21:49:42 +000078
79
80 @classmethod
jamesren3981f442010-02-16 19:27:59 +000081 def from_uri_args(cls, request, label_name, **kwargs):
82 return cls(request, models.Label.objects.get(name=label_name))
showardf828c772010-01-25 21:49:42 +000083
84
85 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +000086 return {'label_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +000087
88
89 def short_representation(self):
90 rep = super(Label, self).short_representation()
91 rep.update({'name': self.instance.name,
92 'is_platform': bool(self.instance.platform)})
93 return rep
94
95
96 def full_representation(self):
97 rep = super(Label, self).full_representation()
showardf46ad4c2010-02-03 20:28:59 +000098 atomic_group_class = AtomicGroupClass.from_optional_instance(
99 self._request, self.instance.atomic_group)
100 rep.update({'atomic_group_class':
101 atomic_group_class.short_representation(),
jamesren3981f442010-02-16 19:27:59 +0000102 'hosts': HostLabelingCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000103 return rep
104
105
106 @classmethod
107 def create_instance(cls, input_dict, containing_collection):
108 cls._check_for_required_fields(input_dict, ('name',))
109 return models.Label.add_object(name=input_dict['name'])
110
111
112 def update(self, input_dict):
113 # TODO update atomic group
jamesrencd7a81a2010-04-21 20:39:08 +0000114 if 'is_platform' in input_dict:
115 self.instance.platform = input_dict['is_platform']
116 self.instance.save()
showardf828c772010-01-25 21:49:42 +0000117
118
119class LabelCollection(resource_lib.Collection):
120 queryset = models.Label.valid_objects.all()
121 entry_class = Label
122
123
jamesren3981f442010-02-16 19:27:59 +0000124class AtomicLabelTagging(resource_lib.Relationship):
125 related_classes = {'label': Label, 'atomic_group_class': AtomicGroupClass}
showardf828c772010-01-25 21:49:42 +0000126
127
jamesren3981f442010-02-16 19:27:59 +0000128class AtomicLabelTaggingCollection(resource_lib.RelationshipCollection):
129 entry_class = AtomicLabelTagging
showardf828c772010-01-25 21:49:42 +0000130
131
jamesren3981f442010-02-16 19:27:59 +0000132class User(resource_lib.InstanceEntry):
133 model = models.User
showardf828c772010-01-25 21:49:42 +0000134 _permitted_methods = ('GET,')
135
136
137 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000138 def from_uri_args(cls, request, username, **kwargs):
showardf828c772010-01-25 21:49:42 +0000139 if username == '@me':
showardf46ad4c2010-02-03 20:28:59 +0000140 username = models.User.current_user().login
141 return cls(request, models.User.objects.get(login=username))
showardf828c772010-01-25 21:49:42 +0000142
143
144 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000145 return {'username': self.instance.login}
showardf828c772010-01-25 21:49:42 +0000146
147
148 def short_representation(self):
149 rep = super(User, self).short_representation()
150 rep['username'] = self.instance.login
151 return rep
152
153
154 def full_representation(self):
155 rep = super(User, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000156 accessible_hosts = HostCollection(self._request)
157 accessible_hosts.set_query_parameters(accessible_by=self.instance.login)
showardf828c772010-01-25 21:49:42 +0000158 rep.update({'jobs': 'TODO',
159 'recurring_runs': 'TODO',
jamesren3981f442010-02-16 19:27:59 +0000160 'acls':
161 UserAclMembershipCollection(fixed_entry=self).link(),
162 'accessible_hosts': accessible_hosts.link()})
showardf828c772010-01-25 21:49:42 +0000163 return rep
164
165
166class UserCollection(resource_lib.Collection):
167 _permitted_methods = ('GET',)
168 queryset = models.User.objects.all()
169 entry_class = User
170
171
jamesren3981f442010-02-16 19:27:59 +0000172class Acl(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000173 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000174 model = models.AclGroup
showardf828c772010-01-25 21:49:42 +0000175
176 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000177 def from_uri_args(cls, request, acl_name, **kwargs):
178 return cls(request, models.AclGroup.objects.get(name=acl_name))
showardf828c772010-01-25 21:49:42 +0000179
180
181 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000182 return {'acl_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000183
184
185 def short_representation(self):
186 rep = super(Acl, self).short_representation()
187 rep['name'] = self.instance.name
188 return rep
189
190
191 def full_representation(self):
192 rep = super(Acl, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000193 rep.update({'users':
194 UserAclMembershipCollection(fixed_entry=self).link(),
195 'hosts':
196 HostAclMembershipCollection(fixed_entry=self).link()})
showardf828c772010-01-25 21:49:42 +0000197 return rep
198
199
200 @classmethod
201 def create_instance(cls, input_dict, containing_collection):
202 cls._check_for_required_fields(input_dict, ('name',))
203 return models.AclGroup.add_object(name=input_dict['name'])
204
205
206 def update(self, input_dict):
207 pass
208
209
210class AclCollection(resource_lib.Collection):
211 queryset = models.AclGroup.objects.all()
212 entry_class = Acl
213
214
jamesren3981f442010-02-16 19:27:59 +0000215class UserAclMembership(resource_lib.Relationship):
216 related_classes = {'user': User, 'acl': Acl}
217
218 # TODO: check permissions
219 # TODO: check for and add/remove "Everyone"
showardf828c772010-01-25 21:49:42 +0000220
221
jamesren3981f442010-02-16 19:27:59 +0000222class UserAclMembershipCollection(resource_lib.RelationshipCollection):
223 entry_class = UserAclMembership
showardf828c772010-01-25 21:49:42 +0000224
225
226class Host(EntryWithInvalid):
jamesren3981f442010-02-16 19:27:59 +0000227 model = models.Host
228
229 @classmethod
230 def add_query_selectors(cls, query_processor):
231 query_processor.add_field_selector('hostname')
232 query_processor.add_field_selector(
233 'locked', value_transform=query_processor.read_boolean)
234 query_processor.add_field_selector(
showardf828c772010-01-25 21:49:42 +0000235 'locked_by', field='locked_by__login',
236 doc='Username of user who locked this host, if locked')
jamesren3981f442010-02-16 19:27:59 +0000237 query_processor.add_field_selector('status')
238 query_processor.add_field_selector(
239 'protection_level', field='protection',
240 doc='Verify/repair protection level',
241 value_transform=cls._read_protection)
242 query_processor.add_field_selector(
243 'accessible_by', field='aclgroup__users__login',
244 doc='Username of user with access to this host')
245 query_processor.add_related_existence_selector(
246 'has_label', models.Label, 'name')
showardf828c772010-01-25 21:49:42 +0000247
248
249 @classmethod
250 def _read_protection(cls, protection_input):
251 return host_protections.Protection.get_value(protection_input)
252
253
254 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000255 def from_uri_args(cls, request, hostname, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000256 return cls(request, models.Host.objects.get(hostname=hostname))
showardf828c772010-01-25 21:49:42 +0000257
258
259 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000260 return {'hostname': self.instance.hostname}
showardf828c772010-01-25 21:49:42 +0000261
262
263 def short_representation(self):
264 rep = super(Host, self).short_representation()
265 # TODO calling platform() over and over is inefficient
showardf46ad4c2010-02-03 20:28:59 +0000266 platform_rep = (Label.from_optional_instance(self._request,
267 self.instance.platform())
showardf828c772010-01-25 21:49:42 +0000268 .short_representation())
269 rep.update({'hostname': self.instance.hostname,
270 'locked': bool(self.instance.locked),
271 'status': self.instance.status,
272 'platform': platform_rep})
273 return rep
274
275
276 def full_representation(self):
277 rep = super(Host, self).full_representation()
278 protection = host_protections.Protection.get_string(
279 self.instance.protection)
showardf46ad4c2010-02-03 20:28:59 +0000280 locked_by = (User.from_optional_instance(self._request,
281 self.instance.locked_by)
showardf828c772010-01-25 21:49:42 +0000282 .short_representation())
jamesren3981f442010-02-16 19:27:59 +0000283 labels = HostLabelingCollection(fixed_entry=self)
284 acls = HostAclMembershipCollection(fixed_entry=self)
285 queue_entries = QueueEntryCollection(self._request)
286 queue_entries.set_query_parameters(host=self.instance.hostname)
287 health_tasks = HealthTaskCollection(self._request)
288 health_tasks.set_query_parameters(host=self.instance.hostname)
289
showardf828c772010-01-25 21:49:42 +0000290 rep.update({'locked_by': locked_by,
291 'locked_on': self._format_datetime(self.instance.lock_time),
292 'invalid': self.instance.invalid,
293 'protection_level': protection,
294 # TODO make these efficient
jamesren3981f442010-02-16 19:27:59 +0000295 'labels': labels.full_representation(),
296 'acls': acls.full_representation(),
297 'queue_entries': queue_entries.link(),
298 'health_tasks': health_tasks.link()})
showardf828c772010-01-25 21:49:42 +0000299 return rep
300
301
302 @classmethod
303 def create_instance(cls, input_dict, containing_collection):
304 cls._check_for_required_fields(input_dict, ('hostname',))
jamesren3981f442010-02-16 19:27:59 +0000305 # include locked here, rather than waiting for update(), to avoid race
306 # conditions
307 host = models.Host.add_object(hostname=input_dict['hostname'],
308 locked=input_dict.get('locked', False))
309 return host
showardf828c772010-01-25 21:49:42 +0000310
311 def update(self, input_dict):
312 data = {'locked': input_dict.get('locked'),
313 'protection': input_dict.get('protection_level')}
314 data = input_dict.remove_unspecified_fields(data)
315
316 if 'protection' in data:
317 data['protection'] = self._read_protection(data['protection'])
318
319 self.instance.update_object(**data)
320
321 if 'platform' in input_dict:
showardf46ad4c2010-02-03 20:28:59 +0000322 label = self.resolve_link(input_dict['platform']) .instance
showardf828c772010-01-25 21:49:42 +0000323 if not label.platform:
Eric Li0a993912011-05-17 12:56:25 -0700324 raise exceptions.BadRequest('Label %s is not a platform' % label.name)
showardf828c772010-01-25 21:49:42 +0000325 for label in self.instance.labels.filter(platform=True):
326 self.instance.labels.remove(label)
327 self.instance.labels.add(label)
328
329
330class HostCollection(resource_lib.Collection):
331 queryset = models.Host.valid_objects.all()
332 entry_class = Host
333
334
jamesren3981f442010-02-16 19:27:59 +0000335class HostLabeling(resource_lib.Relationship):
336 related_classes = {'host': Host, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000337
338
jamesren3981f442010-02-16 19:27:59 +0000339class HostLabelingCollection(resource_lib.RelationshipCollection):
340 entry_class = HostLabeling
showardf828c772010-01-25 21:49:42 +0000341
342
jamesren3981f442010-02-16 19:27:59 +0000343class HostAclMembership(resource_lib.Relationship):
344 related_classes = {'host': Host, 'acl': Acl}
345
346 # TODO: acl.check_for_acl_violation_acl_group()
347 # TODO: models.AclGroup.on_host_membership_change()
showardf828c772010-01-25 21:49:42 +0000348
349
jamesren3981f442010-02-16 19:27:59 +0000350class HostAclMembershipCollection(resource_lib.RelationshipCollection):
351 entry_class = HostAclMembership
showardf828c772010-01-25 21:49:42 +0000352
353
jamesren3981f442010-02-16 19:27:59 +0000354class Test(resource_lib.InstanceEntry):
355 model = models.Test
showardf828c772010-01-25 21:49:42 +0000356
357
showardf828c772010-01-25 21:49:42 +0000358 @classmethod
jamesrencd7a81a2010-04-21 20:39:08 +0000359 def add_query_selectors(cls, query_processor):
360 query_processor.add_field_selector('name')
361
362
363 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000364 def from_uri_args(cls, request, test_name, **kwargs):
365 return cls(request, models.Test.objects.get(name=test_name))
showardf828c772010-01-25 21:49:42 +0000366
367
368 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000369 return {'test_name': self.instance.name}
showardf828c772010-01-25 21:49:42 +0000370
371
372 def short_representation(self):
373 rep = super(Test, self).short_representation()
374 rep['name'] = self.instance.name
375 return rep
376
377
378 def full_representation(self):
379 rep = super(Test, self).full_representation()
380 rep.update({'author': self.instance.author,
381 'class': self.instance.test_class,
382 'control_file_type':
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700383 control_data.CONTROL_TYPE.get_string(
jamesrendd855242010-03-02 22:23:44 +0000384 self.instance.test_type),
showardf828c772010-01-25 21:49:42 +0000385 'control_file_path': self.instance.path,
jamesrencd7a81a2010-04-21 20:39:08 +0000386 'sync_count': self.instance.sync_count,
jamesren3981f442010-02-16 19:27:59 +0000387 'dependencies':
388 TestDependencyCollection(fixed_entry=self).link(),
showardf828c772010-01-25 21:49:42 +0000389 })
390 return rep
391
392
393 @classmethod
394 def create_instance(cls, input_dict, containing_collection):
395 cls._check_for_required_fields(input_dict,
396 ('name', 'control_file_type',
397 'control_file_path'))
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700398 test_type = control_data.CONTROL_TYPE.get_value(
jamesrendd855242010-03-02 22:23:44 +0000399 input['control_file_type'])
showardf828c772010-01-25 21:49:42 +0000400 return models.Test.add_object(name=input_dict['name'],
401 test_type=test_type,
402 path=input_dict['control_file_path'])
403
404
405 def update(self, input_dict):
406 data = {'test_type': input_dict.get('control_file_type'),
407 'path': input_dict.get('control_file_path'),
408 'class': input_dict.get('class'),
409 }
410 data = input_dict.remove_unspecified_fields(data)
411 self.instance.update_object(**data)
412
413
414class TestCollection(resource_lib.Collection):
415 queryset = models.Test.objects.all()
416 entry_class = Test
417
418
jamesren3981f442010-02-16 19:27:59 +0000419class TestDependency(resource_lib.Relationship):
420 related_classes = {'test': Test, 'label': Label}
showardf828c772010-01-25 21:49:42 +0000421
422
jamesren3981f442010-02-16 19:27:59 +0000423class TestDependencyCollection(resource_lib.RelationshipCollection):
424 entry_class = TestDependency
showardf828c772010-01-25 21:49:42 +0000425
426
427# TODO profilers
428
429
430class ExecutionInfo(resource_lib.Resource):
431 _permitted_methods = ('GET','POST')
432 _job_fields = models.Job.get_field_dict()
433 _DEFAULTS = {
434 'control_file': '',
435 'is_server': True,
436 'dependencies': [],
437 'machines_per_execution': 1,
438 'run_verify': bool(_job_fields['run_verify'].default),
Dan Shi07e09af2013-04-12 09:31:29 -0700439 'run_reset': bool(_job_fields['run_reset'].default),
showardf828c772010-01-25 21:49:42 +0000440 'timeout_hrs': _job_fields['timeout'].default,
Simran Basi34217022012-11-06 13:43:15 -0800441 'maximum_runtime_mins': _job_fields['max_runtime_mins'].default,
showardf828c772010-01-25 21:49:42 +0000442 'cleanup_before_job':
jamesrendd855242010-03-02 22:23:44 +0000443 model_attributes.RebootBefore.get_string(
444 models.DEFAULT_REBOOT_BEFORE),
showardf828c772010-01-25 21:49:42 +0000445 'cleanup_after_job':
jamesrendd855242010-03-02 22:23:44 +0000446 model_attributes.RebootAfter.get_string(
447 models.DEFAULT_REBOOT_AFTER),
showardf828c772010-01-25 21:49:42 +0000448 }
449
450
jamesren3981f442010-02-16 19:27:59 +0000451 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000452 return (('tests', 'Comma-separated list of test names to run'),
453 ('kernels', 'TODO'),
454 ('client_control_file',
455 'Client control file segment to run after all specified '
456 'tests'),
457 ('profilers',
458 'Comma-separated list of profilers to activate during the '
459 'job'),
460 ('use_container', 'TODO'),
461 ('profile_only',
462 'If true, run only profiled iterations; otherwise, always run '
463 'at least one non-profiled iteration in addition to a '
464 'profiled iteration'),
465 ('upload_kernel_config',
466 'If true, generate a server control file code that uploads '
467 'the kernel config file to the client and tells the client of '
468 'the new (local) path when compiling the kernel; the tests '
469 'must be server side tests'))
470
471
472 @classmethod
473 def execution_info_from_job(cls, job):
474 return {'control_file': job.control_file,
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700475 'is_server':
476 job.control_type == control_data.CONTROL_TYPE.SERVER,
showardf828c772010-01-25 21:49:42 +0000477 'dependencies': [label.name for label
478 in job.dependency_labels.all()],
479 'machines_per_execution': job.synch_count,
480 'run_verify': bool(job.run_verify),
Dan Shi07e09af2013-04-12 09:31:29 -0700481 'run_reset': bool(job.run_reset),
showardf828c772010-01-25 21:49:42 +0000482 'timeout_hrs': job.timeout,
Simran Basi34217022012-11-06 13:43:15 -0800483 'maximum_runtime_mins': job.max_runtime_mins,
showardf828c772010-01-25 21:49:42 +0000484 'cleanup_before_job':
jamesrendd855242010-03-02 22:23:44 +0000485 model_attributes.RebootBefore.get_string(job.reboot_before),
showardf828c772010-01-25 21:49:42 +0000486 'cleanup_after_job':
jamesrendd855242010-03-02 22:23:44 +0000487 model_attributes.RebootAfter.get_string(job.reboot_after),
showardf828c772010-01-25 21:49:42 +0000488 }
489
490
491 def _get_execution_info(self, input_dict):
492 tests = input_dict.get('tests', '')
493 client_control_file = input_dict.get('client_control_file', None)
494 if not tests and not client_control_file:
495 return self._DEFAULTS
496
497 test_list = tests.split(',')
498 if 'profilers' in input_dict:
499 profilers_list = input_dict['profilers'].split(',')
500 else:
501 profilers_list = []
502 kernels = input_dict.get('kernels', '') # TODO
503 if kernels:
504 kernels = [dict(version=kernel) for kernel in kernels.split(',')]
505
506 cf_info, test_objects, profiler_objects, label = (
507 rpc_utils.prepare_generate_control_file(
508 test_list, kernels, None, profilers_list))
509 control_file_contents = control_file.generate_control(
510 tests=test_objects, kernels=kernels,
511 profilers=profiler_objects, is_server=cf_info['is_server'],
512 client_control_file=client_control_file,
513 profile_only=input_dict.get('profile_only', None),
514 upload_kernel_config=input_dict.get(
515 'upload_kernel_config', None))
516 return dict(self._DEFAULTS,
517 control_file=control_file_contents,
518 is_server=cf_info['is_server'],
519 dependencies=cf_info['dependencies'],
520 machines_per_execution=cf_info['synch_count'])
521
522
showardf46ad4c2010-02-03 20:28:59 +0000523 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000524 result = self.link()
showardf46ad4c2010-02-03 20:28:59 +0000525 result['execution_info'] = self._get_execution_info(
526 self._request.REQUEST)
showardf828c772010-01-25 21:49:42 +0000527 return self._basic_response(result)
528
529
530class QueueEntriesRequest(resource_lib.Resource):
531 _permitted_methods = ('GET',)
532
533
jamesren3981f442010-02-16 19:27:59 +0000534 def _query_parameters_accepted(self):
showardf828c772010-01-25 21:49:42 +0000535 return (('hosts', 'Comma-separated list of hostnames'),
536 ('one_time_hosts',
537 'Comma-separated list of hostnames not already in the '
538 'Autotest system'),
539 ('meta_hosts',
540 'Comma-separated list of label names; for each one, an entry '
541 'will be created and assigned at runtime to an available host '
542 'with that label'),
543 ('atomic_group_class', 'TODO'))
544
545
546 def _read_list(self, list_string):
547 if list_string:
548 return list_string.split(',')
549 return []
550
551
showardf46ad4c2010-02-03 20:28:59 +0000552 def handle_request(self):
553 request_dict = self._request.REQUEST
showardf828c772010-01-25 21:49:42 +0000554 hosts = self._read_list(request_dict.get('hosts'))
555 one_time_hosts = self._read_list(request_dict.get('one_time_hosts'))
556 meta_hosts = self._read_list(request_dict.get('meta_hosts'))
557 atomic_group_class = request_dict.get('atomic_group_class')
558
559 # TODO: bring in all the atomic groups magic from create_job()
560
561 entries = []
562 for hostname in one_time_hosts:
563 models.Host.create_one_time_host(hostname)
564 for hostname in hosts:
showardf46ad4c2010-02-03 20:28:59 +0000565 entry = Host.from_uri_args(self._request, hostname)
showardf828c772010-01-25 21:49:42 +0000566 entries.append({'host': entry.link()})
567 for label_name in meta_hosts:
showardf46ad4c2010-02-03 20:28:59 +0000568 entry = Label.from_uri_args(self._request, label_name)
showardf828c772010-01-25 21:49:42 +0000569 entries.append({'meta_host': entry.link()})
jamesren3e9f6092010-03-11 21:32:10 +0000570 if atomic_group_class:
571 entries.append({'atomic_group_class': atomic_group_class})
showardf828c772010-01-25 21:49:42 +0000572
573 result = self.link()
574 result['queue_entries'] = entries
575 return self._basic_response(result)
576
577
jamesren3981f442010-02-16 19:27:59 +0000578class Job(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000579 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000580 model = models.Job
581
showardf828c772010-01-25 21:49:42 +0000582
583 class _StatusConstraint(query_lib.Constraint):
jamesren3981f442010-02-16 19:27:59 +0000584 def apply_constraint(self, queryset, value, comparison_type,
585 is_inverse):
showardf828c772010-01-25 21:49:42 +0000586 if comparison_type != 'equals' or is_inverse:
587 raise query_lib.ConstraintError('Can only use this selector '
588 'with equals')
589 non_queued_statuses = [
590 status for status, _
591 in models.HostQueueEntry.Status.choices()
592 if status != models.HostQueueEntry.Status.QUEUED]
593 if value == 'queued':
594 return queryset.exclude(
595 hostqueueentry__status__in=non_queued_statuses)
596 elif value == 'active':
597 return queryset.filter(
598 hostqueueentry__status__in=non_queued_statuses).filter(
599 hostqueueentry__complete=False).distinct()
600 elif value == 'complete':
601 return queryset.exclude(hostqueueentry__complete=False)
602 else:
603 raise query_lib.ConstraintError('Value must be one of queued, '
604 'active or complete')
605
606
jamesren3981f442010-02-16 19:27:59 +0000607 @classmethod
608 def add_query_selectors(cls, query_processor):
609 query_processor.add_field_selector('id')
jamesrenc3940222010-02-19 21:57:37 +0000610 query_processor.add_field_selector('name')
jamesren3981f442010-02-16 19:27:59 +0000611 query_processor.add_selector(
612 query_lib.Selector('status',
613 doc='One of queued, active or complete'),
614 Job._StatusConstraint())
jamesrencd7a81a2010-04-21 20:39:08 +0000615 query_processor.add_keyval_selector('has_keyval', models.JobKeyval,
616 'key', 'value')
showardf828c772010-01-25 21:49:42 +0000617
618
619 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000620 def from_uri_args(cls, request, job_id, **kwargs):
showardf46ad4c2010-02-03 20:28:59 +0000621 return cls(request, models.Job.objects.get(id=job_id))
showardf828c772010-01-25 21:49:42 +0000622
623
624 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000625 return {'job_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000626
627
jamesrencd7a81a2010-04-21 20:39:08 +0000628 @classmethod
629 def _do_prepare_for_full_representation(cls, instances):
630 models.Job.objects.populate_relationships(instances, models.JobKeyval,
631 'keyvals')
632
633
showardf828c772010-01-25 21:49:42 +0000634 def short_representation(self):
635 rep = super(Job, self).short_representation()
636 rep.update({'id': self.instance.id,
637 'owner': self.instance.owner,
638 'name': self.instance.name,
639 'priority':
640 models.Job.Priority.get_string(self.instance.priority),
641 'created_on':
642 self._format_datetime(self.instance.created_on),
643 })
644 return rep
645
646
647 def full_representation(self):
648 rep = super(Job, self).full_representation()
jamesren3981f442010-02-16 19:27:59 +0000649 queue_entries = QueueEntryCollection(self._request)
650 queue_entries.set_query_parameters(job=self.instance.id)
jamesren76fcf192010-04-21 20:39:50 +0000651 drone_set = self.instance.drone_set and self.instance.drone_set.name
showardf828c772010-01-25 21:49:42 +0000652 rep.update({'email_list': self.instance.email_list,
653 'parse_failed_repair':
654 bool(self.instance.parse_failed_repair),
jamesren76fcf192010-04-21 20:39:50 +0000655 'drone_set': drone_set,
showardf828c772010-01-25 21:49:42 +0000656 'execution_info':
657 ExecutionInfo.execution_info_from_job(self.instance),
jamesren3981f442010-02-16 19:27:59 +0000658 'queue_entries': queue_entries.link(),
jamesrencd7a81a2010-04-21 20:39:08 +0000659 'keyvals': dict((keyval.key, keyval.value)
660 for keyval in self.instance.keyvals)
showardf828c772010-01-25 21:49:42 +0000661 })
662 return rep
663
664
665 @classmethod
666 def create_instance(cls, input_dict, containing_collection):
jamesrene38a0a72010-04-19 18:05:31 +0000667 owner = input_dict.get('owner')
668 if not owner:
669 owner = models.User.current_user().login
670
showardf828c772010-01-25 21:49:42 +0000671 cls._check_for_required_fields(input_dict, ('name', 'execution_info',
672 'queue_entries'))
673 execution_info = input_dict['execution_info']
674 cls._check_for_required_fields(execution_info, ('control_file',
675 'is_server'))
676
677 if execution_info['is_server']:
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700678 control_type = control_data.CONTROL_TYPE.SERVER
showardf828c772010-01-25 21:49:42 +0000679 else:
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700680 control_type = control_data.CONTROL_TYPE.CLIENT
showardf828c772010-01-25 21:49:42 +0000681 options = dict(
682 name=input_dict['name'],
683 priority=input_dict.get('priority', None),
684 control_file=execution_info['control_file'],
685 control_type=control_type,
686 is_template=input_dict.get('is_template', None),
687 timeout=execution_info.get('timeout_hrs'),
Simran Basi34217022012-11-06 13:43:15 -0800688 max_runtime_mins=execution_info.get('maximum_runtime_mins'),
showardf828c772010-01-25 21:49:42 +0000689 synch_count=execution_info.get('machines_per_execution'),
690 run_verify=execution_info.get('run_verify'),
Dan Shi07e09af2013-04-12 09:31:29 -0700691 run_reset=execution_info.get('run_reset'),
showardf828c772010-01-25 21:49:42 +0000692 email_list=input_dict.get('email_list', None),
693 dependencies=execution_info.get('dependencies', ()),
694 reboot_before=execution_info.get('cleanup_before_job'),
695 reboot_after=execution_info.get('cleanup_after_job'),
696 parse_failed_repair=input_dict.get('parse_failed_repair', None),
jamesren76fcf192010-04-21 20:39:50 +0000697 drone_set=input_dict.get('drone_set', None),
showardf828c772010-01-25 21:49:42 +0000698 keyvals=input_dict.get('keyvals', None))
699
700 host_objects, metahost_label_objects, atomic_group = [], [], None
701 for queue_entry in input_dict['queue_entries']:
702 if 'host' in queue_entry:
703 host = queue_entry['host']
704 if host: # can be None, indicated a hostless job
showardf46ad4c2010-02-03 20:28:59 +0000705 host_entry = containing_collection.resolve_link(host)
showardf828c772010-01-25 21:49:42 +0000706 host_objects.append(host_entry.instance)
707 elif 'meta_host' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000708 label_entry = containing_collection.resolve_link(
showardf828c772010-01-25 21:49:42 +0000709 queue_entry['meta_host'])
710 metahost_label_objects.append(label_entry.instance)
jamesren3e9f6092010-03-11 21:32:10 +0000711 if 'atomic_group_class' in queue_entry:
showardf46ad4c2010-02-03 20:28:59 +0000712 atomic_group_entry = containing_collection.resolve_link(
jamesren3e9f6092010-03-11 21:32:10 +0000713 queue_entry['atomic_group_class'])
showardf828c772010-01-25 21:49:42 +0000714 if atomic_group:
715 assert atomic_group_entry.instance.id == atomic_group.id
716 else:
717 atomic_group = atomic_group_entry.instance
718
719 job_id = rpc_utils.create_new_job(
jamesrene38a0a72010-04-19 18:05:31 +0000720 owner=owner,
showardf828c772010-01-25 21:49:42 +0000721 options=options,
722 host_objects=host_objects,
723 metahost_objects=metahost_label_objects,
724 atomic_group=atomic_group)
725 return models.Job.objects.get(id=job_id)
726
727
728 def update(self, input_dict):
729 # Required for POST, doesn't actually support PUT
730 pass
731
732
733class JobCollection(resource_lib.Collection):
734 queryset = models.Job.objects.order_by('-id')
735 entry_class = Job
736
737
jamesren3981f442010-02-16 19:27:59 +0000738class QueueEntry(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000739 _permitted_methods = ('GET', 'PUT')
jamesren3981f442010-02-16 19:27:59 +0000740 model = models.HostQueueEntry
741
showardf828c772010-01-25 21:49:42 +0000742
743 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000744 def add_query_selectors(cls, query_processor):
745 query_processor.add_field_selector('host', field='host__hostname')
746 query_processor.add_field_selector('job', field='job__id')
747
748
749 @classmethod
750 def from_uri_args(cls, request, queue_entry_id):
showardf828c772010-01-25 21:49:42 +0000751 instance = models.HostQueueEntry.objects.get(id=queue_entry_id)
showardf46ad4c2010-02-03 20:28:59 +0000752 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000753
754
755 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000756 return {'queue_entry_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000757
758
759 def short_representation(self):
760 rep = super(QueueEntry, self).short_representation()
761 if self.instance.host:
showardf46ad4c2010-02-03 20:28:59 +0000762 host = (Host(self._request, self.instance.host)
763 .short_representation())
showardf828c772010-01-25 21:49:42 +0000764 else:
765 host = None
showardf46ad4c2010-02-03 20:28:59 +0000766 job = Job(self._request, self.instance.job)
767 host = Host.from_optional_instance(self._request, self.instance.host)
768 label = Label.from_optional_instance(self._request,
769 self.instance.meta_host)
770 atomic_group_class = AtomicGroupClass.from_optional_instance(
771 self._request, self.instance.atomic_group)
showardf828c772010-01-25 21:49:42 +0000772 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000773 {'job': job.short_representation(),
774 'host': host.short_representation(),
775 'label': label.short_representation(),
showardf828c772010-01-25 21:49:42 +0000776 'atomic_group_class':
showardf46ad4c2010-02-03 20:28:59 +0000777 atomic_group_class.short_representation(),
showardf828c772010-01-25 21:49:42 +0000778 'status': self.instance.status,
779 'execution_path': self.instance.execution_subdir,
780 'started_on': self._format_datetime(self.instance.started_on),
781 'aborted': bool(self.instance.aborted)})
782 return rep
783
784
785 def update(self, input_dict):
786 if 'aborted' in input_dict:
787 if input_dict['aborted'] != True:
Eric Li0a993912011-05-17 12:56:25 -0700788 raise exceptions.BadRequest('"aborted" can only be set to true')
showardf828c772010-01-25 21:49:42 +0000789 query = models.HostQueueEntry.objects.filter(pk=self.instance.pk)
790 models.AclGroup.check_abort_permissions(query)
791 rpc_utils.check_abort_synchronous_jobs(query)
792 self.instance.abort(thread_local.get_user())
793
794
jamesren3981f442010-02-16 19:27:59 +0000795class QueueEntryCollection(resource_lib.Collection):
796 queryset = models.HostQueueEntry.objects.order_by('-id')
797 entry_class = QueueEntry
798
799
800class HealthTask(resource_lib.InstanceEntry):
showardf828c772010-01-25 21:49:42 +0000801 _permitted_methods = ('GET',)
jamesren3981f442010-02-16 19:27:59 +0000802 model = models.SpecialTask
803
showardf828c772010-01-25 21:49:42 +0000804
805 @classmethod
jamesren3981f442010-02-16 19:27:59 +0000806 def add_query_selectors(cls, query_processor):
807 query_processor.add_field_selector('host', field='host__hostname')
808
809
810 @classmethod
811 def from_uri_args(cls, request, task_id):
showardf828c772010-01-25 21:49:42 +0000812 instance = models.SpecialTask.objects.get(id=task_id)
showardf46ad4c2010-02-03 20:28:59 +0000813 return cls(request, instance)
showardf828c772010-01-25 21:49:42 +0000814
815
816 def _uri_args(self):
jamesren3981f442010-02-16 19:27:59 +0000817 return {'task_id': self.instance.id}
showardf828c772010-01-25 21:49:42 +0000818
819
820 def short_representation(self):
821 rep = super(HealthTask, self).short_representation()
showardf46ad4c2010-02-03 20:28:59 +0000822 host = Host(self._request, self.instance.host)
823 queue_entry = QueueEntry.from_optional_instance(
824 self._request, self.instance.queue_entry)
showardf828c772010-01-25 21:49:42 +0000825 rep.update(
showardf46ad4c2010-02-03 20:28:59 +0000826 {'host': host.short_representation(),
showardf828c772010-01-25 21:49:42 +0000827 'task_type': self.instance.task,
828 'started_on':
829 self._format_datetime(self.instance.time_started),
830 'status': self.instance.status,
showardf46ad4c2010-02-03 20:28:59 +0000831 'queue_entry': queue_entry.short_representation()
showardf828c772010-01-25 21:49:42 +0000832 })
833 return rep
834
835
836 @classmethod
837 def create_instance(cls, input_dict, containing_collection):
838 cls._check_for_required_fields(input_dict, ('task_type',))
839 host = containing_collection.base_entry.instance
840 models.AclGroup.check_for_acl_violation_hosts((host,))
841 return models.SpecialTask.schedule_special_task(host,
842 input_dict['task_type'])
843
844
845 def update(self, input_dict):
846 # Required for POST, doesn't actually support PUT
847 pass
848
849
jamesren3981f442010-02-16 19:27:59 +0000850class HealthTaskCollection(resource_lib.Collection):
851 entry_class = HealthTask
852
853
854 def _fresh_queryset(self):
855 return models.SpecialTask.objects.order_by('-id')
856
857
showardf828c772010-01-25 21:49:42 +0000858class ResourceDirectory(resource_lib.Resource):
859 _permitted_methods = ('GET',)
860
showardf46ad4c2010-02-03 20:28:59 +0000861 def handle_request(self):
showardf828c772010-01-25 21:49:42 +0000862 result = self.link()
863 result.update({
showardf46ad4c2010-02-03 20:28:59 +0000864 'atomic_group_classes':
865 AtomicGroupClassCollection(self._request).link(),
866 'labels': LabelCollection(self._request).link(),
867 'users': UserCollection(self._request).link(),
868 'acl_groups': AclCollection(self._request).link(),
869 'hosts': HostCollection(self._request).link(),
870 'tests': TestCollection(self._request).link(),
871 'execution_info': ExecutionInfo(self._request).link(),
872 'queue_entries_request':
873 QueueEntriesRequest(self._request).link(),
874 'jobs': JobCollection(self._request).link(),
showardf828c772010-01-25 21:49:42 +0000875 })
876 return self._basic_response(result)