File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/compute/reservations/util.py
# -*- 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()