Skip to content

Commit 07f8b34

Browse files
Merge branch 'dev' into zhiwei/TPT-4656/nodebalancer-type-backend-connectivity
2 parents b4c3121 + c9daf79 commit 07f8b34

25 files changed

Lines changed: 642 additions & 82 deletions

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,5 @@ jobs:
6767
python-version: ${{ matrix.python-version }}
6868
- name: Run tests
6969
run: |
70-
pip install ".[test]"
71-
tox
70+
pip install ".[unit-test]"
71+
make test-unit

‎AGENTS.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
## Source of Truth
44

55
- This is the official synchronous Python SDK for the Linode API. Supported
6-
Python versions come from [pyproject.toml](pyproject.toml) (`>=3.10`); do not
7-
infer support from the older environment names still present in [tox.ini](tox.ini).
6+
Python versions come from [pyproject.toml](pyproject.toml) (`>=3.10`).
87
- Make changes only in canonical source trees such as `linode_api4/`, `test/`,
98
`docs/`, `examples/`, and `.github/`. Do not edit generated copies or output in
109
`build/`, `dist/`, `docs/build/`, `docs/_build/`, or `linode_api4.egg-info/`.

‎README.rst‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,13 @@ Tests
102102
-----
103103

104104
Tests live in the ``test`` directory. When invoking tests, make sure you are
105-
in the root directory of this project. To run the full suite across all
106-
supported python versions, use tox_:
105+
in the root directory of this project. Install unit-test dependencies and run
106+
the mocked unit suite with:
107107

108108
.. code-block:: shell
109109
110-
tox
111-
112-
Running tox also runs pylint and coverage reports.
110+
python3 -m pip install -e ".[unit-test]"
111+
make test-unit
113112
114113
The test suite uses fixtures stored as JSON in ``test/fixtures``. These files
115114
contain sanitized JSON responses from the API - the file name is the URL called
@@ -131,8 +130,6 @@ from the api base url that should be returned, for example::
131130
linode = self.client.linode.instance_create('g6-standard-2', 'us-east')
132131
self.assertEqual(linode.id, 123) # passes
133132

134-
.. _tox: http://tox.readthedocs.io
135-
136133

137134
Integration Tests
138135
-----------------

‎linode_api4/groups/vpc.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
VPCIPv6RangeOptions,
1212
)
1313
from linode_api4.objects.base import _flatten_request_body_recursive
14+
from linode_api4.objects.vpc import VPCType
1415
from linode_api4.paginated_list import PaginatedList
1516
from linode_api4.util import drop_null_keys
1617

@@ -44,6 +45,7 @@ def create(
4445
subnets: Optional[List[Dict[str, Any]]] = None,
4546
ipv6: Optional[List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]] = None,
4647
ipv4: Optional[List[Union[VPCIPv4RangeOptions, Dict[str, Any]]]] = None,
48+
vpc_type: Optional[Union[VPCType, str]] = None,
4749
**kwargs,
4850
) -> VPC:
4951
"""
@@ -61,6 +63,11 @@ def create(
6163
:type subnets: List[Dict[str, Any]]
6264
:param ipv6: The IPv6 address ranges for this VPC.
6365
:type ipv6: List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]
66+
:param vpc_type: The type of VPC to create. Defaults to ``regular`` on
67+
the API side. Set to ``rdma`` to create a GPUDirect
68+
RDMA VPC (requires the ``GPUDirect RDMA`` account
69+
capability).
70+
:type vpc_type: Optional[Union[VPCType, str]]
6471
:param ipv4: The IPv4 address ranges for this VPC. Note that IPv4 VPCs may not currently be available to all users.
6572
:type ipv4: List[Union[VPCIPv4RangeOptions, Dict[str, Any]]]
6673
@@ -74,6 +81,7 @@ def create(
7481
"ipv4": ipv4,
7582
"ipv6": ipv6,
7683
"subnets": subnets,
84+
"vpc_type": vpc_type,
7785
}
7886

7987
if subnets is not None and len(subnets) > 0:

‎linode_api4/login_client.py‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ class OAuthScopes:
3030
Lists of OAuth Scopes are accepted when calling the :any:`generate_login_url`
3131
method of the :any:`LinodeLoginClient`.
3232
33-
All contained enumerations of OAuth Scopes have two levels, "read_only" and
33+
Most contained enumerations of OAuth Scopes have two levels, "read_only" and
3434
"read_write". "read_only" access grants you the ability to get resources and
3535
of that type, but not to change, create, or delete them. "read_write" access
36-
allows to full access to resources of the requested type. In the above
36+
allows to full access to resources of the requested type. :any:`OAuthScopes.VPC`
37+
is the exception and exposes only "read_write" and "all". In the above
3738
example, you are requesting access to view, modify, create, and delete
3839
Linodes, and to view Domains.
3940
"""
@@ -268,6 +269,33 @@ def __repr__(self):
268269
return "images:*"
269270
return "images:{}".format(self.name)
270271

272+
class Databases(Enum):
273+
"""
274+
Access to Managed Databases
275+
"""
276+
277+
read_only = 0
278+
read_write = 1
279+
all = 2
280+
281+
def __repr__(self):
282+
if self.name == "all":
283+
return "databases:*"
284+
return "databases:{}".format(self.name)
285+
286+
class VPC(Enum):
287+
"""
288+
Access to VPCs and subnets
289+
"""
290+
291+
read_write = 1
292+
all = 2
293+
294+
def __repr__(self):
295+
if self.name == "all":
296+
return "vpc:*"
297+
return "vpc:{}".format(self.name)
298+
271299
_scope_families = {
272300
"linodes": Linodes,
273301
"domains": Domains,
@@ -286,6 +314,8 @@ def __repr__(self):
286314
"nodebalancers": NodeBalancers,
287315
"longview": Longview,
288316
"images": Images,
317+
"databases": Databases,
318+
"vpc": VPC,
289319
}
290320

291321
@staticmethod

‎linode_api4/objects/linode.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,10 +2115,22 @@ def interface_create(
21152115
:param vpc: The VPC-specific configuration of the new interface.
21162116
If set, the new instance will be a VPC interface.
21172117
2118+
.. note::
2119+
RDMA VPC interfaces (``rdma_vpc``) cannot be added via this
2120+
endpoint. They may only be specified at instance creation time via
2121+
:func:`linode_api4.LinodeGroup.instance_create`.
2122+
21182123
:returns: The newly created Linode Interface.
21192124
:rtype: LinodeInterface
21202125
"""
21212126

2127+
if kwargs.get("rdma_vpc") is not None:
2128+
raise ValueError(
2129+
"RDMA VPC interfaces (rdma_vpc) cannot be added via "
2130+
"interface_create(). They may only be specified at instance "
2131+
"creation time via LinodeGroup.instance_create()."
2132+
)
2133+
21222134
params = {
21232135
"firewall_id": firewall,
21242136
"default_route": default_route,

‎linode_api4/objects/linode_interfaces.py‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,47 @@ class LinodeInterfaceVLANOptions(JSONObject):
187187
ipam_address: Optional[str] = None
188188

189189

190+
@dataclass
191+
class LinodeInterfaceRDMAVPCIPv4AddressOptions(JSONObject):
192+
"""
193+
Options accepted for a single address when creating or updating the IPv4
194+
configuration of an RDMA VPC Linode Interface.
195+
196+
Only one address is supported per RDMA VPC interface, and it must be
197+
marked as primary.
198+
"""
199+
200+
address: Optional[str] = None
201+
primary: Optional[bool] = None
202+
203+
204+
@dataclass
205+
class LinodeInterfaceRDMAVPCIPv4Options(JSONObject):
206+
"""
207+
Options accepted when creating or updating the IPv4 configuration of an
208+
RDMA VPC Linode Interface.
209+
210+
The ``addresses`` list MUST contain exactly one element. If omitted, the
211+
API defaults to a single primary ``auto`` address.
212+
"""
213+
214+
addresses: Optional[List[LinodeInterfaceRDMAVPCIPv4AddressOptions]] = None
215+
216+
217+
@dataclass
218+
class LinodeInterfaceRDMAVPCOptions(JSONObject):
219+
"""
220+
RDMA-VPC-exclusive options accepted when creating or updating a Linode
221+
Interface.
222+
223+
Used for GPUDirect RDMA interfaces. Default routes and NAT 1:1 addresses
224+
are not supported on RDMA VPC interfaces.
225+
"""
226+
227+
subnet_id: int = 0
228+
ipv4: Optional[LinodeInterfaceRDMAVPCIPv4Options] = None
229+
230+
190231
@dataclass
191232
class LinodeInterfaceOptions(JSONObject):
192233
"""
@@ -204,6 +245,7 @@ class LinodeInterfaceOptions(JSONObject):
204245
vpc: Optional[LinodeInterfaceVPCOptions] = None
205246
public: Optional[LinodeInterfacePublicOptions] = None
206247
vlan: Optional[LinodeInterfaceVLANOptions] = None
248+
rdma_vpc: Optional[LinodeInterfaceRDMAVPCOptions] = None
207249

208250

209251
# Interface GET Response
@@ -409,6 +451,45 @@ class LinodeInterfaceVLAN(JSONObject):
409451
ipam_address: Optional[str] = None
410452

411453

454+
@dataclass
455+
class LinodeInterfaceRDMAVPCIPv4Address(JSONObject):
456+
"""
457+
A single address under the IPv4 configuration of an RDMA VPC Linode Interface.
458+
"""
459+
460+
put_class = LinodeInterfaceRDMAVPCIPv4AddressOptions
461+
462+
address: str = ""
463+
primary: bool = False
464+
465+
466+
@dataclass
467+
class LinodeInterfaceRDMAVPCIPv4(JSONObject):
468+
"""
469+
The IPv4 configuration of an RDMA VPC Linode Interface.
470+
"""
471+
472+
put_class = LinodeInterfaceRDMAVPCIPv4Options
473+
474+
addresses: List[LinodeInterfaceRDMAVPCIPv4Address] = field(
475+
default_factory=list
476+
)
477+
478+
479+
@dataclass
480+
class LinodeInterfaceRDMAVPC(JSONObject):
481+
"""
482+
RDMA VPC-specific configuration field for a Linode Interface.
483+
"""
484+
485+
put_class = LinodeInterfaceRDMAVPCOptions
486+
487+
vpc_id: int = 0
488+
subnet_id: int = 0
489+
490+
ipv4: Optional[LinodeInterfaceRDMAVPCIPv4] = None
491+
492+
412493
class LinodeInterface(DerivedBase):
413494
"""
414495
A Linode's network interface.
@@ -449,6 +530,7 @@ class LinodeInterface(DerivedBase):
449530
"public": Property(mutable=True, json_object=LinodeInterfacePublic),
450531
"vlan": Property(mutable=True, json_object=LinodeInterfaceVLAN),
451532
"vpc": Property(mutable=True, json_object=LinodeInterfaceVPC),
533+
"rdma_vpc": Property(mutable=True, json_object=LinodeInterfaceRDMAVPC),
452534
}
453535

454536
def firewalls(self, *filters) -> List[Firewall]:

‎linode_api4/objects/region.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class Capability(StrEnum):
6767
ruleset = "Cloud Firewall Rule Set"
6868
prefixlists = "Cloud Firewall Prefix Lists"
6969
current_prefixlists = "Cloud Firewall Prefix List Current References"
70+
gpudirect_rdma = "GPUDirect RDMA"
7071

7172

7273
@dataclass

‎linode_api4/objects/vpc.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,20 @@
55
from linode_api4.objects import Base, DerivedBase, Property, Region
66
from linode_api4.objects.base import _flatten_request_body_recursive
77
from linode_api4.objects.networking import VPCIPAddress
8-
from linode_api4.objects.serializable import JSONObject
8+
from linode_api4.objects.serializable import JSONObject, StrEnum
99
from linode_api4.paginated_list import PaginatedList
1010
from linode_api4.util import drop_null_keys
1111

1212

13+
class VPCType(StrEnum):
14+
"""
15+
VPCType represents the supported VPC types.
16+
"""
17+
18+
regular = "regular"
19+
rdma = "rdma"
20+
21+
1322
@dataclass
1423
class VPCIPv4DefaultRange(JSONObject):
1524
"""
@@ -119,6 +128,7 @@ class VPCSubnet(DerivedBase):
119128
"ipv6": Property(json_object=VPCSubnetIPv6Range, unordered=True),
120129
"linodes": Property(json_object=VPCSubnetLinode, unordered=True),
121130
"databases": Property(json_object=VPCSubnetDatabase, unordered=True),
131+
"vpc_type": Property(),
122132
"created": Property(is_datetime=True),
123133
"updated": Property(is_datetime=True),
124134
}
@@ -144,6 +154,7 @@ class VPC(Base):
144154
),
145155
"ipv6": Property(json_object=VPCIPv6Range, unordered=True),
146156
"subnets": Property(derived_class=VPCSubnet),
157+
"vpc_type": Property(),
147158
"created": Property(is_datetime=True),
148159
"updated": Property(is_datetime=True),
149160
}

‎pyproject.toml‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,37 @@ dependencies = ["requests", "polling", "deprecated"]
3535
dynamic = ["version"]
3636

3737
[project.optional-dependencies]
38-
test = ["tox>=4.4.0"]
39-
40-
dev = [
41-
"tox>=4.4.0",
42-
"mock>=5.0.0",
38+
test-shared = [
4339
"pytest>=7.3.1",
4440
"httpretty>=1.1.4",
41+
"pytest-rerunfailures",
42+
]
43+
unit-test = [
44+
"linode_api4[test-shared]",
45+
"mock>=5.0.0",
46+
]
47+
int-test = [
48+
"linode_api4[test-shared]",
49+
]
50+
lint = [
4551
"black>=23.1.0",
4652
"isort>=5.12.0",
4753
"autoflake>=2.0.1",
4854
"pylint",
4955
"twine>=4.0.2",
5056
"build>=0.10.0",
51-
"Sphinx>=6.0.0",
52-
"sphinx-autobuild>=2021.3.14",
53-
"sphinxcontrib-fulltoc>=1.2.0",
54-
"build>=0.10.0",
55-
"twine>=4.0.2",
56-
"pytest-rerunfailures",
5757
]
58-
5958
doc = [
6059
"Sphinx>=6.0.0",
6160
"sphinx-autobuild>=2021.3.14",
6261
"sphinxcontrib-fulltoc>=1.2.0",
6362
]
63+
dev = [
64+
"linode_api4[unit-test]",
65+
"linode_api4[int-test]",
66+
"linode_api4[lint]",
67+
"linode_api4[doc]",
68+
]
6469

6570
[project.urls]
6671
Homepage = "https://github.com/linode/linode_api4-python"

0 commit comments

Comments
 (0)