252 lines
9.3 KiB
Python
252 lines
9.3 KiB
Python
# -*- 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.
|
|
"""Command for creating disks."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from googlecloudsdk.api_lib.compute import base_classes
|
|
from googlecloudsdk.api_lib.compute import filter_rewrite
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.command_lib.compute import flags
|
|
from googlecloudsdk.command_lib.compute.disks import flags as disks_flags
|
|
from googlecloudsdk.core import log
|
|
from googlecloudsdk.core import properties
|
|
|
|
DETAILED_HELP = {
|
|
'brief':
|
|
"""
|
|
Create multiple Compute Engine disks.
|
|
""",
|
|
'DESCRIPTION':
|
|
"""
|
|
*{command}* facilitates the creation of multiple Compute Engine
|
|
disks with a single command. This includes cloning a set of Async PD
|
|
secondary disks with the same consistency group policy.
|
|
""",
|
|
'EXAMPLES':
|
|
"""
|
|
To consistently clone secondary disks with the same consistency group
|
|
policy 'projects/example-project/regions/us-central1/resourcePolicies/example-group-policy' to target zone 'us-central1-a', run:
|
|
|
|
$ {command} --source-consistency-group-policy=projects/example-project/regions/us-central1/resourcePolicies/example-group-policy --zone=us-central1-a
|
|
""",
|
|
}
|
|
|
|
|
|
def _AlphaArgs(parser):
|
|
disks_flags.AddBulkCreateArgsAlpha(parser)
|
|
disks_flags.SOURCE_INSTANT_SNAPSHOT_GROUP_ARG.AddArgument(parser)
|
|
disks_flags.SOURCE_SNAPSHOT_GROUP_ARG.AddArgument(parser)
|
|
|
|
|
|
def _GetOperations(compute_client,
|
|
project,
|
|
operation_group_id,
|
|
scope_name,
|
|
is_zonal):
|
|
"""Requests operations with group id matching the given one."""
|
|
|
|
errors_to_collect = []
|
|
|
|
_, operation_filter = filter_rewrite.Rewriter().Rewrite(
|
|
expression='operationGroupId=' + operation_group_id)
|
|
|
|
if is_zonal:
|
|
operations_response = compute_client.MakeRequests(
|
|
[(compute_client.apitools_client.zoneOperations, 'List',
|
|
compute_client.apitools_client.zoneOperations.GetRequestType('List')(
|
|
filter=operation_filter, zone=scope_name, project=project))],
|
|
errors_to_collect=errors_to_collect,
|
|
log_result=False,
|
|
always_return_operation=True,
|
|
no_followup=True)
|
|
else:
|
|
operations_response = compute_client.MakeRequests(
|
|
[(compute_client.apitools_client.regionOperations, 'List',
|
|
compute_client.apitools_client.regionOperations.GetRequestType('List')
|
|
(filter=operation_filter, region=scope_name, project=project))],
|
|
errors_to_collect=errors_to_collect,
|
|
log_result=False,
|
|
always_return_operation=True,
|
|
no_followup=True)
|
|
|
|
return operations_response, errors_to_collect
|
|
|
|
|
|
def _GetResult(compute_client, request, operation_group_id, parent_errors):
|
|
"""Requests operations with group id and parses them as an output."""
|
|
|
|
is_zonal = hasattr(request, 'zone')
|
|
scope_name = request.zone if is_zonal else request.region
|
|
operations_response, errors = _GetOperations(compute_client, request.project,
|
|
operation_group_id, scope_name,
|
|
is_zonal)
|
|
result = {'operationGroupId': operation_group_id, 'createdDisksCount': 0}
|
|
if not parent_errors and not errors:
|
|
def IsPerDiskOperation(op):
|
|
return op.operationType == 'insert' and str(
|
|
op.status) == 'DONE' and op.error is None
|
|
|
|
result['createdDisksCount'] = sum(
|
|
map(IsPerDiskOperation, operations_response))
|
|
return result
|
|
|
|
|
|
@base.DefaultUniverseOnly
|
|
@base.ReleaseTracks(base.ReleaseTrack.GA)
|
|
class BulkCreate(base.Command):
|
|
"""Create multiple Compute Engine disks."""
|
|
|
|
@classmethod
|
|
def Args(cls, parser):
|
|
disks_flags.AddBulkCreateArgs(parser)
|
|
|
|
@classmethod
|
|
def _GetApiHolder(cls, no_http=False):
|
|
return base_classes.ComputeApiHolder(cls.ReleaseTrack(), no_http)
|
|
|
|
def Run(self, args):
|
|
return self._Run(args)
|
|
|
|
def _Run(self, args, support_multiple_source_restore=False):
|
|
compute_holder = self._GetApiHolder()
|
|
client = compute_holder.client
|
|
|
|
policy_url = getattr(args, 'source_consistency_group_policy', None)
|
|
project = properties.VALUES.core.project.GetOrFail()
|
|
if not support_multiple_source_restore:
|
|
if args.IsSpecified('zone'):
|
|
request = client.messages.ComputeDisksBulkInsertRequest(
|
|
project=project,
|
|
zone=args.zone,
|
|
bulkInsertDiskResource=client.messages.BulkInsertDiskResource(
|
|
sourceConsistencyGroupPolicy=policy_url))
|
|
request = (client.apitools_client.disks, 'BulkInsert', request)
|
|
else:
|
|
request = client.messages.ComputeRegionDisksBulkInsertRequest(
|
|
project=project,
|
|
region=args.region,
|
|
bulkInsertDiskResource=client.messages.BulkInsertDiskResource(
|
|
sourceConsistencyGroupPolicy=policy_url))
|
|
request = (client.apitools_client.regionDisks, 'BulkInsert', request)
|
|
else:
|
|
isg_ref = disks_flags.SOURCE_INSTANT_SNAPSHOT_GROUP_ARG.ResolveAsResource(
|
|
args,
|
|
compute_holder.resources,
|
|
scope_lister=flags.GetDefaultScopeLister(client),
|
|
)
|
|
if isg_ref is not None:
|
|
isg_params = client.messages.InstantSnapshotGroupParameters(
|
|
sourceInstantSnapshotGroup=isg_ref.SelfLink(),
|
|
)
|
|
else:
|
|
isg_params = None
|
|
ssg_ref = disks_flags.SOURCE_SNAPSHOT_GROUP_ARG.ResolveAsResource(
|
|
args,
|
|
compute_holder.resources,
|
|
)
|
|
if ssg_ref is not None:
|
|
ssg_params = client.messages.SnapshotGroupParameters(
|
|
sourceSnapshotGroup=ssg_ref.SelfLink(),
|
|
)
|
|
else:
|
|
ssg_params = None
|
|
if args.IsSpecified('zone'):
|
|
request = client.messages.ComputeDisksBulkInsertRequest(
|
|
project=project,
|
|
zone=args.zone,
|
|
bulkInsertDiskResource=client.messages.BulkInsertDiskResource(
|
|
sourceConsistencyGroupPolicy=policy_url,
|
|
instantSnapshotGroupParameters=isg_params,
|
|
snapshotGroupParameters=ssg_params))
|
|
request = (client.apitools_client.disks, 'BulkInsert', request)
|
|
else:
|
|
request = client.messages.ComputeRegionDisksBulkInsertRequest(
|
|
project=project,
|
|
region=args.region,
|
|
bulkInsertDiskResource=client.messages.BulkInsertDiskResource(
|
|
sourceConsistencyGroupPolicy=policy_url,
|
|
instantSnapshotGroupParameters=isg_params,
|
|
snapshotGroupParameters=ssg_params))
|
|
request = (client.apitools_client.regionDisks, 'BulkInsert', request)
|
|
errors_to_collect = []
|
|
response = client.MakeRequests([request],
|
|
errors_to_collect=errors_to_collect,
|
|
no_followup=True,
|
|
always_return_operation=True)
|
|
|
|
# filters error object so that only error message is persisted
|
|
if errors_to_collect:
|
|
# workaround to change errors_to_collect since tuples are immutable
|
|
for i in range(len(errors_to_collect)):
|
|
error_tuple = errors_to_collect[i]
|
|
error_list = list(error_tuple)
|
|
# When requests are accepted, but workflow server processed it
|
|
# exceptionally, the error message is in message field. However, when
|
|
# requests are rejected, message field doesn't exist, we don't need to
|
|
# extract error message from message field.
|
|
if hasattr(error_list[1], 'message'):
|
|
error_list[1] = error_list[1].message
|
|
errors_to_collect[i] = tuple(error_list)
|
|
self._errors = errors_to_collect
|
|
if not response:
|
|
return
|
|
operation_group_id = response[0].operationGroupId
|
|
result = _GetResult(client, request[2], operation_group_id,
|
|
errors_to_collect)
|
|
|
|
if response[0].statusMessage:
|
|
result['statusMessage'] = response[0].statusMessage
|
|
return result
|
|
|
|
def Epilog(self, resources_were_displayed):
|
|
del resources_were_displayed
|
|
if self._errors:
|
|
log.error(self._errors[0][1])
|
|
|
|
BulkCreate.detailed_help = DETAILED_HELP
|
|
|
|
|
|
@base.ReleaseTracks(base.ReleaseTrack.BETA)
|
|
class BulkCreateBeta(BulkCreate):
|
|
"""Create multiple Compute Engine disks."""
|
|
|
|
@classmethod
|
|
def Args(cls, parser):
|
|
disks_flags.AddBulkCreateArgs(parser)
|
|
|
|
def Run(self, args):
|
|
return self._Run(args)
|
|
|
|
|
|
BulkCreateBeta.detailed_help = DETAILED_HELP
|
|
|
|
|
|
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
|
|
class BulkCreateAlpha(BulkCreate):
|
|
"""Create multiple Compute Engine disks."""
|
|
|
|
@classmethod
|
|
def Args(cls, parser):
|
|
_AlphaArgs(parser)
|
|
|
|
def Run(self, args):
|
|
return self._Run(args,
|
|
support_multiple_source_restore=True)
|
|
|
|
BulkCreateAlpha.detailed_help = DETAILED_HELP
|