"""Cherry Servers IP address resource management module."""
from __future__ import annotations
from pydantic import Field
from cherryservers_sdk_python import _base, projects, regions
class AddressAttachedError(Exception):
"""Attempted operation forbidden for attached IP addresses."""
def __init__(self, msg: str) -> None:
"""Initialize error."""
super().__init__(msg)
[docs]
class AttachedServerModel(_base.ResourceModel):
"""Cherry Servers attached server model.
This model is frozen by default,
since it represents an actual Cherry Servers server
resource.
This is a minimal server model meant for other resource models
than contain a server. Avoids circular references.
Attributes:
id (int): Server ID. Non-existent server will have value `0`.
href (str | None): Server href.
hostname (str | None): Server hostname.
"""
id: int = Field(description="Server ID.")
href: str | None = Field(description="Server href.", default=None)
hostname: str | None = Field(
description="Server hostname",
default=None,
)
[docs]
class IPModel(_base.ResourceModel):
"""Cherry Servers IP address model.
This model is frozen by default,
since it represents an actual Cherry Servers IP address resource state.
Attributes:
id (int): IP address ID.
address (str | None): IP address.
address_family (str | None): IP address family, such as 4 or 6.
cidr (str | None): IP address CIDR.
gateway (str | None): IP address gateway address, if applicable.
type (str | None): IP address type, such as `floating-ip` or `primary-ip`.
region (cherryservers_sdk_python.regions.RegionModel | None): IP address region.
routed_to (cherryservers_sdk_python.ips.IPModel | None):
IP address that this address is routed, if applicable.
targeted_to (cherryservers_sdk_python.ips.AttachedServerModel | None):
Server that this address is targeted to, if applicable.
project (cherryservers_sdk_python.projects.ProjectModel | None):
The project that the IP address belongs to.
ptr_record (str | None): IP address PTR record, if applicable.
a_record (str | None): IP address A record, if applicable.
tags (dict[str, str] | None): IP address user-defined tags.
href (str | None): IP address href.
"""
id: str = Field(description="IP address ID.")
address: str | None = Field(description="IP address.", default=None)
address_family: int | None = Field(
description="IP address family, such as 4 or 6.", default=None
)
cidr: str | None = Field(description="IP address CIDR.", default=None)
gateway: str | None = Field(
description="IP address gateway address, if applicable.", default=None
)
type: str | None = Field(
description="IP address type, such as floating-ip or primary-ip.", default=None
)
region: regions.RegionModel | None = Field(
description="IP address region.", default=None
)
routed_to: IPModel | None = Field(
description="IP address that this address is routed to, if applicable.",
default=None,
)
targeted_to: AttachedServerModel | None = Field(
description="Server that this address is targeted to, if applicable.",
default=None,
)
project: projects.ProjectModel | None = Field(
description=" Project that the IP address belongs to.", default=None
)
ptr_record: str | None = Field(
description="IP address PTR record, if applicable.", default=None
)
a_record: str | None = Field(
description="IP address A record, if applicable.", default=None
)
tags: dict[str, str] | None = Field(
description="IP address user-defined tags.", default=None
)
href: str | None = Field(description="IP address href.", default=None)
[docs]
class CreationRequest(_base.RequestSchema):
"""Cherry Servers IP address creation request schema.
Attributes:
region (str): IP address region slug. Required.
routed_to (str | None):
ID of the IP address that the created address will be routed to.
Mutually exclusive with `targeted_to`.
targeted_to (int | None):
ID of the server that the created address will be targeted to.
Mutually exclusive with `routed_to`.
ptr_record (str | None): IP address PTR record.
a_record (str | None): IP address A record.
tags (dict[str, str] | None): User-defined IP address tags.
"""
region: str = Field(description="IP address region slug. Required.")
routed_to: str | None = Field(
description="ID of the IP address that the created address will be routed to."
" Mutually exclusive with `targeted_to`. Optional.",
default=None,
)
targeted_to: int | None = Field(
description="ID of the server that the created address will be targeted to."
" Mutually exclusive with `routed_to`.",
default=None,
)
ptr_record: str | None = Field(description="IP address PTR record.", default=None)
a_record: str | None = Field(description="IP address A record.", default=None)
tags: dict[str, str] | None = Field(
description="User-defined IP address tags.", default=None
)
[docs]
class UpdateRequest(_base.RequestSchema):
"""Cherry Servers IP address update request schema.
Attributes:
ptr_record (str | None): IP address PTR record.
a_record (str | None): IP address A record.
routed_to (str | None):
ID of the IP address that this address will be routed to.
Mutually exclusive with `targeted_to`.
targeted_to (int | None):
ID of the server that this address will be targeted to.
Mutually exclusive with `routed_to`.
Set to 0 to unassign IP address from server.
tags (dict[str, str] | None): User-defined IP address tags.
"""
ptr_record: str | None = Field(description="IP address PTR record.", default=None)
a_record: str | None = Field(description="IP address A record.", default=None)
routed_to: str | None = Field(
description="ID of the IP address that this address will be routed to."
" Mutually exclusive with `targeted_to`.",
default=None,
)
targeted_to: int | None = Field(
description="ID of the server that the address will be targeted to."
" Mutually exclusive with `routed_to`."
" Set to 0 to unassign IP address from server.",
default=None,
)
tags: dict[str, str] | None = Field(
description="User-defined IP address tags.", default=None
)
[docs]
class IPClient(_base.ResourceClient):
"""Cherry Servers IP address client.
Manage Cherry Servers IP address resources.
This class should typically be initialized by
:class:`cherryservers_sdk_python.facade.CherryApiFacade`.
Example:
.. code-block:: python
facade = cherryservers_sdk_python.facade.CherryApiFacade(token="my-token")
# Get IP address by id.
ip = facade.ips.get_by_id("c8b0cb54-cbd6-a90f-d291-769b6db0f1b9")
# List all project IPs.
ips = facade.ips.get_by_project(123456)
# Create an IP address.
creation_req = cherryservers_sdk_python.ips.CreationRequest(
region="LT-Siauliai",
ptr_record="test",
a_record="test",
targeted_to=606764,
tags={"env": "test"},
)
fip = facade.ips.create(creation_req, project_id=123456)
# Update IP address.
update_req = cherryservers_sdk_python.ips.UpdateRequest(
ptr_record="",
a_record="",
)
fip.update(update_req)
# Delete IP address.
fip.delete()
"""
[docs]
def get_by_id(self, ip_id: str) -> IP:
"""Retrieve a IP address by ID."""
response = self._api_client.get(
f"ips/{ip_id}",
{"fields": "ip,project,routed_to,region,href,bgp,id,hostname"},
self.request_timeout,
)
ip_model = IPModel.model_validate(response.json())
return IP(self, ip_model)
[docs]
def list_by_project(self, project_id: int) -> list[IP]:
"""Retrieve all IPs that belong to a specified project."""
response = self._api_client.get(
f"projects/{project_id}/ips",
{"fields": "ip,project,routed_to,region,href,bgp,id,hostname"},
self.request_timeout,
)
ips: list[IP] = []
for value in response.json():
ip_model = IPModel.model_validate(value)
ips.append(IP(self, ip_model))
return ips
[docs]
def create(self, creation_schema: CreationRequest, project_id: int) -> IP:
"""Create a new IP address."""
response = self._api_client.post(
f"projects/{project_id}/ips", creation_schema, None, self.request_timeout
)
return self.get_by_id(response.json()["id"])
[docs]
def delete(self, ip_id: str) -> None:
"""Delete IP address by ID."""
self._api_client.delete(f"ips/{ip_id}", None, self.request_timeout)
[docs]
def update(self, ip_id: str, update_schema: UpdateRequest) -> IP:
"""Update IP address by ID."""
response = self._api_client.put(
f"ips/{ip_id}", update_schema, None, self.request_timeout
)
return self.get_by_id(response.json()["id"])
[docs]
class IP(_base.Resource[IPClient, IPModel]):
"""Cherry Servers IP address resource.
This class represents an existing Cherry Servers resource
and should only be initialized by :class:`IPClient`.
"""
[docs]
def delete(self) -> None:
"""Delete Cherry Servers IP address resource."""
if self._model.routed_to:
msg = "Attached IP address cannot be deleted."
raise AddressAttachedError(msg)
self._client.delete(self._model.id)
[docs]
def update(self, update_schema: UpdateRequest) -> None:
"""Update Cherry Servers IP address resource."""
updated = self._client.update(self._model.id, update_schema)
self._model = updated.get_model()
[docs]
def get_id(self) -> str:
"""Get resource ID."""
return self._model.id