455 lines
18 KiB
Python
455 lines
18 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2017 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 labels update to instances."""
|
|
|
|
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 instance_utils
|
|
from googlecloudsdk.api_lib.compute import partner_metadata_utils
|
|
from googlecloudsdk.api_lib.compute.operations import poller
|
|
from googlecloudsdk.api_lib.util import waiter
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.command_lib.compute.instances import flags
|
|
from googlecloudsdk.command_lib.compute.sole_tenancy import flags as sole_tenancy_flags
|
|
from googlecloudsdk.command_lib.compute.sole_tenancy import util as sole_tenancy_util
|
|
from googlecloudsdk.command_lib.util.args import labels_util
|
|
|
|
|
|
DETAILED_HELP = {
|
|
'DESCRIPTION': """
|
|
*{command}* updates labels and requested CPU Platform for a
|
|
Compute
|
|
Engine virtual machine.
|
|
""",
|
|
'EXAMPLES': """
|
|
To modify the instance 'example-instance' in 'us-central1-a' by adding
|
|
labels 'k0', with value 'value1' and label 'k1' with value 'value2' and
|
|
removing labels with key 'k3', run:
|
|
|
|
$ {command} example-instance --zone=us-central1-a --update-labels=k0=value1,k1=value2 --remove-labels=k3
|
|
|
|
Labels can be used to identify the instance. To list instances with the 'k1:value2' label, run:
|
|
|
|
$ {parent_command} list --filter='labels.k1:value2'
|
|
|
|
To list only the labels when describing a resource, use --format to filter the result:
|
|
|
|
$ {parent_command} describe example-instance --format="default(labels)"
|
|
"""
|
|
}
|
|
|
|
|
|
@base.UniverseCompatible
|
|
@base.ReleaseTracks(base.ReleaseTrack.GA)
|
|
class Update(base.UpdateCommand):
|
|
"""Update a Compute Engine virtual machine."""
|
|
|
|
@staticmethod
|
|
def Args(parser):
|
|
|
|
flags.INSTANCE_ARG.AddArgument(parser, operation_type='update')
|
|
labels_util.AddUpdateLabelsFlags(parser)
|
|
flags.AddMinCpuPlatformArgs(parser, Update.ReleaseTrack())
|
|
flags.AddDeletionProtectionFlag(parser, use_default_value=False)
|
|
flags.AddShieldedInstanceConfigArgs(
|
|
parser, use_default_value=False, for_update=True)
|
|
flags.AddShieldedInstanceIntegrityPolicyArgs(parser)
|
|
flags.AddDisplayDeviceArg(parser, is_update=True)
|
|
sole_tenancy_flags.AddNodeAffinityFlagToParser(parser, is_update=True)
|
|
|
|
def Run(self, args):
|
|
return self._Run(args)
|
|
|
|
def _Run(self, args):
|
|
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
|
|
instance_ref = flags.INSTANCE_ARG.ResolveAsResource(
|
|
args, holder.resources,
|
|
scope_lister=flags.GetInstanceZoneScopeLister(holder.client))
|
|
|
|
result = None
|
|
|
|
labels_operation_ref = None
|
|
min_cpu_platform_operation_ref = None
|
|
deletion_protection_operation_ref = None
|
|
shielded_instance_config_ref = None
|
|
display_device_ref = None
|
|
partner_metadata_operation_ref = None
|
|
graceful_shutdown_operation_ref = None
|
|
|
|
labels_diff = labels_util.Diff.FromUpdateArgs(args)
|
|
if labels_diff.MayHaveUpdates():
|
|
instance = client.instances.Get(
|
|
messages.ComputeInstancesGetRequest(**instance_ref.AsDict()))
|
|
result = instance
|
|
labels_operation_ref = self._GetLabelsOperationRef(
|
|
labels_diff, instance, instance_ref, holder)
|
|
if hasattr(args, 'min_cpu_platform') and args.min_cpu_platform is not None:
|
|
min_cpu_platform_operation_ref = self._GetMinCpuPlatformOperationRef(
|
|
args.min_cpu_platform, instance_ref, holder)
|
|
if args.deletion_protection is not None:
|
|
deletion_protection_operation_ref = (
|
|
self._GetDeletionProtectionOperationRef(
|
|
args.deletion_protection, instance_ref, holder))
|
|
if hasattr(args, 'partner_metadata') and (
|
|
args.partner_metadata or args.partner_metadata_from_file
|
|
):
|
|
partner_metadata_operation_ref = self._GetPartnerMetadataOperationRef(
|
|
args, instance_ref, holder
|
|
)
|
|
if (
|
|
hasattr(args, 'graceful_shutdown')
|
|
and args.IsSpecified('graceful_shutdown')
|
|
) or (
|
|
hasattr(args, 'graceful_shutdown_max_duration')
|
|
and args.IsSpecified('graceful_shutdown_max_duration')
|
|
):
|
|
graceful_shutdown_operation_ref = self._GetGracefulShutdownOperationRef(
|
|
args, instance_ref, holder
|
|
)
|
|
|
|
operation_poller = poller.Poller(client.instances)
|
|
result = self._WaitForResult(
|
|
operation_poller, labels_operation_ref,
|
|
'Updating labels of instance [{0}]', instance_ref.Name()) or result
|
|
result = self._WaitForResult(
|
|
operation_poller, min_cpu_platform_operation_ref,
|
|
'Changing minimum CPU platform of instance [{0}]',
|
|
instance_ref.Name()) or result
|
|
result = self._WaitForResult(
|
|
operation_poller, deletion_protection_operation_ref,
|
|
'Setting deletion protection of instance [{0}] to [{1}]',
|
|
instance_ref.Name(), args.deletion_protection) or result
|
|
result = self._WaitForResult(
|
|
operation_poller, partner_metadata_operation_ref,
|
|
'Updating partner metadata of instance [{0}]',
|
|
instance_ref.Name()) or result
|
|
result = (
|
|
self._WaitForResult(
|
|
operation_poller,
|
|
graceful_shutdown_operation_ref,
|
|
'Updating graceful shutdown configuration of instance [{0}]',
|
|
instance_ref.Name(),
|
|
)
|
|
or result
|
|
)
|
|
|
|
if (args.IsSpecified('shielded_vm_secure_boot') or
|
|
args.IsSpecified('shielded_vm_vtpm') or
|
|
args.IsSpecified('shielded_vm_integrity_monitoring')):
|
|
shielded_instance_config_ref = self._GetShieldedInstanceConfigRef(
|
|
instance_ref, args, holder)
|
|
result = self._WaitForResult(
|
|
operation_poller, shielded_instance_config_ref,
|
|
'Setting shieldedInstanceConfig of instance [{0}]',
|
|
instance_ref.Name()) or result
|
|
|
|
if args.IsSpecified('shielded_vm_learn_integrity_policy'):
|
|
shielded_instance_integrity_policy_ref = (
|
|
self._GetShieldedInstanceIntegrityPolicyRef(instance_ref, holder))
|
|
result = self._WaitForResult(
|
|
operation_poller, shielded_instance_integrity_policy_ref,
|
|
'Setting shieldedInstanceIntegrityPolicy of instance [{0}]',
|
|
instance_ref.Name()) or result
|
|
|
|
if args.IsSpecified('enable_display_device'):
|
|
display_device_ref = self._GetDisplayDeviceOperationRef(
|
|
args.enable_display_device, instance_ref, holder)
|
|
result = self._WaitForResult(operation_poller, display_device_ref,
|
|
'Updating display device of instance [{0}]',
|
|
instance_ref.Name()) or result
|
|
|
|
if instance_utils.IsAnySpecified(args, 'node', 'node_affinity_file',
|
|
'node_group', 'clear_node_affinities'):
|
|
update_scheduling_ref = self._GetUpdateInstanceSchedulingRef(
|
|
instance_ref, args, holder)
|
|
result = self._WaitForResult(
|
|
operation_poller,
|
|
update_scheduling_ref, 'Updating the scheduling of instance [{0}]',
|
|
instance_ref.Name()) or result
|
|
|
|
return result
|
|
|
|
def _GetUpdateInstanceSchedulingRef(self, instance_ref, args, holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
if instance_utils.IsAnySpecified(args, 'node', 'node_affinity_file',
|
|
'node_group'):
|
|
affinities = sole_tenancy_util.GetSchedulingNodeAffinityListFromArgs(
|
|
args, messages)
|
|
elif args.IsSpecified('clear_node_affinities'):
|
|
affinities = []
|
|
else:
|
|
# No relevant args were specified. We shouldn't have called this function.
|
|
return None
|
|
instance = client.instances.Get(
|
|
messages.ComputeInstancesGetRequest(**instance_ref.AsDict()))
|
|
instance.scheduling.nodeAffinities = affinities
|
|
|
|
request = messages.ComputeInstancesUpdateRequest(
|
|
instance=instance_ref.Name(),
|
|
project=instance_ref.project,
|
|
zone=instance_ref.zone,
|
|
instanceResource=instance,
|
|
minimalAction=messages.ComputeInstancesUpdateRequest
|
|
.MinimalActionValueValuesEnum.NO_EFFECT,
|
|
mostDisruptiveAllowedAction=messages.ComputeInstancesUpdateRequest
|
|
.MostDisruptiveAllowedActionValueValuesEnum.REFRESH)
|
|
|
|
operation = client.instances.Update(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetShieldedInstanceConfigRef(self, instance_ref, args, holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
|
|
if (args.shielded_vm_secure_boot is None and
|
|
args.shielded_vm_vtpm is None and
|
|
args.shielded_vm_integrity_monitoring is None):
|
|
return None
|
|
shieldedinstance_config_message = (
|
|
instance_utils.CreateShieldedInstanceConfigMessage(
|
|
messages,
|
|
args.shielded_vm_secure_boot,
|
|
args.shielded_vm_vtpm,
|
|
args.shielded_vm_integrity_monitoring)
|
|
)
|
|
|
|
request = messages.ComputeInstancesUpdateShieldedInstanceConfigRequest(
|
|
instance=instance_ref.Name(),
|
|
project=instance_ref.project,
|
|
shieldedInstanceConfig=shieldedinstance_config_message,
|
|
zone=instance_ref.zone)
|
|
|
|
operation = client.instances.UpdateShieldedInstanceConfig(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetShieldedInstanceIntegrityPolicyRef(self, instance_ref, holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
|
|
shieldedinstance_integrity_policy_message = (
|
|
instance_utils.CreateShieldedInstanceIntegrityPolicyMessage(messages))
|
|
|
|
request = messages.ComputeInstancesSetShieldedInstanceIntegrityPolicyRequest(
|
|
instance=instance_ref.Name(),
|
|
project=instance_ref.project,
|
|
shieldedInstanceIntegrityPolicy=shieldedinstance_integrity_policy_message,
|
|
zone=instance_ref.zone)
|
|
|
|
operation = client.instances.SetShieldedInstanceIntegrityPolicy(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetLabelsOperationRef(self, labels_diff, instance, instance_ref, holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
|
|
labels_update = labels_diff.Apply(
|
|
messages.InstancesSetLabelsRequest.LabelsValue,
|
|
instance.labels)
|
|
|
|
if labels_update.needs_update:
|
|
request = messages.ComputeInstancesSetLabelsRequest(
|
|
project=instance_ref.project,
|
|
instance=instance_ref.instance,
|
|
zone=instance_ref.zone,
|
|
instancesSetLabelsRequest=
|
|
messages.InstancesSetLabelsRequest(
|
|
labelFingerprint=instance.labelFingerprint,
|
|
labels=labels_update.labels))
|
|
|
|
operation = client.instances.SetLabels(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetMinCpuPlatformOperationRef(self, min_cpu_platform, instance_ref,
|
|
holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
embedded_request = messages.InstancesSetMinCpuPlatformRequest(
|
|
minCpuPlatform=min_cpu_platform or None)
|
|
request = messages.ComputeInstancesSetMinCpuPlatformRequest(
|
|
instance=instance_ref.instance,
|
|
project=instance_ref.project,
|
|
instancesSetMinCpuPlatformRequest=embedded_request,
|
|
zone=instance_ref.zone)
|
|
|
|
operation = client.instances.SetMinCpuPlatform(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetDeletionProtectionOperationRef(self, deletion_protection,
|
|
instance_ref, holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
request = messages.ComputeInstancesSetDeletionProtectionRequest(
|
|
deletionProtection=deletion_protection,
|
|
project=instance_ref.project,
|
|
resource=instance_ref.instance,
|
|
zone=instance_ref.zone)
|
|
|
|
operation = client.instances.SetDeletionProtection(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetDisplayDeviceOperationRef(self, display_device, instance_ref, holder):
|
|
client = holder.client.apitools_client
|
|
messages = holder.client.messages
|
|
request = messages.ComputeInstancesUpdateDisplayDeviceRequest(
|
|
displayDevice=messages.DisplayDevice(
|
|
enableDisplay=display_device),
|
|
project=instance_ref.project,
|
|
instance=instance_ref.instance,
|
|
zone=instance_ref.zone)
|
|
|
|
operation = client.instances.UpdateDisplayDevice(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _GetGracefulShutdownOperationRef(self, args, instance_ref, holder):
|
|
messages = holder.client.messages
|
|
client = holder.client.apitools_client
|
|
instance = client.instances.Get(
|
|
messages.ComputeInstancesGetRequest(**instance_ref.AsDict())
|
|
)
|
|
|
|
updated_graceful_shutdown_message = instance.scheduling.gracefulShutdown
|
|
if hasattr(args, 'graceful_shutdown') and args.IsSpecified(
|
|
'graceful_shutdown'
|
|
):
|
|
if updated_graceful_shutdown_message is None:
|
|
updated_graceful_shutdown_message = (
|
|
messages.SchedulingGracefulShutdown()
|
|
)
|
|
updated_graceful_shutdown_message.enabled = args.graceful_shutdown
|
|
if hasattr(args, 'graceful_shutdown_max_duration') and args.IsSpecified(
|
|
'graceful_shutdown_max_duration'
|
|
):
|
|
if updated_graceful_shutdown_message is None:
|
|
updated_graceful_shutdown_message = (
|
|
messages.SchedulingGracefulShutdown()
|
|
)
|
|
updated_graceful_shutdown_message.maxDuration = messages.Duration(
|
|
seconds=args.graceful_shutdown_max_duration
|
|
)
|
|
|
|
instance.scheduling.gracefulShutdown = updated_graceful_shutdown_message
|
|
|
|
request = messages.ComputeInstancesUpdateRequest(
|
|
instance=instance_ref.Name(),
|
|
project=instance_ref.project,
|
|
zone=instance_ref.zone,
|
|
instanceResource=instance,
|
|
minimalAction=messages.ComputeInstancesUpdateRequest.MinimalActionValueValuesEnum.NO_EFFECT,
|
|
mostDisruptiveAllowedAction=messages.ComputeInstancesUpdateRequest.MostDisruptiveAllowedActionValueValuesEnum.REFRESH,
|
|
)
|
|
|
|
operation = client.instances.Update(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations'
|
|
)
|
|
|
|
def _GetPartnerMetadataOperationRef(self, args, instance_ref, holder):
|
|
messages = holder.client.messages
|
|
client = holder.client.apitools_client
|
|
partner_metadata_dict = (
|
|
partner_metadata_utils.CreatePartnerMetadataDict(args)
|
|
)
|
|
partner_metadata_utils.ValidatePartnerMetadata(partner_metadata_dict)
|
|
partner_metadata_message = messages.Instance.PartnerMetadataValue()
|
|
for namespace, structured_entries in partner_metadata_dict.items():
|
|
partner_metadata_message.additionalProperties.append(
|
|
messages.Instance.PartnerMetadataValue.AdditionalProperty(
|
|
key=namespace,
|
|
value=partner_metadata_utils.ConvertStructuredEntries(
|
|
structured_entries, messages
|
|
),
|
|
)
|
|
)
|
|
instance = client.instances.Get(
|
|
messages.ComputeInstancesGetRequest(**instance_ref.AsDict()))
|
|
instance.partnerMetadata = partner_metadata_message
|
|
request = messages.ComputeInstancesUpdateRequest(
|
|
instance=instance_ref.Name(),
|
|
project=instance_ref.project,
|
|
zone=instance_ref.zone,
|
|
instanceResource=instance,
|
|
minimalAction=messages.ComputeInstancesUpdateRequest
|
|
.MinimalActionValueValuesEnum.NO_EFFECT,
|
|
mostDisruptiveAllowedAction=messages.ComputeInstancesUpdateRequest
|
|
.MostDisruptiveAllowedActionValueValuesEnum.REFRESH)
|
|
|
|
operation = client.instances.Update(request)
|
|
return holder.resources.Parse(
|
|
operation.selfLink, collection='compute.zoneOperations')
|
|
|
|
def _WaitForResult(self, operation_poller, operation_ref, message, *args):
|
|
if operation_ref:
|
|
return waiter.WaitFor(
|
|
operation_poller, operation_ref, message.format(*args))
|
|
return None
|
|
|
|
|
|
@base.ReleaseTracks(base.ReleaseTrack.BETA)
|
|
class UpdateBeta(Update):
|
|
"""Update a Compute Engine virtual machine."""
|
|
|
|
@staticmethod
|
|
def Args(parser):
|
|
flags.INSTANCE_ARG.AddArgument(parser, operation_type='update')
|
|
labels_util.AddUpdateLabelsFlags(parser)
|
|
flags.AddMinCpuPlatformArgs(parser, UpdateBeta.ReleaseTrack())
|
|
flags.AddDeletionProtectionFlag(parser, use_default_value=False)
|
|
flags.AddShieldedInstanceConfigArgs(
|
|
parser, use_default_value=False, for_update=True)
|
|
flags.AddShieldedInstanceIntegrityPolicyArgs(parser)
|
|
flags.AddDisplayDeviceArg(parser, is_update=True)
|
|
sole_tenancy_flags.AddNodeAffinityFlagToParser(parser, is_update=True)
|
|
partner_metadata_utils.AddPartnerMetadataArgs(parser)
|
|
flags.AddGracefulShutdownArgs(parser)
|
|
|
|
def Run(self, args):
|
|
return self._Run(args)
|
|
|
|
|
|
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
|
|
class UpdateAlpha(UpdateBeta):
|
|
"""Update a Compute Engine virtual machine."""
|
|
|
|
@staticmethod
|
|
def Args(parser):
|
|
flags.INSTANCE_ARG.AddArgument(parser, operation_type='update')
|
|
labels_util.AddUpdateLabelsFlags(parser)
|
|
flags.AddMinCpuPlatformArgs(parser, UpdateAlpha.ReleaseTrack())
|
|
flags.AddDeletionProtectionFlag(parser, use_default_value=False)
|
|
flags.AddShieldedInstanceConfigArgs(
|
|
parser, use_default_value=False, for_update=True)
|
|
flags.AddShieldedInstanceIntegrityPolicyArgs(parser)
|
|
flags.AddDisplayDeviceArg(parser, is_update=True)
|
|
sole_tenancy_flags.AddNodeAffinityFlagToParser(parser, is_update=True)
|
|
partner_metadata_utils.AddPartnerMetadataArgs(parser)
|
|
flags.AddGracefulShutdownArgs(parser)
|
|
|
|
|
|
Update.detailed_help = DETAILED_HELP
|