# -*- coding: utf-8 -*- # # Copyright 2016 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 the compute disks commands.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.calliope import actions from googlecloudsdk.command_lib.compute import completers as compute_completers from googlecloudsdk.command_lib.compute import flags as compute_flags from googlecloudsdk.command_lib.util import completers from googlecloudsdk.core import properties _DETAILED_SOURCE_SNAPSHOT_HELP = """\ Source snapshot used to create the disks. It is safe to delete a snapshot after a disk has been created from the snapshot. In such cases, the disks will no longer reference the deleted snapshot. To get a list of snapshots in your current project, run `gcloud compute snapshots list`. A snapshot from an existing disk can be created using the `gcloud compute disks snapshot` command. This flag is mutually exclusive with *--image*. When using this option, the size of the disks must be at least as large as the snapshot size. Use *--size* to adjust the size of the disks. """ _DETAILED_SOURCE_INSTANT_SNAPSHOT_HELP = """\ Name of the source instant snapshot used to create the disks. """ _DETAILED_SOURCE_INSTANT_SNAPSHOT_PROJECT_HELP = """\ The project containing the instant snapshot used to create the disks. """ _SOURCE_DISK_DETAILED_HELP = """\ Source disk used to create the disk(s). It is safe to delete a source disk after a disk has been created from the source disk. To get a list of disks in your current project, run `gcloud compute disks list`. This flag is mutually exclusive with *--image* and *--source-snapshot*. When using this option, the size of the disks must be at least as large as the source disk size. Use *--size* to adjust the size of the disks. The source disk must be in the same zone/region as the disk to be created. """ _SOURCE_DISK_ZONE_EXPLANATION = """\ Zone of the source disk. This argument is not required if the target disk is in the same zone as the source disk. """ _SOURCE_DISK_REGION_EXPLANATION = """\ Region of the source disk. This argument is not required if the target disk is in the same region as the source disk. """ _ASYNC_PRIMARY_DISK_HELP = """\ Primary disk for asynchronous replication. This flag is required when creating a secondary disk. """ _ASYNC_PRIMARY_DISK_ZONE_EXPLANATION = """\ Zone of the primary disk for asynchronous replication. The primary and secondary disks must not be in the same region. """ _ASYNC_PRIMARY_DISK_REGION_EXPLANATION = """\ Region of the primary disk for asynchronous replication. The primary and secondary disks must not be in the same region. """ _ASYNC_SECONDARY_DISK_HELP = """\ Secondary disk for asynchronous replication. This flag is required when starting replication. """ _ASYNC_SECONDARY_DISK_ZONE_EXPLANATION = """\ Zone of the secondary disk for asynchronous replication. """ _ASYNC_SECONDARY_DISK_REGION_EXPLANATION = """\ Region of the secondary disk for asynchronous replication. """ _ASYNC_SECONDARY_DISK_PROJECT_EXPLANATION = """\ Project of the secondary disk for asynchronous replication. """ _ASYNC_PRIMARY_DISK_PROJECT_EXPLANATION = """\ Project of the primary disk for asynchronous replication. """ DEFAULT_LIST_FORMAT = """\ table( name, zone.basename(), sizeGb, type.basename(), status )""" MULTISCOPE_LIST_FORMAT = """ table( name, location(), location_scope(), sizeGb, type.basename(), status )""" class SnapshotsCompleter(compute_completers.ListCommandCompleter): def __init__(self, **kwargs): super(SnapshotsCompleter, self).__init__( collection='compute.snapshots', list_command='compute snapshots list --uri', **kwargs) class SnapshotGroupsCompleter(compute_completers.ListCommandCompleter): def __init__(self, **kwargs): super(SnapshotGroupsCompleter, self).__init__( collection='compute.snapshotGroups', list_command='alpha compute snapshot-groups list --uri', api_version='alpha', **kwargs) class SnapshotsCompleterAlpha(completers.MultiResourceCompleter): def __init__(self, **kwargs): super(SnapshotsCompleterAlpha, self).__init__( completers=[RegionSnapshotsCompleter, GlobalSnapshotsCompleter], **kwargs ) class GlobalSnapshotsCompleter(compute_completers.ListCommandCompleter): def __init__(self, **kwargs): super(GlobalSnapshotsCompleter, self).__init__( collection='compute.snapshots', list_command='compute snapshots list --uri', api_version='alpha', **kwargs ) class RegionSnapshotsCompleter(compute_completers.ListCommandCompleter): def __init__(self, **kwargs): super(RegionSnapshotsCompleter, self).__init__( collection='compute.regionSnapshots', list_command='compute regionSnapshots list --uri', api_version='alpha', **kwargs ) def MakeDiskArgZonal(plural): return compute_flags.ResourceArgument( resource_name='disk', completer=compute_completers.DisksCompleter, plural=plural, name='DISK_NAME', zonal_collection='compute.disks', zone_explanation=compute_flags.ZONE_PROPERTY_EXPLANATION) def MakeDiskArg(plural): return compute_flags.ResourceArgument( resource_name='disk', completer=compute_completers.DisksCompleter, plural=plural, name='DISK_NAME', zonal_collection='compute.disks', regional_collection='compute.regionDisks', zone_explanation=compute_flags.ZONE_PROPERTY_EXPLANATION, region_explanation=compute_flags.REGION_PROPERTY_EXPLANATION) def AddMultiWriterFlag(parser): return parser.add_argument( '--multi-writer', action='store_true', help=""" Create the disk in multi-writer mode so that it can be attached with read-write access to two VMs. The multi-writer feature requires specialized filesystems, among other restrictions. For more information, see https://cloud.google.com/compute/docs/disks/sharing-disks-between-vms. """) def AddEnableConfidentialComputeFlag(parser): return parser.add_argument( '--confidential-compute', action='store_true', help=""" Creates the disk with confidential compute mode enabled. Encryption with a Cloud KMS key is required to enable this option. """, ) def AddStopGroupAsyncReplicationArgs(parser): """Adds stop group async replication specific arguments to parser.""" parser.add_argument( 'DISK_CONSISTENCY_GROUP_POLICY', help='URL of the disk consistency group resource policy. The resource' 'policy is always in the region of the primary disks.' ) help_text = '{0} of the consistency group\'s primary or secondary disks. {1}' scope_parser = parser.add_mutually_exclusive_group() scope_parser.add_argument( '--zone', completer=compute_completers.ZonesCompleter, action=actions.StoreProperty(properties.VALUES.compute.zone), help=help_text.format('Zone', compute_flags.ZONE_PROPERTY_EXPLANATION)) scope_parser.add_argument( '--region', completer=compute_completers.RegionsCompleter, action=actions.StoreProperty(properties.VALUES.compute.region), help=help_text.format( 'Region', compute_flags.REGION_PROPERTY_EXPLANATION)) def AddBulkCreateArgs(parser): """Adds bulk create specific arguments to parser.""" parser.add_argument( '--source-consistency-group-policy', help=''' URL of the source consistency group resource policy. The resource policy is always the same region as the source disks. ''', # This argument is required because consistent cloning is only supported # feature under the BulkCreate now. May become optional in the future. required=True) help_text = """Target {0} of the created disks, which currently must be the same as the source {0}. {1}""" scope_parser = parser.add_mutually_exclusive_group(required=True) scope_parser.add_argument( '--zone', completer=compute_completers.ZonesCompleter, action=actions.StoreProperty(properties.VALUES.compute.zone), help=help_text.format('zone', compute_flags.ZONE_PROPERTY_EXPLANATION)) scope_parser.add_argument( '--region', completer=compute_completers.RegionsCompleter, action=actions.StoreProperty(properties.VALUES.compute.region), help=help_text.format('region', compute_flags.REGION_PROPERTY_EXPLANATION)) def AddBulkCreateArgsAlpha(parser): """Adds bulk create specific arguments to parser.""" parser.add_argument( '--source-consistency-group-policy', help=''' URL of the source consistency group resource policy. The resource policy is always the same region as the source disks. ''', # This argument is optional because we now support bulk insert from # multiple source types. required=False) help_text = """Target {0} of the created disks, which currently must be the same as the source {0}. {1}""" scope_parser = parser.add_mutually_exclusive_group(required=True) scope_parser.add_argument( '--zone', completer=compute_completers.ZonesCompleter, action=actions.StoreProperty(properties.VALUES.compute.zone), help=help_text.format('zone', compute_flags.ZONE_PROPERTY_EXPLANATION)) scope_parser.add_argument( '--region', completer=compute_completers.RegionsCompleter, action=actions.StoreProperty(properties.VALUES.compute.region), help=help_text.format('region', compute_flags.REGION_PROPERTY_EXPLANATION)) def AddProvisionedIopsFlag(parser, arg_parsers): return parser.add_argument( '--provisioned-iops', type=arg_parsers.BoundedInt(), help=( 'Provisioned IOPS of disk to create. Only for use with disks of type ' 'pd-extreme and hyperdisk-extreme.' ), ) def AddProvisionedThroughputFlag(parser, arg_parsers): return parser.add_argument( '--provisioned-throughput', type=arg_parsers.BoundedInt(), help=( 'Provisioned throughput of disk to create. The throughput unit is ' 'MB per sec. Only for use with disks of type hyperdisk-throughput.')) def AddArchitectureFlag(parser, messages): architecture_enum_type = messages.Disk.ArchitectureValueValuesEnum excluded_enums = [architecture_enum_type.ARCHITECTURE_UNSPECIFIED.name] architecture_choices = sorted( [e for e in architecture_enum_type.names() if e not in excluded_enums]) return parser.add_argument( '--architecture', choices=architecture_choices, help=( 'Specifies the architecture or processor type that this disk can ' 'support. For available processor types on Compute Engine, ' 'see https://cloud.google.com/compute/docs/cpu-platforms.' ), ) def AddAccessModeFlag(parser, messages): if hasattr(messages.Disk, 'AccessModeValueValuesEnum'): access_mode_enum_type = messages.Disk.AccessModeValueValuesEnum return parser.add_argument( '--access-mode', choices=access_mode_enum_type.names(), help=( 'Specifies how VMs attached to the disk can access the data on the' ' disk. To grant read-only access to multiple VMs attached to the' ' disk, set access-mode to READ_ONLY_MANY.' ' To grant read-write access' ' to only one VM attached to the disk, use READ_WRITE_SINGLE.' ' READ_WRITE_SINGLE is used if omitted.' ), ) def AddLocationHintArg(parser): parser.add_argument( '--location-hint', hidden=True, help="""\ Used by internal tools to control sub-zone location of the disk. """) def MakeSecondaryDiskArg(required=False): return compute_flags.ResourceArgument( resource_name='async secondary disk', name='--secondary-disk', completer=compute_completers.DisksCompleter, zonal_collection='compute.disks', regional_collection='compute.regionDisks', short_help='Secondary disk for asynchronous replication.', detailed_help=_ASYNC_SECONDARY_DISK_HELP, plural=False, required=required, scope_flags_usage=compute_flags.ScopeFlagsUsage .GENERATE_DEDICATED_SCOPE_FLAGS, zone_help_text=_ASYNC_SECONDARY_DISK_ZONE_EXPLANATION, region_help_text=_ASYNC_SECONDARY_DISK_REGION_EXPLANATION) def AddSecondaryDiskProject(parser, category=None): parser.add_argument( '--secondary-disk-project', category=category, help=_ASYNC_SECONDARY_DISK_PROJECT_EXPLANATION, ) def AddPrimaryDiskProject(parser, category=None): parser.add_argument( '--primary-disk-project', category=category, help=_ASYNC_PRIMARY_DISK_PROJECT_EXPLANATION, ) def AddKeepOldDiskArgs(parser): """Adds keep old disk argument group to parser.""" group = parser.add_group() group.add_argument( '--keep-old-disk', action='store_true', help=( 'If true, the old disk will be kept after the conversion. ' 'The old disk will be renamed to the original disk name with a ' 'suffix.' ), ) group.add_argument( '--target-disk-name', help=( 'Specifies the name of the new disk, ' 'it can only be used with --keep-old-disk.' ' For details on the naming convention for this ' 'resource, refer to: ' 'https://cloud.google.com/compute/docs/' 'naming-resources' ), ) def AddGuestOsFeatureArgs(parser, messages): group = parser.add_group() guest_os_feature_choices = [ messages.GuestOsFeature.TypeValueValuesEnum.GVNIC.name ] group.add_argument( '--add-guest-os-features', choices=guest_os_feature_choices, help=( 'Specifies guest OS features to add to the disk. Refer to' ' https://cloud.google.com/compute/docs/images/create-custom#guest-os-features' ' for a list of available options.' ), ) def AddSourceInstantSnapshotProject(parser, category=None): parser.add_argument( '--source-instant-snapshot-project', category=category, help=_DETAILED_SOURCE_INSTANT_SNAPSHOT_PROJECT_HELP, ) SOURCE_SNAPSHOT_ARG = compute_flags.ResourceArgument( resource_name='snapshot', completer=SnapshotsCompleter, name='--source-snapshot', plural=False, required=False, global_collection='compute.snapshots', short_help='Source snapshot used to create the disks.', detailed_help=_DETAILED_SOURCE_SNAPSHOT_HELP, ) SOURCE_SNAPSHOT_ARG_ALPHA = compute_flags.ResourceArgument( resource_name='snapshot', completer=SnapshotsCompleterAlpha, name='--source-snapshot', plural=False, required=False, global_collection='compute.snapshots', regional_collection='compute.regionSnapshots', short_help='Source snapshot used to create the disks.', detailed_help=_DETAILED_SOURCE_SNAPSHOT_HELP, ) SOURCE_INSTANT_SNAPSHOT_ARG = compute_flags.ResourceArgument( resource_name='source instant snapshot', completer=compute_completers.InstantSnapshotsCompleter, name='--source-instant-snapshot', zonal_collection='compute.instantSnapshots', regional_collection='compute.regionInstantSnapshots', plural=False, required=False, short_help='Name of the source instant snapshot used to create the disks.', detailed_help=_DETAILED_SOURCE_INSTANT_SNAPSHOT_HELP, scope_flags_usage=compute_flags.ScopeFlagsUsage.USE_EXISTING_SCOPE_FLAGS) SOURCE_INSTANT_SNAPSHOT_GROUP_ARG = compute_flags.ResourceArgument( resource_name='source instant snapshot group', name='--source-instant-snapshot-group', completer=compute_completers.InstantSnapshotGroupsCompleter, short_help='Source instant snapshot group used to create the disks.', zonal_collection='compute.instantSnapshotGroups', regional_collection='compute.regionInstantSnapshotGroups', required=False, ) SOURCE_SNAPSHOT_GROUP_ARG = compute_flags.ResourceArgument( resource_name='source snapshot group', name='--source-snapshot-group', completer=SnapshotGroupsCompleter, short_help='Source snapshot group used to create the disks.', global_collection='compute.snapshotGroups', required=False, ) SOURCE_DISK_ARG = compute_flags.ResourceArgument( resource_name='source disk', name='--source-disk', completer=compute_completers.DisksCompleter, short_help='Source disk used to create the disks. Source disk must be in' ' the same zone/region as the disk to be created.', detailed_help=_SOURCE_DISK_DETAILED_HELP, zonal_collection='compute.disks', regional_collection='compute.regionDisks', required=False, zone_help_text=_SOURCE_DISK_ZONE_EXPLANATION, region_help_text=_SOURCE_DISK_REGION_EXPLANATION) ASYNC_PRIMARY_DISK_ARG = compute_flags.ResourceArgument( resource_name='async primary disk', name='--primary-disk', completer=compute_completers.DisksCompleter, zonal_collection='compute.disks', regional_collection='compute.regionDisks', short_help='Primary disk for asynchronous replication. This option creates' ' a secondary disk for a given primary disk.', detailed_help=_ASYNC_PRIMARY_DISK_HELP, plural=False, required=False, scope_flags_usage=compute_flags.ScopeFlagsUsage .GENERATE_DEDICATED_SCOPE_FLAGS, zone_help_text=_ASYNC_PRIMARY_DISK_ZONE_EXPLANATION, region_help_text=_ASYNC_PRIMARY_DISK_REGION_EXPLANATION) STORAGE_POOL_ARG = compute_flags.ResourceArgument( resource_name='storage pool', name='--storage-pool', short_help=('Specifies the URI of the storage pool in which the disk is ' 'created.'), zonal_collection='compute.storagePools', plural=False, required=False, scope_flags_usage=compute_flags.ScopeFlagsUsage.USE_EXISTING_SCOPE_FLAGS) def AddSourceMachineImageNameArg(parser): # TODO: b/421424530 - switch to compute_flags.ResourceArgument when # disks.insert goes GA. compute_flags.ResourceArgument does not support # the hidden flag. parser.add_argument( '--source-machine-image', help="""\ Specifies the URI of the source machine image contiaining the disk to restore. Requires *--source-machine-image-disk-device-name* with the disk to restores device name. """, hidden=True, ) def AddSourceMachineImageDiskDeviceNameArg(parser): parser.add_argument( '--source-machine-image-disk-device-name', help="""\ Specifies the name of the disk to be restored from the source machine image. Requires *--source-machine-image* with the URI of the source machine image. """, hidden=True, )