# -*- coding: utf-8 -*- # # Copyright 2022 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Flags and helpers for general Cloud NetApp Files commands.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.netapp import constants from googlecloudsdk.calliope import arg_parsers from googlecloudsdk.calliope import base from googlecloudsdk.calliope.concepts import concepts from googlecloudsdk.calliope.concepts import deps from googlecloudsdk.command_lib.util.concepts import presentation_specs from googlecloudsdk.core import properties ## Attribute configs ## def GetLocationAttributeConfig(): """Return the Location Attribute Config for resources. Returns: ResourceParameterAttributeConfig for location. """ fallthroughs = [ ## if location is not set, use value from ## gcloud config get-value netapp/location or netapp/region deps.ArgFallthrough('--location'), deps.PropertyFallthrough(properties.VALUES.netapp.location), ] return concepts.ResourceParameterAttributeConfig( name='location', fallthroughs=fallthroughs, help_text='The location of the {resource}.') def GetStoragePoolAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'storage_pool', 'The instance of the {resource}.') def GetVolumeAttributeConfig(positional=True): """Return the Volume Attribute Config for resources. Args: positional: boolean value describing whether volume attribute is conditional Returns: volume resource parameter attribute config for resource specs """ if positional: fallthroughs = [] else: ## For when we need a volume attribute in Snapshot resource spec, we want ## to assign fallthrough flags fallthroughs = [deps.ArgFallthrough('--volume')] help_text = ('The instance of the {resource}' if positional else ( 'The volume of the {resource}')) return concepts.ResourceParameterAttributeConfig( name='volume', fallthroughs=fallthroughs, help_text=help_text) def GetSnapshotAttributeConfig(positional=True): if positional: help_text = 'The instance of the {resource}' else: help_text = 'The snapshot of the {resource}' return concepts.ResourceParameterAttributeConfig( 'snapshot', help_text=help_text) def GetReplicationAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'replication', 'The instance of the {resource}') def GetOperationAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'operation', 'The Cloud NetApp operation.') def GetActiveDirectoryAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'active_directory', 'The instance of the {resource}.' ) def GetBackupVaultAttributeConfig(positional=True): fallthroughs = [] if not positional: ## For when we need a Backup Vault attribute in Backup resource spec ## we want to assign fallthrough flags fallthroughs = [deps.ArgFallthrough('--backup-vault')] return concepts.ResourceParameterAttributeConfig( 'backup_vault', 'The Backup Vault of the {resource}.', fallthroughs=fallthroughs ) def GetBackupAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'backup', 'The instance of the {resource}.') def GetBackupPolicyAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'backup_policy', 'The instance of the {resource}.' ) def GetKmsConfigAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'kms_config', 'The instance of the {resource}') def GetKeyRingAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'key_ring', 'The instance of the {resource}.' ) def GetCryptoKeyAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'crypto_key', 'The instance of the {resource}.' ) def GetQuotaRuleAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'quota_rule', 'The instance of the {resource}.' ) def GetHostGroupAttributeConfig(): return concepts.ResourceParameterAttributeConfig( 'host_group', 'The instance of the {resource}.' ) ## Resource Specs ## def GetLocationResourceSpec(): location_attribute_config = GetLocationAttributeConfig() location_attribute_config.fallthroughs = [] return concepts.ResourceSpec( constants.LOCATIONS_COLLECTION, resource_name='location', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=location_attribute_config) def GetListingLocationResourceSpec(): location_attribute_config = GetLocationAttributeConfig() location_attribute_config.fallthroughs.insert( 0, deps.Fallthrough(lambda: '-', hint='uses all locations by default.')) return concepts.ResourceSpec( constants.LOCATIONS_COLLECTION, resource_name='location', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=location_attribute_config) def GetOperationResourceSpec(): return concepts.ResourceSpec( constants.OPERATIONS_COLLECTION, resource_name='operation', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), operationsId=GetOperationAttributeConfig()) def GetStoragePoolResourceSpec(): return concepts.ResourceSpec( constants.STORAGEPOOLS_COLLECTION, resource_name='storage_pool', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), storagePoolsId=GetStoragePoolAttributeConfig()) def GetVolumeResourceSpec(positional=True): return concepts.ResourceSpec( constants.VOLUMES_COLLECTION, resource_name='volume', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), volumesId=GetVolumeAttributeConfig(positional=positional), ) def GetSnapshotResourceSpec(source_snapshot_op=False, positional=True): """Gets the Resource Spec for Snapshot. Args: source_snapshot_op: Boolean on whether operation uses snapshot as source or not. positional: Boolean on whether resource is positional arg ornot Returns: The Resource Spec for Snapshot """ location_attribute_config = GetLocationAttributeConfig() volume_attribute_config = GetVolumeAttributeConfig(positional=False) if source_snapshot_op: # if revert op or backup op (create, update) that is a source snapshot op, # we don't want volume attribute to have any fallthroughs # (--volume arg) since volume is positional in revert. volume_attribute_config.fallthroughs = [] if not positional: location_attribute_config.fallthroughs = [ deps.PropertyFallthrough(properties.VALUES.netapp.location), ] return concepts.ResourceSpec( constants.SNAPSHOTS_COLLECTION, resource_name='snapshot', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=location_attribute_config, volumesId=volume_attribute_config, snapshotsId=GetSnapshotAttributeConfig(positional=positional)) def GetReplicationResourceSpec(): location_attribute_config = GetLocationAttributeConfig() volume_attribute_config = GetVolumeAttributeConfig(positional=False) return concepts.ResourceSpec( constants.REPLICATIONS_COLLECTION, resource_name='replication', api_version=constants.BETA_API_VERSION, projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=location_attribute_config, volumesId=volume_attribute_config, replicationsId=GetReplicationAttributeConfig()) def GetActiveDirectoryResourceSpec(): return concepts.ResourceSpec( constants.ACTIVEDIRECTORIES_COLLECTION, resource_name='active_directory', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), activeDirectoriesId=GetActiveDirectoryAttributeConfig()) def GetKmsConfigResourceSpec(): return concepts.ResourceSpec( constants.KMSCONFIGS_COLLECTION, resource_name='kms_config', api_version=constants.BETA_API_VERSION, projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), kmsConfigsId=GetKmsConfigAttributeConfig()) def GetBackupVaultResourceSpec(positional=True): return concepts.ResourceSpec( constants.BACKUPVAULTS_COLLECTION, resource_name='backup_vault', api_version=constants.BETA_API_VERSION, projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), backupVaultsId=GetBackupVaultAttributeConfig(positional=positional), ) def GetBackupResourceSpec(positional=True): location_attribute_config = GetLocationAttributeConfig() backup_vault_attribute_config = GetBackupVaultAttributeConfig( positional=False ) if not positional: location_attribute_config.fallthroughs = [ deps.PropertyFallthrough(properties.VALUES.netapp.location), ] return concepts.ResourceSpec( constants.BACKUPS_COLLECTION, resource_name='backup', api_version=constants.BETA_API_VERSION, projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=location_attribute_config, backupVaultsId=backup_vault_attribute_config, backupsId=GetBackupAttributeConfig(), ) def GetBackupPolicyResourceSpec(): return concepts.ResourceSpec( constants.BACKUPPOLICIES_COLLECTION, resource_name='backup_policy', api_version=constants.BETA_API_VERSION, projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), backupPoliciesId=GetBackupPolicyAttributeConfig()) def GetCryptoKeyResourceSpec(): return concepts.ResourceSpec( 'cloudkms.projects.locations.keyRings.cryptoKeys', resource_name='crypto_key', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), keyRingsId=GetKeyRingAttributeConfig(), cryptoKeysId=GetCryptoKeyAttributeConfig() ) def GetQuotaRuleResourceSpec(): return concepts.ResourceSpec( constants.QUOTA_RULES_COLLECTION, resource_name='quota_rule', api_version=constants.BETA_API_VERSION, projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), volumesId=GetVolumeAttributeConfig(positional=False), quotaRulesId=GetQuotaRuleAttributeConfig(), ) def GetHostGroupResourceSpec(): return concepts.ResourceSpec( constants.HOST_GROUPS_COLLECTION, resource_name='host_group', projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG, locationsId=GetLocationAttributeConfig(), hostGroupsId=GetHostGroupAttributeConfig(), ) ## Presentation Specs ## def GetLocationPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'location', GetLocationResourceSpec(), group_help, required=True) def GetResourceListingLocationPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( '--location', GetListingLocationResourceSpec(), group_help) def GetOperationPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'operation', GetOperationResourceSpec(), group_help, required=True) def GetStoragePoolPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'storage_pool', GetStoragePoolResourceSpec(), group_help, required=True) def GetVolumePresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'volume', GetVolumeResourceSpec(), group_help, required=True) def GetSnapshotPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'snapshot', GetSnapshotResourceSpec(), group_help, required=True, flag_name_overrides={'volume': ''}) def GetReplicationPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'replication', GetReplicationResourceSpec(), group_help, required=True, flag_name_overrides={'volume': ''}) def GetActiveDirectoryPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'active_directory', GetActiveDirectoryResourceSpec(), group_help, required=True) def GetKmsConfigPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'kms_config', GetKmsConfigResourceSpec(), group_help, required=True) def GetBackupVaultPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'backup_vault', GetBackupVaultResourceSpec(), group_help, required=True) def GetBackupPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'backup', GetBackupResourceSpec(), group_help, required=True, flag_name_overrides={'backup_vault': ''}) def GetQuotaRulePresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'quota_rule', GetQuotaRuleResourceSpec(), group_help, required=True, flag_name_overrides={'volume': ''}) def GetHostGroupPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'host_group', GetHostGroupResourceSpec(), group_help, required=True, ) # TODO(b/290375665): Add more unit tests to test Backup Poicy # Presentation, resource specs and flags def GetBackupPolicyPresentationSpec(group_help): return presentation_specs.ResourcePresentationSpec( 'backup_policy', GetBackupPolicyResourceSpec(), group_help, required=True) ## Add args to arg parser ## def AddResourceDescriptionArg(parser, resource_name): """Add Description arg to arg_parser for a resource called resource_name.""" parser.add_argument( '--description', required=False, help='A description of the Cloud NetApp {}'.format(resource_name)) def AddResourceCapacityArg(parser, resource_name, required=True): """Add Capacity arg to arg_parser for a resource called resource_name.""" parser.add_argument( '--capacity', type=arg_parsers.BinarySize( default_unit='GiB', suggested_binary_size_scales=['GiB', 'TiB']), required=required, help=('The desired capacity of the {} in GiB or TiB units.' 'If no capacity unit is specified, GiB is assumed.'.format( resource_name))) def AddResourceAsyncFlag(parser): help_text = """Return immediately, without waiting for the operation in progress to complete.""" concepts.ResourceParameterAttributeConfig(name='async', help_text=help_text) base.ASYNC_FLAG.AddToParser(parser) def AddResourcePeerClusterNameArg(parser, required=True): """Adds the Peer Cluster Name (--peer-cluster-name) arg to the given parser. Args: parser: Argparse parser. required: Required to establish both cluster and svm peering. """ parser.add_argument( '--peer-cluster-name', type=str, required=required, help="""Name of the destination cluster to be peered with the source cluster.""", ) def AddResourcePeerSvmNameArg(parser, required=True): """Adds the Peer SVM Name (--peer-svm-name) arg to the given parser. Args: parser: Argparse parser. required: Required to establish both cluster and svm peering. """ parser.add_argument( '--peer-svm-name', type=str, required=required, help="""Name of the local source vserver svm to be peered with the destination cluster.""", ) def AddResourcePeerVolumeNameArg(parser, required=True): """Adds the Peer Volume Name (--peer-volume-name) arg to the given parser. Args: parser: Argparse parser. required: Required to establish both cluster and svm peering. """ parser.add_argument( '--peer-volume-name', type=str, required=required, help="""Name of the source volume to be peered with the destination volume.""", ) def AddResourcePeerIpAddressesArg(parser): """Adds the Peer IP Addresses (--peer-ip-addresses) arg to the given parser. Args: parser: Argparse parser. Not required for svm peering. """ parser.add_argument( '--peer-ip-addresses', type=arg_parsers.ArgList(min_length=1, element_type=str), metavar='PEER_IP_ADDRESS', help=( 'List of ip addresses to be used for peering. This is required for' ' cluster peering, not required for svm peering.' ), )