Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion linode_api4/objects/linode.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)
from linode_api4.objects.base import MappedObject
from linode_api4.objects.filtering import FilterableAttribute
from linode_api4.objects.networking import IPAddress, IPv6Range
from linode_api4.objects.networking import IPAddress, IPv6Range, VPCIPAddress
from linode_api4.objects.vpc import VPC, VPCSubnet
from linode_api4.paginated_list import PaginatedList

Expand Down Expand Up @@ -693,6 +693,10 @@ def ips(self):
i = IPAddress(self._client, c["address"], c)
reserved.append(i)

vpc = [
VPCIPAddress.from_json(v) for v in result["ipv4"].get("vpc", [])
]

slaac = IPAddress(
self._client,
result["ipv6"]["slaac"]["address"],
Expand All @@ -716,6 +720,7 @@ def ips(self):
"private": v4pri,
"shared": shared_ips,
"reserved": reserved,
"vpc": vpc,
},
"ipv6": {
"slaac": slaac,
Expand Down
27 changes: 27 additions & 0 deletions linode_api4/objects/networking.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from typing import Optional

from linode_api4.errors import UnexpectedResponseError
from linode_api4.objects import Base, DerivedBase, JSONObject, Property, Region
Expand Down Expand Up @@ -106,6 +107,32 @@ def to(self, linode):
return {"address": self.address, "linode_id": linode.id}


@dataclass
class VPCIPAddress(JSONObject):
"""
VPCIPAddress represents the IP address of a VPC.

NOTE: This is not implemented as a typical API object (Base) because VPC IPs
cannot be refreshed through the /networking/ips/{address} endpoint.
"""

address: str = ""
gateway: str = ""
region: str = ""
subnet_mask: str = ""
vpc_id: int = 0
subnet_id: int = 0
linode_id: int = 0
config_id: int = 0
interface_id: int = 0
prefix: int = 0

active: bool = False

address_range: Optional[str] = None
nat_1_1: Optional[str] = None


Comment on lines +133 to +135

@lgarber-akamai lgarber-akamai Mar 18, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we think it'd make sense to implement property methods here to allow for easy access to the corresponding VPC, VPCSubnet, etc. objects from this class? The only issue is that a client isn't embedded in JSON objects as of now, although it would be a relatively straightforward change to make.

edit: Just created an internal feedback entry for this 馃憤

class VLAN(Base):
"""
.. note:: At this time, the Linode API only supports listing VLANs.
Expand Down
171 changes: 94 additions & 77 deletions test/fixtures/linode_instances_123_ips.json
Original file line number Diff line number Diff line change
@@ -1,89 +1,106 @@
{
"ipv4": {
"private": [
{
"address": "192.168.133.234",
"gateway": null,
"linode_id": 123,
"prefix": 17,
"public": false,
"rdns": null,
"region": "us-east",
"subnet_mask": "255.255.128.0",
"type": "ipv4"
}
],
"public": [
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 24,
"public": true,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
],
"reserved": [
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 24,
"public": true,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
],
"shared": [
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 24,
"public": true,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
]
},
"ipv6": {
"global": [
{
"prefix": 124,
"range": "2600:3c01::2:5000:0",
"region": "us-east",
"route_target": "2600:3c01::2:5000:f"
}
],
"link_local": {
"address": "fe80::f03c:91ff:fe24:3a2f",
"gateway": "fe80::1",
"ipv4": {
"private": [
{
"address": "192.168.133.234",
"gateway": null,
"linode_id": 123,
"prefix": 64,
"prefix": 17,
"public": false,
"rdns": null,
"region": "us-east",
"subnet_mask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"type": "ipv6"
},
"slaac": {
"address": "2600:3c03::f03c:91ff:fe24:3a2f",
"gateway": "fe80::1",
"subnet_mask": "255.255.128.0",
"type": "ipv4"
}
],
"public": [
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 64,
"prefix": 24,
"public": true,
"rdns": null,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
],
"reserved": [
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 24,
"public": true,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"type": "ipv6"
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
],
"vpc": [
{
"address": "10.0.0.2",
"address_range": null,
"vpc_id": 39246,
"subnet_id": 39388,
"region": "us-mia",
"linode_id": 55904908,
"config_id": 59036295,
"interface_id": 1186165,
"active": true,
"nat_1_1": "172.233.179.133",
"gateway": "10.0.0.1",
"prefix": 24,
"subnet_mask": "255.255.255.0"
}
],
"shared": [
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 24,
"public": true,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
]
},
"ipv6": {
"global": [
{
"prefix": 124,
"range": "2600:3c01::2:5000:0",
"region": "us-east",
"route_target": "2600:3c01::2:5000:f"
}
],
"link_local": {
"address": "fe80::f03c:91ff:fe24:3a2f",
"gateway": "fe80::1",
"linode_id": 123,
"prefix": 64,
"public": false,
"rdns": null,
"region": "us-east",
"subnet_mask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"type": "ipv6"
},
"slaac": {
"address": "2600:3c03::f03c:91ff:fe24:3a2f",
"gateway": "fe80::1",
"linode_id": 123,
"prefix": 64,
"public": true,
"rdns": null,
"region": "us-east",
"subnet_mask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"type": "ipv6"
}
}
}

14 changes: 14 additions & 0 deletions test/integration/models/test_linode.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,20 @@ def test_create_vpc(
assert interface.ipv4.nat_1_1 == linode.ipv4[0]
assert interface.ip_ranges == ["10.0.0.5/32"]

vpc_ip = linode.ips.ipv4.vpc[0]
vpc_range_ip = linode.ips.ipv4.vpc[1]

assert vpc_ip.nat_1_1 == linode.ips.ipv4.public[0].address
assert vpc_ip.address_range is None
assert vpc_ip.vpc_id == vpc.id
assert vpc_ip.subnet_id == subnet.id
assert vpc_ip.config_id == config.id
assert vpc_ip.interface_id == interface.id
assert not vpc_ip.active

assert vpc_range_ip.address_range == "10.0.0.5/32"
assert not vpc_range_ip.active

def test_update_vpc(
self,
linode_for_network_interface_tests,
Expand Down
28 changes: 19 additions & 9 deletions test/unit/objects/linode_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,25 @@ def test_ips(self):

ips = linode.ips

self.assertIsNotNone(ips.ipv4)
self.assertIsNotNone(ips.ipv6)
self.assertIsNotNone(ips.ipv4.public)
self.assertIsNotNone(ips.ipv4.private)
self.assertIsNotNone(ips.ipv4.shared)
self.assertIsNotNone(ips.ipv4.reserved)
self.assertIsNotNone(ips.ipv6.slaac)
self.assertIsNotNone(ips.ipv6.link_local)
self.assertIsNotNone(ips.ipv6.ranges)
assert ips.ipv4 is not None
assert ips.ipv6 is not None
assert ips.ipv4.public is not None
assert ips.ipv4.private is not None
assert ips.ipv4.shared is not None
assert ips.ipv4.reserved is not None
assert ips.ipv4.vpc is not None
assert ips.ipv6.slaac is not None
assert ips.ipv6.link_local is not None
assert ips.ipv6.ranges is not None

vpc_ip = ips.ipv4.vpc[0]
assert vpc_ip.nat_1_1 == "172.233.179.133"
assert vpc_ip.address_range == None
assert vpc_ip.vpc_id == 39246
assert vpc_ip.subnet_id == 39388
assert vpc_ip.config_id == 59036295
assert vpc_ip.interface_id == 1186165
assert vpc_ip.active

def test_initiate_migration(self):
"""
Expand Down