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/current/lib/surface/compute/ha_controllers/create.py
# -*- coding: utf-8 -*- #
# Copyright 2025 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 to create an HA Controller."""

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

import textwrap

from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.ha_controllers import utils as api_utils
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.ha_controllers import utils
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.core import exceptions as core_exceptions
from googlecloudsdk.core import log


_NODE_AFFINITY_FILE_HELP_TEXT = textwrap.dedent("""\
    The JSON/YAML file containing the configuration of desired nodes onto
    which instance in this zone could be scheduled. These rules filter the nodes
    according to their node affinity labels. A node's affinity labels come
    from the node template of the group the node is in.

    The file should contain a list of a JSON/YAML objects. For an example,
    see https://cloud.google.com/compute/docs/nodes/provisioning-sole-tenant-vms#configure_node_affinity_labels.
    The following list describes the fields:

    *key*::: Corresponds to the node affinity label keys of
    the Node resource.
    *operator*::: Specifies the node selection type. Must be one of:
      `IN`: Requires Compute Engine to seek for matched nodes.
      `NOT_IN`: Requires Compute Engine to avoid certain nodes.
    *values*::: Optional. A list of values which correspond to the node
    affinity label values of the Node resource.
    """)


@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Create(base.CreateCommand):
  """Create an HA Controller.

  Create an High Availability (HA) Controller, which helps
  ensure that a virtual machine (VM) instance remains operational by
  automatically managing failover across two zones.
  """

  @staticmethod
  def Args(parser):
    base.ASYNC_FLAG.AddToParser(parser)
    utils.AddHaControllerNameArgToParser(
        parser, base.ReleaseTrack.ALPHA.name.lower()
    )
    messages = utils.GetMessagesModule('alpha')
    parser.add_argument(
        '--description',
        help=(
            'An optional, user-provided description for the HA Controller to'
            ' help identify its purpose.'
        ),
    )
    parser.add_argument(
        '--instance-name',
        help=(
            'The name of the existing VM that the HA Controller manages. This'
            ' VM must already exist in one of the zones specified in'
            ' --zone-configuration.'
        ),
    )
    parser.add_argument(
        '--failover-initiation',
        required=True,
        type=lambda x: arg_utils.ChoiceToEnum(
            x,
            messages.HaController.FailoverInitiationValueValuesEnum,
        ),
        help=(
            'Specifies how a failover is triggered. Set to MANUAL_ONLY if you'
            ' want to trigger failovers yourself. Must be one of:'
            f' {utils.EnumTypeToChoices(messages.HaController.FailoverInitiationValueValuesEnum)}'
        ),
    )
    parser.add_argument(
        '--secondary-zone-capacity',
        required=True,
        type=lambda x: arg_utils.ChoiceToEnum(
            x,
            messages.HaController.SecondaryZoneCapacityValueValuesEnum,
        ),
        help=(
            'Determines the capacity guarantee in the secondary zone. Use'
            ' BEST_EFFORT to create a VM based on capacity availability at the'
            ' time of failover, suitable for workloads that can tolerate longer'
            ' recovery times. Must be one of:'
            f' {utils.EnumTypeToChoices(messages.HaController.SecondaryZoneCapacityValueValuesEnum)}'
        ),
    )
    parser.add_argument(
        '--zone-configuration',
        required=True,
        type=arg_parsers.ArgObject(
            enable_file_upload=False,
            spec={
                'reservation': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies the reservation name. The reservation must'
                        ' exist within the HA Controller region.'
                    ),
                ),
                'reservation-affinity': arg_parsers.ArgObject(
                    value_type=lambda x: arg_utils.ChoiceToEnum(
                        x,
                        messages.HaControllerZoneConfigurationReservationAffinity.ConsumeReservationTypeValueValuesEnum,
                    ),
                    help_text=(
                        'Specifies the reservation-affinity value.'
                        ' Must be one of:'
                        f' {utils.EnumTypeToChoices(messages.HaControllerZoneConfigurationReservationAffinity.ConsumeReservationTypeValueValuesEnum)}'
                    ),
                ),
                'node': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies the node name. The node must exist within'
                        ' the HA Controller region.'
                    ),
                ),
                'node-group': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies the node-group name. The node-group must'
                        ' exist within the HA Controller region. Must be one'
                        ' of:'
                        f' {utils.EnumTypeToChoices(messages.HaControllerZoneConfigurationNodeAffinity.OperatorValueValuesEnum)}'
                    ),
                ),
                'node-affinity-file': arg_parsers.ArgObject(
                    value_type=arg_parsers.FileContents(),
                    enable_file_upload=False,
                    help_text=_NODE_AFFINITY_FILE_HELP_TEXT,
                ),
                'node-project': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies the name of the project with shared sole'
                        ' tenant node groups to create an instance in.'
                    ),
                ),
                'zone': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies the zone. The zone must be within the HA'
                        ' Controller region.'
                    ),
                ),
            },
        ),
        action=arg_parsers.FlattenAction(),
        help=(
            'Configures the two zones for the HA Controller and specifies how'
            ' VM capacity is reserved in each zone. You must provide two zone'
            ' configurations. You can also specify an existing reservation or'
            ' node-group to guarantee capacity.'
        ),
    )
    parser.add_argument(
        '--network-auto-configuration',
        required=False,
        type=arg_parsers.ArgObject(
            spec={
                'stack-type': arg_parsers.ArgObject(
                    value_type=lambda x: arg_utils.ChoiceToEnum(
                        x,
                        messages.HaControllerNetworkingAutoConfigurationInternal.StackTypeValueValuesEnum),
                    help_text=(
                        'Specifies the stack type for the network'
                        ' configuration. Must match the stack type of the'
                        ' instance. Must be one of:'
                        f' {utils.EnumTypeToChoices(messages.HaControllerNetworkingAutoConfigurationInternal.StackTypeValueValuesEnum)}'
                    ),
                ),
                'address': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies an optional IPv4 address to assign to'
                        ' the instance. If not specified, an ephemeral IP will'
                        ' be generated.'
                    ),
                ),
                'internal-ipv6-address': arg_parsers.ArgObject(
                    value_type=str,
                    help_text=(
                        'Specifies an optional IPv6 address to assign to'
                        ' the instance. If not specified, an ephemeral IP will'
                        ' be generated.'
                    ),
                ),
            },
        ),
        action=arg_parsers.FlattenAction(),
        help=(
            'Adds a network interface to the instance.'
        ),
    )

  def Run(self, args):
    holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
    client = holder.client
    ha_controller_ref = args.CONCEPTS.ha_controller.Parse()
    ha_controller = client.messages.HaController(
        name=ha_controller_ref.Name(),
        region=ha_controller_ref.region,
        description=args.description,
        instanceName=args.instance_name,
        failoverInitiation=args.failover_initiation,
        secondaryZoneCapacity=args.secondary_zone_capacity,
        zoneConfigurations=utils.MakeZoneConfiguration(args.zone_configuration),
        networkingAutoConfiguration=utils.MakeNetworkConfiguration(
            args.network_auto_configuration
        ),
    )
    if not args.async_:
      return api_utils.Insert(ha_controller, ha_controller_ref, holder)

    errors_to_collect = []
    response = api_utils.InsertAsync(
        client, ha_controller, ha_controller_ref, errors_to_collect
    )
    err = getattr(response, 'error', None)
    if err:
      errors_to_collect.append(poller.OperationErrors(err.errors))
    if errors_to_collect:
      raise core_exceptions.MultiError(errors_to_collect)

    operation_ref = holder.resources.Parse(response.selfLink)

    log.status.Print(
        'HA Controller creation in progress for [{}]: {}'.format(
            ha_controller.name, operation_ref.SelfLink()
        )
    )
    log.status.Print(
        'Use [gcloud compute operations describe URI] command '
        'to check the status of the operation.'
    )

    return response