Skip to content

Commit fccff05

Browse files
committed
Merge branch 'proj/rdma-interfaces' into TPT-4462/support-rdma
2 parents 8151e25 + 3ccfd56 commit fccff05

34 files changed

Lines changed: 1977 additions & 225 deletions

conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import os
2+
import sys
3+
4+
# Ensure the repo root is on sys.path so that `from test.unit.base import ...`
5+
# works regardless of which directory pytest is invoked from.
6+
sys.path.insert(0, os.path.dirname(__file__))

linode_api4/groups/linode.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ def instance_create(
166166
kernel: Optional[str] = None,
167167
boot_size: Optional[int] = None,
168168
authorized_users: Optional[List[str]] = None,
169+
ipv4: Optional[List[str]] = None,
169170
**kwargs,
170171
):
171172
"""
@@ -355,6 +356,9 @@ def instance_create(
355356
:param boot_size: The size of the boot disk in MB. If provided, this will be used to create
356357
the boot disk for the Instance.
357358
:type boot_size: int
359+
:param ipv4: A list of reserved IPv4 addresses to assign to this Instance.
360+
NOTE: Reserved IP feature may not currently be available to all users.
361+
:type ipv4: list[str]
358362
359363
:returns: A new Instance object
360364
:rtype: Instance
@@ -401,6 +405,7 @@ def instance_create(
401405
"network_helper": network_helper,
402406
"kernel": kernel,
403407
"boot_size": boot_size,
408+
"ipv4": ipv4,
404409
}
405410

406411
params.update(kwargs)

linode_api4/groups/monitor.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ def dashboards(
4747
dashboard = client.load(MonitorDashboard, 1)
4848
dashboards_by_service = client.monitor.dashboards(service_type="dbaas")
4949
50-
.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.
51-
5250
API Documentation:
5351
- All Dashboards: https://techdocs.akamai.com/linode-api/reference/get-dashboards-all
5452
- Dashboards by Service: https://techdocs.akamai.com/linode-api/reference/get-dashboards
@@ -83,8 +81,6 @@ def services(
8381
supported_services = client.monitor.services()
8482
service_details = client.monitor.load(MonitorService, "dbaas")
8583
86-
.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.
87-
8884
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services
8985
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services-for-service-type
9086
@@ -110,7 +106,6 @@ def metric_definitions(
110106
Returns metrics for a specific service type.
111107
112108
metrics = client.monitor.list_metric_definitions(service_type="dbaas")
113-
.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.
114109
115110
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-information
116111
@@ -136,8 +131,6 @@ def create_token(
136131
Returns a JWE Token for a specific service type.
137132
token = client.monitor.create_token(service_type="dbaas", entity_ids=[1234])
138133
139-
.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.
140-
141134
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-get-token
142135
143136
:param service_type: The service type to create token for.
@@ -175,7 +168,6 @@ def alert_definitions(
175168
176169
alerts = client.monitor.alert_definitions()
177170
alerts_by_service = client.monitor.alert_definitions(service_type="dbaas")
178-
.. note:: This endpoint is in beta and requires using the v4beta base URL.
179171
180172
API Documentation:
181173
https://techdocs.akamai.com/linode-api/reference/get-alert-definitions
@@ -212,8 +204,6 @@ def alert_channels(self, *filters) -> PaginatedList:
212204
Examples:
213205
channels = client.monitor.alert_channels()
214206
215-
.. note:: This endpoint is in beta and requires using the v4beta base URL.
216-
217207
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-notification-channels
218208
219209
:param filters: Optional filter expressions to apply to the collection.
@@ -242,8 +232,6 @@ def create_alert_definition(
242232
The alert definition configures when alerts are fired and which channels
243233
are notified.
244234
245-
.. note:: This endpoint is in beta and requires using the v4beta base URL.
246-
247235
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-alert-definition-for-service-type
248236
249237
:param service_type: Service type for which to create the alert definition
@@ -319,9 +307,7 @@ def alert_definition_entities(
319307
320308
This endpoint supports pagination fields (`page`, `page_size`) in the API.
321309
322-
.. note:: This endpoint is in beta and requires using the v4beta base URL.
323-
324-
API Documentation: TODO
310+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-alert-definition-entities
325311
326312
:param service_type: Service type for the alert definition (e.g. `dbaas`).
327313
:type service_type: str

linode_api4/groups/networking.py

Lines changed: 124 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, Optional, Union
1+
from typing import Any, Dict, List, Optional, Union
22

33
from linode_api4.errors import UnexpectedResponseError
44
from linode_api4.groups import Group
@@ -17,6 +17,8 @@
1717
Region,
1818
)
1919
from linode_api4.objects.base import _flatten_request_body_recursive
20+
from linode_api4.objects.networking import ReservedIPAddress, ReservedIPType
21+
from linode_api4.paginated_list import PaginatedList
2022
from linode_api4.util import drop_null_keys
2123

2224

@@ -328,29 +330,66 @@ def ips_assign(self, region, *assignments):
328330
},
329331
)
330332

331-
def ip_allocate(self, linode, public=True):
333+
def ip_allocate(
334+
self,
335+
linode: Optional[Union[Instance, int]] = None,
336+
public: bool = True,
337+
reserved: bool = False,
338+
region: Optional[Union[Region, str]] = None,
339+
) -> IPAddress:
332340
"""
333-
Allocates an IP to a Instance you own. Additional IPs must be requested
334-
by opening a support ticket first.
341+
Allocates an IP to an Instance you own, or reserves a new IP address.
342+
343+
When ``reserved`` is False (default), ``linode`` is required and an
344+
ephemeral IP is allocated and assigned to that Instance.
345+
346+
When ``reserved`` is True, either ``region`` or ``linode`` must be
347+
provided. Passing only ``region`` creates an unassigned reserved IP.
348+
Passing ``linode`` (with or without ``region``) creates a reserved IP
349+
in the Instance's region and assigns it to that Instance.
335350
336351
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-allocate-ip
337352
338353
:param linode: The Instance to allocate the new IP for.
339354
:type linode: Instance or int
340355
:param public: If True, allocate a public IP address. Defaults to True.
341356
:type public: bool
357+
:param reserved: If True, reserve the new IP address.
358+
NOTE: Reserved IP feature may not currently be available to all users.
359+
:type reserved: bool
360+
:param region: The region for the reserved IP (required when reserved=True and linode is not set).
361+
NOTE: Reserved IP feature may not currently be available to all users.
362+
:type region: str or Region
342363
343364
:returns: The new IPAddress.
344365
:rtype: IPAddress
345366
"""
346-
result = self.client.post(
347-
"/networking/ips/",
348-
data={
349-
"linode_id": linode.id if isinstance(linode, Base) else linode,
350-
"type": "ipv4",
351-
"public": public,
352-
},
353-
)
367+
if not reserved and linode is None:
368+
raise ValueError("linode is required when reserved is False.")
369+
if reserved and linode is None and region is None:
370+
raise ValueError(
371+
"Either linode or region must be provided when reserved is True."
372+
)
373+
if not reserved and region is not None:
374+
raise ValueError("region is only valid when reserved is True.")
375+
376+
data = {
377+
"type": "ipv4",
378+
"public": public,
379+
}
380+
381+
if linode is not None:
382+
data["linode_id"] = (
383+
linode.id if isinstance(linode, Base) else linode
384+
)
385+
386+
if reserved:
387+
data["reserved"] = True
388+
389+
if region is not None:
390+
data["region"] = region.id if isinstance(region, Base) else region
391+
392+
result = self.client.post("/networking/ips/", data=data)
354393

355394
if not "address" in result:
356395
raise UnexpectedResponseError(
@@ -510,3 +549,76 @@ def delete_vlan(self, vlan, region):
510549
return False
511550

512551
return True
552+
553+
def reserved_ips(self, *filters) -> PaginatedList:
554+
"""
555+
Returns a list of reserved IPv4 addresses on your account.
556+
557+
NOTE: Reserved IP feature may not currently be available to all users.
558+
559+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-reserved-ips
560+
561+
:param filters: Any number of filters to apply to this query.
562+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
563+
for more details on filtering.
564+
565+
:returns: A list of reserved IP addresses on the account.
566+
:rtype: PaginatedList of ReservedIPAddress
567+
"""
568+
return self.client._get_and_filter(ReservedIPAddress, *filters)
569+
570+
def reserved_ip_create(
571+
self,
572+
region: Union[Region, str],
573+
tags: Optional[List[str]] = None,
574+
**kwargs,
575+
) -> ReservedIPAddress:
576+
"""
577+
Reserves a new IPv4 address in the given region.
578+
579+
NOTE: Reserved IP feature may not currently be available to all users.
580+
581+
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-reserved-ip
582+
583+
:param region: The region in which to reserve the IP.
584+
:type region: str or Region
585+
:param tags: Tags to apply to the reserved IP.
586+
:type tags: list of str
587+
588+
:returns: The new reserved IP address.
589+
:rtype: ReservedIPAddress
590+
"""
591+
params = {
592+
"region": region.id if isinstance(region, Region) else region,
593+
}
594+
if tags is not None:
595+
params["tags"] = tags
596+
params.update(kwargs)
597+
598+
result = self.client.post("/networking/reserved/ips", data=params)
599+
600+
if "address" not in result:
601+
raise UnexpectedResponseError(
602+
"Unexpected response when reserving IP address!", json=result
603+
)
604+
605+
return ReservedIPAddress(self.client, result["address"], result)
606+
607+
def reserved_ip_types(self, *filters) -> PaginatedList:
608+
"""
609+
Returns a list of reserved IP types with pricing information.
610+
611+
NOTE: Reserved IP feature may not currently be available to all users.
612+
613+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-reserved-ip-types
614+
615+
:param filters: Any number of filters to apply to this query.
616+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
617+
for more details on filtering.
618+
619+
:returns: A list of reserved IP types.
620+
:rtype: PaginatedList of ReservedIPType
621+
"""
622+
return self.client._get_and_filter(
623+
ReservedIPType, *filters, endpoint="/networking/reserved/ips/types"
624+
)

linode_api4/groups/nodebalancer.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,26 @@ def create(self, region, **kwargs):
3232
3333
:param region: The Region in which to create the NodeBalancer.
3434
:type region: Region or str
35+
:param ipv4: A reserved IPv4 address to assign to this NodeBalancer.
36+
NOTE: Reserved IP feature may not currently be available to all users.
37+
:type ipv4: str
3538
3639
:returns: The new NodeBalancer
3740
:rtype: NodeBalancer
3841
"""
42+
ipv4 = kwargs.pop("ipv4", None)
3943
params = {
4044
"region": region.id if isinstance(region, Base) else region,
4145
}
46+
if ipv4 is not None:
47+
params["ipv4"] = ipv4
4248
params.update(kwargs)
4349

4450
result = self.client.post("/nodebalancers", data=params)
4551

4652
if not "id" in result:
4753
raise UnexpectedResponseError(
48-
"Unexpected response when creating Nodebalaner!", json=result
54+
"Unexpected response when creating NodeBalancer!", json=result
4955
)
5056

5157
n = NodeBalancer(self.client, result["id"], result)

linode_api4/groups/object_storage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ def global_quotas(self, *filters):
539539
"""
540540
Lists the active account-level Object Storage quotas applied to your account.
541541
542-
API Documentation: TBD
542+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-object-storage-global-quotas
543543
544544
:param filters: Any number of filters to apply to this query.
545545
See :doc:`Filtering Collections</linode_api4/objects/filtering>`

linode_api4/groups/tag.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def create(
3232
domains=None,
3333
nodebalancers=None,
3434
volumes=None,
35-
entities=[],
35+
entities=None,
36+
reserved_ipv4_addresses=None,
3637
):
3738
"""
3839
Creates a new Tag and optionally applies it to the given entities.
@@ -61,10 +62,14 @@ def create(
6162
:param volumes: A list of Volumes to apply this Tag to upon
6263
creation
6364
:type volumes: list of Volumes or list of int
65+
:param reserved_ipv4_addresses: A list of reserved IPv4 addresses to apply
66+
this Tag to upon creation.
67+
:type reserved_ipv4_addresses: list of str
6468
6569
:returns: The new Tag
6670
:rtype: Tag
6771
"""
72+
entities = entities or []
6873
linode_ids, nodebalancer_ids, domain_ids, volume_ids = [], [], [], []
6974

7075
# filter input into lists of ids
@@ -103,6 +108,7 @@ def create(
103108
"nodebalancers": nodebalancer_ids or None,
104109
"domains": domain_ids or None,
105110
"volumes": volume_ids or None,
111+
"reserved_ipv4_addresses": reserved_ipv4_addresses or None,
106112
}
107113

108114
result = self.client.post("/tags", data=params)

linode_api4/objects/linode.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,7 @@ def snapshot(self, label=None):
15841584
b = Backup(self._client, result["id"], self.id, result)
15851585
return b
15861586

1587-
def ip_allocate(self, public=False):
1587+
def ip_allocate(self, public=False, address=None):
15881588
"""
15891589
Allocates a new :any:`IPAddress` for this Instance. Additional public
15901590
IPs require justification, and you may need to open a :any:`SupportTicket`
@@ -1596,17 +1596,26 @@ def ip_allocate(self, public=False):
15961596
:param public: If the new IP should be public or private. Defaults to
15971597
private.
15981598
:type public: bool
1599+
:param address: A reserved IPv4 address to assign to this Instance instead
1600+
of allocating a new ephemeral IP. The address must be an
1601+
unassigned reserved IP owned by this account.
1602+
NOTE: Reserved IP feature may not currently be available to all users.
1603+
:type address: str
15991604
16001605
:returns: The new IPAddress
16011606
:rtype: IPAddress
16021607
"""
1608+
data = {
1609+
"type": "ipv4",
1610+
"public": public,
1611+
}
1612+
if address is not None:
1613+
data["address"] = address
1614+
16031615
result = self._client.post(
16041616
"{}/ips".format(Instance.api_endpoint),
16051617
model=self,
1606-
data={
1607-
"type": "ipv4",
1608-
"public": public,
1609-
},
1618+
data=data,
16101619
)
16111620

16121621
if not "address" in result:

linode_api4/objects/monitor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ class MonitorDashboard(Base):
278278
"label": Property(),
279279
"service_type": Property(ServiceType),
280280
"type": Property(DashboardType),
281+
"group_by": Property(),
281282
"widgets": Property(json_object=DashboardWidget),
282283
"updated": Property(is_datetime=True),
283284
}

0 commit comments

Comments
 (0)