diff --git a/plugins/module_utils/vendor/hcloud/_exceptions.py b/plugins/module_utils/vendor/hcloud/_exceptions.py index 51e3745..877083f 100644 --- a/plugins/module_utils/vendor/hcloud/_exceptions.py +++ b/plugins/module_utils/vendor/hcloud/_exceptions.py @@ -10,8 +10,8 @@ class HCloudException(Exception): class APIException(HCloudException): """There was an error while performing an API Request""" - def __init__(self, code: int | str, message: str, details: Any): - super().__init__(message) + def __init__(self, code: int | str, message: str | None, details: Any): + super().__init__(code if message is None and isinstance(code, str) else message) self.code = code self.message = message self.details = details diff --git a/plugins/module_utils/vendor/hcloud/_version.py b/plugins/module_utils/vendor/hcloud/_version.py index ce6064e..26b9a05 100644 --- a/plugins/module_utils/vendor/hcloud/_version.py +++ b/plugins/module_utils/vendor/hcloud/_version.py @@ -1,3 +1,3 @@ from __future__ import annotations -VERSION = "1.32.0" # x-release-please-version +VERSION = "1.33.0" # x-release-please-version diff --git a/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py b/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py index 4ac79ce..4bfd799 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py +++ b/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py @@ -7,6 +7,7 @@ from .client import ( # noqa: F401 ) from .domain import ( # noqa: F401 CreateLoadBalancerResponse, + GetMetricsResponse, IPv4Address, IPv6Network, LoadBalancer, diff --git a/plugins/module_utils/vendor/hcloud/load_balancers/client.py b/plugins/module_utils/vendor/hcloud/load_balancers/client.py index 56b93c8..4921213 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancers/client.py +++ b/plugins/module_utils/vendor/hcloud/load_balancers/client.py @@ -1,16 +1,24 @@ from __future__ import annotations +from datetime import datetime from typing import TYPE_CHECKING, Any, NamedTuple +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient from ..certificates import BoundCertificate from ..core import BoundModelBase, ClientEntityBase, Meta from ..load_balancer_types import BoundLoadBalancerType from ..locations import BoundLocation +from ..metrics import Metrics from ..networks import BoundNetwork from ..servers import BoundServer from .domain import ( CreateLoadBalancerResponse, + GetMetricsResponse, IPv4Address, IPv6Network, LoadBalancer, @@ -23,6 +31,7 @@ from .domain import ( LoadBalancerTargetHealthStatus, LoadBalancerTargetIP, LoadBalancerTargetLabelSelector, + MetricsType, PrivateNet, PublicNetwork, ) @@ -177,6 +186,28 @@ class BoundLoadBalancer(BoundModelBase, LoadBalancer): """ return self._client.delete(self) + def get_metrics( + self, + type: MetricsType, + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a LoadBalancer. + + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + return self._client.get_metrics( + self, + type=type, + start=start, + end=end, + step=step, + ) + def get_actions_list( self, status: list[str] | None = None, @@ -533,6 +564,46 @@ class LoadBalancersClient(ClientEntityBase): ) return True + def get_metrics( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + type: MetricsType | list[MetricsType], + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a LoadBalancer. + + :param load_balancer: The Load Balancer to get the metrics for. + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + if not isinstance(type, list): + type = [type] + if isinstance(start, str): + start = isoparse(start) + if isinstance(end, str): + end = isoparse(end) + + params: dict[str, Any] = { + "type": ",".join(type), + "start": start.isoformat(), + "end": end.isoformat(), + } + if step is not None: + params["step"] = step + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/metrics", + method="GET", + params=params, + ) + return GetMetricsResponse( + metrics=Metrics(**response["metrics"]), + ) + def get_actions_list( self, load_balancer: LoadBalancer | BoundLoadBalancer, diff --git a/plugins/module_utils/vendor/hcloud/load_balancers/domain.py b/plugins/module_utils/vendor/hcloud/load_balancers/domain.py index 0b8450d..6d6f070 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancers/domain.py +++ b/plugins/module_utils/vendor/hcloud/load_balancers/domain.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal try: from dateutil.parser import isoparse @@ -14,6 +14,7 @@ if TYPE_CHECKING: from ..certificates import BoundCertificate from ..load_balancer_types import BoundLoadBalancerType from ..locations import BoundLocation + from ..metrics import Metrics from ..networks import BoundNetwork from ..servers import BoundServer from .client import BoundLoadBalancer @@ -511,3 +512,26 @@ class CreateLoadBalancerResponse(BaseDomain): ): self.load_balancer = load_balancer self.action = action + + +MetricsType = Literal[ + "open_connections", + "connections_per_second", + "requests_per_second", + "bandwidth", +] + + +class GetMetricsResponse(BaseDomain): + """Get a Load Balancer Metrics Response Domain + + :param metrics: The Load Balancer metrics + """ + + __slots__ = ("metrics",) + + def __init__( + self, + metrics: Metrics, + ): + self.metrics = metrics diff --git a/plugins/module_utils/vendor/hcloud/metrics/__init__.py b/plugins/module_utils/vendor/hcloud/metrics/__init__.py new file mode 100644 index 0000000..65d393c --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/metrics/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .domain import Metrics, TimeSeries # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/metrics/domain.py b/plugins/module_utils/vendor/hcloud/metrics/domain.py new file mode 100644 index 0000000..a9e7377 --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/metrics/domain.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Dict, List, Literal, Tuple + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +TimeSeries = Dict[str, Dict[Literal["values"], List[Tuple[float, str]]]] + + +class Metrics(BaseDomain): + """Metrics Domain + + :param start: Start of period of metrics reported. + :param end: End of period of metrics reported. + :param step: Resolution of results in seconds. + :param time_series: Dict with time series data, using the name of the time series as + key. The metrics timestamps and values are stored in a list of tuples + ``[(timestamp, value), ...]``. + """ + + start: datetime + end: datetime + step: float + time_series: TimeSeries + + __slots__ = ( + "start", + "end", + "step", + "time_series", + ) + + def __init__( + self, + start: str, + end: str, + step: float, + time_series: TimeSeries, + ): + self.start = isoparse(start) + self.end = isoparse(end) + self.step = step + self.time_series = time_series diff --git a/plugins/module_utils/vendor/hcloud/servers/__init__.py b/plugins/module_utils/vendor/hcloud/servers/__init__.py index a7a61d4..58c811e 100644 --- a/plugins/module_utils/vendor/hcloud/servers/__init__.py +++ b/plugins/module_utils/vendor/hcloud/servers/__init__.py @@ -4,6 +4,7 @@ from .client import BoundServer, ServersClient, ServersPageResult # noqa: F401 from .domain import ( # noqa: F401 CreateServerResponse, EnableRescueResponse, + GetMetricsResponse, IPv4Address, IPv6Network, PrivateNet, diff --git a/plugins/module_utils/vendor/hcloud/servers/client.py b/plugins/module_utils/vendor/hcloud/servers/client.py index b6da0d3..b959b9d 100644 --- a/plugins/module_utils/vendor/hcloud/servers/client.py +++ b/plugins/module_utils/vendor/hcloud/servers/client.py @@ -1,8 +1,14 @@ from __future__ import annotations import warnings +from datetime import datetime from typing import TYPE_CHECKING, Any, NamedTuple +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient from ..core import BoundModelBase, ClientEntityBase, Meta from ..datacenters import BoundDatacenter @@ -10,6 +16,7 @@ from ..firewalls import BoundFirewall from ..floating_ips import BoundFloatingIP from ..images import BoundImage, CreateImageResponse from ..isos import BoundIso +from ..metrics import Metrics from ..placement_groups import BoundPlacementGroup from ..primary_ips import BoundPrimaryIP from ..server_types import BoundServerType @@ -17,8 +24,10 @@ from ..volumes import BoundVolume from .domain import ( CreateServerResponse, EnableRescueResponse, + GetMetricsResponse, IPv4Address, IPv6Network, + MetricsType, PrivateNet, PublicNetwork, PublicNetworkFirewall, @@ -210,6 +219,29 @@ class BoundServer(BoundModelBase, Server): """ return self._client.update(self, name, labels) + def get_metrics( + self, + type: MetricsType | list[MetricsType], + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a Server. + + :param server: The Server to get the metrics for. + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + return self._client.get_metrics( + self, + type=type, + start=start, + end=end, + step=step, + ) + def delete(self) -> BoundAction: """Deletes a server. This immediately removes the server from your account, and it is no longer accessible. @@ -742,6 +774,46 @@ class ServersClient(ClientEntityBase): ) return BoundServer(self, response["server"]) + def get_metrics( + self, + server: Server | BoundServer, + type: MetricsType | list[MetricsType], + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a Server. + + :param server: The Server to get the metrics for. + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + if not isinstance(type, list): + type = [type] + if isinstance(start, str): + start = isoparse(start) + if isinstance(end, str): + end = isoparse(end) + + params: dict[str, Any] = { + "type": ",".join(type), + "start": start.isoformat(), + "end": end.isoformat(), + } + if step is not None: + params["step"] = step + + response = self._client.request( + url=f"/servers/{server.id}/metrics", + method="GET", + params=params, + ) + return GetMetricsResponse( + metrics=Metrics(**response["metrics"]), + ) + def delete(self, server: Server | BoundServer) -> BoundAction: """Deletes a server. This immediately removes the server from your account, and it is no longer accessible. diff --git a/plugins/module_utils/vendor/hcloud/servers/domain.py b/plugins/module_utils/vendor/hcloud/servers/domain.py index 71ca99d..0a0d346 100644 --- a/plugins/module_utils/vendor/hcloud/servers/domain.py +++ b/plugins/module_utils/vendor/hcloud/servers/domain.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Literal try: from dateutil.parser import isoparse @@ -16,6 +16,7 @@ if TYPE_CHECKING: from ..floating_ips import BoundFloatingIP from ..images import BoundImage from ..isos import BoundIso + from ..metrics import Metrics from ..networks import BoundNetwork from ..placement_groups import BoundPlacementGroup from ..primary_ips import BoundPrimaryIP, PrimaryIP @@ -430,3 +431,25 @@ class ServerCreatePublicNetwork(BaseDomain): self.ipv6 = ipv6 self.enable_ipv4 = enable_ipv4 self.enable_ipv6 = enable_ipv6 + + +MetricsType = Literal[ + "cpu", + "disk", + "network", +] + + +class GetMetricsResponse(BaseDomain): + """Get a Server Metrics Response Domain + + :param metrics: The Server metrics + """ + + __slots__ = ("metrics",) + + def __init__( + self, + metrics: Metrics, + ): + self.metrics = metrics diff --git a/scripts/vendor.py b/scripts/vendor.py index b148ade..1e388b3 100755 --- a/scripts/vendor.py +++ b/scripts/vendor.py @@ -22,7 +22,7 @@ from textwrap import dedent logger = logging.getLogger("vendor") HCLOUD_SOURCE_URL = "https://github.com/hetznercloud/hcloud-python" -HCLOUD_VERSION = "v1.32.0" +HCLOUD_VERSION = "v1.33.0" HCLOUD_VENDOR_PATH = "plugins/module_utils/vendor/hcloud"