def update_fields(self, model, filter_dict=None, **kwargs): """Update content in the server's DB. Args: model (type): Django model to apply changes on. filter_dict (dict): arguments to filter by. kwargs (dict): the additional arguments are the changes to apply on the filtered instances. """ if filter_dict is None: filter_dict = {} descriptor = ResourceDescriptor(resource_type=model, **filter_dict) request_data = UpdateFieldsParamsModel({ "resource_descriptor": descriptor.encode(), "changes": kwargs }) response = self.requester.request(UpdateFields, data=request_data, method="put") if isinstance(response, FailureResponseModel): raise Exception(response.details)
def update_resources(self, test_item): """Inform the result server of locked resources of a test. Args: test_item (rotest.core.case.TestCase): the test to update about. """ resources = [] if test_item.locked_resources is not None: resources = [ ResourceDescriptor(type(resource), name=resource.data.name).encode() for resource in test_item.locked_resources.itervalues() ] request_data = UpdateResourcesParamsModel({ "test_details": { "test_id": test_item.identifier, "token": self.token }, "descriptors": resources }) response = self.requester.request(UpdateResources, data=request_data, method="post") if isinstance(response, FailureResponseModel): raise RuntimeError(response.details)
def post(self, request, sessions, *args, **kwargs): """Update the resources list for a test data. Args: test_id (number): the identifier of the test. descriptors (list): the resources the test used. """ session_token = request.model.test_details.token try: session_data = sessions[session_token] test_data = \ session_data.all_tests[request.model.test_details.test_id] except KeyError: raise BadRequest("Invalid token/test_id provided " "(Test timed out?)") test_data.resources.clear() for resource_descriptor in request.model.descriptors: resource_dict = ResourceDescriptor.decode(resource_descriptor) test_data.resources.add(resource_dict.type.objects.get( **resource_dict.properties)) test_data.save() return Response({}, status=http_client.NO_CONTENT)
def post(self, request, *args, **kwargs): """Find and return the resources that answer the client's query. Args: request (Request): QueryResources request. Returns: ResourcesReply. a reply containing matching resources. """ try: descriptor = ResourceDescriptor.decode(request.model.obj) except ResourceTypeError as e: raise BadRequest(str(e)) # query for resources that are usable and match the descriptors query = (Q(is_usable=True, **descriptor.properties)) query_result = [] with transaction.atomic(): matches = descriptor.type.objects.select_for_update().filter(query) if matches.count() == 0: raise BadRequest("No existing resource meets " "the requirements: {!r}".format(descriptor)) encoder = JSONParser() query_result = [ encoder.recursive_encode(resource) for resource in matches ] return Response({"resource_descriptors": query_result}, status=http_client.OK)
def update_resources(self, test_id, resources): """Update the resources list for a test data. Args: test_id (number): the identifier of the test. resources (list): the resources the test used. """ test_data = self.all_tests[test_id] test_data.resources.clear() for resource_descriptor in resources: resource_dict = ResourceDescriptor.decode(resource_descriptor) test_data.resources.add( resource_dict.type.objects.get(**resource_dict.properties)) test_data.save()
def update_resources(self, test_item): """Inform the result server of locked resources of a test. Args: test_item (rotest.core.case.TestCase): the test to update about. """ resources = [] if test_item.locked_resources is not None: resources = [ResourceDescriptor(type(resource), name=resource.data.name).encode() for resource in test_item.locked_resources.itervalues()] msg = messages.UpdateResources(test_id=test_item.identifier, resources=resources) self._request(msg)
def test_lock_resource_message(self): """Test encoding & decoding of LockResources message.""" resource1 = ResourceDescriptor(DemoResource, name="my_resource1", ip_address="1.2.3.4", version=1) resource2 = ResourceDescriptor(DemoResource, name="my_resource2", ip_address="1.2.3.5", version=2) descriptors = [resource1.encode(), resource2.encode()] msg = LockResources(descriptors=descriptors, timeout=self.LOCK_RESOURCES_TIMEOUT) self.validate(msg)
def query_resources(self, request): """Find and return the resources that answer the client's query. Args: request (Request): QueryResources request. Returns: ResourcesReply. a reply containing matching resources. """ desc = ResourceDescriptor.decode(request.message.descriptors) self.logger.debug("Looking for resources with description %r", desc) # query for resources that are usable and match the descriptors query = (Q(is_usable=True, **desc.properties)) matches = desc.type.objects.filter(query) if matches.count() == 0: raise ResourceDoesNotExistError("No existing resource meets " "the requirements: %r" % desc) query_result = [resource for resource in matches] return ResourcesReply(resources=query_result)
def _try_to_lock_available_resource(self, username, groups, descriptor_dict): """Try to lock one of the given available resources. Args: descriptor_dict (dict): a descriptor dict of the wanted resource. Example: { "type": "resourceData", "properties": {} } username (str): the user who wants to lock the resource. groups (list): list of the resource groups that the resource should be taken from. Returns: ResourceData. the locked resource. Raises: BadRequest. If there are no available resources. """ try: descriptor = ResourceDescriptor.decode(descriptor_dict) except ResourceTypeError as e: raise BadRequest(e.message) availables = self._get_available_resources(descriptor, username, groups) try: resource = availables.next() self._lock_resource(resource, username) return resource except StopIteration: raise BadRequest(UNAVAILABLE_RESOURCES.format(descriptor))
def request_resources(self, requests, config=None, skip_init=False, save_state=False, use_previous=True, enable_debug=False, force_initialize=False, base_work_dir=ROTEST_WORK_DIR): """Lock the required resources and prepare them for work. * Requests the resources from the manager server. * Iterates over the locked resources to try to prepare them for work. * The locked and initialized resources then returned as an AttrDict. Args: requests (tuple): List of the ResourceRequest. config (dict): run configuration dictionary. skip_init (bool): True to skip resources initialize and validation. save_state (bool): Determine if storing state is required. use_previous (bool): whether to use previously locked resources and release the unused ones. enable_debug (bool): True to wrap the resource's method with debug. force_initialize (bool): determines if the resources will be initialized even if their validation succeeds. base_work_dir (str): base work directory path. Returns: AttrDict. resources AttrDict {name: BaseResource}. Raises: ServerError. resource manager failed to lock resources. """ requests = list(requests) descriptors = [ResourceDescriptor(request.type, **request.kwargs) for request in requests] initialized_resources = AttrDict() if use_previous: # Find matches in previously locked resources initialized_resources = self._retrieve_previous(requests, descriptors) self.logger.debug("Requesting resources from resource manager") locked_resources = self._lock_resources(descriptors) self.logger.info("Locked resources %s", locked_resources) try: self.logger.debug("Setting up the locked resources") for name, resource in self._setup_resources(requests, locked_resources, save_state, force_initialize, base_work_dir, config, enable_debug, skip_init): initialized_resources[name] = resource if self.keep_resources: self.locked_resources.append(resource) return initialized_resources except Exception: self._cleanup_resources(initialized_resources) self._release_resources(locked_resources) raise
def lock_resources(self, request): """Lock the given resources one by one. Note: If one of the resources fails to lock, all the resources that has been locked until that resource will be released. Args: request (Request): LockResources request. Returns: ResourcesReply. a reply containing requested resources. Raises: ResourceDoesNotExistError. at least one of the requested resources doesn't exist. ResourceUnavailableError. when the requested resources are not available. UnknownUserError. when unknown user has tried to lock a resource. """ locked_resources = [] client = request.worker.name user_name, _ = client.split(":") # splitting <user_name>:<port> if not auth_models.User.objects.filter(username=user_name).exists(): raise UnknownUserError("User %r has no matching object in the DB" % user_name) user = auth_models.User.objects.get(username=user_name) groups = list(user.groups.all()) for descriptor_dict in request.message.descriptors: desc = ResourceDescriptor.decode(descriptor_dict) self.logger.debug("Locking %r resource", desc) # query for resources that are usable and match the user's # preference, which are either belong to a group he's in or # don't belong to any group. query = (Q(is_usable=True, **desc.properties) & (Q(group__isnull=True) | Q(group__in=groups))) matches = desc.type.objects.filter(query).order_by('-reserved') if matches.count() == 0: raise ResourceDoesNotExistError("No existing resource meets " "the requirements: %r" % desc) availables = (resource for resource in matches if resource.is_available(client)) try: resource = availables.next() self._lock_resource(resource, client) locked_resources.append(resource) self.logger.debug("Resource %r locked successfully", desc) except StopIteration: timeout = request.message.timeout waiting_time = time.time() - request.creation_time if timeout is not None and waiting_time > timeout: raise ResourceUnavailableError("No available resource " "meets the requirements: " "%r" % desc) raise _WaitingForResourceException( "Resource %r is unavailable" ", waiting for it to be " "released", desc) return ResourcesReply(resources=locked_resources)