showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 1 | from django import http |
Eric Li | 0a99391 | 2011-05-17 12:56:25 -0700 | [diff] [blame] | 2 | from autotest_lib.frontend.shared import query_lib, resource_lib, exceptions |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 3 | from autotest_lib.frontend.afe import control_file, models, rpc_utils |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 4 | from autotest_lib.frontend.afe import model_attributes |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 5 | from autotest_lib.frontend import thread_local |
| 6 | from autotest_lib.client.common_lib import host_protections |
Aviv Keshet | 3dd8beb | 2013-05-13 17:36:04 -0700 | [diff] [blame] | 7 | from autotest_lib.client.common_lib import control_data |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 8 | |
Eric Li | 0a99391 | 2011-05-17 12:56:25 -0700 | [diff] [blame] | 9 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 10 | class EntryWithInvalid(resource_lib.InstanceEntry): |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 11 | def put(self): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 12 | if self.instance.invalid: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 13 | raise http.Http404('%s has been deleted' % self.instance) |
| 14 | return super(EntryWithInvalid, self).put() |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 15 | |
| 16 | |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 17 | def delete(self): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 18 | if self.instance.invalid: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 19 | raise http.Http404('%s has already been deleted' % self.instance) |
| 20 | return super(EntryWithInvalid, self).delete() |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 21 | |
| 22 | |
| 23 | class AtomicGroupClass(EntryWithInvalid): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 24 | model = models.AtomicGroup |
| 25 | |
| 26 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 27 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 28 | def from_uri_args(cls, request, ag_name, **kwargs): |
| 29 | return cls(request, models.AtomicGroup.objects.get(name=ag_name)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 30 | |
| 31 | |
| 32 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 33 | return {'ag_name': self.instance.name} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 34 | |
| 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, |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 46 | 'labels': |
| 47 | AtomicLabelTaggingCollection(fixed_entry=self).link()}) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 48 | 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 | |
| 64 | class AtomicGroupClassCollection(resource_lib.Collection): |
| 65 | queryset = models.AtomicGroup.valid_objects.all() |
| 66 | entry_class = AtomicGroupClass |
| 67 | |
| 68 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 69 | class Label(EntryWithInvalid): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 70 | 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) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 78 | |
| 79 | |
| 80 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 81 | def from_uri_args(cls, request, label_name, **kwargs): |
| 82 | return cls(request, models.Label.objects.get(name=label_name)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 83 | |
| 84 | |
| 85 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 86 | return {'label_name': self.instance.name} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 87 | |
| 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() |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 98 | 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(), |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 102 | 'hosts': HostLabelingCollection(fixed_entry=self).link()}) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 103 | 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 |
jamesren | cd7a81a | 2010-04-21 20:39:08 +0000 | [diff] [blame] | 114 | if 'is_platform' in input_dict: |
| 115 | self.instance.platform = input_dict['is_platform'] |
| 116 | self.instance.save() |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 117 | |
| 118 | |
| 119 | class LabelCollection(resource_lib.Collection): |
| 120 | queryset = models.Label.valid_objects.all() |
| 121 | entry_class = Label |
| 122 | |
| 123 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 124 | class AtomicLabelTagging(resource_lib.Relationship): |
| 125 | related_classes = {'label': Label, 'atomic_group_class': AtomicGroupClass} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 126 | |
| 127 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 128 | class AtomicLabelTaggingCollection(resource_lib.RelationshipCollection): |
| 129 | entry_class = AtomicLabelTagging |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 130 | |
| 131 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 132 | class User(resource_lib.InstanceEntry): |
| 133 | model = models.User |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 134 | _permitted_methods = ('GET,') |
| 135 | |
| 136 | |
| 137 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 138 | def from_uri_args(cls, request, username, **kwargs): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 139 | if username == '@me': |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 140 | username = models.User.current_user().login |
| 141 | return cls(request, models.User.objects.get(login=username)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 142 | |
| 143 | |
| 144 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 145 | return {'username': self.instance.login} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 146 | |
| 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() |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 156 | accessible_hosts = HostCollection(self._request) |
| 157 | accessible_hosts.set_query_parameters(accessible_by=self.instance.login) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 158 | rep.update({'jobs': 'TODO', |
| 159 | 'recurring_runs': 'TODO', |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 160 | 'acls': |
| 161 | UserAclMembershipCollection(fixed_entry=self).link(), |
| 162 | 'accessible_hosts': accessible_hosts.link()}) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 163 | return rep |
| 164 | |
| 165 | |
| 166 | class UserCollection(resource_lib.Collection): |
| 167 | _permitted_methods = ('GET',) |
| 168 | queryset = models.User.objects.all() |
| 169 | entry_class = User |
| 170 | |
| 171 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 172 | class Acl(resource_lib.InstanceEntry): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 173 | _permitted_methods = ('GET',) |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 174 | model = models.AclGroup |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 175 | |
| 176 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 177 | def from_uri_args(cls, request, acl_name, **kwargs): |
| 178 | return cls(request, models.AclGroup.objects.get(name=acl_name)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 179 | |
| 180 | |
| 181 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 182 | return {'acl_name': self.instance.name} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 183 | |
| 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() |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 193 | rep.update({'users': |
| 194 | UserAclMembershipCollection(fixed_entry=self).link(), |
| 195 | 'hosts': |
| 196 | HostAclMembershipCollection(fixed_entry=self).link()}) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 197 | 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 | |
| 210 | class AclCollection(resource_lib.Collection): |
| 211 | queryset = models.AclGroup.objects.all() |
| 212 | entry_class = Acl |
| 213 | |
| 214 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 215 | class UserAclMembership(resource_lib.Relationship): |
| 216 | related_classes = {'user': User, 'acl': Acl} |
| 217 | |
| 218 | # TODO: check permissions |
| 219 | # TODO: check for and add/remove "Everyone" |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 220 | |
| 221 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 222 | class UserAclMembershipCollection(resource_lib.RelationshipCollection): |
| 223 | entry_class = UserAclMembership |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 224 | |
| 225 | |
| 226 | class Host(EntryWithInvalid): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 227 | 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( |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 235 | 'locked_by', field='locked_by__login', |
| 236 | doc='Username of user who locked this host, if locked') |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 237 | 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') |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 247 | |
| 248 | |
| 249 | @classmethod |
| 250 | def _read_protection(cls, protection_input): |
| 251 | return host_protections.Protection.get_value(protection_input) |
| 252 | |
| 253 | |
| 254 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 255 | def from_uri_args(cls, request, hostname, **kwargs): |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 256 | return cls(request, models.Host.objects.get(hostname=hostname)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 257 | |
| 258 | |
| 259 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 260 | return {'hostname': self.instance.hostname} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 261 | |
| 262 | |
| 263 | def short_representation(self): |
| 264 | rep = super(Host, self).short_representation() |
| 265 | # TODO calling platform() over and over is inefficient |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 266 | platform_rep = (Label.from_optional_instance(self._request, |
| 267 | self.instance.platform()) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 268 | .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) |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 280 | locked_by = (User.from_optional_instance(self._request, |
| 281 | self.instance.locked_by) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 282 | .short_representation()) |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 283 | 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 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 290 | 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 |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 295 | 'labels': labels.full_representation(), |
| 296 | 'acls': acls.full_representation(), |
| 297 | 'queue_entries': queue_entries.link(), |
| 298 | 'health_tasks': health_tasks.link()}) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 299 | return rep |
| 300 | |
| 301 | |
| 302 | @classmethod |
| 303 | def create_instance(cls, input_dict, containing_collection): |
| 304 | cls._check_for_required_fields(input_dict, ('hostname',)) |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 305 | # 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 |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 310 | |
| 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: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 322 | label = self.resolve_link(input_dict['platform']) .instance |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 323 | if not label.platform: |
Eric Li | 0a99391 | 2011-05-17 12:56:25 -0700 | [diff] [blame] | 324 | raise exceptions.BadRequest('Label %s is not a platform' % label.name) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 325 | for label in self.instance.labels.filter(platform=True): |
| 326 | self.instance.labels.remove(label) |
| 327 | self.instance.labels.add(label) |
| 328 | |
| 329 | |
| 330 | class HostCollection(resource_lib.Collection): |
| 331 | queryset = models.Host.valid_objects.all() |
| 332 | entry_class = Host |
| 333 | |
| 334 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 335 | class HostLabeling(resource_lib.Relationship): |
| 336 | related_classes = {'host': Host, 'label': Label} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 337 | |
| 338 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 339 | class HostLabelingCollection(resource_lib.RelationshipCollection): |
| 340 | entry_class = HostLabeling |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 341 | |
| 342 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 343 | class 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() |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 348 | |
| 349 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 350 | class HostAclMembershipCollection(resource_lib.RelationshipCollection): |
| 351 | entry_class = HostAclMembership |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 352 | |
| 353 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 354 | class Test(resource_lib.InstanceEntry): |
| 355 | model = models.Test |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 356 | |
| 357 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 358 | @classmethod |
jamesren | cd7a81a | 2010-04-21 20:39:08 +0000 | [diff] [blame] | 359 | def add_query_selectors(cls, query_processor): |
| 360 | query_processor.add_field_selector('name') |
| 361 | |
| 362 | |
| 363 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 364 | def from_uri_args(cls, request, test_name, **kwargs): |
| 365 | return cls(request, models.Test.objects.get(name=test_name)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 366 | |
| 367 | |
| 368 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 369 | return {'test_name': self.instance.name} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 370 | |
| 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 Keshet | 3dd8beb | 2013-05-13 17:36:04 -0700 | [diff] [blame] | 383 | control_data.CONTROL_TYPE.get_string( |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 384 | self.instance.test_type), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 385 | 'control_file_path': self.instance.path, |
jamesren | cd7a81a | 2010-04-21 20:39:08 +0000 | [diff] [blame] | 386 | 'sync_count': self.instance.sync_count, |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 387 | 'dependencies': |
| 388 | TestDependencyCollection(fixed_entry=self).link(), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 389 | }) |
| 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 Keshet | 3dd8beb | 2013-05-13 17:36:04 -0700 | [diff] [blame] | 398 | test_type = control_data.CONTROL_TYPE.get_value( |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 399 | input['control_file_type']) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 400 | 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 | |
| 414 | class TestCollection(resource_lib.Collection): |
| 415 | queryset = models.Test.objects.all() |
| 416 | entry_class = Test |
| 417 | |
| 418 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 419 | class TestDependency(resource_lib.Relationship): |
| 420 | related_classes = {'test': Test, 'label': Label} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 421 | |
| 422 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 423 | class TestDependencyCollection(resource_lib.RelationshipCollection): |
| 424 | entry_class = TestDependency |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 425 | |
| 426 | |
| 427 | # TODO profilers |
| 428 | |
| 429 | |
| 430 | class 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 Shi | 07e09af | 2013-04-12 09:31:29 -0700 | [diff] [blame^] | 439 | 'run_reset': bool(_job_fields['run_reset'].default), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 440 | 'timeout_hrs': _job_fields['timeout'].default, |
Simran Basi | 3421702 | 2012-11-06 13:43:15 -0800 | [diff] [blame] | 441 | 'maximum_runtime_mins': _job_fields['max_runtime_mins'].default, |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 442 | 'cleanup_before_job': |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 443 | model_attributes.RebootBefore.get_string( |
| 444 | models.DEFAULT_REBOOT_BEFORE), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 445 | 'cleanup_after_job': |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 446 | model_attributes.RebootAfter.get_string( |
| 447 | models.DEFAULT_REBOOT_AFTER), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 448 | } |
| 449 | |
| 450 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 451 | def _query_parameters_accepted(self): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 452 | 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 Keshet | 3dd8beb | 2013-05-13 17:36:04 -0700 | [diff] [blame] | 475 | 'is_server': |
| 476 | job.control_type == control_data.CONTROL_TYPE.SERVER, |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 477 | '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 Shi | 07e09af | 2013-04-12 09:31:29 -0700 | [diff] [blame^] | 481 | 'run_reset': bool(job.run_reset), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 482 | 'timeout_hrs': job.timeout, |
Simran Basi | 3421702 | 2012-11-06 13:43:15 -0800 | [diff] [blame] | 483 | 'maximum_runtime_mins': job.max_runtime_mins, |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 484 | 'cleanup_before_job': |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 485 | model_attributes.RebootBefore.get_string(job.reboot_before), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 486 | 'cleanup_after_job': |
jamesren | dd85524 | 2010-03-02 22:23:44 +0000 | [diff] [blame] | 487 | model_attributes.RebootAfter.get_string(job.reboot_after), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 488 | } |
| 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 | |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 523 | def handle_request(self): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 524 | result = self.link() |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 525 | result['execution_info'] = self._get_execution_info( |
| 526 | self._request.REQUEST) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 527 | return self._basic_response(result) |
| 528 | |
| 529 | |
| 530 | class QueueEntriesRequest(resource_lib.Resource): |
| 531 | _permitted_methods = ('GET',) |
| 532 | |
| 533 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 534 | def _query_parameters_accepted(self): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 535 | 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 | |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 552 | def handle_request(self): |
| 553 | request_dict = self._request.REQUEST |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 554 | 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: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 565 | entry = Host.from_uri_args(self._request, hostname) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 566 | entries.append({'host': entry.link()}) |
| 567 | for label_name in meta_hosts: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 568 | entry = Label.from_uri_args(self._request, label_name) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 569 | entries.append({'meta_host': entry.link()}) |
jamesren | 3e9f609 | 2010-03-11 21:32:10 +0000 | [diff] [blame] | 570 | if atomic_group_class: |
| 571 | entries.append({'atomic_group_class': atomic_group_class}) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 572 | |
| 573 | result = self.link() |
| 574 | result['queue_entries'] = entries |
| 575 | return self._basic_response(result) |
| 576 | |
| 577 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 578 | class Job(resource_lib.InstanceEntry): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 579 | _permitted_methods = ('GET',) |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 580 | model = models.Job |
| 581 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 582 | |
| 583 | class _StatusConstraint(query_lib.Constraint): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 584 | def apply_constraint(self, queryset, value, comparison_type, |
| 585 | is_inverse): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 586 | 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 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 607 | @classmethod |
| 608 | def add_query_selectors(cls, query_processor): |
| 609 | query_processor.add_field_selector('id') |
jamesren | c394022 | 2010-02-19 21:57:37 +0000 | [diff] [blame] | 610 | query_processor.add_field_selector('name') |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 611 | query_processor.add_selector( |
| 612 | query_lib.Selector('status', |
| 613 | doc='One of queued, active or complete'), |
| 614 | Job._StatusConstraint()) |
jamesren | cd7a81a | 2010-04-21 20:39:08 +0000 | [diff] [blame] | 615 | query_processor.add_keyval_selector('has_keyval', models.JobKeyval, |
| 616 | 'key', 'value') |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 617 | |
| 618 | |
| 619 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 620 | def from_uri_args(cls, request, job_id, **kwargs): |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 621 | return cls(request, models.Job.objects.get(id=job_id)) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 622 | |
| 623 | |
| 624 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 625 | return {'job_id': self.instance.id} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 626 | |
| 627 | |
jamesren | cd7a81a | 2010-04-21 20:39:08 +0000 | [diff] [blame] | 628 | @classmethod |
| 629 | def _do_prepare_for_full_representation(cls, instances): |
| 630 | models.Job.objects.populate_relationships(instances, models.JobKeyval, |
| 631 | 'keyvals') |
| 632 | |
| 633 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 634 | 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() |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 649 | queue_entries = QueueEntryCollection(self._request) |
| 650 | queue_entries.set_query_parameters(job=self.instance.id) |
jamesren | 76fcf19 | 2010-04-21 20:39:50 +0000 | [diff] [blame] | 651 | drone_set = self.instance.drone_set and self.instance.drone_set.name |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 652 | rep.update({'email_list': self.instance.email_list, |
| 653 | 'parse_failed_repair': |
| 654 | bool(self.instance.parse_failed_repair), |
jamesren | 76fcf19 | 2010-04-21 20:39:50 +0000 | [diff] [blame] | 655 | 'drone_set': drone_set, |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 656 | 'execution_info': |
| 657 | ExecutionInfo.execution_info_from_job(self.instance), |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 658 | 'queue_entries': queue_entries.link(), |
jamesren | cd7a81a | 2010-04-21 20:39:08 +0000 | [diff] [blame] | 659 | 'keyvals': dict((keyval.key, keyval.value) |
| 660 | for keyval in self.instance.keyvals) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 661 | }) |
| 662 | return rep |
| 663 | |
| 664 | |
| 665 | @classmethod |
| 666 | def create_instance(cls, input_dict, containing_collection): |
jamesren | e38a0a7 | 2010-04-19 18:05:31 +0000 | [diff] [blame] | 667 | owner = input_dict.get('owner') |
| 668 | if not owner: |
| 669 | owner = models.User.current_user().login |
| 670 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 671 | 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 Keshet | 3dd8beb | 2013-05-13 17:36:04 -0700 | [diff] [blame] | 678 | control_type = control_data.CONTROL_TYPE.SERVER |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 679 | else: |
Aviv Keshet | 3dd8beb | 2013-05-13 17:36:04 -0700 | [diff] [blame] | 680 | control_type = control_data.CONTROL_TYPE.CLIENT |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 681 | 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 Basi | 3421702 | 2012-11-06 13:43:15 -0800 | [diff] [blame] | 688 | max_runtime_mins=execution_info.get('maximum_runtime_mins'), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 689 | synch_count=execution_info.get('machines_per_execution'), |
| 690 | run_verify=execution_info.get('run_verify'), |
Dan Shi | 07e09af | 2013-04-12 09:31:29 -0700 | [diff] [blame^] | 691 | run_reset=execution_info.get('run_reset'), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 692 | 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), |
jamesren | 76fcf19 | 2010-04-21 20:39:50 +0000 | [diff] [blame] | 697 | drone_set=input_dict.get('drone_set', None), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 698 | 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 |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 705 | host_entry = containing_collection.resolve_link(host) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 706 | host_objects.append(host_entry.instance) |
| 707 | elif 'meta_host' in queue_entry: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 708 | label_entry = containing_collection.resolve_link( |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 709 | queue_entry['meta_host']) |
| 710 | metahost_label_objects.append(label_entry.instance) |
jamesren | 3e9f609 | 2010-03-11 21:32:10 +0000 | [diff] [blame] | 711 | if 'atomic_group_class' in queue_entry: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 712 | atomic_group_entry = containing_collection.resolve_link( |
jamesren | 3e9f609 | 2010-03-11 21:32:10 +0000 | [diff] [blame] | 713 | queue_entry['atomic_group_class']) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 714 | 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( |
jamesren | e38a0a7 | 2010-04-19 18:05:31 +0000 | [diff] [blame] | 720 | owner=owner, |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 721 | 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 | |
| 733 | class JobCollection(resource_lib.Collection): |
| 734 | queryset = models.Job.objects.order_by('-id') |
| 735 | entry_class = Job |
| 736 | |
| 737 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 738 | class QueueEntry(resource_lib.InstanceEntry): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 739 | _permitted_methods = ('GET', 'PUT') |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 740 | model = models.HostQueueEntry |
| 741 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 742 | |
| 743 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 744 | 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): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 751 | instance = models.HostQueueEntry.objects.get(id=queue_entry_id) |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 752 | return cls(request, instance) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 753 | |
| 754 | |
| 755 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 756 | return {'queue_entry_id': self.instance.id} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 757 | |
| 758 | |
| 759 | def short_representation(self): |
| 760 | rep = super(QueueEntry, self).short_representation() |
| 761 | if self.instance.host: |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 762 | host = (Host(self._request, self.instance.host) |
| 763 | .short_representation()) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 764 | else: |
| 765 | host = None |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 766 | 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) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 772 | rep.update( |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 773 | {'job': job.short_representation(), |
| 774 | 'host': host.short_representation(), |
| 775 | 'label': label.short_representation(), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 776 | 'atomic_group_class': |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 777 | atomic_group_class.short_representation(), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 778 | '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 Li | 0a99391 | 2011-05-17 12:56:25 -0700 | [diff] [blame] | 788 | raise exceptions.BadRequest('"aborted" can only be set to true') |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 789 | 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 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 795 | class QueueEntryCollection(resource_lib.Collection): |
| 796 | queryset = models.HostQueueEntry.objects.order_by('-id') |
| 797 | entry_class = QueueEntry |
| 798 | |
| 799 | |
| 800 | class HealthTask(resource_lib.InstanceEntry): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 801 | _permitted_methods = ('GET',) |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 802 | model = models.SpecialTask |
| 803 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 804 | |
| 805 | @classmethod |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 806 | 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): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 812 | instance = models.SpecialTask.objects.get(id=task_id) |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 813 | return cls(request, instance) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 814 | |
| 815 | |
| 816 | def _uri_args(self): |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 817 | return {'task_id': self.instance.id} |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 818 | |
| 819 | |
| 820 | def short_representation(self): |
| 821 | rep = super(HealthTask, self).short_representation() |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 822 | host = Host(self._request, self.instance.host) |
| 823 | queue_entry = QueueEntry.from_optional_instance( |
| 824 | self._request, self.instance.queue_entry) |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 825 | rep.update( |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 826 | {'host': host.short_representation(), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 827 | 'task_type': self.instance.task, |
| 828 | 'started_on': |
| 829 | self._format_datetime(self.instance.time_started), |
| 830 | 'status': self.instance.status, |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 831 | 'queue_entry': queue_entry.short_representation() |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 832 | }) |
| 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 | |
jamesren | 3981f44 | 2010-02-16 19:27:59 +0000 | [diff] [blame] | 850 | class 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 | |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 858 | class ResourceDirectory(resource_lib.Resource): |
| 859 | _permitted_methods = ('GET',) |
| 860 | |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 861 | def handle_request(self): |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 862 | result = self.link() |
| 863 | result.update({ |
showard | f46ad4c | 2010-02-03 20:28:59 +0000 | [diff] [blame] | 864 | '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(), |
showard | f828c77 | 2010-01-25 21:49:42 +0000 | [diff] [blame] | 875 | }) |
| 876 | return self._basic_response(result) |