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
54 changes: 51 additions & 3 deletions linode_api4/groups/monitor.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from typing import Any, Optional
from typing import Any, Optional, Union

from linode_api4 import PaginatedList
from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.objects import (
AlertChannel,
AlertDefinition,
AlertDefinitionEntity,
AlertScope,
MonitorDashboard,
MonitorMetricsDefinition,
MonitorService,
Expand Down Expand Up @@ -221,6 +223,8 @@ def create_alert_definition(
trigger_conditions: dict,
entity_ids: Optional[list[str]] = None,
description: Optional[str] = None,
scope: Optional[Union[AlertScope, str]] = None,
regions: Optional[list[str]] = None,
) -> AlertDefinition:
"""
Create a new alert definition for a given service type.
Expand Down Expand Up @@ -252,6 +256,10 @@ def create_alert_definition(
:type entity_ids: Optional[list[str]]
:param description: (Optional) Longer description for the alert definition.
:type description: Optional[str]
:param scope: (Optional) Alert scope (for example: `account`, `entity`, or `region`). Defaults to `entity`.
:type scope: Optional[Union[AlertScope, str]]
:param regions: (Optional) Regions to monitor.
:type regions: Optional[list[str]]

:returns: The newly created :class:`AlertDefinition`.
:rtype: AlertDefinition
Expand All @@ -267,10 +275,15 @@ def create_alert_definition(
"rule_criteria": rule_criteria,
"trigger_conditions": trigger_conditions,
}
if description is not None:
params["description"] = description

if entity_ids is not None:
params["entity_ids"] = entity_ids
if description is not None:
params["description"] = description
if scope is not None:
params["scope"] = scope
if regions is not None:
params["regions"] = regions

# API will validate service_type and return an error if missing
result = self.client.post(
Expand All @@ -284,3 +297,38 @@ def create_alert_definition(
)

return AlertDefinition(self.client, result["id"], service_type, result)

def alert_definition_entities(
self,
service_type: str,
id: int,
*filters,
) -> PaginatedList:
"""
List entities associated with a specific alert definition.

This endpoint supports pagination fields (`page`, `page_size`) in the API.

.. note:: This endpoint is in beta and requires using the v4beta base URL.

API Documentation: TODO

:param service_type: Service type for the alert definition (e.g. `dbaas`).
:type service_type: str
:param id: Alert definition identifier.
:type id: int
:param filters: Optional filter expressions to apply to the collection.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`.

:returns: A paginated list of entities associated with the alert definition.
:rtype: PaginatedList[AlertDefinitionEntity]
"""

endpoint = (
f"/monitor/services/{service_type}/alert-definitions/{id}/entities"
)
return self.client._get_and_filter(
AlertDefinitionEntity,
*filters,
endpoint=endpoint,
)
74 changes: 52 additions & 22 deletions linode_api4/objects/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

__all__ = [
"AggregateFunction",
"Alert",
"AlertChannel",
"AlertDefinition",
"AlertDefinitionChannel",
"AlertDefinitionEntity",
"AlertEntities",
"AlertScope",
"AlertType",
"Alerts",
"MonitorDashboard",
"MonitorMetricsDefinition",
"MonitorService",
Expand Down Expand Up @@ -341,15 +343,15 @@ class RuleCriteria(JSONObject):


@dataclass
class Alert(JSONObject):
class AlertDefinitionChannel(JSONObject):
"""
Represents an alert definition reference within an AlertChannel.
Represents the notification channel set up for use with an alert.

Fields:
- id: int - Unique identifier of the alert definition.
- label: str - Human-readable name for the alert definition.
- type: str - Type of the alert (e.g., 'alerts-definitions').
- url: str - API URL for the alert definition.
- id: int - Unique identifier for this notification channel.
- label: str - Human-readable name for the alert channel.
- type: str - Type of notification used with the channel. For a user alert definition, only `email` is supported.
- url: str - URL for the channel that ends in the channel's id.
"""

id: int = 0
Expand All @@ -358,18 +360,6 @@ class Alert(JSONObject):
url: str = ""


@dataclass
class Alerts(JSONObject):
"""
Represents a collection of alert definitions within an AlertChannel.

Fields:
- items: List[Alert] - List of alert definitions.
"""

items: List[Alert] = field(default_factory=list)


class AlertType(StrEnum):
"""
Enumeration of alert origin types used by alert definitions.
Expand All @@ -387,6 +377,43 @@ class AlertType(StrEnum):
user = "user"


class AlertScope(StrEnum):
"""
Scope values supported for alert definitions.
"""

entity = "entity"
region = "region"
account = "account"


@dataclass
class AlertEntities(JSONObject):
"""
Represents entity metadata for an alert definition.

For entity scoped alerts, `entities` envelope contains the URL to list entities,
a count, and a has_more_resources flag.
For region/account scoped alerts, the `entities` are returned as an empty object.
"""

url: str = ""
count: int = 0
has_more_resources: bool = False


@dataclass
class AlertDefinitionEntity(JSONObject):
"""
Represents an entity associated with an alert definition.
"""

id: str = ""
label: str = ""
url: str = ""
_type: str = field(default="", metadata={"json_key": "type"})


class AlertDefinition(DerivedBase):
"""
Represents an alert definition for a monitor service.
Expand All @@ -406,19 +433,22 @@ class AlertDefinition(DerivedBase):
"severity": Property(mutable=True),
"type": Property(mutable=True),
"status": Property(mutable=True),
"has_more_resources": Property(mutable=True),
"has_more_resources": Property(), # Deprecated; use entities.has_more_resources.
"rule_criteria": Property(mutable=True, json_object=RuleCriteria),
"trigger_conditions": Property(
mutable=True, json_object=TriggerConditions
),
"alert_channels": Property(mutable=True, json_object=Alerts),
"alert_channels": Property(json_object=AlertDefinitionChannel),
"created": Property(is_datetime=True),
"updated": Property(is_datetime=True),
"updated_by": Property(),
"created_by": Property(),
"entity_ids": Property(mutable=True),
"description": Property(mutable=True),
"service_class": Property(alias_of="class"),
"scope": Property(AlertScope),
"regions": Property(mutable=True),
"entities": Property(json_object=AlertEntities),
}


Expand Down
17 changes: 15 additions & 2 deletions test/fixtures/monitor_alert-definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,26 @@
"severity": 1,
"type": "user",
"description": "A test alert for dbaas service",
"scope": "entity",
"regions": [],
"entity_ids": ["13217"],
"alert_channels": [],
"entities": {
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
"count": 1,
"has_more_resources": false
},
"alert_channels": [
{
"id": 10000,
"label": "Read-Write Channel",
"type": "email",
"url": "/monitor/alert-channels/10000"
}
],
"has_more_resources": false,
"rule_criteria": null,
"trigger_conditions": null,
"class": "alert",
"notification_groups": [],
"status": "active",
"created": "2024-01-01T00:00:00",
"updated": "2024-01-01T00:00:00",
Expand Down
17 changes: 15 additions & 2 deletions test/fixtures/monitor_services_dbaas_alert-definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@
"severity": 1,
"type": "user",
"description": "A test alert for dbaas service",
"scope": "entity",
"regions": [],
"entity_ids": [
"13217"
],
"alert_channels": [],
"entities": {
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
"count": 1,
"has_more_resources": false
},
"alert_channels": [
{
"id": 10000,
"label": "Read-Write Channel",
"type": "email",
"url": "/monitor/alert-channels/10000"
}
],
"has_more_resources": false,
"rule_criteria": {
"rules": [
Expand Down Expand Up @@ -39,7 +53,6 @@
"trigger_occurrences": 3
},
"class": "alert",
"notification_groups": [],
"status": "active",
"created": "2024-01-01T00:00:00",
"updated": "2024-01-01T00:00:00",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,24 @@
"severity": 1,
"type": "user",
"description": "A test alert for dbaas service",
"scope": "entity",
"regions": [],
"entity_ids": [
"13217"
],
"alert_channels": [],
"entities": {
"url": "/monitor/services/dbaas/alert-definitions/12345/entities",
"count": 1,
"has_more_resources": false
},
"alert_channels": [
{
"id": 10000,
"label": "Read-Write Channel",
"type": "email",
"url": "/monitor/alert-channels/10000"
}
],
"has_more_resources": false,
"rule_criteria": {
"rules": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"data": [
{
"id": "1",
"label": "mydatabase-1",
"url": "/v4/databases/mysql/instances/1",
"type": "dbaas"
},
{
"id": "2",
"label": "mydatabase-2",
"url": "/v4/databases/mysql/instances/2",
"type": "dbaas"
},
{
"id": "3",
"label": "mydatabase-3",
"url": "/v4/databases/mysql/instances/3",
"type": "dbaas"
}
],
"page": 1,
"pages": 1,
"results": 3
}
38 changes: 37 additions & 1 deletion test/integration/models/monitor/test_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

import pytest

from linode_api4 import LinodeClient
from linode_api4 import LinodeClient, PaginatedList
from linode_api4.objects import (
AlertDefinition,
AlertDefinitionEntity,
ApiError,
MonitorDashboard,
MonitorMetricsDefinition,
Expand Down Expand Up @@ -256,12 +257,14 @@ def wait_for_alert_ready(alert_id, service_type: str):

assert created.id
assert getattr(created, "label", None) == label
assert getattr(created, "entities", None) is not None

created = wait_for_alert_ready(created.id, service_type)

updated = client.load(AlertDefinition, created.id, service_type)
updated.label = f"{label}-updated"
updated.save()
assert getattr(updated, "entities", None) is not None

updated = wait_for_alert_ready(updated.id, service_type)

Expand All @@ -275,3 +278,36 @@ def wait_for_alert_ready(alert_id, service_type: str):
AlertDefinition, created.id, service_type
)
delete_alert.delete()


def test_alert_definition_entities(test_linode_client):
"""Test listing entities associated with an alert definition.

This test first retrieves alert definitions for a service type, then lists entities for the first alert definition.
It asserts that the returned entities have expected fields.
"""
client = test_linode_client
service_type = "dbaas"

alert_definitions = client.monitor.alert_definitions(
service_type=service_type
)

if len(alert_definitions) == 0:
pytest.fail("No alert definitions available for dbaas service type")

assert getattr(alert_definitions[0], "entities", None) is not None

alert_def = alert_definitions[0]
entities = client.monitor.alert_definition_entities(
service_type, alert_def.id
)

assert isinstance(entities, PaginatedList)
if len(entities) > 0:
entity = entities[0]
assert isinstance(entity, AlertDefinitionEntity)
assert entity.id
assert entity.label
assert entity.url
assert entity._type == service_type
Loading
Loading