diff --git a/compute/client_library/README.md b/compute/client_library/README.md
new file mode 100644
index 00000000000..cc715bec0c3
--- /dev/null
+++ b/compute/client_library/README.md
@@ -0,0 +1,53 @@
+# Code samples for the Compute Engine library
+
+In this folder you can find the source code for the code samples used throughout the
+[public documentation](https://cloud.google.com/compute/docs/) of Google Compute Engine.
+
+The samples can be found in the `snippets` folder, where they are organized to mimic the
+structure of the public documentation. Files that are saved there are generated by the `sgs.py`
+script from pieces found in `ingredients` and `recipes`. This way, one piece of code can be easily
+included in multiple snippets and updating the code requires less work.
+
+## Working with the SGS
+
+SGS (Snippet Generating System) works by scanning the `recipes` folder, finding all files
+and filling them with pieces of code found in `ingredients`. The folder structure of `recipes` is
+reconstructed in the `snippets` folder.
+
+### Adding new sample
+
+To create a new sample, just prepare a new file in one of the `recipes` subfolders. The SGS will pick it up
+automatically when you run it, by executing `python3 sgs.py generate` in this (`samples/`) directory.
+
+### Removing/moving a sample
+
+To remove or move a sample, you need to simply modify the `recipes` folder to match your desired structure, then delete
+the generated snippet from the `snippets` directory. The SGS script will create the snippet in the new location next
+time you run `python3 sgs.py generate`.
+
+### Interacting with GIT
+
+SGS will not interact with Git repository in any way. All changes made by the script need to be committed manually -
+preferably in the same commit as the update to the source files.
+
+## Preparing an ingredient
+To add a new ingredient, create a new `.py` file with the code you want to later use in the snippets. Mark the beginning
+of the code you want to include with `# ` and the end with `# `.
+
+Please leave the imports required by this ingredient **OUTSIDE** the area marked with ingredient comments. The SGS
+script will automatically collect all the required imports and put them in the final snippet in the right place and in
+right order.
+
+## Preparing a recipe
+Each recipe is a file located in the `recipes` folder. It should have the `.py` extension and should be a valid Python
+script. Each recipe has to have an `# ` line and at least one `# ` line.
+Apart from those restrictions, the contents of the file can be whatever you want.
+
+The SGS will copy the recipe file to the destination folder in `snippets` and replace the `# ` and
+`# ` lines with the `import` statements required by the used ingredients and with the ingredient
+body.
+
+### Regions
+You should use `# ` and `# ` lines to indicate where start and end
+of a region should be placed in the generated snippet. Those lines will be simply replaced with the proper
+`START region_name` and `END region_name` lines.
diff --git a/compute/client_library/__init__.py b/compute/client_library/__init__.py
new file mode 100644
index 00000000000..4bbe0ffdb06
--- /dev/null
+++ b/compute/client_library/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 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.
diff --git a/compute/client_library/ingredients/__init__.py b/compute/client_library/ingredients/__init__.py
new file mode 100644
index 00000000000..81d8b9be3da
--- /dev/null
+++ b/compute/client_library/ingredients/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2022 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
diff --git a/compute/client_library/ingredients/disks/autodelete_change.py b/compute/client_library/ingredients/disks/autodelete_change.py
new file mode 100644
index 00000000000..4238c30949c
--- /dev/null
+++ b/compute/client_library/ingredients/disks/autodelete_change.py
@@ -0,0 +1,53 @@
+# Copyright 2022 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
+import sys
+from typing import NoReturn
+
+
+from google.cloud import compute_v1
+
+
+#
+def set_disk_autodelete(project_id: str, zone: str, instance_name: str, disk_name: str, autodelete: bool) -> NoReturn:
+ """
+ Set the autodelete flag of a disk to given value.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which is the disk you want to modify.
+ instance_name: name of the instance the disk is attached to.
+ disk_name: the name of the disk which flag you want to modify.
+ autodelete: the new value of the autodelete flag.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(project=project_id, zone=zone, instance=instance_name)
+
+ for disk in instance.disks:
+ if disk.device_name == disk_name:
+ break
+ else:
+ raise RuntimeError(f"Instance {instance_name} doesn't have a disk named {disk_name} attached.")
+
+ disk.auto_delete = autodelete
+
+ operation = instance_client.update(project=project_id, zone=zone, instance=instance_name, instance_resource=instance)
+
+ wait_for_extended_operation(operation, "disk update")
+ return
+#
diff --git a/compute/client_library/ingredients/disks/clone_encrypted_disk.py b/compute/client_library/ingredients/disks/clone_encrypted_disk.py
new file mode 100644
index 00000000000..2df729de693
--- /dev/null
+++ b/compute/client_library/ingredients/disks/clone_encrypted_disk.py
@@ -0,0 +1,64 @@
+# Copyright 2022 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_disk_from_customer_encrypted_disk(
+ project_id: str, zone: str, disk_name: str, disk_type: str,
+ disk_size_gb: int, disk_link: str,
+ encryption_key: bytes) -> compute_v1.Disk:
+ """
+ Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk.
+
+ The encryption key must be the same for the source disk and the new disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ encryption_key: customer-supplied encryption key used for encrypting
+ data in the source disk. The data will be encrypted with the same key
+ in the new disk.
+
+ Returns:
+ An attachable copy of an existing disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_disk = disk_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.raw_key = encryption_key
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+#
diff --git a/compute/client_library/ingredients/disks/clone_encrypted_disk_managed_key.py b/compute/client_library/ingredients/disks/clone_encrypted_disk_managed_key.py
new file mode 100644
index 00000000000..53ab7baa5a1
--- /dev/null
+++ b/compute/client_library/ingredients/disks/clone_encrypted_disk_managed_key.py
@@ -0,0 +1,65 @@
+# Copyright 2022 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_disk_from_kms_encrypted_disk(
+ project_id: str, zone: str, disk_name: str, disk_type: str,
+ disk_size_gb: int, disk_link: str,
+ kms_key_name: str) -> compute_v1.Disk:
+ """
+ Creates a zonal non-boot disk in a project with the copy of data from an existing disk.
+
+ The encryption key must be the same for the source disk and the new disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ kms_key_name: URL of the key from KMS. The key might be from another project, as
+ long as you have access to it. The data will be encrypted with the same key
+ in the new disk. This value uses following format:
+ "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}"
+
+ Returns:
+ An attachable copy of an existing disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_disk = disk_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.kms_key_name = kms_key_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+#
diff --git a/compute/client_library/ingredients/disks/create_empty_disk.py b/compute/client_library/ingredients/disks/create_empty_disk.py
new file mode 100644
index 00000000000..b06f283f08f
--- /dev/null
+++ b/compute/client_library/ingredients/disks/create_empty_disk.py
@@ -0,0 +1,55 @@
+# Copyright 2022 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
+import sys
+
+from google.cloud import compute_v1
+
+
+#
+def create_empty_disk(
+ project_id: str, zone: str, disk_name: str, disk_type: str, disk_size_gb: int
+) -> compute_v1.Disk:
+ """
+ Creates a new empty disk in a project in given zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+
+ Returns:
+ An unattached Disk instance.
+ """
+ disk = compute_v1.Disk()
+ disk.size_gb = disk_size_gb
+ disk.name = disk_name
+ disk.zone = zone
+ disk.type_ = disk_type
+
+ disk_client = compute_v1.DisksClient()
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk.name)
+#
diff --git a/compute/client_library/ingredients/disks/create_from_image.py b/compute/client_library/ingredients/disks/create_from_image.py
new file mode 100644
index 00000000000..ab6ebf28ecc
--- /dev/null
+++ b/compute/client_library/ingredients/disks/create_from_image.py
@@ -0,0 +1,59 @@
+# Copyright 2022 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
+import sys
+
+from google.cloud import compute_v1
+
+
+#
+def create_disk_from_image(
+ project_id: str, zone: str, disk_name: str, disk_type: str, disk_size_gb: int, source_image: str
+) -> compute_v1.Disk:
+ """
+ Creates a new disk in a project in given zone using an image as base.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ source_image: source image to use when creating this disk. You must have read access to this disk. This
+ can be one of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+
+ Returns:
+ An unattached Disk instance.
+ """
+ disk = compute_v1.Disk()
+ disk.size_gb = disk_size_gb
+ disk.name = disk_name
+ disk.zone = zone
+ disk.type_ = disk_type
+ disk.source_image = source_image
+
+ disk_client = compute_v1.DisksClient()
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk.name)
+#
diff --git a/compute/client_library/ingredients/disks/create_from_snapshot.py b/compute/client_library/ingredients/disks/create_from_snapshot.py
new file mode 100644
index 00000000000..a2dd1f49595
--- /dev/null
+++ b/compute/client_library/ingredients/disks/create_from_snapshot.py
@@ -0,0 +1,55 @@
+# Copyright 2022 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_disk_from_snapshot(project_id: str, zone: str, disk_name: str, disk_type: str, disk_size_gb: int, snapshot_link: str) -> compute_v1.Disk:
+ """
+ Creates a new disk in a project in given zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ snapshot_link: a link to the snapshot you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ An unattached Disk instance.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_snapshot = snapshot_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+#
diff --git a/compute/client_library/ingredients/disks/create_from_source.py b/compute/client_library/ingredients/disks/create_from_source.py
new file mode 100644
index 00000000000..a7ed3006ceb
--- /dev/null
+++ b/compute/client_library/ingredients/disks/create_from_source.py
@@ -0,0 +1,55 @@
+# Copyright 2022 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_disk_from_disk(project_id: str, zone: str, disk_name: str, disk_type: str,
+ disk_size_gb: int, disk_link: str) -> compute_v1.Disk:
+ """
+ Creates a disk in a project in a given zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+
+ Returns:
+ An attachable disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_disk = disk_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+#
diff --git a/compute/client_library/ingredients/disks/create_kms_encrypted_disk.py b/compute/client_library/ingredients/disks/create_kms_encrypted_disk.py
new file mode 100644
index 00000000000..7078ea6b6b6
--- /dev/null
+++ b/compute/client_library/ingredients/disks/create_kms_encrypted_disk.py
@@ -0,0 +1,70 @@
+# Copyright 2022 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 typing import Optional
+
+from google.cloud import compute_v1
+
+
+#
+def create_kms_encrypted_disk(project_id: str, zone: str, disk_name: str, disk_type: str,
+ disk_size_gb: int, kms_key_name: str,
+ disk_link: Optional[str] = None, image_link: Optional[str] = None) -> compute_v1.Disk:
+ """
+ Creates a zonal disk in a project. If you do not provide values for disk_link or image_link,
+ an empty disk will be created.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ kms_key_name: URL of the key from KMS. The key might be from another project, as
+ long as you have access to it. The data will be encrypted with the same key
+ in the new disk. This value uses following format:
+ "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}"
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ image_link: a link to the image you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+
+ Returns:
+ An attachable disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ if disk_link:
+ disk.source_disk = disk_link
+ if image_link:
+ disk.source_image = image_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.kms_key_name = kms_key_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+#
diff --git a/compute/client_library/ingredients/disks/delete.py b/compute/client_library/ingredients/disks/delete.py
new file mode 100644
index 00000000000..6b3788b9415
--- /dev/null
+++ b/compute/client_library/ingredients/disks/delete.py
@@ -0,0 +1,39 @@
+# Copyright 2022 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
+import sys
+from typing import NoReturn
+
+from google.cloud import compute_v1
+
+
+#
+def delete_disk(project_id: str, zone: str, disk_name: str) -> NoReturn:
+ """
+ Deletes a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which is the disk you want to delete.
+ disk_name: name of the disk you want to delete.
+ """
+ disk_client = compute_v1.DisksClient()
+ operation = disk_client.delete(project=project_id, zone=zone, disk=disk_name)
+ wait_for_extended_operation(operation, "disk deletion")
+ return
+#
diff --git a/compute/client_library/ingredients/disks/disk_from_snapshot.py b/compute/client_library/ingredients/disks/disk_from_snapshot.py
new file mode 100644
index 00000000000..f7abd0c5a68
--- /dev/null
+++ b/compute/client_library/ingredients/disks/disk_from_snapshot.py
@@ -0,0 +1,54 @@
+# Copyright 2022 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 disk_from_snapshot(
+ disk_type: str, disk_size_gb: int, boot: bool, source_snapshot: str, auto_delete: bool = True
+) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses a disk snapshot as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_snapshot: disk snapshot to use when creating this disk. You must have read access to this disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified snapshot.
+ """
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_snapshot = source_snapshot
+ initialize_params.disk_type = disk_type
+ initialize_params.disk_size_gb = disk_size_gb
+ disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ disk.auto_delete = auto_delete
+ disk.boot = boot
+ return disk
+#
diff --git a/compute/client_library/ingredients/disks/empty_disk.py b/compute/client_library/ingredients/disks/empty_disk.py
new file mode 100644
index 00000000000..70c677b7da3
--- /dev/null
+++ b/compute/client_library/ingredients/disks/empty_disk.py
@@ -0,0 +1,49 @@
+# Copyright 2022 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 empty_disk(disk_type: str, disk_size_gb: int, boot: bool = False, auto_delete: bool = True) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. The created disk contains
+ no data and requires formatting before it can be used.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created as an empty disk.
+ """
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.disk_type = disk_type
+ initialize_params.disk_size_gb = disk_size_gb
+ disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ disk.auto_delete = auto_delete
+ disk.boot = boot
+ return disk
+#
diff --git a/compute/client_library/ingredients/disks/from_image.py b/compute/client_library/ingredients/disks/from_image.py
new file mode 100644
index 00000000000..945b018b9f8
--- /dev/null
+++ b/compute/client_library/ingredients/disks/from_image.py
@@ -0,0 +1,55 @@
+# Copyright 2022 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 disk_from_image(
+ disk_type: str, disk_size_gb: int, boot: bool, source_image: str, auto_delete: bool = True
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+#
diff --git a/compute/client_library/ingredients/disks/get.py b/compute/client_library/ingredients/disks/get.py
new file mode 100644
index 00000000000..54b68d9d4ae
--- /dev/null
+++ b/compute/client_library/ingredients/disks/get.py
@@ -0,0 +1,37 @@
+# Copyright 2022 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
+import sys
+from typing import NoReturn, Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def get_disk(project_id: str, zone: str, disk_name: str) -> compute_v1.Disk:
+ """
+ Gets a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone where the disk exists.
+ disk_name: name of the disk you want to retrieve.
+ """
+ disk_client = compute_v1.DisksClient()
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+#
diff --git a/compute/client_library/ingredients/disks/list.py b/compute/client_library/ingredients/disks/list.py
new file mode 100644
index 00000000000..ec3ecab83cc
--- /dev/null
+++ b/compute/client_library/ingredients/disks/list.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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
+import sys
+from typing import NoReturn, Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_disks(project_id: str, zone: str, filter_: str = "") -> Iterable[compute_v1.Disk]:
+ """
+ Deletes a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which is the disk you want to delete.
+ filter_: filter to be applied when listing disks. Learn more about filters here:
+ https://cloud.google.com/python/docs/reference/compute/latest/google.cloud.compute_v1.types.ListDisksRequest
+ """
+ disk_client = compute_v1.DisksClient()
+ request = compute_v1.ListDisksRequest()
+ request.project = project_id
+ request.zone = zone
+ request.filter = filter_
+ return disk_client.list(request)
+#
+
diff --git a/compute/client_library/ingredients/disks/local_ssd.py b/compute/client_library/ingredients/disks/local_ssd.py
new file mode 100644
index 00000000000..8b7f6f54d34
--- /dev/null
+++ b/compute/client_library/ingredients/disks/local_ssd.py
@@ -0,0 +1,41 @@
+# Copyright 2022 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 local_ssd_disk(zone: str) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. The created disk contains
+ no data and requires formatting before it can be used.
+
+ Args:
+ zone: The zone in which the local SSD drive will be attached.
+
+ Returns:
+ AttachedDisk object configured as a local SSD disk.
+ """
+ disk = compute_v1.AttachedDisk()
+ disk.type_ = compute_v1.AttachedDisk.Type.SCRATCH.name
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.disk_type = f"zones/{zone}/diskTypes/local-ssd"
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ return disk
+#
diff --git a/compute/client_library/ingredients/disks/regional_create_from_source.py b/compute/client_library/ingredients/disks/regional_create_from_source.py
new file mode 100644
index 00000000000..af6757d7efb
--- /dev/null
+++ b/compute/client_library/ingredients/disks/regional_create_from_source.py
@@ -0,0 +1,68 @@
+# Copyright 2022 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 typing import Iterable, Optional
+
+from google.cloud import compute_v1
+
+
+#
+def create_regional_disk(project_id: str, region: str, replica_zones: Iterable[str],
+ disk_name: str, disk_type: str,
+ disk_size_gb: int,
+ disk_link: Optional[str] = None,
+ snapshot_link: Optional[str] = None) -> compute_v1.Disk:
+ """
+ Creates a regional disk from an existing zonal disk in a given project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ region: name of the region in which you want to create the disk.
+ replica_zones: an iterable collection of zone names in which you want to keep
+ the new disks' replicas. One of the replica zones of the clone must match
+ the zone of the source disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "regions/{region}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "regions/us-west3/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ snapshot_link: a link to the snapshot you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ An attachable regional disk.
+ """
+ disk_client = compute_v1.RegionDisksClient()
+ disk = compute_v1.Disk()
+ disk.replica_zones = replica_zones
+ disk.size_gb = disk_size_gb
+ if disk_link:
+ disk.source_disk = disk_link
+ if snapshot_link:
+ disk.source_snapshot = snapshot_link
+ disk.type_ = disk_type
+ disk.region = region
+ disk.name = disk_name
+ operation = disk_client.insert(project=project_id, region=region, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, region=region, disk=disk_name)
+#
diff --git a/compute/client_library/ingredients/disks/regional_delete.py b/compute/client_library/ingredients/disks/regional_delete.py
new file mode 100644
index 00000000000..0bc2f59d87a
--- /dev/null
+++ b/compute/client_library/ingredients/disks/regional_delete.py
@@ -0,0 +1,39 @@
+# Copyright 2022 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
+import sys
+from typing import NoReturn
+
+from google.cloud import compute_v1
+
+
+#
+def delete_regional_disk(project_id: str, region: str, disk_name: str) -> NoReturn:
+ """
+ Deletes a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ region:name of the region where the disk is located.
+ disk_name: name of the disk that you want to delete.
+ """
+ disk_client = compute_v1.RegionDisksClient()
+ operation = disk_client.delete(project=project_id, region=region, disk=disk_name)
+ wait_for_extended_operation(operation, "regional disk deletion")
+ return
+#
diff --git a/compute/client_library/ingredients/firewall/create.py b/compute/client_library/ingredients/firewall/create.py
new file mode 100644
index 00000000000..d6f17b090a4
--- /dev/null
+++ b/compute/client_library/ingredients/firewall/create.py
@@ -0,0 +1,72 @@
+# Copyright 2022 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_firewall_rule(
+ project_id: str, firewall_rule_name: str, network: str = "global/networks/default"
+) -> compute_v1.Firewall:
+ """
+ Creates a simple firewall rule allowing for incoming HTTP and HTTPS access from the entire Internet.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule that is created.
+ network: name of the network the rule will be applied to. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.name = firewall_rule_name
+ firewall_rule.direction = "INGRESS"
+
+ allowed_ports = compute_v1.Allowed()
+ allowed_ports.I_p_protocol = "tcp"
+ allowed_ports.ports = ["80", "443"]
+
+ firewall_rule.allowed = [allowed_ports]
+ firewall_rule.source_ranges = ["0.0.0.0/0"]
+ firewall_rule.network = network
+ firewall_rule.description = "Allowing TCP traffic on port 80 and 443 from Internet."
+
+ firewall_rule.target_tags = ["web"]
+
+ # Note that the default value of priority for the firewall API is 1000.
+ # If you check the value of `firewall_rule.priority` at this point it
+ # will be equal to 0, however it is not treated as "set" by the library and thus
+ # the default will be applied to the new rule. If you want to create a rule that
+ # has priority == 0, you need to explicitly set it so:
+ # TODO: Uncomment to set the priority to 0
+ # firewall_rule.priority = 0
+
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.insert(
+ project=project_id, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "firewall rule creation")
+
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+#
diff --git a/compute/client_library/ingredients/firewall/delete.py b/compute/client_library/ingredients/firewall/delete.py
new file mode 100644
index 00000000000..1f4b8703106
--- /dev/null
+++ b/compute/client_library/ingredients/firewall/delete.py
@@ -0,0 +1,39 @@
+# Copyright 2022 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 delete_firewall_rule(project_id: str, firewall_rule_name: str) -> None:
+ """
+ Deletes a firewall rule from the project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the firewall rule you want to delete.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.delete(
+ project=project_id, firewall=firewall_rule_name
+ )
+
+ wait_for_extended_operation(operation, "firewall rule deletion")
+ return
+#
diff --git a/compute/client_library/ingredients/firewall/get.py b/compute/client_library/ingredients/firewall/get.py
new file mode 100644
index 00000000000..0a8388d5699
--- /dev/null
+++ b/compute/client_library/ingredients/firewall/get.py
@@ -0,0 +1,36 @@
+# Copyright 2022 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 get_firewall_rule(project_id: str, firewall_rule_name: str) -> compute_v1.Firewall:
+ """
+ Retrieve a Firewall from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the firewall rule you want to retrieve.
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+#
diff --git a/compute/client_library/ingredients/firewall/list.py b/compute/client_library/ingredients/firewall/list.py
new file mode 100644
index 00000000000..5deeac4e3b7
--- /dev/null
+++ b/compute/client_library/ingredients/firewall/list.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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 typing import Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_firewall_rules(project_id: str) -> Iterable[compute_v1.Firewall]:
+ """
+ Return a list of all the firewall rules in specified project. Also prints the
+ list of firewall names and their descriptions.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ A flat list of all firewall rules defined for given project.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ firewalls_list = firewall_client.list(project=project_id)
+
+ for firewall in firewalls_list:
+ print(f" - {firewall.name}: {firewall.description}")
+
+ return firewalls_list
+#
+
diff --git a/compute/client_library/ingredients/firewall/patch.py b/compute/client_library/ingredients/firewall/patch.py
new file mode 100644
index 00000000000..0c44979cd3b
--- /dev/null
+++ b/compute/client_library/ingredients/firewall/patch.py
@@ -0,0 +1,46 @@
+# Copyright 2022 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 patch_firewall_priority(project_id: str, firewall_rule_name: str, priority: int) -> None:
+ """
+ Modifies the priority of a given firewall rule.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule you want to modify.
+ priority: the new priority to be set for the rule.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.priority = priority
+
+ # The patch operation doesn't require the full definition of a Firewall object. It will only update
+ # the values that were set in it, in this case it will only change the priority.
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.patch(
+ project=project_id, firewall=firewall_rule_name, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "firewall rule patching")
+ return
+#
+
diff --git a/compute/client_library/ingredients/firewall/windows_kms.py b/compute/client_library/ingredients/firewall/windows_kms.py
new file mode 100644
index 00000000000..265d86e666f
--- /dev/null
+++ b/compute/client_library/ingredients/firewall/windows_kms.py
@@ -0,0 +1,62 @@
+# Copyright 2022 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_firewall_rule_for_windows_activation_host(
+ project_id: str, firewall_rule_name: str, network: str = "global/networks/default"
+) -> compute_v1.Firewall:
+ """
+ Creates an egress firewall rule with the highest priority for host
+ kms.windows.googlecloud.com (35.190.247.13) for Windows activation.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule that is created.
+ network: name of the network the rule will be applied to. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.name = firewall_rule_name
+ firewall_rule.network = network
+
+ allowed = compute_v1.Allowed()
+ allowed.ports = ['1688']
+ allowed.I_p_protocol = 'tcp'
+
+ firewall_rule.allowed = [allowed]
+ firewall_rule.destination_ranges = ["35.190.247.13/32"]
+ firewall_rule.direction = compute_v1.Firewall.Direction.EGRESS.name
+ firewall_rule.priority = 0
+
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.insert(project=project_id, firewall_resource=firewall_rule)
+
+ wait_for_extended_operation(operation, "windows KSM firewall rule creation")
+
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+#
+
diff --git a/compute/client_library/ingredients/images/create.py b/compute/client_library/ingredients/images/create.py
new file mode 100644
index 00000000000..b029d7aaa3c
--- /dev/null
+++ b/compute/client_library/ingredients/images/create.py
@@ -0,0 +1,87 @@
+# Copyright 2022 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
+
+import warnings
+from typing import Optional
+
+from google.cloud import compute_v1
+
+#
+STOPPED_MACHINE_STATUS = (
+ compute_v1.Instance.Status.TERMINATED.name,
+ compute_v1.Instance.Status.STOPPED.name
+)
+
+
+def create_image_from_disk(project_id: str, zone: str, source_disk_name: str, image_name: str,
+ storage_location: Optional[str] = None, force_create: bool = False) -> compute_v1.Image:
+ """
+ Creates a new disk image.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ zone: zone of the disk you copy from.
+ source_disk_name: name of the source disk you copy from.
+ image_name: name of the image you want to create.
+ storage_location: storage location for the image. If the value is undefined,
+ function will store the image in the multi-region closest to your image's
+ source location.
+ force_create: create the image even if the source disk is attached to a
+ running instance.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ disk_client = compute_v1.DisksClient()
+ instance_client = compute_v1.InstancesClient()
+
+ # Get source disk
+ disk = disk_client.get(project=project_id, zone=zone, disk=source_disk_name)
+
+ for disk_user in disk.users:
+ instance = instance_client.get(project=project_id, zone=zone, instance=disk_user)
+ if instance.status in STOPPED_MACHINE_STATUS:
+ continue
+ if not force_create:
+ raise RuntimeError(f"Instance {disk_user} should be stopped. For Windows instances please "
+ f"stop the instance using `GCESysprep` command. For Linux instances just "
+ f"shut it down normally. You can supress this error and create an image of"
+ f"the disk by setting `force_create` parameter to true (not recommended). \n"
+ f"More information here: \n"
+ f" * https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api \n"
+ f" * https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#prepare_instance_for_image")
+ else:
+ warnings.warn(f"Warning: The `force_create` option may compromise the integrity of your image. "
+ f"Stop the {disk_user} instance before you create the image if possible.")
+
+ # Create image
+ image = compute_v1.Image()
+ image.source_disk = disk.self_link
+ image.name = image_name
+ if storage_location:
+ image.storage_locations = [storage_location]
+
+ operation = image_client.insert(project=project_id, image_resource=image)
+
+ wait_for_extended_operation(operation, "image creation from disk")
+
+ return image_client.get(project=project_id, image=image_name)
+#
diff --git a/compute/client_library/ingredients/images/create_from_image.py b/compute/client_library/ingredients/images/create_from_image.py
new file mode 100644
index 00000000000..07b6d1c8e76
--- /dev/null
+++ b/compute/client_library/ingredients/images/create_from_image.py
@@ -0,0 +1,69 @@
+# Copyright 2022 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 typing import Optional, Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def create_image_from_image(project_id: str, source_image_name: str, image_name: str,
+ source_project_id: Optional[str] = None,
+ guest_os_features: Optional[Iterable[str]] = None,
+ storage_location: Optional[str] = None) -> compute_v1.Image:
+ """
+ Creates a copy of another image.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to place your new image in.
+ source_image_name: name of the image you want to copy.
+ image_name: name of the image you want to create.
+ source_project_id: name of the project that hosts the source image. If left unset, it's assumed to equal
+ the `project_id`.
+ guest_os_features: an iterable collection of guest features you want to enable for the bootable image.
+ Learn more about Guest OS features here:
+ https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#guest-os-features
+ storage_location: the storage location of your image. For example, specify "us" to store the image in the
+ `us` multi-region, or "us-central1" to store it in the `us-central1` region. If you do not make a selection,
+ Compute Engine stores the image in the multi-region closest to your image's source location.
+
+ Returns:
+ An Image object.
+ """
+ if source_project_id is None:
+ source_project_id = project_id
+
+ image_client = compute_v1.ImagesClient()
+ src_image = image_client.get(project=source_project_id, image=source_image_name)
+
+ image = compute_v1.Image()
+ image.name = image_name
+ image.source_image = src_image.self_link
+ if storage_location:
+ image.storage_locations = [storage_location]
+
+ if guest_os_features:
+ image.guest_os_features = [compute_v1.GuestOsFeature(type_=feature) for feature in guest_os_features]
+
+ operation = image_client.insert(project=project_id, image_resource=image)
+
+ wait_for_extended_operation(operation, "image creation from image")
+
+ return image_client.get(project=project_id, image=image_name)
+#
diff --git a/compute/client_library/ingredients/images/create_from_snapshot.py b/compute/client_library/ingredients/images/create_from_snapshot.py
new file mode 100644
index 00000000000..f882e24914b
--- /dev/null
+++ b/compute/client_library/ingredients/images/create_from_snapshot.py
@@ -0,0 +1,71 @@
+# Copyright 2022 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 typing import Optional, Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def create_image_from_snapshot(project_id: str, source_snapshot_name: str, image_name: str,
+ source_project_id: Optional[str] = None,
+ guest_os_features: Optional[Iterable[str]] = None,
+ storage_location: Optional[str] = None) -> compute_v1.Image:
+ """
+ Creates an image based on a snapshot.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to place your new image in.
+ source_snapshot_name: name of the snapshot you want to use as a base of your image.
+ image_name: name of the image you want to create.
+ source_project_id: name of the project that hosts the source snapshot. If left unset, it's assumed to equal
+ the `project_id`.
+ guest_os_features: an iterable collection of guest features you want to enable for the bootable image.
+ Learn more about Guest OS features here:
+ https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#guest-os-features
+ storage_location: the storage location of your image. For example, specify "us" to store the image in the
+ `us` multi-region, or "us-central1" to store it in the `us-central1` region. If you do not make a selection,
+ Compute Engine stores the image in the multi-region closest to your image's source location.
+
+ Returns:
+ An Image object.
+ """
+ if source_project_id is None:
+ source_project_id = project_id
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ image_client = compute_v1.ImagesClient()
+ src_snapshot = snapshot_client.get(project=source_project_id, snapshot=source_snapshot_name)
+
+ image = compute_v1.Image()
+ image.name = image_name
+ image.source_snapshot = src_snapshot.self_link
+
+ if storage_location:
+ image.storage_locations = [storage_location]
+
+ if guest_os_features:
+ image.guest_os_features = [compute_v1.GuestOsFeature(type_=feature) for feature in guest_os_features]
+
+ operation = image_client.insert(project=project_id, image_resource=image)
+
+ wait_for_extended_operation(operation, "image creation from snapshot")
+
+ return image_client.get(project=project_id, image=image_name)
+#
diff --git a/compute/client_library/ingredients/images/delete.py b/compute/client_library/ingredients/images/delete.py
new file mode 100644
index 00000000000..18059fd5348
--- /dev/null
+++ b/compute/client_library/ingredients/images/delete.py
@@ -0,0 +1,37 @@
+# Copyright 2022 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 typing import NoReturn
+
+from google.cloud import compute_v1
+
+
+#
+def delete_image(project_id: str, image_name: str) -> NoReturn:
+ """
+ Deletes a disk image.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ image_name: name of the image you want to delete.
+ """
+ image_client = compute_v1.ImagesClient()
+ operation = image_client.delete(project=project_id, image=image_name)
+ wait_for_extended_operation(operation, "image deletion")
+#
diff --git a/compute/client_library/ingredients/images/get_image.py b/compute/client_library/ingredients/images/get_image.py
new file mode 100644
index 00000000000..4dcce0e77c0
--- /dev/null
+++ b/compute/client_library/ingredients/images/get_image.py
@@ -0,0 +1,37 @@
+# Copyright 2022 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 get_image(project_id: str, image_name: str) -> compute_v1.Image:
+ """
+ Retrieve detailed information about a single image from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to list images from.
+ image_name: name of the image you want to get details of.
+
+ Returns:
+ An instance of compute_v1.Image object with information about specified image.
+ """
+ image_client = compute_v1.ImagesClient()
+ return image_client.get(project=project_id, image=image_name)
+#
diff --git a/compute/client_library/ingredients/images/get_image_from_family.py b/compute/client_library/ingredients/images/get_image_from_family.py
new file mode 100644
index 00000000000..45daec115fe
--- /dev/null
+++ b/compute/client_library/ingredients/images/get_image_from_family.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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 get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(
+ project=project, family=family
+ )
+ return newest_image
+#
diff --git a/compute/client_library/ingredients/images/list_images.py b/compute/client_library/ingredients/images/list_images.py
new file mode 100644
index 00000000000..b4c191fc3e2
--- /dev/null
+++ b/compute/client_library/ingredients/images/list_images.py
@@ -0,0 +1,37 @@
+# Copyright 2022 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 typing import Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_images(project_id: str) -> Iterable[compute_v1.Image]:
+ """
+ Retrieve a list of images available in given project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to list images from.
+
+ Returns:
+ An iterable collection of compute_v1.Image objects.
+ """
+ image_client = compute_v1.ImagesClient()
+ return image_client.list(project=project_id)
+#
diff --git a/compute/client_library/ingredients/images/set_depracation_status.py b/compute/client_library/ingredients/images/set_depracation_status.py
new file mode 100644
index 00000000000..a817a03e63e
--- /dev/null
+++ b/compute/client_library/ingredients/images/set_depracation_status.py
@@ -0,0 +1,46 @@
+# Copyright 2022 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 typing import NoReturn
+
+from google.cloud import compute_v1
+
+
+#
+def set_deprecation_status(project_id: str, image_name: str, status: compute_v1.DeprecationStatus.State) -> NoReturn:
+ """
+ Modify the deprecation status of an image.
+
+ Note: Image objects by default don't have the `deprecated` attribute at all unless it's set.
+
+ Args:
+ project_id: project ID or project number of the Cloud project that hosts the image.
+ image_name: name of the image you want to modify
+ status: the status you want to set for the image. Available values are available in
+ `compute_v1.DeprecationStatus.State` enum. Learn more about image deprecation statuses:
+ https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#deprecation-states
+ """
+ image_client = compute_v1.ImagesClient()
+ deprecation_status = compute_v1.DeprecationStatus()
+ deprecation_status.state = status.name
+ operation = image_client.deprecate(project=project_id, image=image_name,
+ deprecation_status_resource=deprecation_status)
+
+ wait_for_extended_operation(operation, "changing deprecation state of an image")
+#
diff --git a/compute/client_library/ingredients/instance-templates/create.py b/compute/client_library/ingredients/instance-templates/create.py
new file mode 100644
index 00000000000..9c79f8c76b7
--- /dev/null
+++ b/compute/client_library/ingredients/instance-templates/create.py
@@ -0,0 +1,75 @@
+# Copyright 2022 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
+import sys
+
+from google.cloud import compute_v1
+
+
+#
+def create_template(project_id: str, template_name: str) -> compute_v1.InstanceTemplate:
+ """
+ Create a new instance template with the provided name and a specific
+ instance configuration.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the new template to create.
+
+ Returns:
+ InstanceTemplate object that represents the new instance template.
+ """
+ # The template describes the size and source image of the boot disk
+ # to attach to the instance.
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-11"
+ )
+ initialize_params.disk_size_gb = 250
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+
+ # The template connects the instance to the `default` network,
+ # without specifying a subnetwork.
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.name = "global/networks/default"
+
+ # The template lets the instance use an external IP address.
+ access_config = compute_v1.AccessConfig()
+ access_config.name = "External NAT"
+ access_config.type_ = "ONE_TO_ONE_NAT"
+ access_config.network_tier = "PREMIUM"
+ network_interface.access_configs = [access_config]
+
+ template = compute_v1.InstanceTemplate()
+ template.name = template_name
+ template.properties.disks = [disk]
+ template.properties.machine_type = "e2-standard-4"
+ template.properties.network_interfaces = [network_interface]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.insert(
+ project=project_id, instance_template_resource=template
+ )
+
+ wait_for_extended_operation(operation, "instance template creation")
+
+ return template_client.get(project=project_id, instance_template=template_name)
+#
diff --git a/compute/client_library/ingredients/instance-templates/create_from_instance.py b/compute/client_library/ingredients/instance-templates/create_from_instance.py
new file mode 100644
index 00000000000..1450cf02f21
--- /dev/null
+++ b/compute/client_library/ingredients/instance-templates/create_from_instance.py
@@ -0,0 +1,64 @@
+# Copyright 2022 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_template_from_instance(
+ project_id: str, instance: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Create a new instance template based on an existing instance.
+ This new template specifies a different boot disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ instance: the instance to base the new template on. This value uses
+ the following format: "projects/{project}/zones/{zone}/instances/{instance_name}"
+ template_name: name of the new template to create.
+
+ Returns:
+ InstanceTemplate object that represents the new instance template.
+ """
+ disk = compute_v1.DiskInstantiationConfig()
+ # Device name must match the name of a disk attached to the instance you are
+ # basing your template on.
+ disk.device_name = "disk-1"
+ # Replace the original boot disk image used in your instance with a Rocky Linux image.
+ disk.instantiate_from = "CUSTOM_IMAGE"
+ disk.custom_image = "projects/rocky-linux-cloud/global/images/family/rocky-linux-8"
+ # Override the auto_delete setting.
+ disk.auto_delete = True
+
+ template = compute_v1.InstanceTemplate()
+ template.name = template_name
+ template.source_instance = instance
+ template.source_instance_params = compute_v1.SourceInstanceParams()
+ template.source_instance_params.disk_configs = [disk]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.insert(
+ project=project_id, instance_template_resource=template
+ )
+
+ wait_for_extended_operation(operation, "instance template creation")
+
+ return template_client.get(project=project_id, instance_template=template_name)
+#
diff --git a/compute/client_library/ingredients/instance-templates/create_with_subnet.py b/compute/client_library/ingredients/instance-templates/create_with_subnet.py
new file mode 100644
index 00000000000..fd91bb3c977
--- /dev/null
+++ b/compute/client_library/ingredients/instance-templates/create_with_subnet.py
@@ -0,0 +1,72 @@
+# Copyright 2022 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_template_with_subnet(
+ project_id: str, network: str, subnetwork: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Create an instance template that uses a provided subnet.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ network: the network to be used in the new template. This value uses
+ the following format: "projects/{project}/global/networks/{network}"
+ subnetwork: the subnetwork to be used in the new template. This value
+ uses the following format: "projects/{project}/regions/{region}/subnetworks/{subnetwork}"
+ template_name: name of the new template to create.
+
+ Returns:
+ InstanceTemplate object that represents the new instance template.
+ """
+ # The template describes the size and source image of the book disk to
+ # attach to the instance.
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-11"
+ )
+ initialize_params.disk_size_gb = 250
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+
+ template = compute_v1.InstanceTemplate()
+ template.name = template_name
+ template.properties = compute_v1.InstanceProperties()
+ template.properties.disks = [disk]
+ template.properties.machine_type = "e2-standard-4"
+
+ # The template connects the instance to the specified network and subnetwork.
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.network = network
+ network_interface.subnetwork = subnetwork
+ template.properties.network_interfaces = [network_interface]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.insert(
+ project=project_id, instance_template_resource=template
+ )
+ wait_for_extended_operation(operation, "instance template creation")
+
+ return template_client.get(project=project_id, instance_template=template_name)
+#
diff --git a/compute/client_library/ingredients/instance-templates/delete.py b/compute/client_library/ingredients/instance-templates/delete.py
new file mode 100644
index 00000000000..f3720666026
--- /dev/null
+++ b/compute/client_library/ingredients/instance-templates/delete.py
@@ -0,0 +1,38 @@
+# Copyright 2022 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 delete_instance_template(project_id: str, template_name: str):
+ """
+ Delete an instance template.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the template to delete.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.delete(
+ project=project_id, instance_template=template_name
+ )
+ wait_for_extended_operation(operation, "instance template deletion")
+ return
+#
diff --git a/compute/client_library/ingredients/instance-templates/get.py b/compute/client_library/ingredients/instance-templates/get.py
new file mode 100644
index 00000000000..99aae684df0
--- /dev/null
+++ b/compute/client_library/ingredients/instance-templates/get.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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 get_instance_template(
+ project_id: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Retrieve an instance template, which you can use to create virtual machine
+ (VM) instances and managed instance groups (MIGs).
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the template to retrieve.
+
+ Returns:
+ InstanceTemplate object that represents the retrieved template.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ return template_client.get(project=project_id, instance_template=template_name)
+#
diff --git a/compute/client_library/ingredients/instance-templates/list.py b/compute/client_library/ingredients/instance-templates/list.py
new file mode 100644
index 00000000000..851e2c48e50
--- /dev/null
+++ b/compute/client_library/ingredients/instance-templates/list.py
@@ -0,0 +1,37 @@
+# Copyright 2022 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 typing import Iterable
+from google.cloud import compute_v1
+
+
+#
+def list_instance_templates(project_id: str) -> Iterable[compute_v1.InstanceTemplate]:
+ """
+ Get a list of InstanceTemplate objects available in a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+
+ Returns:
+ Iterable list of InstanceTemplate objects.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ return template_client.list(project=project_id)
+#
diff --git a/compute/client_library/ingredients/instances/__init__.py b/compute/client_library/ingredients/instances/__init__.py
new file mode 100644
index 00000000000..81d8b9be3da
--- /dev/null
+++ b/compute/client_library/ingredients/instances/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2022 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
diff --git a/compute/client_library/ingredients/instances/bulk_insert.py b/compute/client_library/ingredients/instances/bulk_insert.py
new file mode 100644
index 00000000000..d7cf578d41a
--- /dev/null
+++ b/compute/client_library/ingredients/instances/bulk_insert.py
@@ -0,0 +1,90 @@
+# Copyright 2022 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
+from typing import Iterable, Optional
+import uuid
+
+from google.cloud import compute_v1
+
+
+#
+def bulk_insert_instance(project_id: str, zone: str, template: compute_v1.InstanceTemplate,
+ count: int, name_pattern: str, min_count: Optional[int] = None,
+ labels: Optional[dict] = None) -> Iterable[compute_v1.Instance]:
+ """
+ Create multiple VMs based on an Instance Template. The newly created instances will
+ be returned as a list and will share a label with key `bulk_batch` and a random
+ value.
+
+ If the bulk insert operation fails and the requested number of instances can't be created,
+ and more than min_count instances are created, then those instances can be found using
+ the `bulk_batch` label with value attached to the raised exception in bulk_batch_id
+ attribute. So, you can use the following filter: f"label.bulk_batch={err.bulk_batch_id}"
+ when listing instances in a zone to get the instances that were successfully created.
+
+ 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"
+ template: an Instance Template to be used for creation of the new VMs.
+ name_pattern: The string pattern used for the names of the VMs. The pattern
+ must contain one continuous sequence of placeholder hash characters (#)
+ with each character corresponding to one digit of the generated instance
+ name. Example: a name_pattern of inst-#### generates instance names such
+ as inst-0001 and inst-0002. If existing instances in the same project and
+ zone have names that match the name pattern then the generated instance
+ numbers start after the biggest existing number. For example, if there
+ exists an instance with name inst-0050, then instance names generated
+ using the pattern inst-#### begin with inst-0051. The name pattern
+ placeholder #...# can contain up to 18 characters.
+ count: The maximum number of instances to create.
+ min_count (optional): The minimum number of instances to create. If no min_count is
+ specified then count is used as the default value. If min_count instances
+ cannot be created, then no instances will be created and instances already
+ created will be deleted.
+ labels (optional): A dictionary with labels to be added to the new VMs.
+ """
+ bulk_insert_resource = compute_v1.BulkInsertInstanceResource()
+ bulk_insert_resource.source_instance_template = template.self_link
+ bulk_insert_resource.count = count
+ bulk_insert_resource.min_count = min_count or count
+ bulk_insert_resource.name_pattern = name_pattern
+
+ if not labels:
+ labels = {}
+
+ labels['bulk_batch'] = uuid.uuid4().hex
+ instance_prop = compute_v1.InstanceProperties()
+ instance_prop.labels = labels
+ bulk_insert_resource.instance_properties = instance_prop
+
+ bulk_insert_request = compute_v1.BulkInsertInstanceRequest()
+ bulk_insert_request.bulk_insert_instance_resource_resource = bulk_insert_resource
+ bulk_insert_request.project = project_id
+ bulk_insert_request.zone = zone
+
+ client = compute_v1.InstancesClient()
+ operation = client.bulk_insert(bulk_insert_request)
+
+ try:
+ wait_for_extended_operation(operation, "bulk instance creation")
+ except Exception as err:
+ err.bulk_batch_id = labels['bulk_batch']
+ raise err
+
+ list_req = compute_v1.ListInstancesRequest()
+ list_req.project = project_id
+ list_req.zone = zone
+ list_req.filter = " AND ".join(f"labels.{key}:{value}" for key, value in labels.items())
+ return client.list(list_req)
+#
diff --git a/compute/client_library/ingredients/instances/create_instance.py b/compute/client_library/ingredients/instances/create_instance.py
new file mode 100644
index 00000000000..afbbbef330a
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_instance.py
@@ -0,0 +1,155 @@
+# Copyright 2022 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
+
+import re
+from typing import List
+import warnings
+
+from google.cloud import compute_v1
+
+
+#
+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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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)
+#
diff --git a/compute/client_library/ingredients/instances/create_instance_from_template.py b/compute/client_library/ingredients/instances/create_instance_from_template.py
new file mode 100644
index 00000000000..b007f4f4023
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_instance_from_template.py
@@ -0,0 +1,56 @@
+# Copyright 2022 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_instance_from_template(
+ project_id: str, zone: str, instance_name: str, instance_template_url: str
+) -> compute_v1.Instance:
+ """
+ Creates a Compute Engine VM instance from an instance template.
+
+ Args:
+ project_id: ID or number of the project you want to use.
+ zone: Name of the zone you want to check, for example: us-west3-b
+ instance_name: Name of the new instance.
+ instance_template_url: URL of the instance template used for creating the new instance.
+ It can be a full or partial URL.
+ Examples:
+ - https://www.googleapis.com/compute/v1/projects/project/global/instanceTemplates/example-instance-template
+ - projects/project/global/instanceTemplates/example-instance-template
+ - global/instanceTemplates/example-instance-template
+
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ instance_insert_request = compute_v1.InsertInstanceRequest()
+ instance_insert_request.project = project_id
+ instance_insert_request.zone = zone
+ instance_insert_request.source_instance_template = instance_template_url
+ instance_insert_request.instance_resource.name = instance_name
+
+ operation = instance_client.insert(instance_insert_request)
+ wait_for_extended_operation(operation, "instance creation")
+
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+#
diff --git a/compute/client_library/ingredients/instances/create_instance_from_template_with_overrides.py b/compute/client_library/ingredients/instances/create_instance_from_template_with_overrides.py
new file mode 100644
index 00000000000..39432712b92
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_instance_from_template_with_overrides.py
@@ -0,0 +1,96 @@
+# Copyright 2022 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_instance_from_template_with_overrides(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ instance_template_name: str,
+ machine_type: str,
+ new_disk_source_image: str,
+) -> compute_v1.Instance:
+ """
+ Creates a Compute Engine VM instance from an instance template, changing the machine type and
+ adding a new disk created from a source image.
+
+ Args:
+ project_id: ID or number of the project you want to use.
+ zone: Name of the zone you want to check, for example: us-west3-b
+ instance_name: Name of the new instance.
+ instance_template_name: Name of the instance template used for creating the new instance.
+ machine_type: Machine type you want to set in following format:
+ "zones/{zone}/machineTypes/{type_name}". For example:
+ - "zones/europe-west3-c/machineTypes/f1-micro"
+ - You can find the list of available machine types using:
+ https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
+ new_disk_source_image: Path the the disk image you want to use for your new
+ disk. This can be one of the public images
+ (like "projects/debian-cloud/global/images/family/debian-10")
+ or a private image you have access to.
+ For a list of available public images, see the documentation:
+ http://cloud.google.com/compute/docs/images
+
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance_template_client = compute_v1.InstanceTemplatesClient()
+
+ # Retrieve an instance template by name.
+ instance_template = instance_template_client.get(
+ project=project_id, instance_template=instance_template_name
+ )
+
+ # Adjust diskType field of the instance template to use the URL formatting required by instances.insert.diskType
+ # For instance template, there is only a name, not URL.
+ for disk in instance_template.properties.disks:
+ if disk.initialize_params.disk_type:
+ disk.initialize_params.disk_type = (
+ f"zones/{zone}/diskTypes/{disk.initialize_params.disk_type}"
+ )
+
+ instance = compute_v1.Instance()
+ instance.name = instance_name
+ instance.machine_type = machine_type
+ instance.disks = list(instance_template.properties.disks)
+
+ new_disk = compute_v1.AttachedDisk()
+ new_disk.initialize_params.disk_size_gb = 50
+ new_disk.initialize_params.source_image = new_disk_source_image
+ new_disk.auto_delete = True
+ new_disk.boot = False
+ new_disk.type_ = "PERSISTENT"
+
+ instance.disks.append(new_disk)
+
+ instance_insert_request = compute_v1.InsertInstanceRequest()
+ instance_insert_request.project = project_id
+ instance_insert_request.zone = zone
+ instance_insert_request.instance_resource = instance
+ instance_insert_request.source_instance_template = instance_template.self_link
+
+ operation = instance_client.insert(instance_insert_request)
+ wait_for_extended_operation(operation, "instance creation")
+
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+#
\ No newline at end of file
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_from_custom_image.py b/compute/client_library/ingredients/instances/create_start_instance/create_from_custom_image.py
new file mode 100644
index 00000000000..f3b9bd58ec7
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_from_custom_image.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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_from_custom_image(
+ project_id: str, zone: str, instance_name: str, custom_image_link: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with custom image used as its boot disk.
+
+ 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.
+ custom_image_link: link to the custom image you want to use in the form of:
+ "projects/{project_name}/global/images/{image_name}"
+
+ Returns:
+ Instance object.
+ """
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, custom_image_link, True)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_from_public_image.py b/compute/client_library/ingredients/instances/create_start_instance/create_from_public_image.py
new file mode 100644
index 00000000000..fdf2e35f748
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_from_public_image.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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_from_public_image(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link, True)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_from_snapshot.py b/compute/client_library/ingredients/instances/create_start_instance/create_from_snapshot.py
new file mode 100644
index 00000000000..a2729332cf1
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_from_snapshot.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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
+
+
+#
+def create_from_snapshot(
+ project_id: str, zone: str, instance_name: str, snapshot_link: str
+):
+ """
+ Create a new VM instance with boot disk created from a snapshot. The
+ new boot disk will have 20 gigabytes.
+
+ 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.
+ snapshot_link: link to the snapshot you want to use as the source of your
+ boot disk in the form of: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ Instance object.
+ """
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_snapshot(disk_type, 20, True, snapshot_link)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_windows_instance.py b/compute/client_library/ingredients/instances/create_start_instance/create_windows_instance.py
new file mode 100644
index 00000000000..ee437e5c562
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_windows_instance.py
@@ -0,0 +1,81 @@
+# Copyright 2022 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 typing import Optional
+
+from google.cloud import compute_v1
+
+
+#
+def create_windows_instance(project_id: str, zone: str, instance_name: str,
+ machine_type: str, source_image_family: str = "windows-2022",
+ network_link: str = "global/networks/default",
+ subnetwork_link: Optional[str] = None) -> compute_v1.Instance:
+ """
+ Creates a new Windows Server instance that has only an internal IP address.
+
+ 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.
+ machine_type: machine type you want to create in following format:
+ "zones/{zone}/machineTypes/{type_name}". For example:
+ "zones/europe-west3-c/machineTypes/f1-micro"
+ You can find the list of available machine types using:
+ https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
+ source_image_family: name of the public image family for Windows Server or SQL Server images.
+ https://cloud.google.com/compute/docs/images#os-compute-support
+ 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}"
+
+ Returns:
+ Instance object.
+ """
+ if subnetwork_link is None:
+ subnetwork_link = f'regions/{zone}/subnetworks/default'
+
+ base_image = get_image_from_family(
+ project="windows-cloud", family=source_image_family
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 100, True, base_image.self_link, True)]
+
+ # You must verify or configure routes and firewall rules in your VPC network
+ # to allow access to kms.windows.googlecloud.com.
+ # More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server
+
+ # Additionally, you must enable Private Google Access for subnets in your VPC network
+ # that contain Windows instances with only internal IP addresses.
+ # More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
+
+ instance = create_instance(
+ project_id,
+ zone,
+ instance_name,
+ disks,
+ machine_type=machine_type,
+ network_link=network_link,
+ subnetwork_link=subnetwork_link,
+ external_access=True, # Set this to False to disable external IP for your instance
+ )
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_with_additional_disk.py b/compute/client_library/ingredients/instances/create_start_instance/create_with_additional_disk.py
new file mode 100644
index 00000000000..b921f27a3a0
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_with_additional_disk.py
@@ -0,0 +1,46 @@
+# Copyright 2022 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_additional_disk(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system on a 20 GB disk
+ and a 25 GB additional empty disk.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [
+ disk_from_image(disk_type, 20, True, newest_debian.self_link),
+ empty_disk(disk_type, 25),
+ ]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+#
\ No newline at end of file
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_with_existing_disks.py b/compute/client_library/ingredients/instances/create_start_instance/create_with_existing_disks.py
new file mode 100644
index 00000000000..c4b90a62f93
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_with_existing_disks.py
@@ -0,0 +1,50 @@
+# Copyright 2022 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 typing import List
+
+from google.cloud import compute_v1
+
+
+#
+def create_with_existing_disks(project_id: str, zone: str, instance_name: str, disk_names: List[str]) -> compute_v1.Instance:
+ """
+ Create a new VM instance using selected disks. The first disk in disk_names will
+ be used as boot disk.
+
+ 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.
+ disk_names: list of disk names to be attached to the new virtual machine.
+ First disk in this list will be used as the boot device.
+
+ Returns:
+ Instance object.
+ """
+ assert len(disk_names) >= 1
+ disks = [get_disk(project_id, zone, disk_name) for disk_name in disk_names]
+ attached_disks = []
+ for disk in disks:
+ adisk = compute_v1.AttachedDisk()
+ adisk.source = disk.self_link
+ attached_disks.append(adisk)
+ attached_disks[0].boot = True
+ instance = create_instance(project_id, zone, instance_name, attached_disks)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_with_local_ssd.py b/compute/client_library/ingredients/instances/create_start_instance/create_with_local_ssd.py
new file mode 100644
index 00000000000..9802a1552d1
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_with_local_ssd.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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_ssd(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system and SSD local disk.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link, True),
+ local_ssd_disk(zone)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_start_instance/create_with_snapshotted_data_disk.py b/compute/client_library/ingredients/instances/create_start_instance/create_with_snapshotted_data_disk.py
new file mode 100644
index 00000000000..ea87201e5f1
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_start_instance/create_with_snapshotted_data_disk.py
@@ -0,0 +1,48 @@
+# Copyright 2022 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
+
+
+#
+def create_with_snapshotted_data_disk(
+ project_id: str, zone: str, instance_name: str, snapshot_link: str
+):
+ """
+ Create a new VM instance with Debian 10 operating system and data disk created from snapshot.
+
+ 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.
+ snapshot_link: link to the snapshot you want to use as the source of your
+ data disk in the form of: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [
+ disk_from_image(disk_type, 10, True, newest_debian.self_link),
+ disk_from_snapshot(disk_type, 11, False, snapshot_link),
+ ]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/create_with_subnet.py b/compute/client_library/ingredients/instances/create_with_subnet.py
new file mode 100644
index 00000000000..bc39ff2231a
--- /dev/null
+++ b/compute/client_library/ingredients/instances/create_with_subnet.py
@@ -0,0 +1,57 @@
+# Copyright 2022 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_subnet(
+ project_id: str, zone: str, instance_name: str, network_link: str, subnet_link: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system in specified network and subnetwork.
+
+ 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.
+ 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}"
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(
+ project_id,
+ zone,
+ instance_name,
+ disks,
+ network_link=network_link,
+ subnetwork_link=subnet_link,
+ )
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/custom_hostname/create.py b/compute/client_library/ingredients/instances/custom_hostname/create.py
new file mode 100644
index 00000000000..9a990cb9fa4
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_hostname/create.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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_instance_custom_hostname(project_id: str, zone: str, instance_name: str, hostname: str) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system and a custom hostname.
+
+ 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.
+ hostname: the hostname you want to use for the new instance.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-11"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(project_id, zone, instance_name, disks, custom_hostname=hostname)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/custom_hostname/get.py b/compute/client_library/ingredients/instances/custom_hostname/get.py
new file mode 100644
index 00000000000..b362fce26e1
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_hostname/get.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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 get_hostname(project_id: str, zone: str, instance_name: str) -> str:
+ """
+ Retrieve the hostname of given instance.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to check.
+
+ Returns:
+ The hostname of an instance.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.hostname
+#
diff --git a/compute/client_library/ingredients/instances/custom_machine_types/create_extra_mem_no_helper.py b/compute/client_library/ingredients/instances/custom_machine_types/create_extra_mem_no_helper.py
new file mode 100644
index 00000000000..536455f669d
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_machine_types/create_extra_mem_no_helper.py
@@ -0,0 +1,71 @@
+# Copyright 2022 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 typing import List
+
+from google.cloud import compute_v1
+
+
+#
+def create_custom_instances_extra_mem(
+ project_id: str, zone: str, instance_name: str, core_count: int, memory: int
+) -> List[compute_v1.Instance]:
+ """
+ Create 3 new VM instances with extra memory without using a CustomMachineType helper class.
+
+ 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.
+ core_count: number of CPU cores you want to use.
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Returns:
+ List of Instance objects.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ # The core_count and memory values are not validated anywhere and can be rejected by the API.
+ instances = [
+ create_instance(
+ project_id,
+ zone,
+ f"{instance_name}_n1_extra_mem",
+ disks,
+ f"zones/{zone}/machineTypes/custom-{core_count}-{memory}-ext",
+ ),
+ create_instance(
+ project_id,
+ zone,
+ f"{instance_name}_n2_extra_mem",
+ disks,
+ f"zones/{zone}/machineTypes/n2-custom-{core_count}-{memory}-ext",
+ ),
+ create_instance(
+ project_id,
+ zone,
+ f"{instance_name}_n2d_extra_mem",
+ disks,
+ f"zones/{zone}/machineTypes/n2d-custom-{core_count}-{memory}-ext",
+ ),
+ ]
+ return instances
+#
diff --git a/compute/client_library/ingredients/instances/custom_machine_types/create_shared_with_helper.py b/compute/client_library/ingredients/instances/custom_machine_types/create_shared_with_helper.py
new file mode 100644
index 00000000000..a29193438ff
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_machine_types/create_shared_with_helper.py
@@ -0,0 +1,60 @@
+# Copyright 2022 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_custom_shared_core_instance(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ cpu_series: CustomMachineType.CPUSeries,
+ memory: int,
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with a custom type using shared CPUs.
+
+ 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.
+ cpu_series: the type of CPU you want to use. Pick one value from the CustomMachineType.CPUSeries enum.
+ For example: CustomMachineType.CPUSeries.E2_MICRO
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Return:
+ Instance object.
+ """
+ assert cpu_series in (
+ CustomMachineType.CPUSeries.E2_MICRO,
+ CustomMachineType.CPUSeries.E2_SMALL,
+ CustomMachineType.CPUSeries.E2_MEDIUM,
+ )
+ custom_type = CustomMachineType(zone, cpu_series, memory)
+
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ return create_instance(project_id, zone, instance_name, disks, str(custom_type))
+#
diff --git a/compute/client_library/ingredients/instances/custom_machine_types/create_with_helper.py b/compute/client_library/ingredients/instances/custom_machine_types/create_with_helper.py
new file mode 100644
index 00000000000..2731f40da89
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_machine_types/create_with_helper.py
@@ -0,0 +1,61 @@
+# Copyright 2022 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_custom_instance(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ cpu_series: CustomMachineType.CPUSeries,
+ core_count: int,
+ memory: int,
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with a custom machine type.
+
+ 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.
+ cpu_series: the type of CPU you want to use. Select one value from the CustomMachineType.CPUSeries enum.
+ For example: CustomMachineType.CPUSeries.N2
+ core_count: number of CPU cores you want to use.
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Return:
+ Instance object.
+ """
+ assert cpu_series in (
+ CustomMachineType.CPUSeries.E2,
+ CustomMachineType.CPUSeries.N1,
+ CustomMachineType.CPUSeries.N2,
+ CustomMachineType.CPUSeries.N2D,
+ )
+ custom_type = CustomMachineType(zone, cpu_series, memory, core_count)
+
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ return create_instance(project_id, zone, instance_name, disks, str(custom_type))
+#
diff --git a/compute/client_library/ingredients/instances/custom_machine_types/create_without_helper.py b/compute/client_library/ingredients/instances/custom_machine_types/create_without_helper.py
new file mode 100644
index 00000000000..a17a979bb8f
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_machine_types/create_without_helper.py
@@ -0,0 +1,60 @@
+# Copyright 2022 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 typing import List
+
+from google.cloud import compute_v1
+
+
+#
+def create_custom_instances_no_helper(
+ project_id: str, zone: str, instance_name: str, core_count: int, memory: int
+) -> List[compute_v1.Instance]:
+ """
+ Create 7 new VM instances without using a CustomMachineType helper function.
+
+ 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.
+ core_count: number of CPU cores you want to use.
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Returns:
+ List of Instance objects.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ params = [
+ (f"{instance_name}_n1", f"zones/{zone}/machineTypes/custom-{core_count}-{memory}"),
+ (f"{instance_name}_n2", f"zones/{zone}/machineTypes/n2-custom-{core_count}-{memory}"),
+ (f"{instance_name}_n2d", f"zones/{zone}/machineTypes/n2d-custom-{core_count}-{memory}"),
+ (f"{instance_name}_e2", f"zones/{zone}/machineTypes/e2-custom-{core_count}-{memory}"),
+ (f"{instance_name}_e2_micro", f"zones/{zone}/machineTypes/e2-custom-micro-{memory}"),
+ (f"{instance_name}_e2_small", f"zones/{zone}/machineTypes/e2-custom-small-{memory}"),
+ (f"{instance_name}_e2_medium", f"zones/{zone}/machineTypes/e2-custom-medium-{memory}"),
+ ]
+ # The core_count and memory values are not validated anywhere and can be rejected by the API.
+ instances = [create_instance(project_id, zone, name, disks, type) for name, type in params]
+ return instances
+#
diff --git a/compute/client_library/ingredients/instances/custom_machine_types/helper_class.py b/compute/client_library/ingredients/instances/custom_machine_types/helper_class.py
new file mode 100644
index 00000000000..616961943b8
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_machine_types/helper_class.py
@@ -0,0 +1,211 @@
+# Copyright 2022 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 collections import namedtuple
+from enum import Enum, unique
+
+
+#
+def gb_to_mb(value: int) -> int:
+ return value << 10
+
+
+class CustomMachineType:
+ """
+ Allows to create custom machine types to be used with the VM instances.
+ """
+
+ @unique
+ class CPUSeries(Enum):
+ N1 = "custom"
+ N2 = "n2-custom"
+ N2D = "n2d-custom"
+ E2 = "e2-custom"
+ E2_MICRO = "e2-custom-micro"
+ E2_SMALL = "e2-custom-small"
+ E2_MEDIUM = "e2-custom-medium"
+
+ TypeLimits = namedtuple(
+ "TypeLimits",
+ [
+ "allowed_cores",
+ "min_mem_per_core",
+ "max_mem_per_core",
+ "allow_extra_memory",
+ "extra_memory_limit",
+ ],
+ )
+
+ # The limits for various CPU types are described on:
+ # https://cloud.google.com/compute/docs/general-purpose-machines
+ LIMITS = {
+ CPUSeries.E2: TypeLimits(frozenset(range(2, 33, 2)), 512, 8192, False, 0),
+ CPUSeries.E2_MICRO: TypeLimits(frozenset(), 1024, 2048, False, 0),
+ CPUSeries.E2_SMALL: TypeLimits(frozenset(), 2048, 4096, False, 0),
+ CPUSeries.E2_MEDIUM: TypeLimits(frozenset(), 4096, 8192, False, 0),
+ CPUSeries.N2: TypeLimits(
+ frozenset(range(2, 33, 2)).union(set(range(36, 129, 4))),
+ 512,
+ 8192,
+ True,
+ gb_to_mb(624),
+ ),
+ CPUSeries.N2D: TypeLimits(
+ frozenset({2, 4, 8, 16, 32, 48, 64, 80, 96}), 512, 8192, True, gb_to_mb(768)
+ ),
+ CPUSeries.N1: TypeLimits(
+ frozenset({1}.union(range(2, 97, 2))), 922, 6656, True, gb_to_mb(624)
+ ),
+ }
+
+ def __init__(
+ self, zone: str, cpu_series: CPUSeries, memory_mb: int, core_count: int = 0
+ ):
+ self.zone = zone
+ self.cpu_series = cpu_series
+ self.limits = self.LIMITS[self.cpu_series]
+ # Shared machine types (e2-small, e2-medium and e2-micro) always have
+ # 2 vCPUs: https://cloud.google.com/compute/docs/general-purpose-machines#e2_limitations
+ self.core_count = 2 if self.is_shared() else core_count
+ self.memory_mb = memory_mb
+ self._checked = False
+ self._check_parameters()
+ self.extra_memory_used = self._check_extra_memory()
+
+ def is_shared(self):
+ return self.cpu_series in (
+ CustomMachineType.CPUSeries.E2_SMALL,
+ CustomMachineType.CPUSeries.E2_MICRO,
+ CustomMachineType.CPUSeries.E2_MEDIUM,
+ )
+
+ def _check_extra_memory(self) -> bool:
+ if self._checked:
+ return self.memory_mb > self.core_count * self.limits.max_mem_per_core
+ else:
+ raise RuntimeError("You need to call _check_parameters() before calling _check_extra_memory()")
+
+ def _check_parameters(self):
+ """
+ Check whether the requested parameters are allowed. Find more information about limitations of custom machine
+ types at: https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types
+ """
+ # Check the number of cores
+ if (
+ self.limits.allowed_cores
+ and self.core_count not in self.limits.allowed_cores
+ ):
+ raise RuntimeError(
+ f"Invalid number of cores requested. Allowed number of cores for {self.cpu_series.name} is: {sorted(self.limits.allowed_cores)}"
+ )
+
+ # Memory must be a multiple of 256 MB
+ if self.memory_mb % 256 != 0:
+ raise RuntimeError("Requested memory must be a multiple of 256 MB.")
+
+ # Check if the requested memory isn't too little
+ if self.memory_mb < self.core_count * self.limits.min_mem_per_core:
+ raise RuntimeError(
+ f"Requested memory is too low. Minimal memory for {self.cpu_series.name} is {self.limits.min_mem_per_core} MB per core."
+ )
+
+ # Check if the requested memory isn't too much
+ if self.memory_mb > self.core_count * self.limits.max_mem_per_core:
+ if self.limits.allow_extra_memory:
+ if self.memory_mb > self.limits.extra_memory_limit:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.extra_memory_limit} MB."
+ )
+ else:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.max_mem_per_core} MB per core."
+ )
+
+ self._checked = True
+
+ def __str__(self) -> str:
+ """
+ Return the custom machine type in form of a string acceptable by Compute Engine API.
+ """
+ if self.cpu_series in {
+ self.CPUSeries.E2_SMALL,
+ self.CPUSeries.E2_MICRO,
+ self.CPUSeries.E2_MEDIUM,
+ }:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.memory_mb}"
+
+ if self.extra_memory_used:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}-ext"
+
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}"
+
+ def short_type_str(self) -> str:
+ """
+ Return machine type in a format without the zone. For example, n2-custom-0-10240.
+ This format is used to create instance templates.
+ """
+ return str(self).rsplit("/", maxsplit=1)[1]
+
+ @classmethod
+ def from_str(cls, machine_type: str):
+ """
+ Construct a new object from a string. The string needs to be a valid custom machine type like:
+ - https://www.googleapis.com/compute/v1/projects/diregapic-mestiv/zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - e2-custom-4-8192 (in this case, the zone parameter will not be set)
+ """
+ zone = None
+ if machine_type.startswith("http"):
+ machine_type = machine_type[machine_type.find("zones/") :]
+
+ if machine_type.startswith("zones/"):
+ _, zone, _, machine_type = machine_type.split("/")
+
+ extra_mem = machine_type.endswith("-ext")
+
+ if machine_type.startswith("custom"):
+ cpu = cls.CPUSeries.N1
+ _, cores, memory = machine_type.rsplit("-", maxsplit=2)
+ else:
+ if extra_mem:
+ cpu_series, _, cores, memory, _ = machine_type.split("-")
+ else:
+ cpu_series, _, cores, memory = machine_type.split("-")
+ if cpu_series == "n2":
+ cpu = cls.CPUSeries.N2
+ elif cpu_series == "n2d":
+ cpu = cls.CPUSeries.N2D
+ elif cpu_series == "e2":
+ cpu = cls.CPUSeries.E2
+ if cores == "micro":
+ cpu = cls.CPUSeries.E2_MICRO
+ cores = 2
+ elif cores == "small":
+ cpu = cls.CPUSeries.E2_SMALL
+ cores = 2
+ elif cores == "medium":
+ cpu = cls.CPUSeries.E2_MEDIUM
+ cores = 2
+ else:
+ raise RuntimeError("Unknown CPU series.")
+
+ cores = int(cores)
+ memory = int(memory)
+
+ return cls(zone, cpu, memory, cores)
+#
diff --git a/compute/client_library/ingredients/instances/custom_machine_types/update_memory.py b/compute/client_library/ingredients/instances/custom_machine_types/update_memory.py
new file mode 100644
index 00000000000..fd1afac8912
--- /dev/null
+++ b/compute/client_library/ingredients/instances/custom_machine_types/update_memory.py
@@ -0,0 +1,88 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def add_extended_memory_to_instance(
+ project_id: str, zone: str, instance_name: str, new_memory: int
+):
+ """
+ Modify an existing VM to use extended memory.
+
+ 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.
+ new_memory: the amount of memory for the VM instance, in megabytes.
+
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ if not ("n1-" in instance.machine_type or "n2-" in instance.machine_type or "n2d-" in instance.machine_type):
+ raise RuntimeError("Extra memory is available only for N1, N2 and N2D CPUs.")
+
+ # Make sure that the machine is turned off
+ if instance.status not in (
+ instance.Status.TERMINATED.name,
+ instance.Status.STOPPED.name,
+ ):
+ operation = instance_client.stop(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ wait_for_extended_operation(operation, "instance stopping")
+ start = time.time()
+ while instance.status not in (
+ instance.Status.TERMINATED.name,
+ instance.Status.STOPPED.name,
+ ):
+ # Waiting for the instance to be turned off.
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ time.sleep(2)
+ if time.time() - start >= 300: # 5 minutes
+ raise TimeoutError()
+
+ # Modify the machine definition, remember that extended memory is available only for N1, N2 and N2D CPUs
+ start, end = instance.machine_type.rsplit("-", maxsplit=1)
+ instance.machine_type = start + f"-{new_memory}-ext"
+ # TODO: If you prefer to use the CustomMachineType helper class, uncomment this code and comment the 2 lines above
+ # Using CustomMachineType helper
+ # cmt = CustomMachineType.from_str(instance.machine_type)
+ # cmt.memory_mb = new_memory
+ # cmt.extra_memory_used = True
+ # instance.machine_type = str(cmt)
+ operation = instance_client.update(
+ project=project_id,
+ zone=zone,
+ instance=instance_name,
+ instance_resource=instance,
+ )
+ wait_for_extended_operation(operation, "instance update")
+
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+#
diff --git a/compute/client_library/ingredients/instances/delete.py b/compute/client_library/ingredients/instances/delete.py
new file mode 100644
index 00000000000..bd16d439ae9
--- /dev/null
+++ b/compute/client_library/ingredients/instances/delete.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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
+import sys
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def delete_instance(project_id: str, zone: str, machine_name: str) -> None:
+ """
+ Send an instance deletion 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 you want to use. For example: “us-west3-b”
+ machine_name: name of the machine you want to delete.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ print(f"Deleting {machine_name} from {zone}...")
+ operation = instance_client.delete(
+ project=project_id, zone=zone, instance=machine_name
+ )
+ wait_for_extended_operation(operation, "instance deletion")
+ print(f"Instance {machine_name} deleted.")
+ return
+#
diff --git a/compute/client_library/ingredients/instances/delete_protection/__init__.py b/compute/client_library/ingredients/instances/delete_protection/__init__.py
new file mode 100644
index 00000000000..8fb7cb024dd
--- /dev/null
+++ b/compute/client_library/ingredients/instances/delete_protection/__init__.py
@@ -0,0 +1,19 @@
+# Copyright 2022 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
+
diff --git a/compute/client_library/ingredients/instances/delete_protection/create.py b/compute/client_library/ingredients/instances/delete_protection/create.py
new file mode 100644
index 00000000000..eb431ec7e84
--- /dev/null
+++ b/compute/client_library/ingredients/instances/delete_protection/create.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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_protected_instance(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system and delete protection
+ turned on.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-11"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(project_id, zone, instance_name, disks, delete_protection=True)
+ return instance
+#
\ No newline at end of file
diff --git a/compute/client_library/ingredients/instances/delete_protection/get.py b/compute/client_library/ingredients/instances/delete_protection/get.py
new file mode 100644
index 00000000000..f57b1624e05
--- /dev/null
+++ b/compute/client_library/ingredients/instances/delete_protection/get.py
@@ -0,0 +1,38 @@
+# Copyright 2022 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 get_delete_protection(project_id: str, zone: str, instance_name: str) -> bool:
+ """
+ Returns the state of delete protection flag of given instance.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the virtual machine to check.
+ Returns:
+ The boolean value of the delete protection setting.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.deletion_protection
+#
diff --git a/compute/client_library/ingredients/instances/delete_protection/set.py b/compute/client_library/ingredients/instances/delete_protection/set.py
new file mode 100644
index 00000000000..4d3a73a5be7
--- /dev/null
+++ b/compute/client_library/ingredients/instances/delete_protection/set.py
@@ -0,0 +1,46 @@
+# Copyright 2022 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 set_delete_protection(
+ project_id: str, zone: str, instance_name: str, delete_protection: bool
+) -> None:
+ """
+ Updates the delete protection setting of given instance.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the instance to update.
+ delete_protection: boolean value indicating if the virtual machine should be
+ protected against deletion or not.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ request = compute_v1.SetDeletionProtectionInstanceRequest()
+ request.project = project_id
+ request.zone = zone
+ request.resource = instance_name
+ request.deletion_protection = delete_protection
+
+ operation = instance_client.set_deletion_protection(request)
+ wait_for_extended_operation(operation, "changing delete protection setting")
+ return
+#
diff --git a/compute/client_library/ingredients/instances/get.py b/compute/client_library/ingredients/instances/get.py
new file mode 100644
index 00000000000..702696ad789
--- /dev/null
+++ b/compute/client_library/ingredients/instances/get.py
@@ -0,0 +1,39 @@
+# Copyright 2022 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 get_instance(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Get information about a VM instance in the given zone in the specified project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the VM instance you want to query.
+ Returns:
+ An Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(project=project_id, zone=zone, instance=instance_name)
+
+ return instance
+#
+
diff --git a/compute/client_library/ingredients/instances/get_serial_port.py b/compute/client_library/ingredients/instances/get_serial_port.py
new file mode 100644
index 00000000000..a673e1d27e1
--- /dev/null
+++ b/compute/client_library/ingredients/instances/get_serial_port.py
@@ -0,0 +1,38 @@
+# Copyright 2022 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 get_instance_serial_port_output(project_id: str, zone: str, instance_name: str) -> compute_v1.SerialPortOutput:
+ """
+ Returns the last 1 MB of serial port output from the specified instance.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the VM instance you want to query.
+ Returns:
+ Content of the serial port output of an instance inside a compute_v1.SerialPortOutput object.
+ More about this type: https://cloud.google.com/python/docs/reference/compute/latest/google.cloud.compute_v1.types.SerialPortOutput
+
+ """
+ instance_client = compute_v1.InstancesClient()
+ return instance_client.get_serial_port_output(project=project_id, zone=zone, instance=instance_name)
+#
diff --git a/compute/client_library/ingredients/instances/list.py b/compute/client_library/ingredients/instances/list.py
new file mode 100644
index 00000000000..089f7fdabb8
--- /dev/null
+++ b/compute/client_library/ingredients/instances/list.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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 typing import Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_instances(project_id: str, zone: str) -> Iterable[compute_v1.Instance]:
+ """
+ List all instances in the given zone in the specified project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ Returns:
+ An iterable collection of Instance objects.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance_list = instance_client.list(project=project_id, zone=zone)
+
+ print(f"Instances found in zone {zone}:")
+ for instance in instance_list:
+ print(f" - {instance.name} ({instance.machine_type})")
+
+ return instance_list
+#
+
diff --git a/compute/client_library/ingredients/instances/list_all.py b/compute/client_library/ingredients/instances/list_all.py
new file mode 100644
index 00000000000..60498c4df67
--- /dev/null
+++ b/compute/client_library/ingredients/instances/list_all.py
@@ -0,0 +1,58 @@
+# Copyright 2022 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 collections import defaultdict
+from typing import Dict, Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_all_instances(
+ project_id: str,
+) -> Dict[str, Iterable[compute_v1.Instance]]:
+ """
+ Returns a dictionary of all instances present in a project, grouped by their zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ Returns:
+ A dictionary with zone names as keys (in form of "zones/{zone_name}") and
+ iterable collections of Instance objects as values.
+ """
+ instance_client = compute_v1.InstancesClient()
+ request = compute_v1.AggregatedListInstancesRequest()
+ request.project = project_id
+ # Use the `max_results` parameter to limit the number of results that the API returns per response page.
+ request.max_results = 50
+
+ agg_list = instance_client.aggregated_list(request=request)
+
+ all_instances = defaultdict(list)
+ print("Instances found:")
+ # Despite using the `max_results` parameter, you don't need to handle the pagination
+ # yourself. The returned `AggregatedListPager` object handles pagination
+ # automatically, returning separated pages as you iterate over the results.
+ for zone, response in agg_list:
+ if response.instances:
+ all_instances[zone].extend(response.instances)
+ print(f" {zone}:")
+ for instance in response.instances:
+ print(f" - {instance.name} ({instance.machine_type})")
+ return all_instances
+#
diff --git a/compute/client_library/ingredients/instances/preemptible/__init__.py b/compute/client_library/ingredients/instances/preemptible/__init__.py
new file mode 100644
index 00000000000..81d8b9be3da
--- /dev/null
+++ b/compute/client_library/ingredients/instances/preemptible/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2022 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
diff --git a/compute/client_library/ingredients/instances/preemptible/create.py b/compute/client_library/ingredients/instances/preemptible/create.py
new file mode 100644
index 00000000000..145fbc3679f
--- /dev/null
+++ b/compute/client_library/ingredients/instances/preemptible/create.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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_preemptible_instance(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Create a new preemptible VM instance with Debian 10 operating system.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-11"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(project_id, zone, instance_name, disks, preemptible=True)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/preemptible/get.py b/compute/client_library/ingredients/instances/preemptible/get.py
new file mode 100644
index 00000000000..7ff9fc860e6
--- /dev/null
+++ b/compute/client_library/ingredients/instances/preemptible/get.py
@@ -0,0 +1,38 @@
+# Copyright 2022 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 is_preemptible(project_id: str, zone: str, instance_name: str) -> bool:
+ """
+ Check if a given instance is preemptible or not.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to check.
+ Returns:
+ The preemptible status of the instance.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.scheduling.preemptible
+#
diff --git a/compute/client_library/ingredients/instances/preemptible/preemption_history.py b/compute/client_library/ingredients/instances/preemptible/preemption_history.py
new file mode 100644
index 00000000000..53b6a3da767
--- /dev/null
+++ b/compute/client_library/ingredients/instances/preemptible/preemption_history.py
@@ -0,0 +1,56 @@
+# Copyright 2022 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
+import datetime
+from typing import List, Tuple
+
+
+#
+def preemption_history(
+ project_id: str, zone: str, instance_name: str = None
+) -> List[Tuple[str, datetime.datetime]]:
+ """
+ Get a list of preemption operations from given zone in a project. Optionally limit
+ the results to instance name.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to look for.
+ Returns:
+ List of preemption operations in given zone.
+ """
+ if instance_name:
+ filter = (
+ f'operationType="compute.instances.preempted" '
+ f"AND targetLink:instances/{instance_name}"
+ )
+ else:
+ filter = 'operationType="compute.instances.preempted"'
+
+ history = []
+
+ for operation in list_zone_operations(project_id, zone, filter):
+ this_instance_name = operation.target_link.rsplit("/", maxsplit=1)[1]
+ if instance_name and this_instance_name == instance_name:
+ # The filter used is not 100% accurate, it's `contains` not `equals`
+ # So we need to check the name to make sure it's the one we want.
+ moment = datetime.datetime.fromisoformat(operation.insert_time)
+ history.append((instance_name, moment))
+
+ return history
+#
diff --git a/compute/client_library/ingredients/instances/reset.py b/compute/client_library/ingredients/instances/reset.py
new file mode 100644
index 00000000000..f784e4b91dc
--- /dev/null
+++ b/compute/client_library/ingredients/instances/reset.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def reset_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Resets a stopped Google Compute Engine instance (with unencrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to reset.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.reset(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "instance reset")
+
+ return
+#
diff --git a/compute/client_library/ingredients/instances/resume.py b/compute/client_library/ingredients/instances/resume.py
new file mode 100644
index 00000000000..f29ffab6aee
--- /dev/null
+++ b/compute/client_library/ingredients/instances/resume.py
@@ -0,0 +1,47 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def resume_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Resume a suspended Google Compute Engine instance (with unencrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance you want to resume.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ instance = instance_client.get(project=project_id, zone=zone, instance=instance_name)
+ if instance.status != compute_v1.Instance.Status.SUSPENDED.name:
+ raise RuntimeError(f"Only suspended instances can be resumed. "
+ f"Instance {instance_name} is in {instance.status} state.")
+
+ operation = instance_client.resume(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "instance resumption")
+ return
+#
+
diff --git a/compute/client_library/ingredients/instances/spot/create.py b/compute/client_library/ingredients/instances/spot/create.py
new file mode 100644
index 00000000000..ba87875cf8e
--- /dev/null
+++ b/compute/client_library/ingredients/instances/spot/create.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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_spot_instance(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Create a new Spot VM instance with Debian 10 operating system.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-11"
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(project_id, zone, instance_name, disks, spot=True)
+ return instance
+#
diff --git a/compute/client_library/ingredients/instances/spot/get.py b/compute/client_library/ingredients/instances/spot/get.py
new file mode 100644
index 00000000000..3366cf36d59
--- /dev/null
+++ b/compute/client_library/ingredients/instances/spot/get.py
@@ -0,0 +1,39 @@
+# Copyright 2022 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 is_spot_vm(project_id: str, zone: str, instance_name: str) -> bool:
+ """
+ Check if a given instance is Spot VM or not.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to check.
+ Returns:
+ The Spot VM status of the instance.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.scheduling.provisioning_model == compute_v1.Scheduling.ProvisioningModel.SPOT.name
+#
+
diff --git a/compute/client_library/ingredients/instances/start.py b/compute/client_library/ingredients/instances/start.py
new file mode 100644
index 00000000000..eeba32eb77c
--- /dev/null
+++ b/compute/client_library/ingredients/instances/start.py
@@ -0,0 +1,41 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def start_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Starts a stopped Google Compute Engine instance (with unencrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to start.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.start(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "instance start")
+ return
+#
diff --git a/compute/client_library/ingredients/instances/start_encrypted.py b/compute/client_library/ingredients/instances/start_encrypted.py
new file mode 100644
index 00000000000..201d2233d36
--- /dev/null
+++ b/compute/client_library/ingredients/instances/start_encrypted.py
@@ -0,0 +1,63 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def start_instance_with_encryption_key(
+ project_id: str, zone: str, instance_name: str, key: bytes
+):
+ """
+ Starts a stopped Google Compute Engine instance (with encrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to start.
+ key: bytes object representing a raw base64 encoded key to your machines boot disk.
+ For more information about disk encryption see:
+ https://cloud.google.com/compute/docs/disks/customer-supplied-encryption#specifications
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ instance_data = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ # Prepare the information about disk encryption
+ disk_data = compute_v1.CustomerEncryptionKeyProtectedDisk()
+ disk_data.source = instance_data.disks[0].source
+ disk_data.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ # Use raw_key to send over the key to unlock the disk
+ # To use a key stored in KMS, you need to provide `kms_key_name` and `kms_key_service_account`
+ disk_data.disk_encryption_key.raw_key = key
+ enc_data = compute_v1.InstancesStartWithEncryptionKeyRequest()
+ enc_data.disks = [disk_data]
+
+ operation = instance_client.start_with_encryption_key(
+ project=project_id,
+ zone=zone,
+ instance=instance_name,
+ instances_start_with_encryption_key_request_resource=enc_data,
+ )
+
+ wait_for_extended_operation(operation, "instance start (with encrypted disk)")
+ return
+#
diff --git a/compute/client_library/ingredients/instances/stop.py b/compute/client_library/ingredients/instances/stop.py
new file mode 100644
index 00000000000..ea1b532f89e
--- /dev/null
+++ b/compute/client_library/ingredients/instances/stop.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def stop_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Stops a running Google Compute Engine instance.
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to stop.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.stop(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ wait_for_extended_operation(operation, "instance stopping")
+ return
+#
diff --git a/compute/client_library/ingredients/instances/suspend.py b/compute/client_library/ingredients/instances/suspend.py
new file mode 100644
index 00000000000..64a15da740e
--- /dev/null
+++ b/compute/client_library/ingredients/instances/suspend.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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
+import time
+
+from google.cloud import compute_v1
+
+
+#
+def suspend_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Suspend a running Google Compute Engine instance.
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance you want to suspend.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.suspend(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "suspend instance")
+ return
+#
+
diff --git a/compute/client_library/ingredients/operations/__init__.py b/compute/client_library/ingredients/operations/__init__.py
new file mode 100644
index 00000000000..81d8b9be3da
--- /dev/null
+++ b/compute/client_library/ingredients/operations/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 2022 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
diff --git a/compute/client_library/ingredients/operations/handle_extended_operation.py b/compute/client_library/ingredients/operations/handle_extended_operation.py
new file mode 100644
index 00000000000..1a0c74213eb
--- /dev/null
+++ b/compute/client_library/ingredients/operations/handle_extended_operation.py
@@ -0,0 +1,69 @@
+# Copyright 2022 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
+import sys
+from typing import Any
+
+from google.api_core.extended_operation import ExtendedOperation
+
+
+#
+def wait_for_extended_operation(
+ operation: ExtendedOperation,
+ verbose_name: str = "operation",
+ timeout: int = 300) -> Any:
+ """
+ This method will wait 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
+#
diff --git a/compute/client_library/ingredients/operations/list_zone_operations.py b/compute/client_library/ingredients/operations/list_zone_operations.py
new file mode 100644
index 00000000000..472e893d5a2
--- /dev/null
+++ b/compute/client_library/ingredients/operations/list_zone_operations.py
@@ -0,0 +1,46 @@
+# Copyright 2022 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
+
+from google.cloud.compute_v1.services.zone_operations import pagers
+
+
+#
+def list_zone_operations(
+ project_id: str, zone: str, filter: str = ""
+) -> pagers.ListPager:
+ """
+ List all recent operations the happened in given zone in a project. Optionally filter those
+ operations by providing a filter. More about using the filter can be found here:
+ https://cloud.google.com/compute/docs/reference/rest/v1/zoneOperations/list
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ filter: filter string to be used for this listing operation.
+ Returns:
+ List of preemption operations in given zone.
+ """
+ operation_client = compute_v1.ZoneOperationsClient()
+ request = compute_v1.ListZoneOperationsRequest()
+ request.project = project_id
+ request.zone = zone
+ request.filter = filter
+
+ return operation_client.list(request)
+#
diff --git a/compute/client_library/ingredients/operations/wait_for_operation.py b/compute/client_library/ingredients/operations/wait_for_operation.py
new file mode 100644
index 00000000000..53913076e69
--- /dev/null
+++ b/compute/client_library/ingredients/operations/wait_for_operation.py
@@ -0,0 +1,50 @@
+# Copyright 2022 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 wait_for_operation(
+ operation: compute_v1.Operation, project_id: str
+) -> compute_v1.Operation:
+ """
+ This method waits for an operation to be completed. Calling this function
+ will block until the operation is finished.
+
+ Args:
+ operation: The Operation object representing the operation you want to
+ wait on.
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ Finished Operation object.
+ """
+ kwargs = {"project": project_id, "operation": operation.name}
+ if operation.zone:
+ client = compute_v1.ZoneOperationsClient()
+ # Operation.zone is a full URL address of a zone, so we need to extract just the name
+ kwargs["zone"] = operation.zone.rsplit("/", maxsplit=1)[1]
+ elif operation.region:
+ client = compute_v1.RegionOperationsClient()
+ # Operation.region is a full URL address of a region, so we need to extract just the name
+ kwargs["region"] = operation.region.rsplit("/", maxsplit=1)[1]
+ else:
+ client = compute_v1.GlobalOperationsClient()
+ return client.wait(**kwargs)
+#
diff --git a/compute/client_library/ingredients/routes/create.py b/compute/client_library/ingredients/routes/create.py
new file mode 100644
index 00000000000..a309150c38c
--- /dev/null
+++ b/compute/client_library/ingredients/routes/create.py
@@ -0,0 +1,84 @@
+# Copyright 2022 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 typing import Optional
+
+from google.cloud import compute_v1
+
+
+#
+def create_route(project_id: str, network: str, route_name: str, destination_range: str, *,
+ next_hop_gateway: Optional[str] = None,
+ next_hop_ip: Optional[str] = None, next_hop_instance: Optional[str] = None,
+ next_hop_vpn_tunnel: Optional[str] = None, next_hop_ilb: Optional[str] = None) -> compute_v1.Route:
+ """
+ Create a new route in selected network by providing a destination and next hop name.
+
+ Note: The set of {next_hop_gateway, next_hop_ip, next_hop_instance, next_hop_vpn_tunnel,
+ next_hop_ilb} is exclusive, you and only specify one of those parameters.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ network: name of the network the route will be created in. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+ route_name: name of the new route.
+ destination_range: range of destination IPs this route should be applied to. E.g. 10.0.0.0/16.
+ next_hop_gateway: name of the gateway the traffic should be directed to.
+ next_hop_ip: IP address the traffic should be directed to.
+ next_hop_instance: name of the instance the traffic should be directed to. Name format:
+ "projects/{project}/zones/{zone}/instances/{instance_name}"
+ next_hop_vpn_tunnel: name of the VPN tunnel the traffic should be directed to. Name format:
+ "projects/{project}/regions/{region}/vpnTunnels/{vpn_tunnel_name}"
+ next_hop_ilb: name of a forwarding rule of the Internal Load Balancer the traffic
+ should be directed to. Name format:
+ "projects/{project}/regions/{region}/forwardingRules/{forwarding_rule_region}"
+
+ Returns:
+ A new compute_v1.Route object.
+ """
+ excl_args = {next_hop_instance, next_hop_ilb, next_hop_vpn_tunnel, next_hop_gateway, next_hop_ip}
+ args_set = sum(1 if arg is not None else 0 for arg in excl_args)
+
+ if args_set != 1:
+ raise RuntimeError("You must specify exactly one next_hop_* parameter.")
+
+ route = compute_v1.Route()
+ route.name = route_name
+ route.network = network
+ route.dest_range = destination_range
+
+ if next_hop_gateway:
+ route.next_hop_gateway = next_hop_gateway
+ elif next_hop_ip:
+ route.next_hop_ip = next_hop_ip
+ elif next_hop_instance:
+ route.next_hop_instance = next_hop_instance
+ elif next_hop_vpn_tunnel:
+ route.next_hop_vpn_tunnel = next_hop_vpn_tunnel
+ elif next_hop_ilb:
+ route.next_hop_ilb = next_hop_ilb
+
+ route_client = compute_v1.RoutesClient()
+ operation = route_client.insert(project=project_id, route_resource=route)
+
+ wait_for_extended_operation(operation, "route creation")
+
+ return route_client.get(project=project_id, route=route_name)
+#
diff --git a/compute/client_library/ingredients/routes/delete.py b/compute/client_library/ingredients/routes/delete.py
new file mode 100644
index 00000000000..52328a55906
--- /dev/null
+++ b/compute/client_library/ingredients/routes/delete.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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 typing import NoReturn
+
+from google.cloud import compute_v1
+
+
+#
+def delete_route(project_id: str, route_name: str) -> NoReturn:
+ """
+ Delete a route in project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ route_name: name of the route to delete.
+ """
+
+ route_client = compute_v1.RoutesClient()
+ operation = route_client.delete(project=project_id, route=route_name)
+
+ wait_for_extended_operation(operation, "route deletion")
+
+ return
+#
diff --git a/compute/client_library/ingredients/routes/list.py b/compute/client_library/ingredients/routes/list.py
new file mode 100644
index 00000000000..494f5da6868
--- /dev/null
+++ b/compute/client_library/ingredients/routes/list.py
@@ -0,0 +1,38 @@
+# Copyright 2022 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 typing import Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_routes(project_id: str, ) -> Iterable[compute_v1.Route]:
+ """
+ Lists routes in project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ An iterable collection of routes found in given project.
+ """
+
+ route_client = compute_v1.RoutesClient()
+ return route_client.list(project=project_id)
+#
diff --git a/compute/client_library/ingredients/snapshots/create.py b/compute/client_library/ingredients/snapshots/create.py
new file mode 100644
index 00000000000..9b0e0cac78b
--- /dev/null
+++ b/compute/client_library/ingredients/snapshots/create.py
@@ -0,0 +1,81 @@
+# Copyright 2022 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 typing import Optional
+
+from google.cloud import compute_v1
+
+
+#
+def create_snapshot(project_id: str, disk_name: str, snapshot_name: str, *,
+ zone: Optional[str] = None, region: Optional[str] = None,
+ location: Optional[str] = None, disk_project_id: Optional[str] = None) -> compute_v1.Snapshot:
+ """
+ Create a snapshot of a disk.
+
+ You need to pass `zone` or `region` parameter relevant to the disk you want to
+ snapshot, but not both. Pass `zone` parameter for zonal disks and `region` for
+ regional disks.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want
+ to use to store the snapshot.
+ disk_name: name of the disk you want to snapshot.
+ snapshot_name: name of the snapshot to be created.
+ zone: name of the zone in which is the disk you want to snapshot (for zonal disks).
+ region: name of the region in which is the disk you want to snapshot (for regional disks).
+ location: The Cloud Storage multi-region or the Cloud Storage region where you
+ want to store your snapshot.
+ You can specify only one storage location. Available locations:
+ https://cloud.google.com/storage/docs/locations#available-locations
+ disk_project_id: project ID or project number of the Cloud project that
+ hosts the disk you want to snapshot. If not provided, will look for
+ the disk in the `project_id` project.
+
+ Returns:
+ The new snapshot instance.
+ """
+ if zone is None and region is None:
+ raise RuntimeError("You need to specify `zone` or `region` for this function to work.")
+ if zone is not None and region is not None:
+ raise RuntimeError("You can't set both `zone` and `region` parameters.")
+
+ if disk_project_id is None:
+ disk_project_id = project_id
+
+ if zone is not None:
+ disk_client = compute_v1.DisksClient()
+ disk = disk_client.get(project=disk_project_id, zone=zone, disk=disk_name)
+ else:
+ regio_disk_client = compute_v1.RegionDisksClient()
+ disk = regio_disk_client.get(project=disk_project_id, region=region, disk=disk_name)
+
+ snapshot = compute_v1.Snapshot()
+ snapshot.source_disk = disk.self_link
+ snapshot.name = snapshot_name
+ if location:
+ snapshot.storage_locations = [location]
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ operation = snapshot_client.insert(project=project_id, snapshot_resource=snapshot)
+
+ wait_for_extended_operation(operation, "snapshot creation")
+
+ return snapshot_client.get(project=project_id, snapshot=snapshot_name)
+
+#
diff --git a/compute/client_library/ingredients/snapshots/delete.py b/compute/client_library/ingredients/snapshots/delete.py
new file mode 100644
index 00000000000..e7f5af9bc12
--- /dev/null
+++ b/compute/client_library/ingredients/snapshots/delete.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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 typing import NoReturn
+
+from google.cloud import compute_v1
+
+
+#
+def delete_snapshot(project_id: str, snapshot_name: str) -> NoReturn:
+ """
+ Delete a snapshot of a disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ snapshot_name: name of the snapshot to delete.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ operation = snapshot_client.delete(project=project_id, snapshot=snapshot_name)
+
+ wait_for_extended_operation(operation, "snapshot deletion")
+
+ return
+#
diff --git a/compute/client_library/ingredients/snapshots/get.py b/compute/client_library/ingredients/snapshots/get.py
new file mode 100644
index 00000000000..d645880feed
--- /dev/null
+++ b/compute/client_library/ingredients/snapshots/get.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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 typing import Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def get_snapshot(project_id: str, snapshot_name: str) -> compute_v1.Snapshot:
+ """
+ Get information about a Snapshot.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ snapshot_name: the name of the snapshot you want to look up.
+
+ Returns:
+ A Snapshot object.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+
+ return snapshot_client.get(project=project_id, snapshot=snapshot_name)
+#
+
+
diff --git a/compute/client_library/ingredients/snapshots/list.py b/compute/client_library/ingredients/snapshots/list.py
new file mode 100644
index 00000000000..a87b2260169
--- /dev/null
+++ b/compute/client_library/ingredients/snapshots/list.py
@@ -0,0 +1,45 @@
+# Copyright 2022 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 typing import Iterable
+
+from google.cloud import compute_v1
+
+
+#
+def list_snapshots(project_id: str, filter: str = "") -> Iterable[compute_v1.Snapshot]:
+ """
+ List snapshots from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ filter: filter to be applied when listing snapshots. Learn more about filters here:
+ https://cloud.google.com/python/docs/reference/compute/latest/google.cloud.compute_v1.types.ListSnapshotsRequest
+
+ Returns:
+ An iterable containing all Snapshots that match the provided filter.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ request = compute_v1.ListSnapshotsRequest()
+ request.project = project_id
+ request.filter = filter
+
+ return snapshot_client.list(request)
+#
+
diff --git a/compute/client_library/ingredients/usage_report/disable.py b/compute/client_library/ingredients/usage_report/disable.py
new file mode 100644
index 00000000000..4fc7ad6c142
--- /dev/null
+++ b/compute/client_library/ingredients/usage_report/disable.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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 disable_usage_export(project_id: str) -> None:
+ """
+ Disable Compute Engine usage export bucket for the Cloud Project.
+
+ Args:
+ project_id: project ID or project number of the project to update.
+ """
+ projects_client = compute_v1.ProjectsClient()
+
+ # Setting `usage_export_location_resource` to an
+ # empty object will disable the usage report generation.
+ operation = projects_client.set_usage_export_bucket(
+ project=project_id, usage_export_location_resource={}
+ )
+
+ wait_for_extended_operation(operation, "disabling GCE usage bucket")
+#
+
diff --git a/compute/client_library/ingredients/usage_report/get_bucket.py b/compute/client_library/ingredients/usage_report/get_bucket.py
new file mode 100644
index 00000000000..8b5a3b0b4e4
--- /dev/null
+++ b/compute/client_library/ingredients/usage_report/get_bucket.py
@@ -0,0 +1,55 @@
+# Copyright 2022 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 get_usage_export_bucket(project_id: str) -> compute_v1.UsageExportLocation:
+ """
+ Retrieve Compute Engine usage export bucket for the Cloud project.
+ Replaces the empty value returned by the API with the default value used
+ to generate report file names.
+
+ Args:
+ project_id: project ID or project number of the project to update.
+ Returns:
+ UsageExportLocation object describing the current usage export settings
+ for project project_id.
+ """
+ projects_client = compute_v1.ProjectsClient()
+ project_data = projects_client.get(project=project_id)
+
+ uel = project_data.usage_export_location
+
+ if not uel.bucket_name:
+ # The usage reports are disabled.
+ return uel
+
+ if not uel.report_name_prefix:
+ # Although the server sent the empty string value, the next usage report
+ # generated with these settings still has the default prefix value
+ # "usage_gce". (see https://cloud.google.com/compute/docs/reference/rest/v1/projects/get)
+ print(
+ "Report name prefix not set, replacing with default value of "
+ "`usage_gce`."
+ )
+ uel.report_name_prefix = "usage_gce"
+ return uel
+#
+
diff --git a/compute/client_library/ingredients/usage_report/set_bucket.py b/compute/client_library/ingredients/usage_report/set_bucket.py
new file mode 100644
index 00000000000..f308257df33
--- /dev/null
+++ b/compute/client_library/ingredients/usage_report/set_bucket.py
@@ -0,0 +1,58 @@
+# Copyright 2022 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 set_usage_export_bucket(
+ project_id: str, bucket_name: str, report_name_prefix: str = ""
+) -> None:
+ """
+ Set Compute Engine usage export bucket for the Cloud project.
+ This sample presents how to interpret the default value for the
+ report name prefix parameter.
+
+ Args:
+ project_id: project ID or project number of the project to update.
+ bucket_name: Google Cloud Storage bucket used to store Compute Engine
+ usage reports. An existing Google Cloud Storage bucket is required.
+ report_name_prefix: Prefix of the usage report name which defaults to an empty string
+ to showcase default values behaviour.
+ """
+ usage_export_location = compute_v1.UsageExportLocation()
+ usage_export_location.bucket_name = bucket_name
+ usage_export_location.report_name_prefix = report_name_prefix
+
+ if not report_name_prefix:
+ # Sending an empty value for report_name_prefix results in the
+ # next usage report being generated with the default prefix value
+ # "usage_gce". (ref: https://cloud.google.com/compute/docs/reference/rest/v1/projects/setUsageExportBucket)
+ print(
+ "Setting report_name_prefix to empty value causes the report "
+ "to have the default prefix of `usage_gce`."
+ )
+
+ projects_client = compute_v1.ProjectsClient()
+ operation = projects_client.set_usage_export_bucket(
+ project=project_id, usage_export_location_resource=usage_export_location
+ )
+
+ wait_for_extended_operation(operation, "setting GCE usage bucket")
+#
+
diff --git a/compute/client_library/noxfile_config.py b/compute/client_library/noxfile_config.py
new file mode 100644
index 00000000000..13928a8af82
--- /dev/null
+++ b/compute/client_library/noxfile_config.py
@@ -0,0 +1,18 @@
+# Copyright 2022 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.
+
+TEST_CONFIG_OVERRIDE = {
+ # Tests in test_default_values.py require separate projects to not interfere with each other.
+ "gcloud_project_env": "BUILD_SPECIFIC_GCLOUD_PROJECT",
+}
diff --git a/compute/client_library/recipes/__init__.py b/compute/client_library/recipes/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/disks/__init__.py b/compute/client_library/recipes/disks/__init__.py
new file mode 100644
index 00000000000..4bbe0ffdb06
--- /dev/null
+++ b/compute/client_library/recipes/disks/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 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.
diff --git a/compute/client_library/recipes/disks/autodelete_change.py b/compute/client_library/recipes/disks/autodelete_change.py
new file mode 100644
index 00000000000..62465ac913f
--- /dev/null
+++ b/compute/client_library/recipes/disks/autodelete_change.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/clone_encrypted_disk.py b/compute/client_library/recipes/disks/clone_encrypted_disk.py
new file mode 100644
index 00000000000..9ce0ddf795d
--- /dev/null
+++ b/compute/client_library/recipes/disks/clone_encrypted_disk.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/clone_encrypted_disk_managed_key.py b/compute/client_library/recipes/disks/clone_encrypted_disk_managed_key.py
new file mode 100644
index 00000000000..d4b100af644
--- /dev/null
+++ b/compute/client_library/recipes/disks/clone_encrypted_disk_managed_key.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/create_empty_disk.py b/compute/client_library/recipes/disks/create_empty_disk.py
new file mode 100644
index 00000000000..c044d56f326
--- /dev/null
+++ b/compute/client_library/recipes/disks/create_empty_disk.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/create_from_image.py b/compute/client_library/recipes/disks/create_from_image.py
new file mode 100644
index 00000000000..a1aa318af70
--- /dev/null
+++ b/compute/client_library/recipes/disks/create_from_image.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/create_from_snapshot.py b/compute/client_library/recipes/disks/create_from_snapshot.py
new file mode 100644
index 00000000000..efc3e29cc62
--- /dev/null
+++ b/compute/client_library/recipes/disks/create_from_snapshot.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/create_from_source.py b/compute/client_library/recipes/disks/create_from_source.py
new file mode 100644
index 00000000000..f40f561ec3f
--- /dev/null
+++ b/compute/client_library/recipes/disks/create_from_source.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/create_kms_encrypted_disk.py b/compute/client_library/recipes/disks/create_kms_encrypted_disk.py
new file mode 100644
index 00000000000..06cc0d049d7
--- /dev/null
+++ b/compute/client_library/recipes/disks/create_kms_encrypted_disk.py
@@ -0,0 +1,24 @@
+# Copyright 2022 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/delete.py b/compute/client_library/recipes/disks/delete.py
new file mode 100644
index 00000000000..c8bf1d904b3
--- /dev/null
+++ b/compute/client_library/recipes/disks/delete.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/list.py b/compute/client_library/recipes/disks/list.py
new file mode 100644
index 00000000000..3b0e685faaf
--- /dev/null
+++ b/compute/client_library/recipes/disks/list.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/regional_create_from_source.py b/compute/client_library/recipes/disks/regional_create_from_source.py
new file mode 100644
index 00000000000..aceeaa99c9f
--- /dev/null
+++ b/compute/client_library/recipes/disks/regional_create_from_source.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/regional_delete.py b/compute/client_library/recipes/disks/regional_delete.py
new file mode 100644
index 00000000000..986f7dfcda6
--- /dev/null
+++ b/compute/client_library/recipes/disks/regional_delete.py
@@ -0,0 +1,24 @@
+# Copyright 2022 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/firewall/__init__.py b/compute/client_library/recipes/firewall/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/firewall/create.py b/compute/client_library/recipes/firewall/create.py
new file mode 100644
index 00000000000..b4770f78a72
--- /dev/null
+++ b/compute/client_library/recipes/firewall/create.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/firewall/delete.py b/compute/client_library/recipes/firewall/delete.py
new file mode 100644
index 00000000000..ed25d5d1fe0
--- /dev/null
+++ b/compute/client_library/recipes/firewall/delete.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/firewall/list.py b/compute/client_library/recipes/firewall/list.py
new file mode 100644
index 00000000000..fbd0149f667
--- /dev/null
+++ b/compute/client_library/recipes/firewall/list.py
@@ -0,0 +1,21 @@
+# Copyright 2022 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/firewall/main.py b/compute/client_library/recipes/firewall/main.py
new file mode 100644
index 00000000000..c8a4d83b0d3
--- /dev/null
+++ b/compute/client_library/recipes/firewall/main.py
@@ -0,0 +1,58 @@
+# Copyright 2022 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
+
+#
+
+
+#
+
+#
+
+#
+
+#
+
+#
+
+if __name__ == "__main__":
+ import google.auth
+ import google.auth.exceptions
+
+ try:
+ default_project_id = google.auth.default()[1]
+ print(f"Using project {default_project_id}.")
+ except google.auth.exceptions.DefaultCredentialsError:
+ print(
+ "Please use `gcloud auth application-default login` "
+ "or set GOOGLE_APPLICATION_CREDENTIALS to use this script."
+ )
+ else:
+ import uuid
+
+ rule_name = "firewall-sample-" + uuid.uuid4().hex[:10]
+ print(f"Creating firewall rule {rule_name}...")
+ # The rule will be created with default priority of 1000.
+ create_firewall_rule(default_project_id, rule_name)
+ try:
+ print("Rule created:")
+ print(get_firewall_rule(default_project_id, rule_name))
+ print("Updating rule priority to 10...")
+ patch_firewall_priority(default_project_id, rule_name, 10)
+ print("Rule updated: ")
+ print(get_firewall_rule(default_project_id, rule_name))
+ print(f"Deleting rule {rule_name}...")
+ finally:
+ delete_firewall_rule(default_project_id, rule_name)
+ print("Done.")
diff --git a/compute/client_library/recipes/firewall/patch.py b/compute/client_library/recipes/firewall/patch.py
new file mode 100644
index 00000000000..48b7173089e
--- /dev/null
+++ b/compute/client_library/recipes/firewall/patch.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/firewall/windows_kms.py b/compute/client_library/recipes/firewall/windows_kms.py
new file mode 100644
index 00000000000..f0948a35e0c
--- /dev/null
+++ b/compute/client_library/recipes/firewall/windows_kms.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/images/__init__.py b/compute/client_library/recipes/images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/images/create.py b/compute/client_library/recipes/images/create.py
new file mode 100644
index 00000000000..22c0d19cc50
--- /dev/null
+++ b/compute/client_library/recipes/images/create.py
@@ -0,0 +1,24 @@
+# Copyright 2022 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/images/create_from_image.py b/compute/client_library/recipes/images/create_from_image.py
new file mode 100644
index 00000000000..d1e56b4419e
--- /dev/null
+++ b/compute/client_library/recipes/images/create_from_image.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/images/create_from_snapshot.py b/compute/client_library/recipes/images/create_from_snapshot.py
new file mode 100644
index 00000000000..a64f33fe31b
--- /dev/null
+++ b/compute/client_library/recipes/images/create_from_snapshot.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/images/delete.py b/compute/client_library/recipes/images/delete.py
new file mode 100644
index 00000000000..6796dae9eea
--- /dev/null
+++ b/compute/client_library/recipes/images/delete.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/images/get.py b/compute/client_library/recipes/images/get.py
new file mode 100644
index 00000000000..4524e8f2f22
--- /dev/null
+++ b/compute/client_library/recipes/images/get.py
@@ -0,0 +1,28 @@
+# Copyright 2022 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/images/list.py b/compute/client_library/recipes/images/list.py
new file mode 100644
index 00000000000..80d3074cdc3
--- /dev/null
+++ b/compute/client_library/recipes/images/list.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/images/pagination.py b/compute/client_library/recipes/images/pagination.py
new file mode 100644
index 00000000000..aa58b4f86a3
--- /dev/null
+++ b/compute/client_library/recipes/images/pagination.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+
+# Copyright 2022 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.
+
+#
+#
+import google.cloud.compute_v1 as compute_v1
+
+#
+#
+
+
+#
+def print_images_list(project: str) -> str:
+ """
+ Prints a list of all non-deprecated image names available in given project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to list images from.
+
+ Returns:
+ The output as a string.
+ """
+ images_client = compute_v1.ImagesClient()
+ # Listing only non-deprecated images to reduce the size of the reply.
+ images_list_request = compute_v1.ListImagesRequest(
+ project=project, max_results=100, filter="deprecated.state != DEPRECATED"
+ )
+ output = []
+
+ # Although the `max_results` parameter is specified in the request, the iterable returned
+ # by the `list()` method hides the pagination mechanic. The library makes multiple
+ # requests to the API for you, so you can simply iterate over all the images.
+ for img in images_client.list(request=images_list_request):
+ print(f" - {img.name}")
+ output.append(f" - {img.name}")
+ return "\n".join(output)
+
+
+#
+
+
+#
+def print_images_list_by_page(project: str, page_size: int = 10) -> str:
+ """
+ Prints a list of all non-deprecated image names available in a given project,
+ divided into pages as returned by the Compute Engine API.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to list images from.
+ page_size: size of the pages you want the API to return on each call.
+
+ Returns:
+ Output as a string.
+ """
+ images_client = compute_v1.ImagesClient()
+ # Listing only non-deprecated images to reduce the size of the reply.
+ images_list_request = compute_v1.ListImagesRequest(
+ project=project, max_results=page_size, filter="deprecated.state != DEPRECATED"
+ )
+ output = []
+
+ # Use the `pages` attribute of returned iterable to have more granular control of
+ # iteration over paginated results from the API. Each time you want to access the
+ # next page, the library retrieves that page from the API.
+ for page_num, page in enumerate(
+ images_client.list(request=images_list_request).pages, start=1
+ ):
+ print(f"Page {page_num}: ")
+ output.append(f"Page {page_num}: ")
+ for img in page.items:
+ print(f" - {img.name}")
+ output.append(f" - {img.name}")
+ return "\n".join(output)
+
+
+#
+
+
+if __name__ == "__main__":
+ print("=================== Flat list of images ===================")
+ print_images_list("windows-sql-cloud")
+ print("================= Paginated list of images ================")
+ print_images_list_by_page("windows-sql-cloud", 5)
diff --git a/compute/client_library/recipes/images/set_deprecation_status.py b/compute/client_library/recipes/images/set_deprecation_status.py
new file mode 100644
index 00000000000..e92d5254211
--- /dev/null
+++ b/compute/client_library/recipes/images/set_deprecation_status.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/instance_templates/__init__.py b/compute/client_library/recipes/instance_templates/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/instance_templates/create.py b/compute/client_library/recipes/instance_templates/create.py
new file mode 100644
index 00000000000..f3fe9d7971f
--- /dev/null
+++ b/compute/client_library/recipes/instance_templates/create.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instance_templates/create_from_instance.py b/compute/client_library/recipes/instance_templates/create_from_instance.py
new file mode 100644
index 00000000000..2059793f321
--- /dev/null
+++ b/compute/client_library/recipes/instance_templates/create_from_instance.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instance_templates/create_with_subnet.py b/compute/client_library/recipes/instance_templates/create_with_subnet.py
new file mode 100644
index 00000000000..0aeba05dd1f
--- /dev/null
+++ b/compute/client_library/recipes/instance_templates/create_with_subnet.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instance_templates/delete.py b/compute/client_library/recipes/instance_templates/delete.py
new file mode 100644
index 00000000000..8a9743b613b
--- /dev/null
+++ b/compute/client_library/recipes/instance_templates/delete.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instance_templates/get.py b/compute/client_library/recipes/instance_templates/get.py
new file mode 100644
index 00000000000..3036e73488a
--- /dev/null
+++ b/compute/client_library/recipes/instance_templates/get.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instance_templates/list.py b/compute/client_library/recipes/instance_templates/list.py
new file mode 100644
index 00000000000..6ce5c5b73c0
--- /dev/null
+++ b/compute/client_library/recipes/instance_templates/list.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/__init__.py b/compute/client_library/recipes/instances/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/instances/bulk_insert.py b/compute/client_library/recipes/instances/bulk_insert.py
new file mode 100644
index 00000000000..f5bfda9617a
--- /dev/null
+++ b/compute/client_library/recipes/instances/bulk_insert.py
@@ -0,0 +1,40 @@
+# Copyright 2022 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
+
+#
+#
+
+#
+
+#
+
+#
+
+
+def create_five_instances(project_id: str, zone: str, template_name: str,
+ name_pattern: str):
+ """
+ Create five instances of an instance template.
+
+ 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"
+ template_name: name of the template that will be used to create new VMs.
+ name_pattern: The string pattern used for the names of the VMs.
+ """
+ template = get_instance_template(project_id, template_name)
+ instances = bulk_insert_instance(project_id, zone, template, 5, name_pattern)
+ return instances
+#
diff --git a/compute/client_library/recipes/instances/create.py b/compute/client_library/recipes/instances/create.py
new file mode 100644
index 00000000000..3f35a1e4c5a
--- /dev/null
+++ b/compute/client_library/recipes/instances/create.py
@@ -0,0 +1,50 @@
+# Copyright 2022 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
+
+#
+#
+
+#
+
+#
+
+#
+
+#
+#
+
+if __name__ == "__main__":
+ import uuid
+ import google.auth
+ import google.auth.exceptions
+
+ try:
+ default_project_id = google.auth.default()[1]
+ except google.auth.exceptions.DefaultCredentialsError:
+ print(
+ "Please use `gcloud auth application-default login` "
+ "or set GOOGLE_APPLICATION_CREDENTIALS to use this script."
+ )
+ else:
+ instance_name = "quickstart-" + uuid.uuid4().hex[:10]
+ instance_zone = "europe-central2-b"
+
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{instance_zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ create_instance(default_project_id, instance_zone, instance_name, disks)
diff --git a/compute/client_library/recipes/instances/create_start_instance/__init__.py b/compute/client_library/recipes/instances/create_start_instance/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/instances/create_start_instance/create_from_custom_image.py b/compute/client_library/recipes/instances/create_start_instance/create_from_custom_image.py
new file mode 100644
index 00000000000..a093f8cb31c
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_from_custom_image.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/create_start_instance/create_from_public_image.py b/compute/client_library/recipes/instances/create_start_instance/create_from_public_image.py
new file mode 100644
index 00000000000..ac6f2d9bdfb
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_from_public_image.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/create_start_instance/create_from_snapshot.py b/compute/client_library/recipes/instances/create_start_instance/create_from_snapshot.py
new file mode 100644
index 00000000000..34d3e9a9bab
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_from_snapshot.py
@@ -0,0 +1,28 @@
+# Copyright 2022 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/instances/create_start_instance/create_windows_instance.py b/compute/client_library/recipes/instances/create_start_instance/create_windows_instance.py
new file mode 100644
index 00000000000..53f565600e1
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_windows_instance.py
@@ -0,0 +1,33 @@
+# Copyright 2022 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/instances/create_start_instance/create_with_additional_disk.py b/compute/client_library/recipes/instances/create_start_instance/create_with_additional_disk.py
new file mode 100644
index 00000000000..097cc1cf244
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_with_additional_disk.py
@@ -0,0 +1,35 @@
+# Copyright 2022 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/instances/create_start_instance/create_with_existing_disks.py b/compute/client_library/recipes/instances/create_start_instance/create_with_existing_disks.py
new file mode 100644
index 00000000000..dfa59eb3646
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_with_existing_disks.py
@@ -0,0 +1,29 @@
+# Copyright 2022 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/instances/create_start_instance/create_with_local_ssd.py b/compute/client_library/recipes/instances/create_start_instance/create_with_local_ssd.py
new file mode 100644
index 00000000000..853a58e1686
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_with_local_ssd.py
@@ -0,0 +1,32 @@
+# Copyright 2022 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/instances/create_start_instance/create_with_snapshotted_data_disk.py b/compute/client_library/recipes/instances/create_start_instance/create_with_snapshotted_data_disk.py
new file mode 100644
index 00000000000..19aec72dc13
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_start_instance/create_with_snapshotted_data_disk.py
@@ -0,0 +1,35 @@
+# Copyright 2022 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/instances/create_with_subnet.py b/compute/client_library/recipes/instances/create_with_subnet.py
new file mode 100644
index 00000000000..989e6448360
--- /dev/null
+++ b/compute/client_library/recipes/instances/create_with_subnet.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/custom_hostname/create.py b/compute/client_library/recipes/instances/custom_hostname/create.py
new file mode 100644
index 00000000000..588c603827b
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_hostname/create.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/custom_hostname/get.py b/compute/client_library/recipes/instances/custom_hostname/get.py
new file mode 100644
index 00000000000..d69cce045fa
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_hostname/get.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/custom_machine_types/__init__.py b/compute/client_library/recipes/instances/custom_machine_types/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/instances/custom_machine_types/create_shared_with_helper.py b/compute/client_library/recipes/instances/custom_machine_types/create_shared_with_helper.py
new file mode 100644
index 00000000000..a4d355d4dd8
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_machine_types/create_shared_with_helper.py
@@ -0,0 +1,34 @@
+# Copyright 2022 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/instances/custom_machine_types/create_with_helper.py b/compute/client_library/recipes/instances/custom_machine_types/create_with_helper.py
new file mode 100644
index 00000000000..8619e68227f
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_machine_types/create_with_helper.py
@@ -0,0 +1,36 @@
+# Copyright 2022 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/instances/custom_machine_types/create_without_helper.py b/compute/client_library/recipes/instances/custom_machine_types/create_without_helper.py
new file mode 100644
index 00000000000..6b99722f296
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_machine_types/create_without_helper.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/custom_machine_types/extra_mem_no_helper.py b/compute/client_library/recipes/instances/custom_machine_types/extra_mem_no_helper.py
new file mode 100644
index 00000000000..3b998965d70
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_machine_types/extra_mem_no_helper.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/custom_machine_types/helper_class.py b/compute/client_library/recipes/instances/custom_machine_types/helper_class.py
new file mode 100644
index 00000000000..e5a48c0c962
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_machine_types/helper_class.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/custom_machine_types/update_memory.py b/compute/client_library/recipes/instances/custom_machine_types/update_memory.py
new file mode 100644
index 00000000000..4d6b2ced889
--- /dev/null
+++ b/compute/client_library/recipes/instances/custom_machine_types/update_memory.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instances/delete.py b/compute/client_library/recipes/instances/delete.py
new file mode 100644
index 00000000000..99cff9e259c
--- /dev/null
+++ b/compute/client_library/recipes/instances/delete.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/instances/delete_protection/__init__.py b/compute/client_library/recipes/instances/delete_protection/__init__.py
new file mode 100644
index 00000000000..a3ded82a3b6
--- /dev/null
+++ b/compute/client_library/recipes/instances/delete_protection/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 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/instances/delete_protection/create.py b/compute/client_library/recipes/instances/delete_protection/create.py
new file mode 100644
index 00000000000..c45d757eae1
--- /dev/null
+++ b/compute/client_library/recipes/instances/delete_protection/create.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/delete_protection/get.py b/compute/client_library/recipes/instances/delete_protection/get.py
new file mode 100644
index 00000000000..1d7697dd103
--- /dev/null
+++ b/compute/client_library/recipes/instances/delete_protection/get.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/delete_protection/set.py b/compute/client_library/recipes/instances/delete_protection/set.py
new file mode 100644
index 00000000000..ebd5b5970a3
--- /dev/null
+++ b/compute/client_library/recipes/instances/delete_protection/set.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instances/from_instance_template/__init__.py b/compute/client_library/recipes/instances/from_instance_template/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/instances/from_instance_template/create_from_template.py b/compute/client_library/recipes/instances/from_instance_template/create_from_template.py
new file mode 100644
index 00000000000..9a07cd10c36
--- /dev/null
+++ b/compute/client_library/recipes/instances/from_instance_template/create_from_template.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instances/from_instance_template/create_from_template_with_overrides.py b/compute/client_library/recipes/instances/from_instance_template/create_from_template_with_overrides.py
new file mode 100644
index 00000000000..c4d5ca1094f
--- /dev/null
+++ b/compute/client_library/recipes/instances/from_instance_template/create_from_template_with_overrides.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instances/get.py b/compute/client_library/recipes/instances/get.py
new file mode 100644
index 00000000000..2dcc5c8849e
--- /dev/null
+++ b/compute/client_library/recipes/instances/get.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/list.py b/compute/client_library/recipes/instances/list.py
new file mode 100644
index 00000000000..92aff46b1f5
--- /dev/null
+++ b/compute/client_library/recipes/instances/list.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/list_all.py b/compute/client_library/recipes/instances/list_all.py
new file mode 100644
index 00000000000..e1fafd7f2aa
--- /dev/null
+++ b/compute/client_library/recipes/instances/list_all.py
@@ -0,0 +1,20 @@
+# Copyright 2022 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/instances/preemptible/__init__.py b/compute/client_library/recipes/instances/preemptible/__init__.py
new file mode 100644
index 00000000000..a3ded82a3b6
--- /dev/null
+++ b/compute/client_library/recipes/instances/preemptible/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 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/instances/preemptible/create_preemptible.py b/compute/client_library/recipes/instances/preemptible/create_preemptible.py
new file mode 100644
index 00000000000..18791d35d5e
--- /dev/null
+++ b/compute/client_library/recipes/instances/preemptible/create_preemptible.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/preemptible/is_preemptible.py b/compute/client_library/recipes/instances/preemptible/is_preemptible.py
new file mode 100644
index 00000000000..d57031c83fe
--- /dev/null
+++ b/compute/client_library/recipes/instances/preemptible/is_preemptible.py
@@ -0,0 +1,21 @@
+# Copyright 2022 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/instances/preemptible/preemption_history.py b/compute/client_library/recipes/instances/preemptible/preemption_history.py
new file mode 100644
index 00000000000..0c9a9a8ce6d
--- /dev/null
+++ b/compute/client_library/recipes/instances/preemptible/preemption_history.py
@@ -0,0 +1,24 @@
+# Copyright 2022 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/instances/reset.py b/compute/client_library/recipes/instances/reset.py
new file mode 100644
index 00000000000..ce09f66e79b
--- /dev/null
+++ b/compute/client_library/recipes/instances/reset.py
@@ -0,0 +1,22 @@
+# Copyright 2022 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/instances/resume.py b/compute/client_library/recipes/instances/resume.py
new file mode 100644
index 00000000000..4cc8d7ef5d9
--- /dev/null
+++ b/compute/client_library/recipes/instances/resume.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/instances/spot/__init__.py b/compute/client_library/recipes/instances/spot/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/instances/spot/create.py b/compute/client_library/recipes/instances/spot/create.py
new file mode 100644
index 00000000000..fba16e4ce8a
--- /dev/null
+++ b/compute/client_library/recipes/instances/spot/create.py
@@ -0,0 +1,31 @@
+# Copyright 2022 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/instances/spot/is_spot_vm.py b/compute/client_library/recipes/instances/spot/is_spot_vm.py
new file mode 100644
index 00000000000..ea613650ab0
--- /dev/null
+++ b/compute/client_library/recipes/instances/spot/is_spot_vm.py
@@ -0,0 +1,21 @@
+# Copyright 2022 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/instances/start.py b/compute/client_library/recipes/instances/start.py
new file mode 100644
index 00000000000..913680aa9fa
--- /dev/null
+++ b/compute/client_library/recipes/instances/start.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/instances/start_encrypted.py b/compute/client_library/recipes/instances/start_encrypted.py
new file mode 100644
index 00000000000..d6a0194bd5a
--- /dev/null
+++ b/compute/client_library/recipes/instances/start_encrypted.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/instances/stop.py b/compute/client_library/recipes/instances/stop.py
new file mode 100644
index 00000000000..49abe79a036
--- /dev/null
+++ b/compute/client_library/recipes/instances/stop.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/instances/suspend.py b/compute/client_library/recipes/instances/suspend.py
new file mode 100644
index 00000000000..59c5b75f091
--- /dev/null
+++ b/compute/client_library/recipes/instances/suspend.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/operations/__init__.py b/compute/client_library/recipes/operations/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/operations/operation_check.py b/compute/client_library/recipes/operations/operation_check.py
new file mode 100644
index 00000000000..8913e7324cc
--- /dev/null
+++ b/compute/client_library/recipes/operations/operation_check.py
@@ -0,0 +1,33 @@
+# Copyright 2022 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
+#
+# 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/routes/create.py b/compute/client_library/recipes/routes/create.py
new file mode 100644
index 00000000000..6862af01252
--- /dev/null
+++ b/compute/client_library/recipes/routes/create.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/routes/create_kms_route.py b/compute/client_library/recipes/routes/create_kms_route.py
new file mode 100644
index 00000000000..ec693259e97
--- /dev/null
+++ b/compute/client_library/recipes/routes/create_kms_route.py
@@ -0,0 +1,57 @@
+# Copyright 2022 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.
+#
+# 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
+
+#
+#
+
+#
+
+#
+
+def create_route_to_windows_activation_host(project_id: str, network: str, route_name: str) -> compute_v1.Route:
+ """
+ If you have Windows instances without external IP addresses,
+ you must also enable Private Google Access so that instances
+ with only internal IP addresses can send traffic to the external
+ IP address for kms.windows.googlecloud.com.
+ More infromation: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ network: name of the network the route will be created in. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+ route_name: name of the new route.
+
+ Returns:
+ A new compute_v1.Route object.
+ """
+ return create_route(project_id=project_id, network=network, route_name=route_name,
+ destination_range='35.190.247.13/32',
+ next_hop_gateway=f"projects/{project_id}/global/gateways/default-internet-gateway")
+#
diff --git a/compute/client_library/recipes/routes/delete.py b/compute/client_library/recipes/routes/delete.py
new file mode 100644
index 00000000000..5ea48302b4b
--- /dev/null
+++ b/compute/client_library/recipes/routes/delete.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/routes/list.py b/compute/client_library/recipes/routes/list.py
new file mode 100644
index 00000000000..3869f571b9c
--- /dev/null
+++ b/compute/client_library/recipes/routes/list.py
@@ -0,0 +1,21 @@
+# Copyright 2022 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/snapshots/__init__.py b/compute/client_library/recipes/snapshots/__init__.py
new file mode 100644
index 00000000000..4bbe0ffdb06
--- /dev/null
+++ b/compute/client_library/recipes/snapshots/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 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.
diff --git a/compute/client_library/recipes/snapshots/create.py b/compute/client_library/recipes/snapshots/create.py
new file mode 100644
index 00000000000..5b54951b1d5
--- /dev/null
+++ b/compute/client_library/recipes/snapshots/create.py
@@ -0,0 +1,24 @@
+# Copyright 2022 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/snapshots/delete.py b/compute/client_library/recipes/snapshots/delete.py
new file mode 100644
index 00000000000..a9686d6d3c3
--- /dev/null
+++ b/compute/client_library/recipes/snapshots/delete.py
@@ -0,0 +1,23 @@
+# Copyright 2022 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/snapshots/delete_by_filter.py b/compute/client_library/recipes/snapshots/delete_by_filter.py
new file mode 100644
index 00000000000..d250c37a9d5
--- /dev/null
+++ b/compute/client_library/recipes/snapshots/delete_by_filter.py
@@ -0,0 +1,37 @@
+# Copyright 2022 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
+
+#
+#
+
+#
+
+#
+
+#
+
+def delete_snapshots_by_filter(project_id: str, filter: str):
+ """
+ Deletes all snapshots in project that meet the filter criteria.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ filter: filter to be applied when looking for snapshots for deletion.
+ """
+ for snapshot in list_snapshots(project_id, filter):
+ delete_snapshot(project_id, snapshot.name)
+
+#
\ No newline at end of file
diff --git a/compute/client_library/recipes/snapshots/get.py b/compute/client_library/recipes/snapshots/get.py
new file mode 100644
index 00000000000..bb0a324aeb9
--- /dev/null
+++ b/compute/client_library/recipes/snapshots/get.py
@@ -0,0 +1,21 @@
+# Copyright 2022 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/snapshots/list.py b/compute/client_library/recipes/snapshots/list.py
new file mode 100644
index 00000000000..34b5701da86
--- /dev/null
+++ b/compute/client_library/recipes/snapshots/list.py
@@ -0,0 +1,21 @@
+# Copyright 2022 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/usage_report/__init__.py b/compute/client_library/recipes/usage_report/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/recipes/usage_report/usage_reports.py b/compute/client_library/recipes/usage_report/usage_reports.py
new file mode 100644
index 00000000000..d153b48d9e3
--- /dev/null
+++ b/compute/client_library/recipes/usage_report/usage_reports.py
@@ -0,0 +1,51 @@
+# Copyright 2022 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
+"""
+A sample script showing how to handle default values when communicating
+with the Compute Engine API and how to configure usage reports using the API.
+"""
+#
+#
+#
+#
+#
+
+#
+#
+#
+
+
+#
+
+#
+
+#
+
+#
+
+#
+#
+
+#
+#
+
+
+#
+
+#
+
+#
+
+#
diff --git a/compute/client_library/requirements-test.txt b/compute/client_library/requirements-test.txt
new file mode 100644
index 00000000000..1a35c12716c
--- /dev/null
+++ b/compute/client_library/requirements-test.txt
@@ -0,0 +1,6 @@
+pytest==7.2.0
+pytest-parallel==0.1.1
+flaky==3.7.0
+google-cloud-storage==2.5.0
+google-cloud-kms==2.12.3
+py==1.11.0
diff --git a/compute/client_library/requirements.txt b/compute/client_library/requirements.txt
new file mode 100644
index 00000000000..0de8929bd5e
--- /dev/null
+++ b/compute/client_library/requirements.txt
@@ -0,0 +1,3 @@
+isort==5.10.1
+black==22.10.0
+google-cloud-compute==1.6.1
diff --git a/compute/client_library/sgs.py b/compute/client_library/sgs.py
new file mode 100644
index 00000000000..e3fa70a8b94
--- /dev/null
+++ b/compute/client_library/sgs.py
@@ -0,0 +1,350 @@
+# Copyright 2021 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 script is used to generate the full code samples inside the `snippets`
+directory, to be then used in Google Compute Engine public documentation.
+"""
+import argparse
+import ast
+from collections import defaultdict
+from dataclasses import dataclass
+from dataclasses import field
+import glob
+import os
+from pathlib import Path
+import re
+import subprocess
+from typing import List, Tuple
+import warnings
+
+import isort
+
+INGREDIENTS_START = re.compile(r"\s*#\s*")
+INGREDIENTS_END = re.compile(r"\s*#\s*")
+
+IMPORTS_FILL = re.compile(r"\s*#\s*")
+INGREDIENT_FILL = re.compile(r"\s*#\s*")
+
+REGION_START = re.compile(r"#\s*")
+REGION_END = re.compile(r"#\s*")
+
+HEADER = """\
+# 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.
+"""
+
+DEFAULT_OUTPUT_PATH = Path("snippets")
+INGREDIENTS_PATH = Path("ingredients")
+RECIPES_PATH = Path("recipes")
+
+
+@dataclass
+class ImportItem:
+ """
+ Represents a single import item in a script, created either by
+ `import something as something_else` or
+ `from module import something as something_else`.
+ """
+
+ name: str
+ asname: str
+
+ def __hash__(self):
+ return hash(f"{self.name} as {self.asname}")
+
+
+@dataclass
+class Ingredient:
+ """
+ This class represents a piece of code that can be used as part of a code snippet.
+ Each ingredient has a name. It is made of a list of imports that it'll require and
+ text that will be pasted into the snippet.
+ """
+
+ simple_imports: List[ImportItem] = field(default_factory=list)
+ imports_from: List[Tuple[str, ImportItem]] = field(default_factory=list)
+ text: str = ""
+ name: str = ""
+
+ def __repr__(self):
+ return f""
+
+
+IGNORED_OUTPUT_FILES = (
+ re.compile(r".*noxfile\.py$"),
+ re.compile(r".*noxfile_config\.py$"),
+ re.compile(r".*README\.md$"),
+ re.compile(r".*requirements\.txt$"),
+ re.compile(r".*requirements-test\.txt$"),
+ re.compile(r".*?/tests/.*"),
+ re.compile(r".*?/__pycache__/.*"),
+ re.compile(r".*?sponge_log.xml.*"),
+)
+
+
+def parse_imports(script: str) -> Tuple[List[ImportItem], List[Tuple[str, ImportItem]]]:
+ """
+ Reads a Python script file and analyzes it to extract information
+ about the various things it imports. Returns a pair of lists containing
+ information about the "simple imports" (`import abc as xyz`) and "imports from"
+ (`from collections import deque as ...`).
+ """
+ parsed_script = ast.parse(script)
+ simple_imports = []
+ imports_from = []
+ for node in parsed_script.body:
+ if isinstance(node, ast.Import):
+ for alias in node.names:
+ simple_imports.append(ImportItem(name=alias.name, asname=alias.asname))
+ elif isinstance(node, ast.ImportFrom):
+ for alias in node.names:
+ imports_from.append(
+ (node.module, ImportItem(name=alias.name, asname=alias.asname))
+ )
+ return simple_imports, imports_from
+
+
+def load_ingredient(path: Path) -> Ingredient:
+ ingredient_lines = []
+ in_ingredient = False
+ ingredient_name = ""
+ with path.open() as file:
+ file_content = file.read()
+ # Read imports
+ simple_imports, imports_from = parse_imports(file_content)
+ # Read the script
+ for line in file_content.splitlines(keepends=True):
+ if in_ingredient and INGREDIENTS_END.match(line):
+ break
+ elif in_ingredient:
+ ingredient_lines.append(line)
+ elif INGREDIENTS_START.match(line):
+ ingredient_name = INGREDIENTS_START.match(line).group(1)
+ in_ingredient = True
+ else:
+ if in_ingredient:
+ warnings.warn(
+ f"The ingredient in {path} has no closing tag.", SyntaxWarning
+ )
+ return Ingredient(
+ name=ingredient_name,
+ text="".join(ingredient_lines),
+ simple_imports=simple_imports,
+ imports_from=imports_from,
+ )
+
+
+def load_ingredients(path: Path) -> dict:
+ ingredients = {}
+ for ipath in path.iterdir():
+ if ipath.is_dir():
+ ingredients.update(load_ingredients(ipath))
+ elif ipath.is_file():
+ if "__pycache__" in str(ipath.absolute()):
+ continue
+ ingredient = load_ingredient(ipath)
+ ingredients[ingredient.name] = ingredient
+ return ingredients
+
+
+def load_recipe(path: Path) -> str:
+ with path.open() as file:
+ return file.read()
+
+
+def load_recipes(path: Path) -> dict:
+ recipes = {}
+ for ipath in path.iterdir():
+ if ipath.is_dir():
+ recipes.update(load_recipes(ipath))
+ elif ipath.is_file():
+ recipes[ipath.absolute()] = load_recipe(ipath)
+ return recipes
+
+
+def render_recipe(recipe: str, ingredients: dict) -> str:
+ """
+ Replace all `# IMPORTS` and `# INGREDIENT ` occurrences in
+ the provided recipe, producing a script ready to be saved to a file.
+ """
+ ingredients_used = []
+ file_lines = recipe.splitlines()
+
+ # Scan the file to used ingredients
+ for line in file_lines:
+ match = INGREDIENT_FILL.match(line)
+ if match:
+ ingredients_used.append(ingredients[match.group(1)])
+
+ simple_imports_used = set()
+ for ingredient in ingredients_used:
+ for simple_import in ingredient.simple_imports:
+ simple_imports_used.add(simple_import)
+
+ from_imports_used = defaultdict(set)
+ for ingredient in ingredients_used:
+ for import_from in ingredient.imports_from:
+ from_imports_used[import_from[0]].add(import_from[1])
+
+ import_lines = set()
+ for simple_import in simple_imports_used:
+ if simple_import.asname:
+ import_lines.add(f"import {simple_import.name} as {simple_import.asname}")
+ else:
+ import_lines.add(f"import {simple_import.name}")
+
+ for module, from_imports in from_imports_used.items():
+ names = set()
+ for from_import in from_imports:
+ if from_import.asname:
+ name = f"{from_import.name} as {from_import.asname}"
+ else:
+ name = from_import.name
+ names.add(name)
+ names = ", ".join(names)
+ import_lines.add(f"from {module} import {names}")
+
+ import_lines = isort.code(
+ "\n".join(import_lines), config=isort.Config(profile="google")
+ )
+
+ output_file = []
+ header_added = False
+ for line in file_lines:
+
+ if IMPORTS_FILL.search(line):
+ output_file.append(import_lines)
+ elif INGREDIENT_FILL.search(line):
+ match = INGREDIENT_FILL.search(line)
+ output_file.append(ingredients[match.group(1)].text)
+ elif REGION_START.search(line):
+ # The string has to be broken up, so that the snippet
+ # machine doesn't recognize it as a valid start of a region
+ output_file.append(REGION_START.sub("# [" + "START \\1]", line))
+ elif REGION_END.search(line):
+ # The string has to be broken up, so that the snippet
+ # machine doesn't recognize it as a valid start of a region
+ output_file.append(REGION_END.sub("# [" + "END \\1]", line))
+ else:
+ output_file.append(line)
+ continue
+ if not header_added:
+ end = output_file[-1]
+ output_file[-1] = ""
+ output_file.append(HEADER)
+ output_file.append("")
+ output_file.append(end)
+ header_added = True
+
+ if output_file and not output_file[-1].endswith("\n"):
+ output_file.append("")
+
+ return os.linesep.join(output_file)
+
+
+def save_rendered_recipe(
+ recipe_path: Path,
+ rendered_recipe: str,
+ output_dir: Path = DEFAULT_OUTPUT_PATH,
+ recipes_path: Path = RECIPES_PATH,
+) -> Path:
+ output_dir.mkdir(parents=True, exist_ok=True)
+ output_path = output_dir / recipe_path.relative_to(recipes_path)
+ output_path.parent.mkdir(parents=True, exist_ok=True)
+
+ with output_path.open(mode="w") as out_file:
+ out_file.write(rendered_recipe)
+
+ subprocess.run(
+ ["black", str(output_path)],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ return output_path
+
+
+def generate(
+ args: argparse.Namespace,
+ ingredients_path: Path = INGREDIENTS_PATH,
+ recipes_path: Path = RECIPES_PATH,
+):
+ ingredients = load_ingredients(ingredients_path)
+ recipes = load_recipes(recipes_path)
+
+ updated_paths = set()
+
+ for path, recipe in recipes.items():
+ rendered = render_recipe(recipe, ingredients)
+ out = save_rendered_recipe(
+ path.absolute(),
+ rendered,
+ recipes_path=recipes_path.absolute(),
+ output_dir=Path(args.output_dir),
+ )
+ updated_paths.add(str(out))
+
+ print("Generated files:")
+ for file in sorted(updated_paths):
+ print(f" - {repr(file)}")
+
+ all_files = glob.glob(f"{args.output_dir}/**", recursive=True)
+ unknown_files = set()
+ for file in all_files:
+ if file in updated_paths:
+ continue
+ if any(pattern.match(file) for pattern in IGNORED_OUTPUT_FILES):
+ continue
+ pfile = Path(file)
+ if pfile.is_dir() and pfile.iterdir():
+ # Don't report non-empty dirs.
+ continue
+ unknown_files.add(file)
+
+ if unknown_files:
+ print("Found following unknown files: ")
+ for file in sorted(unknown_files):
+ print(f" - {repr(file)}")
+
+
+def verify(args: argparse.Namespace):
+ # TODO: Needs to check if the files are up to date. Will be used to auto-check every commit.
+ pass
+
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(
+ description="Generates full code snippets from their recipes."
+ )
+ subparsers = parser.add_subparsers()
+
+ gen_parser = subparsers.add_parser("generate", help="Generates the code samples.")
+ gen_parser.set_defaults(func=generate)
+ gen_parser.add_argument("--output_dir", default=DEFAULT_OUTPUT_PATH)
+
+ verify_parser = subparsers.add_parser(
+ "verify", help="Verify if the generated samples match the sources."
+ )
+ verify_parser.set_defaults(func=verify)
+
+ return parser.parse_args()
+
+
+def main():
+ args = parse_arguments()
+ args.func(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compute/client_library/sgs_test_fixtures/ingredients/ingredient1.pytest b/compute/client_library/sgs_test_fixtures/ingredients/ingredient1.pytest
new file mode 100644
index 00000000000..c547d737447
--- /dev/null
+++ b/compute/client_library/sgs_test_fixtures/ingredients/ingredient1.pytest
@@ -0,0 +1,30 @@
+# Copyright 2022 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.
+
+from collections import defaultdict
+from functools import reduce
+import pprint
+
+
+#
+def some_function(a: int, b: str) -> defaultdict:
+ """
+ Do something with a and b that will give a defaultdict.
+ """
+ out = defaultdict(int)
+ for letter in b:
+ out[letter] += a * ord(letter) + reduce(lambda x, y: x+ord(y), b, 0)
+ pprint.pprint(out)
+ return out
+# \
diff --git a/compute/client_library/sgs_test_fixtures/ingredients/ingredient2.pytest b/compute/client_library/sgs_test_fixtures/ingredients/ingredient2.pytest
new file mode 100644
index 00000000000..3b50b3b62b3
--- /dev/null
+++ b/compute/client_library/sgs_test_fixtures/ingredients/ingredient2.pytest
@@ -0,0 +1,31 @@
+# Copyright 2022 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.
+
+from collections import Counter
+from functools import cache
+from functools import reduce
+
+
+#
+@cache
+def other_function(word: str, number: int) -> Counter:
+ """
+ Do something with the arguments. I don't care what.
+ """
+ new_word = reduce(lambda s1, s2: s1 + s2 + s2, word, '')
+ letters = Counter(new_word)
+ for letter in word:
+ letters.update({letter: number*ord(letter)})
+ return letters
+#
diff --git a/compute/client_library/sgs_test_fixtures/output/experimental_recipe.pytest b/compute/client_library/sgs_test_fixtures/output/experimental_recipe.pytest
new file mode 100644
index 00000000000..f8846776f88
--- /dev/null
+++ b/compute/client_library/sgs_test_fixtures/output/experimental_recipe.pytest
@@ -0,0 +1,60 @@
+# Copyright 2022 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 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.
+
+
+from collections import Counter
+from collections import defaultdict
+from functools import cache
+from functools import reduce
+import pprint
+
+
+def some_function(a: int, b: str) -> defaultdict:
+ """
+ Do something with a and b that will give a defaultdict.
+ """
+ out = defaultdict(int)
+ for letter in b:
+ out[letter] += a * ord(letter) + reduce(lambda x, y: x + ord(y), b, 0)
+ pprint.pprint(out)
+ return out
+
+
+# I can have some random things between ingredients
+def test():
+ print("This is a test. The only thing I shouldn't place in recipes is imports.")
+
+
+@cache
+def other_function(word: str, number: int) -> Counter:
+ """
+ Do something with the arguments. I don't care what.
+ """
+ new_word = reduce(lambda s1, s2: s1 + s2 + s2, word, "")
+ letters = Counter(new_word)
+ for letter in word:
+ letters.update({letter: number * ord(letter)})
+ return letters
+
+
+if __name__ == "__main__":
+ print("Here is an example of two functions:")
+ some_function(14, "google")
+ other_function("google", 9001)
+ print("That's it :)")
diff --git a/compute/client_library/sgs_test_fixtures/recipes/experimental_recipe.pytest b/compute/client_library/sgs_test_fixtures/recipes/experimental_recipe.pytest
new file mode 100644
index 00000000000..6f384ed4f79
--- /dev/null
+++ b/compute/client_library/sgs_test_fixtures/recipes/experimental_recipe.pytest
@@ -0,0 +1,29 @@
+# Copyright 2022 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.
+
+#
+
+#
+
+# I can have some random things between ingredients
+def test():
+ print("This is a test. The only thing I shouldn't place in recipes is imports.")
+
+#
+
+if __name__ == '__main__':
+ print("Here is an example of two functions:")
+ some_function(14, "google")
+ other_function("google", 9001)
+ print("That's it :)")
diff --git a/compute/client_library/snippets/README.md b/compute/client_library/snippets/README.md
new file mode 100644
index 00000000000..64d0ec565fd
--- /dev/null
+++ b/compute/client_library/snippets/README.md
@@ -0,0 +1,31 @@
+# google-cloud-compute library samples
+
+These samples demonstrate usage of the google-cloud-compute library to interact
+with the Google Compute Engine API.
+
+## Running the quickstart script
+
+### Before you begin
+
+1. If you haven't already, set up a Python Development Environment by following the [python setup guide](https://cloud.google.com/python/setup) and
+[create a project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project).
+
+1. Use `gcloud auth application-default login` to allow the script to authenticate using
+your credentials to the Google Cloud APIs.
+
+### Install requirements
+
+Create a new virtual environment and install the required libraries.
+```bash
+virtualenv --python python3 name-of-your-virtualenv
+source name-of-your-virtualenv/bin/activate
+pip install -r ../requirements.txt
+```
+
+### Run the demo
+
+Run the quickstart script, it will create and destroy a `n1-standard-1`
+type machine in the `europe-central2-b` zone.
+```bash
+python quickstart.py
+```
diff --git a/compute/client_library/snippets/__init__.py b/compute/client_library/snippets/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/disks/__init__.py b/compute/client_library/snippets/disks/__init__.py
new file mode 100644
index 00000000000..4bbe0ffdb06
--- /dev/null
+++ b/compute/client_library/snippets/disks/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 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.
diff --git a/compute/client_library/snippets/disks/autodelete_change.py b/compute/client_library/snippets/disks/autodelete_change.py
new file mode 100644
index 00000000000..bb903a7268b
--- /dev/null
+++ b/compute/client_library/snippets/disks/autodelete_change.py
@@ -0,0 +1,116 @@
+# Copyright 2022 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_autodelete_change]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 set_disk_autodelete(
+ project_id: str, zone: str, instance_name: str, disk_name: str, autodelete: bool
+) -> NoReturn:
+ """
+ Set the autodelete flag of a disk to given value.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which is the disk you want to modify.
+ instance_name: name of the instance the disk is attached to.
+ disk_name: the name of the disk which flag you want to modify.
+ autodelete: the new value of the autodelete flag.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ for disk in instance.disks:
+ if disk.device_name == disk_name:
+ break
+ else:
+ raise RuntimeError(
+ f"Instance {instance_name} doesn't have a disk named {disk_name} attached."
+ )
+
+ disk.auto_delete = autodelete
+
+ operation = instance_client.update(
+ project=project_id,
+ zone=zone,
+ instance=instance_name,
+ instance_resource=instance,
+ )
+
+ wait_for_extended_operation(operation, "disk update")
+ return
+
+
+# [END compute_disk_autodelete_change]
diff --git a/compute/client_library/snippets/disks/clone_encrypted_disk.py b/compute/client_library/snippets/disks/clone_encrypted_disk.py
new file mode 100644
index 00000000000..5850656e2b8
--- /dev/null
+++ b/compute/client_library/snippets/disks/clone_encrypted_disk.py
@@ -0,0 +1,124 @@
+# Copyright 2022 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_clone_encrypted_disk]
+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:
+ """
+ This method will wait 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_disk_from_customer_encrypted_disk(
+ project_id: str,
+ zone: str,
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ disk_link: str,
+ encryption_key: bytes,
+) -> compute_v1.Disk:
+ """
+ Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk.
+
+ The encryption key must be the same for the source disk and the new disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ encryption_key: customer-supplied encryption key used for encrypting
+ data in the source disk. The data will be encrypted with the same key
+ in the new disk.
+
+ Returns:
+ An attachable copy of an existing disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_disk = disk_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.raw_key = encryption_key
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+
+# [END compute_disk_clone_encrypted_disk]
diff --git a/compute/client_library/snippets/disks/clone_encrypted_disk_managed_key.py b/compute/client_library/snippets/disks/clone_encrypted_disk_managed_key.py
new file mode 100644
index 00000000000..8f89d191be5
--- /dev/null
+++ b/compute/client_library/snippets/disks/clone_encrypted_disk_managed_key.py
@@ -0,0 +1,125 @@
+# Copyright 2022 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_clone_encrypted_disk_kms]
+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:
+ """
+ This method will wait 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_disk_from_kms_encrypted_disk(
+ project_id: str,
+ zone: str,
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ disk_link: str,
+ kms_key_name: str,
+) -> compute_v1.Disk:
+ """
+ Creates a zonal non-boot disk in a project with the copy of data from an existing disk.
+
+ The encryption key must be the same for the source disk and the new disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ kms_key_name: URL of the key from KMS. The key might be from another project, as
+ long as you have access to it. The data will be encrypted with the same key
+ in the new disk. This value uses following format:
+ "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}"
+
+ Returns:
+ An attachable copy of an existing disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_disk = disk_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.kms_key_name = kms_key_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+
+# [END compute_disk_clone_encrypted_disk_kms]
diff --git a/compute/client_library/snippets/disks/create_empty_disk.py b/compute/client_library/snippets/disks/create_empty_disk.py
new file mode 100644
index 00000000000..331f0b21283
--- /dev/null
+++ b/compute/client_library/snippets/disks/create_empty_disk.py
@@ -0,0 +1,109 @@
+# Copyright 2022 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_create_empty_disk]
+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:
+ """
+ This method will wait 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_empty_disk(
+ project_id: str, zone: str, disk_name: str, disk_type: str, disk_size_gb: int
+) -> compute_v1.Disk:
+ """
+ Creates a new empty disk in a project in given zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+
+ Returns:
+ An unattached Disk instance.
+ """
+ disk = compute_v1.Disk()
+ disk.size_gb = disk_size_gb
+ disk.name = disk_name
+ disk.zone = zone
+ disk.type_ = disk_type
+
+ disk_client = compute_v1.DisksClient()
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk.name)
+
+
+# [END compute_disk_create_empty_disk]
diff --git a/compute/client_library/snippets/disks/create_from_image.py b/compute/client_library/snippets/disks/create_from_image.py
new file mode 100644
index 00000000000..0b8e17ea060
--- /dev/null
+++ b/compute/client_library/snippets/disks/create_from_image.py
@@ -0,0 +1,118 @@
+# Copyright 2022 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_create_from_image]
+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:
+ """
+ This method will wait 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_disk_from_image(
+ project_id: str,
+ zone: str,
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ source_image: str,
+) -> compute_v1.Disk:
+ """
+ Creates a new disk in a project in given zone using an image as base.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ source_image: source image to use when creating this disk. You must have read access to this disk. This
+ can be one of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+
+ Returns:
+ An unattached Disk instance.
+ """
+ disk = compute_v1.Disk()
+ disk.size_gb = disk_size_gb
+ disk.name = disk_name
+ disk.zone = zone
+ disk.type_ = disk_type
+ disk.source_image = source_image
+
+ disk_client = compute_v1.DisksClient()
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk.name)
+
+
+# [END compute_disk_create_from_image]
diff --git a/compute/client_library/snippets/disks/create_from_snapshot.py b/compute/client_library/snippets/disks/create_from_snapshot.py
new file mode 100644
index 00000000000..deb0f9dfd5f
--- /dev/null
+++ b/compute/client_library/snippets/disks/create_from_snapshot.py
@@ -0,0 +1,116 @@
+# Copyright 2022 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_create_from_snapshot]
+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:
+ """
+ This method will wait 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_disk_from_snapshot(
+ project_id: str,
+ zone: str,
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ snapshot_link: str,
+) -> compute_v1.Disk:
+ """
+ Creates a new disk in a project in given zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ snapshot_link: a link to the snapshot you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ An unattached Disk instance.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_snapshot = snapshot_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+
+# [END compute_disk_create_from_snapshot]
diff --git a/compute/client_library/snippets/disks/create_from_source.py b/compute/client_library/snippets/disks/create_from_source.py
new file mode 100644
index 00000000000..85a1d38254e
--- /dev/null
+++ b/compute/client_library/snippets/disks/create_from_source.py
@@ -0,0 +1,116 @@
+# Copyright 2022 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_create_from_disk]
+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:
+ """
+ This method will wait 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_disk_from_disk(
+ project_id: str,
+ zone: str,
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ disk_link: str,
+) -> compute_v1.Disk:
+ """
+ Creates a disk in a project in a given zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+
+ Returns:
+ An attachable disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ disk.source_disk = disk_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+
+# [END compute_disk_create_from_disk]
diff --git a/compute/client_library/snippets/disks/create_kms_encrypted_disk.py b/compute/client_library/snippets/disks/create_kms_encrypted_disk.py
new file mode 100644
index 00000000000..d3f64377a16
--- /dev/null
+++ b/compute/client_library/snippets/disks/create_kms_encrypted_disk.py
@@ -0,0 +1,130 @@
+# Copyright 2022 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_create_kms_encrypted_disk]
+import sys
+from typing import Any, Optional
+
+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:
+ """
+ This method will wait 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_kms_encrypted_disk(
+ project_id: str,
+ zone: str,
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ kms_key_name: str,
+ disk_link: Optional[str] = None,
+ image_link: Optional[str] = None,
+) -> compute_v1.Disk:
+ """
+ Creates a zonal disk in a project. If you do not provide values for disk_link or image_link,
+ an empty disk will be created.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which you want to create the disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ kms_key_name: URL of the key from KMS. The key might be from another project, as
+ long as you have access to it. The data will be encrypted with the same key
+ in the new disk. This value uses following format:
+ "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}"
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ image_link: a link to the image you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+
+ Returns:
+ An attachable disk.
+ """
+ disk_client = compute_v1.DisksClient()
+ disk = compute_v1.Disk()
+ disk.zone = zone
+ disk.size_gb = disk_size_gb
+ if disk_link:
+ disk.source_disk = disk_link
+ if image_link:
+ disk.source_image = image_link
+ disk.type_ = disk_type
+ disk.name = disk_name
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.kms_key_name = kms_key_name
+ operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+
+# [END compute_create_kms_encrypted_disk]
diff --git a/compute/client_library/snippets/disks/delete.py b/compute/client_library/snippets/disks/delete.py
new file mode 100644
index 00000000000..c396911eb21
--- /dev/null
+++ b/compute/client_library/snippets/disks/delete.py
@@ -0,0 +1,92 @@
+# Copyright 2022 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_delete]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 delete_disk(project_id: str, zone: str, disk_name: str) -> NoReturn:
+ """
+ Deletes a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which is the disk you want to delete.
+ disk_name: name of the disk you want to delete.
+ """
+ disk_client = compute_v1.DisksClient()
+ operation = disk_client.delete(project=project_id, zone=zone, disk=disk_name)
+ wait_for_extended_operation(operation, "disk deletion")
+ return
+
+
+# [END compute_disk_delete]
diff --git a/compute/client_library/snippets/disks/list.py b/compute/client_library/snippets/disks/list.py
new file mode 100644
index 00000000000..2b522e2b982
--- /dev/null
+++ b/compute/client_library/snippets/disks/list.py
@@ -0,0 +1,49 @@
+# Copyright 2022 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_list]
+import sys
+from typing import Iterable, NoReturn
+
+from google.cloud import compute_v1
+
+
+def list_disks(
+ project_id: str, zone: str, filter_: str = ""
+) -> Iterable[compute_v1.Disk]:
+ """
+ Deletes a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone in which is the disk you want to delete.
+ filter_: filter to be applied when listing disks. Learn more about filters here:
+ https://cloud.google.com/python/docs/reference/compute/latest/google.cloud.compute_v1.types.ListDisksRequest
+ """
+ disk_client = compute_v1.DisksClient()
+ request = compute_v1.ListDisksRequest()
+ request.project = project_id
+ request.zone = zone
+ request.filter = filter_
+ return disk_client.list(request)
+
+
+# [END compute_disk_list]
diff --git a/compute/client_library/snippets/disks/regional_create_from_source.py b/compute/client_library/snippets/disks/regional_create_from_source.py
new file mode 100644
index 00000000000..25a682b391c
--- /dev/null
+++ b/compute/client_library/snippets/disks/regional_create_from_source.py
@@ -0,0 +1,129 @@
+# Copyright 2022 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_regional_disk_create_from_disk]
+import sys
+from typing import Any, Iterable, Optional
+
+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:
+ """
+ This method will wait 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_regional_disk(
+ project_id: str,
+ region: str,
+ replica_zones: Iterable[str],
+ disk_name: str,
+ disk_type: str,
+ disk_size_gb: int,
+ disk_link: Optional[str] = None,
+ snapshot_link: Optional[str] = None,
+) -> compute_v1.Disk:
+ """
+ Creates a regional disk from an existing zonal disk in a given project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ region: name of the region in which you want to create the disk.
+ replica_zones: an iterable collection of zone names in which you want to keep
+ the new disks' replicas. One of the replica zones of the clone must match
+ the zone of the source disk.
+ disk_name: name of the disk you want to create.
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "regions/{region}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "regions/us-west3/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ disk_link: a link to the disk you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
+ snapshot_link: a link to the snapshot you want to use as a source for the new disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ An attachable regional disk.
+ """
+ disk_client = compute_v1.RegionDisksClient()
+ disk = compute_v1.Disk()
+ disk.replica_zones = replica_zones
+ disk.size_gb = disk_size_gb
+ if disk_link:
+ disk.source_disk = disk_link
+ if snapshot_link:
+ disk.source_snapshot = snapshot_link
+ disk.type_ = disk_type
+ disk.region = region
+ disk.name = disk_name
+ operation = disk_client.insert(
+ project=project_id, region=region, disk_resource=disk
+ )
+
+ wait_for_extended_operation(operation, "disk creation")
+
+ return disk_client.get(project=project_id, region=region, disk=disk_name)
+
+
+# [END compute_regional_disk_create_from_disk]
diff --git a/compute/client_library/snippets/disks/regional_delete.py b/compute/client_library/snippets/disks/regional_delete.py
new file mode 100644
index 00000000000..b68bbd96981
--- /dev/null
+++ b/compute/client_library/snippets/disks/regional_delete.py
@@ -0,0 +1,92 @@
+# Copyright 2022 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_regional_disk_delete]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 delete_regional_disk(project_id: str, region: str, disk_name: str) -> NoReturn:
+ """
+ Deletes a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ region:name of the region where the disk is located.
+ disk_name: name of the disk that you want to delete.
+ """
+ disk_client = compute_v1.RegionDisksClient()
+ operation = disk_client.delete(project=project_id, region=region, disk=disk_name)
+ wait_for_extended_operation(operation, "regional disk deletion")
+ return
+
+
+# [END compute_regional_disk_delete]
diff --git a/compute/client_library/snippets/firewall/__init__.py b/compute/client_library/snippets/firewall/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/firewall/create.py b/compute/client_library/snippets/firewall/create.py
new file mode 100644
index 00000000000..bcdacb55920
--- /dev/null
+++ b/compute/client_library/snippets/firewall/create.py
@@ -0,0 +1,127 @@
+# Copyright 2022 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_firewall_create]
+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:
+ """
+ This method will wait 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_firewall_rule(
+ project_id: str, firewall_rule_name: str, network: str = "global/networks/default"
+) -> compute_v1.Firewall:
+ """
+ Creates a simple firewall rule allowing for incoming HTTP and HTTPS access from the entire Internet.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule that is created.
+ network: name of the network the rule will be applied to. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.name = firewall_rule_name
+ firewall_rule.direction = "INGRESS"
+
+ allowed_ports = compute_v1.Allowed()
+ allowed_ports.I_p_protocol = "tcp"
+ allowed_ports.ports = ["80", "443"]
+
+ firewall_rule.allowed = [allowed_ports]
+ firewall_rule.source_ranges = ["0.0.0.0/0"]
+ firewall_rule.network = network
+ firewall_rule.description = "Allowing TCP traffic on port 80 and 443 from Internet."
+
+ firewall_rule.target_tags = ["web"]
+
+ # Note that the default value of priority for the firewall API is 1000.
+ # If you check the value of `firewall_rule.priority` at this point it
+ # will be equal to 0, however it is not treated as "set" by the library and thus
+ # the default will be applied to the new rule. If you want to create a rule that
+ # has priority == 0, you need to explicitly set it so:
+ # TODO: Uncomment to set the priority to 0
+ # firewall_rule.priority = 0
+
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.insert(
+ project=project_id, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "firewall rule creation")
+
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+
+
+# [END compute_firewall_create]
diff --git a/compute/client_library/snippets/firewall/delete.py b/compute/client_library/snippets/firewall/delete.py
new file mode 100644
index 00000000000..f645f618d17
--- /dev/null
+++ b/compute/client_library/snippets/firewall/delete.py
@@ -0,0 +1,92 @@
+# Copyright 2022 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_firewall_delete]
+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:
+ """
+ This method will wait 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 delete_firewall_rule(project_id: str, firewall_rule_name: str) -> None:
+ """
+ Deletes a firewall rule from the project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the firewall rule you want to delete.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.delete(project=project_id, firewall=firewall_rule_name)
+
+ wait_for_extended_operation(operation, "firewall rule deletion")
+ return
+
+
+# [END compute_firewall_delete]
diff --git a/compute/client_library/snippets/firewall/list.py b/compute/client_library/snippets/firewall/list.py
new file mode 100644
index 00000000000..7a0636ae89c
--- /dev/null
+++ b/compute/client_library/snippets/firewall/list.py
@@ -0,0 +1,48 @@
+# Copyright 2022 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_firewall_list]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def list_firewall_rules(project_id: str) -> Iterable[compute_v1.Firewall]:
+ """
+ Return a list of all the firewall rules in specified project. Also prints the
+ list of firewall names and their descriptions.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ A flat list of all firewall rules defined for given project.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ firewalls_list = firewall_client.list(project=project_id)
+
+ for firewall in firewalls_list:
+ print(f" - {firewall.name}: {firewall.description}")
+
+ return firewalls_list
+
+
+# [END compute_firewall_list]
diff --git a/compute/client_library/snippets/firewall/main.py b/compute/client_library/snippets/firewall/main.py
new file mode 100644
index 00000000000..b506328df3b
--- /dev/null
+++ b/compute/client_library/snippets/firewall/main.py
@@ -0,0 +1,181 @@
+# Copyright 2022 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.
+
+
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def create_firewall_rule(
+ project_id: str, firewall_rule_name: str, network: str = "global/networks/default"
+) -> compute_v1.Firewall:
+ """
+ Creates a simple firewall rule allowing for incoming HTTP and HTTPS access from the entire Internet.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule that is created.
+ network: name of the network the rule will be applied to. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.name = firewall_rule_name
+ firewall_rule.direction = "INGRESS"
+
+ allowed_ports = compute_v1.Allowed()
+ allowed_ports.I_p_protocol = "tcp"
+ allowed_ports.ports = ["80", "443"]
+
+ firewall_rule.allowed = [allowed_ports]
+ firewall_rule.source_ranges = ["0.0.0.0/0"]
+ firewall_rule.network = network
+ firewall_rule.description = "Allowing TCP traffic on port 80 and 443 from Internet."
+
+ firewall_rule.target_tags = ["web"]
+
+ # Note that the default value of priority for the firewall API is 1000.
+ # If you check the value of `firewall_rule.priority` at this point it
+ # will be equal to 0, however it is not treated as "set" by the library and thus
+ # the default will be applied to the new rule. If you want to create a rule that
+ # has priority == 0, you need to explicitly set it so:
+ # TODO: Uncomment to set the priority to 0
+ # firewall_rule.priority = 0
+
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.insert(
+ project=project_id, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "firewall rule creation")
+
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+
+
+def delete_firewall_rule(project_id: str, firewall_rule_name: str) -> None:
+ """
+ Deletes a firewall rule from the project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the firewall rule you want to delete.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.delete(project=project_id, firewall=firewall_rule_name)
+
+ wait_for_extended_operation(operation, "firewall rule deletion")
+ return
+
+
+def get_firewall_rule(project_id: str, firewall_rule_name: str) -> compute_v1.Firewall:
+ """
+ Retrieve a Firewall from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the firewall rule you want to retrieve.
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+
+
+def list_firewall_rules(project_id: str) -> Iterable[compute_v1.Firewall]:
+ """
+ Return a list of all the firewall rules in specified project. Also prints the
+ list of firewall names and their descriptions.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ A flat list of all firewall rules defined for given project.
+ """
+ firewall_client = compute_v1.FirewallsClient()
+ firewalls_list = firewall_client.list(project=project_id)
+
+ for firewall in firewalls_list:
+ print(f" - {firewall.name}: {firewall.description}")
+
+ return firewalls_list
+
+
+def patch_firewall_priority(
+ project_id: str, firewall_rule_name: str, priority: int
+) -> None:
+ """
+ Modifies the priority of a given firewall rule.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule you want to modify.
+ priority: the new priority to be set for the rule.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.priority = priority
+
+ # The patch operation doesn't require the full definition of a Firewall object. It will only update
+ # the values that were set in it, in this case it will only change the priority.
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.patch(
+ project=project_id, firewall=firewall_rule_name, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "firewall rule patching")
+ return
+
+
+if __name__ == "__main__":
+ import google.auth
+ import google.auth.exceptions
+
+ try:
+ default_project_id = google.auth.default()[1]
+ print(f"Using project {default_project_id}.")
+ except google.auth.exceptions.DefaultCredentialsError:
+ print(
+ "Please use `gcloud auth application-default login` "
+ "or set GOOGLE_APPLICATION_CREDENTIALS to use this script."
+ )
+ else:
+ import uuid
+
+ rule_name = "firewall-sample-" + uuid.uuid4().hex[:10]
+ print(f"Creating firewall rule {rule_name}...")
+ # The rule will be created with default priority of 1000.
+ create_firewall_rule(default_project_id, rule_name)
+ try:
+ print("Rule created:")
+ print(get_firewall_rule(default_project_id, rule_name))
+ print("Updating rule priority to 10...")
+ patch_firewall_priority(default_project_id, rule_name, 10)
+ print("Rule updated: ")
+ print(get_firewall_rule(default_project_id, rule_name))
+ print(f"Deleting rule {rule_name}...")
+ finally:
+ delete_firewall_rule(default_project_id, rule_name)
+ print("Done.")
diff --git a/compute/client_library/snippets/firewall/patch.py b/compute/client_library/snippets/firewall/patch.py
new file mode 100644
index 00000000000..f288c35ade0
--- /dev/null
+++ b/compute/client_library/snippets/firewall/patch.py
@@ -0,0 +1,102 @@
+# Copyright 2022 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_firewall_patch]
+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:
+ """
+ This method will wait 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 patch_firewall_priority(
+ project_id: str, firewall_rule_name: str, priority: int
+) -> None:
+ """
+ Modifies the priority of a given firewall rule.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule you want to modify.
+ priority: the new priority to be set for the rule.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.priority = priority
+
+ # The patch operation doesn't require the full definition of a Firewall object. It will only update
+ # the values that were set in it, in this case it will only change the priority.
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.patch(
+ project=project_id, firewall=firewall_rule_name, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "firewall rule patching")
+ return
+
+
+# [END compute_firewall_patch]
diff --git a/compute/client_library/snippets/firewall/windows_kms.py b/compute/client_library/snippets/firewall/windows_kms.py
new file mode 100644
index 00000000000..ed869386fa2
--- /dev/null
+++ b/compute/client_library/snippets/firewall/windows_kms.py
@@ -0,0 +1,118 @@
+# Copyright 2022 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_create_egress_rule_windows_activation]
+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:
+ """
+ This method will wait 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_firewall_rule_for_windows_activation_host(
+ project_id: str, firewall_rule_name: str, network: str = "global/networks/default"
+) -> compute_v1.Firewall:
+ """
+ Creates an egress firewall rule with the highest priority for host
+ kms.windows.googlecloud.com (35.190.247.13) for Windows activation.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ firewall_rule_name: name of the rule that is created.
+ network: name of the network the rule will be applied to. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+
+ Returns:
+ A Firewall object.
+ """
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.name = firewall_rule_name
+ firewall_rule.network = network
+
+ allowed = compute_v1.Allowed()
+ allowed.ports = ["1688"]
+ allowed.I_p_protocol = "tcp"
+
+ firewall_rule.allowed = [allowed]
+ firewall_rule.destination_ranges = ["35.190.247.13/32"]
+ firewall_rule.direction = compute_v1.Firewall.Direction.EGRESS.name
+ firewall_rule.priority = 0
+
+ firewall_client = compute_v1.FirewallsClient()
+ operation = firewall_client.insert(
+ project=project_id, firewall_resource=firewall_rule
+ )
+
+ wait_for_extended_operation(operation, "windows KSM firewall rule creation")
+
+ return firewall_client.get(project=project_id, firewall=firewall_rule_name)
+
+
+# [END compute_create_egress_rule_windows_activation]
diff --git a/compute/client_library/snippets/images/__init__.py b/compute/client_library/snippets/images/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/images/create.py b/compute/client_library/snippets/images/create.py
new file mode 100644
index 00000000000..21fa76dd3fa
--- /dev/null
+++ b/compute/client_library/snippets/images/create.py
@@ -0,0 +1,154 @@
+# Copyright 2022 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_windows_image_create]
+# [START compute_images_create]
+import sys
+from typing import Any, Optional
+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:
+ """
+ This method will wait 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
+
+
+STOPPED_MACHINE_STATUS = (
+ compute_v1.Instance.Status.TERMINATED.name,
+ compute_v1.Instance.Status.STOPPED.name,
+)
+
+
+def create_image_from_disk(
+ project_id: str,
+ zone: str,
+ source_disk_name: str,
+ image_name: str,
+ storage_location: Optional[str] = None,
+ force_create: bool = False,
+) -> compute_v1.Image:
+ """
+ Creates a new disk image.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ zone: zone of the disk you copy from.
+ source_disk_name: name of the source disk you copy from.
+ image_name: name of the image you want to create.
+ storage_location: storage location for the image. If the value is undefined,
+ function will store the image in the multi-region closest to your image's
+ source location.
+ force_create: create the image even if the source disk is attached to a
+ running instance.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ disk_client = compute_v1.DisksClient()
+ instance_client = compute_v1.InstancesClient()
+
+ # Get source disk
+ disk = disk_client.get(project=project_id, zone=zone, disk=source_disk_name)
+
+ for disk_user in disk.users:
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=disk_user
+ )
+ if instance.status in STOPPED_MACHINE_STATUS:
+ continue
+ if not force_create:
+ raise RuntimeError(
+ f"Instance {disk_user} should be stopped. For Windows instances please "
+ f"stop the instance using `GCESysprep` command. For Linux instances just "
+ f"shut it down normally. You can supress this error and create an image of"
+ f"the disk by setting `force_create` parameter to true (not recommended). \n"
+ f"More information here: \n"
+ f" * https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api \n"
+ f" * https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#prepare_instance_for_image"
+ )
+ else:
+ warnings.warn(
+ f"Warning: The `force_create` option may compromise the integrity of your image. "
+ f"Stop the {disk_user} instance before you create the image if possible."
+ )
+
+ # Create image
+ image = compute_v1.Image()
+ image.source_disk = disk.self_link
+ image.name = image_name
+ if storage_location:
+ image.storage_locations = [storage_location]
+
+ operation = image_client.insert(project=project_id, image_resource=image)
+
+ wait_for_extended_operation(operation, "image creation from disk")
+
+ return image_client.get(project=project_id, image=image_name)
+
+
+# [END compute_images_create]
+# [END compute_windows_image_create]
diff --git a/compute/client_library/snippets/images/create_from_image.py b/compute/client_library/snippets/images/create_from_image.py
new file mode 100644
index 00000000000..3dddea1a599
--- /dev/null
+++ b/compute/client_library/snippets/images/create_from_image.py
@@ -0,0 +1,128 @@
+# Copyright 2022 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_images_create_from_image]
+import sys
+from typing import Any, Iterable, Optional
+
+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:
+ """
+ This method will wait 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_image_from_image(
+ project_id: str,
+ source_image_name: str,
+ image_name: str,
+ source_project_id: Optional[str] = None,
+ guest_os_features: Optional[Iterable[str]] = None,
+ storage_location: Optional[str] = None,
+) -> compute_v1.Image:
+ """
+ Creates a copy of another image.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to place your new image in.
+ source_image_name: name of the image you want to copy.
+ image_name: name of the image you want to create.
+ source_project_id: name of the project that hosts the source image. If left unset, it's assumed to equal
+ the `project_id`.
+ guest_os_features: an iterable collection of guest features you want to enable for the bootable image.
+ Learn more about Guest OS features here:
+ https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#guest-os-features
+ storage_location: the storage location of your image. For example, specify "us" to store the image in the
+ `us` multi-region, or "us-central1" to store it in the `us-central1` region. If you do not make a selection,
+ Compute Engine stores the image in the multi-region closest to your image's source location.
+
+ Returns:
+ An Image object.
+ """
+ if source_project_id is None:
+ source_project_id = project_id
+
+ image_client = compute_v1.ImagesClient()
+ src_image = image_client.get(project=source_project_id, image=source_image_name)
+
+ image = compute_v1.Image()
+ image.name = image_name
+ image.source_image = src_image.self_link
+ if storage_location:
+ image.storage_locations = [storage_location]
+
+ if guest_os_features:
+ image.guest_os_features = [
+ compute_v1.GuestOsFeature(type_=feature) for feature in guest_os_features
+ ]
+
+ operation = image_client.insert(project=project_id, image_resource=image)
+
+ wait_for_extended_operation(operation, "image creation from image")
+
+ return image_client.get(project=project_id, image=image_name)
+
+
+# [END compute_images_create_from_image]
diff --git a/compute/client_library/snippets/images/create_from_snapshot.py b/compute/client_library/snippets/images/create_from_snapshot.py
new file mode 100644
index 00000000000..18365ea0632
--- /dev/null
+++ b/compute/client_library/snippets/images/create_from_snapshot.py
@@ -0,0 +1,132 @@
+# Copyright 2022 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_images_create_from_snapshot]
+import sys
+from typing import Any, Iterable, Optional
+
+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:
+ """
+ This method will wait 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_image_from_snapshot(
+ project_id: str,
+ source_snapshot_name: str,
+ image_name: str,
+ source_project_id: Optional[str] = None,
+ guest_os_features: Optional[Iterable[str]] = None,
+ storage_location: Optional[str] = None,
+) -> compute_v1.Image:
+ """
+ Creates an image based on a snapshot.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to place your new image in.
+ source_snapshot_name: name of the snapshot you want to use as a base of your image.
+ image_name: name of the image you want to create.
+ source_project_id: name of the project that hosts the source snapshot. If left unset, it's assumed to equal
+ the `project_id`.
+ guest_os_features: an iterable collection of guest features you want to enable for the bootable image.
+ Learn more about Guest OS features here:
+ https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#guest-os-features
+ storage_location: the storage location of your image. For example, specify "us" to store the image in the
+ `us` multi-region, or "us-central1" to store it in the `us-central1` region. If you do not make a selection,
+ Compute Engine stores the image in the multi-region closest to your image's source location.
+
+ Returns:
+ An Image object.
+ """
+ if source_project_id is None:
+ source_project_id = project_id
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ image_client = compute_v1.ImagesClient()
+ src_snapshot = snapshot_client.get(
+ project=source_project_id, snapshot=source_snapshot_name
+ )
+
+ image = compute_v1.Image()
+ image.name = image_name
+ image.source_snapshot = src_snapshot.self_link
+
+ if storage_location:
+ image.storage_locations = [storage_location]
+
+ if guest_os_features:
+ image.guest_os_features = [
+ compute_v1.GuestOsFeature(type_=feature) for feature in guest_os_features
+ ]
+
+ operation = image_client.insert(project=project_id, image_resource=image)
+
+ wait_for_extended_operation(operation, "image creation from snapshot")
+
+ return image_client.get(project=project_id, image=image_name)
+
+
+# [END compute_images_create_from_snapshot]
diff --git a/compute/client_library/snippets/images/delete.py b/compute/client_library/snippets/images/delete.py
new file mode 100644
index 00000000000..5412bcd686a
--- /dev/null
+++ b/compute/client_library/snippets/images/delete.py
@@ -0,0 +1,90 @@
+# Copyright 2022 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_images_delete]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 delete_image(project_id: str, image_name: str) -> NoReturn:
+ """
+ Deletes a disk image.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ image_name: name of the image you want to delete.
+ """
+ image_client = compute_v1.ImagesClient()
+ operation = image_client.delete(project=project_id, image=image_name)
+ wait_for_extended_operation(operation, "image deletion")
+
+
+# [END compute_images_delete]
diff --git a/compute/client_library/snippets/images/get.py b/compute/client_library/snippets/images/get.py
new file mode 100644
index 00000000000..dbf8b3a2625
--- /dev/null
+++ b/compute/client_library/snippets/images/get.py
@@ -0,0 +1,65 @@
+# Copyright 2022 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_images_get_from_family]
+# [START compute_images_get]
+from google.cloud import compute_v1
+
+# [END compute_images_get]
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+# [END compute_images_get_from_family]
+
+
+# [START compute_images_get]
+def get_image(project_id: str, image_name: str) -> compute_v1.Image:
+ """
+ Retrieve detailed information about a single image from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to list images from.
+ image_name: name of the image you want to get details of.
+
+ Returns:
+ An instance of compute_v1.Image object with information about specified image.
+ """
+ image_client = compute_v1.ImagesClient()
+ return image_client.get(project=project_id, image=image_name)
+
+
+# [END compute_images_get]
diff --git a/compute/client_library/snippets/images/list.py b/compute/client_library/snippets/images/list.py
new file mode 100644
index 00000000000..3618295331f
--- /dev/null
+++ b/compute/client_library/snippets/images/list.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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_images_get_list]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def list_images(project_id: str) -> Iterable[compute_v1.Image]:
+ """
+ Retrieve a list of images available in given project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to list images from.
+
+ Returns:
+ An iterable collection of compute_v1.Image objects.
+ """
+ image_client = compute_v1.ImagesClient()
+ return image_client.list(project=project_id)
+
+
+# [END compute_images_get_list]
diff --git a/compute/client_library/snippets/images/pagination.py b/compute/client_library/snippets/images/pagination.py
new file mode 100644
index 00000000000..603480b15c0
--- /dev/null
+++ b/compute/client_library/snippets/images/pagination.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+
+# Copyright 2022 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 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_images_list_page]
+# [START compute_images_list]
+import google.cloud.compute_v1 as compute_v1
+
+# [END compute_images_list]
+# [END compute_images_list_page]
+
+
+# [START compute_images_list]
+def print_images_list(project: str) -> str:
+ """
+ Prints a list of all non-deprecated image names available in given project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to list images from.
+
+ Returns:
+ The output as a string.
+ """
+ images_client = compute_v1.ImagesClient()
+ # Listing only non-deprecated images to reduce the size of the reply.
+ images_list_request = compute_v1.ListImagesRequest(
+ project=project, max_results=100, filter="deprecated.state != DEPRECATED"
+ )
+ output = []
+
+ # Although the `max_results` parameter is specified in the request, the iterable returned
+ # by the `list()` method hides the pagination mechanic. The library makes multiple
+ # requests to the API for you, so you can simply iterate over all the images.
+ for img in images_client.list(request=images_list_request):
+ print(f" - {img.name}")
+ output.append(f" - {img.name}")
+ return "\n".join(output)
+
+
+# [END compute_images_list]
+
+
+# [START compute_images_list_page]
+def print_images_list_by_page(project: str, page_size: int = 10) -> str:
+ """
+ Prints a list of all non-deprecated image names available in a given project,
+ divided into pages as returned by the Compute Engine API.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to list images from.
+ page_size: size of the pages you want the API to return on each call.
+
+ Returns:
+ Output as a string.
+ """
+ images_client = compute_v1.ImagesClient()
+ # Listing only non-deprecated images to reduce the size of the reply.
+ images_list_request = compute_v1.ListImagesRequest(
+ project=project, max_results=page_size, filter="deprecated.state != DEPRECATED"
+ )
+ output = []
+
+ # Use the `pages` attribute of returned iterable to have more granular control of
+ # iteration over paginated results from the API. Each time you want to access the
+ # next page, the library retrieves that page from the API.
+ for page_num, page in enumerate(
+ images_client.list(request=images_list_request).pages, start=1
+ ):
+ print(f"Page {page_num}: ")
+ output.append(f"Page {page_num}: ")
+ for img in page.items:
+ print(f" - {img.name}")
+ output.append(f" - {img.name}")
+ return "\n".join(output)
+
+
+# [END compute_images_list_page]
+
+
+if __name__ == "__main__":
+ print("=================== Flat list of images ===================")
+ print_images_list("windows-sql-cloud")
+ print("================= Paginated list of images ================")
+ print_images_list_by_page("windows-sql-cloud", 5)
diff --git a/compute/client_library/snippets/images/set_deprecation_status.py b/compute/client_library/snippets/images/set_deprecation_status.py
new file mode 100644
index 00000000000..c98ee04adc3
--- /dev/null
+++ b/compute/client_library/snippets/images/set_deprecation_status.py
@@ -0,0 +1,104 @@
+# Copyright 2022 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_images_set_deprecation_status]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 set_deprecation_status(
+ project_id: str, image_name: str, status: compute_v1.DeprecationStatus.State
+) -> NoReturn:
+ """
+ Modify the deprecation status of an image.
+
+ Note: Image objects by default don't have the `deprecated` attribute at all unless it's set.
+
+ Args:
+ project_id: project ID or project number of the Cloud project that hosts the image.
+ image_name: name of the image you want to modify
+ status: the status you want to set for the image. Available values are available in
+ `compute_v1.DeprecationStatus.State` enum. Learn more about image deprecation statuses:
+ https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#deprecation-states
+ """
+ image_client = compute_v1.ImagesClient()
+ deprecation_status = compute_v1.DeprecationStatus()
+ deprecation_status.state = status.name
+ operation = image_client.deprecate(
+ project=project_id,
+ image=image_name,
+ deprecation_status_resource=deprecation_status,
+ )
+
+ wait_for_extended_operation(operation, "changing deprecation state of an image")
+
+
+# [END compute_images_set_deprecation_status]
diff --git a/compute/client_library/snippets/instance_templates/__init__.py b/compute/client_library/snippets/instance_templates/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/instance_templates/create.py b/compute/client_library/snippets/instance_templates/create.py
new file mode 100644
index 00000000000..1041206c914
--- /dev/null
+++ b/compute/client_library/snippets/instance_templates/create.py
@@ -0,0 +1,129 @@
+# Copyright 2022 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_template_create]
+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:
+ """
+ This method will wait 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_template(project_id: str, template_name: str) -> compute_v1.InstanceTemplate:
+ """
+ Create a new instance template with the provided name and a specific
+ instance configuration.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the new template to create.
+
+ Returns:
+ InstanceTemplate object that represents the new instance template.
+ """
+ # The template describes the size and source image of the boot disk
+ # to attach to the instance.
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-11"
+ )
+ initialize_params.disk_size_gb = 250
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+
+ # The template connects the instance to the `default` network,
+ # without specifying a subnetwork.
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.name = "global/networks/default"
+
+ # The template lets the instance use an external IP address.
+ access_config = compute_v1.AccessConfig()
+ access_config.name = "External NAT"
+ access_config.type_ = "ONE_TO_ONE_NAT"
+ access_config.network_tier = "PREMIUM"
+ network_interface.access_configs = [access_config]
+
+ template = compute_v1.InstanceTemplate()
+ template.name = template_name
+ template.properties.disks = [disk]
+ template.properties.machine_type = "e2-standard-4"
+ template.properties.network_interfaces = [network_interface]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.insert(
+ project=project_id, instance_template_resource=template
+ )
+
+ wait_for_extended_operation(operation, "instance template creation")
+
+ return template_client.get(project=project_id, instance_template=template_name)
+
+
+# [END compute_template_create]
diff --git a/compute/client_library/snippets/instance_templates/create_from_instance.py b/compute/client_library/snippets/instance_templates/create_from_instance.py
new file mode 100644
index 00000000000..a77c813eefb
--- /dev/null
+++ b/compute/client_library/snippets/instance_templates/create_from_instance.py
@@ -0,0 +1,119 @@
+# Copyright 2022 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_template_create_from_instance]
+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:
+ """
+ This method will wait 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_template_from_instance(
+ project_id: str, instance: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Create a new instance template based on an existing instance.
+ This new template specifies a different boot disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ instance: the instance to base the new template on. This value uses
+ the following format: "projects/{project}/zones/{zone}/instances/{instance_name}"
+ template_name: name of the new template to create.
+
+ Returns:
+ InstanceTemplate object that represents the new instance template.
+ """
+ disk = compute_v1.DiskInstantiationConfig()
+ # Device name must match the name of a disk attached to the instance you are
+ # basing your template on.
+ disk.device_name = "disk-1"
+ # Replace the original boot disk image used in your instance with a Rocky Linux image.
+ disk.instantiate_from = "CUSTOM_IMAGE"
+ disk.custom_image = "projects/rocky-linux-cloud/global/images/family/rocky-linux-8"
+ # Override the auto_delete setting.
+ disk.auto_delete = True
+
+ template = compute_v1.InstanceTemplate()
+ template.name = template_name
+ template.source_instance = instance
+ template.source_instance_params = compute_v1.SourceInstanceParams()
+ template.source_instance_params.disk_configs = [disk]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.insert(
+ project=project_id, instance_template_resource=template
+ )
+
+ wait_for_extended_operation(operation, "instance template creation")
+
+ return template_client.get(project=project_id, instance_template=template_name)
+
+
+# [END compute_template_create_from_instance]
diff --git a/compute/client_library/snippets/instance_templates/create_with_subnet.py b/compute/client_library/snippets/instance_templates/create_with_subnet.py
new file mode 100644
index 00000000000..3051e949534
--- /dev/null
+++ b/compute/client_library/snippets/instance_templates/create_with_subnet.py
@@ -0,0 +1,127 @@
+# Copyright 2022 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_template_create_with_subnet]
+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:
+ """
+ This method will wait 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_template_with_subnet(
+ project_id: str, network: str, subnetwork: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Create an instance template that uses a provided subnet.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ network: the network to be used in the new template. This value uses
+ the following format: "projects/{project}/global/networks/{network}"
+ subnetwork: the subnetwork to be used in the new template. This value
+ uses the following format: "projects/{project}/regions/{region}/subnetworks/{subnetwork}"
+ template_name: name of the new template to create.
+
+ Returns:
+ InstanceTemplate object that represents the new instance template.
+ """
+ # The template describes the size and source image of the book disk to
+ # attach to the instance.
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-11"
+ )
+ initialize_params.disk_size_gb = 250
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+
+ template = compute_v1.InstanceTemplate()
+ template.name = template_name
+ template.properties = compute_v1.InstanceProperties()
+ template.properties.disks = [disk]
+ template.properties.machine_type = "e2-standard-4"
+
+ # The template connects the instance to the specified network and subnetwork.
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.network = network
+ network_interface.subnetwork = subnetwork
+ template.properties.network_interfaces = [network_interface]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.insert(
+ project=project_id, instance_template_resource=template
+ )
+ wait_for_extended_operation(operation, "instance template creation")
+
+ return template_client.get(project=project_id, instance_template=template_name)
+
+
+# [END compute_template_create_with_subnet]
diff --git a/compute/client_library/snippets/instance_templates/delete.py b/compute/client_library/snippets/instance_templates/delete.py
new file mode 100644
index 00000000000..382fd64d4c8
--- /dev/null
+++ b/compute/client_library/snippets/instance_templates/delete.py
@@ -0,0 +1,93 @@
+# Copyright 2022 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_template_delete]
+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:
+ """
+ This method will wait 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 delete_instance_template(project_id: str, template_name: str):
+ """
+ Delete an instance template.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the template to delete.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation = template_client.delete(
+ project=project_id, instance_template=template_name
+ )
+ wait_for_extended_operation(operation, "instance template deletion")
+ return
+
+
+# [END compute_template_delete]
diff --git a/compute/client_library/snippets/instance_templates/get.py b/compute/client_library/snippets/instance_templates/get.py
new file mode 100644
index 00000000000..439b3bea979
--- /dev/null
+++ b/compute/client_library/snippets/instance_templates/get.py
@@ -0,0 +1,44 @@
+# Copyright 2022 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_template_get]
+from google.cloud import compute_v1
+
+
+def get_instance_template(
+ project_id: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Retrieve an instance template, which you can use to create virtual machine
+ (VM) instances and managed instance groups (MIGs).
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the template to retrieve.
+
+ Returns:
+ InstanceTemplate object that represents the retrieved template.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ return template_client.get(project=project_id, instance_template=template_name)
+
+
+# [END compute_template_get]
diff --git a/compute/client_library/snippets/instance_templates/list.py b/compute/client_library/snippets/instance_templates/list.py
new file mode 100644
index 00000000000..495686c62d8
--- /dev/null
+++ b/compute/client_library/snippets/instance_templates/list.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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_template_list]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def list_instance_templates(project_id: str) -> Iterable[compute_v1.InstanceTemplate]:
+ """
+ Get a list of InstanceTemplate objects available in a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+
+ Returns:
+ Iterable list of InstanceTemplate objects.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ return template_client.list(project=project_id)
+
+
+# [END compute_template_list]
diff --git a/compute/client_library/snippets/instances/__init__.py b/compute/client_library/snippets/instances/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/instances/bulk_insert.py b/compute/client_library/snippets/instances/bulk_insert.py
new file mode 100644
index 00000000000..efe095e3e1f
--- /dev/null
+++ b/compute/client_library/snippets/instances/bulk_insert.py
@@ -0,0 +1,191 @@
+# Copyright 2022 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_instances_bulk_insert]
+import sys
+from typing import Any, Iterable, Optional
+import uuid
+
+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:
+ """
+ This method will wait 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 get_instance_template(
+ project_id: str, template_name: str
+) -> compute_v1.InstanceTemplate:
+ """
+ Retrieve an instance template, which you can use to create virtual machine
+ (VM) instances and managed instance groups (MIGs).
+
+ Args:
+ project_id: project ID or project number of the Cloud project you use.
+ template_name: name of the template to retrieve.
+
+ Returns:
+ InstanceTemplate object that represents the retrieved template.
+ """
+ template_client = compute_v1.InstanceTemplatesClient()
+ return template_client.get(project=project_id, instance_template=template_name)
+
+
+def bulk_insert_instance(
+ project_id: str,
+ zone: str,
+ template: compute_v1.InstanceTemplate,
+ count: int,
+ name_pattern: str,
+ min_count: Optional[int] = None,
+ labels: Optional[dict] = None,
+) -> Iterable[compute_v1.Instance]:
+ """
+ Create multiple VMs based on an Instance Template. The newly created instances will
+ be returned as a list and will share a label with key `bulk_batch` and a random
+ value.
+
+ If the bulk insert operation fails and the requested number of instances can't be created,
+ and more than min_count instances are created, then those instances can be found using
+ the `bulk_batch` label with value attached to the raised exception in bulk_batch_id
+ attribute. So, you can use the following filter: f"label.bulk_batch={err.bulk_batch_id}"
+ when listing instances in a zone to get the instances that were successfully created.
+
+ 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"
+ template: an Instance Template to be used for creation of the new VMs.
+ name_pattern: The string pattern used for the names of the VMs. The pattern
+ must contain one continuous sequence of placeholder hash characters (#)
+ with each character corresponding to one digit of the generated instance
+ name. Example: a name_pattern of inst-#### generates instance names such
+ as inst-0001 and inst-0002. If existing instances in the same project and
+ zone have names that match the name pattern then the generated instance
+ numbers start after the biggest existing number. For example, if there
+ exists an instance with name inst-0050, then instance names generated
+ using the pattern inst-#### begin with inst-0051. The name pattern
+ placeholder #...# can contain up to 18 characters.
+ count: The maximum number of instances to create.
+ min_count (optional): The minimum number of instances to create. If no min_count is
+ specified then count is used as the default value. If min_count instances
+ cannot be created, then no instances will be created and instances already
+ created will be deleted.
+ labels (optional): A dictionary with labels to be added to the new VMs.
+ """
+ bulk_insert_resource = compute_v1.BulkInsertInstanceResource()
+ bulk_insert_resource.source_instance_template = template.self_link
+ bulk_insert_resource.count = count
+ bulk_insert_resource.min_count = min_count or count
+ bulk_insert_resource.name_pattern = name_pattern
+
+ if not labels:
+ labels = {}
+
+ labels["bulk_batch"] = uuid.uuid4().hex
+ instance_prop = compute_v1.InstanceProperties()
+ instance_prop.labels = labels
+ bulk_insert_resource.instance_properties = instance_prop
+
+ bulk_insert_request = compute_v1.BulkInsertInstanceRequest()
+ bulk_insert_request.bulk_insert_instance_resource_resource = bulk_insert_resource
+ bulk_insert_request.project = project_id
+ bulk_insert_request.zone = zone
+
+ client = compute_v1.InstancesClient()
+ operation = client.bulk_insert(bulk_insert_request)
+
+ try:
+ wait_for_extended_operation(operation, "bulk instance creation")
+ except Exception as err:
+ err.bulk_batch_id = labels["bulk_batch"]
+ raise err
+
+ list_req = compute_v1.ListInstancesRequest()
+ list_req.project = project_id
+ list_req.zone = zone
+ list_req.filter = " AND ".join(
+ f"labels.{key}:{value}" for key, value in labels.items()
+ )
+ return client.list(list_req)
+
+
+def create_five_instances(
+ project_id: str, zone: str, template_name: str, name_pattern: str
+):
+ """
+ Create five instances of an instance template.
+
+ 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"
+ template_name: name of the template that will be used to create new VMs.
+ name_pattern: The string pattern used for the names of the VMs.
+ """
+ template = get_instance_template(project_id, template_name)
+ instances = bulk_insert_instance(project_id, zone, template, 5, name_pattern)
+ return instances
+
+
+# [END compute_instances_bulk_insert]
diff --git a/compute/client_library/snippets/instances/create.py b/compute/client_library/snippets/instances/create.py
new file mode 100644
index 00000000000..801c05b16a8
--- /dev/null
+++ b/compute/client_library/snippets/instances/create.py
@@ -0,0 +1,290 @@
+# Copyright 2022 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_instances_create]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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)
+
+
+# [END compute_instances_create]
+
+if __name__ == "__main__":
+ import uuid
+ import google.auth
+ import google.auth.exceptions
+
+ try:
+ default_project_id = google.auth.default()[1]
+ except google.auth.exceptions.DefaultCredentialsError:
+ print(
+ "Please use `gcloud auth application-default login` "
+ "or set GOOGLE_APPLICATION_CREDENTIALS to use this script."
+ )
+ else:
+ instance_name = "quickstart-" + uuid.uuid4().hex[:10]
+ instance_zone = "europe-central2-b"
+
+ newest_debian = get_image_from_family(
+ project="debian-cloud", family="debian-10"
+ )
+ disk_type = f"zones/{instance_zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ create_instance(default_project_id, instance_zone, instance_name, disks)
diff --git a/compute/client_library/snippets/instances/create_start_instance/__init__.py b/compute/client_library/snippets/instances/create_start_instance/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_from_custom_image.py b/compute/client_library/snippets/instances/create_start_instance/create_from_custom_image.py
new file mode 100644
index 00000000000..d78dd819868
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_from_custom_image.py
@@ -0,0 +1,288 @@
+# Copyright 2022 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_instances_create_from_custom_image]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_from_custom_image(
+ project_id: str, zone: str, instance_name: str, custom_image_link: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with custom image used as its boot disk.
+
+ 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.
+ custom_image_link: link to the custom image you want to use in the form of:
+ "projects/{project_name}/global/images/{image_name}"
+
+ Returns:
+ Instance object.
+ """
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, custom_image_link, True)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+
+
+# [END compute_instances_create_from_custom_image]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_from_public_image.py b/compute/client_library/snippets/instances/create_start_instance/create_from_public_image.py
new file mode 100644
index 00000000000..50bf6bffdc9
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_from_public_image.py
@@ -0,0 +1,287 @@
+# Copyright 2022 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_instances_create_from_image]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_from_public_image(
+ project_id: str, zone: str, instance_name: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link, True)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+
+
+# [END compute_instances_create_from_image]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_from_snapshot.py b/compute/client_library/snippets/instances/create_start_instance/create_from_snapshot.py
new file mode 100644
index 00000000000..d71bd4500f9
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_from_snapshot.py
@@ -0,0 +1,271 @@
+# Copyright 2022 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_instances_create_from_snapshot]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def disk_from_snapshot(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_snapshot: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses a disk snapshot as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_snapshot: disk snapshot to use when creating this disk. You must have read access to this disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified snapshot.
+ """
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_snapshot = source_snapshot
+ initialize_params.disk_type = disk_type
+ initialize_params.disk_size_gb = disk_size_gb
+ disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ disk.auto_delete = auto_delete
+ disk.boot = boot
+ return disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_from_snapshot(
+ project_id: str, zone: str, instance_name: str, snapshot_link: str
+):
+ """
+ Create a new VM instance with boot disk created from a snapshot. The
+ new boot disk will have 20 gigabytes.
+
+ 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.
+ snapshot_link: link to the snapshot you want to use as the source of your
+ boot disk in the form of: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ Instance object.
+ """
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_snapshot(disk_type, 20, True, snapshot_link)]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+
+
+# [END compute_instances_create_from_snapshot]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_windows_instance.py b/compute/client_library/snippets/instances/create_start_instance/create_windows_instance.py
new file mode 100644
index 00000000000..ce0620da216
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_windows_instance.py
@@ -0,0 +1,331 @@
+# Copyright 2022 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_create_windows_instance_external_ip]
+# [START compute_create_windows_instance_internal_ip]
+import re
+import sys
+from typing import Any, List, Optional
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_windows_instance(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ machine_type: str,
+ source_image_family: str = "windows-2022",
+ network_link: str = "global/networks/default",
+ subnetwork_link: Optional[str] = None,
+) -> compute_v1.Instance:
+ """
+ Creates a new Windows Server instance that has only an internal IP address.
+
+ 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.
+ machine_type: machine type you want to create in following format:
+ "zones/{zone}/machineTypes/{type_name}". For example:
+ "zones/europe-west3-c/machineTypes/f1-micro"
+ You can find the list of available machine types using:
+ https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
+ source_image_family: name of the public image family for Windows Server or SQL Server images.
+ https://cloud.google.com/compute/docs/images#os-compute-support
+ 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}"
+
+ Returns:
+ Instance object.
+ """
+ if subnetwork_link is None:
+ subnetwork_link = f"regions/{zone}/subnetworks/default"
+
+ base_image = get_image_from_family(
+ project="windows-cloud", family=source_image_family
+ )
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 100, True, base_image.self_link, True)]
+
+ # You must verify or configure routes and firewall rules in your VPC network
+ # to allow access to kms.windows.googlecloud.com.
+ # More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server
+
+ # Additionally, you must enable Private Google Access for subnets in your VPC network
+ # that contain Windows instances with only internal IP addresses.
+ # More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
+
+ instance = create_instance(
+ project_id,
+ zone,
+ instance_name,
+ disks,
+ machine_type=machine_type,
+ network_link=network_link,
+ subnetwork_link=subnetwork_link,
+ external_access=True, # Set this to False to disable external IP for your instance
+ )
+ return instance
+
+
+# [END compute_create_windows_instance_internal_ip]
+# [END compute_create_windows_instance_external_ip]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_with_additional_disk.py b/compute/client_library/snippets/instances/create_start_instance/create_with_additional_disk.py
new file mode 100644
index 00000000000..e75aadccd78
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_with_additional_disk.py
@@ -0,0 +1,321 @@
+# Copyright 2022 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_instances_create_from_image_plus_empty_disk]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def empty_disk(
+ disk_type: str, disk_size_gb: int, boot: bool = False, auto_delete: bool = True
+) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. The created disk contains
+ no data and requires formatting before it can be used.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created as an empty disk.
+ """
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.disk_type = disk_type
+ initialize_params.disk_size_gb = disk_size_gb
+ disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ disk.auto_delete = auto_delete
+ disk.boot = boot
+ return disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_additional_disk(
+ project_id: str, zone: str, instance_name: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system on a 20 GB disk
+ and a 25 GB additional empty disk.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [
+ disk_from_image(disk_type, 20, True, newest_debian.self_link),
+ empty_disk(disk_type, 25),
+ ]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+
+
+# [END compute_instances_create_from_image_plus_empty_disk]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_with_existing_disks.py b/compute/client_library/snippets/instances/create_start_instance/create_with_existing_disks.py
new file mode 100644
index 00000000000..4fa4cd2e34c
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_with_existing_disks.py
@@ -0,0 +1,253 @@
+# Copyright 2022 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_instances_create_with_existing_disks]
+import re
+import sys
+from typing import Any, Iterable, List, NoReturn
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_disk(project_id: str, zone: str, disk_name: str) -> compute_v1.Disk:
+ """
+ Gets a disk from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone where the disk exists.
+ disk_name: name of the disk you want to retrieve.
+ """
+ disk_client = compute_v1.DisksClient()
+ return disk_client.get(project=project_id, zone=zone, disk=disk_name)
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_existing_disks(
+ project_id: str, zone: str, instance_name: str, disk_names: List[str]
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance using selected disks. The first disk in disk_names will
+ be used as boot disk.
+
+ 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.
+ disk_names: list of disk names to be attached to the new virtual machine.
+ First disk in this list will be used as the boot device.
+
+ Returns:
+ Instance object.
+ """
+ assert len(disk_names) >= 1
+ disks = [get_disk(project_id, zone, disk_name) for disk_name in disk_names]
+ attached_disks = []
+ for disk in disks:
+ adisk = compute_v1.AttachedDisk()
+ adisk.source = disk.self_link
+ attached_disks.append(adisk)
+ attached_disks[0].boot = True
+ instance = create_instance(project_id, zone, instance_name, attached_disks)
+ return instance
+
+
+# [END compute_instances_create_with_existing_disks]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_with_local_ssd.py b/compute/client_library/snippets/instances/create_start_instance/create_with_local_ssd.py
new file mode 100644
index 00000000000..c8038abd276
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_with_local_ssd.py
@@ -0,0 +1,310 @@
+# Copyright 2022 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_instances_create_with_local_ssd]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def local_ssd_disk(zone: str) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. The created disk contains
+ no data and requires formatting before it can be used.
+
+ Args:
+ zone: The zone in which the local SSD drive will be attached.
+
+ Returns:
+ AttachedDisk object configured as a local SSD disk.
+ """
+ disk = compute_v1.AttachedDisk()
+ disk.type_ = compute_v1.AttachedDisk.Type.SCRATCH.name
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.disk_type = f"zones/{zone}/diskTypes/local-ssd"
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ return disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_ssd(
+ project_id: str, zone: str, instance_name: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system and SSD local disk.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [
+ disk_from_image(disk_type, 10, True, newest_debian.self_link, True),
+ local_ssd_disk(zone),
+ ]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+
+
+# [END compute_instances_create_with_local_ssd]
diff --git a/compute/client_library/snippets/instances/create_start_instance/create_with_snapshotted_data_disk.py b/compute/client_library/snippets/instances/create_start_instance/create_with_snapshotted_data_disk.py
new file mode 100644
index 00000000000..057eba548e6
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_start_instance/create_with_snapshotted_data_disk.py
@@ -0,0 +1,329 @@
+# Copyright 2022 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_instances_create_from_image_plus_snapshot_disk]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def disk_from_snapshot(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_snapshot: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk():
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses a disk snapshot as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_snapshot: disk snapshot to use when creating this disk. You must have read access to this disk.
+ This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified snapshot.
+ """
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_snapshot = source_snapshot
+ initialize_params.disk_type = disk_type
+ initialize_params.disk_size_gb = disk_size_gb
+ disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ disk.auto_delete = auto_delete
+ disk.boot = boot
+ return disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_snapshotted_data_disk(
+ project_id: str, zone: str, instance_name: str, snapshot_link: str
+):
+ """
+ Create a new VM instance with Debian 10 operating system and data disk created from snapshot.
+
+ 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.
+ snapshot_link: link to the snapshot you want to use as the source of your
+ data disk in the form of: "projects/{project_name}/global/snapshots/{snapshot_name}"
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [
+ disk_from_image(disk_type, 10, True, newest_debian.self_link),
+ disk_from_snapshot(disk_type, 11, False, snapshot_link),
+ ]
+ instance = create_instance(project_id, zone, instance_name, disks)
+ return instance
+
+
+# [END compute_instances_create_from_image_plus_snapshot_disk]
diff --git a/compute/client_library/snippets/instances/create_with_subnet.py b/compute/client_library/snippets/instances/create_with_subnet.py
new file mode 100644
index 00000000000..6696dc4a198
--- /dev/null
+++ b/compute/client_library/snippets/instances/create_with_subnet.py
@@ -0,0 +1,300 @@
+# Copyright 2022 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_instances_create_with_subnet]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_subnet(
+ project_id: str, zone: str, instance_name: str, network_link: str, subnet_link: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system in specified network and subnetwork.
+
+ 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.
+ 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}"
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(
+ project_id,
+ zone,
+ instance_name,
+ disks,
+ network_link=network_link,
+ subnetwork_link=subnet_link,
+ )
+ return instance
+
+
+# [END compute_instances_create_with_subnet]
diff --git a/compute/client_library/snippets/instances/custom_hostname/create.py b/compute/client_library/snippets/instances/custom_hostname/create.py
new file mode 100644
index 00000000000..6813bc4dcd5
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_hostname/create.py
@@ -0,0 +1,290 @@
+# Copyright 2022 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_instances_create_custom_hostname]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_instance_custom_hostname(
+ project_id: str, zone: str, instance_name: str, hostname: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system and a custom hostname.
+
+ 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.
+ hostname: the hostname you want to use for the new instance.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-11")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(
+ project_id, zone, instance_name, disks, custom_hostname=hostname
+ )
+ return instance
+
+
+# [END compute_instances_create_custom_hostname]
diff --git a/compute/client_library/snippets/instances/custom_hostname/get.py b/compute/client_library/snippets/instances/custom_hostname/get.py
new file mode 100644
index 00000000000..673d5c81066
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_hostname/get.py
@@ -0,0 +1,45 @@
+# Copyright 2022 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_instances_get_hostname]
+from google.cloud import compute_v1
+
+
+def get_hostname(project_id: str, zone: str, instance_name: str) -> str:
+ """
+ Retrieve the hostname of given instance.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to check.
+
+ Returns:
+ The hostname of an instance.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.hostname
+
+
+# [END compute_instances_get_hostname]
diff --git a/compute/client_library/snippets/instances/custom_machine_types/__init__.py b/compute/client_library/snippets/instances/custom_machine_types/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/instances/custom_machine_types/create_shared_with_helper.py b/compute/client_library/snippets/instances/custom_machine_types/create_shared_with_helper.py
new file mode 100644
index 00000000000..99dea3ef2da
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_machine_types/create_shared_with_helper.py
@@ -0,0 +1,495 @@
+# Copyright 2022 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_custom_machine_type_create_shared_with_helper]
+from collections import namedtuple
+from enum import Enum
+from enum import unique
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def gb_to_mb(value: int) -> int:
+ return value << 10
+
+
+class CustomMachineType:
+ """
+ Allows to create custom machine types to be used with the VM instances.
+ """
+
+ @unique
+ class CPUSeries(Enum):
+ N1 = "custom"
+ N2 = "n2-custom"
+ N2D = "n2d-custom"
+ E2 = "e2-custom"
+ E2_MICRO = "e2-custom-micro"
+ E2_SMALL = "e2-custom-small"
+ E2_MEDIUM = "e2-custom-medium"
+
+ TypeLimits = namedtuple(
+ "TypeLimits",
+ [
+ "allowed_cores",
+ "min_mem_per_core",
+ "max_mem_per_core",
+ "allow_extra_memory",
+ "extra_memory_limit",
+ ],
+ )
+
+ # The limits for various CPU types are described on:
+ # https://cloud.google.com/compute/docs/general-purpose-machines
+ LIMITS = {
+ CPUSeries.E2: TypeLimits(frozenset(range(2, 33, 2)), 512, 8192, False, 0),
+ CPUSeries.E2_MICRO: TypeLimits(frozenset(), 1024, 2048, False, 0),
+ CPUSeries.E2_SMALL: TypeLimits(frozenset(), 2048, 4096, False, 0),
+ CPUSeries.E2_MEDIUM: TypeLimits(frozenset(), 4096, 8192, False, 0),
+ CPUSeries.N2: TypeLimits(
+ frozenset(range(2, 33, 2)).union(set(range(36, 129, 4))),
+ 512,
+ 8192,
+ True,
+ gb_to_mb(624),
+ ),
+ CPUSeries.N2D: TypeLimits(
+ frozenset({2, 4, 8, 16, 32, 48, 64, 80, 96}), 512, 8192, True, gb_to_mb(768)
+ ),
+ CPUSeries.N1: TypeLimits(
+ frozenset({1}.union(range(2, 97, 2))), 922, 6656, True, gb_to_mb(624)
+ ),
+ }
+
+ def __init__(
+ self, zone: str, cpu_series: CPUSeries, memory_mb: int, core_count: int = 0
+ ):
+ self.zone = zone
+ self.cpu_series = cpu_series
+ self.limits = self.LIMITS[self.cpu_series]
+ # Shared machine types (e2-small, e2-medium and e2-micro) always have
+ # 2 vCPUs: https://cloud.google.com/compute/docs/general-purpose-machines#e2_limitations
+ self.core_count = 2 if self.is_shared() else core_count
+ self.memory_mb = memory_mb
+ self._checked = False
+ self._check_parameters()
+ self.extra_memory_used = self._check_extra_memory()
+
+ def is_shared(self):
+ return self.cpu_series in (
+ CustomMachineType.CPUSeries.E2_SMALL,
+ CustomMachineType.CPUSeries.E2_MICRO,
+ CustomMachineType.CPUSeries.E2_MEDIUM,
+ )
+
+ def _check_extra_memory(self) -> bool:
+ if self._checked:
+ return self.memory_mb > self.core_count * self.limits.max_mem_per_core
+ else:
+ raise RuntimeError(
+ "You need to call _check_parameters() before calling _check_extra_memory()"
+ )
+
+ def _check_parameters(self):
+ """
+ Check whether the requested parameters are allowed. Find more information about limitations of custom machine
+ types at: https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types
+ """
+ # Check the number of cores
+ if (
+ self.limits.allowed_cores
+ and self.core_count not in self.limits.allowed_cores
+ ):
+ raise RuntimeError(
+ f"Invalid number of cores requested. Allowed number of cores for {self.cpu_series.name} is: {sorted(self.limits.allowed_cores)}"
+ )
+
+ # Memory must be a multiple of 256 MB
+ if self.memory_mb % 256 != 0:
+ raise RuntimeError("Requested memory must be a multiple of 256 MB.")
+
+ # Check if the requested memory isn't too little
+ if self.memory_mb < self.core_count * self.limits.min_mem_per_core:
+ raise RuntimeError(
+ f"Requested memory is too low. Minimal memory for {self.cpu_series.name} is {self.limits.min_mem_per_core} MB per core."
+ )
+
+ # Check if the requested memory isn't too much
+ if self.memory_mb > self.core_count * self.limits.max_mem_per_core:
+ if self.limits.allow_extra_memory:
+ if self.memory_mb > self.limits.extra_memory_limit:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.extra_memory_limit} MB."
+ )
+ else:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.max_mem_per_core} MB per core."
+ )
+
+ self._checked = True
+
+ def __str__(self) -> str:
+ """
+ Return the custom machine type in form of a string acceptable by Compute Engine API.
+ """
+ if self.cpu_series in {
+ self.CPUSeries.E2_SMALL,
+ self.CPUSeries.E2_MICRO,
+ self.CPUSeries.E2_MEDIUM,
+ }:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.memory_mb}"
+
+ if self.extra_memory_used:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}-ext"
+
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}"
+
+ def short_type_str(self) -> str:
+ """
+ Return machine type in a format without the zone. For example, n2-custom-0-10240.
+ This format is used to create instance templates.
+ """
+ return str(self).rsplit("/", maxsplit=1)[1]
+
+ @classmethod
+ def from_str(cls, machine_type: str):
+ """
+ Construct a new object from a string. The string needs to be a valid custom machine type like:
+ - https://www.googleapis.com/compute/v1/projects/diregapic-mestiv/zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - e2-custom-4-8192 (in this case, the zone parameter will not be set)
+ """
+ zone = None
+ if machine_type.startswith("http"):
+ machine_type = machine_type[machine_type.find("zones/") :]
+
+ if machine_type.startswith("zones/"):
+ _, zone, _, machine_type = machine_type.split("/")
+
+ extra_mem = machine_type.endswith("-ext")
+
+ if machine_type.startswith("custom"):
+ cpu = cls.CPUSeries.N1
+ _, cores, memory = machine_type.rsplit("-", maxsplit=2)
+ else:
+ if extra_mem:
+ cpu_series, _, cores, memory, _ = machine_type.split("-")
+ else:
+ cpu_series, _, cores, memory = machine_type.split("-")
+ if cpu_series == "n2":
+ cpu = cls.CPUSeries.N2
+ elif cpu_series == "n2d":
+ cpu = cls.CPUSeries.N2D
+ elif cpu_series == "e2":
+ cpu = cls.CPUSeries.E2
+ if cores == "micro":
+ cpu = cls.CPUSeries.E2_MICRO
+ cores = 2
+ elif cores == "small":
+ cpu = cls.CPUSeries.E2_SMALL
+ cores = 2
+ elif cores == "medium":
+ cpu = cls.CPUSeries.E2_MEDIUM
+ cores = 2
+ else:
+ raise RuntimeError("Unknown CPU series.")
+
+ cores = int(cores)
+ memory = int(memory)
+
+ return cls(zone, cpu, memory, cores)
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_custom_shared_core_instance(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ cpu_series: CustomMachineType.CPUSeries,
+ memory: int,
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with a custom type using shared CPUs.
+
+ 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.
+ cpu_series: the type of CPU you want to use. Pick one value from the CustomMachineType.CPUSeries enum.
+ For example: CustomMachineType.CPUSeries.E2_MICRO
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Return:
+ Instance object.
+ """
+ assert cpu_series in (
+ CustomMachineType.CPUSeries.E2_MICRO,
+ CustomMachineType.CPUSeries.E2_SMALL,
+ CustomMachineType.CPUSeries.E2_MEDIUM,
+ )
+ custom_type = CustomMachineType(zone, cpu_series, memory)
+
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ return create_instance(project_id, zone, instance_name, disks, str(custom_type))
+
+
+# [END compute_custom_machine_type_create_shared_with_helper]
diff --git a/compute/client_library/snippets/instances/custom_machine_types/create_with_helper.py b/compute/client_library/snippets/instances/custom_machine_types/create_with_helper.py
new file mode 100644
index 00000000000..18f642549ca
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_machine_types/create_with_helper.py
@@ -0,0 +1,498 @@
+# Copyright 2022 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_custom_machine_type_create_with_helper]
+from collections import namedtuple
+from enum import Enum
+from enum import unique
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def gb_to_mb(value: int) -> int:
+ return value << 10
+
+
+class CustomMachineType:
+ """
+ Allows to create custom machine types to be used with the VM instances.
+ """
+
+ @unique
+ class CPUSeries(Enum):
+ N1 = "custom"
+ N2 = "n2-custom"
+ N2D = "n2d-custom"
+ E2 = "e2-custom"
+ E2_MICRO = "e2-custom-micro"
+ E2_SMALL = "e2-custom-small"
+ E2_MEDIUM = "e2-custom-medium"
+
+ TypeLimits = namedtuple(
+ "TypeLimits",
+ [
+ "allowed_cores",
+ "min_mem_per_core",
+ "max_mem_per_core",
+ "allow_extra_memory",
+ "extra_memory_limit",
+ ],
+ )
+
+ # The limits for various CPU types are described on:
+ # https://cloud.google.com/compute/docs/general-purpose-machines
+ LIMITS = {
+ CPUSeries.E2: TypeLimits(frozenset(range(2, 33, 2)), 512, 8192, False, 0),
+ CPUSeries.E2_MICRO: TypeLimits(frozenset(), 1024, 2048, False, 0),
+ CPUSeries.E2_SMALL: TypeLimits(frozenset(), 2048, 4096, False, 0),
+ CPUSeries.E2_MEDIUM: TypeLimits(frozenset(), 4096, 8192, False, 0),
+ CPUSeries.N2: TypeLimits(
+ frozenset(range(2, 33, 2)).union(set(range(36, 129, 4))),
+ 512,
+ 8192,
+ True,
+ gb_to_mb(624),
+ ),
+ CPUSeries.N2D: TypeLimits(
+ frozenset({2, 4, 8, 16, 32, 48, 64, 80, 96}), 512, 8192, True, gb_to_mb(768)
+ ),
+ CPUSeries.N1: TypeLimits(
+ frozenset({1}.union(range(2, 97, 2))), 922, 6656, True, gb_to_mb(624)
+ ),
+ }
+
+ def __init__(
+ self, zone: str, cpu_series: CPUSeries, memory_mb: int, core_count: int = 0
+ ):
+ self.zone = zone
+ self.cpu_series = cpu_series
+ self.limits = self.LIMITS[self.cpu_series]
+ # Shared machine types (e2-small, e2-medium and e2-micro) always have
+ # 2 vCPUs: https://cloud.google.com/compute/docs/general-purpose-machines#e2_limitations
+ self.core_count = 2 if self.is_shared() else core_count
+ self.memory_mb = memory_mb
+ self._checked = False
+ self._check_parameters()
+ self.extra_memory_used = self._check_extra_memory()
+
+ def is_shared(self):
+ return self.cpu_series in (
+ CustomMachineType.CPUSeries.E2_SMALL,
+ CustomMachineType.CPUSeries.E2_MICRO,
+ CustomMachineType.CPUSeries.E2_MEDIUM,
+ )
+
+ def _check_extra_memory(self) -> bool:
+ if self._checked:
+ return self.memory_mb > self.core_count * self.limits.max_mem_per_core
+ else:
+ raise RuntimeError(
+ "You need to call _check_parameters() before calling _check_extra_memory()"
+ )
+
+ def _check_parameters(self):
+ """
+ Check whether the requested parameters are allowed. Find more information about limitations of custom machine
+ types at: https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types
+ """
+ # Check the number of cores
+ if (
+ self.limits.allowed_cores
+ and self.core_count not in self.limits.allowed_cores
+ ):
+ raise RuntimeError(
+ f"Invalid number of cores requested. Allowed number of cores for {self.cpu_series.name} is: {sorted(self.limits.allowed_cores)}"
+ )
+
+ # Memory must be a multiple of 256 MB
+ if self.memory_mb % 256 != 0:
+ raise RuntimeError("Requested memory must be a multiple of 256 MB.")
+
+ # Check if the requested memory isn't too little
+ if self.memory_mb < self.core_count * self.limits.min_mem_per_core:
+ raise RuntimeError(
+ f"Requested memory is too low. Minimal memory for {self.cpu_series.name} is {self.limits.min_mem_per_core} MB per core."
+ )
+
+ # Check if the requested memory isn't too much
+ if self.memory_mb > self.core_count * self.limits.max_mem_per_core:
+ if self.limits.allow_extra_memory:
+ if self.memory_mb > self.limits.extra_memory_limit:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.extra_memory_limit} MB."
+ )
+ else:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.max_mem_per_core} MB per core."
+ )
+
+ self._checked = True
+
+ def __str__(self) -> str:
+ """
+ Return the custom machine type in form of a string acceptable by Compute Engine API.
+ """
+ if self.cpu_series in {
+ self.CPUSeries.E2_SMALL,
+ self.CPUSeries.E2_MICRO,
+ self.CPUSeries.E2_MEDIUM,
+ }:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.memory_mb}"
+
+ if self.extra_memory_used:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}-ext"
+
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}"
+
+ def short_type_str(self) -> str:
+ """
+ Return machine type in a format without the zone. For example, n2-custom-0-10240.
+ This format is used to create instance templates.
+ """
+ return str(self).rsplit("/", maxsplit=1)[1]
+
+ @classmethod
+ def from_str(cls, machine_type: str):
+ """
+ Construct a new object from a string. The string needs to be a valid custom machine type like:
+ - https://www.googleapis.com/compute/v1/projects/diregapic-mestiv/zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - e2-custom-4-8192 (in this case, the zone parameter will not be set)
+ """
+ zone = None
+ if machine_type.startswith("http"):
+ machine_type = machine_type[machine_type.find("zones/") :]
+
+ if machine_type.startswith("zones/"):
+ _, zone, _, machine_type = machine_type.split("/")
+
+ extra_mem = machine_type.endswith("-ext")
+
+ if machine_type.startswith("custom"):
+ cpu = cls.CPUSeries.N1
+ _, cores, memory = machine_type.rsplit("-", maxsplit=2)
+ else:
+ if extra_mem:
+ cpu_series, _, cores, memory, _ = machine_type.split("-")
+ else:
+ cpu_series, _, cores, memory = machine_type.split("-")
+ if cpu_series == "n2":
+ cpu = cls.CPUSeries.N2
+ elif cpu_series == "n2d":
+ cpu = cls.CPUSeries.N2D
+ elif cpu_series == "e2":
+ cpu = cls.CPUSeries.E2
+ if cores == "micro":
+ cpu = cls.CPUSeries.E2_MICRO
+ cores = 2
+ elif cores == "small":
+ cpu = cls.CPUSeries.E2_SMALL
+ cores = 2
+ elif cores == "medium":
+ cpu = cls.CPUSeries.E2_MEDIUM
+ cores = 2
+ else:
+ raise RuntimeError("Unknown CPU series.")
+
+ cores = int(cores)
+ memory = int(memory)
+
+ return cls(zone, cpu, memory, cores)
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_custom_instance(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ cpu_series: CustomMachineType.CPUSeries,
+ core_count: int,
+ memory: int,
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with a custom machine type.
+
+ 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.
+ cpu_series: the type of CPU you want to use. Select one value from the CustomMachineType.CPUSeries enum.
+ For example: CustomMachineType.CPUSeries.N2
+ core_count: number of CPU cores you want to use.
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Return:
+ Instance object.
+ """
+ assert cpu_series in (
+ CustomMachineType.CPUSeries.E2,
+ CustomMachineType.CPUSeries.N1,
+ CustomMachineType.CPUSeries.N2,
+ CustomMachineType.CPUSeries.N2D,
+ )
+ custom_type = CustomMachineType(zone, cpu_series, memory, core_count)
+
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ return create_instance(project_id, zone, instance_name, disks, str(custom_type))
+
+
+# [END compute_custom_machine_type_create_with_helper]
diff --git a/compute/client_library/snippets/instances/custom_machine_types/create_without_helper.py b/compute/client_library/snippets/instances/custom_machine_types/create_without_helper.py
new file mode 100644
index 00000000000..30b2edd9e9a
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_machine_types/create_without_helper.py
@@ -0,0 +1,322 @@
+# Copyright 2022 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_custom_machine_type_create_without_helper]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_custom_instances_no_helper(
+ project_id: str, zone: str, instance_name: str, core_count: int, memory: int
+) -> List[compute_v1.Instance]:
+ """
+ Create 7 new VM instances without using a CustomMachineType helper function.
+
+ 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.
+ core_count: number of CPU cores you want to use.
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Returns:
+ List of Instance objects.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ params = [
+ (
+ f"{instance_name}_n1",
+ f"zones/{zone}/machineTypes/custom-{core_count}-{memory}",
+ ),
+ (
+ f"{instance_name}_n2",
+ f"zones/{zone}/machineTypes/n2-custom-{core_count}-{memory}",
+ ),
+ (
+ f"{instance_name}_n2d",
+ f"zones/{zone}/machineTypes/n2d-custom-{core_count}-{memory}",
+ ),
+ (
+ f"{instance_name}_e2",
+ f"zones/{zone}/machineTypes/e2-custom-{core_count}-{memory}",
+ ),
+ (
+ f"{instance_name}_e2_micro",
+ f"zones/{zone}/machineTypes/e2-custom-micro-{memory}",
+ ),
+ (
+ f"{instance_name}_e2_small",
+ f"zones/{zone}/machineTypes/e2-custom-small-{memory}",
+ ),
+ (
+ f"{instance_name}_e2_medium",
+ f"zones/{zone}/machineTypes/e2-custom-medium-{memory}",
+ ),
+ ]
+ # The core_count and memory values are not validated anywhere and can be rejected by the API.
+ instances = [
+ create_instance(project_id, zone, name, disks, type) for name, type in params
+ ]
+ return instances
+
+
+# [END compute_custom_machine_type_create_without_helper]
diff --git a/compute/client_library/snippets/instances/custom_machine_types/extra_mem_no_helper.py b/compute/client_library/snippets/instances/custom_machine_types/extra_mem_no_helper.py
new file mode 100644
index 00000000000..de210032724
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_machine_types/extra_mem_no_helper.py
@@ -0,0 +1,312 @@
+# Copyright 2022 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_custom_machine_type_extra_mem_no_helper]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_custom_instances_extra_mem(
+ project_id: str, zone: str, instance_name: str, core_count: int, memory: int
+) -> List[compute_v1.Instance]:
+ """
+ Create 3 new VM instances with extra memory without using a CustomMachineType helper class.
+
+ 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.
+ core_count: number of CPU cores you want to use.
+ memory: the amount of memory for the VM instance, in megabytes.
+
+ Returns:
+ List of Instance objects.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ # The core_count and memory values are not validated anywhere and can be rejected by the API.
+ instances = [
+ create_instance(
+ project_id,
+ zone,
+ f"{instance_name}_n1_extra_mem",
+ disks,
+ f"zones/{zone}/machineTypes/custom-{core_count}-{memory}-ext",
+ ),
+ create_instance(
+ project_id,
+ zone,
+ f"{instance_name}_n2_extra_mem",
+ disks,
+ f"zones/{zone}/machineTypes/n2-custom-{core_count}-{memory}-ext",
+ ),
+ create_instance(
+ project_id,
+ zone,
+ f"{instance_name}_n2d_extra_mem",
+ disks,
+ f"zones/{zone}/machineTypes/n2d-custom-{core_count}-{memory}-ext",
+ ),
+ ]
+ return instances
+
+
+# [END compute_custom_machine_type_extra_mem_no_helper]
diff --git a/compute/client_library/snippets/instances/custom_machine_types/helper_class.py b/compute/client_library/snippets/instances/custom_machine_types/helper_class.py
new file mode 100644
index 00000000000..568710aee3f
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_machine_types/helper_class.py
@@ -0,0 +1,219 @@
+# Copyright 2022 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_custom_machine_type_helper_class]
+from collections import namedtuple
+from enum import Enum
+from enum import unique
+
+
+def gb_to_mb(value: int) -> int:
+ return value << 10
+
+
+class CustomMachineType:
+ """
+ Allows to create custom machine types to be used with the VM instances.
+ """
+
+ @unique
+ class CPUSeries(Enum):
+ N1 = "custom"
+ N2 = "n2-custom"
+ N2D = "n2d-custom"
+ E2 = "e2-custom"
+ E2_MICRO = "e2-custom-micro"
+ E2_SMALL = "e2-custom-small"
+ E2_MEDIUM = "e2-custom-medium"
+
+ TypeLimits = namedtuple(
+ "TypeLimits",
+ [
+ "allowed_cores",
+ "min_mem_per_core",
+ "max_mem_per_core",
+ "allow_extra_memory",
+ "extra_memory_limit",
+ ],
+ )
+
+ # The limits for various CPU types are described on:
+ # https://cloud.google.com/compute/docs/general-purpose-machines
+ LIMITS = {
+ CPUSeries.E2: TypeLimits(frozenset(range(2, 33, 2)), 512, 8192, False, 0),
+ CPUSeries.E2_MICRO: TypeLimits(frozenset(), 1024, 2048, False, 0),
+ CPUSeries.E2_SMALL: TypeLimits(frozenset(), 2048, 4096, False, 0),
+ CPUSeries.E2_MEDIUM: TypeLimits(frozenset(), 4096, 8192, False, 0),
+ CPUSeries.N2: TypeLimits(
+ frozenset(range(2, 33, 2)).union(set(range(36, 129, 4))),
+ 512,
+ 8192,
+ True,
+ gb_to_mb(624),
+ ),
+ CPUSeries.N2D: TypeLimits(
+ frozenset({2, 4, 8, 16, 32, 48, 64, 80, 96}), 512, 8192, True, gb_to_mb(768)
+ ),
+ CPUSeries.N1: TypeLimits(
+ frozenset({1}.union(range(2, 97, 2))), 922, 6656, True, gb_to_mb(624)
+ ),
+ }
+
+ def __init__(
+ self, zone: str, cpu_series: CPUSeries, memory_mb: int, core_count: int = 0
+ ):
+ self.zone = zone
+ self.cpu_series = cpu_series
+ self.limits = self.LIMITS[self.cpu_series]
+ # Shared machine types (e2-small, e2-medium and e2-micro) always have
+ # 2 vCPUs: https://cloud.google.com/compute/docs/general-purpose-machines#e2_limitations
+ self.core_count = 2 if self.is_shared() else core_count
+ self.memory_mb = memory_mb
+ self._checked = False
+ self._check_parameters()
+ self.extra_memory_used = self._check_extra_memory()
+
+ def is_shared(self):
+ return self.cpu_series in (
+ CustomMachineType.CPUSeries.E2_SMALL,
+ CustomMachineType.CPUSeries.E2_MICRO,
+ CustomMachineType.CPUSeries.E2_MEDIUM,
+ )
+
+ def _check_extra_memory(self) -> bool:
+ if self._checked:
+ return self.memory_mb > self.core_count * self.limits.max_mem_per_core
+ else:
+ raise RuntimeError(
+ "You need to call _check_parameters() before calling _check_extra_memory()"
+ )
+
+ def _check_parameters(self):
+ """
+ Check whether the requested parameters are allowed. Find more information about limitations of custom machine
+ types at: https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types
+ """
+ # Check the number of cores
+ if (
+ self.limits.allowed_cores
+ and self.core_count not in self.limits.allowed_cores
+ ):
+ raise RuntimeError(
+ f"Invalid number of cores requested. Allowed number of cores for {self.cpu_series.name} is: {sorted(self.limits.allowed_cores)}"
+ )
+
+ # Memory must be a multiple of 256 MB
+ if self.memory_mb % 256 != 0:
+ raise RuntimeError("Requested memory must be a multiple of 256 MB.")
+
+ # Check if the requested memory isn't too little
+ if self.memory_mb < self.core_count * self.limits.min_mem_per_core:
+ raise RuntimeError(
+ f"Requested memory is too low. Minimal memory for {self.cpu_series.name} is {self.limits.min_mem_per_core} MB per core."
+ )
+
+ # Check if the requested memory isn't too much
+ if self.memory_mb > self.core_count * self.limits.max_mem_per_core:
+ if self.limits.allow_extra_memory:
+ if self.memory_mb > self.limits.extra_memory_limit:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.extra_memory_limit} MB."
+ )
+ else:
+ raise RuntimeError(
+ f"Requested memory is too large.. Maximum memory allowed for {self.cpu_series.name} is {self.limits.max_mem_per_core} MB per core."
+ )
+
+ self._checked = True
+
+ def __str__(self) -> str:
+ """
+ Return the custom machine type in form of a string acceptable by Compute Engine API.
+ """
+ if self.cpu_series in {
+ self.CPUSeries.E2_SMALL,
+ self.CPUSeries.E2_MICRO,
+ self.CPUSeries.E2_MEDIUM,
+ }:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.memory_mb}"
+
+ if self.extra_memory_used:
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}-ext"
+
+ return f"zones/{self.zone}/machineTypes/{self.cpu_series.value}-{self.core_count}-{self.memory_mb}"
+
+ def short_type_str(self) -> str:
+ """
+ Return machine type in a format without the zone. For example, n2-custom-0-10240.
+ This format is used to create instance templates.
+ """
+ return str(self).rsplit("/", maxsplit=1)[1]
+
+ @classmethod
+ def from_str(cls, machine_type: str):
+ """
+ Construct a new object from a string. The string needs to be a valid custom machine type like:
+ - https://www.googleapis.com/compute/v1/projects/diregapic-mestiv/zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - zones/us-central1-b/machineTypes/e2-custom-4-8192
+ - e2-custom-4-8192 (in this case, the zone parameter will not be set)
+ """
+ zone = None
+ if machine_type.startswith("http"):
+ machine_type = machine_type[machine_type.find("zones/") :]
+
+ if machine_type.startswith("zones/"):
+ _, zone, _, machine_type = machine_type.split("/")
+
+ extra_mem = machine_type.endswith("-ext")
+
+ if machine_type.startswith("custom"):
+ cpu = cls.CPUSeries.N1
+ _, cores, memory = machine_type.rsplit("-", maxsplit=2)
+ else:
+ if extra_mem:
+ cpu_series, _, cores, memory, _ = machine_type.split("-")
+ else:
+ cpu_series, _, cores, memory = machine_type.split("-")
+ if cpu_series == "n2":
+ cpu = cls.CPUSeries.N2
+ elif cpu_series == "n2d":
+ cpu = cls.CPUSeries.N2D
+ elif cpu_series == "e2":
+ cpu = cls.CPUSeries.E2
+ if cores == "micro":
+ cpu = cls.CPUSeries.E2_MICRO
+ cores = 2
+ elif cores == "small":
+ cpu = cls.CPUSeries.E2_SMALL
+ cores = 2
+ elif cores == "medium":
+ cpu = cls.CPUSeries.E2_MEDIUM
+ cores = 2
+ else:
+ raise RuntimeError("Unknown CPU series.")
+
+ cores = int(cores)
+ memory = int(memory)
+
+ return cls(zone, cpu, memory, cores)
+
+
+# [END compute_custom_machine_type_helper_class]
diff --git a/compute/client_library/snippets/instances/custom_machine_types/update_memory.py b/compute/client_library/snippets/instances/custom_machine_types/update_memory.py
new file mode 100644
index 00000000000..d6e94237466
--- /dev/null
+++ b/compute/client_library/snippets/instances/custom_machine_types/update_memory.py
@@ -0,0 +1,147 @@
+# Copyright 2022 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_custom_machine_type_update_memory]
+import sys
+import time
+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:
+ """
+ This method will wait 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 add_extended_memory_to_instance(
+ project_id: str, zone: str, instance_name: str, new_memory: int
+):
+ """
+ Modify an existing VM to use extended memory.
+
+ 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.
+ new_memory: the amount of memory for the VM instance, in megabytes.
+
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ if not (
+ "n1-" in instance.machine_type
+ or "n2-" in instance.machine_type
+ or "n2d-" in instance.machine_type
+ ):
+ raise RuntimeError("Extra memory is available only for N1, N2 and N2D CPUs.")
+
+ # Make sure that the machine is turned off
+ if instance.status not in (
+ instance.Status.TERMINATED.name,
+ instance.Status.STOPPED.name,
+ ):
+ operation = instance_client.stop(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ wait_for_extended_operation(operation, "instance stopping")
+ start = time.time()
+ while instance.status not in (
+ instance.Status.TERMINATED.name,
+ instance.Status.STOPPED.name,
+ ):
+ # Waiting for the instance to be turned off.
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ time.sleep(2)
+ if time.time() - start >= 300: # 5 minutes
+ raise TimeoutError()
+
+ # Modify the machine definition, remember that extended memory is available only for N1, N2 and N2D CPUs
+ start, end = instance.machine_type.rsplit("-", maxsplit=1)
+ instance.machine_type = start + f"-{new_memory}-ext"
+ # TODO: If you prefer to use the CustomMachineType helper class, uncomment this code and comment the 2 lines above
+ # Using CustomMachineType helper
+ # cmt = CustomMachineType.from_str(instance.machine_type)
+ # cmt.memory_mb = new_memory
+ # cmt.extra_memory_used = True
+ # instance.machine_type = str(cmt)
+ operation = instance_client.update(
+ project=project_id,
+ zone=zone,
+ instance=instance_name,
+ instance_resource=instance,
+ )
+ wait_for_extended_operation(operation, "instance update")
+
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+
+
+# [END compute_custom_machine_type_update_memory]
diff --git a/compute/client_library/snippets/instances/delete.py b/compute/client_library/snippets/instances/delete.py
new file mode 100644
index 00000000000..1a8eb3edfb9
--- /dev/null
+++ b/compute/client_library/snippets/instances/delete.py
@@ -0,0 +1,98 @@
+# Copyright 2022 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_instances_delete]
+import sys
+import time
+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:
+ """
+ This method will wait 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 delete_instance(project_id: str, zone: str, machine_name: str) -> None:
+ """
+ Send an instance deletion 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 you want to use. For example: “us-west3-b”
+ machine_name: name of the machine you want to delete.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ print(f"Deleting {machine_name} from {zone}...")
+ operation = instance_client.delete(
+ project=project_id, zone=zone, instance=machine_name
+ )
+ wait_for_extended_operation(operation, "instance deletion")
+ print(f"Instance {machine_name} deleted.")
+ return
+
+
+# [END compute_instances_delete]
diff --git a/compute/client_library/snippets/instances/delete_protection/__init__.py b/compute/client_library/snippets/instances/delete_protection/__init__.py
new file mode 100644
index 00000000000..a3ded82a3b6
--- /dev/null
+++ b/compute/client_library/snippets/instances/delete_protection/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 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/delete_protection/create.py b/compute/client_library/snippets/instances/delete_protection/create.py
new file mode 100644
index 00000000000..7d9bbe9bbfa
--- /dev/null
+++ b/compute/client_library/snippets/instances/delete_protection/create.py
@@ -0,0 +1,290 @@
+# Copyright 2022 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_delete_protection_create]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_protected_instance(
+ project_id: str, zone: str, instance_name: str
+) -> compute_v1.Instance:
+ """
+ Create a new VM instance with Debian 10 operating system and delete protection
+ turned on.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-11")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(
+ project_id, zone, instance_name, disks, delete_protection=True
+ )
+ return instance
+
+
+# [END compute_delete_protection_create]
diff --git a/compute/client_library/snippets/instances/delete_protection/get.py b/compute/client_library/snippets/instances/delete_protection/get.py
new file mode 100644
index 00000000000..35bf1dc1f01
--- /dev/null
+++ b/compute/client_library/snippets/instances/delete_protection/get.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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_delete_protection_get]
+from google.cloud import compute_v1
+
+
+def get_delete_protection(project_id: str, zone: str, instance_name: str) -> bool:
+ """
+ Returns the state of delete protection flag of given instance.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the virtual machine to check.
+ Returns:
+ The boolean value of the delete protection setting.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.deletion_protection
+
+
+# [END compute_delete_protection_get]
diff --git a/compute/client_library/snippets/instances/delete_protection/set.py b/compute/client_library/snippets/instances/delete_protection/set.py
new file mode 100644
index 00000000000..f8c692a3155
--- /dev/null
+++ b/compute/client_library/snippets/instances/delete_protection/set.py
@@ -0,0 +1,102 @@
+# Copyright 2022 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_delete_protection_set]
+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:
+ """
+ This method will wait 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 set_delete_protection(
+ project_id: str, zone: str, instance_name: str, delete_protection: bool
+) -> None:
+ """
+ Updates the delete protection setting of given instance.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the instance to update.
+ delete_protection: boolean value indicating if the virtual machine should be
+ protected against deletion or not.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ request = compute_v1.SetDeletionProtectionInstanceRequest()
+ request.project = project_id
+ request.zone = zone
+ request.resource = instance_name
+ request.deletion_protection = delete_protection
+
+ operation = instance_client.set_deletion_protection(request)
+ wait_for_extended_operation(operation, "changing delete protection setting")
+ return
+
+
+# [END compute_delete_protection_set]
diff --git a/compute/client_library/snippets/instances/from_instance_template/__init__.py b/compute/client_library/snippets/instances/from_instance_template/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/instances/from_instance_template/create_from_template.py b/compute/client_library/snippets/instances/from_instance_template/create_from_template.py
new file mode 100644
index 00000000000..bcc4b77e970
--- /dev/null
+++ b/compute/client_library/snippets/instances/from_instance_template/create_from_template.py
@@ -0,0 +1,111 @@
+# Copyright 2022 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_instances_create_from_template]
+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:
+ """
+ This method will wait 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_from_template(
+ project_id: str, zone: str, instance_name: str, instance_template_url: str
+) -> compute_v1.Instance:
+ """
+ Creates a Compute Engine VM instance from an instance template.
+
+ Args:
+ project_id: ID or number of the project you want to use.
+ zone: Name of the zone you want to check, for example: us-west3-b
+ instance_name: Name of the new instance.
+ instance_template_url: URL of the instance template used for creating the new instance.
+ It can be a full or partial URL.
+ Examples:
+ - https://www.googleapis.com/compute/v1/projects/project/global/instanceTemplates/example-instance-template
+ - projects/project/global/instanceTemplates/example-instance-template
+ - global/instanceTemplates/example-instance-template
+
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ instance_insert_request = compute_v1.InsertInstanceRequest()
+ instance_insert_request.project = project_id
+ instance_insert_request.zone = zone
+ instance_insert_request.source_instance_template = instance_template_url
+ instance_insert_request.instance_resource.name = instance_name
+
+ operation = instance_client.insert(instance_insert_request)
+ wait_for_extended_operation(operation, "instance creation")
+
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+
+
+# [END compute_instances_create_from_template]
diff --git a/compute/client_library/snippets/instances/from_instance_template/create_from_template_with_overrides.py b/compute/client_library/snippets/instances/from_instance_template/create_from_template_with_overrides.py
new file mode 100644
index 00000000000..9ff3ba4af4f
--- /dev/null
+++ b/compute/client_library/snippets/instances/from_instance_template/create_from_template_with_overrides.py
@@ -0,0 +1,151 @@
+# Copyright 2022 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_instances_create_from_template_with_overrides]
+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:
+ """
+ This method will wait 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_from_template_with_overrides(
+ project_id: str,
+ zone: str,
+ instance_name: str,
+ instance_template_name: str,
+ machine_type: str,
+ new_disk_source_image: str,
+) -> compute_v1.Instance:
+ """
+ Creates a Compute Engine VM instance from an instance template, changing the machine type and
+ adding a new disk created from a source image.
+
+ Args:
+ project_id: ID or number of the project you want to use.
+ zone: Name of the zone you want to check, for example: us-west3-b
+ instance_name: Name of the new instance.
+ instance_template_name: Name of the instance template used for creating the new instance.
+ machine_type: Machine type you want to set in following format:
+ "zones/{zone}/machineTypes/{type_name}". For example:
+ - "zones/europe-west3-c/machineTypes/f1-micro"
+ - You can find the list of available machine types using:
+ https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
+ new_disk_source_image: Path the the disk image you want to use for your new
+ disk. This can be one of the public images
+ (like "projects/debian-cloud/global/images/family/debian-10")
+ or a private image you have access to.
+ For a list of available public images, see the documentation:
+ http://cloud.google.com/compute/docs/images
+
+ Returns:
+ Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance_template_client = compute_v1.InstanceTemplatesClient()
+
+ # Retrieve an instance template by name.
+ instance_template = instance_template_client.get(
+ project=project_id, instance_template=instance_template_name
+ )
+
+ # Adjust diskType field of the instance template to use the URL formatting required by instances.insert.diskType
+ # For instance template, there is only a name, not URL.
+ for disk in instance_template.properties.disks:
+ if disk.initialize_params.disk_type:
+ disk.initialize_params.disk_type = (
+ f"zones/{zone}/diskTypes/{disk.initialize_params.disk_type}"
+ )
+
+ instance = compute_v1.Instance()
+ instance.name = instance_name
+ instance.machine_type = machine_type
+ instance.disks = list(instance_template.properties.disks)
+
+ new_disk = compute_v1.AttachedDisk()
+ new_disk.initialize_params.disk_size_gb = 50
+ new_disk.initialize_params.source_image = new_disk_source_image
+ new_disk.auto_delete = True
+ new_disk.boot = False
+ new_disk.type_ = "PERSISTENT"
+
+ instance.disks.append(new_disk)
+
+ instance_insert_request = compute_v1.InsertInstanceRequest()
+ instance_insert_request.project = project_id
+ instance_insert_request.zone = zone
+ instance_insert_request.instance_resource = instance
+ instance_insert_request.source_instance_template = instance_template.self_link
+
+ operation = instance_client.insert(instance_insert_request)
+ wait_for_extended_operation(operation, "instance creation")
+
+ return instance_client.get(project=project_id, zone=zone, instance=instance_name)
+
+
+# [END compute_instances_create_from_template_with_overrides]
diff --git a/compute/client_library/snippets/instances/get.py b/compute/client_library/snippets/instances/get.py
new file mode 100644
index 00000000000..427ea19a1d8
--- /dev/null
+++ b/compute/client_library/snippets/instances/get.py
@@ -0,0 +1,45 @@
+# Copyright 2022 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_instances_get]
+from google.cloud import compute_v1
+
+
+def get_instance(project_id: str, zone: str, instance_name: str) -> compute_v1.Instance:
+ """
+ Get information about a VM instance in the given zone in the specified project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ instance_name: name of the VM instance you want to query.
+ Returns:
+ An Instance object.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ return instance
+
+
+# [END compute_instances_get]
diff --git a/compute/client_library/snippets/instances/list.py b/compute/client_library/snippets/instances/list.py
new file mode 100644
index 00000000000..45830c72ea4
--- /dev/null
+++ b/compute/client_library/snippets/instances/list.py
@@ -0,0 +1,48 @@
+# Copyright 2022 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_instances_list]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def list_instances(project_id: str, zone: str) -> Iterable[compute_v1.Instance]:
+ """
+ List all instances in the given zone in the specified project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: “us-west3-b”
+ Returns:
+ An iterable collection of Instance objects.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance_list = instance_client.list(project=project_id, zone=zone)
+
+ print(f"Instances found in zone {zone}:")
+ for instance in instance_list:
+ print(f" - {instance.name} ({instance.machine_type})")
+
+ return instance_list
+
+
+# [END compute_instances_list]
diff --git a/compute/client_library/snippets/instances/list_all.py b/compute/client_library/snippets/instances/list_all.py
new file mode 100644
index 00000000000..47302fe423a
--- /dev/null
+++ b/compute/client_library/snippets/instances/list_all.py
@@ -0,0 +1,63 @@
+# Copyright 2022 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_instances_list_all]
+from collections import defaultdict
+from typing import Dict, Iterable
+
+from google.cloud import compute_v1
+
+
+def list_all_instances(
+ project_id: str,
+) -> Dict[str, Iterable[compute_v1.Instance]]:
+ """
+ Returns a dictionary of all instances present in a project, grouped by their zone.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ Returns:
+ A dictionary with zone names as keys (in form of "zones/{zone_name}") and
+ iterable collections of Instance objects as values.
+ """
+ instance_client = compute_v1.InstancesClient()
+ request = compute_v1.AggregatedListInstancesRequest()
+ request.project = project_id
+ # Use the `max_results` parameter to limit the number of results that the API returns per response page.
+ request.max_results = 50
+
+ agg_list = instance_client.aggregated_list(request=request)
+
+ all_instances = defaultdict(list)
+ print("Instances found:")
+ # Despite using the `max_results` parameter, you don't need to handle the pagination
+ # yourself. The returned `AggregatedListPager` object handles pagination
+ # automatically, returning separated pages as you iterate over the results.
+ for zone, response in agg_list:
+ if response.instances:
+ all_instances[zone].extend(response.instances)
+ print(f" {zone}:")
+ for instance in response.instances:
+ print(f" - {instance.name} ({instance.machine_type})")
+ return all_instances
+
+
+# [END compute_instances_list_all]
diff --git a/compute/client_library/snippets/instances/preemptible/__init__.py b/compute/client_library/snippets/instances/preemptible/__init__.py
new file mode 100644
index 00000000000..a3ded82a3b6
--- /dev/null
+++ b/compute/client_library/snippets/instances/preemptible/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022 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/preemptible/create_preemptible.py b/compute/client_library/snippets/instances/preemptible/create_preemptible.py
new file mode 100644
index 00000000000..5816d39f4b0
--- /dev/null
+++ b/compute/client_library/snippets/instances/preemptible/create_preemptible.py
@@ -0,0 +1,287 @@
+# Copyright 2022 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_preemptible_create]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_preemptible_instance(
+ project_id: str, zone: str, instance_name: str
+) -> compute_v1.Instance:
+ """
+ Create a new preemptible VM instance with Debian 10 operating system.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-11")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(project_id, zone, instance_name, disks, preemptible=True)
+ return instance
+
+
+# [END compute_preemptible_create]
diff --git a/compute/client_library/snippets/instances/preemptible/is_preemptible.py b/compute/client_library/snippets/instances/preemptible/is_preemptible.py
new file mode 100644
index 00000000000..8a0c966fdfc
--- /dev/null
+++ b/compute/client_library/snippets/instances/preemptible/is_preemptible.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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_preemptible_check]
+from google.cloud import compute_v1
+
+
+def is_preemptible(project_id: str, zone: str, instance_name: str) -> bool:
+ """
+ Check if a given instance is preemptible or not.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to check.
+ Returns:
+ The preemptible status of the instance.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return instance.scheduling.preemptible
+
+
+# [END compute_preemptible_check]
diff --git a/compute/client_library/snippets/instances/preemptible/preemption_history.py b/compute/client_library/snippets/instances/preemptible/preemption_history.py
new file mode 100644
index 00000000000..23a1f79745d
--- /dev/null
+++ b/compute/client_library/snippets/instances/preemptible/preemption_history.py
@@ -0,0 +1,87 @@
+# Copyright 2022 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_preemptible_history]
+import datetime
+from typing import List, Tuple
+
+from google.cloud import compute_v1
+from google.cloud.compute_v1.services.zone_operations import pagers
+
+
+def list_zone_operations(
+ project_id: str, zone: str, filter: str = ""
+) -> pagers.ListPager:
+ """
+ List all recent operations the happened in given zone in a project. Optionally filter those
+ operations by providing a filter. More about using the filter can be found here:
+ https://cloud.google.com/compute/docs/reference/rest/v1/zoneOperations/list
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ filter: filter string to be used for this listing operation.
+ Returns:
+ List of preemption operations in given zone.
+ """
+ operation_client = compute_v1.ZoneOperationsClient()
+ request = compute_v1.ListZoneOperationsRequest()
+ request.project = project_id
+ request.zone = zone
+ request.filter = filter
+
+ return operation_client.list(request)
+
+
+def preemption_history(
+ project_id: str, zone: str, instance_name: str = None
+) -> List[Tuple[str, datetime.datetime]]:
+ """
+ Get a list of preemption operations from given zone in a project. Optionally limit
+ the results to instance name.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to look for.
+ Returns:
+ List of preemption operations in given zone.
+ """
+ if instance_name:
+ filter = (
+ f'operationType="compute.instances.preempted" '
+ f"AND targetLink:instances/{instance_name}"
+ )
+ else:
+ filter = 'operationType="compute.instances.preempted"'
+
+ history = []
+
+ for operation in list_zone_operations(project_id, zone, filter):
+ this_instance_name = operation.target_link.rsplit("/", maxsplit=1)[1]
+ if instance_name and this_instance_name == instance_name:
+ # The filter used is not 100% accurate, it's `contains` not `equals`
+ # So we need to check the name to make sure it's the one we want.
+ moment = datetime.datetime.fromisoformat(operation.insert_time)
+ history.append((instance_name, moment))
+
+ return history
+
+
+# [END compute_preemptible_history]
diff --git a/compute/client_library/snippets/instances/reset.py b/compute/client_library/snippets/instances/reset.py
new file mode 100644
index 00000000000..110d84eb1d1
--- /dev/null
+++ b/compute/client_library/snippets/instances/reset.py
@@ -0,0 +1,97 @@
+# Copyright 2022 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_reset_instance]
+import sys
+import time
+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:
+ """
+ This method will wait 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 reset_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Resets a stopped Google Compute Engine instance (with unencrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to reset.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.reset(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "instance reset")
+
+ return
+
+
+# [END compute_reset_instance]
diff --git a/compute/client_library/snippets/instances/resume.py b/compute/client_library/snippets/instances/resume.py
new file mode 100644
index 00000000000..04d08705c93
--- /dev/null
+++ b/compute/client_library/snippets/instances/resume.py
@@ -0,0 +1,105 @@
+# Copyright 2022 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_resume_instance]
+import sys
+import time
+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:
+ """
+ This method will wait 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 resume_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Resume a suspended Google Compute Engine instance (with unencrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance you want to resume.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ if instance.status != compute_v1.Instance.Status.SUSPENDED.name:
+ raise RuntimeError(
+ f"Only suspended instances can be resumed. "
+ f"Instance {instance_name} is in {instance.status} state."
+ )
+
+ operation = instance_client.resume(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "instance resumption")
+ return
+
+
+# [END compute_resume_instance]
diff --git a/compute/client_library/snippets/instances/spot/__init__.py b/compute/client_library/snippets/instances/spot/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/instances/spot/create.py b/compute/client_library/snippets/instances/spot/create.py
new file mode 100644
index 00000000000..ddf437f6bfc
--- /dev/null
+++ b/compute/client_library/snippets/instances/spot/create.py
@@ -0,0 +1,287 @@
+# Copyright 2022 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_spot_create]
+import re
+import sys
+from typing import Any, List
+import warnings
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+def get_image_from_family(project: str, family: str) -> compute_v1.Image:
+ """
+ Retrieve the newest image that is part of a given family in a project.
+
+ Args:
+ project: project ID or project number of the Cloud project you want to get image from.
+ family: name of the image family you want to get image from.
+
+ Returns:
+ An Image object.
+ """
+ image_client = compute_v1.ImagesClient()
+ # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
+ newest_image = image_client.get_from_family(project=project, family=family)
+ return newest_image
+
+
+def disk_from_image(
+ disk_type: str,
+ disk_size_gb: int,
+ boot: bool,
+ source_image: str,
+ auto_delete: bool = True,
+) -> compute_v1.AttachedDisk:
+ """
+ Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
+ source for the new disk.
+
+ Args:
+ disk_type: the type of disk you want to create. This value uses the following format:
+ "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
+ For example: "zones/us-west3-b/diskTypes/pd-ssd"
+ disk_size_gb: size of the new disk in gigabytes
+ boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
+ source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
+ of the publicly available images or an image from one of your projects.
+ This value uses the following format: "projects/{project_name}/global/images/{image_name}"
+ auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it
+
+ Returns:
+ AttachedDisk object configured to be created using the specified image.
+ """
+ boot_disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = source_image
+ initialize_params.disk_size_gb = disk_size_gb
+ initialize_params.disk_type = disk_type
+ boot_disk.initialize_params = initialize_params
+ # Remember to set auto_delete to True if you want the disk to be deleted when you delete
+ # your VM instance.
+ boot_disk.auto_delete = auto_delete
+ boot_disk.boot = boot
+ return boot_disk
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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.name = 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}"
+
+ if accelerators:
+ instance.guest_accelerators = accelerators
+
+ 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 = compute_v1.Scheduling()
+ 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_spot_instance(
+ project_id: str, zone: str, instance_name: str
+) -> compute_v1.Instance:
+ """
+ Create a new Spot VM instance with Debian 10 operating system.
+
+ 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.
+
+ Returns:
+ Instance object.
+ """
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-11")
+ disk_type = f"zones/{zone}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+ instance = create_instance(project_id, zone, instance_name, disks, spot=True)
+ return instance
+
+
+# [END compute_spot_create]
diff --git a/compute/client_library/snippets/instances/spot/is_spot_vm.py b/compute/client_library/snippets/instances/spot/is_spot_vm.py
new file mode 100644
index 00000000000..4cb83657094
--- /dev/null
+++ b/compute/client_library/snippets/instances/spot/is_spot_vm.py
@@ -0,0 +1,46 @@
+# Copyright 2022 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_spot_check]
+from google.cloud import compute_v1
+
+
+def is_spot_vm(project_id: str, zone: str, instance_name: str) -> bool:
+ """
+ Check if a given instance is Spot VM or not.
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ zone: name of the zone you want to use. For example: "us-west3-b"
+ instance_name: name of the virtual machine to check.
+ Returns:
+ The Spot VM status of the instance.
+ """
+ instance_client = compute_v1.InstancesClient()
+ instance = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ return (
+ instance.scheduling.provisioning_model
+ == compute_v1.Scheduling.ProvisioningModel.SPOT.name
+ )
+
+
+# [END compute_spot_check]
diff --git a/compute/client_library/snippets/instances/start.py b/compute/client_library/snippets/instances/start.py
new file mode 100644
index 00000000000..70e3e34481d
--- /dev/null
+++ b/compute/client_library/snippets/instances/start.py
@@ -0,0 +1,96 @@
+# Copyright 2022 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_start_instance]
+import sys
+import time
+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:
+ """
+ This method will wait 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_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Starts a stopped Google Compute Engine instance (with unencrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to start.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.start(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "instance start")
+ return
+
+
+# [END compute_start_instance]
diff --git a/compute/client_library/snippets/instances/start_encrypted.py b/compute/client_library/snippets/instances/start_encrypted.py
new file mode 100644
index 00000000000..ecfa1cd2620
--- /dev/null
+++ b/compute/client_library/snippets/instances/start_encrypted.py
@@ -0,0 +1,118 @@
+# Copyright 2022 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_start_enc_instance]
+import sys
+import time
+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:
+ """
+ This method will wait 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_instance_with_encryption_key(
+ project_id: str, zone: str, instance_name: str, key: bytes
+):
+ """
+ Starts a stopped Google Compute Engine instance (with encrypted disks).
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to start.
+ key: bytes object representing a raw base64 encoded key to your machines boot disk.
+ For more information about disk encryption see:
+ https://cloud.google.com/compute/docs/disks/customer-supplied-encryption#specifications
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ instance_data = instance_client.get(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ # Prepare the information about disk encryption
+ disk_data = compute_v1.CustomerEncryptionKeyProtectedDisk()
+ disk_data.source = instance_data.disks[0].source
+ disk_data.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ # Use raw_key to send over the key to unlock the disk
+ # To use a key stored in KMS, you need to provide `kms_key_name` and `kms_key_service_account`
+ disk_data.disk_encryption_key.raw_key = key
+ enc_data = compute_v1.InstancesStartWithEncryptionKeyRequest()
+ enc_data.disks = [disk_data]
+
+ operation = instance_client.start_with_encryption_key(
+ project=project_id,
+ zone=zone,
+ instance=instance_name,
+ instances_start_with_encryption_key_request_resource=enc_data,
+ )
+
+ wait_for_extended_operation(operation, "instance start (with encrypted disk)")
+ return
+
+
+# [END compute_start_enc_instance]
diff --git a/compute/client_library/snippets/instances/stop.py b/compute/client_library/snippets/instances/stop.py
new file mode 100644
index 00000000000..1b675c4f115
--- /dev/null
+++ b/compute/client_library/snippets/instances/stop.py
@@ -0,0 +1,95 @@
+# Copyright 2022 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_stop_instance]
+import sys
+import time
+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:
+ """
+ This method will wait 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_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Stops a running Google Compute Engine instance.
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance your want to stop.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.stop(
+ project=project_id, zone=zone, instance=instance_name
+ )
+ wait_for_extended_operation(operation, "instance stopping")
+ return
+
+
+# [END compute_stop_instance]
diff --git a/compute/client_library/snippets/instances/suspend.py b/compute/client_library/snippets/instances/suspend.py
new file mode 100644
index 00000000000..4a7c373dafc
--- /dev/null
+++ b/compute/client_library/snippets/instances/suspend.py
@@ -0,0 +1,96 @@
+# Copyright 2022 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_suspend_instance]
+import sys
+import time
+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:
+ """
+ This method will wait 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 suspend_instance(project_id: str, zone: str, instance_name: str) -> None:
+ """
+ Suspend a running Google Compute Engine instance.
+ Args:
+ project_id: project ID or project number of the Cloud project your instance belongs to.
+ zone: name of the zone your instance belongs to.
+ instance_name: name of the instance you want to suspend.
+ """
+ instance_client = compute_v1.InstancesClient()
+
+ operation = instance_client.suspend(
+ project=project_id, zone=zone, instance=instance_name
+ )
+
+ wait_for_extended_operation(operation, "suspend instance")
+ return
+
+
+# [END compute_suspend_instance]
diff --git a/compute/client_library/snippets/operations/__init__.py b/compute/client_library/snippets/operations/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/operations/operation_check.py b/compute/client_library/snippets/operations/operation_check.py
new file mode 100644
index 00000000000..d136372c39c
--- /dev/null
+++ b/compute/client_library/snippets/operations/operation_check.py
@@ -0,0 +1,68 @@
+# Copyright 2022 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
+#
+# 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_instances_operation_check]
+from google.cloud import compute_v1
+
+
+def wait_for_operation(
+ operation: compute_v1.Operation, project_id: str
+) -> compute_v1.Operation:
+ """
+ This method waits for an operation to be completed. Calling this function
+ will block until the operation is finished.
+
+ Args:
+ operation: The Operation object representing the operation you want to
+ wait on.
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ Finished Operation object.
+ """
+ kwargs = {"project": project_id, "operation": operation.name}
+ if operation.zone:
+ client = compute_v1.ZoneOperationsClient()
+ # Operation.zone is a full URL address of a zone, so we need to extract just the name
+ kwargs["zone"] = operation.zone.rsplit("/", maxsplit=1)[1]
+ elif operation.region:
+ client = compute_v1.RegionOperationsClient()
+ # Operation.region is a full URL address of a region, so we need to extract just the name
+ kwargs["region"] = operation.region.rsplit("/", maxsplit=1)[1]
+ else:
+ client = compute_v1.GlobalOperationsClient()
+ return client.wait(**kwargs)
+
+
+# [END compute_instances_operation_check]
diff --git a/compute/client_library/snippets/routes/create.py b/compute/client_library/snippets/routes/create.py
new file mode 100644
index 00000000000..abc3c77e5b3
--- /dev/null
+++ b/compute/client_library/snippets/routes/create.py
@@ -0,0 +1,152 @@
+# Copyright 2022 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_route_create]
+import sys
+from typing import Any, Optional
+
+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:
+ """
+ This method will wait 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_route(
+ project_id: str,
+ network: str,
+ route_name: str,
+ destination_range: str,
+ *,
+ next_hop_gateway: Optional[str] = None,
+ next_hop_ip: Optional[str] = None,
+ next_hop_instance: Optional[str] = None,
+ next_hop_vpn_tunnel: Optional[str] = None,
+ next_hop_ilb: Optional[str] = None,
+) -> compute_v1.Route:
+ """
+ Create a new route in selected network by providing a destination and next hop name.
+
+ Note: The set of {next_hop_gateway, next_hop_ip, next_hop_instance, next_hop_vpn_tunnel,
+ next_hop_ilb} is exclusive, you and only specify one of those parameters.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ network: name of the network the route will be created in. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+ route_name: name of the new route.
+ destination_range: range of destination IPs this route should be applied to. E.g. 10.0.0.0/16.
+ next_hop_gateway: name of the gateway the traffic should be directed to.
+ next_hop_ip: IP address the traffic should be directed to.
+ next_hop_instance: name of the instance the traffic should be directed to. Name format:
+ "projects/{project}/zones/{zone}/instances/{instance_name}"
+ next_hop_vpn_tunnel: name of the VPN tunnel the traffic should be directed to. Name format:
+ "projects/{project}/regions/{region}/vpnTunnels/{vpn_tunnel_name}"
+ next_hop_ilb: name of a forwarding rule of the Internal Load Balancer the traffic
+ should be directed to. Name format:
+ "projects/{project}/regions/{region}/forwardingRules/{forwarding_rule_region}"
+
+ Returns:
+ A new compute_v1.Route object.
+ """
+ excl_args = {
+ next_hop_instance,
+ next_hop_ilb,
+ next_hop_vpn_tunnel,
+ next_hop_gateway,
+ next_hop_ip,
+ }
+ args_set = sum(1 if arg is not None else 0 for arg in excl_args)
+
+ if args_set != 1:
+ raise RuntimeError("You must specify exactly one next_hop_* parameter.")
+
+ route = compute_v1.Route()
+ route.name = route_name
+ route.network = network
+ route.dest_range = destination_range
+
+ if next_hop_gateway:
+ route.next_hop_gateway = next_hop_gateway
+ elif next_hop_ip:
+ route.next_hop_ip = next_hop_ip
+ elif next_hop_instance:
+ route.next_hop_instance = next_hop_instance
+ elif next_hop_vpn_tunnel:
+ route.next_hop_vpn_tunnel = next_hop_vpn_tunnel
+ elif next_hop_ilb:
+ route.next_hop_ilb = next_hop_ilb
+
+ route_client = compute_v1.RoutesClient()
+ operation = route_client.insert(project=project_id, route_resource=route)
+
+ wait_for_extended_operation(operation, "route creation")
+
+ return route_client.get(project=project_id, route=route_name)
+
+
+# [END compute_route_create]
diff --git a/compute/client_library/snippets/routes/create_kms_route.py b/compute/client_library/snippets/routes/create_kms_route.py
new file mode 100644
index 00000000000..eeb2cffc876
--- /dev/null
+++ b/compute/client_library/snippets/routes/create_kms_route.py
@@ -0,0 +1,194 @@
+# Copyright 2022 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.
+#
+# 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_create_route_windows_activation]
+import sys
+from typing import Any, Optional
+
+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:
+ """
+ This method will wait 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_route(
+ project_id: str,
+ network: str,
+ route_name: str,
+ destination_range: str,
+ *,
+ next_hop_gateway: Optional[str] = None,
+ next_hop_ip: Optional[str] = None,
+ next_hop_instance: Optional[str] = None,
+ next_hop_vpn_tunnel: Optional[str] = None,
+ next_hop_ilb: Optional[str] = None,
+) -> compute_v1.Route:
+ """
+ Create a new route in selected network by providing a destination and next hop name.
+
+ Note: The set of {next_hop_gateway, next_hop_ip, next_hop_instance, next_hop_vpn_tunnel,
+ next_hop_ilb} is exclusive, you and only specify one of those parameters.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ network: name of the network the route will be created in. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+ route_name: name of the new route.
+ destination_range: range of destination IPs this route should be applied to. E.g. 10.0.0.0/16.
+ next_hop_gateway: name of the gateway the traffic should be directed to.
+ next_hop_ip: IP address the traffic should be directed to.
+ next_hop_instance: name of the instance the traffic should be directed to. Name format:
+ "projects/{project}/zones/{zone}/instances/{instance_name}"
+ next_hop_vpn_tunnel: name of the VPN tunnel the traffic should be directed to. Name format:
+ "projects/{project}/regions/{region}/vpnTunnels/{vpn_tunnel_name}"
+ next_hop_ilb: name of a forwarding rule of the Internal Load Balancer the traffic
+ should be directed to. Name format:
+ "projects/{project}/regions/{region}/forwardingRules/{forwarding_rule_region}"
+
+ Returns:
+ A new compute_v1.Route object.
+ """
+ excl_args = {
+ next_hop_instance,
+ next_hop_ilb,
+ next_hop_vpn_tunnel,
+ next_hop_gateway,
+ next_hop_ip,
+ }
+ args_set = sum(1 if arg is not None else 0 for arg in excl_args)
+
+ if args_set != 1:
+ raise RuntimeError("You must specify exactly one next_hop_* parameter.")
+
+ route = compute_v1.Route()
+ route.name = route_name
+ route.network = network
+ route.dest_range = destination_range
+
+ if next_hop_gateway:
+ route.next_hop_gateway = next_hop_gateway
+ elif next_hop_ip:
+ route.next_hop_ip = next_hop_ip
+ elif next_hop_instance:
+ route.next_hop_instance = next_hop_instance
+ elif next_hop_vpn_tunnel:
+ route.next_hop_vpn_tunnel = next_hop_vpn_tunnel
+ elif next_hop_ilb:
+ route.next_hop_ilb = next_hop_ilb
+
+ route_client = compute_v1.RoutesClient()
+ operation = route_client.insert(project=project_id, route_resource=route)
+
+ wait_for_extended_operation(operation, "route creation")
+
+ return route_client.get(project=project_id, route=route_name)
+
+
+def create_route_to_windows_activation_host(
+ project_id: str, network: str, route_name: str
+) -> compute_v1.Route:
+ """
+ If you have Windows instances without external IP addresses,
+ you must also enable Private Google Access so that instances
+ with only internal IP addresses can send traffic to the external
+ IP address for kms.windows.googlecloud.com.
+ More infromation: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ network: name of the network the route will be created in. Available name formats:
+ * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network}
+ * projects/{project_id}/global/networks/{network}
+ * global/networks/{network}
+ route_name: name of the new route.
+
+ Returns:
+ A new compute_v1.Route object.
+ """
+ return create_route(
+ project_id=project_id,
+ network=network,
+ route_name=route_name,
+ destination_range="35.190.247.13/32",
+ next_hop_gateway=f"projects/{project_id}/global/gateways/default-internet-gateway",
+ )
+
+
+# [END compute_create_route_windows_activation]
diff --git a/compute/client_library/snippets/routes/delete.py b/compute/client_library/snippets/routes/delete.py
new file mode 100644
index 00000000000..6ef1de95b1c
--- /dev/null
+++ b/compute/client_library/snippets/routes/delete.py
@@ -0,0 +1,94 @@
+# Copyright 2022 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_route_delete]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 delete_route(project_id: str, route_name: str) -> NoReturn:
+ """
+ Delete a route in project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ route_name: name of the route to delete.
+ """
+
+ route_client = compute_v1.RoutesClient()
+ operation = route_client.delete(project=project_id, route=route_name)
+
+ wait_for_extended_operation(operation, "route deletion")
+
+ return
+
+
+# [END compute_route_delete]
diff --git a/compute/client_library/snippets/routes/list.py b/compute/client_library/snippets/routes/list.py
new file mode 100644
index 00000000000..b4f83fb07d3
--- /dev/null
+++ b/compute/client_library/snippets/routes/list.py
@@ -0,0 +1,45 @@
+# Copyright 2022 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_route_list]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def list_routes(
+ project_id: str,
+) -> Iterable[compute_v1.Route]:
+ """
+ Lists routes in project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+
+ Returns:
+ An iterable collection of routes found in given project.
+ """
+
+ route_client = compute_v1.RoutesClient()
+ return route_client.list(project=project_id)
+
+
+# [END compute_route_list]
diff --git a/compute/client_library/snippets/snapshots/__init__.py b/compute/client_library/snippets/snapshots/__init__.py
new file mode 100644
index 00000000000..4bbe0ffdb06
--- /dev/null
+++ b/compute/client_library/snippets/snapshots/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 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.
diff --git a/compute/client_library/snippets/snapshots/create.py b/compute/client_library/snippets/snapshots/create.py
new file mode 100644
index 00000000000..fb1b75c0fa5
--- /dev/null
+++ b/compute/client_library/snippets/snapshots/create.py
@@ -0,0 +1,145 @@
+# Copyright 2022 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_snapshot_create]
+import sys
+from typing import Any, Optional
+
+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:
+ """
+ This method will wait 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_snapshot(
+ project_id: str,
+ disk_name: str,
+ snapshot_name: str,
+ *,
+ zone: Optional[str] = None,
+ region: Optional[str] = None,
+ location: Optional[str] = None,
+ disk_project_id: Optional[str] = None,
+) -> compute_v1.Snapshot:
+ """
+ Create a snapshot of a disk.
+
+ You need to pass `zone` or `region` parameter relevant to the disk you want to
+ snapshot, but not both. Pass `zone` parameter for zonal disks and `region` for
+ regional disks.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want
+ to use to store the snapshot.
+ disk_name: name of the disk you want to snapshot.
+ snapshot_name: name of the snapshot to be created.
+ zone: name of the zone in which is the disk you want to snapshot (for zonal disks).
+ region: name of the region in which is the disk you want to snapshot (for regional disks).
+ location: The Cloud Storage multi-region or the Cloud Storage region where you
+ want to store your snapshot.
+ You can specify only one storage location. Available locations:
+ https://cloud.google.com/storage/docs/locations#available-locations
+ disk_project_id: project ID or project number of the Cloud project that
+ hosts the disk you want to snapshot. If not provided, will look for
+ the disk in the `project_id` project.
+
+ Returns:
+ The new snapshot instance.
+ """
+ if zone is None and region is None:
+ raise RuntimeError(
+ "You need to specify `zone` or `region` for this function to work."
+ )
+ if zone is not None and region is not None:
+ raise RuntimeError("You can't set both `zone` and `region` parameters.")
+
+ if disk_project_id is None:
+ disk_project_id = project_id
+
+ if zone is not None:
+ disk_client = compute_v1.DisksClient()
+ disk = disk_client.get(project=disk_project_id, zone=zone, disk=disk_name)
+ else:
+ regio_disk_client = compute_v1.RegionDisksClient()
+ disk = regio_disk_client.get(
+ project=disk_project_id, region=region, disk=disk_name
+ )
+
+ snapshot = compute_v1.Snapshot()
+ snapshot.source_disk = disk.self_link
+ snapshot.name = snapshot_name
+ if location:
+ snapshot.storage_locations = [location]
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ operation = snapshot_client.insert(project=project_id, snapshot_resource=snapshot)
+
+ wait_for_extended_operation(operation, "snapshot creation")
+
+ return snapshot_client.get(project=project_id, snapshot=snapshot_name)
+
+
+# [END compute_snapshot_create]
diff --git a/compute/client_library/snippets/snapshots/delete.py b/compute/client_library/snippets/snapshots/delete.py
new file mode 100644
index 00000000000..5735dfba25e
--- /dev/null
+++ b/compute/client_library/snippets/snapshots/delete.py
@@ -0,0 +1,94 @@
+# Copyright 2022 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_snapshot_delete]
+import sys
+from typing import Any, NoReturn
+
+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:
+ """
+ This method will wait 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 delete_snapshot(project_id: str, snapshot_name: str) -> NoReturn:
+ """
+ Delete a snapshot of a disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ snapshot_name: name of the snapshot to delete.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ operation = snapshot_client.delete(project=project_id, snapshot=snapshot_name)
+
+ wait_for_extended_operation(operation, "snapshot deletion")
+
+ return
+
+
+# [END compute_snapshot_delete]
diff --git a/compute/client_library/snippets/snapshots/delete_by_filter.py b/compute/client_library/snippets/snapshots/delete_by_filter.py
new file mode 100644
index 00000000000..a71bda1de45
--- /dev/null
+++ b/compute/client_library/snippets/snapshots/delete_by_filter.py
@@ -0,0 +1,128 @@
+# Copyright 2022 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_snapshot_delete_by_filter]
+import sys
+from typing import Any, Iterable, NoReturn
+
+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:
+ """
+ This method will wait 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 delete_snapshot(project_id: str, snapshot_name: str) -> NoReturn:
+ """
+ Delete a snapshot of a disk.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ snapshot_name: name of the snapshot to delete.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ operation = snapshot_client.delete(project=project_id, snapshot=snapshot_name)
+
+ wait_for_extended_operation(operation, "snapshot deletion")
+
+ return
+
+
+def list_snapshots(project_id: str, filter: str = "") -> Iterable[compute_v1.Snapshot]:
+ """
+ List snapshots from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ filter: filter to be applied when listing snapshots. Learn more about filters here:
+ https://cloud.google.com/python/docs/reference/compute/latest/google.cloud.compute_v1.types.ListSnapshotsRequest
+
+ Returns:
+ An iterable containing all Snapshots that match the provided filter.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ request = compute_v1.ListSnapshotsRequest()
+ request.project = project_id
+ request.filter = filter
+
+ return snapshot_client.list(request)
+
+
+def delete_snapshots_by_filter(project_id: str, filter: str):
+ """
+ Deletes all snapshots in project that meet the filter criteria.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ filter: filter to be applied when looking for snapshots for deletion.
+ """
+ for snapshot in list_snapshots(project_id, filter):
+ delete_snapshot(project_id, snapshot.name)
+
+
+# [END compute_snapshot_delete_by_filter]
diff --git a/compute/client_library/snippets/snapshots/get.py b/compute/client_library/snippets/snapshots/get.py
new file mode 100644
index 00000000000..bb6fa1d38a0
--- /dev/null
+++ b/compute/client_library/snippets/snapshots/get.py
@@ -0,0 +1,45 @@
+# Copyright 2022 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_snapshot_get]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def get_snapshot(project_id: str, snapshot_name: str) -> compute_v1.Snapshot:
+ """
+ Get information about a Snapshot.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ snapshot_name: the name of the snapshot you want to look up.
+
+ Returns:
+ A Snapshot object.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+
+ return snapshot_client.get(project=project_id, snapshot=snapshot_name)
+
+
+# [END compute_snapshot_get]
diff --git a/compute/client_library/snippets/snapshots/list.py b/compute/client_library/snippets/snapshots/list.py
new file mode 100644
index 00000000000..4c5a89761e9
--- /dev/null
+++ b/compute/client_library/snippets/snapshots/list.py
@@ -0,0 +1,49 @@
+# Copyright 2022 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_snapshot_list]
+from typing import Iterable
+
+from google.cloud import compute_v1
+
+
+def list_snapshots(project_id: str, filter: str = "") -> Iterable[compute_v1.Snapshot]:
+ """
+ List snapshots from a project.
+
+ Args:
+ project_id: project ID or project number of the Cloud project you want to use.
+ filter: filter to be applied when listing snapshots. Learn more about filters here:
+ https://cloud.google.com/python/docs/reference/compute/latest/google.cloud.compute_v1.types.ListSnapshotsRequest
+
+ Returns:
+ An iterable containing all Snapshots that match the provided filter.
+ """
+
+ snapshot_client = compute_v1.SnapshotsClient()
+ request = compute_v1.ListSnapshotsRequest()
+ request.project = project_id
+ request.filter = filter
+
+ return snapshot_client.list(request)
+
+
+# [END compute_snapshot_list]
diff --git a/compute/client_library/snippets/tests/__init__.py b/compute/client_library/snippets/tests/__init__.py
new file mode 100644
index 00000000000..4bbe0ffdb06
--- /dev/null
+++ b/compute/client_library/snippets/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 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.
diff --git a/compute/client_library/snippets/tests/test_bulk.py b/compute/client_library/snippets/tests/test_bulk.py
new file mode 100644
index 00000000000..2d270f1245a
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_bulk.py
@@ -0,0 +1,76 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+from ..instances.bulk_insert import create_five_instances
+from ..instances.delete import delete_instance
+
+PROJECT = google.auth.default()[1]
+INSTANCE_ZONE = "australia-southeast1-a"
+
+
+@pytest.fixture
+def instance_template():
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-11"
+ )
+ initialize_params.disk_size_gb = 25
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.name = "global/networks/default"
+
+ template = compute_v1.InstanceTemplate()
+ template.name = "test-template-" + uuid.uuid4().hex[:10]
+ template.properties.disks = [disk]
+ template.properties.machine_type = "n1-standard-4"
+ template.properties.network_interfaces = [network_interface]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation_client = compute_v1.GlobalOperationsClient()
+ op = template_client.insert_unary(
+ project=PROJECT, instance_template_resource=template
+ )
+ operation_client.wait(project=PROJECT, operation=op.name)
+
+ template = template_client.get(project=PROJECT, instance_template=template.name)
+
+ yield template
+
+ op = template_client.delete_unary(project=PROJECT, instance_template=template.name)
+ operation_client.wait(project=PROJECT, operation=op.name)
+
+
+def test_bulk_create(instance_template):
+ name_pattern = "i-##-" + uuid.uuid4().hex[:5]
+
+ instances = create_five_instances(PROJECT, INSTANCE_ZONE, instance_template.name,
+ name_pattern)
+
+ names = [instance.name for instance in instances]
+ try:
+ for i in range(1, 6):
+ name = name_pattern.replace('##', f"0{i}")
+ assert name in names
+ finally:
+ for name in names:
+ delete_instance(PROJECT, INSTANCE_ZONE, name)
diff --git a/compute/client_library/snippets/tests/test_create_vm.py b/compute/client_library/snippets/tests/test_create_vm.py
new file mode 100644
index 00000000000..5a2235efe0d
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_create_vm.py
@@ -0,0 +1,256 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+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
+
+from ..instances.create_start_instance.create_from_custom_image import (
+ create_from_custom_image,
+)
+from ..instances.create_start_instance.create_from_public_image import (
+ create_from_public_image,
+)
+from ..instances.create_start_instance.create_from_snapshot import create_from_snapshot
+from ..instances.create_start_instance.create_with_additional_disk import (
+ create_with_additional_disk,
+)
+from ..instances.create_start_instance.create_with_existing_disks import create_with_existing_disks
+from ..instances.create_start_instance.create_with_local_ssd import create_with_ssd
+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
+
+PROJECT = google.auth.default()[1]
+REGION = "us-central1"
+INSTANCE_ZONE = "us-central1-b"
+
+
+def get_active_debian():
+ image_client = compute_v1.ImagesClient()
+
+ return image_client.get_from_family(project="debian-cloud", family="debian-11")
+
+
+@pytest.fixture()
+def src_disk():
+ disk_client = compute_v1.DisksClient()
+
+ disk = compute_v1.Disk()
+ disk.source_image = get_active_debian().self_link
+ disk.name = "test-disk-" + uuid.uuid4().hex[:10]
+ op = disk_client.insert_unary(
+ project=PROJECT, zone=INSTANCE_ZONE, disk_resource=disk
+ )
+
+ wait_for_operation(op, PROJECT)
+ try:
+ disk = disk_client.get(project=PROJECT, zone=INSTANCE_ZONE, disk=disk.name)
+ yield disk
+ finally:
+ op = disk_client.delete_unary(
+ project=PROJECT, zone=INSTANCE_ZONE, disk=disk.name
+ )
+ wait_for_operation(op, PROJECT)
+
+
+@pytest.fixture()
+def snapshot(src_disk):
+ snapshot_client = compute_v1.SnapshotsClient()
+ snapshot = compute_v1.Snapshot()
+ snapshot.name = "test-snap-" + uuid.uuid4().hex[:10]
+ disk_client = compute_v1.DisksClient()
+ op = disk_client.create_snapshot_unary(
+ project=PROJECT,
+ zone=INSTANCE_ZONE,
+ disk=src_disk.name,
+ snapshot_resource=snapshot,
+ )
+ wait_for_operation(op, PROJECT)
+ try:
+ snapshot = snapshot_client.get(
+ project=PROJECT, snapshot=snapshot.name
+ )
+
+ yield snapshot
+ finally:
+ op = snapshot_client.delete_unary(project=PROJECT, snapshot=snapshot.name)
+ wait_for_operation(op, PROJECT)
+
+
+@pytest.fixture()
+def image(src_disk):
+ image_client = compute_v1.ImagesClient()
+ image = compute_v1.Image()
+ image.source_disk = src_disk.self_link
+ image.name = "test-image-" + uuid.uuid4().hex[:10]
+ op = image_client.insert_unary(project=PROJECT, image_resource=image)
+
+ wait_for_operation(op, PROJECT)
+ try:
+ image = image_client.get(project=PROJECT, image=image.name)
+ yield image
+ finally:
+ op = image_client.delete_unary(project=PROJECT, image=image.name)
+ wait_for_operation(op, PROJECT)
+
+
+@pytest.fixture()
+def boot_disk():
+ debian_image = get_active_debian()
+ disk_name = "test-disk-" + uuid.uuid4().hex[:10]
+ disk = create_disk_from_image(PROJECT, INSTANCE_ZONE, disk_name,
+ f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard",
+ 13, debian_image.self_link)
+ yield disk
+ delete_disk(PROJECT, INSTANCE_ZONE, disk_name)
+
+
+@pytest.fixture()
+def empty_disk():
+ disk_name = "test-disk-" + uuid.uuid4().hex[:10]
+ disk = create_empty_disk(PROJECT, INSTANCE_ZONE, disk_name,
+ f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard",
+ 14)
+
+ yield disk
+ delete_disk(PROJECT, INSTANCE_ZONE, disk_name)
+
+
+def test_create_from_custom_image(image):
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_from_custom_image(
+ PROJECT, INSTANCE_ZONE, instance_name, image.self_link
+ )
+ try:
+ assert (
+ instance.disks[0].disk_size_gb == 10
+ )
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_from_public_image():
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_from_public_image(
+ PROJECT,
+ INSTANCE_ZONE,
+ instance_name,
+ )
+ try:
+ assert instance.disks[0].disk_size_gb == 10
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_from_snapshot(snapshot):
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_from_snapshot(
+ PROJECT, INSTANCE_ZONE, instance_name, snapshot.self_link
+ )
+ try:
+ assert (
+ instance.disks[0].disk_size_gb == 20
+ )
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_with_additional_disk():
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_with_additional_disk(PROJECT, INSTANCE_ZONE, instance_name)
+ try:
+ assert any(
+ disk.disk_size_gb == 20 for disk in instance.disks
+ )
+ assert any(
+ disk.disk_size_gb == 25 for disk in instance.disks
+ )
+ assert len(instance.disks) == 2
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_with_snapshotted_data_disk(snapshot):
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_with_snapshotted_data_disk(
+ PROJECT, INSTANCE_ZONE, instance_name, snapshot.self_link
+ )
+ try:
+ assert any(
+ disk.disk_size_gb == 11 for disk in instance.disks
+ )
+ assert any(
+ disk.disk_size_gb == 10 for disk in instance.disks
+ )
+ assert len(instance.disks) == 2
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_with_subnet():
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_with_subnet(
+ PROJECT,
+ INSTANCE_ZONE,
+ instance_name,
+ "global/networks/default",
+ f"regions/{REGION}/subnetworks/default",
+ )
+ try:
+ assert instance.network_interfaces[0].network.endswith("global/networks/default")
+ assert (
+ instance.network_interfaces[0].subnetwork.endswith(f"regions/{REGION}/subnetworks/default")
+ )
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_with_existing_disks(boot_disk, empty_disk):
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_with_existing_disks(PROJECT, INSTANCE_ZONE, instance_name,
+ [boot_disk.name, empty_disk.name])
+
+ try:
+ assert any(
+ disk.disk_size_gb == 13 for disk in instance.disks
+ )
+ assert any(
+ disk.disk_size_gb == 14 for disk in instance.disks
+ )
+ assert len(instance.disks) == 2
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_with_ssd():
+ instance_name = "i" + uuid.uuid4().hex[:10]
+ instance = create_with_ssd(PROJECT, INSTANCE_ZONE, instance_name)
+
+ try:
+ assert any(
+ disk.type_ == compute_v1.AttachedDisk.Type.SCRATCH.name
+ for disk in instance.disks
+ )
+ assert len(instance.disks) == 2
+ finally:
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
diff --git a/compute/client_library/snippets/tests/test_custom_hostnames.py b/compute/client_library/snippets/tests/test_custom_hostnames.py
new file mode 100644
index 00000000000..b8583db39bb
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_custom_hostnames.py
@@ -0,0 +1,51 @@
+# Copyright 2022 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.
+import random
+import uuid
+
+import google.auth
+import pytest
+
+from ..instances.custom_hostname.create import create_instance_custom_hostname
+from ..instances.custom_hostname.get import get_hostname
+from ..instances.delete import delete_instance
+
+PROJECT = google.auth.default()[1]
+INSTANCE_ZONE = "europe-north1-c"
+
+
+@pytest.fixture
+def autodelete_instance_name():
+ instance_name = "test-host-instance-" + uuid.uuid4().hex[:10]
+
+ yield instance_name
+
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+@pytest.fixture
+def random_hostname():
+ yield "instance.{}.hostname".format(random.randint(0, 2 ** 10))
+
+
+def test_custom_hostname(autodelete_instance_name, random_hostname):
+ instance = create_instance_custom_hostname(
+ PROJECT, INSTANCE_ZONE, autodelete_instance_name, random_hostname
+ )
+ assert instance.name == autodelete_instance_name
+ assert instance.hostname == random_hostname
+ assert (
+ get_hostname(PROJECT, INSTANCE_ZONE, autodelete_instance_name)
+ == random_hostname
+ )
diff --git a/compute/client_library/snippets/tests/test_custom_types.py b/compute/client_library/snippets/tests/test_custom_types.py
new file mode 100644
index 00000000000..4b7c8108cb2
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_custom_types.py
@@ -0,0 +1,212 @@
+# Copyright 2021 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.
+import uuid
+
+import google.auth
+import pytest
+
+from ..images.get import get_image_from_family
+from ..instances.create import create_instance
+from ..instances.create_start_instance.create_from_public_image import disk_from_image
+from ..instances.custom_machine_types.create_shared_with_helper import (
+ create_custom_shared_core_instance,
+)
+from ..instances.custom_machine_types.create_with_helper import create_custom_instance
+from ..instances.custom_machine_types.helper_class import CustomMachineType
+from ..instances.custom_machine_types.update_memory import (
+ add_extended_memory_to_instance,
+)
+from ..instances.delete import delete_instance
+
+PROJECT = google.auth.default()[1]
+REGION = "us-central1"
+INSTANCE_ZONE = "us-central1-b"
+
+
+@pytest.fixture
+def auto_delete_instance_name():
+ instance_name = "test-instance-" + uuid.uuid4().hex[:10]
+ yield instance_name
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+@pytest.fixture
+def instance():
+ instance_name = "test-instance-" + uuid.uuid4().hex[:10]
+
+ newest_debian = get_image_from_family(project="debian-cloud", family="debian-10")
+ disk_type = f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link)]
+
+ instance = create_instance(
+ PROJECT, INSTANCE_ZONE, instance_name, disks, "n2-custom-8-10240"
+ )
+ yield instance
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_custom_instance_creation(auto_delete_instance_name):
+ # Need to import CustomMachineType from this module, or the assertion will fail
+ from ..instances.custom_machine_types.create_with_helper import CustomMachineType
+
+ instance = create_custom_instance(
+ PROJECT,
+ INSTANCE_ZONE,
+ auto_delete_instance_name,
+ CustomMachineType.CPUSeries.E2,
+ 4,
+ 8192,
+ )
+
+ assert instance.name == auto_delete_instance_name
+ assert instance.machine_type.endswith(
+ f"zones/{INSTANCE_ZONE}/machineTypes/e2-custom-4-8192"
+ )
+
+
+def test_custom_shared_instance_creation(auto_delete_instance_name):
+ # Need to import CustomMachineType from this module, or the assertion will fail
+ from ..instances.custom_machine_types.create_shared_with_helper import (
+ CustomMachineType,
+ )
+
+ instance = create_custom_shared_core_instance(
+ PROJECT,
+ INSTANCE_ZONE,
+ auto_delete_instance_name,
+ CustomMachineType.CPUSeries.E2_MICRO,
+ 2048,
+ )
+
+ assert instance.name == auto_delete_instance_name
+ assert instance.machine_type.endswith(
+ f"zones/{INSTANCE_ZONE}/machineTypes/e2-custom-micro-2048"
+ )
+
+
+def test_custom_machine_type_good():
+ # N1
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.N1, 8192, 8)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/custom-8-8192"
+ assert cmt.short_type_str() == "custom-8-8192"
+ # N2
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.N2, 4096, 4)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/n2-custom-4-4096"
+ assert cmt.short_type_str() == "n2-custom-4-4096"
+ # N2D
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.N2D, 8192, 4)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/n2d-custom-4-8192"
+ assert cmt.short_type_str() == "n2d-custom-4-8192"
+ # E2
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.E2, 8192, 8)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/e2-custom-8-8192"
+ assert cmt.short_type_str() == "e2-custom-8-8192"
+ # E2 SMALL
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.E2_SMALL, 4096)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/e2-custom-small-4096"
+ assert cmt.short_type_str() == "e2-custom-small-4096"
+ # E2 MICRO
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.E2_MICRO, 2048)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/e2-custom-micro-2048"
+ assert cmt.short_type_str() == "e2-custom-micro-2048"
+ # E2 MEDIUM
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.E2_MEDIUM, 8192)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/e2-custom-medium-8192"
+ assert cmt.short_type_str() == "e2-custom-medium-8192"
+
+
+def test_custom_machine_type_bad_memory_256():
+ try:
+ CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.N1, 8194, 8)
+ except RuntimeError as err:
+ assert err.args[0] == "Requested memory must be a multiple of 256 MB."
+ else:
+ assert not "This test should have raised an exception!"
+
+
+def test_custom_machine_type_ext_memory():
+ cmt = CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.N2, 638720, 8)
+ assert str(cmt) == f"zones/{INSTANCE_ZONE}/machineTypes/n2-custom-8-638720-ext"
+
+
+def test_custom_machine_type_bad_cpu_count():
+ try:
+ CustomMachineType(INSTANCE_ZONE, CustomMachineType.CPUSeries.N2, 8194, 66)
+ except RuntimeError as err:
+ assert err.args[0].startswith(
+ "Invalid number of cores requested. Allowed number of cores for"
+ )
+ else:
+ assert not "This test should have raised an exception!"
+
+
+def test_add_extended_memory_to_instance(instance):
+ instance = add_extended_memory_to_instance(
+ PROJECT, INSTANCE_ZONE, instance.name, 819200
+ )
+ assert instance.machine_type.endswith("819200-ext")
+
+
+def test_from_str_creation():
+ cmt = CustomMachineType.from_str(
+ "https://www.googleapis.com/compute/v1/projects/diregapic-mestiv/zones/us-central1-b/machineTypes/e2-custom-4-8192"
+ )
+ assert cmt.zone == "us-central1-b"
+ assert cmt.memory_mb == 8192
+ assert cmt.extra_memory_used is False
+ assert cmt.cpu_series is CustomMachineType.CPUSeries.E2
+ assert cmt.core_count == 4
+
+ cmt = CustomMachineType.from_str(
+ "zones/europe-west4-b/machineTypes/n2-custom-8-81920-ext"
+ )
+ assert cmt.zone == "europe-west4-b"
+ assert cmt.memory_mb == 81920
+ assert cmt.extra_memory_used is True
+ assert cmt.cpu_series is CustomMachineType.CPUSeries.N2
+ assert cmt.core_count == 8
+
+ cmt = CustomMachineType.from_str(
+ "zones/europe-west4-b/machineTypes/e2-custom-small-4096"
+ )
+ assert cmt.zone == "europe-west4-b"
+ assert cmt.memory_mb == 4096
+ assert cmt.extra_memory_used is False
+ assert cmt.cpu_series == CustomMachineType.CPUSeries.E2_SMALL
+ assert cmt.core_count == 2
+
+ cmt = CustomMachineType.from_str(
+ "zones/europe-central2-b/machineTypes/custom-2-2048"
+ )
+ assert cmt.zone == "europe-central2-b"
+ assert cmt.memory_mb == 2048
+ assert cmt.extra_memory_used is False
+ assert cmt.cpu_series is CustomMachineType.CPUSeries.N1
+ assert cmt.core_count == 2
+
+ try:
+ CustomMachineType.from_str(
+ "zones/europe-central2-b/machineTypes/n8-custom-2-1024"
+ )
+ except RuntimeError as err:
+ assert err.args[0] == "Unknown CPU series."
+ else:
+ assert not "This was supposed to raise a RuntimeError."
+
+ cmt = CustomMachineType.from_str("n2d-custom-8-81920-ext")
+ assert cmt.zone is None
+ assert cmt.memory_mb == 81920
+ assert cmt.extra_memory_used is True
+ assert cmt.cpu_series is CustomMachineType.CPUSeries.N2D
+ assert cmt.core_count == 8
diff --git a/compute/client_library/snippets/tests/test_default_values.py b/compute/client_library/snippets/tests/test_default_values.py
new file mode 100644
index 00000000000..f609b3dd522
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_default_values.py
@@ -0,0 +1,74 @@
+# Copyright 2021 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.
+import time
+import typing
+import uuid
+
+from flaky import flaky
+import google.auth
+import google.cloud.storage as storage
+import pytest
+
+from ..usage_report.usage_reports import disable_usage_export
+from ..usage_report.usage_reports import get_usage_export_bucket
+from ..usage_report.usage_reports import set_usage_export_bucket
+
+PROJECT = google.auth.default()[1]
+BUCKET_NAME = "test" + uuid.uuid4().hex[:10]
+TEST_PREFIX = "some-prefix"
+
+
+@pytest.fixture
+def temp_bucket():
+ storage_client = storage.Client()
+ bucket = storage_client.create_bucket(BUCKET_NAME)
+ yield bucket
+ bucket.delete(force=True)
+
+
+@flaky(max_runs=3)
+def test_set_usage_export_bucket_default(
+ capsys: typing.Any, temp_bucket: storage.Bucket
+) -> None:
+ set_usage_export_bucket(project_id=PROJECT, bucket_name=temp_bucket.name)
+ time.sleep(5) # To make sure the settings are properly updated
+ uel = get_usage_export_bucket(project_id=PROJECT)
+ assert uel.bucket_name == temp_bucket.name
+ assert uel.report_name_prefix == "usage_gce"
+ out, _ = capsys.readouterr()
+ assert "default prefix of `usage_gce`." in out
+
+ disable_usage_export(project_id=PROJECT)
+ time.sleep(5) # To make sure the settings are properly updated
+ uel = get_usage_export_bucket(project_id=PROJECT)
+ assert uel.bucket_name == ""
+ assert uel.report_name_prefix == ""
+
+ # Testing setting a custom export bucket. Keeping this in one test function
+ # to avoid race conditions, as this is a global setting for the project.
+ set_usage_export_bucket(
+ project_id=PROJECT, bucket_name=temp_bucket.name, report_name_prefix=TEST_PREFIX
+ )
+ time.sleep(5) # To make sure the settings are properly updated
+ uel = get_usage_export_bucket(project_id=PROJECT)
+ assert uel.bucket_name == temp_bucket.name
+ assert uel.report_name_prefix == TEST_PREFIX
+ out, _ = capsys.readouterr()
+ assert "usage_gce" not in out
+
+ disable_usage_export(project_id=PROJECT)
+ time.sleep(5) # To make sure the settings are properly updated
+ uel = get_usage_export_bucket(project_id=PROJECT)
+ assert uel.bucket_name == ""
+ assert uel.report_name_prefix == ""
diff --git a/compute/client_library/snippets/tests/test_delete_protection.py b/compute/client_library/snippets/tests/test_delete_protection.py
new file mode 100644
index 00000000000..643c9294d15
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_delete_protection.py
@@ -0,0 +1,54 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+import pytest
+
+from ..instances.delete import delete_instance
+from ..instances.delete_protection.create import create_protected_instance
+from ..instances.delete_protection.get import get_delete_protection
+from ..instances.delete_protection.set import set_delete_protection
+
+PROJECT = google.auth.default()[1]
+INSTANCE_ZONE = "europe-central2-a"
+
+
+@pytest.fixture
+def autodelete_instance_name():
+ instance_name = "test-instance-" + uuid.uuid4().hex[:10]
+
+ yield instance_name
+
+ if get_delete_protection(PROJECT, INSTANCE_ZONE, instance_name):
+ set_delete_protection(PROJECT, INSTANCE_ZONE, instance_name, False)
+
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_delete_protection(autodelete_instance_name):
+ instance = create_protected_instance(
+ PROJECT, INSTANCE_ZONE, autodelete_instance_name
+ )
+ assert instance.name == autodelete_instance_name
+
+ assert (
+ get_delete_protection(PROJECT, INSTANCE_ZONE, autodelete_instance_name) is True
+ )
+
+ set_delete_protection(PROJECT, INSTANCE_ZONE, autodelete_instance_name, False)
+
+ assert (
+ get_delete_protection(PROJECT, INSTANCE_ZONE, autodelete_instance_name) is False
+ )
diff --git a/compute/client_library/snippets/tests/test_disks.py b/compute/client_library/snippets/tests/test_disks.py
new file mode 100644
index 00000000000..75fa2e9867b
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_disks.py
@@ -0,0 +1,175 @@
+# Copyright 2022 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.
+import uuid
+
+from google.api_core.exceptions import NotFound
+import google.auth
+from google.cloud import kms_v1
+import pytest
+
+from ..disks.clone_encrypted_disk_managed_key import \
+ create_disk_from_kms_encrypted_disk
+from ..disks.create_from_image import create_disk_from_image
+from ..disks.create_from_source import create_disk_from_disk
+from ..disks.create_kms_encrypted_disk import create_kms_encrypted_disk
+from ..disks.delete import delete_disk
+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 ..images.get import get_image_from_family
+from ..snapshots.create import create_snapshot
+from ..snapshots.delete import delete_snapshot
+
+PROJECT = google.auth.default()[1]
+ZONE = 'europe-north1-c'
+REGION = 'europe-central2'
+KMS_KEYRING_NAME = 'compute-test-keyring'
+KMS_KEY_NAME = 'compute-test-key'
+
+
+@pytest.fixture()
+def kms_key():
+ client = kms_v1.KeyManagementServiceClient()
+ location = f"projects/{PROJECT}/locations/global"
+ keyring_link = f"projects/{PROJECT}/locations/global/keyRings/{KMS_KEYRING_NAME}"
+ key_name = f"{keyring_link}/cryptoKeys/{KMS_KEY_NAME}"
+
+ for ring in client.list_key_rings(parent=location):
+ if ring.name == keyring_link:
+ break
+ else:
+ client.create_key_ring(parent=location, key_ring_id=KMS_KEYRING_NAME)
+
+ for key in client.list_crypto_keys(parent=keyring_link):
+ if key.name == key_name:
+ break
+ else:
+ key = kms_v1.CryptoKey()
+ key.purpose = key.CryptoKeyPurpose.ENCRYPT_DECRYPT
+ client.create_crypto_key(parent=keyring_link, crypto_key_id=KMS_KEY_NAME, crypto_key=key)
+
+ yield client.get_crypto_key(name=key_name)
+
+
+@pytest.fixture
+def test_disk():
+ """
+ Get the newest version of debian 11 and make a disk from it.
+ """
+ new_debian = get_image_from_family('debian-cloud', 'debian-11')
+ test_disk_name = "test-disk-" + uuid.uuid4().hex[:10]
+ disk = create_disk_from_image(PROJECT, ZONE, test_disk_name,
+ f"zones/{ZONE}/diskTypes/pd-standard",
+ 20, new_debian.self_link)
+ yield disk
+ delete_disk(PROJECT, ZONE, test_disk_name)
+
+
+@pytest.fixture
+def test_snapshot(test_disk):
+ """
+ Make a snapshot that will be deleted when tests are done.
+ """
+ test_snap_name = "test-snap-" + uuid.uuid4().hex[:10]
+ snap = create_snapshot(PROJECT, test_disk.name, test_snap_name, zone=test_disk.zone.rsplit('/')[-1])
+ yield snap
+ delete_snapshot(PROJECT, snap.name)
+
+
+@pytest.fixture()
+def autodelete_disk_name():
+ disk_name = "test-disk-" + uuid.uuid4().hex[:10]
+ yield disk_name
+ try:
+ delete_disk(PROJECT, ZONE, disk_name)
+ except NotFound:
+ # The disk was already deleted
+ pass
+
+
+# To use the fixture 2 times in one test:
+# https://stackoverflow.com/questions/36100624/pytest-use-same-fixture-twice-in-one-function
+autodelete_disk_name2 = autodelete_disk_name
+
+
+@pytest.fixture()
+def autodelete_src_disk(autodelete_disk_name):
+ disk_type = f"zones/{ZONE}/diskTypes/pd-standard"
+ debian_image = get_image_from_family('debian-cloud', 'debian-11')
+ disk = create_disk_from_image(PROJECT, ZONE, autodelete_disk_name, disk_type, 24, debian_image.self_link)
+ yield disk
+
+
+def test_disk_create_delete(autodelete_disk_name):
+ disk_type = f"zones/{ZONE}/diskTypes/pd-standard"
+ debian_image = get_image_from_family('debian-cloud', 'debian-11')
+
+ disk = create_disk_from_image(PROJECT, ZONE, autodelete_disk_name, disk_type, 17, debian_image.self_link)
+ assert disk.name == autodelete_disk_name
+ assert disk.type_.endswith(disk_type)
+ assert disk.size_gb == 17
+
+ for i_disk in list_disks(PROJECT, ZONE):
+ if i_disk.name == autodelete_disk_name:
+ break
+ else:
+ pytest.fail("Couldn't find newly created disk on the disk list.")
+
+ delete_disk(PROJECT, ZONE, autodelete_disk_name)
+
+ for i_disk in list_disks(PROJECT, ZONE):
+ if i_disk.name == autodelete_disk_name:
+ pytest.fail("Found a disk that should be deleted on the disk list.")
+
+
+def test_create_and_clone_encrypted_disk(autodelete_disk_name, kms_key, autodelete_disk_name2):
+ # The service account service-{PROJECT_ID}@compute-system.iam.gserviceaccount.com needs to have the
+ # cloudkms.cryptoKeyVersions.useToEncrypt permission to execute this test.
+ disk_type = f"zones/{ZONE}/diskTypes/pd-standard"
+ debian_image = get_image_from_family('debian-cloud', 'debian-11')
+
+ disk = create_kms_encrypted_disk(PROJECT, ZONE, autodelete_disk_name, disk_type, 25, kms_key.name,
+ image_link=debian_image.self_link)
+ assert disk.name == autodelete_disk_name
+ assert disk.type_.endswith(disk_type)
+
+ disk2 = create_disk_from_kms_encrypted_disk(PROJECT, ZONE, autodelete_disk_name2, disk_type,
+ 25, disk_link=disk.self_link, kms_key_name=kms_key.name)
+ assert disk2.name == autodelete_disk_name2
+ assert disk2.type_.endswith(disk_type)
+
+
+def test_create_disk_from_disk(autodelete_src_disk, autodelete_disk_name2):
+ disk_type = f"zones/{ZONE}/diskTypes/pd-standard"
+ new_disk = create_disk_from_disk(PROJECT, ZONE, autodelete_disk_name2, disk_type, 24, autodelete_src_disk.self_link)
+
+ assert new_disk.type_.endswith(disk_type)
+ assert new_disk.name == autodelete_disk_name2
+
+
+def test_create_and_delete_regional_disk(test_snapshot):
+ disk_name = "test-rdisk-" + uuid.uuid4().hex[:10]
+ disk_type = f"regions/{REGION}/diskTypes/pd-balanced"
+ replica_zones = [
+ f"projects/diregapic-mestiv/zones/{REGION}-a",
+ f"projects/diregapic-mestiv/zones/{REGION}-b",
+ ]
+
+ try:
+ regional_disk = create_regional_disk(PROJECT, REGION, replica_zones, disk_name,
+ disk_type, 25, snapshot_link=test_snapshot.self_link)
+ assert regional_disk.name == disk_name
+ assert regional_disk.type_.endswith(disk_type)
+ finally:
+ delete_regional_disk(PROJECT, REGION, disk_name)
diff --git a/compute/client_library/snippets/tests/test_firewall.py b/compute/client_library/snippets/tests/test_firewall.py
new file mode 100644
index 00000000000..86f97867585
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_firewall.py
@@ -0,0 +1,94 @@
+# Copyright 2021 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.
+import time
+import uuid
+
+import google.api_core.exceptions
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+from ..firewall.create import create_firewall_rule
+from ..firewall.delete import delete_firewall_rule
+from ..firewall.main import get_firewall_rule
+from ..firewall.patch import patch_firewall_priority
+
+PROJECT = google.auth.default()[1]
+
+
+@pytest.fixture
+def firewall_rule():
+ firewall_rule = compute_v1.Firewall()
+ firewall_rule.name = "firewall-sample-test" + uuid.uuid4().hex[:10]
+ firewall_rule.direction = "INGRESS"
+ allowed_ports = compute_v1.Allowed()
+ allowed_ports.I_p_protocol = "tcp"
+ allowed_ports.ports = ["80"]
+ firewall_rule.allowed = [allowed_ports]
+ firewall_rule.source_ranges = ["0.0.0.0/0"]
+ firewall_rule.network = "global/networks/default"
+ firewall_rule.description = "Rule generated by Python sample test fixture."
+ firewall_rule.target_tags = ["web"]
+
+ firewall_client = compute_v1.FirewallsClient()
+ op = firewall_client.insert_unary(project=PROJECT, firewall_resource=firewall_rule)
+
+ op_client = compute_v1.GlobalOperationsClient()
+ op_client.wait(project=PROJECT, operation=op.name)
+
+ yield firewall_client.get(project=PROJECT, firewall=firewall_rule.name)
+
+ try:
+ op = firewall_client.delete_unary(project=PROJECT, firewall=firewall_rule.name)
+ op_client.wait(project=PROJECT, operation=op.name)
+ except google.api_core.exceptions.BadRequest as err:
+ if err.code == 400 and "is not ready" in err.message:
+ # This means GCE enforcer has already deleted that rule.
+ pass
+ else:
+ raise err
+
+
+@pytest.fixture
+def autodelete_firewall_name():
+ """
+ Provide a name for a firewall rule and then delete the rule.
+ """
+ rule_name = "firewall-sample-test-" + uuid.uuid4().hex[:10]
+ yield rule_name
+ try:
+ delete_firewall_rule(PROJECT, rule_name)
+ except google.api_core.exceptions.BadRequest as err:
+ if err.code == 400 and "is not ready" in err.message:
+ # We can ignore this, this is most likely GCE Enforcer removing the rule before us.
+ pass
+ else:
+ # Something else went wrong, let's escalate it.
+ raise err
+
+
+def test_create(autodelete_firewall_name):
+ create_firewall_rule(PROJECT, autodelete_firewall_name)
+ rule = get_firewall_rule(PROJECT, autodelete_firewall_name)
+ assert rule.name == autodelete_firewall_name
+ assert "web" in rule.target_tags
+
+
+def test_patch_rule(firewall_rule):
+ fw_client = compute_v1.FirewallsClient()
+ assert firewall_rule.priority == 1000
+ patch_firewall_priority(PROJECT, firewall_rule.name, 500)
+ time.sleep(2)
+ updated_firewall_rule = fw_client.get(project=PROJECT, firewall=firewall_rule.name)
+ assert updated_firewall_rule.priority == 500
diff --git a/compute/client_library/snippets/tests/test_images.py b/compute/client_library/snippets/tests/test_images.py
new file mode 100644
index 00000000000..1cc7e6bb58f
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_images.py
@@ -0,0 +1,143 @@
+# Copyright 2021 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.
+import uuid
+
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+from ..disks.create_from_image import create_disk_from_image
+from ..disks.delete import delete_disk
+from ..images.create import create_image_from_disk
+from ..images.create_from_image import create_image_from_image
+from ..images.create_from_snapshot import create_image_from_snapshot
+from ..images.delete import delete_image
+from ..images.get import get_image
+from ..images.get import get_image_from_family
+from ..images.list import list_images
+from ..images.set_deprecation_status import set_deprecation_status
+from ..snapshots.create import create_snapshot
+from ..snapshots.delete import delete_snapshot
+
+PROJECT = google.auth.default()[1]
+ZONE = 'europe-central2-c'
+
+
+@pytest.fixture
+def test_disk():
+ """
+ Get the newest version of debian 11 and make a disk from it.
+ """
+ new_debian = get_image_from_family('debian-cloud', 'debian-11')
+ test_disk_name = "test-disk-" + uuid.uuid4().hex[:10]
+ disk = create_disk_from_image(PROJECT, ZONE, test_disk_name,
+ f"zones/{ZONE}/diskTypes/pd-standard",
+ 20, new_debian.self_link)
+ yield disk
+ delete_disk(PROJECT, ZONE, test_disk_name)
+
+
+@pytest.fixture
+def test_snapshot(test_disk):
+ """
+ Make a snapshot that will be deleted when tests are done.
+ """
+ test_snap_name = "test-snap-" + uuid.uuid4().hex[:10]
+ snap = create_snapshot(PROJECT, test_disk.name, test_snap_name, zone=test_disk.zone.rsplit('/')[-1])
+ yield snap
+ delete_snapshot(PROJECT, snap.name)
+
+
+@pytest.fixture
+def autodelete_image_name():
+ """
+ Provide a name for an image that will be deleted after the test is done.
+ """
+ test_img_name = "test-img-" + uuid.uuid4().hex[:10]
+ yield test_img_name
+
+ delete_image(PROJECT, test_img_name)
+
+
+@pytest.fixture()
+def autodelete_image(autodelete_image_name):
+ """
+ An image that will be deleted after the test is done.
+ """
+ src_img = get_image_from_family('debian-cloud', 'debian-11')
+ new_image = create_image_from_image(PROJECT, src_img.name, autodelete_image_name, 'debian-cloud',
+ storage_location='eu')
+ yield new_image
+
+
+def test_list_images():
+ images = list_images("debian-cloud")
+ for img in images:
+ assert img.kind == "compute#image"
+ assert img.self_link.startswith(
+ "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/"
+ )
+
+
+def test_get_image():
+ images = list_images("debian-cloud")
+ image = next(iter(images))
+
+ image2 = get_image("debian-cloud", image.name)
+
+ assert image.name == image2.name
+
+
+def test_create_delete_image(test_disk):
+ test_image_name = "test-image-" + uuid.uuid4().hex[:10]
+ new_image = create_image_from_disk(PROJECT, ZONE, test_disk.name, test_image_name)
+ try:
+ assert new_image.name == test_image_name
+ assert new_image.disk_size_gb == 20
+ assert isinstance(new_image, compute_v1.Image)
+ finally:
+ delete_image(PROJECT, test_image_name)
+
+ for image in list_images(PROJECT):
+ if image.name == test_image_name:
+ pytest.fail(f"Image {test_image_name} should have been deleted.")
+
+
+def test_image_from_image(autodelete_image_name):
+ src_img = get_image_from_family('ubuntu-os-cloud', 'ubuntu-2204-lts')
+ new_image = create_image_from_image(PROJECT, src_img.name, autodelete_image_name, 'ubuntu-os-cloud',
+ guest_os_features=[compute_v1.GuestOsFeature.Type.MULTI_IP_SUBNET.name],
+ storage_location='eu')
+
+ assert new_image.storage_locations == ['eu']
+ assert new_image.disk_size_gb == src_img.disk_size_gb
+ assert new_image.name == autodelete_image_name
+ assert any(feature.type_ == compute_v1.GuestOsFeature.Type.MULTI_IP_SUBNET.name for feature in new_image.guest_os_features)
+
+
+def test_image_from_snapshot(test_snapshot, autodelete_image_name):
+ img = create_image_from_snapshot(PROJECT, test_snapshot.name, autodelete_image_name,
+ guest_os_features=[compute_v1.GuestOsFeature.Type.MULTI_IP_SUBNET.name],
+ storage_location='us-central1')
+ assert img.storage_locations == ['us-central1']
+ assert img.name == autodelete_image_name
+ assert any(
+ feature.type_ == compute_v1.GuestOsFeature.Type.MULTI_IP_SUBNET.name for feature in img.guest_os_features)
+
+
+def test_status_change(autodelete_image):
+ set_deprecation_status(PROJECT, autodelete_image.name, compute_v1.DeprecationStatus.State.DEPRECATED)
+ img = get_image(PROJECT, autodelete_image.name)
+ assert img.name == autodelete_image.name
+ assert img.deprecated.state == compute_v1.DeprecationStatus.State.DEPRECATED.name
diff --git a/compute/client_library/snippets/tests/test_instance_from_template.py b/compute/client_library/snippets/tests/test_instance_from_template.py
new file mode 100644
index 00000000000..e8a20f372d8
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_instance_from_template.py
@@ -0,0 +1,104 @@
+# Copyright 2021 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.
+import uuid
+
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+from ..instances.delete import delete_instance
+from ..instances.from_instance_template.create_from_template import (
+ create_instance_from_template,
+)
+from ..instances.from_instance_template.create_from_template_with_overrides import (
+ create_instance_from_template_with_overrides,
+)
+
+PROJECT = google.auth.default()[1]
+INSTANCE_ZONE = "europe-north1-c"
+
+
+@pytest.fixture
+def instance_template():
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-11"
+ )
+ initialize_params.disk_size_gb = 25
+ initialize_params.disk_type = 'pd-balanced'
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.name = "global/networks/default"
+
+ template = compute_v1.InstanceTemplate()
+ template.name = "test-template-" + uuid.uuid4().hex[:10]
+ template.properties.disks = [disk]
+ template.properties.machine_type = "n1-standard-4"
+ template.properties.network_interfaces = [network_interface]
+
+ template_client = compute_v1.InstanceTemplatesClient()
+ operation_client = compute_v1.GlobalOperationsClient()
+ op = template_client.insert_unary(
+ project=PROJECT, instance_template_resource=template
+ )
+ operation_client.wait(project=PROJECT, operation=op.name)
+
+ template = template_client.get(project=PROJECT, instance_template=template.name)
+
+ yield template
+
+ op = template_client.delete_unary(project=PROJECT, instance_template=template.name)
+ operation_client.wait(project=PROJECT, operation=op.name)
+
+
+@pytest.fixture()
+def autodelete_instance_name():
+ instance_name = "test-instance-" + uuid.uuid4().hex[:10]
+ yield instance_name
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_create_instance_from_template(instance_template, autodelete_instance_name):
+ instance = create_instance_from_template(
+ PROJECT, INSTANCE_ZONE, autodelete_instance_name, instance_template.self_link
+ )
+
+ assert instance.name == autodelete_instance_name
+ assert instance.zone.endswith(INSTANCE_ZONE)
+
+
+def test_create_instance_from_template_override(
+ instance_template, autodelete_instance_name
+):
+ image_client = compute_v1.ImagesClient()
+
+ image = image_client.get_from_family(
+ project="ubuntu-os-cloud", family="ubuntu-2004-lts"
+ )
+ instance = create_instance_from_template_with_overrides(
+ PROJECT,
+ INSTANCE_ZONE,
+ autodelete_instance_name,
+ instance_template.name,
+ f"zones/{INSTANCE_ZONE}/machineTypes/n2-standard-2",
+ image.self_link,
+ )
+
+ assert instance.name == autodelete_instance_name
+ assert instance.machine_type.endswith("n2-standard-2")
+ assert len(instance.disks) == 2
diff --git a/compute/client_library/snippets/tests/test_instance_start_stop.py b/compute/client_library/snippets/tests/test_instance_start_stop.py
new file mode 100644
index 00000000000..8c6efc802de
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_instance_start_stop.py
@@ -0,0 +1,189 @@
+# Copyright 2021 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.
+import base64
+import random
+import string
+import time
+import uuid
+
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+from ..disks.clone_encrypted_disk import create_disk_from_customer_encrypted_disk
+from ..disks.delete import delete_disk
+from ..instances.start import start_instance
+from ..instances.start_encrypted import start_instance_with_encryption_key
+from ..instances.stop import stop_instance
+
+PROJECT = google.auth.default()[1]
+
+INSTANCE_ZONE = "europe-central2-b"
+
+KEY = "".join(random.sample(string.ascii_letters, 32))
+KEY_B64 = base64.b64encode(
+ KEY.encode()
+) # for example: b'VEdORldtY3NKellPdWRDcUF5YlNVREtJdm5qaFJYSFA='
+
+
+def _make_disk(raw_key: bytes = None):
+ disk = compute_v1.AttachedDisk()
+ initialize_params = compute_v1.AttachedDiskInitializeParams()
+ initialize_params.source_image = (
+ "projects/debian-cloud/global/images/family/debian-10"
+ )
+ initialize_params.disk_size_gb = 10
+ disk.initialize_params = initialize_params
+ disk.auto_delete = True
+ disk.boot = True
+ disk.type_ = "PERSISTENT"
+ disk.device_name = "disk-1"
+
+ if raw_key:
+ disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
+ disk.disk_encryption_key.raw_key = raw_key
+
+ return disk
+
+
+def _make_request(disk: compute_v1.AttachedDisk):
+ network_interface = compute_v1.NetworkInterface()
+ network_interface.name = "default"
+ network_interface.access_configs = []
+
+ # Collect information into the Instance object.
+ instance = compute_v1.Instance()
+ instance.name = "i" + uuid.uuid4().hex[:10]
+ instance.disks = [disk]
+ full_machine_type_name = f"zones/{INSTANCE_ZONE}/machineTypes/e2-micro"
+ instance.machine_type = full_machine_type_name
+ instance.network_interfaces = [network_interface]
+
+ # Prepare the request to insert an instance.
+ request = compute_v1.InsertInstanceRequest()
+ request.zone = INSTANCE_ZONE
+ request.project = PROJECT
+ request.instance_resource = instance
+ return request
+
+
+def _create_instance(request: compute_v1.InsertInstanceRequest):
+ instance_client = compute_v1.InstancesClient()
+ operation_client = compute_v1.ZoneOperationsClient()
+
+ operation = instance_client.insert_unary(request=request)
+ while operation.status != compute_v1.Operation.Status.DONE:
+ operation = operation_client.wait(
+ operation=operation.name, zone=INSTANCE_ZONE, project=PROJECT
+ )
+
+ return instance_client.get(
+ project=PROJECT, zone=INSTANCE_ZONE, instance=request.instance_resource.name
+ )
+
+
+def _delete_instance(instance: compute_v1.Instance):
+ instance_client = compute_v1.InstancesClient()
+ operation_client = compute_v1.ZoneOperationsClient()
+
+ operation = instance_client.delete_unary(
+ project=PROJECT, zone=INSTANCE_ZONE, instance=instance.name
+ )
+ while operation.status != compute_v1.Operation.Status.DONE:
+ operation = operation_client.wait(
+ operation=operation.name, zone=INSTANCE_ZONE, project=PROJECT
+ )
+
+
+def _get_status(instance: compute_v1.Instance) -> compute_v1.Instance.Status:
+ instance_client = compute_v1.InstancesClient()
+ return instance_client.get(
+ project=PROJECT, zone=INSTANCE_ZONE, instance=instance.name
+ ).status
+
+
+@pytest.fixture
+def compute_instance():
+ disk = _make_disk()
+ request = _make_request(disk)
+
+ instance = _create_instance(request)
+
+ yield instance
+
+ _delete_instance(instance)
+
+
+@pytest.fixture
+def compute_encrypted_instance():
+ disk = _make_disk(KEY_B64)
+ request = _make_request(disk)
+
+ instance = _create_instance(request)
+
+ yield instance
+
+ _delete_instance(instance)
+
+
+@pytest.fixture
+def autodelete_disk_name():
+ name = "d" + uuid.uuid4().hex[:10]
+ yield name
+ delete_disk(PROJECT, INSTANCE_ZONE, name)
+
+
+def test_instance_operations(compute_instance):
+ assert _get_status(compute_instance) == "RUNNING"
+
+ stop_instance(PROJECT, INSTANCE_ZONE, compute_instance.name)
+
+ while _get_status(compute_instance) == "STOPPING":
+ # Since we can't configure timeout parameter for operation wait() (b/188037306)
+ # We need to do some manual waiting for the stopping to finish...
+ time.sleep(5)
+
+ assert _get_status(compute_instance) == "TERMINATED"
+
+ start_instance(PROJECT, INSTANCE_ZONE, compute_instance.name)
+ assert _get_status(compute_instance) == "RUNNING"
+
+
+def test_instance_encrypted(compute_encrypted_instance):
+ assert _get_status(compute_encrypted_instance) == "RUNNING"
+
+ stop_instance(PROJECT, INSTANCE_ZONE, compute_encrypted_instance.name)
+ while _get_status(compute_encrypted_instance) == "STOPPING":
+ # Since we can't configure timeout parameter for operation wait() (b/188037306)
+ # We need to do some manual waiting for the stopping to finish...
+ time.sleep(5)
+
+ assert _get_status(compute_encrypted_instance) == "TERMINATED"
+
+ start_instance_with_encryption_key(
+ PROJECT, INSTANCE_ZONE, compute_encrypted_instance.name, KEY_B64
+ )
+ assert _get_status(compute_encrypted_instance) == "RUNNING"
+
+
+def test_clone_encrypted_disk(autodelete_disk_name, compute_encrypted_instance):
+ assert _get_status(compute_encrypted_instance) == "RUNNING"
+
+ new_disk = create_disk_from_customer_encrypted_disk(
+ PROJECT, INSTANCE_ZONE, autodelete_disk_name,
+ f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard",
+ 10, compute_encrypted_instance.disks[0].source,
+ encryption_key=KEY_B64)
+
+ assert new_disk.name == autodelete_disk_name
diff --git a/compute/client_library/snippets/tests/test_instance_suspend_resume.py b/compute/client_library/snippets/tests/test_instance_suspend_resume.py
new file mode 100644
index 00000000000..a6f2ca103f0
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_instance_suspend_resume.py
@@ -0,0 +1,69 @@
+# Copyright 2022 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.
+import time
+import uuid
+
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+
+from ..images.get import get_image_from_family
+from ..instances.create import create_instance, disk_from_image
+from ..instances.delete import delete_instance
+from ..instances.resume import resume_instance
+from ..instances.suspend import suspend_instance
+
+PROJECT = google.auth.default()[1]
+
+INSTANCE_ZONE = "europe-central2-b"
+
+
+def _get_status(instance: compute_v1.Instance) -> compute_v1.Instance.Status:
+ instance_client = compute_v1.InstancesClient()
+ return instance_client.get(
+ project=PROJECT, zone=INSTANCE_ZONE, instance=instance.name
+ ).status
+
+
+@pytest.fixture
+def compute_instance():
+ instance_name = "test-instance-" + uuid.uuid4().hex[:10]
+ newest_debian = get_image_from_family(project="ubuntu-os-cloud", family="ubuntu-2004-lts")
+ disk_type = f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard"
+ disks = [disk_from_image(disk_type, 100, True, newest_debian.self_link)]
+ instance = create_instance(
+ PROJECT, INSTANCE_ZONE, instance_name, disks
+ )
+ yield instance
+
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_instance_suspend_resume(compute_instance):
+ assert _get_status(compute_instance) == compute_v1.Instance.Status.RUNNING.name
+
+ # Once the machine is running, give it some time to fully start all processes
+ # before trying to suspend it
+ time.sleep(45)
+
+ suspend_instance(PROJECT, INSTANCE_ZONE, compute_instance.name)
+
+ while _get_status(compute_instance) == compute_v1.Instance.Status.SUSPENDING.name:
+ time.sleep(5)
+
+ assert _get_status(compute_instance) == compute_v1.Instance.Status.SUSPENDED.name
+
+ resume_instance(PROJECT, INSTANCE_ZONE, compute_instance.name)
+ assert _get_status(compute_instance) == compute_v1.Instance.Status.RUNNING.name
diff --git a/compute/client_library/snippets/tests/test_pagination.py b/compute/client_library/snippets/tests/test_pagination.py
new file mode 100644
index 00000000000..41e06703d13
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_pagination.py
@@ -0,0 +1,27 @@
+# Copyright 2021 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.
+from ..images.pagination import print_images_list
+from ..images.pagination import print_images_list_by_page
+
+PROJECT = "windows-sql-cloud"
+
+
+def test_pagination() -> None:
+ out = print_images_list(PROJECT)
+ assert len(out.splitlines()) > 2
+
+
+def test_pagination_page() -> None:
+ out = print_images_list_by_page(PROJECT, 2)
+ assert "Page 2" in out
diff --git a/compute/client_library/snippets/tests/test_preemptible.py b/compute/client_library/snippets/tests/test_preemptible.py
new file mode 100644
index 00000000000..06c5092799c
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_preemptible.py
@@ -0,0 +1,58 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+import pytest
+
+from ..instances.delete import delete_instance
+from ..instances.preemptible.create_preemptible import create_preemptible_instance
+from ..instances.preemptible.is_preemptible import is_preemptible
+from ..instances.preemptible.preemption_history import list_zone_operations
+
+PROJECT = google.auth.default()[1]
+INSTANCE_ZONE = "europe-central2-c"
+
+
+@pytest.fixture
+def autodelete_instance_name():
+ instance_name = "i" + uuid.uuid4().hex[:10]
+
+ yield instance_name
+
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_preemptible_creation(autodelete_instance_name):
+ instance = create_preemptible_instance(
+ PROJECT, INSTANCE_ZONE, autodelete_instance_name
+ )
+
+ assert instance.name == autodelete_instance_name
+ assert is_preemptible(PROJECT, INSTANCE_ZONE, instance.name)
+
+ operations = list_zone_operations(
+ PROJECT,
+ INSTANCE_ZONE,
+ f'targetLink="https://www.googleapis.com/compute/v1/projects/'
+ f'{PROJECT}/zones/{INSTANCE_ZONE}/instances/{instance.name}"',
+ )
+
+ # Since ListPagers don't support len(), we need to check it manually
+ try:
+ next(iter(operations))
+ except StopIteration:
+ pytest.fail(
+ "There should be at least one operation for this instance at this point."
+ )
diff --git a/compute/client_library/snippets/tests/test_request_id.py b/compute/client_library/snippets/tests/test_request_id.py
new file mode 100644
index 00000000000..90dcc7e028c
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_request_id.py
@@ -0,0 +1,98 @@
+# Copyright 2022 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.
+from copy import deepcopy
+import uuid
+
+import google.api_core.exceptions
+import google.auth
+from google.cloud import compute_v1
+import pytest
+
+
+from ..disks.delete import delete_disk
+from ..disks.list import list_disks
+from ..instances.create_start_instance.create_windows_instance import \
+ get_image_from_family
+
+PROJECT = google.auth.default()[1]
+ZONE = "europe-north1-c"
+
+
+def test_request_id():
+ disk = compute_v1.Disk()
+ disk.size_gb = 20
+ disk.name = "test-disk-" + uuid.uuid4().hex[:10]
+ disk.zone = ZONE
+ disk.type_ = f"zones/{ZONE}/diskTypes/pd-standard"
+ disk.source_image = get_image_from_family("debian-cloud", "debian-11").self_link
+
+ disk2 = deepcopy(disk)
+ disk2.name = "test-disk-" + uuid.uuid4().hex[:10]
+
+ request = compute_v1.InsertDiskRequest()
+ request.request_id = str(uuid.uuid4())
+ request.project = PROJECT
+ request.zone = ZONE
+ request.disk_resource = disk
+
+ # Creating a different request, but with the same request_id
+ # This should not be executed, because the previous request
+ # has the same ID.
+ request2 = deepcopy(request)
+ request2.disk_resource = disk2
+
+ disk_client = compute_v1.DisksClient()
+ try:
+ operation = disk_client.insert(request)
+ operation2 = disk_client.insert(request2)
+ operation.result()
+ operation2.result()
+ except Exception as err:
+ pytest.fail(f"There was an error: {err}")
+ raise err
+ else:
+ disks = list_disks(PROJECT, ZONE)
+ assert any(i_disk.name == disk.name for i_disk in disks)
+ assert all(i_disk.name != disk2.name for i_disk in disks)
+ finally:
+ delete_disk(PROJECT, ZONE, disk.name)
+ try:
+ delete_disk(PROJECT, ZONE, disk2.name)
+ except google.api_core.exceptions.NotFound:
+ pass
+
+
+def test_request_id_op_id():
+ disk = compute_v1.Disk()
+ disk.size_gb = 20
+ disk.name = "test-disk-" + uuid.uuid4().hex[:10]
+ disk.zone = ZONE
+ disk.type_ = f"zones/{ZONE}/diskTypes/pd-standard"
+ disk.source_image = get_image_from_family("debian-cloud", "debian-11").self_link
+
+ request = compute_v1.InsertDiskRequest()
+ request.request_id = str(uuid.uuid4())
+ request.project = PROJECT
+ request.zone = ZONE
+ request.disk_resource = disk
+
+ disk_client = compute_v1.DisksClient()
+
+ try:
+ op1 = disk_client.insert(request)
+ op2 = disk_client.insert(request)
+ op1.result()
+ assert op1.name == op2.name
+ finally:
+ delete_disk(PROJECT, ZONE, disk.name)
diff --git a/compute/client_library/snippets/tests/test_route.py b/compute/client_library/snippets/tests/test_route.py
new file mode 100644
index 00000000000..65ccbf81ece
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_route.py
@@ -0,0 +1,38 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+import pytest
+
+from ..routes.create_kms_route import create_route_to_windows_activation_host
+from ..routes.delete import delete_route
+from ..routes.list import list_routes
+
+PROJECT = google.auth.default()[1]
+
+
+def test_route_create_delete():
+ route_name = "test-route" + uuid.uuid4().hex[:10]
+ route = create_route_to_windows_activation_host(PROJECT, "global/networks/default", route_name)
+ try:
+ assert route.name == route_name
+ assert route.dest_range == "35.190.247.13/32"
+ finally:
+
+ delete_route(PROJECT, route_name)
+
+ for route in list_routes(PROJECT):
+ if route.name == route_name:
+ pytest.fail(f"Failed to delete test route {route_name}.")
diff --git a/compute/client_library/snippets/tests/test_snapshots.py b/compute/client_library/snippets/tests/test_snapshots.py
new file mode 100644
index 00000000000..b62978788e3
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_snapshots.py
@@ -0,0 +1,64 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+import pytest
+
+from ..disks.create_from_image import create_disk_from_image
+from ..disks.delete import delete_disk
+from ..images.get import get_image_from_family
+from ..snapshots.create import create_snapshot
+from ..snapshots.delete import delete_snapshot
+from ..snapshots.get import get_snapshot
+from ..snapshots.list import list_snapshots
+
+PROJECT = google.auth.default()[1]
+ZONE = 'europe-north1-c'
+
+
+@pytest.fixture
+def test_disk():
+ debian_image = get_image_from_family('debian-cloud', 'debian-11')
+ test_disk_name = "test-disk-" + uuid.uuid4().hex[:10]
+
+ disk_type = f"zones/{ZONE}/diskTypes/pd-standard"
+
+ disk = create_disk_from_image(PROJECT, ZONE, test_disk_name, disk_type, 20, debian_image.self_link)
+
+ yield disk
+
+ delete_disk(PROJECT, ZONE, test_disk_name)
+
+
+def test_snapshot_create_delete(test_disk):
+ snapshot_name = "test-snapshot-" + uuid.uuid4().hex[:10]
+ snapshot = create_snapshot(PROJECT, test_disk.name, snapshot_name, zone=ZONE)
+ assert snapshot.name == snapshot_name
+ assert snapshot.source_disk == test_disk.self_link
+ for i_snapshot in list_snapshots(PROJECT):
+ if i_snapshot.name == snapshot_name:
+ break
+ else:
+ pytest.fail("Couldn't find the created snapshot on snapshot list.")
+
+ snapshot_get = get_snapshot(PROJECT, snapshot_name)
+ assert snapshot_get.name == snapshot_name
+ assert snapshot_get.disk_size_gb == snapshot.disk_size_gb
+ assert snapshot_get.self_link == snapshot.self_link
+
+ delete_snapshot(PROJECT, snapshot_name)
+ for i_snapshot in list_snapshots(PROJECT):
+ if i_snapshot.name == snapshot_name:
+ pytest.fail("Test snapshot found on snapshot list, while it should already be gone.")
diff --git a/compute/client_library/snippets/tests/test_spot_vms.py b/compute/client_library/snippets/tests/test_spot_vms.py
new file mode 100644
index 00000000000..5e6114161ca
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_spot_vms.py
@@ -0,0 +1,42 @@
+# Copyright 2022 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.
+import uuid
+
+import google.auth
+import pytest
+
+from ..instances.delete import delete_instance
+from ..instances.spot.create import create_spot_instance
+from ..instances.spot.is_spot_vm import is_spot_vm
+
+PROJECT = google.auth.default()[1]
+INSTANCE_ZONE = "europe-central2-c"
+
+
+@pytest.fixture
+def autodelete_instance_name():
+ instance_name = "i" + uuid.uuid4().hex[:10]
+
+ yield instance_name
+
+ delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
+
+
+def test_preemptible_creation(autodelete_instance_name):
+ instance = create_spot_instance(
+ PROJECT, INSTANCE_ZONE, autodelete_instance_name
+ )
+
+ assert instance.name == autodelete_instance_name
+ assert is_spot_vm(PROJECT, INSTANCE_ZONE, instance.name)
diff --git a/compute/client_library/snippets/tests/test_templates.py b/compute/client_library/snippets/tests/test_templates.py
new file mode 100644
index 00000000000..bf04fa12b8a
--- /dev/null
+++ b/compute/client_library/snippets/tests/test_templates.py
@@ -0,0 +1,107 @@
+# Copyright 2021 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.
+
+import uuid
+
+import google.auth
+import pytest
+
+# Turning off F401 check because flake8 doesn't recognize using
+# PyTest fixture as parameter as usage.
+from .test_instance_start_stop import compute_instance # noqa: F401
+
+from ..instance_templates.create import create_template
+from ..instance_templates.create_from_instance import \
+ create_template_from_instance
+from ..instance_templates.create_with_subnet import create_template_with_subnet
+from ..instance_templates.delete import delete_instance_template
+from ..instance_templates.list import list_instance_templates
+
+PROJECT = google.auth.default()[1]
+
+INSTANCE_ZONE = "europe-central2-b"
+
+
+@pytest.fixture
+def deletable_template_name():
+ template_name = "i" + uuid.uuid4().hex[:10]
+ yield template_name
+ delete_instance_template(PROJECT, template_name)
+
+
+@pytest.fixture
+def template_to_be_deleted():
+ template_name = "i" + uuid.uuid4().hex[:10]
+ template = create_template(PROJECT, template_name)
+ yield template
+
+
+def test_create_template_and_list(deletable_template_name):
+
+ template = create_template(PROJECT, deletable_template_name)
+
+ assert template.name == deletable_template_name
+ assert any(
+ template.name == deletable_template_name
+ for template in list_instance_templates(PROJECT)
+ )
+ assert template.properties.disks[0].initialize_params.disk_size_gb == 250
+ assert "debian-11" in template.properties.disks[0].initialize_params.source_image
+ assert template.properties.network_interfaces[0].name == "global/networks/default"
+ assert template.properties.machine_type == "e2-standard-4"
+
+
+def test_create_from_instance(compute_instance, deletable_template_name): # noqa: F811
+
+ template = create_template_from_instance(
+ PROJECT, compute_instance.self_link, deletable_template_name
+ )
+
+ assert template.name == deletable_template_name
+ assert template.properties.machine_type in compute_instance.machine_type
+ assert (
+ template.properties.disks[0].disk_size_gb
+ == compute_instance.disks[0].disk_size_gb
+ )
+ assert (
+ template.properties.disks[0].initialize_params.source_image
+ == "projects/rocky-linux-cloud/global/images/family/rocky-linux-8"
+ )
+
+
+def test_create_template_with_subnet(deletable_template_name):
+ template = create_template_with_subnet(
+ PROJECT,
+ "global/networks/default",
+ "regions/asia-east1/subnetworks/default",
+ deletable_template_name,
+ )
+
+ assert template.name == deletable_template_name
+ assert (
+ "global/networks/default" in template.properties.network_interfaces[0].network
+ )
+ assert (
+ "regions/asia-east1/subnetworks/default"
+ in template.properties.network_interfaces[0].subnetwork
+ )
+
+
+def test_delete_template(template_to_be_deleted):
+ delete_instance_template(PROJECT, template_to_be_deleted.name)
+
+ assert all(
+ template.name != template_to_be_deleted.name
+ for template in list_instance_templates(PROJECT)
+ )
diff --git a/compute/client_library/snippets/usage_report/__init__.py b/compute/client_library/snippets/usage_report/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/compute/client_library/snippets/usage_report/usage_reports.py b/compute/client_library/snippets/usage_report/usage_reports.py
new file mode 100644
index 00000000000..e04a3a8a162
--- /dev/null
+++ b/compute/client_library/snippets/usage_report/usage_reports.py
@@ -0,0 +1,235 @@
+# Copyright 2022 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
+"""
+A sample script showing how to handle default values when communicating
+with the Compute Engine API and how to configure usage reports using the API.
+"""
+
+# 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_instances_verify_default_value]
+# [START compute_usage_report_set]
+# [START compute_usage_report_get]
+# [START compute_usage_report_disable]
+import sys
+from typing import Any
+
+from google.api_core.extended_operation import ExtendedOperation
+from google.cloud import compute_v1
+
+
+# [END compute_usage_report_disable]
+# [END compute_usage_report_get]
+# [END compute_usage_report_set]
+
+
+# [START compute_usage_report_set]
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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 set_usage_export_bucket(
+ project_id: str, bucket_name: str, report_name_prefix: str = ""
+) -> None:
+ """
+ Set Compute Engine usage export bucket for the Cloud project.
+ This sample presents how to interpret the default value for the
+ report name prefix parameter.
+
+ Args:
+ project_id: project ID or project number of the project to update.
+ bucket_name: Google Cloud Storage bucket used to store Compute Engine
+ usage reports. An existing Google Cloud Storage bucket is required.
+ report_name_prefix: Prefix of the usage report name which defaults to an empty string
+ to showcase default values behaviour.
+ """
+ usage_export_location = compute_v1.UsageExportLocation()
+ usage_export_location.bucket_name = bucket_name
+ usage_export_location.report_name_prefix = report_name_prefix
+
+ if not report_name_prefix:
+ # Sending an empty value for report_name_prefix results in the
+ # next usage report being generated with the default prefix value
+ # "usage_gce". (ref: https://cloud.google.com/compute/docs/reference/rest/v1/projects/setUsageExportBucket)
+ print(
+ "Setting report_name_prefix to empty value causes the report "
+ "to have the default prefix of `usage_gce`."
+ )
+
+ projects_client = compute_v1.ProjectsClient()
+ operation = projects_client.set_usage_export_bucket(
+ project=project_id, usage_export_location_resource=usage_export_location
+ )
+
+ wait_for_extended_operation(operation, "setting GCE usage bucket")
+
+
+# [END compute_usage_report_set]
+
+# [START compute_usage_report_get]
+def get_usage_export_bucket(project_id: str) -> compute_v1.UsageExportLocation:
+ """
+ Retrieve Compute Engine usage export bucket for the Cloud project.
+ Replaces the empty value returned by the API with the default value used
+ to generate report file names.
+
+ Args:
+ project_id: project ID or project number of the project to update.
+ Returns:
+ UsageExportLocation object describing the current usage export settings
+ for project project_id.
+ """
+ projects_client = compute_v1.ProjectsClient()
+ project_data = projects_client.get(project=project_id)
+
+ uel = project_data.usage_export_location
+
+ if not uel.bucket_name:
+ # The usage reports are disabled.
+ return uel
+
+ if not uel.report_name_prefix:
+ # Although the server sent the empty string value, the next usage report
+ # generated with these settings still has the default prefix value
+ # "usage_gce". (see https://cloud.google.com/compute/docs/reference/rest/v1/projects/get)
+ print(
+ "Report name prefix not set, replacing with default value of "
+ "`usage_gce`."
+ )
+ uel.report_name_prefix = "usage_gce"
+ return uel
+
+
+# [END compute_usage_report_get]
+# [END compute_instances_verify_default_value]
+
+
+# [START compute_usage_report_disable]
+
+
+def wait_for_extended_operation(
+ operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
+) -> Any:
+ """
+ This method will wait 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 disable_usage_export(project_id: str) -> None:
+ """
+ Disable Compute Engine usage export bucket for the Cloud Project.
+
+ Args:
+ project_id: project ID or project number of the project to update.
+ """
+ projects_client = compute_v1.ProjectsClient()
+
+ # Setting `usage_export_location_resource` to an
+ # empty object will disable the usage report generation.
+ operation = projects_client.set_usage_export_bucket(
+ project=project_id, usage_export_location_resource={}
+ )
+
+ wait_for_extended_operation(operation, "disabling GCE usage bucket")
+
+
+# [END compute_usage_report_disable]
diff --git a/compute/client_library/test_sgs.py b/compute/client_library/test_sgs.py
new file mode 100644
index 00000000000..fafae9ed3c7
--- /dev/null
+++ b/compute/client_library/test_sgs.py
@@ -0,0 +1,57 @@
+# Copyright 2022 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.
+from argparse import Namespace
+import glob
+from pathlib import Path
+import tempfile
+
+import pytest
+
+from . import sgs
+
+FIXTURE_INGREDIENTS = Path("sgs_test_fixtures/ingredients")
+FIXTURE_RECIPES = Path("sgs_test_fixtures/recipes")
+FIXTURE_OUTPUT = Path("sgs_test_fixtures/output")
+
+
+def test_sgs_generate():
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ args = Namespace(output_dir=tmp_dir)
+ sgs.generate(args, FIXTURE_INGREDIENTS.absolute(), FIXTURE_RECIPES.absolute())
+ for test_file in map(Path, glob.glob(f"{tmp_dir}/**")):
+ match_file = FIXTURE_OUTPUT / test_file.relative_to(tmp_dir)
+ assert test_file.read_bytes() == match_file.read_bytes()
+
+
+def test_snippets_freshness():
+ """
+ Make sure that the snippets/ folder is up-to-date and matches
+ ingredients/ and recipes/. This test will generate SGS output
+ in a temporary directory and compare it to the content of
+ snippets/ folder.
+ """
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ args = Namespace(output_dir=tmp_dir)
+ sgs.generate(args, Path("ingredients/").absolute(), Path("recipes/").absolute())
+ print(list(map(Path, glob.glob(f"{tmp_dir}/**"))))
+ for test_file in map(Path, glob.glob(f"{tmp_dir}/**", recursive=True)):
+ match_file = Path("snippets/") / test_file.relative_to(tmp_dir)
+ if test_file.is_file():
+ if test_file.read_bytes() != match_file.read_bytes():
+ pytest.fail(
+ f"This test fails because file {match_file} seems to be outdated. Please run "
+ f"`python sgs.py generate` to update your snippets."
+ )
+ elif test_file.is_dir():
+ assert match_file.is_dir()