Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions linode_api4/groups/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from linode_api4.objects import (
Database,
DatabaseEngine,
DatabasePrivateNetwork,
DatabaseType,
MySQLDatabase,
PostgreSQLDatabase,
Expand Down Expand Up @@ -126,6 +127,7 @@ def mysql_create(
engine,
ltype,
engine_config: Union[MySQLDatabaseConfigOptions, Dict[str, Any]] = None,
private_network: Union[DatabasePrivateNetwork, Dict[str, Any]] = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -159,6 +161,8 @@ def mysql_create(
:type ltype: str or Type
:param engine_config: The configuration options for this MySQL cluster
:type engine_config: Dict[str, Any] or MySQLDatabaseConfigOptions
:param private_network: The private network settings to use for this cluster
:type private_network: Dict[str, Any] or DatabasePrivateNetwork
"""

params = {
Expand All @@ -167,6 +171,7 @@ def mysql_create(
"engine": engine,
"type": ltype,
"engine_config": engine_config,
"private_network": private_network,
}
params.update(kwargs)

Expand Down Expand Up @@ -262,6 +267,7 @@ def postgresql_create(
engine_config: Union[
PostgreSQLDatabaseConfigOptions, Dict[str, Any]
] = None,
private_network: Union[DatabasePrivateNetwork, Dict[str, Any]] = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -295,6 +301,8 @@ def postgresql_create(
:type ltype: str or Type
:param engine_config: The configuration options for this PostgreSQL cluster
:type engine_config: Dict[str, Any] or PostgreSQLDatabaseConfigOptions
:param private_network: The private network settings to use for this cluster
:type private_network: Dict[str, Any] or DatabasePrivateNetwork
"""

params = {
Expand All @@ -303,6 +311,7 @@ def postgresql_create(
"engine": engine,
"type": ltype,
"engine_config": engine_config,
"private_network": private_network,
}
params.update(kwargs)

Expand Down
20 changes: 13 additions & 7 deletions linode_api4/groups/vpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.objects import VPC, Region, VPCIPAddress
from linode_api4.objects import VPC, Region, VPCIPAddress, VPCIPv6RangeOptions
from linode_api4.objects.base import _flatten_request_body_recursive
from linode_api4.paginated_list import PaginatedList
from linode_api4.util import drop_null_keys


class VPCGroup(Group):
Expand Down Expand Up @@ -33,6 +35,7 @@ def create(
region: Union[Region, str],
description: Optional[str] = None,
subnets: Optional[List[Dict[str, Any]]] = None,
ipv6: Optional[List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]] = None,
**kwargs,
) -> VPC:
"""
Expand All @@ -48,30 +51,33 @@ def create(
:type description: Optional[str]
:param subnets: A list of subnets to create under this VPC.
:type subnets: List[Dict[str, Any]]
:param ipv6: The IPv6 address ranges for this VPC.
:type ipv6: List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]

:returns: The new VPC object.
:rtype: VPC
"""
params = {
"label": label,
"region": region.id if isinstance(region, Region) else region,
"description": description,
"ipv6": ipv6,
"subnets": subnets,
}

if description is not None:
params["description"] = description

if subnets is not None and len(subnets) > 0:
for subnet in subnets:
if not isinstance(subnet, dict):
raise ValueError(
f"Unsupported type for subnet: {type(subnet)}"
)

params["subnets"] = subnets

params.update(kwargs)

result = self.client.post("/vpcs", data=params)
result = self.client.post(
"/vpcs",
data=drop_null_keys(_flatten_request_body_recursive(params)),
)

if not "id" in result:
raise UnexpectedResponseError(
Expand Down
21 changes: 21 additions & 0 deletions linode_api4/objects/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ def invalidate(self):
Base.invalidate(self)


@dataclass
class DatabasePrivateNetwork(JSONObject):
"""
DatabasePrivateNetwork is used to specify
a Database Cluster's private network settings during its creation.
"""

vpc_id: Optional[int] = None
subnet_id: Optional[int] = None
public_access: Optional[bool] = None


@deprecated(
reason="Backups are not supported for non-legacy database clusters."
)
Expand Down Expand Up @@ -304,6 +316,9 @@ class MySQLDatabase(Base):
"engine_config": Property(
mutable=True, json_object=MySQLDatabaseConfigOptions
),
"private_network": Property(
mutable=True, json_object=DatabasePrivateNetwork, nullable=True
),
}

@property
Expand Down Expand Up @@ -470,6 +485,9 @@ class PostgreSQLDatabase(Base):
"engine_config": Property(
mutable=True, json_object=PostgreSQLDatabaseConfigOptions
),
"private_network": Property(
mutable=True, json_object=DatabasePrivateNetwork, nullable=True
),
}

@property
Expand Down Expand Up @@ -636,6 +654,9 @@ class Database(Base):
"updated": Property(),
"updates": Property(),
"version": Property(),
"private_network": Property(
json_object=DatabasePrivateNetwork, nullable=True
),
}

@property
Expand Down
120 changes: 99 additions & 21 deletions linode_api4/objects/linode.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,83 @@ def _populate(self, json):

@dataclass
class ConfigInterfaceIPv4(JSONObject):
"""
ConfigInterfaceIPv4 represents the IPv4 configuration of a VPC interface.
"""

vpc: str = ""
nat_1_1: str = ""


@dataclass
class ConfigInterfaceIPv6SLAACOptions(JSONObject):
"""
ConfigInterfaceIPv6SLAACOptions is used to set a single IPv6 SLAAC configuration of a VPC interface.
"""

range: str = ""


@dataclass
class ConfigInterfaceIPv6RangeOptions(JSONObject):
"""
ConfigInterfaceIPv6RangeOptions is used to set a single IPv6 range configuration of a VPC interface.
"""

range: str = ""


@dataclass
class ConfigInterfaceIPv6Options(JSONObject):
"""
ConfigInterfaceIPv6Options is used to set the IPv6 configuration of a VPC interface.
"""

slaac: List[ConfigInterfaceIPv6SLAACOptions] = field(
default_factory=lambda: []
)
ranges: List[ConfigInterfaceIPv6RangeOptions] = field(
default_factory=lambda: []
)
is_public: bool = False


@dataclass
class ConfigInterfaceIPv6SLAAC(JSONObject):
"""
ConfigInterfaceIPv6SLAAC represents a single SLAAC address under a VPC interface's IPv6 configuration.
"""

put_class = ConfigInterfaceIPv6SLAACOptions

range: str = ""
address: str = ""


@dataclass
class ConfigInterfaceIPv6Range(JSONObject):
"""
ConfigInterfaceIPv6Range represents a single IPv6 address under a VPC interface's IPv6 configuration.
"""

put_class = ConfigInterfaceIPv6RangeOptions

range: str = ""


@dataclass
class ConfigInterfaceIPv6(JSONObject):
"""
ConfigInterfaceIPv6 represents the IPv6 configuration of a VPC interface.
"""

put_class = ConfigInterfaceIPv6Options

slaac: List[ConfigInterfaceIPv6SLAAC] = field(default_factory=lambda: [])
ranges: List[ConfigInterfaceIPv6Range] = field(default_factory=lambda: [])
is_public: bool = False


class NetworkInterface(DerivedBase):
"""
This class represents a Configuration Profile's network interface object.
Expand All @@ -329,6 +402,7 @@ class NetworkInterface(DerivedBase):
"vpc_id": Property(id_relationship=VPC),
"subnet_id": Property(),
"ipv4": Property(mutable=True, json_object=ConfigInterfaceIPv4),
"ipv6": Property(mutable=True, json_object=ConfigInterfaceIPv6),
"ip_ranges": Property(mutable=True),
}

Expand Down Expand Up @@ -400,7 +474,10 @@ class ConfigInterface(JSONObject):
# VPC-specific
vpc_id: Optional[int] = None
subnet_id: Optional[int] = None

ipv4: Optional[Union[ConfigInterfaceIPv4, Dict[str, Any]]] = None
ipv6: Optional[Union[ConfigInterfaceIPv6, Dict[str, Any]]] = None

ip_ranges: Optional[List[str]] = None

# Computed
Expand All @@ -409,7 +486,7 @@ class ConfigInterface(JSONObject):
def __repr__(self):
return f"Interface: {self.purpose}"

def _serialize(self, *args, **kwargs):
def _serialize(self, is_put: bool = False):
purpose_formats = {
"public": {"purpose": "public", "primary": self.primary},
"vlan": {
Expand All @@ -421,11 +498,8 @@ def _serialize(self, *args, **kwargs):
"purpose": "vpc",
"primary": self.primary,
"subnet_id": self.subnet_id,
"ipv4": (
self.ipv4.dict
if isinstance(self.ipv4, ConfigInterfaceIPv4)
else self.ipv4
),
"ipv4": self.ipv4,
"ipv6": self.ipv6,
"ip_ranges": self.ip_ranges,
},
}
Expand All @@ -435,11 +509,14 @@ def _serialize(self, *args, **kwargs):
f"Unknown interface purpose: {self.purpose}",
)

return {
k: v
for k, v in purpose_formats[self.purpose].items()
if v is not None
}
return _flatten_request_body_recursive(
{
k: v
for k, v in purpose_formats[self.purpose].items()
if v is not None
},
is_put=is_put,
)


class Config(DerivedBase):
Expand Down Expand Up @@ -580,6 +657,7 @@ def interface_create_vpc(
subnet: Union[int, VPCSubnet],
primary=False,
ipv4: Union[Dict[str, Any], ConfigInterfaceIPv4] = None,
ipv6: Union[Dict[str, Any], ConfigInterfaceIPv6Options] = None,
ip_ranges: Optional[List[str]] = None,
) -> NetworkInterface:
"""
Expand All @@ -593,6 +671,8 @@ def interface_create_vpc(
:type primary: bool
:param ipv4: The IPv4 configuration of the interface for the associated subnet.
:type ipv4: Dict or ConfigInterfaceIPv4
:param ipv6: The IPv6 configuration of the interface for the associated subnet.
:type ipv6: Dict or ConfigInterfaceIPv6Options
:param ip_ranges: A list of IPs or IP ranges in the VPC subnet.
Packets to these CIDRs are routed through the
VPC network interface.
Expand All @@ -603,19 +683,16 @@ def interface_create_vpc(
"""
params = {
"purpose": "vpc",
"subnet_id": subnet.id if isinstance(subnet, VPCSubnet) else subnet,
"subnet_id": subnet,
"primary": primary,
"ipv4": ipv4,
"ipv6": ipv6,
"ip_ranges": ip_ranges,
}

if ipv4 is not None:
params["ipv4"] = (
ipv4.dict if isinstance(ipv4, ConfigInterfaceIPv4) else ipv4
)

if ip_ranges is not None:
params["ip_ranges"] = ip_ranges

return self._interface_create(params)
return self._interface_create(
drop_null_keys(_flatten_request_body_recursive(params))
)

def interface_reorder(self, interfaces: List[Union[int, NetworkInterface]]):
"""
Expand Down Expand Up @@ -2018,6 +2095,7 @@ def linode_interfaces(self) -> Optional[list[LinodeInterface]]:

if self.interface_generation != InterfaceGeneration.LINODE:
return None

if not hasattr(self, "_interfaces"):
result = self._client.get(
"{}/interfaces".format(Instance.api_endpoint),
Expand Down
Loading
Loading