Skip to content

Commit 140b9da

Browse files
feat: Compute create replicated boot disk Sample (#13003)
* New replicated boot disk Sample
1 parent 5e1a891 commit 140b9da

File tree

4 files changed

+383
-0
lines changed

4 files changed

+383
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# This is an ingredient file. It is not meant to be run directly. Check the samples/snippets
16+
# folder for complete code samples that are ready to be used.
17+
# Disabling flake8 for the ingredients file, as it would fail F821 - undefined name check.
18+
# flake8: noqa
19+
20+
from google.cloud import compute_v1
21+
22+
23+
# <INGREDIENT create_with_regional_boot_disk>
24+
def create_with_regional_boot_disk(
25+
project_id: str,
26+
zone: str,
27+
instance_name: str,
28+
source_snapshot: str,
29+
disk_region: str,
30+
disk_type: str = "pd-balanced",
31+
) -> compute_v1.Instance:
32+
"""
33+
Creates a new instance with a regional boot disk
34+
Args:
35+
project_id (str): The ID of the Google Cloud project.
36+
zone (str): The zone where the instance will be created.
37+
instance_name (str): The name of the instance.
38+
source_snapshot (str): The name of snapshot to create the boot disk from.
39+
disk_region (str): The region where the disk replicas will be located.
40+
disk_type (str): The type of the disk. Default is 'pd-balanced'.
41+
Returns:
42+
Instance object.
43+
"""
44+
45+
disk = compute_v1.AttachedDisk()
46+
47+
initialize_params = compute_v1.AttachedDiskInitializeParams()
48+
initialize_params.source_snapshot = f"global/snapshots/{source_snapshot}"
49+
initialize_params.disk_type = (
50+
f"projects/{project_id}/zones/{zone}/diskTypes/{disk_type}"
51+
)
52+
initialize_params.replica_zones = [
53+
f"projects/{project_id}/zones/{disk_region}-a",
54+
f"projects/{project_id}/zones/{disk_region}-b",
55+
]
56+
57+
disk.initialize_params = initialize_params
58+
disk.boot = True
59+
disk.auto_delete = True
60+
61+
instance = create_instance(project_id, zone, instance_name, [disk])
62+
63+
return instance
64+
65+
66+
# </INGREDIENT>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# flake8: noqa
15+
16+
# <REGION compute_instance_create_replicated_boot_disk>
17+
# <IMPORTS/>
18+
19+
# <INGREDIENT wait_for_extended_operation />
20+
21+
# <INGREDIENT create_instance />
22+
23+
# <INGREDIENT create_with_regional_boot_disk />
24+
25+
# </REGION compute_instance_create_replicated_boot_disk>
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# flake8: noqa
15+
16+
17+
# This file is automatically generated. Please do not modify it directly.
18+
# Find the relevant recipe file in the samples/recipes or samples/ingredients
19+
# directory and apply your changes there.
20+
21+
22+
# [START compute_instance_create_replicated_boot_disk]
23+
from __future__ import annotations
24+
25+
import re
26+
import sys
27+
from typing import Any
28+
import warnings
29+
30+
from google.api_core.extended_operation import ExtendedOperation
31+
from google.cloud import compute_v1
32+
33+
34+
def wait_for_extended_operation(
35+
operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
36+
) -> Any:
37+
"""
38+
Waits for the extended (long-running) operation to complete.
39+
40+
If the operation is successful, it will return its result.
41+
If the operation ends with an error, an exception will be raised.
42+
If there were any warnings during the execution of the operation
43+
they will be printed to sys.stderr.
44+
45+
Args:
46+
operation: a long-running operation you want to wait on.
47+
verbose_name: (optional) a more verbose name of the operation,
48+
used only during error and warning reporting.
49+
timeout: how long (in seconds) to wait for operation to finish.
50+
If None, wait indefinitely.
51+
52+
Returns:
53+
Whatever the operation.result() returns.
54+
55+
Raises:
56+
This method will raise the exception received from `operation.exception()`
57+
or RuntimeError if there is no exception set, but there is an `error_code`
58+
set for the `operation`.
59+
60+
In case of an operation taking longer than `timeout` seconds to complete,
61+
a `concurrent.futures.TimeoutError` will be raised.
62+
"""
63+
result = operation.result(timeout=timeout)
64+
65+
if operation.error_code:
66+
print(
67+
f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
68+
file=sys.stderr,
69+
flush=True,
70+
)
71+
print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
72+
raise operation.exception() or RuntimeError(operation.error_message)
73+
74+
if operation.warnings:
75+
print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
76+
for warning in operation.warnings:
77+
print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)
78+
79+
return result
80+
81+
82+
def create_instance(
83+
project_id: str,
84+
zone: str,
85+
instance_name: str,
86+
disks: list[compute_v1.AttachedDisk],
87+
machine_type: str = "n1-standard-1",
88+
network_link: str = "global/networks/default",
89+
subnetwork_link: str = None,
90+
internal_ip: str = None,
91+
external_access: bool = False,
92+
external_ipv4: str = None,
93+
accelerators: list[compute_v1.AcceleratorConfig] = None,
94+
preemptible: bool = False,
95+
spot: bool = False,
96+
instance_termination_action: str = "STOP",
97+
custom_hostname: str = None,
98+
delete_protection: bool = False,
99+
) -> compute_v1.Instance:
100+
"""
101+
Send an instance creation request to the Compute Engine API and wait for it to complete.
102+
103+
Args:
104+
project_id: project ID or project number of the Cloud project you want to use.
105+
zone: name of the zone to create the instance in. For example: "us-west3-b"
106+
instance_name: name of the new virtual machine (VM) instance.
107+
disks: a list of compute_v1.AttachedDisk objects describing the disks
108+
you want to attach to your new instance.
109+
machine_type: machine type of the VM being created. This value uses the
110+
following format: "zones/{zone}/machineTypes/{type_name}".
111+
For example: "zones/europe-west3-c/machineTypes/f1-micro"
112+
network_link: name of the network you want the new instance to use.
113+
For example: "global/networks/default" represents the network
114+
named "default", which is created automatically for each project.
115+
subnetwork_link: name of the subnetwork you want the new instance to use.
116+
This value uses the following format:
117+
"regions/{region}/subnetworks/{subnetwork_name}"
118+
internal_ip: internal IP address you want to assign to the new instance.
119+
By default, a free address from the pool of available internal IP addresses of
120+
used subnet will be used.
121+
external_access: boolean flag indicating if the instance should have an external IPv4
122+
address assigned.
123+
external_ipv4: external IPv4 address to be assigned to this instance. If you specify
124+
an external IP address, it must live in the same region as the zone of the instance.
125+
This setting requires `external_access` to be set to True to work.
126+
accelerators: a list of AcceleratorConfig objects describing the accelerators that will
127+
be attached to the new instance.
128+
preemptible: boolean value indicating if the new instance should be preemptible
129+
or not. Preemptible VMs have been deprecated and you should now use Spot VMs.
130+
spot: boolean value indicating if the new instance should be a Spot VM or not.
131+
instance_termination_action: What action should be taken once a Spot VM is terminated.
132+
Possible values: "STOP", "DELETE"
133+
custom_hostname: Custom hostname of the new VM instance.
134+
Custom hostnames must conform to RFC 1035 requirements for valid hostnames.
135+
delete_protection: boolean value indicating if the new virtual machine should be
136+
protected against deletion or not.
137+
Returns:
138+
Instance object.
139+
"""
140+
instance_client = compute_v1.InstancesClient()
141+
142+
# Use the network interface provided in the network_link argument.
143+
network_interface = compute_v1.NetworkInterface()
144+
network_interface.network = network_link
145+
if subnetwork_link:
146+
network_interface.subnetwork = subnetwork_link
147+
148+
if internal_ip:
149+
network_interface.network_i_p = internal_ip
150+
151+
if external_access:
152+
access = compute_v1.AccessConfig()
153+
access.type_ = compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT.name
154+
access.name = "External NAT"
155+
access.network_tier = access.NetworkTier.PREMIUM.name
156+
if external_ipv4:
157+
access.nat_i_p = external_ipv4
158+
network_interface.access_configs = [access]
159+
160+
# Collect information into the Instance object.
161+
instance = compute_v1.Instance()
162+
instance.network_interfaces = [network_interface]
163+
instance.name = instance_name
164+
instance.disks = disks
165+
if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
166+
instance.machine_type = machine_type
167+
else:
168+
instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"
169+
170+
instance.scheduling = compute_v1.Scheduling()
171+
if accelerators:
172+
instance.guest_accelerators = accelerators
173+
instance.scheduling.on_host_maintenance = (
174+
compute_v1.Scheduling.OnHostMaintenance.TERMINATE.name
175+
)
176+
177+
if preemptible:
178+
# Set the preemptible setting
179+
warnings.warn(
180+
"Preemptible VMs are being replaced by Spot VMs.", DeprecationWarning
181+
)
182+
instance.scheduling = compute_v1.Scheduling()
183+
instance.scheduling.preemptible = True
184+
185+
if spot:
186+
# Set the Spot VM setting
187+
instance.scheduling.provisioning_model = (
188+
compute_v1.Scheduling.ProvisioningModel.SPOT.name
189+
)
190+
instance.scheduling.instance_termination_action = instance_termination_action
191+
192+
if custom_hostname is not None:
193+
# Set the custom hostname for the instance
194+
instance.hostname = custom_hostname
195+
196+
if delete_protection:
197+
# Set the delete protection bit
198+
instance.deletion_protection = True
199+
200+
# Prepare the request to insert an instance.
201+
request = compute_v1.InsertInstanceRequest()
202+
request.zone = zone
203+
request.project = project_id
204+
request.instance_resource = instance
205+
206+
# Wait for the create operation to complete.
207+
print(f"Creating the {instance_name} instance in {zone}...")
208+
209+
operation = instance_client.insert(request=request)
210+
211+
wait_for_extended_operation(operation, "instance creation")
212+
213+
print(f"Instance {instance_name} created.")
214+
return instance_client.get(project=project_id, zone=zone, instance=instance_name)
215+
216+
217+
def create_with_regional_boot_disk(
218+
project_id: str,
219+
zone: str,
220+
instance_name: str,
221+
source_snapshot: str,
222+
disk_region: str,
223+
disk_type: str = "pd-balanced",
224+
) -> compute_v1.Instance:
225+
"""
226+
Creates a new instance with a regional boot disk
227+
Args:
228+
project_id (str): The ID of the Google Cloud project.
229+
zone (str): The zone where the instance will be created.
230+
instance_name (str): The name of the instance.
231+
source_snapshot (str): The name of snapshot to create the boot disk from.
232+
disk_region (str): The region where the disk replicas will be located.
233+
disk_type (str): The type of the disk. Default is 'pd-balanced'.
234+
Returns:
235+
Instance object.
236+
"""
237+
238+
disk = compute_v1.AttachedDisk()
239+
240+
initialize_params = compute_v1.AttachedDiskInitializeParams()
241+
initialize_params.source_snapshot = f"global/snapshots/{source_snapshot}"
242+
initialize_params.disk_type = (
243+
f"projects/{project_id}/zones/{zone}/diskTypes/{disk_type}"
244+
)
245+
initialize_params.replica_zones = [
246+
f"projects/{project_id}/zones/{disk_region}-a",
247+
f"projects/{project_id}/zones/{disk_region}-b",
248+
]
249+
250+
disk.initialize_params = initialize_params
251+
disk.boot = True
252+
disk.auto_delete = True
253+
254+
instance = create_instance(project_id, zone, instance_name, [disk])
255+
256+
return instance
257+
258+
259+
# [END compute_instance_create_replicated_boot_disk]

0 commit comments

Comments
 (0)