Skip to content

Commit f2db196

Browse files
TPT-4602: Added support for NAT Service #1033 (#738)
* Added support for NAT Gateway * Fix lint * Address CoPilot suggestions * Added doc links * Address CoPilot fixes * Address more CoPilot suggestions
1 parent 301fad1 commit f2db196

25 files changed

Lines changed: 1258 additions & 18 deletions

‎linode_api4/groups/networking.py‎

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@
1313
IPAddress,
1414
IPv6Pool,
1515
IPv6Range,
16+
NATGateway,
17+
NATGatewayAddress,
1618
NetworkTransferPrice,
1719
Region,
1820
)
1921
from linode_api4.objects.base import _flatten_request_body_recursive
20-
from linode_api4.objects.networking import ReservedIPAddress, ReservedIPType
22+
from linode_api4.objects.networking import (
23+
NATGatewaySettings,
24+
NATGatewayType,
25+
ReservedIPAddress,
26+
ReservedIPType,
27+
)
2128
from linode_api4.paginated_list import PaginatedList
2229
from linode_api4.util import drop_null_keys
2330

@@ -622,3 +629,116 @@ def reserved_ip_types(self, *filters) -> PaginatedList:
622629
return self.client._get_and_filter(
623630
ReservedIPType, *filters, endpoint="/networking/reserved/ips/types"
624631
)
632+
633+
def natgateways(self, *filters):
634+
"""
635+
Retrieves the NAT Gateways your user has access to.
636+
637+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-natgateways
638+
639+
:param filters: Any number of filters to apply to this query.
640+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
641+
for more details on filtering.
642+
643+
:returns: A list of NAT Gateways the acting user can access.
644+
:rtype: PaginatedList of NATGateway
645+
"""
646+
return self.client._get_and_filter(NATGateway, *filters)
647+
648+
def natgateway_create(
649+
self,
650+
region: Union[Region, str],
651+
label: str,
652+
addresses: Optional[
653+
List[Union[NATGatewayAddress, Dict[str, Any]]]
654+
] = None,
655+
default_ports_per_interface: Optional[int] = None,
656+
use_autoscaling: Optional[bool] = None,
657+
vpc_subnet_id: Optional[int] = None,
658+
**kwargs,
659+
) -> NATGateway:
660+
"""
661+
Create a NAT Gateway in the given region.
662+
663+
NOTE: NAT Gateways may not currently be available to all users.
664+
665+
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-natgateways
666+
667+
:param region: The region in which to create the NAT Gateway.
668+
:type region: str or Region
669+
:param label: The label of the NAT Gateway.
670+
:type label: str
671+
:param addresses: The reserved IP addresses to assign to this NAT Gateway.
672+
:type addresses: list of NATGatewayAddress or list of dict
673+
:param default_ports_per_interface: The default ports per interface to use.
674+
Defaults to 4096 on the API side.
675+
:type default_ports_per_interface: int
676+
:param use_autoscaling: Whether the NAT Gateway should autoscale its address pool.
677+
:type use_autoscaling: bool
678+
:param vpc_subnet_id: The ID of the VPC subnet this NAT Gateway should attach to.
679+
:type vpc_subnet_id: int
680+
681+
:returns: The new NAT Gateway.
682+
:rtype: NATGateway
683+
"""
684+
params = {
685+
"region": region.id if isinstance(region, Region) else region,
686+
"label": label,
687+
"addresses": addresses,
688+
"default_ports_per_interface": default_ports_per_interface,
689+
"use_autoscaling": use_autoscaling,
690+
"vpc_subnet_id": vpc_subnet_id,
691+
}
692+
params.update(kwargs)
693+
694+
result = self.client.post(
695+
"/networking/natgateways",
696+
data=drop_null_keys(_flatten_request_body_recursive(params)),
697+
)
698+
699+
if "id" not in result:
700+
raise UnexpectedResponseError(
701+
"Unexpected response when creating NAT Gateway!", json=result
702+
)
703+
704+
return NATGateway(self.client, result["id"], result)
705+
706+
def natgateway_types(self, *filters) -> PaginatedList:
707+
"""
708+
Returns a list of NAT Gateway types with pricing information.
709+
710+
NOTE: NAT Gateways may not currently be available to all users.
711+
712+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-natgateway-types
713+
714+
:param filters: Any number of filters to apply to this query.
715+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
716+
for more details on filtering.
717+
718+
:returns: A list of NAT Gateway types.
719+
:rtype: PaginatedList of NATGatewayType
720+
"""
721+
return self.client._get_and_filter(
722+
NATGatewayType, *filters, endpoint="/networking/natgateways/types"
723+
)
724+
725+
def natgateway_settings(self) -> NATGatewaySettings:
726+
"""
727+
Returns the account-wide NAT Gateway settings and limits for the current user.
728+
729+
NOTE: NAT Gateways may not currently be available to all users.
730+
731+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-natgateway-settings
732+
733+
:returns: The NAT Gateway settings for the current user.
734+
:rtype: NATGatewaySettings
735+
"""
736+
result = self.client.get("/networking/natgateways/settings")
737+
738+
if "allowed_ports_per_interface" not in result:
739+
raise UnexpectedResponseError(
740+
"Unexpected response when getting NAT Gateway settings!",
741+
json=result,
742+
)
743+
744+
return NATGatewaySettings.from_json(result)

‎linode_api4/groups/vpc.py‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ def create(
7373
"description": description,
7474
"ipv4": ipv4,
7575
"ipv6": ipv6,
76-
"subnets": subnets,
7776
}
7877

7978
if subnets is not None and len(subnets) > 0:
@@ -85,10 +84,30 @@ def create(
8584

8685
params.update(kwargs)
8786

88-
result = self.client.post(
89-
"/vpcs",
90-
data=drop_null_keys(_flatten_request_body_recursive(params)),
91-
)
87+
data = drop_null_keys(_flatten_request_body_recursive(params))
88+
89+
# Recursively clean each subnet while preserving the ``{"id": null}``
90+
# detach signal on any explicitly-supplied ``natgateway`` object.
91+
if subnets is not None:
92+
cleaned_subnets = []
93+
for subnet in subnets:
94+
flattened = _flatten_request_body_recursive(subnet)
95+
cleaned = drop_null_keys(flattened)
96+
97+
# If the caller explicitly supplied a ``natgateway`` field,
98+
# re-inject its flattened (but not null-stripped) form so
99+
# the detach signal survives.
100+
if (
101+
isinstance(flattened, dict)
102+
and "natgateway" in flattened
103+
and flattened["natgateway"] is not None
104+
):
105+
cleaned["natgateway"] = flattened["natgateway"]
106+
107+
cleaned_subnets.append(cleaned)
108+
data["subnets"] = cleaned_subnets
109+
110+
result = self.client.post("/vpcs", data=data)
92111

93112
if not "id" in result:
94113
raise UnexpectedResponseError(

‎linode_api4/objects/linode_interfaces.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,38 @@ class LinodeInterfaceVPCIPv4Range(JSONObject):
245245
range: str = ""
246246

247247

248+
@dataclass
249+
class LinodeInterfaceVPCIPv4NATGatewayPortsetPort(JSONObject):
250+
start: int = 0
251+
end: int = 0
252+
253+
254+
@dataclass
255+
class LinodeInterfaceVPCIPv4NATGatewayPortset(JSONObject):
256+
address: str = ""
257+
ports: List[LinodeInterfaceVPCIPv4NATGatewayPortsetPort] = field(
258+
default_factory=list
259+
)
260+
261+
262+
@dataclass
263+
class LinodeInterfaceVPCIPv4NATGateway(JSONObject):
264+
"""
265+
A NAT gateway under the IPv4 configuration of a VPC Linode Interface.
266+
"""
267+
268+
id: int = 0
269+
label: str = ""
270+
type: str = ""
271+
url: str = ""
272+
addresses: List[str] = field(default_factory=list)
273+
portset_assignments: int = 0
274+
portset_capacity: int = 0
275+
portsets: List[LinodeInterfaceVPCIPv4NATGatewayPortset] = field(
276+
default_factory=list
277+
) # NOTE: This field may not be available to all users.
278+
279+
248280
@dataclass
249281
class LinodeInterfaceVPCIPv4(JSONObject):
250282
"""
@@ -255,6 +287,7 @@ class LinodeInterfaceVPCIPv4(JSONObject):
255287

256288
addresses: List[LinodeInterfaceVPCIPv4Address] = field(default_factory=list)
257289
ranges: List[LinodeInterfaceVPCIPv4Range] = field(default_factory=list)
290+
natgateway: Optional[LinodeInterfaceVPCIPv4NATGateway] = None
258291

259292

260293
@dataclass

0 commit comments

Comments
 (0)