Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
062d9e0
TPT-4278 python-sdk: Implement support for Reserved IP for IPv4
mgwoj Mar 26, 2026
220daed
TPT-4278: python-sdk: Implement support for Reserved IP for IPv4
mgwoj Apr 15, 2026
3535a89
TPT-4278: python-sdk: Implement support for Reserved IP for IPv4
mgwoj Apr 15, 2026
4c2a5e1
TPT-4278: python-sdk: Implement support for Reserved IP for IPv4
mgwoj Apr 15, 2026
97c4a72
TPT-4278: python-sdk: Implement support for Reserved IP for IPv4
mgwoj Apr 15, 2026
a99193f
Update linode_api4/groups/networking.py
mgwoj Apr 15, 2026
3a94fc5
Update conftest.py
mgwoj Apr 15, 2026
94fbe6c
Create int tests for Reserved IPs networking endpoints
mawilk90 Apr 24, 2026
9e7dbbf
Create int tests for Reserved IPs: types, allocate
mawilk90 Apr 24, 2026
9e6ad27
Create int tests for Reserved IPs: ephemeral
mawilk90 Apr 24, 2026
107ac3f
Create int tests for Reserved IPs: linode instances
mawilk90 Apr 24, 2026
bd24819
Move fixtures for reserved IPs into conftest
mawilk90 Apr 27, 2026
3961d4b
Create int tests for Reserved IPs: linode interfaces
mawilk90 Apr 27, 2026
00afc23
Create int tests for Reserved IPs: nodebalancers
mawilk90 Apr 27, 2026
0f7e06c
Create int tests for Reserved IPs: tags
mawilk90 Apr 27, 2026
59004bf
Refactor
mawilk90 Apr 27, 2026
3f867e3
Merge branch 'proj/reserved-ips' into feature/TPT-4285-python-sdk-imp…
mawilk90 Apr 28, 2026
ebe8d96
Update tests after API changes on DevCloud
mawilk90 Apr 28, 2026
760ad62
Create int tests for Reserved IPs: tags #2
mawilk90 Apr 28, 2026
7a7d708
Linter fix
mawilk90 Apr 28, 2026
4effa3e
Linter fix
mawilk90 Apr 28, 2026
3e8c558
Remove pytest.ini
mawilk90 Apr 28, 2026
2401b02
Remove unused assertions after API Team clarifications
mawilk90 Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions test/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
PlacementGroupPolicy,
PlacementGroupType,
PostgreSQLDatabase,
ReservedIPAddress,
)
from linode_api4.errors import ApiError
from linode_api4.linode_client import LinodeClient, MonitorClient
Expand Down Expand Up @@ -728,3 +729,47 @@ def test_monitor_client(get_monitor_token_for_db_entities):
)

return client, entity_ids


@pytest.fixture
def create_reserved_ip(test_linode_client):
client = test_linode_client
region = get_region(client, {"Linodes", "Cloud Firewall"}, site_type="core")
reserved_ip = client.networking.reserved_ip_create(
region=region, tags=["test"]
)

yield reserved_ip

# Delete only if IP exists (some tests delete it earlier)
if client.networking.reserved_ips(
ReservedIPAddress.address == reserved_ip.address
):
reserved_ip.delete()


@pytest.fixture
def create_reserved_ip_assigned(test_linode_client, create_linode):
client = test_linode_client
linode = create_linode
reserved_ip = client.networking.reserved_ip_create(
region=linode.region,
tags=["test", "assigned"],
)

client.networking.ip_addresses_assign(
assignments=[{"address": reserved_ip.address, "linode_id": linode.id}],
region=linode.region,
)

reserved_ip = test_linode_client.load(
ReservedIPAddress, reserved_ip.address
)

yield linode, reserved_ip

# Delete only if IP exists (some tests delete it earlier)
if client.networking.reserved_ips(
ReservedIPAddress.address == reserved_ip.address
):
reserved_ip.delete()
78 changes: 78 additions & 0 deletions test/integration/models/linode/interfaces/test_interfaces.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import copy
import ipaddress
from test.integration.helpers import get_test_label

import pytest

from linode_api4 import (
ApiError,
Instance,
InterfaceGeneration,
LinodeInterface,
LinodeInterfaceDefaultRouteOptions,
LinodeInterfaceOptions,
LinodeInterfacePublicIPv4AddressOptions,
LinodeInterfacePublicIPv4Options,
LinodeInterfacePublicIPv6Options,
Expand All @@ -18,9 +21,28 @@
LinodeInterfaceVPCIPv4Options,
LinodeInterfaceVPCIPv4RangeOptions,
LinodeInterfaceVPCOptions,
ReservedIPAddress,
)


def build_interface_public_ipv4(firewall, ip_address):
return LinodeInterfaceOptions(
firewall_id=firewall,
default_route=LinodeInterfaceDefaultRouteOptions(
ipv4=True,
),
public=LinodeInterfacePublicOptions(
ipv4=LinodeInterfacePublicIPv4Options(
addresses=[
LinodeInterfacePublicIPv4AddressOptions(
address=ip_address, primary=True
)
],
),
),
)


def test_linode_create_with_linode_interfaces(
create_vpc_with_subnet,
linode_with_linode_interfaces,
Expand Down Expand Up @@ -359,3 +381,59 @@ def test_linode_interface_firewalls(e2e_test_firewall, linode_interface_public):
firewall = firewalls[0]
assert firewall.id == e2e_test_firewall.id
assert firewall.label == e2e_test_firewall.label


@pytest.mark.parametrize(
"iface_type",
[InterfaceGeneration.LEGACY_CONFIG, InterfaceGeneration.LINODE],
)
def test_linode_interfaces_with_reserved_ips(
test_linode_client, e2e_test_firewall, create_reserved_ip, iface_type
):
client = test_linode_client
reserved_ip = create_reserved_ip
label = get_test_label(length=8)

if iface_type == InterfaceGeneration.LEGACY_CONFIG:
linode, _ = client.linode.instance_create(
"g6-nanode-1",
reserved_ip.region,
image="linode/debian12",
label=label,
firewall=e2e_test_firewall,
interface_generation=iface_type,
ipv4=[reserved_ip.address],
)
else:
interface = build_interface_public_ipv4(
e2e_test_firewall.id, reserved_ip.address
)
linode, _ = client.linode.instance_create(
"g6-nanode-1",
reserved_ip.region,
image="linode/debian12",
label=label,
interface_generation=iface_type,
interfaces=[interface],
)

linode_ips = linode.ips.ipv4.public
assert len(linode_ips) == 1
assert linode_ips[0].address == reserved_ip.address
assert linode_ips[0].reserved == True
assert linode_ips[0].linode_id == linode.id
assert linode_ips[0].assigned_entity.id == linode.id
assert linode_ips[0].assigned_entity.type == "linode"
assert linode_ips[0].assigned_entity.label == linode.label
assert (
linode_ips[0].assigned_entity.url == f"/v4/linode/instances/{linode.id}"
)

linode.delete()
reserved_ips_list = client.networking.reserved_ips(
ReservedIPAddress.address == reserved_ip.address
)
assert len(reserved_ips_list) == 1
assert reserved_ips_list[0].reserved == True
assert reserved_ips_list[0].linode_id is None
assert reserved_ips_list[0].assigned_entity is None
40 changes: 40 additions & 0 deletions test/integration/models/linode/test_linode.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Instance,
InterfaceGeneration,
LinodeInterface,
ReservedIPAddress,
Type,
)
from linode_api4.objects.linode import InstanceDiskEncryptionType, MigrationType
Expand Down Expand Up @@ -1147,3 +1148,42 @@ def test_update_linode_maintenance_policy(create_linode, test_linode_client):
linode.invalidate()
assert result
assert linode.maintenance_policy_id == non_default_policy.slug


def test_update_linode_with_reserved_ip_in_address(
test_linode_client, e2e_test_firewall, create_reserved_ip
):
label = get_test_label(length=8)
client = test_linode_client
reserved_ip = create_reserved_ip

linode, _ = client.linode.instance_create(
"g6-nanode-1",
reserved_ip.region,
image="linode/debian12",
label=label,
firewall=e2e_test_firewall,
)

linode_ips = linode.ips.ipv4.public
assert len(linode_ips) == 1
assert linode_ips[0].address != reserved_ip.address

linode.ip_allocate(True, reserved_ip.address)
delattr(linode, "_ips")
linode_ips = linode.ips.ipv4.public
assert len(linode_ips) == 2
assert reserved_ip.address in [ip.address for ip in linode_ips]

reserved_ip = client.networking.reserved_ips(
ReservedIPAddress.address == reserved_ip.address
)[0]
assert reserved_ip.linode_id == linode.id
assert reserved_ip.assigned_entity.id == linode.id
assert reserved_ip.assigned_entity.type == "linode"
assert reserved_ip.assigned_entity.label == linode.label
assert (
reserved_ip.assigned_entity.url == f"/v4/linode/instances/{linode.id}"
)

linode.delete()
Loading
Loading