diff --git a/linode_api4/objects/linode.py b/linode_api4/objects/linode.py index 9fcdae113..9477dd105 100644 --- a/linode_api4/objects/linode.py +++ b/linode_api4/objects/linode.py @@ -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 @@ -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"], @@ -716,6 +720,7 @@ def ips(self): "private": v4pri, "shared": shared_ips, "reserved": reserved, + "vpc": vpc, }, "ipv6": { "slaac": slaac, diff --git a/linode_api4/objects/networking.py b/linode_api4/objects/networking.py index 1b0e46994..17d0ec4c6 100644 --- a/linode_api4/objects/networking.py +++ b/linode_api4/objects/networking.py @@ -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 @@ -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 + + class VLAN(Base): """ .. note:: At this time, the Linode API only supports listing VLANs. diff --git a/test/fixtures/linode_instances_123_ips.json b/test/fixtures/linode_instances_123_ips.json index 147020248..22d61f7b0 100644 --- a/test/fixtures/linode_instances_123_ips.json +++ b/test/fixtures/linode_instances_123_ips.json @@ -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" } } +} \ No newline at end of file diff --git a/test/integration/models/test_linode.py b/test/integration/models/test_linode.py index f46a3fc29..2a69afb65 100644 --- a/test/integration/models/test_linode.py +++ b/test/integration/models/test_linode.py @@ -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, diff --git a/test/unit/objects/linode_test.py b/test/unit/objects/linode_test.py index e9362eabb..c68dcfef6 100644 --- a/test/unit/objects/linode_test.py +++ b/test/unit/objects/linode_test.py @@ -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): """