423 lines
15 KiB
Python
423 lines
15 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2019 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.
|
|
"""Common utility functions to construct compute reservations message."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from googlecloudsdk.calliope import exceptions
|
|
from googlecloudsdk.command_lib.compute import flags as compute_flags
|
|
from googlecloudsdk.command_lib.compute import scope as compute_scope
|
|
from googlecloudsdk.command_lib.compute.resource_policies import util as maintenance_util
|
|
from googlecloudsdk.core.util import times
|
|
import six
|
|
|
|
|
|
def MakeReservationMessageFromArgs(messages, args, reservation_ref, resources):
|
|
"""Construct reservation message from args passed in."""
|
|
accelerators = MakeGuestAccelerators(messages,
|
|
getattr(args, 'accelerator', None))
|
|
local_ssds = MakeLocalSsds(messages, getattr(args, 'local_ssd', None))
|
|
share_settings = MakeShareSettingsWithArgs(
|
|
messages, args, getattr(args, 'share_setting', None))
|
|
source_instance_template_ref = (
|
|
ResolveSourceInstanceTemplate(args, resources)
|
|
if args.IsKnownAndSpecified('source_instance_template')
|
|
else None
|
|
)
|
|
specific_reservation = MakeSpecificSKUReservationMessage(
|
|
messages,
|
|
args.vm_count,
|
|
accelerators,
|
|
local_ssds,
|
|
args.machine_type,
|
|
args.min_cpu_platform,
|
|
getattr(args, 'location_hint', None),
|
|
getattr(args, 'maintenance_freeze_duration', None),
|
|
getattr(args, 'maintenance_interval', None),
|
|
source_instance_template_ref,
|
|
)
|
|
resource_policies = MakeResourcePolicies(
|
|
messages, reservation_ref, getattr(args, 'resource_policies', None),
|
|
resources)
|
|
|
|
scheduling_type = None
|
|
if args.IsKnownAndSpecified('scheduling_type'):
|
|
scheduling_type = getattr(args, 'scheduling_type', None)
|
|
|
|
return MakeReservationMessage(
|
|
messages,
|
|
reservation_ref.Name(),
|
|
share_settings,
|
|
specific_reservation,
|
|
resource_policies,
|
|
args.require_specific_reservation,
|
|
reservation_ref.zone,
|
|
getattr(args, 'delete_at_time', None),
|
|
getattr(args, 'delete_after_duration', None),
|
|
getattr(args, 'reservation_sharing_policy', None),
|
|
getattr(args, 'enable_emergent_maintenance', None),
|
|
scheduling_type,
|
|
)
|
|
|
|
|
|
def ResolveSourceInstanceTemplate(args, resources):
|
|
return compute_flags.ResourceArgument(
|
|
'--source-instance-template',
|
|
resource_name='instance template',
|
|
scope_flags_usage=compute_flags.ScopeFlagsUsage.DONT_USE_SCOPE_FLAGS,
|
|
global_collection='compute.instanceTemplates',
|
|
regional_collection='compute.regionInstanceTemplates',
|
|
).ResolveAsResource(
|
|
args, resources, default_scope=compute_scope.ScopeEnum.GLOBAL
|
|
)
|
|
|
|
|
|
def MakeGuestAccelerators(messages, accelerator_configs):
|
|
"""Constructs the repeated accelerator message objects."""
|
|
if accelerator_configs is None:
|
|
return []
|
|
|
|
accelerators = []
|
|
|
|
for a in accelerator_configs:
|
|
m = messages.AcceleratorConfig(
|
|
acceleratorCount=a['count'], acceleratorType=a['type'])
|
|
accelerators.append(m)
|
|
|
|
return accelerators
|
|
|
|
|
|
def MakeLocalSsds(messages, ssd_configs):
|
|
"""Constructs the repeated local_ssd message objects."""
|
|
if ssd_configs is None:
|
|
return []
|
|
|
|
local_ssds = []
|
|
disk_msg = (
|
|
messages
|
|
.AllocationSpecificSKUAllocationAllocatedInstancePropertiesReservedDisk)
|
|
interface_msg = disk_msg.InterfaceValueValuesEnum
|
|
for s in ssd_configs:
|
|
if s['interface'].upper() == 'NVME':
|
|
interface = interface_msg.NVME
|
|
elif s['interface'].upper() == 'SCSI':
|
|
interface = interface_msg.SCSI
|
|
else:
|
|
raise exceptions.InvalidArgumentException(
|
|
'--local-ssd',
|
|
'Must specify a valid interface (NVME, SCSI) for SSDs attached to the'
|
|
' instance.',
|
|
)
|
|
m = disk_msg(diskSizeGb=s['size'], interface=interface)
|
|
partitions = s.get('count', 1)
|
|
if partitions < 1:
|
|
raise exceptions.InvalidArgumentException(
|
|
'--local-ssd',
|
|
'Must specify a valid count (>= 1) for SSDs attached to the '
|
|
'reservation.',
|
|
)
|
|
local_ssds.extend([m] * partitions)
|
|
|
|
return local_ssds
|
|
|
|
|
|
def MakeShareSettingsWithArgs(messages,
|
|
args,
|
|
setting_configs,
|
|
share_with='share_with'):
|
|
"""Constructs the share settings message object from raw args as input."""
|
|
if setting_configs:
|
|
if setting_configs == 'organization':
|
|
return messages.ShareSettings(shareType=messages.ShareSettings
|
|
.ShareTypeValueValuesEnum.ORGANIZATION)
|
|
if setting_configs == 'local':
|
|
if args.IsSpecified(share_with) and share_with != 'remove_share_with':
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_with',
|
|
'The scope this reservation is to be shared with must not be '
|
|
'specified with share setting local.')
|
|
return messages.ShareSettings(
|
|
shareType=messages.ShareSettings.ShareTypeValueValuesEnum.LOCAL)
|
|
if setting_configs == 'projects':
|
|
if not args.IsSpecified(share_with):
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_with',
|
|
'The projects this reservation is to be shared with must be '
|
|
'specified.')
|
|
project_map = None
|
|
if share_with != 'remove_share_with':
|
|
project_map = MakeProjectMapFromProjectList(
|
|
messages, getattr(args, share_with, None))
|
|
return messages.ShareSettings(
|
|
shareType=messages.ShareSettings.ShareTypeValueValuesEnum
|
|
.SPECIFIC_PROJECTS,
|
|
projectMap=project_map)
|
|
if setting_configs == 'folders':
|
|
if not args.IsSpecified(share_with):
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_with',
|
|
'The folders this reservation is to be shared with must be '
|
|
'specified.')
|
|
return messages.ShareSettings(
|
|
shareType=messages.ShareSettings.ShareTypeValueValuesEnum
|
|
.DIRECT_PROJECTS_UNDER_SPECIFIC_FOLDERS,
|
|
folderMap=MakeFolderMapFromFolderList(messages,
|
|
getattr(args, share_with,
|
|
None)))
|
|
else:
|
|
if args.IsKnownAndSpecified(share_with):
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_setting',
|
|
'Please specify share setting if specifying share with.')
|
|
return None
|
|
|
|
|
|
def MakeShareSettingsWithDict(messages, dictionary, setting_configs):
|
|
"""Constructs the share settings message object from dictionary form of input."""
|
|
if setting_configs:
|
|
if setting_configs == 'organization':
|
|
return messages.ShareSettings(shareType=messages.ShareSettings
|
|
.ShareTypeValueValuesEnum.ORGANIZATION)
|
|
if setting_configs == 'local':
|
|
if 'share_with' in dictionary.keys():
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_with',
|
|
'The scope this reservation is to be shared with must not be '
|
|
'specified with share setting local.')
|
|
return messages.ShareSettings(
|
|
shareType=messages.ShareSettings.ShareTypeValueValuesEnum.LOCAL)
|
|
if setting_configs == 'projects':
|
|
if 'share_with' not in dictionary.keys():
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_with',
|
|
'The projects this reservation is to be shared with must be '
|
|
'specified.')
|
|
return messages.ShareSettings(
|
|
shareType=messages.ShareSettings.ShareTypeValueValuesEnum
|
|
.SPECIFIC_PROJECTS,
|
|
projectMap=MakeProjectMapFromProjectList(
|
|
messages, dictionary.get('share_with', None)))
|
|
if setting_configs == 'folders':
|
|
if 'share_with' not in dictionary.keys():
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_with',
|
|
'The folders this reservation is to be shared with must be '
|
|
'specified.')
|
|
return messages.ShareSettings(
|
|
shareType=messages.ShareSettings.ShareTypeValueValuesEnum
|
|
.DIRECT_PROJECTS_UNDER_SPECIFIC_FOLDERS,
|
|
folderMap=MakeFolderMapFromFolderList(
|
|
messages, dictionary.get('share_with', None)))
|
|
else:
|
|
if 'share_with' in dictionary.keys():
|
|
raise exceptions.InvalidArgumentException(
|
|
'--share_setting',
|
|
'Please specify share setting if specifying share with.')
|
|
return None
|
|
|
|
|
|
def MakeSpecificSKUReservationMessage(
|
|
messages,
|
|
vm_count,
|
|
accelerators,
|
|
local_ssds,
|
|
machine_type,
|
|
min_cpu_platform,
|
|
location_hint=None,
|
|
freeze_duration=None,
|
|
freeze_interval=None,
|
|
source_instance_template_ref=None,
|
|
):
|
|
"""Constructs a single specific sku reservation message object."""
|
|
prop_msgs = (
|
|
messages.AllocationSpecificSKUAllocationReservedInstanceProperties)
|
|
if source_instance_template_ref:
|
|
return messages.AllocationSpecificSKUReservation(
|
|
count=vm_count,
|
|
sourceInstanceTemplate=source_instance_template_ref.SelfLink(),
|
|
instanceProperties=None,
|
|
)
|
|
else:
|
|
instance_properties = prop_msgs(
|
|
guestAccelerators=accelerators,
|
|
localSsds=local_ssds,
|
|
machineType=machine_type,
|
|
minCpuPlatform=min_cpu_platform)
|
|
if freeze_duration:
|
|
instance_properties.maintenanceFreezeDurationHours = freeze_duration // 3600
|
|
if freeze_interval:
|
|
instance_properties.maintenanceInterval = (
|
|
messages.AllocationSpecificSKUAllocationReservedInstanceProperties
|
|
.MaintenanceIntervalValueValuesEnum(freeze_interval))
|
|
if location_hint:
|
|
instance_properties.locationHint = location_hint
|
|
return messages.AllocationSpecificSKUReservation(
|
|
count=vm_count, instanceProperties=instance_properties)
|
|
|
|
|
|
def MakeReservationMessage(
|
|
messages,
|
|
reservation_name,
|
|
share_settings,
|
|
specific_reservation,
|
|
resource_policies,
|
|
require_specific_reservation,
|
|
reservation_zone,
|
|
delete_at_time=None,
|
|
delete_after_duration=None,
|
|
reservation_sharing_policy=None,
|
|
enable_emergent_maintenance=None,
|
|
scheduling_type=None,
|
|
):
|
|
"""Constructs a single reservations message object."""
|
|
reservation_message = messages.Reservation(
|
|
name=reservation_name,
|
|
specificReservation=specific_reservation,
|
|
specificReservationRequired=require_specific_reservation,
|
|
zone=reservation_zone)
|
|
if share_settings:
|
|
reservation_message.shareSettings = share_settings
|
|
if resource_policies:
|
|
reservation_message.resourcePolicies = resource_policies
|
|
|
|
if delete_at_time:
|
|
reservation_message.deleteAtTime = times.FormatDateTime(delete_at_time)
|
|
|
|
if delete_after_duration:
|
|
reservation_message.deleteAfterDuration = messages.Duration(
|
|
seconds=delete_after_duration
|
|
)
|
|
|
|
if reservation_sharing_policy:
|
|
reservation_message.reservationSharingPolicy = (
|
|
MakeReservationSharingPolicyMessage(
|
|
messages, reservation_sharing_policy
|
|
)
|
|
)
|
|
|
|
if enable_emergent_maintenance is not None:
|
|
reservation_message.enableEmergentMaintenance = enable_emergent_maintenance
|
|
|
|
if scheduling_type is not None:
|
|
reservation_message.schedulingType = (
|
|
MakeSchedulingType(messages, scheduling_type)
|
|
)
|
|
|
|
return reservation_message
|
|
|
|
|
|
def MakeReservationSharingPolicyMessage(messages, reservation_sharing_policy):
|
|
if reservation_sharing_policy == 'DISALLOW_ALL':
|
|
return messages.AllocationReservationSharingPolicy(
|
|
serviceShareType=messages.AllocationReservationSharingPolicy.ServiceShareTypeValueValuesEnum.DISALLOW_ALL
|
|
)
|
|
elif reservation_sharing_policy == 'ALLOW_ALL':
|
|
return messages.AllocationReservationSharingPolicy(
|
|
serviceShareType=messages.AllocationReservationSharingPolicy.ServiceShareTypeValueValuesEnum.ALLOW_ALL
|
|
)
|
|
else:
|
|
return None
|
|
|
|
|
|
def MakeProjectMapFromProjectList(messages, projects):
|
|
additional_properties = []
|
|
for project in projects:
|
|
additional_properties.append(
|
|
messages.ShareSettings.ProjectMapValue.AdditionalProperty(
|
|
key=project,
|
|
value=messages.ShareSettingsProjectConfig(projectId=project)))
|
|
return messages.ShareSettings.ProjectMapValue(
|
|
additionalProperties=additional_properties)
|
|
|
|
|
|
def MakeFolderMapFromFolderList(messages, folders):
|
|
additional_properties = []
|
|
for folder in folders:
|
|
additional_properties.append(
|
|
messages.ShareSettings.FolderMapValue.AdditionalProperty(
|
|
key=folder,
|
|
value=messages.ShareSettingsFolderConfig(folderId=folder)))
|
|
return messages.ShareSettings.FolderMapValue(
|
|
additionalProperties=additional_properties)
|
|
|
|
|
|
def MakeResourcePolicies(messages, reservation_ref, resource_policy_dictionary,
|
|
resources):
|
|
"""Constructs the resource policies message objects."""
|
|
if resource_policy_dictionary is None:
|
|
return None
|
|
|
|
return messages.Reservation.ResourcePoliciesValue(additionalProperties=[
|
|
messages.Reservation.ResourcePoliciesValue.AdditionalProperty(
|
|
key=key, value=MakeUrl(resources, value, reservation_ref))
|
|
for key, value in sorted(six.iteritems(resource_policy_dictionary))
|
|
])
|
|
|
|
|
|
def MakeReservationsMaintenanceScope(messages, maintenance_scope):
|
|
"""Constructs the maintenance scope message object for reservations."""
|
|
if maintenance_scope == 'all':
|
|
return (
|
|
messages.ReservationsPerformMaintenanceRequest.MaintenanceScopeValueValuesEnum.ALL
|
|
)
|
|
elif maintenance_scope == 'unused':
|
|
return (
|
|
messages.ReservationsPerformMaintenanceRequest.MaintenanceScopeValueValuesEnum.UNUSED_CAPACITY
|
|
)
|
|
elif maintenance_scope == 'running':
|
|
return (
|
|
messages.ReservationsPerformMaintenanceRequest.MaintenanceScopeValueValuesEnum.RUNNING_VMS
|
|
)
|
|
else:
|
|
return None
|
|
|
|
|
|
def MakeReservationBlocksMaintenanceScope(messages, maintenance_scope):
|
|
"""Constructs the maintenance scope message object for reservation blocks."""
|
|
if maintenance_scope == 'all':
|
|
return (
|
|
messages.ReservationsBlocksPerformMaintenanceRequest.MaintenanceScopeValueValuesEnum.ALL
|
|
)
|
|
elif maintenance_scope == 'unused':
|
|
return (
|
|
messages.ReservationsBlocksPerformMaintenanceRequest.MaintenanceScopeValueValuesEnum.UNUSED_CAPACITY
|
|
)
|
|
elif maintenance_scope == 'running':
|
|
return (
|
|
messages.ReservationsBlocksPerformMaintenanceRequest.MaintenanceScopeValueValuesEnum.RUNNING_VMS
|
|
)
|
|
else:
|
|
return None
|
|
|
|
|
|
def MakeSchedulingType(messages, scheduling_type):
|
|
"""Constructs the scheduling type enum value."""
|
|
if scheduling_type:
|
|
if scheduling_type == 'GROUPED':
|
|
return messages.Reservation.SchedulingTypeValueValuesEnum.GROUPED
|
|
if scheduling_type == 'INDEPENDENT':
|
|
return messages.Reservation.SchedulingTypeValueValuesEnum.INDEPENDENT
|
|
return None
|
|
|
|
|
|
def MakeUrl(resources, value, reservation_ref):
|
|
return maintenance_util.ParseResourcePolicyWithZone(
|
|
resources,
|
|
value,
|
|
project=reservation_ref.project,
|
|
zone=reservation_ref.zone).SelfLink()
|