| from django import http |
| from autotest_lib.frontend.shared import query_lib, resource_lib, exceptions |
| from autotest_lib.frontend.afe import control_file, models, rpc_utils |
| from autotest_lib.frontend.afe import model_attributes |
| from autotest_lib.frontend import thread_local |
| from autotest_lib.client.common_lib import host_protections |
| from autotest_lib.client.common_lib import control_data |
| from autotest_lib.client.common_lib import priorities |
| |
| |
| class EntryWithInvalid(resource_lib.InstanceEntry): |
| def put(self): |
| if self.instance.invalid: |
| raise http.Http404('%s has been deleted' % self.instance) |
| return super(EntryWithInvalid, self).put() |
| |
| |
| def delete(self): |
| if self.instance.invalid: |
| raise http.Http404('%s has already been deleted' % self.instance) |
| return super(EntryWithInvalid, self).delete() |
| |
| |
| class AtomicGroupClass(EntryWithInvalid): |
| model = models.AtomicGroup |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, ag_name, **kwargs): |
| return cls(request, models.AtomicGroup.objects.get(name=ag_name)) |
| |
| |
| def _uri_args(self): |
| return {'ag_name': self.instance.name} |
| |
| |
| def short_representation(self): |
| rep = super(AtomicGroupClass, self).short_representation() |
| rep['name'] = self.instance.name |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(AtomicGroupClass, self).full_representation() |
| rep.update({'max_number_of_machines': |
| self.instance.max_number_of_machines, |
| 'labels': |
| AtomicLabelTaggingCollection(fixed_entry=self).link()}) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| cls._check_for_required_fields(input_dict, ('name',)) |
| return models.AtomicGroup.add_object(name=input_dict['name']) |
| |
| |
| def update(self, input_dict): |
| data = {'max_number_of_machines': |
| input_dict.get('max_number_of_machines')} |
| data = input_dict.remove_unspecified_fields(data) |
| self.instance.update_object(**data) |
| |
| |
| class AtomicGroupClassCollection(resource_lib.Collection): |
| queryset = models.AtomicGroup.valid_objects.all() |
| entry_class = AtomicGroupClass |
| |
| |
| class Label(EntryWithInvalid): |
| model = models.Label |
| |
| @classmethod |
| def add_query_selectors(cls, query_processor): |
| query_processor.add_field_selector('name') |
| query_processor.add_field_selector( |
| 'is_platform', field='platform', |
| value_transform=query_processor.read_boolean) |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, label_name, **kwargs): |
| return cls(request, models.Label.objects.get(name=label_name)) |
| |
| |
| def _uri_args(self): |
| return {'label_name': self.instance.name} |
| |
| |
| def short_representation(self): |
| rep = super(Label, self).short_representation() |
| rep.update({'name': self.instance.name, |
| 'is_platform': bool(self.instance.platform)}) |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(Label, self).full_representation() |
| atomic_group_class = AtomicGroupClass.from_optional_instance( |
| self._request, self.instance.atomic_group) |
| rep.update({'atomic_group_class': |
| atomic_group_class.short_representation(), |
| 'hosts': HostLabelingCollection(fixed_entry=self).link()}) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| cls._check_for_required_fields(input_dict, ('name',)) |
| return models.Label.add_object(name=input_dict['name']) |
| |
| |
| def update(self, input_dict): |
| # TODO update atomic group |
| if 'is_platform' in input_dict: |
| self.instance.platform = input_dict['is_platform'] |
| self.instance.save() |
| |
| |
| class LabelCollection(resource_lib.Collection): |
| queryset = models.Label.valid_objects.all() |
| entry_class = Label |
| |
| |
| class AtomicLabelTagging(resource_lib.Relationship): |
| related_classes = {'label': Label, 'atomic_group_class': AtomicGroupClass} |
| |
| |
| class AtomicLabelTaggingCollection(resource_lib.RelationshipCollection): |
| entry_class = AtomicLabelTagging |
| |
| |
| class User(resource_lib.InstanceEntry): |
| model = models.User |
| _permitted_methods = ('GET,') |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, username, **kwargs): |
| if username == '@me': |
| username = models.User.current_user().login |
| return cls(request, models.User.objects.get(login=username)) |
| |
| |
| def _uri_args(self): |
| return {'username': self.instance.login} |
| |
| |
| def short_representation(self): |
| rep = super(User, self).short_representation() |
| rep['username'] = self.instance.login |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(User, self).full_representation() |
| accessible_hosts = HostCollection(self._request) |
| accessible_hosts.set_query_parameters(accessible_by=self.instance.login) |
| rep.update({'jobs': 'TODO', |
| 'recurring_runs': 'TODO', |
| 'acls': |
| UserAclMembershipCollection(fixed_entry=self).link(), |
| 'accessible_hosts': accessible_hosts.link()}) |
| return rep |
| |
| |
| class UserCollection(resource_lib.Collection): |
| _permitted_methods = ('GET',) |
| queryset = models.User.objects.all() |
| entry_class = User |
| |
| |
| class Acl(resource_lib.InstanceEntry): |
| _permitted_methods = ('GET',) |
| model = models.AclGroup |
| |
| @classmethod |
| def from_uri_args(cls, request, acl_name, **kwargs): |
| return cls(request, models.AclGroup.objects.get(name=acl_name)) |
| |
| |
| def _uri_args(self): |
| return {'acl_name': self.instance.name} |
| |
| |
| def short_representation(self): |
| rep = super(Acl, self).short_representation() |
| rep['name'] = self.instance.name |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(Acl, self).full_representation() |
| rep.update({'users': |
| UserAclMembershipCollection(fixed_entry=self).link(), |
| 'hosts': |
| HostAclMembershipCollection(fixed_entry=self).link()}) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| cls._check_for_required_fields(input_dict, ('name',)) |
| return models.AclGroup.add_object(name=input_dict['name']) |
| |
| |
| def update(self, input_dict): |
| pass |
| |
| |
| class AclCollection(resource_lib.Collection): |
| queryset = models.AclGroup.objects.all() |
| entry_class = Acl |
| |
| |
| class UserAclMembership(resource_lib.Relationship): |
| related_classes = {'user': User, 'acl': Acl} |
| |
| # TODO: check permissions |
| # TODO: check for and add/remove "Everyone" |
| |
| |
| class UserAclMembershipCollection(resource_lib.RelationshipCollection): |
| entry_class = UserAclMembership |
| |
| |
| class Host(EntryWithInvalid): |
| model = models.Host |
| |
| @classmethod |
| def add_query_selectors(cls, query_processor): |
| query_processor.add_field_selector('hostname') |
| query_processor.add_field_selector( |
| 'locked', value_transform=query_processor.read_boolean) |
| query_processor.add_field_selector( |
| 'locked_by', field='locked_by__login', |
| doc='Username of user who locked this host, if locked') |
| query_processor.add_field_selector('status') |
| query_processor.add_field_selector( |
| 'protection_level', field='protection', |
| doc='Verify/repair protection level', |
| value_transform=cls._read_protection) |
| query_processor.add_field_selector( |
| 'accessible_by', field='aclgroup__users__login', |
| doc='Username of user with access to this host') |
| query_processor.add_related_existence_selector( |
| 'has_label', models.Label, 'name') |
| |
| |
| @classmethod |
| def _read_protection(cls, protection_input): |
| return host_protections.Protection.get_value(protection_input) |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, hostname, **kwargs): |
| return cls(request, models.Host.objects.get(hostname=hostname)) |
| |
| |
| def _uri_args(self): |
| return {'hostname': self.instance.hostname} |
| |
| |
| def short_representation(self): |
| rep = super(Host, self).short_representation() |
| # TODO calling platform() over and over is inefficient |
| platform_rep = (Label.from_optional_instance(self._request, |
| self.instance.platform()) |
| .short_representation()) |
| rep.update({'hostname': self.instance.hostname, |
| 'locked': bool(self.instance.locked), |
| 'status': self.instance.status, |
| 'platform': platform_rep}) |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(Host, self).full_representation() |
| protection = host_protections.Protection.get_string( |
| self.instance.protection) |
| locked_by = (User.from_optional_instance(self._request, |
| self.instance.locked_by) |
| .short_representation()) |
| labels = HostLabelingCollection(fixed_entry=self) |
| acls = HostAclMembershipCollection(fixed_entry=self) |
| queue_entries = QueueEntryCollection(self._request) |
| queue_entries.set_query_parameters(host=self.instance.hostname) |
| health_tasks = HealthTaskCollection(self._request) |
| health_tasks.set_query_parameters(host=self.instance.hostname) |
| |
| rep.update({'locked_by': locked_by, |
| 'locked_on': self._format_datetime(self.instance.lock_time), |
| 'invalid': self.instance.invalid, |
| 'protection_level': protection, |
| # TODO make these efficient |
| 'labels': labels.full_representation(), |
| 'acls': acls.full_representation(), |
| 'queue_entries': queue_entries.link(), |
| 'health_tasks': health_tasks.link()}) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| cls._check_for_required_fields(input_dict, ('hostname',)) |
| # include locked here, rather than waiting for update(), to avoid race |
| # conditions |
| host = models.Host.add_object(hostname=input_dict['hostname'], |
| locked=input_dict.get('locked', False)) |
| return host |
| |
| def update(self, input_dict): |
| data = {'locked': input_dict.get('locked'), |
| 'protection': input_dict.get('protection_level')} |
| data = input_dict.remove_unspecified_fields(data) |
| |
| if 'protection' in data: |
| data['protection'] = self._read_protection(data['protection']) |
| |
| self.instance.update_object(**data) |
| |
| if 'platform' in input_dict: |
| label = self.resolve_link(input_dict['platform']) .instance |
| if not label.platform: |
| raise exceptions.BadRequest('Label %s is not a platform' % label.name) |
| for label in self.instance.labels.filter(platform=True): |
| self.instance.labels.remove(label) |
| self.instance.labels.add(label) |
| |
| |
| class HostCollection(resource_lib.Collection): |
| queryset = models.Host.valid_objects.all() |
| entry_class = Host |
| |
| |
| class HostLabeling(resource_lib.Relationship): |
| related_classes = {'host': Host, 'label': Label} |
| |
| |
| class HostLabelingCollection(resource_lib.RelationshipCollection): |
| entry_class = HostLabeling |
| |
| |
| class HostAclMembership(resource_lib.Relationship): |
| related_classes = {'host': Host, 'acl': Acl} |
| |
| # TODO: acl.check_for_acl_violation_acl_group() |
| # TODO: models.AclGroup.on_host_membership_change() |
| |
| |
| class HostAclMembershipCollection(resource_lib.RelationshipCollection): |
| entry_class = HostAclMembership |
| |
| |
| class Test(resource_lib.InstanceEntry): |
| model = models.Test |
| |
| |
| @classmethod |
| def add_query_selectors(cls, query_processor): |
| query_processor.add_field_selector('name') |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, test_name, **kwargs): |
| return cls(request, models.Test.objects.get(name=test_name)) |
| |
| |
| def _uri_args(self): |
| return {'test_name': self.instance.name} |
| |
| |
| def short_representation(self): |
| rep = super(Test, self).short_representation() |
| rep['name'] = self.instance.name |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(Test, self).full_representation() |
| rep.update({'author': self.instance.author, |
| 'class': self.instance.test_class, |
| 'control_file_type': |
| control_data.CONTROL_TYPE.get_string( |
| self.instance.test_type), |
| 'control_file_path': self.instance.path, |
| 'sync_count': self.instance.sync_count, |
| 'dependencies': |
| TestDependencyCollection(fixed_entry=self).link(), |
| }) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| cls._check_for_required_fields(input_dict, |
| ('name', 'control_file_type', |
| 'control_file_path')) |
| test_type = control_data.CONTROL_TYPE.get_value( |
| input['control_file_type']) |
| return models.Test.add_object(name=input_dict['name'], |
| test_type=test_type, |
| path=input_dict['control_file_path']) |
| |
| |
| def update(self, input_dict): |
| data = {'test_type': input_dict.get('control_file_type'), |
| 'path': input_dict.get('control_file_path'), |
| 'class': input_dict.get('class'), |
| } |
| data = input_dict.remove_unspecified_fields(data) |
| self.instance.update_object(**data) |
| |
| |
| class TestCollection(resource_lib.Collection): |
| queryset = models.Test.objects.all() |
| entry_class = Test |
| |
| |
| class TestDependency(resource_lib.Relationship): |
| related_classes = {'test': Test, 'label': Label} |
| |
| |
| class TestDependencyCollection(resource_lib.RelationshipCollection): |
| entry_class = TestDependency |
| |
| |
| # TODO profilers |
| |
| |
| class ExecutionInfo(resource_lib.Resource): |
| _permitted_methods = ('GET','POST') |
| _job_fields = models.Job.get_field_dict() |
| _DEFAULTS = { |
| 'control_file': '', |
| 'is_server': True, |
| 'dependencies': [], |
| 'machines_per_execution': 1, |
| 'run_verify': bool(_job_fields['run_verify'].default), |
| 'run_reset': bool(_job_fields['run_reset'].default), |
| 'timeout_mins': _job_fields['timeout_mins'].default, |
| 'maximum_runtime_mins': _job_fields['max_runtime_mins'].default, |
| 'cleanup_before_job': |
| model_attributes.RebootBefore.get_string( |
| models.DEFAULT_REBOOT_BEFORE), |
| 'cleanup_after_job': |
| model_attributes.RebootAfter.get_string( |
| models.DEFAULT_REBOOT_AFTER), |
| } |
| |
| |
| def _query_parameters_accepted(self): |
| return (('tests', 'Comma-separated list of test names to run'), |
| ('kernels', 'TODO'), |
| ('client_control_file', |
| 'Client control file segment to run after all specified ' |
| 'tests'), |
| ('profilers', |
| 'Comma-separated list of profilers to activate during the ' |
| 'job'), |
| ('use_container', 'TODO'), |
| ('profile_only', |
| 'If true, run only profiled iterations; otherwise, always run ' |
| 'at least one non-profiled iteration in addition to a ' |
| 'profiled iteration'), |
| ('upload_kernel_config', |
| 'If true, generate a server control file code that uploads ' |
| 'the kernel config file to the client and tells the client of ' |
| 'the new (local) path when compiling the kernel; the tests ' |
| 'must be server side tests')) |
| |
| |
| @classmethod |
| def execution_info_from_job(cls, job): |
| return {'control_file': job.control_file, |
| 'is_server': |
| job.control_type == control_data.CONTROL_TYPE.SERVER, |
| 'dependencies': [label.name for label |
| in job.dependency_labels.all()], |
| 'machines_per_execution': job.synch_count, |
| 'run_verify': bool(job.run_verify), |
| 'run_reset': bool(job.run_reset), |
| 'timeout_mins': job.timeout_mins, |
| 'maximum_runtime_mins': job.max_runtime_mins, |
| 'cleanup_before_job': |
| model_attributes.RebootBefore.get_string(job.reboot_before), |
| 'cleanup_after_job': |
| model_attributes.RebootAfter.get_string(job.reboot_after), |
| } |
| |
| |
| def _get_execution_info(self, input_dict): |
| tests = input_dict.get('tests', '') |
| client_control_file = input_dict.get('client_control_file', None) |
| if not tests and not client_control_file: |
| return self._DEFAULTS |
| |
| test_list = tests.split(',') |
| if 'profilers' in input_dict: |
| profilers_list = input_dict['profilers'].split(',') |
| else: |
| profilers_list = [] |
| kernels = input_dict.get('kernels', '') # TODO |
| if kernels: |
| kernels = [dict(version=kernel) for kernel in kernels.split(',')] |
| |
| cf_info, test_objects, profiler_objects, label = ( |
| rpc_utils.prepare_generate_control_file( |
| test_list, kernels, None, profilers_list)) |
| control_file_contents = control_file.generate_control( |
| tests=test_objects, kernels=kernels, |
| profilers=profiler_objects, is_server=cf_info['is_server'], |
| client_control_file=client_control_file, |
| profile_only=input_dict.get('profile_only', None), |
| upload_kernel_config=input_dict.get( |
| 'upload_kernel_config', None)) |
| return dict(self._DEFAULTS, |
| control_file=control_file_contents, |
| is_server=cf_info['is_server'], |
| dependencies=cf_info['dependencies'], |
| machines_per_execution=cf_info['synch_count']) |
| |
| |
| def handle_request(self): |
| result = self.link() |
| result['execution_info'] = self._get_execution_info( |
| self._request.REQUEST) |
| return self._basic_response(result) |
| |
| |
| class QueueEntriesRequest(resource_lib.Resource): |
| _permitted_methods = ('GET',) |
| |
| |
| def _query_parameters_accepted(self): |
| return (('hosts', 'Comma-separated list of hostnames'), |
| ('one_time_hosts', |
| 'Comma-separated list of hostnames not already in the ' |
| 'Autotest system'), |
| ('meta_hosts', |
| 'Comma-separated list of label names; for each one, an entry ' |
| 'will be created and assigned at runtime to an available host ' |
| 'with that label'), |
| ('atomic_group_class', 'TODO')) |
| |
| |
| def _read_list(self, list_string): |
| if list_string: |
| return list_string.split(',') |
| return [] |
| |
| |
| def handle_request(self): |
| request_dict = self._request.REQUEST |
| hosts = self._read_list(request_dict.get('hosts')) |
| one_time_hosts = self._read_list(request_dict.get('one_time_hosts')) |
| meta_hosts = self._read_list(request_dict.get('meta_hosts')) |
| atomic_group_class = request_dict.get('atomic_group_class') |
| |
| # TODO: bring in all the atomic groups magic from create_job() |
| |
| entries = [] |
| for hostname in one_time_hosts: |
| models.Host.create_one_time_host(hostname) |
| for hostname in hosts: |
| entry = Host.from_uri_args(self._request, hostname) |
| entries.append({'host': entry.link()}) |
| for label_name in meta_hosts: |
| entry = Label.from_uri_args(self._request, label_name) |
| entries.append({'meta_host': entry.link()}) |
| if atomic_group_class: |
| entries.append({'atomic_group_class': atomic_group_class}) |
| |
| result = self.link() |
| result['queue_entries'] = entries |
| return self._basic_response(result) |
| |
| |
| class Job(resource_lib.InstanceEntry): |
| _permitted_methods = ('GET',) |
| model = models.Job |
| |
| |
| class _StatusConstraint(query_lib.Constraint): |
| def apply_constraint(self, queryset, value, comparison_type, |
| is_inverse): |
| if comparison_type != 'equals' or is_inverse: |
| raise query_lib.ConstraintError('Can only use this selector ' |
| 'with equals') |
| non_queued_statuses = [ |
| status for status, _ |
| in models.HostQueueEntry.Status.choices() |
| if status != models.HostQueueEntry.Status.QUEUED] |
| if value == 'queued': |
| return queryset.exclude( |
| hostqueueentry__status__in=non_queued_statuses) |
| elif value == 'active': |
| return queryset.filter( |
| hostqueueentry__status__in=non_queued_statuses).filter( |
| hostqueueentry__complete=False).distinct() |
| elif value == 'complete': |
| return queryset.exclude(hostqueueentry__complete=False) |
| else: |
| raise query_lib.ConstraintError('Value must be one of queued, ' |
| 'active or complete') |
| |
| |
| @classmethod |
| def add_query_selectors(cls, query_processor): |
| query_processor.add_field_selector('id') |
| query_processor.add_field_selector('name') |
| query_processor.add_selector( |
| query_lib.Selector('status', |
| doc='One of queued, active or complete'), |
| Job._StatusConstraint()) |
| query_processor.add_keyval_selector('has_keyval', models.JobKeyval, |
| 'key', 'value') |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, job_id, **kwargs): |
| return cls(request, models.Job.objects.get(id=job_id)) |
| |
| |
| def _uri_args(self): |
| return {'job_id': self.instance.id} |
| |
| |
| @classmethod |
| def _do_prepare_for_full_representation(cls, instances): |
| models.Job.objects.populate_relationships(instances, models.JobKeyval, |
| 'keyvals') |
| |
| |
| def short_representation(self): |
| rep = super(Job, self).short_representation() |
| try: |
| string_priority = priorities.Priority.get_string( |
| self.instance.priority) |
| except ValueError: |
| string_priority = str(self.instance.priority) |
| rep.update({'id': self.instance.id, |
| 'owner': self.instance.owner, |
| 'name': self.instance.name, |
| 'priority': string_priority, |
| 'created_on': |
| self._format_datetime(self.instance.created_on), |
| }) |
| return rep |
| |
| |
| def full_representation(self): |
| rep = super(Job, self).full_representation() |
| queue_entries = QueueEntryCollection(self._request) |
| queue_entries.set_query_parameters(job=self.instance.id) |
| drone_set = self.instance.drone_set and self.instance.drone_set.name |
| rep.update({'email_list': self.instance.email_list, |
| 'parse_failed_repair': |
| bool(self.instance.parse_failed_repair), |
| 'drone_set': drone_set, |
| 'execution_info': |
| ExecutionInfo.execution_info_from_job(self.instance), |
| 'queue_entries': queue_entries.link(), |
| 'keyvals': dict((keyval.key, keyval.value) |
| for keyval in self.instance.keyvals) |
| }) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| owner = input_dict.get('owner') |
| if not owner: |
| owner = models.User.current_user().login |
| |
| cls._check_for_required_fields(input_dict, ('name', 'execution_info', |
| 'queue_entries')) |
| execution_info = input_dict['execution_info'] |
| cls._check_for_required_fields(execution_info, ('control_file', |
| 'is_server')) |
| |
| if execution_info['is_server']: |
| control_type = control_data.CONTROL_TYPE.SERVER |
| else: |
| control_type = control_data.CONTROL_TYPE.CLIENT |
| options = dict( |
| name=input_dict['name'], |
| priority=input_dict.get('priority', None), |
| control_file=execution_info['control_file'], |
| control_type=control_type, |
| is_template=input_dict.get('is_template', None), |
| timeout_mins=execution_info.get('timeout_mins'), |
| max_runtime_mins=execution_info.get('maximum_runtime_mins'), |
| synch_count=execution_info.get('machines_per_execution'), |
| run_verify=execution_info.get('run_verify'), |
| run_reset=execution_info.get('run_reset'), |
| email_list=input_dict.get('email_list', None), |
| dependencies=execution_info.get('dependencies', ()), |
| reboot_before=execution_info.get('cleanup_before_job'), |
| reboot_after=execution_info.get('cleanup_after_job'), |
| parse_failed_repair=input_dict.get('parse_failed_repair', None), |
| drone_set=input_dict.get('drone_set', None), |
| keyvals=input_dict.get('keyvals', None)) |
| |
| host_objects, metahost_label_objects, atomic_group = [], [], None |
| for queue_entry in input_dict['queue_entries']: |
| if 'host' in queue_entry: |
| host = queue_entry['host'] |
| if host: # can be None, indicated a hostless job |
| host_entry = containing_collection.resolve_link(host) |
| host_objects.append(host_entry.instance) |
| elif 'meta_host' in queue_entry: |
| label_entry = containing_collection.resolve_link( |
| queue_entry['meta_host']) |
| metahost_label_objects.append(label_entry.instance) |
| if 'atomic_group_class' in queue_entry: |
| atomic_group_entry = containing_collection.resolve_link( |
| queue_entry['atomic_group_class']) |
| if atomic_group: |
| assert atomic_group_entry.instance.id == atomic_group.id |
| else: |
| atomic_group = atomic_group_entry.instance |
| |
| job_id = rpc_utils.create_new_job( |
| owner=owner, |
| options=options, |
| host_objects=host_objects, |
| metahost_objects=metahost_label_objects, |
| atomic_group=atomic_group) |
| return models.Job.objects.get(id=job_id) |
| |
| |
| def update(self, input_dict): |
| # Required for POST, doesn't actually support PUT |
| pass |
| |
| |
| class JobCollection(resource_lib.Collection): |
| queryset = models.Job.objects.order_by('-id') |
| entry_class = Job |
| |
| |
| class QueueEntry(resource_lib.InstanceEntry): |
| _permitted_methods = ('GET', 'PUT') |
| model = models.HostQueueEntry |
| |
| |
| @classmethod |
| def add_query_selectors(cls, query_processor): |
| query_processor.add_field_selector('host', field='host__hostname') |
| query_processor.add_field_selector('job', field='job__id') |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, queue_entry_id): |
| instance = models.HostQueueEntry.objects.get(id=queue_entry_id) |
| return cls(request, instance) |
| |
| |
| def _uri_args(self): |
| return {'queue_entry_id': self.instance.id} |
| |
| |
| def short_representation(self): |
| rep = super(QueueEntry, self).short_representation() |
| if self.instance.host: |
| host = (Host(self._request, self.instance.host) |
| .short_representation()) |
| else: |
| host = None |
| job = Job(self._request, self.instance.job) |
| host = Host.from_optional_instance(self._request, self.instance.host) |
| label = Label.from_optional_instance(self._request, |
| self.instance.meta_host) |
| atomic_group_class = AtomicGroupClass.from_optional_instance( |
| self._request, self.instance.atomic_group) |
| rep.update( |
| {'job': job.short_representation(), |
| 'host': host.short_representation(), |
| 'label': label.short_representation(), |
| 'atomic_group_class': |
| atomic_group_class.short_representation(), |
| 'status': self.instance.status, |
| 'execution_path': self.instance.execution_subdir, |
| 'started_on': self._format_datetime(self.instance.started_on), |
| 'aborted': bool(self.instance.aborted)}) |
| return rep |
| |
| |
| def update(self, input_dict): |
| if 'aborted' in input_dict: |
| if input_dict['aborted'] != True: |
| raise exceptions.BadRequest('"aborted" can only be set to true') |
| query = models.HostQueueEntry.objects.filter(pk=self.instance.pk) |
| models.AclGroup.check_abort_permissions(query) |
| rpc_utils.check_abort_synchronous_jobs(query) |
| self.instance.abort(thread_local.get_user()) |
| |
| |
| class QueueEntryCollection(resource_lib.Collection): |
| queryset = models.HostQueueEntry.objects.order_by('-id') |
| entry_class = QueueEntry |
| |
| |
| class HealthTask(resource_lib.InstanceEntry): |
| _permitted_methods = ('GET',) |
| model = models.SpecialTask |
| |
| |
| @classmethod |
| def add_query_selectors(cls, query_processor): |
| query_processor.add_field_selector('host', field='host__hostname') |
| |
| |
| @classmethod |
| def from_uri_args(cls, request, task_id): |
| instance = models.SpecialTask.objects.get(id=task_id) |
| return cls(request, instance) |
| |
| |
| def _uri_args(self): |
| return {'task_id': self.instance.id} |
| |
| |
| def short_representation(self): |
| rep = super(HealthTask, self).short_representation() |
| host = Host(self._request, self.instance.host) |
| queue_entry = QueueEntry.from_optional_instance( |
| self._request, self.instance.queue_entry) |
| rep.update( |
| {'host': host.short_representation(), |
| 'task_type': self.instance.task, |
| 'started_on': |
| self._format_datetime(self.instance.time_started), |
| 'status': self.instance.status, |
| 'queue_entry': queue_entry.short_representation() |
| }) |
| return rep |
| |
| |
| @classmethod |
| def create_instance(cls, input_dict, containing_collection): |
| cls._check_for_required_fields(input_dict, ('task_type',)) |
| host = containing_collection.base_entry.instance |
| models.AclGroup.check_for_acl_violation_hosts((host,)) |
| return models.SpecialTask.schedule_special_task(host, |
| input_dict['task_type']) |
| |
| |
| def update(self, input_dict): |
| # Required for POST, doesn't actually support PUT |
| pass |
| |
| |
| class HealthTaskCollection(resource_lib.Collection): |
| entry_class = HealthTask |
| |
| |
| def _fresh_queryset(self): |
| return models.SpecialTask.objects.order_by('-id') |
| |
| |
| class ResourceDirectory(resource_lib.Resource): |
| _permitted_methods = ('GET',) |
| |
| def handle_request(self): |
| result = self.link() |
| result.update({ |
| 'atomic_group_classes': |
| AtomicGroupClassCollection(self._request).link(), |
| 'labels': LabelCollection(self._request).link(), |
| 'users': UserCollection(self._request).link(), |
| 'acl_groups': AclCollection(self._request).link(), |
| 'hosts': HostCollection(self._request).link(), |
| 'tests': TestCollection(self._request).link(), |
| 'execution_info': ExecutionInfo(self._request).link(), |
| 'queue_entries_request': |
| QueueEntriesRequest(self._request).link(), |
| 'jobs': JobCollection(self._request).link(), |
| }) |
| return self._basic_response(result) |