514 lines
20 KiB
Python
514 lines
20 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2015 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 managed instance group."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import sys
|
|
|
|
from apitools.base.py import encoding
|
|
from googlecloudsdk.api_lib.compute import base_classes
|
|
from googlecloudsdk.api_lib.compute import managed_instance_groups_utils
|
|
from googlecloudsdk.api_lib.compute import utils
|
|
from googlecloudsdk.api_lib.compute import zone_utils
|
|
from googlecloudsdk.api_lib.compute.instance_groups.managed import stateful_policy_utils as policy_utils
|
|
from googlecloudsdk.api_lib.compute.managed_instance_groups_utils import ValueOrNone
|
|
from googlecloudsdk.calliope import arg_parsers
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.calliope import exceptions
|
|
from googlecloudsdk.command_lib.compute import flags
|
|
from googlecloudsdk.command_lib.compute import resource_manager_tags_utils
|
|
from googlecloudsdk.command_lib.compute import scope as compute_scope
|
|
from googlecloudsdk.command_lib.compute.instance_groups import flags as instance_groups_flags
|
|
from googlecloudsdk.command_lib.compute.instance_groups.managed import flags as managed_flags
|
|
from googlecloudsdk.command_lib.compute.managed_instance_groups import auto_healing_utils
|
|
from googlecloudsdk.command_lib.util.apis import arg_utils
|
|
from googlecloudsdk.core import properties
|
|
import six
|
|
|
|
# API allows up to 58 characters but asked us to send only 54 (unless user
|
|
# explicitly asks us for more).
|
|
_MAX_LEN_FOR_DEDUCED_BASE_INSTANCE_NAME = 54
|
|
|
|
# Flags valid only for regional MIGs.
|
|
REGIONAL_FLAGS = [
|
|
'instance_redistribution_type',
|
|
'target_distribution_shape',
|
|
'on_repair_allow_changing_zone',
|
|
]
|
|
|
|
|
|
def _AddInstanceGroupManagerArgs(parser):
|
|
"""Adds args."""
|
|
parser.add_argument(
|
|
'--base-instance-name',
|
|
help=('Base name to use for the Compute Engine instances that will '
|
|
'be created with the managed instance group. If not provided '
|
|
'base instance name will be the prefix of instance group name.'))
|
|
parser.add_argument(
|
|
'--size',
|
|
required=True,
|
|
type=arg_parsers.BoundedInt(0, sys.maxsize, unlimited=True),
|
|
help='Initial number of instances you want in this group.')
|
|
instance_groups_flags.AddDescriptionFlag(parser)
|
|
parser.add_argument(
|
|
'--target-pool',
|
|
type=arg_parsers.ArgList(),
|
|
metavar='TARGET_POOL',
|
|
help=('Specifies any target pools you want the instances of this '
|
|
'managed instance group to be part of.'))
|
|
managed_flags.INSTANCE_TEMPLATE_ARG.AddArgument(parser)
|
|
|
|
|
|
def _IsZonalGroup(ref):
|
|
"""Checks if reference to instance group is zonal."""
|
|
return ref.Collection() == 'compute.instanceGroupManagers'
|
|
|
|
|
|
def ValidateUpdatePolicyAgainstStateful(update_policy, group_ref,
|
|
stateful_policy, client):
|
|
"""Validates and fixed update policy for stateful MIG.
|
|
|
|
Sets default values in update_policy for stateful IGMs or throws exception
|
|
if the wrong value is set explicitly.
|
|
|
|
Args:
|
|
update_policy: Update policy to be validated
|
|
group_ref: Reference of IGM being validated
|
|
stateful_policy: Stateful policy to check if the group is stateful
|
|
client: The compute API client
|
|
"""
|
|
if stateful_policy is None or _IsZonalGroup(group_ref):
|
|
return
|
|
redistribution_type_none = (
|
|
client.messages.InstanceGroupManagerUpdatePolicy
|
|
.InstanceRedistributionTypeValueValuesEnum.NONE)
|
|
if (not update_policy or
|
|
update_policy.instanceRedistributionType != redistribution_type_none):
|
|
raise exceptions.RequiredArgumentException(
|
|
'--instance-redistribution-type',
|
|
'Stateful regional IGMs need to have instance redistribution type '
|
|
'set to \'NONE\'. Use \'--instance-redistribution-type=NONE\'.')
|
|
|
|
|
|
@base.UniverseCompatible
|
|
@base.ReleaseTracks(base.ReleaseTrack.GA)
|
|
class CreateGA(base.CreateCommand):
|
|
"""Create Compute Engine managed instance groups."""
|
|
|
|
support_update_policy_min_ready_flag = False
|
|
support_resource_manager_tags = False
|
|
|
|
@classmethod
|
|
def Args(cls, parser):
|
|
parser.display_info.AddFormat(managed_flags.DEFAULT_CREATE_OR_LIST_FORMAT)
|
|
_AddInstanceGroupManagerArgs(parser)
|
|
auto_healing_utils.AddAutohealingArgs(parser)
|
|
igm_arg = instance_groups_flags.GetInstanceGroupManagerArg(zones_flag=True)
|
|
igm_arg.AddArgument(parser, operation_type='create')
|
|
instance_groups_flags.AddZonesFlag(parser)
|
|
instance_groups_flags.AddMigCreateStatefulFlags(parser)
|
|
instance_groups_flags.AddMigCreateStatefulIPsFlags(parser)
|
|
managed_flags.AddMigInstanceRedistributionTypeFlag(parser)
|
|
managed_flags.AddMigDistributionPolicyTargetShapeFlag(parser)
|
|
managed_flags.AddMigListManagedInstancesResultsFlag(parser)
|
|
managed_flags.AddMigUpdatePolicyFlags(
|
|
parser, support_min_ready_flag=cls.support_update_policy_min_ready_flag)
|
|
managed_flags.AddMigForceUpdateOnRepairFlags(parser)
|
|
if cls.support_resource_manager_tags:
|
|
managed_flags.AddMigResourceManagerTagsFlags(parser)
|
|
managed_flags.AddMigDefaultActionOnVmFailure(parser, cls.ReleaseTrack())
|
|
managed_flags.AddInstanceFlexibilityPolicyArgs(parser)
|
|
managed_flags.AddStandbyPolicyFlags(parser)
|
|
managed_flags.AddWorkloadPolicyFlag(parser)
|
|
# When adding RMIG-specific flag, update REGIONAL_FLAGS constant.
|
|
|
|
def _HandleStatefulArgs(self, instance_group_manager, args, client):
|
|
instance_groups_flags.ValidateManagedInstanceGroupStatefulDisksProperties(
|
|
args)
|
|
instance_groups_flags.ValidateManagedInstanceGroupStatefulIPsProperties(
|
|
args
|
|
)
|
|
if (
|
|
args.stateful_disk
|
|
or args.stateful_internal_ip
|
|
or args.stateful_external_ip
|
|
):
|
|
instance_group_manager.statefulPolicy = (
|
|
self._CreateStatefulPolicy(args, client))
|
|
|
|
def _CreateStatefulPolicy(self, args, client):
|
|
"""Create stateful policy from disks of args --stateful-disk, and ips of args --stateful-external-ips and --stateful-internal-ips."""
|
|
stateful_disks = []
|
|
for stateful_disk_dict in (args.stateful_disk or []):
|
|
stateful_disks.append(
|
|
policy_utils.MakeStatefulPolicyPreservedStateDiskEntry(
|
|
client.messages, stateful_disk_dict))
|
|
stateful_disks.sort(key=lambda x: x.key)
|
|
stateful_policy = policy_utils.MakeStatefulPolicy(
|
|
client.messages, stateful_disks
|
|
)
|
|
|
|
stateful_internal_ips = []
|
|
for stateful_ip_dict in args.stateful_internal_ip or []:
|
|
stateful_internal_ips.append(
|
|
policy_utils.MakeStatefulPolicyPreservedStateInternalIPEntry(
|
|
client.messages, stateful_ip_dict
|
|
)
|
|
)
|
|
stateful_internal_ips.sort(key=lambda x: x.key)
|
|
stateful_policy.preservedState.internalIPs = (
|
|
client.messages.StatefulPolicyPreservedState.InternalIPsValue(
|
|
additionalProperties=stateful_internal_ips
|
|
)
|
|
)
|
|
|
|
stateful_external_ips = []
|
|
for stateful_ip_dict in args.stateful_external_ip or []:
|
|
stateful_external_ips.append(
|
|
policy_utils.MakeStatefulPolicyPreservedStateExternalIPEntry(
|
|
client.messages, stateful_ip_dict
|
|
)
|
|
)
|
|
stateful_external_ips.sort(key=lambda x: x.key)
|
|
stateful_policy.preservedState.externalIPs = (
|
|
client.messages.StatefulPolicyPreservedState.ExternalIPsValue(
|
|
additionalProperties=stateful_external_ips
|
|
)
|
|
)
|
|
|
|
return stateful_policy
|
|
|
|
def _CreateGroupReference(self, args, client, resources):
|
|
if args.zones:
|
|
zone_ref = resources.Parse(
|
|
args.zones[0],
|
|
collection='compute.zones',
|
|
params={'project': properties.VALUES.core.project.GetOrFail})
|
|
region = utils.ZoneNameToRegionName(zone_ref.Name())
|
|
return resources.Parse(
|
|
args.name,
|
|
params={
|
|
'region': region,
|
|
'project': properties.VALUES.core.project.GetOrFail
|
|
},
|
|
collection='compute.regionInstanceGroupManagers')
|
|
group_ref = (
|
|
instance_groups_flags.GetInstanceGroupManagerArg().ResolveAsResource)(
|
|
args,
|
|
resources,
|
|
default_scope=compute_scope.ScopeEnum.ZONE,
|
|
scope_lister=flags.GetDefaultScopeLister(client))
|
|
if _IsZonalGroup(group_ref):
|
|
zonal_resource_fetcher = zone_utils.ZoneResourceFetcher(client)
|
|
zonal_resource_fetcher.WarnForZonalCreation([group_ref])
|
|
return group_ref
|
|
|
|
def _CreateDistributionPolicy(self, args, resources, messages):
|
|
distribution_policy = messages.DistributionPolicy()
|
|
|
|
if args.zones:
|
|
policy_zones = []
|
|
for zone in args.zones:
|
|
zone_ref = resources.Parse(
|
|
zone,
|
|
collection='compute.zones',
|
|
params={'project': properties.VALUES.core.project.GetOrFail})
|
|
policy_zones.append(
|
|
messages.DistributionPolicyZoneConfiguration(
|
|
zone=zone_ref.SelfLink()))
|
|
distribution_policy.zones = policy_zones
|
|
|
|
if args.target_distribution_shape:
|
|
distribution_policy.targetShape = arg_utils.ChoiceToEnum(
|
|
args.target_distribution_shape,
|
|
messages.DistributionPolicy.TargetShapeValueValuesEnum)
|
|
return ValueOrNone(distribution_policy)
|
|
|
|
def _GetRegionForGroup(self, group_ref):
|
|
if _IsZonalGroup(group_ref):
|
|
return utils.ZoneNameToRegionName(group_ref.zone)
|
|
else:
|
|
return group_ref.region
|
|
|
|
def _GetServiceForGroup(self, group_ref, compute):
|
|
if _IsZonalGroup(group_ref):
|
|
return compute.instanceGroupManagers
|
|
else:
|
|
return compute.regionInstanceGroupManagers
|
|
|
|
def _CreateResourceRequest(self, group_ref, instance_group_manager, client,
|
|
resources):
|
|
if _IsZonalGroup(group_ref):
|
|
instance_group_manager.zone = group_ref.zone
|
|
return client.messages.ComputeInstanceGroupManagersInsertRequest(
|
|
instanceGroupManager=instance_group_manager,
|
|
project=group_ref.project,
|
|
zone=group_ref.zone)
|
|
else:
|
|
region_link = resources.Parse(
|
|
group_ref.region,
|
|
params={'project': properties.VALUES.core.project.GetOrFail},
|
|
collection='compute.regions')
|
|
instance_group_manager.region = region_link.SelfLink()
|
|
return client.messages.ComputeRegionInstanceGroupManagersInsertRequest(
|
|
instanceGroupManager=instance_group_manager,
|
|
project=group_ref.project,
|
|
region=group_ref.region)
|
|
|
|
def _GetInstanceGroupManagerTargetPools(self, target_pools, group_ref,
|
|
holder):
|
|
pool_refs = []
|
|
if target_pools:
|
|
region = self._GetRegionForGroup(group_ref)
|
|
for pool in target_pools:
|
|
pool_refs.append(
|
|
holder.resources.Parse(
|
|
pool,
|
|
params={
|
|
'project': properties.VALUES.core.project.GetOrFail,
|
|
'region': region
|
|
},
|
|
collection='compute.targetPools'))
|
|
return [pool_ref.SelfLink() for pool_ref in pool_refs]
|
|
|
|
def _CreateParams(self, client, resource_manager_tags):
|
|
resource_manager_tags_map = (
|
|
resource_manager_tags_utils.GetResourceManagerTags(
|
|
resource_manager_tags
|
|
)
|
|
)
|
|
params = client.messages.InstanceGroupManagerParams
|
|
additional_properties = [
|
|
params.ResourceManagerTagsValue.AdditionalProperty(key=key, value=value)
|
|
for key, value in sorted(six.iteritems(resource_manager_tags_map))
|
|
]
|
|
return params(
|
|
resourceManagerTags=params.ResourceManagerTagsValue(
|
|
additionalProperties=additional_properties
|
|
)
|
|
)
|
|
|
|
def _CreateInstanceGroupManager(self, args, group_ref, template_ref, client,
|
|
holder):
|
|
"""Create parts of Instance Group Manager shared for the track."""
|
|
managed_flags.ValidateRegionalMigFlagsUsage(args, REGIONAL_FLAGS, group_ref)
|
|
instance_groups_flags.ValidateManagedInstanceGroupScopeArgs(
|
|
args, holder.resources)
|
|
health_check = managed_instance_groups_utils.GetHealthCheckUri(
|
|
holder.resources, args)
|
|
auto_healing_policies = (
|
|
managed_instance_groups_utils.CreateAutohealingPolicies(
|
|
client.messages, health_check, args.initial_delay))
|
|
managed_instance_groups_utils.ValidateAutohealingPolicies(
|
|
auto_healing_policies)
|
|
update_policy = managed_instance_groups_utils.PatchUpdatePolicy(
|
|
client, args, None)
|
|
instance_lifecycle_policy = (
|
|
managed_instance_groups_utils.CreateInstanceLifecyclePolicy(
|
|
client.messages, args
|
|
)
|
|
)
|
|
instance_flexibility_policy = (
|
|
managed_instance_groups_utils.CreateInstanceFlexibilityPolicy(
|
|
args, client.messages
|
|
)
|
|
)
|
|
resource_policies = managed_instance_groups_utils.CreateResourcePolicies(
|
|
client.messages, args
|
|
)
|
|
|
|
instance_group_manager = client.messages.InstanceGroupManager(
|
|
name=group_ref.Name(),
|
|
description=args.description,
|
|
instanceTemplate=template_ref.SelfLink(),
|
|
baseInstanceName=args.base_instance_name,
|
|
targetPools=self._GetInstanceGroupManagerTargetPools(
|
|
args.target_pool, group_ref, holder
|
|
),
|
|
targetSize=int(args.size),
|
|
autoHealingPolicies=auto_healing_policies,
|
|
distributionPolicy=self._CreateDistributionPolicy(
|
|
args, holder.resources, client.messages
|
|
),
|
|
updatePolicy=update_policy,
|
|
instanceLifecyclePolicy=instance_lifecycle_policy,
|
|
instanceFlexibilityPolicy=instance_flexibility_policy,
|
|
resourcePolicies=resource_policies,
|
|
)
|
|
|
|
if args.IsSpecified('list_managed_instances_results'):
|
|
instance_group_manager.listManagedInstancesResults = (
|
|
client.messages.InstanceGroupManager
|
|
.ListManagedInstancesResultsValueValuesEnum)(
|
|
args.list_managed_instances_results.upper())
|
|
|
|
if self.support_resource_manager_tags and args.resource_manager_tags:
|
|
instance_group_manager.params = self._CreateParams(
|
|
client, args.resource_manager_tags
|
|
)
|
|
|
|
self._HandleStatefulArgs(instance_group_manager, args, client)
|
|
|
|
# Validate updatePolicy + statefulPolicy combination
|
|
ValidateUpdatePolicyAgainstStateful(instance_group_manager.updatePolicy,
|
|
group_ref,
|
|
instance_group_manager.statefulPolicy,
|
|
client)
|
|
|
|
standby_policy = managed_instance_groups_utils.CreateStandbyPolicy(
|
|
client.messages,
|
|
args.standby_policy_initial_delay,
|
|
args.standby_policy_mode,
|
|
)
|
|
if standby_policy:
|
|
instance_group_manager.standbyPolicy = standby_policy
|
|
if args.suspended_size:
|
|
instance_group_manager.targetSuspendedSize = args.suspended_size
|
|
if args.stopped_size:
|
|
instance_group_manager.targetStoppedSize = args.stopped_size
|
|
|
|
if args.IsKnownAndSpecified('target_size_policy_mode'):
|
|
instance_group_manager.targetSizePolicy = (
|
|
managed_instance_groups_utils.CreateTargetSizePolicy(
|
|
client.messages, args.target_size_policy_mode
|
|
)
|
|
)
|
|
|
|
return instance_group_manager
|
|
|
|
def _PostProcessOutput(self, holder, migs):
|
|
# 0 to 1 MIGs.
|
|
for mig in [encoding.MessageToDict(m) for m in migs]:
|
|
# At this point we're missing information about autoscaler and current
|
|
# size. To avoid making additional calls to API, we assume current size to
|
|
# be 0, since MIG has just been created. We also assume that there's no
|
|
# autoscaler, since API doesn't allow to insert MIG simultaneously with
|
|
# autoscaler.
|
|
mig['size'] = 0
|
|
# Same as "mig['autoscaled'] = 'no'", but making sure that property value
|
|
# is consistent with the one used to list groups.
|
|
managed_instance_groups_utils.ResolveAutoscalingStatusForMig(
|
|
holder.client, mig)
|
|
yield mig
|
|
|
|
def Run(self, args):
|
|
"""Creates and issues an instanceGroupManagers.Insert request.
|
|
|
|
Args:
|
|
args: the argparse arguments that this command was invoked with.
|
|
|
|
Returns:
|
|
List containing one dictionary: resource augmented with 'autoscaled'
|
|
property
|
|
"""
|
|
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
|
|
client = holder.client
|
|
|
|
group_ref = self._CreateGroupReference(args, client, holder.resources)
|
|
|
|
template_ref = managed_flags.INSTANCE_TEMPLATE_ARG.ResolveAsResource(
|
|
args,
|
|
holder.resources,
|
|
default_scope=flags.compute_scope.ScopeEnum.GLOBAL,
|
|
)
|
|
|
|
instance_group_manager = self._CreateInstanceGroupManager(
|
|
args, group_ref, template_ref, client, holder)
|
|
request = self._CreateResourceRequest(group_ref, instance_group_manager,
|
|
client, holder.resources)
|
|
service = self._GetServiceForGroup(group_ref, client.apitools_client)
|
|
migs = client.MakeRequests([(service, 'Insert', request)])
|
|
return self._PostProcessOutput(holder, migs)
|
|
|
|
|
|
CreateGA.detailed_help = {
|
|
'brief': 'Create a Compute Engine managed instance group',
|
|
'DESCRIPTION': """\
|
|
*{command}* creates a Compute Engine managed instance group.
|
|
""",
|
|
'EXAMPLES': """\
|
|
Running:
|
|
|
|
$ {command} example-managed-instance-group --zone=us-central1-a --template=example-global-instance-template --size=1
|
|
|
|
will create a managed instance group called 'example-managed-instance-group'
|
|
in the ``us-central1-a'' zone with a global instance template resource
|
|
'example-global-instance-template'.
|
|
|
|
To use a regional instance template, specify the full or partial URL of the template.
|
|
|
|
Running:
|
|
|
|
$ {command} example-managed-instance-group --zone=us-central1-a \\
|
|
--template=projects/example-project/regions/us-central1/instanceTemplates/example-regional-instance-template \\
|
|
--size=1
|
|
|
|
will create a managed instance group called
|
|
'example-managed-instance-group' in the ``us-central1-a'' zone with a
|
|
regional instance template resource 'example-regional-instance-template'.
|
|
""",
|
|
}
|
|
|
|
|
|
@base.ReleaseTracks(base.ReleaseTrack.BETA)
|
|
class CreateBeta(CreateGA):
|
|
"""Create Compute Engine managed instance groups."""
|
|
|
|
support_update_policy_min_ready_flag = True
|
|
support_resource_manager_tags = True
|
|
|
|
@classmethod
|
|
def Args(cls, parser):
|
|
managed_flags.AddMigActionOnVmFailedHealthCheck(parser)
|
|
managed_flags.AddTargetSizePolicyModeFlag(parser)
|
|
managed_flags.AddOnRepairFlags(parser)
|
|
super(CreateBeta, cls).Args(parser)
|
|
|
|
def _CreateInstanceGroupManager(self, args, group_ref, template_ref, client,
|
|
holder):
|
|
instance_group_manager = super(CreateBeta,
|
|
self)._CreateInstanceGroupManager(
|
|
args, group_ref, template_ref, client,
|
|
holder)
|
|
return instance_group_manager
|
|
|
|
|
|
CreateBeta.detailed_help = CreateGA.detailed_help
|
|
|
|
|
|
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
|
|
class CreateAlpha(CreateBeta):
|
|
"""Create Compute Engine managed instance groups."""
|
|
|
|
support_resource_manager_tags = True
|
|
|
|
@classmethod
|
|
def Args(cls, parser):
|
|
super(CreateAlpha, cls).Args(parser)
|
|
|
|
def _CreateInstanceGroupManager(
|
|
self, args, group_ref, template_ref, client, holder
|
|
):
|
|
instance_group_manager = super(
|
|
CreateAlpha, self
|
|
)._CreateInstanceGroupManager(args, group_ref, template_ref, client, holder)
|
|
|
|
return instance_group_manager
|
|
|
|
CreateAlpha.detailed_help = CreateGA.detailed_help
|