From 322055e44d3bd5597a1218a79e9223c80ab94b3e Mon Sep 17 00:00:00 2001 From: Maciej Strzelczyk Date: Thu, 5 May 2022 16:10:00 +0200 Subject: [PATCH] docs(samples): Adding Windows server samples (#274) * docs(samples): Adding Windows instance related sampels Co-authored-by: Anthonios Partheniou --- .../ingredients/firewall/windows_kms.py | 62 ++++ .../create_windows_instance.py | 75 +++++ .../ingredients/instances/get_serial_port.py | 38 +++ compute/compute/ingredients/routes/create.py | 84 +++++ compute/compute/ingredients/routes/delete.py | 40 +++ compute/compute/ingredients/routes/list.py | 38 +++ .../compute/recipes/firewall/windows_kms.py | 23 ++ .../create_windows_instance.py | 31 ++ compute/compute/recipes/routes/create.py | 23 ++ .../recipes/routes/create_kms_route.py | 57 ++++ compute/compute/recipes/routes/delete.py | 23 ++ compute/compute/recipes/routes/list.py | 21 ++ .../compute/snippets/firewall/windows_kms.py | 118 +++++++ .../create_windows_instance.py | 313 ++++++++++++++++++ compute/compute/snippets/routes/create.py | 152 +++++++++ .../snippets/routes/create_kms_route.py | 194 +++++++++++ compute/compute/snippets/routes/delete.py | 94 ++++++ compute/compute/snippets/routes/list.py | 45 +++ compute/compute/snippets/tests/test_route.py | 38 +++ 19 files changed, 1469 insertions(+) create mode 100644 compute/compute/ingredients/firewall/windows_kms.py create mode 100644 compute/compute/ingredients/instances/create_start_instance/create_windows_instance.py create mode 100644 compute/compute/ingredients/instances/get_serial_port.py create mode 100644 compute/compute/ingredients/routes/create.py create mode 100644 compute/compute/ingredients/routes/delete.py create mode 100644 compute/compute/ingredients/routes/list.py create mode 100644 compute/compute/recipes/firewall/windows_kms.py create mode 100644 compute/compute/recipes/instances/create_start_instance/create_windows_instance.py create mode 100644 compute/compute/recipes/routes/create.py create mode 100644 compute/compute/recipes/routes/create_kms_route.py create mode 100644 compute/compute/recipes/routes/delete.py create mode 100644 compute/compute/recipes/routes/list.py create mode 100644 compute/compute/snippets/firewall/windows_kms.py create mode 100644 compute/compute/snippets/instances/create_start_instance/create_windows_instance.py create mode 100644 compute/compute/snippets/routes/create.py create mode 100644 compute/compute/snippets/routes/create_kms_route.py create mode 100644 compute/compute/snippets/routes/delete.py create mode 100644 compute/compute/snippets/routes/list.py create mode 100644 compute/compute/snippets/tests/test_route.py diff --git a/compute/compute/ingredients/firewall/windows_kms.py b/compute/compute/ingredients/firewall/windows_kms.py new file mode 100644 index 000000000000..265d86e666f9 --- /dev/null +++ b/compute/compute/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/compute/ingredients/instances/create_start_instance/create_windows_instance.py b/compute/compute/ingredients/instances/create_start_instance/create_windows_instance.py new file mode 100644 index 000000000000..51d139a5fa41 --- /dev/null +++ b/compute/compute/ingredients/instances/create_start_instance/create_windows_instance.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 +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, + ) + return instance +# diff --git a/compute/compute/ingredients/instances/get_serial_port.py b/compute/compute/ingredients/instances/get_serial_port.py new file mode 100644 index 000000000000..a673e1d27e17 --- /dev/null +++ b/compute/compute/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/compute/ingredients/routes/create.py b/compute/compute/ingredients/routes/create.py new file mode 100644 index 000000000000..a309150c38c6 --- /dev/null +++ b/compute/compute/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/compute/ingredients/routes/delete.py b/compute/compute/ingredients/routes/delete.py new file mode 100644 index 000000000000..52328a559069 --- /dev/null +++ b/compute/compute/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/compute/ingredients/routes/list.py b/compute/compute/ingredients/routes/list.py new file mode 100644 index 000000000000..494f5da6868a --- /dev/null +++ b/compute/compute/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/compute/recipes/firewall/windows_kms.py b/compute/compute/recipes/firewall/windows_kms.py new file mode 100644 index 000000000000..f0948a35e0c9 --- /dev/null +++ b/compute/compute/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/compute/recipes/instances/create_start_instance/create_windows_instance.py b/compute/compute/recipes/instances/create_start_instance/create_windows_instance.py new file mode 100644 index 000000000000..d11fd6fcc185 --- /dev/null +++ b/compute/compute/recipes/instances/create_start_instance/create_windows_instance.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/compute/recipes/routes/create.py b/compute/compute/recipes/routes/create.py new file mode 100644 index 000000000000..6862af012528 --- /dev/null +++ b/compute/compute/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/compute/recipes/routes/create_kms_route.py b/compute/compute/recipes/routes/create_kms_route.py new file mode 100644 index 000000000000..ec693259e978 --- /dev/null +++ b/compute/compute/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/compute/recipes/routes/delete.py b/compute/compute/recipes/routes/delete.py new file mode 100644 index 000000000000..5ea48302b4be --- /dev/null +++ b/compute/compute/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/compute/recipes/routes/list.py b/compute/compute/recipes/routes/list.py new file mode 100644 index 000000000000..3869f571b9ce --- /dev/null +++ b/compute/compute/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/compute/snippets/firewall/windows_kms.py b/compute/compute/snippets/firewall/windows_kms.py new file mode 100644 index 000000000000..ed869386fa28 --- /dev/null +++ b/compute/compute/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/compute/snippets/instances/create_start_instance/create_windows_instance.py b/compute/compute/snippets/instances/create_start_instance/create_windows_instance.py new file mode 100644 index 000000000000..7ed909d37a60 --- /dev/null +++ b/compute/compute/snippets/instances/create_start_instance/create_windows_instance.py @@ -0,0 +1,313 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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_internal_ip] +import re +import sys +from typing import Any, List, Optional + +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, + 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. + 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.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 + + instance.network_interfaces = [network_interface] + + if preemptible: + # Set the preemptible setting + instance.scheduling = compute_v1.Scheduling() + instance.scheduling.preemptible = True + + 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, + ) + return instance + + +# [END compute_create_windows_instance_internal_ip] diff --git a/compute/compute/snippets/routes/create.py b/compute/compute/snippets/routes/create.py new file mode 100644 index 000000000000..abc3c77e5b33 --- /dev/null +++ b/compute/compute/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/compute/snippets/routes/create_kms_route.py b/compute/compute/snippets/routes/create_kms_route.py new file mode 100644 index 000000000000..eeb2cffc876f --- /dev/null +++ b/compute/compute/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/compute/snippets/routes/delete.py b/compute/compute/snippets/routes/delete.py new file mode 100644 index 000000000000..6ef1de95b1c7 --- /dev/null +++ b/compute/compute/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/compute/snippets/routes/list.py b/compute/compute/snippets/routes/list.py new file mode 100644 index 000000000000..b4f83fb07d38 --- /dev/null +++ b/compute/compute/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/compute/snippets/tests/test_route.py b/compute/compute/snippets/tests/test_route.py new file mode 100644 index 000000000000..65ccbf81ece8 --- /dev/null +++ b/compute/compute/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}.")