Skip to content

Commit

Permalink
[snipets][compute_ip_address_assign_static_existing_vm] (#11598)
Browse files Browse the repository at this point in the history
* [snipets][compute_ip_address_assign_static_existing_vm]

* [snipets][compute_ip_address_assign_static_existing_vm] PR comments fix

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Update compute/client_library/ingredients/instances/ip_address/assign_static_ip_to_existing_vm.py

Co-authored-by: Maciej Strzelczyk <strzelczyk@google.com>

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: Stepan Rasputny <srasp@softserveinc.com>
Co-authored-by: Maciej Strzelczyk <strzelczyk@google.com>
  • Loading branch information
4 people authored May 16, 2024
1 parent 9864a37 commit 2e65782
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# flake8: noqa


import uuid

from google.cloud.compute_v1 import InstancesClient
from google.cloud.compute_v1.types import (
AccessConfig,
AddAccessConfigInstanceRequest,
DeleteAccessConfigInstanceRequest,
)


# <INGREDIENT assign_static_ip_to_existing_vm>
def assign_static_ip_to_existing_vm(
project_id: str,
zone: str,
instance_name: str,
ip_address: str,
network_interface_name: str = "nic0",
):
"""
Updates or creates an access configuration for a VM instance to assign a static external IP.
As network interface is immutable - deletion stage is required in case of any assigned ip (static or ephemeral).
VM and ip address must be created before calling this function.
IMPORTANT: VM and assigned IP must be in the same region.
Args:
project_id (str): Project ID.
zone (str): Zone where the VM is located.
instance_name (str): Name of the VM instance.
ip_address (str): New static external IP address to assign to the VM.
network_interface_name (str): Name of the network interface to assign.
Returns:
google.cloud.compute_v1.types.Instance: Updated instance object.
"""
client = InstancesClient()
instance = client.get(project=project_id, zone=zone, instance=instance_name)
network_interface = next(
(ni for ni in instance.network_interfaces if ni.name == network_interface_name),
None,
)

if network_interface is None:
raise ValueError(
f"No network interface named '{network_interface_name}' found on instance {instance_name}."
)

access_config = next(
(ac for ac in network_interface.access_configs if ac.type_ == "ONE_TO_ONE_NAT"),
None,
)

if access_config:
# Delete the existing access configuration first
delete_request = DeleteAccessConfigInstanceRequest(
project=project_id,
zone=zone,
instance=instance_name,
access_config=access_config.name,
network_interface=network_interface_name,
request_id=str(uuid.uuid4()),
)
delete_operation = client.delete_access_config(delete_request)
delete_operation.result()

# Add a new access configuration with the new IP
add_request = AddAccessConfigInstanceRequest(
project=project_id,
zone=zone,
instance=instance_name,
network_interface="nic0",
access_config_resource=AccessConfig(
nat_i_p=ip_address, type_="ONE_TO_ONE_NAT", name="external-nat"
),
request_id=str(uuid.uuid4()),
)
add_operation = client.add_access_config(add_request)
add_operation.result()

updated_instance = client.get(project=project_id, zone=zone, instance=instance_name)
return updated_instance


# </INGREDIENT>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# flake8: noqa


# <REGION compute_ip_address_assign_static_existing_vm>
# <IMPORTS/>

# <INGREDIENT assign_static_ip_to_existing_vm />

# </REGION compute_ip_address_assign_static_existing_vm>

if __name__ == "__main__":
import google.auth

PROJECT = google.auth.default()[1]
ZONE = "us-central1-a"
INSTANCE_NAME = "instance-for-ip-check"
ADDRESS_IP = "34.343.343.34" # put your IP here
assign_static_ip_to_existing_vm(PROJECT, ZONE, INSTANCE_NAME, ADDRESS_IP)
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# flake8: noqa


# This file is automatically generated. Please do not modify it directly.
# Find the relevant recipe file in the samples/recipes or samples/ingredients
# directory and apply your changes there.


# [START compute_ip_address_assign_static_existing_vm]
import uuid

from google.cloud.compute_v1 import InstancesClient
from google.cloud.compute_v1.types import AccessConfig
from google.cloud.compute_v1.types import AddAccessConfigInstanceRequest
from google.cloud.compute_v1.types import DeleteAccessConfigInstanceRequest


def assign_static_ip_to_existing_vm(
project_id: str,
zone: str,
instance_name: str,
ip_address: str,
network_interface_name: str = "nic0",
):
"""
Updates or creates an access configuration for a VM instance to assign a static external IP.
As network interface is immutable - deletion stage is required in case of any assigned ip (static or ephemeral).
VM and ip address must be created before calling this function.
IMPORTANT: VM and assigned IP must be in the same region.
Args:
project_id (str): Project ID.
zone (str): Zone where the VM is located.
instance_name (str): Name of the VM instance.
ip_address (str): New static external IP address to assign to the VM.
network_interface_name (str): Name of the network interface to assign.
Returns:
google.cloud.compute_v1.types.Instance: Updated instance object.
"""
client = InstancesClient()
instance = client.get(project=project_id, zone=zone, instance=instance_name)
network_interface = next(
(ni for ni in instance.network_interfaces if ni.name == network_interface_name),
None,
)

if network_interface is None:
raise ValueError(
f"No network interface named '{network_interface_name}' found on instance {instance_name}."
)

access_config = next(
(ac for ac in network_interface.access_configs if ac.type_ == "ONE_TO_ONE_NAT"),
None,
)

if access_config:
# Delete the existing access configuration first
delete_request = DeleteAccessConfigInstanceRequest(
project=project_id,
zone=zone,
instance=instance_name,
access_config=access_config.name,
network_interface=network_interface_name,
request_id=str(uuid.uuid4()),
)
delete_operation = client.delete_access_config(delete_request)
delete_operation.result()

# Add a new access configuration with the new IP
add_request = AddAccessConfigInstanceRequest(
project=project_id,
zone=zone,
instance=instance_name,
network_interface="nic0",
access_config_resource=AccessConfig(
nat_i_p=ip_address, type_="ONE_TO_ONE_NAT", name="external-nat"
),
request_id=str(uuid.uuid4()),
)
add_operation = client.add_access_config(add_request)
add_operation.result()

updated_instance = client.get(project=project_id, zone=zone, instance=instance_name)
return updated_instance


# [END compute_ip_address_assign_static_existing_vm]

if __name__ == "__main__":
import google.auth

PROJECT = google.auth.default()[1]
ZONE = "us-central1-a"
INSTANCE_NAME = "instance-for-ip-check"
ADDRESS_IP = "34.343.343.34" # put your IP here
assign_static_ip_to_existing_vm(PROJECT, ZONE, INSTANCE_NAME, ADDRESS_IP)
23 changes: 23 additions & 0 deletions compute/client_library/snippets/tests/test_ip_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
get_image_from_family,
)
from ..instances.delete import delete_instance
from ..instances.ip_address.assign_static_ip_to_existing_vm import (
assign_static_ip_to_existing_vm,
)
from ..instances.ip_address.get_static_ip_address import get_static_ip_address
from ..instances.ip_address.get_vm_address import get_instance_ip_address, IPType
from ..instances.ip_address.list_static_ip_addresses import list_static_ip_addresses
Expand Down Expand Up @@ -273,6 +276,26 @@ def test_release_static_ip(static_ip: Address):
assert static_ip.name not in ips


@pytest.mark.parametrize("static_ip", [{"region": "us-central1"}], indirect=True)
def test_assign_static_ip_to_existing_vm(
instance_with_ips: Instance, static_ip: Address
):
PROJECT = google.auth.default()[1]
ZONE = "us-central1-b"
REGION = "us-central1"

client = AddressesClient()
ip_address = client.get(project=PROJECT, region=REGION, address=static_ip.name)

updated_instance = assign_static_ip_to_existing_vm(
PROJECT, ZONE, instance_with_ips.name, ip_address.address
)
assert (
updated_instance.network_interfaces[0].access_configs[0].nat_i_p
== ip_address.address
)


def test_unassign_static_ip_from_existing_vm(instance_with_ips: Instance):
PROJECT = google.auth.default()[1]
ZONE = "us-central1-b"
Expand Down

0 comments on commit 2e65782

Please sign in to comment.