def Run(self, args): self.ref = self.CreateReference(args) get_request = self.GetGetRequest(args) errors = [] objects = list(request_helper.MakeRequests( requests=[get_request], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='There was a problem fetching the resource:') new_object = self.Modify(args, objects[0]) # If existing object is equal to the proposed object or if # Modify() returns None, then there is no work to be done, so we # print the resource and return. if not new_object or objects[0] == new_object: for resource in lister.ProcessResults( resources=[objects[0]], field_selector=property_selector.PropertySelector( properties=None, transformations=self.transformations)): yield resource return resources = request_helper.MakeRequests( requests=[self.GetSetRequest(args, new_object, objects[0])], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None) resources = lister.ProcessResults( resources=resources, field_selector=property_selector.PropertySelector( properties=None, transformations=self.transformations)) for resource in resources: yield resource if errors: utils.RaiseToolException( errors, error_message='There was a problem modifying the resource:')
def FetchChoiceResources(self, attribute, service, flag_names, prefix_filter=None): """Returns a list of choices used to prompt with.""" if prefix_filter: filter_expr = 'name eq {0}.*'.format(prefix_filter) else: filter_expr = None errors = [] global_resources = lister.GetGlobalResources(service=service, project=self.project, filter_expr=filter_expr, http=self.http, batch_url=self.batch_url, errors=errors) choices = [resource for resource in global_resources] if errors or not choices: punctuation = ':' if errors else '.' utils.RaiseToolException( errors, 'Unable to fetch a list of {0}s. Specifying [{1}] may fix this ' 'issue{2}'.format(attribute, ', or '.join(flag_names), punctuation)) return choices
def Run(self, args): ref = self.CreateReference(args) request_class = self.service.GetRequestType(self.method) request = request_class(project=self.project) self.ScopeRequest(ref, request) self.SetResourceName(ref, request) get_policy_request = (self.service, self.method, request) errors = [] objects = request_helper.MakeRequests(requests=[get_policy_request], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None) # Converting the objects genrator to a list triggers the # logic that actually populates the errors list. resources = list(objects) if errors: utils.RaiseToolException(errors, error_message='Could not fetch resource:') # TODO(broudy): determine how this output should look when empty. # GetIamPolicy always returns either an error or a valid policy. # If no policy has been set it returns a valid empty policy (just an etag.) # It is not possible to have multiple policies for one resource. return resources[0]
def ComputeInstanceGroupSize(self, items): """Add information about Instance Group size.""" errors = [] items = list(items) zone_names = sets.Set( [path_simplifier.Name(result['zone']) for result in items]) instance_groups = lister.GetZonalResources( service=self.compute.instanceGroups, project=self.project, requested_zones=zone_names, filter_expr=None, http=self.http, batch_url=self.batch_url, errors=errors) instance_group_ref_to_size = dict([ (path_simplifier.ScopedSuffix(ig.selfLink), ig.size) for ig in instance_groups ]) if errors: utils.RaiseToolException(errors) for item in items: self_link = item['selfLink'] gm_self_link = self_link.replace( '/instanceGroupManagers/', '/instanceGroups/') scoped_suffix = path_simplifier.ScopedSuffix(gm_self_link) size = instance_group_ref_to_size[scoped_suffix] item['size'] = str(size) yield item
def Run(self, args): """Yields JSON-serializable dicts of resources.""" # The field selector should be constructed before any resources # are fetched, so if there are any syntactic errors with the # fields, we can fail fast. field_selector = property_selector.PropertySelector(properties=args.fields) ref = self.CreateReference(args) get_request_class = self.service.GetRequestType(self.method) request = get_request_class(project=self.project) self.SetNameField(ref, request) self.ScopeRequest(ref, request) get_request = (self.service, self.method, request) errors = [] objects = request_helper.MakeRequests( requests=[get_request], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None) resources = lister.ProcessResults(objects, field_selector) resources = list(self.ComputeDynamicProperties(args, resources)) if errors: utils.RaiseToolException( errors, error_message='Could not fetch resource:') return resources[0]
def Run(self, args): sort_key_fn = None descending = False errors = [] if args.uri: field_selector = None else: field_selector = property_selector.PropertySelector( properties=None, transformations=self._FIELD_TRANSFORMS) sort_key_fn, descending = GetSortKey(args.sort_by, self._LIST_TABS) responses, errors = self.GetResources(args) if errors: utils.RaiseToolException(errors) items = lister.ProcessResults( resources=list(_UnwrapResponse(responses, self.list_field)), field_selector=field_selector, sort_key_fn=sort_key_fn, reverse_sort=descending, limit=args.limit) for item in items: if args.uri: yield item['instance'] else: yield item
def ProcessEditedResource(self, file_contents, args): """Returns an updated resource that was edited by the user.""" # It's very important that we replace the characters of comment # lines with spaces instead of removing the comment lines # entirely. JSON and YAML deserialization give error messages # containing line, column, and the character offset of where the # error occurred. If the deserialization fails; we want to make # sure those numbers map back to what the user actually had in # front of him or her otherwise the errors will not be very # useful. non_comment_lines = '\n'.join( ' ' * len(line) if line.startswith('#') else line for line in file_contents.splitlines()) modified_record = _DeserializeValue(non_comment_lines, args.format or BaseEdit.DEFAULT_FORMAT) # Normalizes all of the fields that refer to other # resource. (i.e., translates short names to URIs) reference_normalizer = property_selector.PropertySelector( transformations=self.reference_normalizers) modified_record = reference_normalizer.Apply(modified_record) if self.modifiable_record == modified_record: new_object = None else: modified_record['name'] = self.original_record['name'] fingerprint = self.original_record.get('fingerprint') if fingerprint: modified_record['fingerprint'] = fingerprint new_object = encoding.DictToMessage( modified_record, self._resource_spec.message_class) # If existing object is equal to the proposed object or if # there is no new object, then there is no work to be done, so we # return the original object. if not new_object or self.original_object == new_object: return [self.original_object] errors = [] resources = list(request_helper.MakeRequests( requests=[self.GetSetRequest(args, new_object, self.original_object)], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not update resource:') return resources
def Run(self, args): field_selector = property_selector.PropertySelector( properties=None, transformations=[]) sort_key_fn, descending = GetSortKey(args.sort_by, self._COLUMNS) responses, errors = self._GetResources(args) if errors: utils.RaiseToolException(errors) return lister.ProcessResults( resources=list(_UnwrapResponse(responses, 'namedPorts')), field_selector=field_selector, sort_key_fn=sort_key_fn, reverse_sort=descending, limit=args.limit)
def AutoscalersForZones(zones, project, compute, http, batch_url, fail_when_api_not_supported=True): """Finds all Autoscalers defined for a given project and zones. Args: zones: target zones project: project owning resources. compute: module representing compute api. http: communication channel. batch_url: batch url. fail_when_api_not_supported: If true, raise tool exception if API does not support autoscaling. Returns: A list of Autoscaler objects. """ # Errors is passed through library calls and modified with # (ERROR_CODE, ERROR_MESSAGE) tuples. errors = [] if hasattr(compute, 'autoscalers'): # Explicit list() is required to unwind the generator and make sure errors # are detected at this level. autoscalers = list( lister.GetZonalResources( service=compute.autoscalers, project=project, requested_zones=zones, http=http, batch_url=batch_url, errors=errors, filter_expr=None, )) else: autoscalers = [] if fail_when_api_not_supported: errors.append((None, 'API does not support autoscaling')) if errors: utils.RaiseToolException( errors, error_message='Could not check if the Managed Instance Group is ' 'Autoscaled.') return autoscalers
def GetProject(self): """Returns the project object.""" errors = [] objects = list( request_helper.MakeRequests(requests=[ (self.compute.projects, 'Get', self.messages.ComputeProjectsGetRequest( project=properties.VALUES.core.project.Get( required=True), )) ], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not fetch project resource:') return objects[0]
def GetImage(self, image_ref): """Returns the image resource corresponding to the given reference.""" errors = [] res = list(request_helper.MakeRequests( requests=[(self.compute.images, 'Get', self.messages.ComputeImagesGetRequest( image=image_ref.Name(), project=image_ref.project))], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not fetch image resource:') return res[0]
def GetAddress(self, address_ref): """Returns the address resource corresponding to the given reference.""" errors = [] res = list(request_helper.MakeRequests( requests=[(self.compute.addresses, 'Get', self.messages.ComputeAddressesGetRequest( address=address_ref.Name(), project=address_ref.project, region=address_ref.region))], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not fetch address resource:') return res[0]
def ComputeInstanceGroupManagerMembership(self, items, filter_mode=( InstanceGroupFilteringMode .all_groups)): """Add information if instance group is managed.""" errors = [] items = list(items) zone_names = sets.Set( [path_simplifier.Name(result['zone']) for result in items]) instance_group_managers = lister.GetZonalResources( service=self.compute.instanceGroupManagers, project=self.project, requested_zones=zone_names, filter_expr=None, http=self.http, batch_url=self.batch_url, errors=errors) instance_group_managers_refs = sets.Set([ path_simplifier.ScopedSuffix(igm.selfLink) for igm in instance_group_managers]) if errors: utils.RaiseToolException(errors) 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 yield item
def GetInstanceExternalIpAddress(self, instance_ref): """Returns the external ip address for the given instance.""" request = (self.compute.instances, 'Get', self.messages.ComputeInstancesGetRequest( instance=instance_ref.Name(), project=self.project, zone=instance_ref.zone)) errors = [] objects = list( request_helper.MakeRequests(requests=[request], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException(errors, error_message='Could not fetch instance:') return GetExternalIPAddress(objects[0])
def SetProjectMetadata(self, new_metadata): """Sets the project metadata to the new metadata.""" compute = self.compute errors = [] list( request_helper.MakeRequests(requests=[ (compute.projects, 'SetCommonInstanceMetadata', self.messages.ComputeProjectsSetCommonInstanceMetadataRequest( metadata=new_metadata, project=properties.VALUES.core.project.Get(required=True), )) ], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not add SSH key to project metadata:')
def Run(self, args, request_protobufs=None, service=None): if request_protobufs is None: request_protobufs = self.CreateRequests(args) if service is None: service = self.service requests = [] # If a method is not passed as part of a tuple then use the self.method # default for request in request_protobufs: if isinstance(request, tuple): method = request[0] proto = request[1] else: method = self.method proto = request requests.append((service, method, proto)) errors = [] # We want to run through the generator that MakeRequests returns in order to # actually make the requests, since these requests mutate resources. resources = list(request_helper.MakeRequests( requests=requests, http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=self.custom_get_requests)) resources = lister.ProcessResults( resources=resources, field_selector=property_selector.PropertySelector( properties=None, transformations=self.transformations)) if errors: utils.RaiseToolException(errors) return resources
def ExpandImageFlag(self, args, return_image_resource=False): """Resolves the --image flag value. If the value of --image is one of the aliases defined in the constants module, both the user's project and the public image project for the alias are queried. Otherwise, only the user's project is queried. If --image is an alias and --image-project is provided, only the given project is queried. Args: args: The command-line flags. The flags accessed are --image and --image-project. return_image_resource: If True, always makes an API call to also fetch the image resource. Returns: A tuple where the first element is the self link of the image. If return_image_resource is False, the second element is None, otherwise it is the image resource. """ image_ref = self.resources.Parse( args.image or constants.DEFAULT_IMAGE, collection='compute.images', resolve=False) # If an image project was specified, then assume that image refers # to an image in that project. if args.image_project: image_project_ref = self.resources.Parse( args.image_project, collection='compute.projects') image_ref.project = image_project_ref.Name() image_ref.Resolve() return (image_ref.SelfLink(), self.GetImage(image_ref) if return_image_resource else None) image_ref.Resolve() alias = constants.IMAGE_ALIASES.get(image_ref.Name()) # Check for hidden aliases. if not alias: alias = constants.HIDDEN_IMAGE_ALIASES.get(image_ref.Name()) # If the image name given is not an alias and no image project was # provided, then assume that the image value refers to an image in # the user's project. if not alias: return (image_ref.SelfLink(), self.GetImage(image_ref) if return_image_resource else None) # At this point, the image is an alias and now we have to find the # latest one among the public image project and the user's # project. errors = [] images = self.GetMatchingImages(image_ref.Name(), alias, errors) user_image = None public_images = [] for image in images: if image.deprecated: continue if '/projects/{0}/'.format(self.project) in image.selfLink: user_image = image else: public_images.append(image) if errors or not public_images: # This should happen only if there is something wrong with the # image project (e.g., operator error) or the global control # plane is down. utils.RaiseToolException( errors, 'Failed to find image for alias [{0}] in public image project [{1}].' .format(image_ref.Name(), alias.project)) def GetVersion(image): """Extracts the "20140718" from an image name like "debian-v20140718".""" parts = image.name.rsplit('v', 1) if len(parts) != 2: log.debug('Skipping image with malformed name [%s].', image.name) return None return parts[1] public_candidate = max(public_images, key=GetVersion) if user_image: options = [user_image, public_candidate] idx = console_io.PromptChoice( options=[image.selfLink for image in options], default=0, message=('Found two possible choices for [--image] value [{0}].' .format(image_ref.Name()))) res = options[idx] else: res = public_candidate log.debug('Image resolved to [%s].', res.selfLink) return (res.selfLink, res if return_image_resource else None)
def Run(self, args): self.ref = self.CreateReference(args) get_request = self.GetGetRequest(args) errors = [] objects = list(request_helper.MakeRequests( requests=[get_request], http=self.http, batch_url=self.batch_url, errors=errors, custom_get_requests=None)) if errors: utils.RaiseToolException( errors, error_message='Could not fetch resource:') self.original_object = objects[0] self.original_record = encoding.MessageToDict(self.original_object) # Selects only the fields that can be modified. field_selector = property_selector.PropertySelector( properties=self._resource_spec.editables) self.modifiable_record = field_selector.Apply(self.original_record) buf = cStringIO.StringIO() for line in _HELP.splitlines(): buf.write('#') if line: buf.write(' ') buf.write(line) buf.write('\n') buf.write('\n') buf.write(_SerializeDict(self.modifiable_record, args.format or BaseEdit.DEFAULT_FORMAT)) buf.write('\n') example = _SerializeDict( encoding.MessageToDict(self.example_resource), args.format or BaseEdit.DEFAULT_FORMAT) _WriteResourceInCommentBlock(example, 'Example resource:', buf) buf.write('#\n') original = _SerializeDict(self.original_record, args.format or BaseEdit.DEFAULT_FORMAT) _WriteResourceInCommentBlock(original, 'Original resource:', buf) file_contents = buf.getvalue() while True: try: file_contents = edit.OnlineEdit(file_contents) except edit.NoSaveException: raise calliope_exceptions.ToolException('Edit aborted by user.') try: resources = self.ProcessEditedResource(file_contents, args) break except (ValueError, yaml.error.YAMLError, protorpc.messages.ValidationError, calliope_exceptions.ToolException) as e: if isinstance(e, ValueError): message = e.message else: message = str(e) if isinstance(e, calliope_exceptions.ToolException): problem_type = 'applying' else: problem_type = 'parsing' message = ('There was a problem {0} your changes: {1}' .format(problem_type, message)) if not console_io.PromptContinue( message=message, prompt_string='Would you like to edit the resource again?'): raise calliope_exceptions.ToolException('Edit aborted by user.') resources = lister.ProcessResults( resources=resources, field_selector=property_selector.PropertySelector( properties=None, transformations=self.transformations)) for resource in resources: yield resource
def Run(self, args): """Yields JSON-serializable dicts of resources or self links.""" # Data structures used to perform client-side filtering of # resources by their names and/or URIs. self.self_links = set() self.names = set() self.resource_refs = [] if args.uri: field_selector = None else: # The field selector should be constructed before any resources # are fetched, so if there are any syntactic errors with the # fields, we can fail fast. field_selector = property_selector.PropertySelector( properties=None, transformations=self.transformations) if args.sort_by: if args.sort_by.startswith('~'): sort_by = args.sort_by[1:] descending = True else: sort_by = args.sort_by descending = False for col_name, path in self._resource_spec.table_cols: if sort_by == col_name: sort_by = path break if isinstance(sort_by, property_selector.PropertyGetter): property_getter = sort_by else: property_getter = property_selector.PropertyGetter(sort_by) sort_key_fn = property_getter.Get else: sort_key_fn = None descending = False errors = [] self.PopulateResourceFilteringStructures(args) items = self.FilterResults(args, self.GetResources(args, errors)) items = lister.ProcessResults( resources=items, field_selector=field_selector, sort_key_fn=sort_key_fn, reverse_sort=descending, limit=args.limit) items = self.ComputeDynamicProperties(args, items) for item in items: if args.uri: yield item['selfLink'] else: yield item if errors: utils.RaiseToolException(errors)