HEX
Server: Apache/2.4.65 (Ubuntu)
System: Linux ielts-store-v2 6.8.0-1036-gcp #38~22.04.1-Ubuntu SMP Thu Aug 14 01:19:18 UTC 2025 x86_64
User: root (0)
PHP: 7.2.34-54+ubuntu20.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: //snap/google-cloud-cli/396/lib/googlecloudsdk/command_lib/compute/commitments/flags.py
# -*- 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.

"""Flags and helpers for the compute commitments commands."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import datetime

from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.command_lib.compute import completers as compute_completers
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.instances import flags as instance_flags
from googlecloudsdk.command_lib.compute.reservations import flags as reservation_flags
from googlecloudsdk.command_lib.compute.reservations import resource_args
from googlecloudsdk.command_lib.util.apis import arg_utils
import pytz


VALID_PLANS = ['12-month', '36-month']
EXTENDED_VALID_PLANS = ['12-month', '24-month', '36-month', '60-month']
VALID_UPDATE_PLANS = ['36-month']
EXTENDED_VALID_UPDATE_PLANS = ['24-month', '36-month', '60-month']
RFC3339_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
DATE_ONLY_FORMAT = '%Y-%m-%d'
_REQUIRED_RESOURCES = sorted(['vcpu', 'memory'])


class RegionCommitmentsCompleter(compute_completers.ListCommandCompleter):

  def __init__(self, **kwargs):
    super(RegionCommitmentsCompleter, self).__init__(
        collection='compute.regionCommitments',
        list_command='alpha compute commitments list --uri',
        **kwargs
    )


def _GetFlagToPlanMap(messages):
  result = {
      '12-month': messages.Commitment.PlanValueValuesEnum.TWELVE_MONTH,
      '36-month': messages.Commitment.PlanValueValuesEnum.THIRTY_SIX_MONTH,
  }
  if hasattr(messages.Commitment.PlanValueValuesEnum, 'TWENTY_FOUR_MONTH'):
    result['24-month'] = (
        messages.Commitment.PlanValueValuesEnum.TWENTY_FOUR_MONTH
    )
  if hasattr(messages.Commitment.PlanValueValuesEnum, 'SIXTY_MONTH'):
    result['60-month'] = messages.Commitment.PlanValueValuesEnum.SIXTY_MONTH
  return result


def TranslatePlanArg(messages, plan_arg):
  return _GetFlagToPlanMap(messages)[plan_arg]


def TranslateAutoRenewArgForCreate(args):
  if args.IsSpecified('auto_renew'):
    return args.auto_renew
  return False


def TranslateAutoRenewArgForUpdate(args):
  if args.IsSpecified('auto_renew'):
    return args.auto_renew
  return None


def TranslateCustomEndTimeArg(args):
  """Translates the custom end time arg to a RFC3339 format."""
  if args.IsSpecified('custom_end_time'):
    # make sure if along with the RFC3339 format.
    try_date_only_parse = False
    final_date_time = None
    try:
      datetime.datetime.strptime(args.custom_end_time, RFC3339_FORMAT)
      final_date_time = args.custom_end_time
    except ValueError:
      # Swallow the exception and try to parse the date string in the format of
      # YYYY-MM-DD
      try_date_only_parse = True

    if try_date_only_parse:
      try:
        # try to parse the date string in the format of YYYY-MM-DD
        tz = pytz.timezone('America/Los_Angeles')
        midnight_date_time_mtv = tz.localize(
            datetime.datetime.strptime(args.custom_end_time, DATE_ONLY_FORMAT)
        )
        final_date_time = midnight_date_time_mtv.astimezone(pytz.utc).strftime(
            RFC3339_FORMAT
        )
      except ValueError:
        # Swallow the exception and throw canonical one.
        pass

    if not final_date_time:
      raise ValueError(
          'Invalid custom end time. Expected format: YYYY-MM-DD or'
          ' YYYY-MM-DDTHH:MM:SSZ'
      )

    return final_date_time

  return None


def TranslateResourcesArg(messages, resources_arg):
  return [
      messages.ResourceCommitment(
          amount=resources_arg['vcpu'],
          type=messages.ResourceCommitment.TypeValueValuesEnum.VCPU,
      ),
      # Arg is in B API accepts values in MB.
      messages.ResourceCommitment(
          amount=resources_arg['memory'] // (1024 * 1024),
          type=messages.ResourceCommitment.TypeValueValuesEnum.MEMORY,
      ),
  ]


def TranslateResourcesArgGroup(messages, args):
  """Util functions to parse ResourceCommitments."""
  resources_arg = args.resources
  resources = TranslateResourcesArg(messages, resources_arg)

  if 'local-ssd' in resources_arg.keys():
    resources.append(
        messages.ResourceCommitment(
            amount=resources_arg['local-ssd'],
            type=messages.ResourceCommitment.TypeValueValuesEnum.LOCAL_SSD,
        )
    )

  if args.IsSpecified('resources_accelerator'):
    accelerator_arg = args.resources_accelerator
    resources.append(
        messages.ResourceCommitment(
            amount=accelerator_arg['count'],
            acceleratorType=accelerator_arg['type'],
            type=messages.ResourceCommitment.TypeValueValuesEnum.ACCELERATOR,
        )
    )

  return resources


def TranslateMergeArg(arg):
  """List arguments are delineated by a comma."""
  return arg.split(',') if arg else []


def MakeCommitmentArg(plural):
  return compute_flags.ResourceArgument(
      resource_name='commitment',
      completer=RegionCommitmentsCompleter,
      plural=plural,
      name='commitment',
      regional_collection='compute.regionCommitments',
      region_explanation=compute_flags.REGION_PROPERTY_EXPLANATION,
  )


def AddCreateFlags(
    parser,
    support_share_setting=False,
    support_stable_fleet=False,
    support_existing_reservation=False,
    support_reservation_sharing_policy=False,
    support_60_month_plan=False,
    support_24_month_plan=False,
):
  """Add general arguments for `commitments create` flag."""
  AddPlanForCreate(parser, support_60_month_plan, support_24_month_plan)
  AddReservationArgGroup(
      parser,
      support_share_setting,
      support_stable_fleet,
      support_existing_reservation,
      support_reservation_sharing_policy,
  )
  AddResourcesArgGroup(parser)
  AddSplitSourceCommitment(parser)
  AddMergeSourceCommitments(parser)
  AddCustomEndTime(parser)


def AddUpdateFlags(
    parser, support_60_month_plan=False, support_24_month_plan=False
):
  """Add general arguments for `commitments update` flag."""
  AddAutoRenew(parser)
  AddPlanForUpdate(parser, support_60_month_plan, support_24_month_plan)


def AddPlanForCreate(parser, support_60_month_plan, support_24_month_plan):
  return parser.add_argument(
      '--plan',
      required=True,
      choices=EXTENDED_VALID_PLANS
      if support_60_month_plan or support_24_month_plan
      else VALID_PLANS,
      help='Duration of the commitment.',
  )


def AddPlanForUpdate(parser, support_60_month_plan, support_24_month_plan):
  return parser.add_argument(
      '--plan',
      required=False,
      choices=EXTENDED_VALID_UPDATE_PLANS
      if support_60_month_plan or support_24_month_plan
      else VALID_UPDATE_PLANS,
      help='Duration of the commitment.',
  )


def AddAutoRenew(parser):
  return parser.add_argument(
      '--auto-renew',
      action='store_true',
      default=False,
      help='Enable auto renewal for the commitment.',
  )


def AddLicenceBasedFlags(parser):
  """Add license based flags for `commitments create` flag."""
  parser.add_argument(
      '--license',
      required=True,
      help=(
          'Applicable license URI. For example: '
          '`https://www.googleapis.com/compute/v1/projects/suse-sap-cloud/global/licenses/sles-sap-12`'
      ),
  )  #  pylint:disable=line-too-long
  parser.add_argument(
      '--cores-per-license',
      required=False,
      type=str,
      help=(
          'Core range of the instance. Must be one of: `1-2`,'
          ' `3-4`, `5+`. Required for SAP licenses.'
      ),
  )
  parser.add_argument(
      '--amount', required=True, type=int, help='Number of licenses purchased.'
  )
  AddPlanForCreate(
      parser, support_60_month_plan=False, support_24_month_plan=False
  )


def AddSplitSourceCommitment(parser):
  return parser.add_argument(
      '--split-source-commitment',
      required=False,
      help=(
          'Creates the new commitment by splitting the specified '
          'source commitment and redistributing the specified resources.'
      ),
  )


def AddMergeSourceCommitments(parser):
  return parser.add_argument(
      '--merge-source-commitments',
      required=False,
      help=(
          'Creates the new commitment by merging the specified '
          'source commitments and combining their resources.'
      ),
  )


def AddCustomEndTime(parser):
  return parser.add_argument(
      '--custom-end-time',
      required=False,
      type=str,
      help=(
          "Specifies a custom future end date and extends the commitment's"
          ' ongoing term.'
      ),
  )


def AddResourcesArgGroup(parser):
  """Add the argument group for ResourceCommitment support in commitment."""
  resources_group = parser.add_group(
      'Manage the commitment for particular resources.', required=True
  )

  resources_help = """\
Resources to be included in the commitment. For details and examples of valid
specifications, refer to the
[custom machine type guide](https://cloud.google.com/compute/docs/instances/creating-instance-with-custom-machine-type#specifications).
*memory*::: The size of the memory, should include units (e.g. 3072MB or 9GB). If no units are specified, GB is assumed.
*vcpu*::: The number of the vCPU cores.
*local-ssd*::: The size of local SSD.
"""

  resources_group.add_argument(
      '--resources',
      help=resources_help,
      type=arg_parsers.ArgDict(
          spec={
              'vcpu': int,
              'local-ssd': int,
              'memory': arg_parsers.BinarySize(),
          }
      ),
  )
  accelerator_help = """\
Manage the configuration of the type and number of accelerator cards to include in the commitment.
*count*::: The number of accelerators to include.
*type*::: The specific type (e.g. nvidia-tesla-k80 for NVIDIA Tesla K80) of the accelerator. Use `gcloud compute accelerator-types list` to learn about all available accelerator types.
"""
  resources_group.add_argument(
      '--resources-accelerator',
      help=accelerator_help,
      type=arg_parsers.ArgDict(spec={'count': int, 'type': str}),
  )


def GetTypeMapperFlag(messages):
  """Helper to get a choice flag from the commitment type enum."""
  return arg_utils.ChoiceEnumMapper(
      '--type',
      messages.Commitment.TypeValueValuesEnum,
      help_str=(
          'Type of commitment. `memory-optimized` indicates that the '
          'commitment is for memory-optimized VMs.'
      ),
      default='general-purpose',
      include_filter=lambda x: x != 'TYPE_UNSPECIFIED',
  )


def AddUpdateReservationGroup(parser):
  """Add reservation arguments to the update-reservations command."""
  parent_reservations_group = parser.add_group(
      'Manage reservations that are attached to the commitment.', mutex=True
  )
  AddReservationsFromFileFlag(
      parent_reservations_group,
      custom_text="Path to a YAML file of two reservations' configuration.",
  )
  reservations_group = parent_reservations_group.add_group(
      'Specify source and destination reservations configuration.'
  )
  AddReservationArguments(reservations_group)
  reservation_flags.GetAcceleratorFlag('--source-accelerator').AddToParser(
      reservations_group
  )
  reservation_flags.GetAcceleratorFlag('--dest-accelerator').AddToParser(
      reservations_group
  )
  reservation_flags.GetLocalSsdFlag('--source-local-ssd').AddToParser(
      reservations_group
  )
  reservation_flags.GetLocalSsdFlag('--dest-local-ssd').AddToParser(
      reservations_group
  )

  # Add share-setting and share-with flags.
  reservation_flags.GetSharedSettingFlag('--source-share-setting').AddToParser(
      reservations_group
  )
  reservation_flags.GetShareWithFlag('--source-share-with').AddToParser(
      reservations_group
  )
  reservation_flags.GetSharedSettingFlag('--dest-share-setting').AddToParser(
      reservations_group
  )
  reservation_flags.GetShareWithFlag('--dest-share-with').AddToParser(
      reservations_group
  )
  return parser


def AddReservationArguments(parser):
  """Add --source-reservation and --dest-reservation arguments to parser."""
  help_text = """
{0} reservation configuration.
*reservation*::: Name of the {0} reservation to operate on.
*reservation-zone*:::  Zone of the {0} reservation to operate on.
*vm-count*::: The number of VM instances that are allocated to this reservation.
The value of this field must be an int in the range [1, 1000].
*machine-type*:::  The type of machine (name only) which has a fixed number of
vCPUs and a fixed amount of memory. This also includes specifying custom machine
type following `custom-number_of_CPUs-amount_of_memory` pattern, e.g. `custom-32-29440`.
*min-cpu-platform*::: Optional minimum CPU platform of the reservation to create.
*require-specific-reservation*::: Indicates whether the reservation can be consumed by VMs with "any reservation"
defined. If enabled, then only VMs that target this reservation by name using
`--reservation-affinity=specific` can consume from this reservation.
"""
  reservation_spec = {
      'reservation': str,
      'reservation-zone': str,
      'vm-count': int,
      'machine-type': str,
      'min-cpu-platform': str,
      'require-specific-reservation': bool,
  }

  parser.add_argument(
      '--source-reservation',
      type=arg_parsers.ArgDict(spec=reservation_spec),
      help=help_text.format('source'),
      required=True,
  )
  parser.add_argument(
      '--dest-reservation',
      type=arg_parsers.ArgDict(spec=reservation_spec),
      help=help_text.format('destination'),
      required=True,
  )
  return parser


def AddReservationsFromFileFlag(parser, custom_text=None):
  help_text = (
      custom_text
      if custom_text
      else "Path to a YAML file of multiple reservations' configuration."
  )
  return parser.add_argument(
      '--reservations-from-file',
      type=arg_parsers.FileContents(),
      help=help_text,
  )


def AddExistingReservationFlag(parser):
  """Add --existing-reservation argument to parser."""
  help_text = """
  Details of the existing on-demand reservation or auto-created future
  reservation that you want to attach to your commitment. Specify a new instance
  of this flag for every existing reservation that you want to attach. The
  reservations must be in the same region as the commitment.
  *name*::: The name of the reservation.
  *zone*::: The zone of the reservation.
  For example, to attach an existing reservation named reservation-name in the
  zone reservation-zone, use the following text:
  --existing-reservation=name=reservation-name,zone=reservation-zone
  """
  return parser.add_argument(
      '--existing-reservation',
      type=arg_parsers.ArgDict(
          spec={'name': str, 'zone': str}, required_keys=['name', 'zone']
      ),
      action='append',
      help=help_text,
  )


def ResolveExistingReservationArgs(args, resources):
  """Method to translate existing-reservations args into URLs."""
  resolver = compute_flags.ResourceResolver.FromMap(
      'reservation', {compute_scope.ScopeEnum.ZONE: 'compute.reservations'}
  )
  existing_reservations = getattr(args, 'existing_reservation', None)
  if existing_reservations is None:
    return []
  reservation_urls = []
  for reservation in existing_reservations:
    reservation_ref = resolver.ResolveResources(
        [reservation['name']],
        compute_scope.ScopeEnum.ZONE,
        reservation['zone'],
        resources,
    )[0]
    reservation_urls.append(reservation_ref.SelfLink())
  return reservation_urls


def AddReservationArgGroup(
    parser,
    support_share_setting=False,
    support_stable_fleet=False,
    support_existing_reservations=False,
    support_reservation_sharing_policy=False,
):
  """Adds all flags needed for reservations creation."""
  reservations_manage_group = parser.add_group(
      'Manage the reservations to be created with the commitment.', mutex=True
  )

  AddReservationsFromFileFlag(reservations_manage_group)
  if support_existing_reservations:
    AddExistingReservationFlag(reservations_manage_group)

  single_reservation_group = reservations_manage_group.add_argument_group(
      help='Manage the reservation to be created with the commitment.'
  )
  resource_args.GetReservationResourceArg(positional=False).AddArgument(
      single_reservation_group
  )
  single_reservation_group.add_argument(
      '--reservation-type',
      hidden=True,
      choices=['specific'],
      default='specific',
      help='The type of the reservation to be created.',
  )

  specific_sku_reservation_group = single_reservation_group.add_argument_group(
      help='Manage the specific SKU reservation properties to create.'
  )
  AddFlagsToSpecificSkuGroup(
      specific_sku_reservation_group, support_stable_fleet
  )

  if support_share_setting:
    share_setting_reservation_group = (
        single_reservation_group.add_argument_group(
            help='Manage the properties of a shared reservation to create',
            required=False,
        )
    )
    AddFlagsToShareSettingGroup(share_setting_reservation_group)

  if support_reservation_sharing_policy:
    reservation_sharing_policy_group = single_reservation_group.add_argument_group(
        help='Manage the properties of a reservation sharing policy to create',
        required=False,
    )
    AddFlagsToReservationSharingPolicyGroup(reservation_sharing_policy_group)


def AddFlagsToReservationSharingPolicyGroup(group):
  """Adds flags needed for a reservation sharing policy."""
  args = [
      reservation_flags.GetReservationSharingPolicyFlag(),
  ]
  for arg in args:
    arg.AddToParser(group)


def AddFlagsToSpecificSkuGroup(group, support_stable_fleet=False):
  """Adds flags needed for a specific sku zonal allocation."""
  args = [
      reservation_flags.GetRequireSpecificAllocation(),
      reservation_flags.GetVmCountFlag(required=False),
      reservation_flags.GetMinCpuPlatform(),
      reservation_flags.GetMachineType(required=False),
      reservation_flags.GetLocalSsdFlag(),
      reservation_flags.GetAcceleratorFlag(),
      reservation_flags.GetResourcePolicyFlag(),
  ]

  if support_stable_fleet:
    args.append(instance_flags.AddMaintenanceInterval())

  for arg in args:
    arg.AddToParser(group)


def AddFlagsToShareSettingGroup(group):
  """Adds flags needed for an allocation with share setting."""
  args = [
      reservation_flags.GetSharedSettingFlag(),
      reservation_flags.GetShareWithFlag(),
  ]

  for arg in args:
    arg.AddToParser(group)