def CreateInstanceReferences(resources, compute_client, igm_ref, instance_names_or_urls): """Creates reference to instances in instance group (zonal or regional). Args: resources: Resources parser for the client. compute_client: Client for the current release track. igm_ref: URL to the target IGM. instance_names_or_urls: names or full URLs of target instances. Returns: A dict where instance names are keys, and corresponding references are values. Unresolved names are present in dict with value None. """ _InstanceNameWithReference = collections.namedtuple( 'InstanceNameWithReference', ['instance_name', 'instance_reference']) instance_references = [] # Make sure we don't have URLs here. names_to_resolve = [ path_simplifier.Name(name_or_url) for name_or_url in instance_names_or_urls ] if igm_ref.Collection() == 'compute.instanceGroupManagers': for instance_name in names_to_resolve: instance_ref = resources.Parse(instance_name, params={ 'project': igm_ref.project, 'zone': igm_ref.zone, }, collection='compute.instances') instance_references.append( _InstanceNameWithReference( instance_name=instance_name, instance_reference=instance_ref.SelfLink())) return instance_references elif igm_ref.Collection() == 'compute.regionInstanceGroupManagers': service = compute_client.apitools_client.regionInstanceGroupManagers request = service.GetRequestType('ListManagedInstances')( instanceGroupManager=igm_ref.Name(), region=igm_ref.region, project=igm_ref.project) resolved_references = {} for instance_ref in compute_client.MakeRequests( requests=[(service, 'ListManagedInstances', request)]): resolved_references[path_simplifier.Name( instance_ref.instance)] = instance_ref.instance for instance_name in names_to_resolve: if instance_name in resolved_references: instance_references.append( _InstanceNameWithReference( instance_name=instance_name, instance_reference=resolved_references[instance_name])) else: instance_references.append( _InstanceNameWithReference(instance_name=instance_name, instance_reference=None)) return instance_references else: raise ValueError('Unknown reference type {0}'.format( igm_ref.Collection()))
def ResourceGetRequest(self): """"Generates apitools request message to get the resource.""" target_link = self.operation.targetLink if self.project: request = self.resource_service.GetRequestType('Get')( project=self.project) else: # Gets the flexible resource ID. if target_link is None: log.status.write('{0}.\n'.format( _HumanFriendlyNameForOpPastTense( self.operation.operationType).capitalize())) return token_list = target_link.split('/') flexible_resource_id = token_list[-1] request = self.resource_service.GetRequestType('Get')( securityPolicy=flexible_resource_id) if self.operation.zone: request.zone = path_simplifier.Name(self.operation.zone) elif self.operation.region: request.region = path_simplifier.Name(self.operation.region) name_field = self.resource_service.GetMethodConfig( 'Get').ordered_params[-1] resource_name = self.followup_override or path_simplifier.Name( self.operation.targetLink) setattr(request, name_field, resource_name) return request
def _LocationName(instance_group): """Returns a location name, could be region name or zone name.""" if 'zone' in instance_group: return path_simplifier.Name(instance_group['zone']) elif 'region' in instance_group: return path_simplifier.Name(instance_group['region']) else: return None
def _GenerateAutoscalerDeleteRequests(self, holder, project, mig_requests): """Generates Delete requestes for autoscalers attached to instance groups. Args: holder: ComputeApiHolder, object encapsulating compute api. project: str, project this request should apply to. mig_requests: Messages which will be sent to delete instance group managers. Returns: Messages, which will be sent to delete autoscalers. """ mig_requests = zip(*mig_requests)[2] if mig_requests else [] zone_migs = [(request.instanceGroupManager, 'zone', managed_instance_groups_utils.CreateZoneRef( holder.resources, request)) for request in mig_requests if hasattr(request, 'zone') and request.zone is not None] region_migs = [ (request.instanceGroupManager, 'region', managed_instance_groups_utils.CreateRegionRef( holder.resources, request)) for request in mig_requests if hasattr(request, 'region') and request.region is not None ] zones = zip(*zone_migs)[2] if zone_migs else [] regions = zip(*region_migs)[2] if region_migs else [] client = holder.client.apitools_client messages = client.MESSAGES_MODULE autoscalers_to_delete = managed_instance_groups_utils.AutoscalersForMigs( migs=zone_migs + region_migs, autoscalers=managed_instance_groups_utils.AutoscalersForLocations( zones=zones, regions=regions, compute=client, http=client.http, batch_url=holder.client.batch_url)) requests = [] for autoscaler in autoscalers_to_delete: if autoscaler.zone: service = client.autoscalers request = messages.ComputeAutoscalersDeleteRequest( zone=path_simplifier.Name(autoscaler.zone)) else: service = client.regionAutoscalers request = messages.ComputeRegionAutoscalersDeleteRequest( region=path_simplifier.Name(autoscaler.region)) request.autoscaler = autoscaler.name request.project = project requests.append((service, 'Delete', request)) return requests
def _GenerateAutoscalerDeleteRequests(self, mig_requests): """Generates Delete requestes for autoscalers attached to instance groups. Args: mig_requests: Messages which will be sent to delete instance group managers. Returns: Messages, which will be sent to delete autoscalers. """ mig_requests = zip(*mig_requests)[2] if mig_requests else [] zone_migs = [(request.instanceGroupManager, 'zone', request.zone) for request in mig_requests if hasattr(request, 'zone') and request.zone is not None] region_migs = [ (request.instanceGroupManager, 'region', request.region) for request in mig_requests if hasattr(request, 'region') and request.region is not None ] zones = sorted(set(zip(*zone_migs)[2])) if zone_migs else [] regions = sorted(set(zip(*region_migs)[2])) if region_migs else [] autoscalers_to_delete = managed_instance_groups_utils.AutoscalersForMigs( migs=zone_migs + region_migs, autoscalers=managed_instance_groups_utils.AutoscalersForLocations( zones=zones, regions=regions, project=self.project, compute=self.compute, http=self.http, batch_url=self.batch_url), project=self.project) requests = [] for autoscaler in autoscalers_to_delete: if autoscaler.zone: service = self.compute.autoscalers request = service.GetRequestType('Delete')( zone=path_simplifier.Name(autoscaler.zone)) else: service = self.compute.regionAutoscalers request = service.GetRequestType('Delete')( region=path_simplifier.Name(autoscaler.region)) request.autoscaler = autoscaler.name request.project = self.project requests.append((service, 'Delete', request)) return requests
def _do_get_instance_config(self, igm_ref, instance_ref): """Returns instance config for given instance.""" instance_name = path_simplifier.Name(str(instance_ref)) filter_param = 'name eq {0}'.format(instance_name) if igm_ref.Collection() == 'compute.instanceGroupManagers': service = self._client.apitools_client.instanceGroupManagers request = ( self._client.messages. ComputeInstanceGroupManagersListPerInstanceConfigsRequest)( instanceGroupManager=igm_ref.Name(), project=igm_ref.project, zone=igm_ref.zone, filter=filter_param, maxResults=1, ) elif igm_ref.Collection() == 'compute.regionInstanceGroupManagers': service = self._client.apitools_client.regionInstanceGroupManagers request = ( self._client.messages. ComputeRegionInstanceGroupManagersListPerInstanceConfigsRequest )( instanceGroupManager=igm_ref.Name(), project=igm_ref.project, region=igm_ref.region, filter=filter_param, maxResults=1, ) else: raise ValueError('Unknown reference type {0}'.format( igm_ref.Collection())) per_instance_configs = service.ListPerInstanceConfigs(request).items if per_instance_configs: return per_instance_configs[0] else: return None
def CreatePerInstanceConfigMessage(holder, instance_ref, stateful_disks, stateful_metadata, disk_getter=None): """Create per-instance config message from the given stateful disks and metadata.""" if not disk_getter: disk_getter = instance_disk_getter.InstanceDiskGetter( instance_ref=instance_ref, holder=holder) messages = holder.client.messages disk_overrides = [] for stateful_disk in stateful_disks or []: disk_overrides.append( GetDiskOverride( messages=messages, stateful_disk=stateful_disk, disk_getter=disk_getter)) metadata_overrides = [] # Keeping the metadata sorted to maintain consistency across commands for metadata_key, metadata_value in sorted(six.iteritems(stateful_metadata)): metadata_overrides.append( messages.ManagedInstanceOverride.MetadataValueListEntry( key=metadata_key, value=metadata_value)) return messages.PerInstanceConfig( instance=str(instance_ref), name=path_simplifier.Name(str(instance_ref)), override=messages.ManagedInstanceOverride( disks=disk_overrides, metadata=metadata_overrides), preservedState=\ MakePreservedStateFromOverrides( holder.client.messages, disk_overrides, metadata_overrides))
def CreateInstanceReferences(resources, compute_client, igm_ref, instance_names): """Creates reference to instances in instance group (zonal or regional).""" if igm_ref.Collection() == 'compute.instanceGroupManagers': instance_refs = [] for instance in instance_names: instance_refs.append( resources.Parse(instance, params={ 'project': igm_ref.project, 'zone': igm_ref.zone, }, collection='compute.instances')) return [instance_ref.SelfLink() for instance_ref in instance_refs] else: service = compute_client.apitools_client.regionInstanceGroupManagers request = service.GetRequestType('ListManagedInstances')( instanceGroupManager=igm_ref.Name(), region=igm_ref.region, project=igm_ref.project) results = compute_client.MakeRequests( requests=[(service, 'ListManagedInstances', request)])[0].managedInstances # here we assume that instances are uniquely named within RMIG return [ instance_ref.instance for instance_ref in results if path_simplifier.Name(instance_ref.instance) in instance_names or instance_ref.instance in instance_names ]
def CreatePerInstanceConfigMessage(holder, instance_ref, stateful_disks, stateful_metadata, disk_getter=None, set_preserved_state=True): """Create per-instance config message from the given stateful disks and metadata.""" if not disk_getter: disk_getter = instance_disk_getter.InstanceDiskGetter( instance_ref=instance_ref, holder=holder) messages = holder.client.messages preserved_state_disks = [] for stateful_disk in stateful_disks or []: preserved_state_disks.append( MakePreservedStateDiskEntry(messages, stateful_disk, disk_getter)) preserved_state_metadata = [] # Keeping the metadata sorted to maintain consistency across commands for metadata_key, metadata_value in sorted(six.iteritems(stateful_metadata)): preserved_state_metadata.append( MakePreservedStateMetadataEntry( messages, key=metadata_key, value=metadata_value)) per_instance_config = messages.PerInstanceConfig( name=path_simplifier.Name(six.text_type(instance_ref))) if set_preserved_state: per_instance_config.preservedState = messages.PreservedState( disks=messages.PreservedState.DisksValue( additionalProperties=preserved_state_disks), metadata=messages.PreservedState.MetadataValue( additionalProperties=preserved_state_metadata)) return per_instance_config
def _CreateAlias(instance_resource): """Returns the alias for the given instance.""" parts = [ instance_resource.name, path_simplifier.Name(instance_resource.zone), properties.VALUES.core.project.Get(required=True), ] return '.'.join(parts)
def _OperationRequest(self, verb): """Generates apitools request message to poll the operation.""" if self.project: request = self.operation_service.GetRequestType(verb)( operation=self.operation.name, project=self.project) else: # Fetches the parent ID from the operation name. token_list = self.operation.name.split('-') parent_id = 'organizations/' + token_list[1] request = self.operation_service.GetRequestType(verb)( operation=self.operation.name, parentId=parent_id) if self.operation.zone: request.zone = path_simplifier.Name(self.operation.zone) elif self.operation.region: request.region = path_simplifier.Name(self.operation.region) return request
def GetExternalInterface(instance_resource, no_raise=False): """Returns the network interface of the instance with an external IP address. Args: instance_resource: An instance resource object. no_raise: A boolean flag indicating whether or not to return None instead of raising. Raises: UnallocatedIPAddressError: If the instance_resource's external IP address has yet to be allocated. MissingExternalIPAddressError: If no external IP address is found for the instance_resource and no_raise is False. Returns: A network interface resource object or None if no_raise and a network interface with an external IP address does not exist. """ if instance_resource.networkInterfaces: for network_interface in instance_resource.networkInterfaces: access_configs = network_interface.accessConfigs if access_configs: if access_configs[0].natIP: return network_interface elif not no_raise: raise UnallocatedIPAddressError( 'Instance [{0}] in zone [{1}] has not been allocated an external ' 'IP address yet. Try rerunning this command later.'. format(instance_resource.name, path_simplifier.Name(instance_resource.zone))) if no_raise: return None raise MissingExternalIPAddressError( 'Instance [{0}] in zone [{1}] does not have an external IP address, ' 'so you cannot SSH into it. To add an external IP address to the ' 'instance, use [gcloud compute instances add-access-config].'.format( instance_resource.name, path_simplifier.Name(instance_resource.zone)))
def ResourceGetRequest(self): """"Generates apitool request message to get the resource.""" project = self.project operation = self.operation resource_service = self.resource_service followup_override = self.followup_override target_link = operation.targetLink if project: request = resource_service.GetRequestType('Get')(project=project) else: # Gets the flexible resource ID. if target_link is None: log.status.write('{0}.\n'.format( _HumanFriendlyNameForOpPastTense( operation.operationType).capitalize())) return token_list = target_link.split('/') flexible_resource_id = token_list[-1] request = resource_service.GetRequestType('Get')( securityPolicy=flexible_resource_id) if operation.zone: request.zone = path_simplifier.Name(operation.zone) elif operation.region: request.region = path_simplifier.Name(operation.region) name_field = resource_service.GetMethodConfig('Get').ordered_params[-1] # If the new resource that is created's name differs from the one used # for polling, this will check for the new resource (such as when # renaming an instance). if followup_override: resource_name = followup_override else: resource_name = path_simplifier.Name(operation.targetLink) setattr(request, name_field, resource_name) return request
def GetExternalIPAddress(instance_resource, no_raise=False): """Returns the external IP address of the instance. Args: instance_resource: An instance resource object. no_raise: A boolean flag indicating whether or not to return None instead of raising. Raises: ToolException: If no external IP address is found for the instance_resource and no_raise is False. Returns: A string IP or None is no_raise is True and no ip exists. """ if instance_resource.networkInterfaces: access_configs = instance_resource.networkInterfaces[0].accessConfigs if access_configs: ip_address = access_configs[0].natIP if ip_address: return ip_address elif not no_raise: raise exceptions.ToolException( 'Instance [{0}] in zone [{1}] has not been allocated an external ' 'IP address yet. Try rerunning this command later.'.format( instance_resource.name, path_simplifier.Name(instance_resource.zone))) if no_raise: return None raise exceptions.ToolException( 'Instance [{0}] in zone [{1}] does not have an external IP address, ' 'so you cannot SSH into it. To add an external IP address to the ' 'instance, use [gcloud compute instances add-access-config].'.format( instance_resource.name, path_simplifier.Name(instance_resource.zone)))
def AddAutoscalersToMigs(migs_iterator, project, compute, http, batch_url, fail_when_api_not_supported=True): """Add Autoscaler to each IGM object if autoscaling is enabled for it.""" migs = list(migs_iterator) zone_names = set( [path_simplifier.Name(mig['zone']) for mig in migs if 'zone' in mig]) region_names = set([ path_simplifier.Name(mig['region']) for mig in migs if 'region' in mig ]) autoscalers = {} all_autoscalers = AutoscalersForLocations( zones=zone_names, regions=region_names, project=project, compute=compute, http=http, batch_url=batch_url, fail_when_api_not_supported=fail_when_api_not_supported) for scope_name in list(zone_names) + list(region_names): autoscalers[scope_name] = [] for autoscaler in all_autoscalers: autoscaler_scope = None if autoscaler.zone is not None: autoscaler_scope = path_simplifier.Name(autoscaler.zone) if hasattr(autoscaler, 'region') and autoscaler.region is not None: autoscaler_scope = path_simplifier.Name(autoscaler.region) if autoscaler_scope is not None: autoscalers.setdefault(autoscaler_scope, []) autoscalers[autoscaler_scope].append(autoscaler) for mig in migs: scope_name = None scope_type = None if 'region' in mig: scope_name = path_simplifier.Name(mig['region']) scope_type = 'region' elif 'zone' in mig: scope_name = path_simplifier.Name(mig['zone']) scope_type = 'zone' autoscaler = None if scope_name and scope_type: autoscaler = AutoscalerForMig(mig_name=mig['name'], autoscalers=autoscalers[scope_name], project=project, scope_name=scope_name, scope_type=scope_type) if autoscaler: mig['autoscaler'] = autoscaler yield mig
def MakeRequestsAndGetStatusPerInstance(client, requests, instances_holder_field, errors_to_collect): """Make *-instances requests with feedback per instance. Args: client: Compute client. requests: [(service, method, request)]. instances_holder_field: name of field inside request holding list of instances. errors_to_collect: A list for capturing errors. If any response contains an error, it is added to this list. Returns: A list of request statuses per instance. Requests status is a dictionary object, see SendInstancesRequestsAndPostProcessOutputs for details. """ # Make requests and collect errors for each. request_results = [] for service, method, request in requests: errors = [] client.MakeRequests([(service, method, request)], errors) request_results.append((request, errors)) errors_to_collect.extend(errors) # Determine status of instances. status_per_instance = [] for request, errors in request_results: # Currently, any validation failure means that whole request is rejected. if errors: instance_status = 'FAIL' else: instance_status = 'SUCCESS' for instance in getattr(request, instances_holder_field).instances: status_per_instance.append({ 'selfLink': instance, 'instanceName': path_simplifier.Name(instance), 'status': instance_status }) return status_per_instance
def GetInternalInterface(instance_resource): """Returns the a network interface of the instance. Args: instance_resource: An instance resource object. Raises: ToolException: If instance has no network interfaces. Returns: A network interface resource object. """ if instance_resource.networkInterfaces: return instance_resource.networkInterfaces[0] raise exceptions.ToolException( 'Instance [{0}] in zone [{1}] has no network interfaces.'.format( instance_resource.name, path_simplifier.Name(instance_resource.zone)))
def GetInternalIPAddress(instance_resource): """Returns the internal IP address of the instance. Args: instance_resource: An instance resource object. Raises: ToolException: If instance has no network interfaces. Returns: A string IP or None if no_raise is True and no ip exists. """ if instance_resource.networkInterfaces: return instance_resource.networkInterfaces[0].networkIP raise exceptions.ToolException( 'Instance [{0}] in zone [{1}] has no network interfaces.'.format( instance_resource.name, path_simplifier.Name(instance_resource.zone)))
def CreateInstanceReferences(self, group_ref, instance_names, errors): if group_ref.Collection() == 'compute.instanceGroupManagers': instances_refs = self.CreateZonalReferences( instance_names, group_ref.zone, resource_type='instances') return [instance_ref.SelfLink() for instance_ref in instances_refs] else: service = self.compute.regionInstanceGroupManagers request = service.GetRequestType('ListManagedInstances')( instanceGroupManager=group_ref.Name(), region=group_ref.region, project=self.context['project']) results = list(request_helper.MakeRequests( requests=[(service, 'ListManagedInstances', request)], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None))[0].managedInstances # here we assume that instances are uniquely named within RMIG return [instance_ref.instance for instance_ref in results if path_simplifier.Name(instance_ref.instance) in instance_names or instance_ref.instance in instance_names]
def CreateInstanceReferences(holder, igm_ref, instance_names): """Creates references to instances in instance group (zonal or regional).""" if igm_ref.Collection() == 'compute.instanceGroupManagers': instance_refs = [] for instance in instance_names: instance_refs.append( holder.resources.Parse(instance, params={ 'project': igm_ref.project, 'zone': igm_ref.zone, }, collection='compute.instances')) return instance_refs elif igm_ref.Collection() == 'compute.regionInstanceGroupManagers': messages = holder.client.messages request = ( messages. ComputeRegionInstanceGroupManagersListManagedInstancesRequest)( instanceGroupManager=igm_ref.Name(), region=igm_ref.region, project=igm_ref.project) managed_instances = list_pager.YieldFromList( service=holder.client.apitools_client.regionInstanceGroupManagers, batch_size=500, request=request, method='ListManagedInstances', field='managedInstances', ) instances_to_return = [] for instance_ref in managed_instances: if path_simplifier.Name( instance_ref.instance ) in instance_names or instance_ref.instance in instance_names: instances_to_return.append(instance_ref.instance) return instances_to_return else: raise ValueError('Unknown reference type {0}'.format( igm_ref.Collection()))
def CreateInstanceReferences(scope_prompter, compute_client, group_ref, instance_names): """Creates reference to instances in instance group (zonal or regional).""" compute = compute_client.apitools_client if group_ref.Collection() == 'compute.instanceGroupManagers': instances_refs = scope_prompter.CreateZonalReferences( instance_names, group_ref.zone, resource_type='instances') return [instance_ref.SelfLink() for instance_ref in instances_refs] else: service = compute.regionInstanceGroupManagers request = service.GetRequestType('ListManagedInstances')( instanceGroupManager=group_ref.Name(), region=group_ref.region, project=group_ref.project) results = compute_client.MakeRequests( requests=[(service, 'ListManagedInstances', request)])[0].managedInstances # here we assume that instances are uniquely named within RMIG return [ instance_ref.instance for instance_ref in results if path_simplifier.Name(instance_ref.instance) in instance_names or instance_ref.instance in instance_names ]
def testName(self): for _, path in six.iteritems(self.paths): name = path_simplifier.Name(path) self.assertEqual('my-name', name)
def ComputeInstanceGroupManagerMembership( compute, project, http, batch_url, items, filter_mode=(InstanceGroupFilteringMode.ALL_GROUPS)): """Add information if instance group is managed. Args: compute: GCE Compute API client, project: str, project name http: http client, batch_url: str, batch url items: list of instance group messages, filter_mode: InstanceGroupFilteringMode, managed/unmanaged filtering options Returns: list of instance groups with computed dynamic properties """ errors = [] items = list(items) zone_names = set( [path_simplifier.Name(ig['zone']) for ig in items if 'zone' in ig]) region_names = set( [path_simplifier.Name(ig['region']) for ig in items if 'region' in ig]) if zone_names: zonal_instance_group_managers = lister.GetZonalResources( service=compute.instanceGroupManagers, project=project, requested_zones=zone_names, filter_expr=None, http=http, batch_url=batch_url, errors=errors) else: zonal_instance_group_managers = [] if region_names and hasattr(compute, 'regionInstanceGroups'): # regional instance groups are just in 'alpha' API regional_instance_group_managers = lister.GetRegionalResources( service=compute.regionInstanceGroupManagers, project=project, requested_regions=region_names, filter_expr=None, http=http, batch_url=batch_url, errors=errors) else: regional_instance_group_managers = [] instance_group_managers = (list(zonal_instance_group_managers) + list(regional_instance_group_managers)) instance_group_managers_refs = set([ path_simplifier.ScopedSuffix(igm.selfLink) for igm in instance_group_managers ]) if errors: utils.RaiseToolException(errors) results = [] for item in items: self_link = item['selfLink'] igm_self_link = self_link.replace('/instanceGroups/', '/instanceGroupManagers/') scoped_suffix = path_simplifier.ScopedSuffix(igm_self_link) is_managed = scoped_suffix in instance_group_managers_refs if (is_managed and filter_mode == InstanceGroupFilteringMode.ONLY_UNMANAGED_GROUPS): continue elif (not is_managed and filter_mode == InstanceGroupFilteringMode.ONLY_MANAGED_GROUPS): continue item['isManaged'] = ('Yes' if is_managed else 'No') if is_managed: item['instanceGroupManagerUri'] = igm_self_link results.append(item) return results
def _TargetPoolHealthChecksToCell(target_pool): """Comma-joins the names of health checks of the given target pool.""" return ','.join(path_simplifier.Name(check) for check in target_pool.get('healthChecks', []))
def _TargetProxySslCertificatesToCell(target_proxy): """Joins the names of ssl certificates of the given HTTPS or SSL proxy.""" return ','.join(path_simplifier.Name(cert) for cert in target_proxy.get('sslCertificates', []))
def WaitForOperations(operations, project, operation_service, resource_service, http, batch_url, warnings, errors, custom_get_requests=None, timeout=None): """Blocks until the given operations are done or until a timeout is reached. Args: operations: A list of Operation objects to poll. project: The project to which the resources belog. operation_service: The service that can be used to get operation objects. resource_service: The service of the collection being mutated by the operations. If the operation type is not delete, this service is used to fetch the mutated objects after the operations are done. http: An HTTP object. batch_url: The URL to which batch requests should be sent. warnings: An output parameter for capturing warnings. errors: An output parameter for capturing errors. custom_get_requests: A mapping of resource names to requests. If this is provided, when an operation is DONE, instead of performing a get on the targetLink, this function will consult custom_get_requests and perform the request dictated by custom_get_requests. timeout: The maximum amount of time, in seconds, to wait for the operations to reach the DONE state. Yields: The resources pointed to by the operations' targetLink fields if the operation type is not delete. Only resources whose corresponding operations reach done are yielded. """ timeout = timeout or _POLLING_TIMEOUT_SEC operation_type = operation_service.GetResponseType('Get') responses = [] start = time_util.CurrentTimeSec() sleep_sec = 0 while operations: resource_requests = [] operation_requests = [] log.debug('Operations to inspect: %s', operations) for operation in operations: if operation.status == operation_type.StatusValueValuesEnum.DONE: # The operation has reached the DONE state, so we record any # problems it contains (if any) and proceed to get the target # resource if there were no problems and the operation is not # a deletion. _RecordProblems(operation, warnings, errors) # We shouldn't attempt to get the target resource if there was # anything wrong with the operation. Note that # httpErrorStatusCode is set only when the operation is not # successful. if (operation.httpErrorStatusCode and operation.httpErrorStatusCode != httplib.OK): continue # Just in case the server did not set httpErrorStatusCode but # the operation did fail, we check the "error" field. if operation.error: continue target_link = operation.targetLink if custom_get_requests: target_link, service, request_protobuf = ( custom_get_requests[operation.targetLink]) resource_requests.append( (service, 'Get', request_protobuf)) # We shouldn't get the target resource if the operation type # is delete because there will be no resource left. elif not _IsDeleteOp(operation.operationType): request = resource_service.GetRequestType('Get')( project=project) if operation.zone: request.zone = path_simplifier.Name(operation.zone) elif operation.region: request.region = path_simplifier.Name(operation.region) name_field = resource_service.GetMethodConfig( 'Get').ordered_params[-1] setattr(request, name_field, path_simplifier.Name(operation.targetLink)) resource_requests.append( (resource_service, 'Get', request)) log.status.write('{0} [{1}].\n'.format( _HumanFrieldlyNameForOpPastTense( operation.operationType).capitalize(), target_link)) else: # The operation has not reached the DONE state, so we add a # get request to poll the operation. request = operation_service.GetRequestType('Get')( operation=operation.name, project=project) if operation.zone: request.zone = path_simplifier.Name(operation.zone) elif operation.region: request.region = path_simplifier.Name(operation.region) operation_requests.append((operation_service, 'Get', request)) requests = resource_requests + operation_requests if not requests: break responses, request_errors = batch_helper.MakeRequests( requests=requests, http=http, batch_url=batch_url) errors.extend(request_errors) operations = [] for response in responses: if isinstance(response, operation_type): operations.append(response) else: yield response # If there are no more operations, we are done. if not operations: break # Did we time out? If so, record the operations that timed out so # they can be reported to the user. if time_util.CurrentTimeSec() - start > timeout: log.debug('Timeout of %ss reached.', timeout) _RecordUnfinishedOperations(operations, errors) break # Sleeps before trying to poll the operations again. sleep_sec += 1 # Don't re-use sleep_sec, since we want to keep the same time increment sleep_time = min(sleep_sec, _MAX_TIME_BETWEEN_POLLS_SEC) log.debug('Sleeping for %ss.', sleep_time) time_util.Sleep(sleep_time)
def WaitForOperations( operations_data, http, batch_url, warnings, errors, progress_tracker=None, timeout=None): """Blocks until the given operations are done or until a timeout is reached. Args: operations_data: A list of OperationData objects holding Operations to poll. http: An HTTP object. batch_url: The URL to which batch requests should be sent. warnings: An output parameter for capturing warnings. errors: An output parameter for capturing errors. progress_tracker: progress tracker to tick while waiting for operations to finish. timeout: The maximum amount of time, in seconds, to wait for the operations to reach the DONE state. Yields: The resources pointed to by the operations' targetLink fields if the operation type is not delete. Only resources whose corresponding operations reach done are yielded. """ timeout = timeout or _POLLING_TIMEOUT_SEC # Operation -> OperationData mapping will be used to reify operation_service # and resource_service from operation_service.Get(operation) response. # It is necessary because poll operation is returning only response, but we # also need to get operation details to know the service to poll for all # unfinished_operations. operation_details = {} unfinished_operations = [] for operation in operations_data: operation_details[operation.operation.selfLink] = operation unfinished_operations.append(operation.operation) responses = [] start = time_util.CurrentTimeSec() sleep_sec = 0 while unfinished_operations: if progress_tracker: progress_tracker.Tick() resource_requests = [] operation_requests = [] log.debug('Operations to inspect: %s', unfinished_operations) for operation in unfinished_operations: # Reify operation data = operation_details[operation.selfLink] project = data.project operation_service = data.operation_service resource_service = data.resource_service operation_type = operation_service.GetResponseType('Get') if operation.status == operation_type.StatusValueValuesEnum.DONE: # The operation has reached the DONE state, so we record any # problems it contains (if any) and proceed to get the target # resource if there were no problems and the operation is not # a deletion. _RecordProblems(operation, warnings, errors) # We shouldn't attempt to get the target resource if there was # anything wrong with the operation. Note that # httpErrorStatusCode is set only when the operation is not # successful. if (operation.httpErrorStatusCode and operation.httpErrorStatusCode != 200): # httplib.OK continue # Just in case the server did not set httpErrorStatusCode but # the operation did fail, we check the "error" field. if operation.error: continue target_link = operation.targetLink # We shouldn't get the target resource if the operation type # is delete because there will be no resource left. if not _IsDeleteOp(operation.operationType): request = resource_service.GetRequestType('Get')(project=project) if operation.zone: request.zone = path_simplifier.Name(operation.zone) elif operation.region: request.region = path_simplifier.Name(operation.region) name_field = resource_service.GetMethodConfig( 'Get').ordered_params[-1] setattr(request, name_field, path_simplifier.Name(operation.targetLink)) resource_requests.append((resource_service, 'Get', request)) log.status.write('{0} [{1}].\n'.format( _HumanFrieldlyNameForOpPastTense( operation.operationType).capitalize(), target_link)) else: # The operation has not reached the DONE state, so we add a # get request to poll the operation. request = operation_service.GetRequestType('Get')( operation=operation.name, project=project) if operation.zone: request.zone = path_simplifier.Name(operation.zone) elif operation.region: request.region = path_simplifier.Name(operation.region) operation_requests.append((operation_service, 'Get', request)) requests = resource_requests + operation_requests if not requests: break responses, request_errors = batch_helper.MakeRequests( requests=requests, http=http, batch_url=batch_url) errors.extend(request_errors) unfinished_operations = [] for response in responses: if isinstance(response, operation_type): unfinished_operations.append(response) else: yield response # If there are no more operations, we are done. if not unfinished_operations: break # Did we time out? If so, record the operations that timed out so # they can be reported to the user. if time_util.CurrentTimeSec() - start > timeout: log.debug('Timeout of %ss reached.', timeout) _RecordUnfinishedOperations(unfinished_operations, errors) break # Sleeps before trying to poll the operations again. sleep_sec += 1 # Don't re-use sleep_sec, since we want to keep the same time increment sleep_time = min(sleep_sec, _MAX_TIME_BETWEEN_POLLS_SEC) log.debug('Sleeping for %ss.', sleep_time) time_util.Sleep(sleep_time)
def MakeRequestsAndGetStatusPerInstanceFromOperation(client, requests, instances_holder_field, warnings_to_collect, errors_to_collect): """Make *-instances requests with feedback per instance. Specialized version of MakeRequestsAndGetStatusPerInstance. Checks operations for warnings presence to evaluate statuses per instance. Gracefully validated requests may produce warnings on operations, indicating instances skipped. It would be merged with MakeRequestsAndGetStatusPerInstance after we see there's no issues with this implementation. Args: client: Compute client. requests: [(service, method, request)]. instances_holder_field: name of field inside request holding list of instances. warnings_to_collect: A list for capturing warnings. If any completed operation will contain skipped instances, function will append warning suggesting how to find additional details on the operation, warnings unrelated to graceful validation will be collected as is. errors_to_collect: A list for capturing errors. If any response contains an error, it is added to this list. Returns: See MakeRequestsAndGetStatusPerInstance. """ # Make requests and collect errors for each. request_results = [] for service, method, request in requests: errors = [] operations = client.MakeRequests([(service, method, request)], errors, log_warnings=False, no_followup=True) # There should be only one operation in the list. [operation] = operations request_results.append((request, operation, errors)) errors_to_collect.extend(errors) # Determine status of instances. status_per_instance = [] for request, operation, errors in request_results: # If there's any errors, we assume that operation failed for all instances. if errors: for instance in getattr(request, instances_holder_field).instances: status_per_instance.append({ 'selfLink': instance, 'instanceName': path_simplifier.Name(instance), 'status': 'FAIL' }) else: skipped_instances = ExtractSkippedInstancesAndCollectOtherWarnings( operation, warnings_to_collect) for instance in getattr(request, instances_holder_field).instances: # Extract public path of an instance from URI. Public path is a part of # instance URI, which starts with 'projects/' instance_path = instance[instance.find('/projects/') + 1:] validation_error = None if instance_path in skipped_instances: instance_status = 'SKIPPED' validation_error = skipped_instances[instance_path] else: instance_status = 'SUCCESS' status_per_instance.append({ 'selfLink': instance, 'instanceName': path_simplifier.Name(instance), 'status': instance_status, 'validationError': validation_error }) return status_per_instance