def testWaitingForNonHomogenousOperations(self): def CreateRegionalOperation(status): return self.messages.Operation( name='operation-0', operationType='insert', selfLink= ('https://compute.googleapis.com/compute/v1/projects/my-project/' 'regions/us-central2/operations/operation-0'), status=status, targetLink= ('https://compute.googleapis.com/compute/v1/projects/my-project/' 'regions/us-central2/address-0'), region= ('https://compute.googleapis.com/compute/v1/projects/my-project/' 'regions/us-central2')) self.RegisterCalls( Call(requests=[(self.compute.regionOperations, 'Wait', self.messages.ComputeRegionOperationsWaitRequest( operation='operation-0', project='my-project', region='us-central2')), (self.compute.zoneOperations, 'Wait', self.messages.ComputeZoneOperationsWaitRequest( operation='operation-0', project='my-project', zone='us-central2-a'))], responses=[ CreateRegionalOperation(DONE_V1), self.CreateOperations([DONE_V1])[0] ]), Call(requests=[(self.compute.addresses, 'Get', self.messages.ComputeAddressesGetRequest( address='address-0', project='my-project', region='us-central2')), (self.compute.instances, 'Get', self.messages.ComputeInstancesGetRequest( instance='instance-0', project='my-project', zone='us-central2-a'))], responses=[ self.messages.Address(name='address-0'), self.messages.Instance(name='instance-0') ])) self.assertEqual( self.Wait(operations_data=[ waiters.OperationData(CreateRegionalOperation(PENDING_V1), self.compute.regionOperations, self.compute.addresses, project='my-project'), self.CreateOperationsData([PENDING_V1])[0] ]), [ self.messages.Address(name='address-0'), self.messages.Instance(name='instance-0') ]) self.AssertSleeps() self.assertEqual(self.errors, []) self.assertEqual(self.warnings, [])
def testWithProgressTracker(self): operations = [ self.messages.Operation(targetLink='my-instance', zone='my-zone') ] resources = [self.messages.Instance(name='my-instance')] self.batch_helper.side_effect = iter([ (operations, []), ]) self.wait_for_operations.side_effect = iter([ resources, ]) requests = [ (self.compute_v1.instances, 'Insert', self.messages.ComputeInstancesInsertRequest( instance=self.messages.Instance(name='my-instance'), project='my-project', zone='my-zone')), ] errors = [] mock_progress_tracker = mock.MagicMock() responses = request_helper.MakeRequests( requests=requests, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute', errors=errors, progress_tracker=mock_progress_tracker) self.assertEqual(list(responses), resources) self.assertFalse(errors) self.batch_helper.assert_called_once_with( requests=requests, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute') operations_data = [] for operation in operations: operations_data.append( waiters.OperationData(operation, self.compute_v1.zoneOperations, self.compute_v1.instances, project='my-project')) self.wait_for_operations.assert_called_once_with( operations_data=operations_data, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute', progress_tracker=mock_progress_tracker, warnings=[], errors=[], log_result=True, timeout=None)
def _CreateOperationData(compute, messages, status, operation_id=None, operation_type=None): operation = _CreateOperation(messages, status, operation_id, operation_type) operation_data = waiters.OperationData(operation, compute.zoneOperations, compute.instances, project='my-project') return operation_data
def testWithSingleRegionMutation(self): operations = [ self.messages.Operation(targetLink='my-address', region='my-region'), ] resources = [self.messages.Address(name='my-address')] self.batch_helper.side_effect = iter([ (operations, []), ]) self.wait_for_operations.side_effect = iter([ resources, ]) requests = [ (self.compute_v1.addresses, 'Insert', self.messages.ComputeAddressesInsertRequest( address=self.messages.Address(name='my-address'), project='my-project', region='my-region')), ] errors = [] responses = request_helper.MakeRequests( requests=requests, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute', errors=errors) self.assertEqual(list(responses), resources) self.assertFalse(errors) self.batch_helper.assert_called_once_with( requests=requests, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute') operations_data = [] for operation in operations: operations_data.append( waiters.OperationData(operation, self.compute_v1.regionOperations, self.compute_v1.addresses, project='my-project')) self.wait_for_operations.assert_called_once_with( operations_data=operations_data, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute', progress_tracker=None, warnings=[], errors=[], log_result=True, timeout=None)
def CreateOperationsData(self, statuses, ids=None, operation_type=None, followup_overrides=None): operations = self.CreateOperations(statuses, ids, operation_type) if not followup_overrides: followup_overrides = [None for _ in statuses] operations_data = [] for operation, followup_override in zip(operations, followup_overrides): operations_data.append( waiters.OperationData(operation, self.compute.zoneOperations, self.compute.instances, project='my-project', followup_override=followup_override)) return operations_data
def MakeRequests(requests, http, batch_url, errors, progress_tracker=None): """Makes one or more requests to the API. Each request can be either a synchronous API call or an asynchronous one. For synchronous calls (e.g., get and list), the result from the server is yielded immediately. For asynchronous calls (e.g., calls that return operations like insert), this function waits until the operation reaches the DONE state and fetches the corresponding object and yields that object (nothing is yielded for deletions). Currently, a heterogenous set of synchronous calls can be made (e.g., get request to fetch a disk and instance), however, the asynchronous requests must be homogenous (e.g., they must all be the same verb on the same collection). In the future, heterogenous asynchronous requests will be supported. For now, it is up to the client to ensure that the asynchronous requests are homogenous. Synchronous and asynchronous requests can be mixed. Args: requests: A list of requests to make. Each element must be a 3-element tuple where the first element is the service, the second element is the string name of the method on the service, and the last element is a protocol buffer representing the request. http: An httplib2.Http-like object. batch_url: The handler for making batch requests. errors: A list for capturing errors. If any response contains an error, it is added to this list. progress_tracker: progress tracker to be ticked while waiting for operations to finish. Yields: A response for each request. For deletion requests, no corresponding responses are returned. """ if _RequestsAreListRequests(requests): for item in _List(requests=requests, http=http, batch_url=batch_url, errors=errors): yield item return responses, new_errors = batch_helper.MakeRequests(requests=requests, http=http, batch_url=batch_url) errors.extend(new_errors) operation_service = None resource_service = None project = None # Collects all operation objects in a list so they can be waited on # and yields all non-operation objects since non-operation responses # cannot be waited on. operations_data = [] for request, response in zip(requests, responses): if response is None: continue service, _, request_body = request if (isinstance(response, service.client.MESSAGES_MODULE.Operation) and service.__class__.__name__ not in ('GlobalOperationsService', 'RegionOperationsService', 'ZoneOperationsService', 'GlobalAccountsOperationsService')): resource_service = service project = request_body.project if response.zone: operation_service = service.client.zoneOperations elif response.region: operation_service = service.client.regionOperations else: operation_service = service.client.globalOperations operations_data.append( waiters.OperationData(response, project, operation_service, resource_service)) else: yield response if operations_data: warnings = [] for response in waiters.WaitForOperations( operations_data=operations_data, http=http, batch_url=batch_url, warnings=warnings, progress_tracker=progress_tracker, errors=errors): yield response if warnings: log.warning( utils.ConstructList('Some requests generated warnings:', warnings))
def testWithAsynchronousAndSynchronousRequests(self): operations = [ self.messages.Operation(targetLink='my-instance-3', zone='my-zone'), self.messages.Operation(targetLink='my-instance-4', zone='my-zone'), ] batch_helper_sync_response = [ self.messages.Instance(name='my-instance-1'), self.messages.Instance(name='my-instance-2'), ] wait_response = [ self.messages.Instance(name='my-instance-3'), self.messages.Instance(name='my-instance-4'), ] self.batch_helper.side_effect = iter([ (batch_helper_sync_response + operations, []), ]) self.wait_for_operations.side_effect = iter([ wait_response, ]) requests = [ (self.compute_v1.instances, 'Get', self.messages.ComputeInstancesGetRequest(instance='my-instance-1', project='my-project', zone='my-zone')), (self.compute_v1.instances, 'Insert', self.messages.ComputeInstancesInsertRequest( instance=self.messages.Instance(name='my-instance-3'), project='my-project', zone='my-zone')), (self.compute_v1.instances, 'Get', self.messages.ComputeInstancesGetRequest(instance='my-instance-2', project='my-project', zone='my-zone')), (self.compute_v1.instances, 'Insert', self.messages.ComputeInstancesInsertRequest( instance=self.messages.Instance(name='my-instance-4'), project='my-project', zone='my-zone')), ] errors = [] responses = request_helper.MakeRequests( requests=requests, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute', errors=errors) self.assertEqual(list(responses), batch_helper_sync_response + wait_response) self.assertFalse(errors) self.batch_helper.assert_called_once_with( requests=requests, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute') operations_data = [] for operation in operations: operations_data.append( waiters.OperationData(operation, self.compute_v1.zoneOperations, self.compute_v1.instances, project='my-project')) self.wait_for_operations.assert_called_once_with( operations_data=operations_data, http=self.mock_http, batch_url='https://www.googleapis.com/batch/compute', progress_tracker=None, warnings=[], errors=[], log_result=True, timeout=None)
def MakeRequests(requests, http, batch_url, errors, progress_tracker=None, no_followup=False, always_return_operation=False, followup_overrides=None, log_result=True, log_warnings=True, timeout=None): """Makes one or more requests to the API. Each request can be either a synchronous API call or an asynchronous one. For synchronous calls (e.g., get and list), the result from the server is yielded immediately. For asynchronous calls (e.g., calls that return operations like insert), this function waits until the operation reaches the DONE state and fetches the corresponding object and yields that object (nothing is yielded for deletions). Currently, a heterogenous set of synchronous calls can be made (e.g., get request to fetch a disk and instance), however, the asynchronous requests must be homogenous (e.g., they must all be the same verb on the same collection). In the future, heterogenous asynchronous requests will be supported. For now, it is up to the client to ensure that the asynchronous requests are homogenous. Synchronous and asynchronous requests can be mixed. Args: requests: A list of requests to make. Each element must be a 3-element tuple where the first element is the service, the second element is the string name of the method on the service, and the last element is a protocol buffer representing the request. http: An httplib2.Http-like object. batch_url: The handler for making batch requests. errors: A list for capturing errors. If any response contains an error, it is added to this list. progress_tracker: progress tracker to be ticked while waiting for operations to finish. no_followup: If True, do not followup operation with a GET request. always_return_operation: If True, return operation object even if operation fails. followup_overrides: A list of new resource names to GET once the operation finishes. Generally used in renaming calls. log_result: Whether the Operation Waiter should print the result in past tense of each request. log_warnings: Whether warnings for completed operation should be printed. timeout: The maximum amount of time, in seconds, to wait for the operations to reach the DONE state. Yields: A response for each request. For deletion requests, no corresponding responses are returned. """ if _RequestsAreListRequests(requests): for item in _List(requests=requests, http=http, batch_url=batch_url, errors=errors): yield item return responses, new_errors = batch_helper.MakeRequests(requests=requests, http=http, batch_url=batch_url) errors.extend(new_errors) operation_service = None resource_service = None # Collects all operation objects in a list so they can be waited on # and yields all non-operation objects since non-operation responses # cannot be waited on. operations_data = [] if not followup_overrides: followup_overrides = [None for _ in requests] for request, response, followup_override in zip(requests, responses, followup_overrides): if response is None: continue service, _, request_body = request if (isinstance(response, service.client.MESSAGES_MODULE.Operation) and not _IsEmptyOperation(response, service) and service.__class__.__name__ not in ('GlobalOperationsService', 'RegionOperationsService', 'ZoneOperationsService', 'GlobalOrganizationOperationsService', 'GlobalAccountsOperationsService')): resource_service = service project = None if hasattr(request_body, 'project'): project = request_body.project if response.zone: operation_service = service.client.zoneOperations elif response.region: operation_service = service.client.regionOperations else: operation_service = service.client.globalOperations else: operation_service = service.client.globalOrganizationOperations operations_data.append( waiters.OperationData( response, operation_service, resource_service, project=project, no_followup=no_followup, followup_override=followup_override, always_return_operation=always_return_operation)) else: yield response if operations_data: warnings = [] for response in waiters.WaitForOperations( operations_data=operations_data, http=http, batch_url=batch_url, warnings=warnings, progress_tracker=progress_tracker, errors=errors, log_result=log_result, timeout=timeout): yield response if warnings and log_warnings: log.warning( utils.ConstructList('Some requests generated warnings:', warnings))