diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_with_regional_disk.py b/compute/client_library/ingredients/instances/create_start_instance/create_with_regional_disk.py
new file mode 100644
index 000000000000..800d1297981b
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_with_regional_disk.py
@@ -0,0 +1,66 @@
+# Copyright 2024 Google LLC
+#
+# 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.
+
+# This is an ingredient file. It is not meant to be run directly. Check the samples/snippets
+# folder for complete code samples that are ready to be used.
+# Disabling flake8 for the ingredients file, as it would fail F821 - undefined name check.
+# flake8: noqa
+
+from google.cloud import compute_v1
+
+
+#
+def create_with_regional_boot_disk(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ source_snapshot: str,
+ disk_region: str,
+ disk_type: str = "pd-balanced",
+) -> compute_v1.Instance:
+ """
+ Creates a new instance with a regional boot disk
+ Args:
+ project_id (str): The ID of the Google Cloud project.
+ zone (str): The zone where the instance will be created.
+ instance_name (str): The name of the instance.
+ source_snapshot (str): The name of snapshot to create the boot disk from.
+ disk_region (str): The region where the disk replicas will be located.
+ disk_type (str): The type of the disk. Default is 'pd-balanced'.
+ Returns:
+ Instance object.
+ """
+
+ disk = compute_v1.AttachedDisk()
+
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_snapshot = f"global/snapshots/{source_snapshot}"
+ initialize_params.disk_type = (
+ f"projects/{project_id}/zones/{zone}/diskTypes/{disk_type}"
+ )
+ initialize_params.replica_zones = [
+ f"projects/{project_id}/zones/{disk_region}-a",
+ f"projects/{project_id}/zones/{disk_region}-b",
+ ]
+
+ disk.initialize_params = initialize_params
+ disk.boot = True
+ disk.auto_delete = True
+
+ instance = create_instance(project_id, zone, instance_name, [disk])
+
+ return instance
+
+
+#
diff --git a/compute/client_library/recipes/instances/create_start_instance/create_with_regional_disk.py b/compute/client_library/recipes/instances/create_start_instance/create_with_regional_disk.py
new file mode 100644
index 000000000000..dca97006a938
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_with_regional_disk.py
@@ -0,0 +1,25 @@
+# Copyright 2024 Google LLC
+#
+# 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.
+# flake8: noqa
+
+#
+#
+
+#
+
+#
+
+#
+
+#
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_with_regional_disk.py b/compute/client_library/snippets/instances/create_start_instance/create_with_regional_disk.py
new file mode 100644
index 000000000000..3fd8dfb19de7
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_with_regional_disk.py
@@ -0,0 +1,259 @@
+# Copyright 2024 Google LLC
+#
+# 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.
+# flake8: noqa
+
+
+# This file is automatically generated. Please do not modify it directly.
+# Find the relevant recipe file in the samples/recipes or samples/ingredients
+# directory and apply your changes there.
+
+
+# [START compute_instance_create_replicated_boot_disk]
+from __future__ import annotations
+
+import re
+import sys
+from typing import Any
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ Waits for the extended (long-running) operation to complete.
+
+ If the operation is successful, it will return its result.
+ If the operation ends with an error, an exception will be raised.
+ If there were any warnings during the execution of the operation
+ they will be printed to sys.stderr.
+
+ Args:
+ operation: a long-running operation you want to wait on.
+ verbose_name: (optional) a more verbose name of the operation,
+ used only during error and warning reporting.
+ timeout: how long (in seconds) to wait for operation to finish.
+ If None, wait indefinitely.
+
+ Returns:
+ Whatever the operation.result() returns.
+
+ Raises:
+ This method will raise the exception received from `operation.exception()`
+ or RuntimeError if there is no exception set, but there is an `error_code`
+ set for the `operation`.
+
+ In case of an operation taking longer than `timeout` seconds to complete,
+ a `concurrent.futures.TimeoutError` will be raised.
+ """
+ result = operation.result(timeout=timeout)
+
+ if operation.error_code:
+ print(
+ f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
+ file=sys.stderr,
+ flush=True,
+ )
+ print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
+ raise operation.exception() or RuntimeError(operation.error_message)
+
+ if operation.warnings:
+ print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
+ for warning in operation.warnings:
+ print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)
+
+ return result
+
+
+def create_instance(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ disks: list[compute_v1.AttachedDisk],
+ machine_type: str = "n1-standard-1",
+ network_link: str = "global/networks/default",
+ subnetwork_link: str = None,
+ internal_ip: str = None,
+ external_access: bool = False,
+ external_ipv4: str = None,
+ accelerators: list[compute_v1.AcceleratorConfig] = None,
+ preemptible: bool = False,
+ spot: bool = False,
+ instance_termination_action: str = "STOP",
+ custom_hostname: str = None,
+ delete_protection: bool = False,
+) -> compute_v1.Instance:
+ """
+ Send an instance creation request to the Compute Engine API and wait for it to complete.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone to create the instance in. For example: "us-west3-b"
+ instance_name: name of the new virtual machine (VM) instance.
+ disks: a list of compute_v1.AttachedDisk objects describing the disks
+ you want to attach to your new instance.
+ machine_type: machine type of the VM being created. This value uses the
+ following format: "zones/{zone}/machineTypes/{type_name}".
+ For example: "zones/europe-west3-c/machineTypes/f1-micro"
+ network_link: name of the network you want the new instance to use.
+ For example: "global/networks/default" represents the network
+ named "default", which is created automatically for each project.
+ subnetwork_link: name of the subnetwork you want the new instance to use.
+ This value uses the following format:
+ "regions/{region}/subnetworks/{subnetwork_name}"
+ internal_ip: internal IP address you want to assign to the new instance.
+ By default, a free address from the pool of available internal IP addresses of
+ used subnet will be used.
+ external_access: boolean flag indicating if the instance should have an external IPv4
+ address assigned.
+ external_ipv4: external IPv4 address to be assigned to this instance. If you specify
+ an external IP address, it must live in the same region as the zone of the instance.
+ This setting requires `external_access` to be set to True to work.
+ accelerators: a list of AcceleratorConfig objects describing the accelerators that will
+ be attached to the new instance.
+ preemptible: boolean value indicating if the new instance should be preemptible
+ or not. Preemptible VMs have been deprecated and you should now use Spot VMs.
+ spot: boolean value indicating if the new instance should be a Spot VM or not.
+ instance_termination_action: What action should be taken once a Spot VM is terminated.
+ Possible values: "STOP", "DELETE"
+ custom_hostname: Custom hostname of the new VM instance.
+ Custom hostnames must conform to RFC 1035 requirements for valid hostnames.
+ delete_protection: boolean value indicating if the new virtual machine should be
+ protected against deletion or not.
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ # Use the network interface provided in the network_link argument.
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.network = network_link
+ if subnetwork_link:
+ network_interface.subnetwork = subnetwork_link
+
+ if internal_ip:
+ network_interface.network_i_p = internal_ip
+
+ if external_access:
+ access = compute_v1.AccessConfig()
+ access.type_ = compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT.name
+ access.name = "External NAT"
+ access.network_tier = access.NetworkTier.PREMIUM.name
+ if external_ipv4:
+ access.nat_i_p = external_ipv4
+ network_interface.access_configs = [access]
+
+ # Collect information into the Instance object.
+ instance = compute_v1.Instance()
+ instance.network_interfaces = [network_interface]
+ instance.name = instance_name
+ instance.disks = disks
+ if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
+ instance.machine_type = machine_type
+ else:
+ instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"
+
+ instance.scheduling = compute_v1.Scheduling()
+ if accelerators:
+ instance.guest_accelerators = accelerators
+ instance.scheduling.on_host_maintenance = (
+ compute_v1.Scheduling.OnHostMaintenance.TERMINATE.name
+ )
+
+ if preemptible:
+ # Set the preemptible setting
+ warnings.warn(
+ "Preemptible VMs are being replaced by Spot VMs.", DeprecationWarning
+ )
+ instance.scheduling = compute_v1.Scheduling()
+ instance.scheduling.preemptible = True
+
+ if spot:
+ # Set the Spot VM setting
+ instance.scheduling.provisioning_model = (
+ compute_v1.Scheduling.ProvisioningModel.SPOT.name
+ )
+ instance.scheduling.instance_termination_action = instance_termination_action
+
+ if custom_hostname is not None:
+ # Set the custom hostname for the instance
+ instance.hostname = custom_hostname
+
+ if delete_protection:
+ # Set the delete protection bit
+ instance.deletion_protection = True
+
+ # Prepare the request to insert an instance.
+ request = compute_v1.InsertInstanceRequest()
+ request.zone = zone
+ request.project = project_id
+ request.instance_resource = instance
+
+ # Wait for the create operation to complete.
+ print(f"Creating the {instance_name} instance in {zone}...")
+
+ operation = instance_client.insert(request=request)
+
+ wait_for_extended_operation(operation, "instance creation")
+
+ print(f"Instance {instance_name} created.")
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+
+
+def create_with_regional_boot_disk(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ source_snapshot: str,
+ disk_region: str,
+ disk_type: str = "pd-balanced",
+) -> compute_v1.Instance:
+ """
+ Creates a new instance with a regional boot disk
+ Args:
+ project_id (str): The ID of the Google Cloud project.
+ zone (str): The zone where the instance will be created.
+ instance_name (str): The name of the instance.
+ source_snapshot (str): The name of snapshot to create the boot disk from.
+ disk_region (str): The region where the disk replicas will be located.
+ disk_type (str): The type of the disk. Default is 'pd-balanced'.
+ Returns:
+ Instance object.
+ """
+
+ disk = compute_v1.AttachedDisk()
+
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_snapshot = f"global/snapshots/{source_snapshot}"
+ initialize_params.disk_type = (
+ f"projects/{project_id}/zones/{zone}/diskTypes/{disk_type}"
+ )
+ initialize_params.replica_zones = [
+ f"projects/{project_id}/zones/{disk_region}-a",
+ f"projects/{project_id}/zones/{disk_region}-b",
+ ]
+
+ disk.initialize_params = initialize_params
+ disk.boot = True
+ disk.auto_delete = True
+
+ instance = create_instance(project_id, zone, instance_name, [disk])
+
+ return instance
+
+
+# [END compute_instance_create_replicated_boot_disk]
diff --git a/compute/client_library/snippets/tests/test_create_vm.py b/compute/client_library/snippets/tests/test_create_vm.py
index 6fef8c9be65e..7f2319a506e8 100644
--- a/compute/client_library/snippets/tests/test_create_vm.py
+++ b/compute/client_library/snippets/tests/test_create_vm.py
@@ -17,6 +17,9 @@
from google.cloud import compute_v1
import pytest
+from .test_disks import autodelete_regional_blank_disk # noqa: F401
+from .test_disks import DISK_SIZE
+
from ..disks.create_empty_disk import create_empty_disk
from ..disks.create_from_image import create_disk_from_image
from ..disks.delete import delete_disk
@@ -35,16 +38,23 @@
create_with_existing_disks,
)
from ..instances.create_start_instance.create_with_local_ssd import create_with_ssd
+from ..instances.create_start_instance.create_with_regional_disk import (
+ create_with_regional_boot_disk,
+)
from ..instances.create_start_instance.create_with_snapshotted_data_disk import (
create_with_snapshotted_data_disk,
)
from ..instances.create_with_subnet import create_with_subnet
from ..instances.delete import delete_instance
from ..operations.operation_check import wait_for_operation
+from ..snapshots.create import create_snapshot
+
PROJECT = google.auth.default()[1]
REGION = "us-central1"
+REGION_SECOND = "europe-west2"
INSTANCE_ZONE = "us-central1-b"
+INSTANCE_ZONE_SECOND = "europe-west2-b"
def get_active_debian():
@@ -250,3 +260,26 @@ def test_create_with_ssd():
assert len(instance.disks) == 2
finally:
delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_with_regional_boot_disk(autodelete_regional_blank_disk): # noqa: F811
+ snapshot_name = "test-snap-" + uuid.uuid4().hex[:10]
+ instance_name = "test-vm-" + uuid.uuid4().hex[:10]
+ test_snapshot = create_snapshot(
+ project_id=PROJECT,
+ disk_name=autodelete_regional_blank_disk.name,
+ snapshot_name=snapshot_name,
+ region=REGION_SECOND,
+ )
+ instance = create_with_regional_boot_disk(
+ PROJECT, INSTANCE_ZONE_SECOND, instance_name, test_snapshot.name, REGION_SECOND
+ )
+ # Disk size takes from test_disk.py
+ try:
+ assert any(disk.disk_size_gb == DISK_SIZE for disk in instance.disks)
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE_SECOND, instance_name)
+ op = compute_v1.SnapshotsClient().delete_unary(
+ project=PROJECT, snapshot=snapshot_name
+ )
+ wait_for_operation(op, PROJECT)