diff --git a/compute/client_library/ingredients/disks/replication_disk_start.py b/compute/client_library/ingredients/disks/replication_disk_start.py
new file mode 100644
index 00000000000..69188dc2c76
--- /dev/null
+++ b/compute/client_library/ingredients/disks/replication_disk_start.py
@@ -0,0 +1,69 @@
+# 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 start_disk_replication(
+ project_id: str,
+ primary_disk_location: str,
+ primary_disk_name: str,
+ secondary_disk_location: str,
+ secondary_disk_name: str,
+) -> bool:
+ """Starts the asynchronous replication of a primary disk to a secondary disk.
+ Args:
+ project_id (str): The ID of the Google Cloud project.
+ primary_disk_location (str): The location of the primary disk, either a zone or a region.
+ primary_disk_name (str): The name of the primary disk.
+ secondary_disk_location (str): The location of the secondary disk, either a zone or a region.
+ secondary_disk_name (str): The name of the secondary disk.
+ Returns:
+ bool: True if the replication was successfully started.
+ """
+ # Check if the primary disk location is a region or a zone.
+ if primary_disk_location[-1].isdigit():
+ region_client = compute_v1.RegionDisksClient()
+ request_resource = compute_v1.RegionDisksStartAsyncReplicationRequest(
+ async_secondary_disk=f"projects/{project_id}/regions/{secondary_disk_location}/disks/{secondary_disk_name}"
+ )
+ operation = region_client.start_async_replication(
+ project=project_id,
+ region=primary_disk_location,
+ disk=primary_disk_name,
+ region_disks_start_async_replication_request_resource=request_resource,
+ )
+ else:
+ client = compute_v1.DisksClient()
+ request_resource = compute_v1.DisksStartAsyncReplicationRequest(
+ async_secondary_disk=f"zones/{secondary_disk_location}/disks/{secondary_disk_name}"
+ )
+ operation = client.start_async_replication(
+ project=project_id,
+ zone=primary_disk_location,
+ disk=primary_disk_name,
+ disks_start_async_replication_request_resource=request_resource,
+ )
+ wait_for_extended_operation(operation, verbose_name="replication operation")
+ print(f"Replication for disk {primary_disk_name} started.")
+ return True
+
+
+#
diff --git a/compute/client_library/ingredients/disks/replication_disk_stop.py b/compute/client_library/ingredients/disks/replication_disk_stop.py
new file mode 100644
index 00000000000..678370b2c16
--- /dev/null
+++ b/compute/client_library/ingredients/disks/replication_disk_stop.py
@@ -0,0 +1,53 @@
+# 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 stop_disk_replication(
+ project_id: str, primary_disk_location: str, primary_disk_name: str
+) -> bool:
+ """
+ Stops the asynchronous replication of a disk.
+ Args:
+ project_id (str): The ID of the Google Cloud project.
+ primary_disk_location (str): The location of the primary disk, either a zone or a region.
+ primary_disk_name (str): The name of the primary disk.
+ Returns:
+ bool: True if the replication was successfully stopped.
+ """
+ # Check if the primary disk is in a region or a zone
+ if primary_disk_location[-1].isdigit():
+ region_client = compute_v1.RegionDisksClient()
+ operation = region_client.stop_async_replication(
+ project=project_id, region=primary_disk_location, disk=primary_disk_name
+ )
+ else:
+ zone_client = compute_v1.DisksClient()
+ operation = zone_client.stop_async_replication(
+ project=project_id, zone=primary_disk_location, disk=primary_disk_name
+ )
+
+ wait_for_extended_operation(operation, verbose_name="replication operation")
+ print(f"Replication for disk {primary_disk_name} stopped.")
+ return True
+
+
+#
diff --git a/compute/client_library/recipes/disks/replication_disk_start.py b/compute/client_library/recipes/disks/replication_disk_start.py
new file mode 100644
index 00000000000..f729bcec55f
--- /dev/null
+++ b/compute/client_library/recipes/disks/replication_disk_start.py
@@ -0,0 +1,23 @@
+# 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/recipes/disks/replication_disk_stop.py b/compute/client_library/recipes/disks/replication_disk_stop.py
new file mode 100644
index 00000000000..b13e1a371eb
--- /dev/null
+++ b/compute/client_library/recipes/disks/replication_disk_stop.py
@@ -0,0 +1,23 @@
+# 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/disks/replication_disk_start.py b/compute/client_library/snippets/disks/replication_disk_start.py
new file mode 100644
index 00000000000..3a3f988b8dc
--- /dev/null
+++ b/compute/client_library/snippets/disks/replication_disk_start.py
@@ -0,0 +1,125 @@
+# 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_disk_start_replication]
+from __future__ import annotations
+
+import sys
+from typing import Any
+
+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 start_disk_replication(
+ project_id: str,
+ primary_disk_location: str,
+ primary_disk_name: str,
+ secondary_disk_location: str,
+ secondary_disk_name: str,
+) -> bool:
+ """Starts the asynchronous replication of a primary disk to a secondary disk.
+ Args:
+ project_id (str): The ID of the Google Cloud project.
+ primary_disk_location (str): The location of the primary disk, either a zone or a region.
+ primary_disk_name (str): The name of the primary disk.
+ secondary_disk_location (str): The location of the secondary disk, either a zone or a region.
+ secondary_disk_name (str): The name of the secondary disk.
+ Returns:
+ bool: True if the replication was successfully started.
+ """
+ # Check if the primary disk location is a region or a zone.
+ if primary_disk_location[-1].isdigit():
+ region_client = compute_v1.RegionDisksClient()
+ request_resource = compute_v1.RegionDisksStartAsyncReplicationRequest(
+ async_secondary_disk=f"projects/{project_id}/regions/{secondary_disk_location}/disks/{secondary_disk_name}"
+ )
+ operation = region_client.start_async_replication(
+ project=project_id,
+ region=primary_disk_location,
+ disk=primary_disk_name,
+ region_disks_start_async_replication_request_resource=request_resource,
+ )
+ else:
+ client = compute_v1.DisksClient()
+ request_resource = compute_v1.DisksStartAsyncReplicationRequest(
+ async_secondary_disk=f"zones/{secondary_disk_location}/disks/{secondary_disk_name}"
+ )
+ operation = client.start_async_replication(
+ project=project_id,
+ zone=primary_disk_location,
+ disk=primary_disk_name,
+ disks_start_async_replication_request_resource=request_resource,
+ )
+ wait_for_extended_operation(operation, verbose_name="replication operation")
+ print(f"Replication for disk {primary_disk_name} started.")
+ return True
+
+
+# [END compute_disk_start_replication]
diff --git a/compute/client_library/snippets/disks/replication_disk_stop.py b/compute/client_library/snippets/disks/replication_disk_stop.py
new file mode 100644
index 00000000000..b2d89e4daed
--- /dev/null
+++ b/compute/client_library/snippets/disks/replication_disk_stop.py
@@ -0,0 +1,109 @@
+# 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_disk_stop_replication]
+from __future__ import annotations
+
+import sys
+from typing import Any
+
+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 stop_disk_replication(
+ project_id: str, primary_disk_location: str, primary_disk_name: str
+) -> bool:
+ """
+ Stops the asynchronous replication of a disk.
+ Args:
+ project_id (str): The ID of the Google Cloud project.
+ primary_disk_location (str): The location of the primary disk, either a zone or a region.
+ primary_disk_name (str): The name of the primary disk.
+ Returns:
+ bool: True if the replication was successfully stopped.
+ """
+ # Check if the primary disk is in a region or a zone
+ if primary_disk_location[-1].isdigit():
+ region_client = compute_v1.RegionDisksClient()
+ operation = region_client.stop_async_replication(
+ project=project_id, region=primary_disk_location, disk=primary_disk_name
+ )
+ else:
+ zone_client = compute_v1.DisksClient()
+ operation = zone_client.stop_async_replication(
+ project=project_id, zone=primary_disk_location, disk=primary_disk_name
+ )
+
+ wait_for_extended_operation(operation, verbose_name="replication operation")
+ print(f"Replication for disk {primary_disk_name} stopped.")
+ return True
+
+
+# [END compute_disk_stop_replication]
diff --git a/compute/client_library/snippets/tests/test_disks.py b/compute/client_library/snippets/tests/test_disks.py
index 11a639c0818..1e2b20f9eb0 100644
--- a/compute/client_library/snippets/tests/test_disks.py
+++ b/compute/client_library/snippets/tests/test_disks.py
@@ -35,6 +35,8 @@
from ..disks.list import list_disks
from ..disks.regional_create_from_source import create_regional_disk
from ..disks.regional_delete import delete_regional_disk
+from ..disks.replication_disk_start import start_disk_replication
+from ..disks.replication_disk_stop import stop_disk_replication
from ..disks.resize_disk import resize_disk
from ..images.get import get_image_from_family
from ..instances.create import create_instance, disk_from_image
@@ -443,3 +445,57 @@ def test_create_custom_secondary_disk(
)
assert disk.labels["secondary-disk-for-replication"] == "true"
assert disk.labels["source-disk"] == test_empty_pd_balanced_disk.name
+
+
+def test_start_stop_region_replication(
+ autodelete_regional_blank_disk, autodelete_regional_disk_name
+):
+ create_secondary_region_disk(
+ autodelete_regional_blank_disk.name,
+ PROJECT,
+ REGION,
+ autodelete_regional_disk_name,
+ PROJECT,
+ REGION_SECONDARY,
+ DISK_SIZE,
+ )
+ assert start_disk_replication(
+ project_id=PROJECT,
+ primary_disk_location=REGION,
+ primary_disk_name=autodelete_regional_blank_disk.name,
+ secondary_disk_location=REGION_SECONDARY,
+ secondary_disk_name=autodelete_regional_disk_name,
+ )
+ assert stop_disk_replication(
+ project_id=PROJECT,
+ primary_disk_location=REGION,
+ primary_disk_name=autodelete_regional_blank_disk.name,
+ )
+ # Wait for the replication to stop
+ time.sleep(20)
+
+
+def test_start_stop_zone_replication(test_empty_pd_balanced_disk, autodelete_disk_name):
+ create_secondary_disk(
+ test_empty_pd_balanced_disk.name,
+ PROJECT,
+ ZONE_SECONDARY,
+ autodelete_disk_name,
+ PROJECT,
+ ZONE,
+ DISK_SIZE,
+ )
+ assert start_disk_replication(
+ project_id=PROJECT,
+ primary_disk_location=ZONE_SECONDARY,
+ primary_disk_name=test_empty_pd_balanced_disk.name,
+ secondary_disk_location=ZONE,
+ secondary_disk_name=autodelete_disk_name,
+ )
+ assert stop_disk_replication(
+ project_id=PROJECT,
+ primary_disk_location=ZONE_SECONDARY,
+ primary_disk_name=test_empty_pd_balanced_disk.name,
+ )
+ # Wait for the replication to stop
+ time.sleep(20)