Skip to content

Commit 6e9a204

Browse files
Enhanced Interfaces: Add support for Linode-related endpoints and fields (#533)
* Add support for Linode-related endpoints and fields * oops * tiny fixes * fix docsa * Add docs examples * Docs fixes * oops * Remove irrelevant test * Add LA notices * Fill in API documentation URLs * Add return types
1 parent 5727f10 commit 6e9a204

22 files changed

+2446
-46
lines changed

linode_api4/groups/linode.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import base64
22
import os
3-
from collections.abc import Iterable
4-
from typing import Any, Dict, Optional, Union
3+
from typing import Any, Dict, List, Optional, Union
54

65
from linode_api4.common import load_and_validate_keys
76
from linode_api4.errors import UnexpectedResponseError
87
from linode_api4.groups import Group
98
from linode_api4.objects import (
10-
ConfigInterface,
119
Firewall,
1210
Instance,
1311
InstanceDiskEncryptionType,
@@ -21,8 +19,13 @@
2119
from linode_api4.objects.linode import (
2220
Backup,
2321
InstancePlacementGroupAssignment,
22+
InterfaceGeneration,
23+
NetworkInterface,
2424
_expand_placement_group_assignment,
2525
)
26+
from linode_api4.objects.linode_interfaces import (
27+
LinodeInterfaceOptions,
28+
)
2629
from linode_api4.util import drop_null_keys
2730

2831

@@ -153,6 +156,13 @@ def instance_create(
153156
int,
154157
]
155158
] = None,
159+
interfaces: Optional[
160+
List[
161+
Union[LinodeInterfaceOptions, NetworkInterface, Dict[str, Any]],
162+
]
163+
] = None,
164+
interface_generation: Optional[Union[InterfaceGeneration, str]] = None,
165+
network_helper: Optional[bool] = None,
156166
**kwargs,
157167
):
158168
"""
@@ -230,6 +240,30 @@ def instance_create(
230240
"us-east",
231241
backup=snapshot)
232242
243+
**Create an Instance with explicit interfaces:**
244+
245+
To create a new Instance with explicit interfaces, provide list of
246+
LinodeInterfaceOptions objects or dicts to the "interfaces" field::
247+
248+
linode, password = client.linode.instance_create(
249+
"g6-standard-1",
250+
"us-mia",
251+
image="linode/ubuntu24.04",
252+
253+
# This can be configured as an account-wide default
254+
interface_generation=InterfaceGeneration.LINODE,
255+
256+
interfaces=[
257+
LinodeInterfaceOptions(
258+
default_route=LinodeInterfaceDefaultRouteOptions(
259+
ipv4=True,
260+
ipv6=True
261+
),
262+
public=LinodeInterfacePublicOptions
263+
)
264+
]
265+
)
266+
233267
**Create an empty Instance**
234268
235269
If you want to create an empty Instance that you will configure manually,
@@ -293,9 +327,13 @@ def instance_create(
293327
:type disk_encryption: InstanceDiskEncryptionType or str
294328
:param interfaces: An array of Network Interfaces to add to this Linode’s Configuration Profile.
295329
At least one and up to three Interface objects can exist in this array.
296-
:type interfaces: list[ConfigInterface] or list[dict[str, Any]]
330+
:type interfaces: List[LinodeInterfaceOptions], List[NetworkInterface], or List[dict[str, Any]]
297331
:param placement_group: A Placement Group to create this Linode under.
298332
:type placement_group: Union[InstancePlacementGroupAssignment, PlacementGroup, Dict[str, Any], int]
333+
:param interface_generation: The generation of network interfaces this Linode uses.
334+
:type interface_generation: InterfaceGeneration or str
335+
:param network_helper: Whether this instance should have Network Helper enabled.
336+
:type network_helper: bool
299337
300338
:returns: A new Instance object, or a tuple containing the new Instance and
301339
the generated password.
@@ -311,13 +349,6 @@ def instance_create(
311349
ret_pass = Instance.generate_root_password()
312350
kwargs["root_pass"] = ret_pass
313351

314-
interfaces = kwargs.get("interfaces", None)
315-
if interfaces is not None and isinstance(interfaces, Iterable):
316-
kwargs["interfaces"] = [
317-
i._serialize() if isinstance(i, ConfigInterface) else i
318-
for i in interfaces
319-
]
320-
321352
params = {
322353
"type": ltype,
323354
"region": region,
@@ -336,6 +367,9 @@ def instance_create(
336367
if placement_group
337368
else None
338369
),
370+
"interfaces": interfaces,
371+
"interface_generation": interface_generation,
372+
"network_helper": network_helper,
339373
}
340374

341375
params.update(kwargs)

linode_api4/groups/networking.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def firewall_templates(self, *filters):
119119
"""
120120
Returns a list of Firewall Templates available to the current user.
121121
122-
API Documentation: Not yet available.
122+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-firewall-templates
123123
124124
NOTE: This feature may not currently be available to all users.
125125
@@ -135,7 +135,9 @@ def firewall_templates(self, *filters):
135135
def firewall_settings(self) -> FirewallSettings:
136136
"""
137137
Returns an object representing the Linode Firewall settings for the current user.
138-
API Documentation: Not yet available.
138+
139+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-firewall-settings
140+
139141
NOTE: This feature may not currently be available to all users.
140142
:returns: An object representing the Linode Firewall settings for the current user.
141143
:rtype: FirewallSettings
@@ -180,6 +182,64 @@ def ipv6_ranges(self, *filters):
180182
"""
181183
return self.client._get_and_filter(IPv6Range, *filters)
182184

185+
def ipv6_range_allocate(
186+
self,
187+
prefix_length: int,
188+
route_target: Optional[str] = None,
189+
linode: Optional[Union[Instance, int]] = None,
190+
**kwargs,
191+
) -> IPv6Range:
192+
"""
193+
Creates an IPv6 Range and assigns it based on the provided Linode or route target IPv6 SLAAC address.
194+
195+
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-ipv6-range
196+
197+
Create an IPv6 range assigned to a Linode by ID::
198+
199+
range = client.networking.ipv6_range_allocate(64, linode_id=123)
200+
201+
202+
Create an IPv6 range assigned to a Linode by SLAAC::
203+
204+
range = client.networking.ipv6_range_allocate(
205+
64,
206+
route_target=instance.ipv6.split("/")[0]
207+
)
208+
209+
:param prefix_length: The prefix length of the IPv6 range.
210+
:type prefix_length: int
211+
:param route_target: The IPv6 SLAAC address to assign this range to. Required if linode is not specified.
212+
:type route_target: str
213+
:param linode: The ID of the Linode to assign this range to.
214+
The SLAAC address for the provided Linode is used as the range's route_target.
215+
Required if linode is not specified.
216+
:type linode: Instance or int
217+
218+
:returns: The new IPAddress.
219+
:rtype: IPAddress
220+
"""
221+
222+
params = {
223+
"prefix_length": prefix_length,
224+
"route_target": route_target,
225+
"linode_id": linode,
226+
}
227+
228+
params.update(**kwargs)
229+
230+
result = self.client.post(
231+
"/networking/ipv6/ranges",
232+
data=drop_null_keys(_flatten_request_body_recursive(params)),
233+
)
234+
235+
if not "range" in result:
236+
raise UnexpectedResponseError(
237+
"Unexpected response when allocating IPv6 range!", json=result
238+
)
239+
240+
result = IPv6Range(self.client, result["range"], result)
241+
return result
242+
183243
def ipv6_pools(self, *filters):
184244
"""
185245
Returns a list of IPv6 pools on this account.

linode_api4/objects/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .region import Region
77
from .image import Image
88
from .linode import *
9+
from .linode_interfaces import *
910
from .volume import *
1011
from .domain import *
1112
from .account import *

linode_api4/objects/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ def __setattr__(self, name, value):
239239
"""
240240
Enforces allowing editing of only Properties defined as mutable
241241
"""
242+
242243
if name in type(self).properties.keys():
243244
if not type(self).properties[name].mutable:
244245
raise AttributeError(

0 commit comments

Comments
 (0)