Skip to content

adding SDK changes for ACLP APIs #528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions linode_api4/groups/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .lke import *
from .lke_tier import *
from .longview import *
from .monitor import *

Check notice

Code scanning / CodeQL

'import *' may pollute namespace Note

Import pollutes the enclosing namespace, as the imported module
linode_api4.groups.monitor
does not define '__all__'.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To address this issue, I'd suggest to change the namespace to a less general one, i.e., monitor_service.

from .networking import *
from .nodebalancer import *
from .object_storage import *
Expand Down
149 changes: 149 additions & 0 deletions linode_api4/groups/monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.objects import (
CreateToken,
Dashboard,
DashboardByService,
MetricDefinition,
MonitorServiceSupported,
ServiceDetails,
)


class MonitorGroup(Group):
"""
Encapsulates Monitor-related methods of the :any:`LinodeClient`. This
should not be instantiated on its own, but should instead be used through
an instance of :any:`LinodeClient`::

client = LinodeClient(token)
instances = client.monitor.dashboards() # use the LKEGroup

This group contains all features beneath the `/monitor` group in the API v4.
"""

def dashboards(self, *filters):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explicitly declare the return type please? Same for all functions in this PR

"""
Returns a list of dashboards on your account.

.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-dashboards-all

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: A list of Dashboards.
:rtype: PaginatedList of Dashboard
"""
return self.client._get_and_filter(Dashboard, *filters)

def dashboards_by_service(self, service_type: str, *filters):
"""
Returns a dashboards on your account based on the service passed.

.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-dashboards

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: A Dashboards filtered by Service Type.
:rtype: PaginatedList of the Dashboards
"""

return self.client._get_and_filter(
DashboardByService,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type can be Dashboard

*filters,
endpoint=f"/monitor/services/{service_type}/dashboards",
)

def supported_services(self, *filters):
"""
Returns a list of services supported by ACLP.

.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: A list of Supported Services
:rtype: PaginatedList of the Dashboards
Comment on lines +66 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring here seems not relevant to this functon

"""

return self.client._get_and_filter(MonitorServiceSupported, *filters)

def details_by_service(self, service_type: str, *filters):
"""
Returns a details about a particular service.

.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services-for-service-type

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: Details about a Supported Services
:rtype: PaginatedList of the Service
"""
return self.client._get_and_filter(
ServiceDetails,
*filters,
endpoint=f"/monitor/services/{service_type}",
)
Comment on lines +82 to +101
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one seems not a listing endpoint judging from the API doc


def metric_definitions(self, service_type: str, *filters):
"""
Returns metrics for a specific service type.

.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-information

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: Returns a List of metrics for a service
:rtype: PaginatedList of metrics
"""
Comment on lines +104 to +117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring is not relevant

return self.client._get_and_filter(
MetricDefinition,
*filters,
endpoint=f"/monitor/services/{service_type}/metric-definitions",
)

def create_token(self, service_type: str, entity_ids: list, *filters):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter is not needed since it's a post endpoint

"""
Returns a JWE Token for a specific service type.

.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.

API Documentation: https://techdocs.akamai.com/linode-api/reference/post-get-token
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

Comment on lines +131 to +134
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you better describe the params needed?

:returns: Returns a token for a service
:rtype: str
"""

params = {"entity_ids": entity_ids}

result = self.client.post(
f"/monitor/services/{service_type}/token", data=params
)

if "token" not in result:
raise UnexpectedResponseError(
"Unexpected response when creating token!", json=result
)
return CreateToken(self.client, result["token"], result)
3 changes: 3 additions & 0 deletions linode_api4/linode_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
LinodeGroup,
LKEGroup,
LongviewGroup,
MonitorGroup,
NetworkingGroup,
NodeBalancerGroup,
ObjectStorageGroup,
Expand Down Expand Up @@ -201,6 +202,8 @@ def __init__(
#: Access methods related to VM placement - See :any:`PlacementAPIGroup` for more information.
self.placement = PlacementAPIGroup(self)

self.monitor = MonitorGroup(self)

@property
def _user_agent(self):
return "{}python-linode_api4/{} {}".format(
Expand Down
1 change: 1 addition & 0 deletions linode_api4/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
from .vpc import *
from .beta import *
from .placement import *
from .monitor import *

Check notice

Code scanning / CodeQL

'import *' may pollute namespace Note

Import pollutes the enclosing namespace, as the imported module
linode_api4.objects.monitor
does not define '__all__'.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the group and object to include 'all'

82 changes: 82 additions & 0 deletions linode_api4/objects/monitor.py
Copy link
Contributor

@yec-akamai yec-akamai May 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many properties that are enums in the objects. Can you build them as StrEnum, like we did in the linodego https://github.com/linode/linodego/pull/722/files?

Plus, it might be good to align the naming with lindoego too

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from linode_api4.objects import Base, Property


class Dashboard(Base):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a brief description to each class please?

"""
List dashboards: https://techdocs.akamai.com/linode-api/get-dashboards-all
"""

api_endpoint = "/monitor/dashboards/{id}"
properties = {
"id": Property(identifier=True),
"created": Property(is_datetime=True),
"label": Property(),
"service_type": Property(),
"type": Property(),
"widgets": Property(mutable=True),
"updated": Property(is_datetime=True),
}


class DashboardByService(Base):
"""
Get a dashboard: https://techdocs.akamai.com/linode-api/reference/get-dashboards
"""

properties = {
"id": Property(identifier=True),
"created": Property(is_datetime=True),
"label": Property(),
"service_type": Property(),
"type": Property(),
"widgets": Property(mutable=True),
"updated": Property(is_datetime=True),
}
Comment on lines +21 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This object seems has the exact same properties as Dashboard. It's not necessary to create a separate class here



class MonitorServiceSupported(Base):

api_endpoint = "/monitor/services/"
id_attribute = "service_type"
properties = {
"service_type": Property(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add the identifier label to the property

Suggested change
"service_type": Property(),
"service_type": Property(identifier=True),

I want to double check, is there an endpoint to get a single supported service?

"label": Property(mutable=True),
}


class ServiceDetails(Base):
"""
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services-for-service-type
"""

id_attribute = "service_type"
properties = {
"label": Property(),
"service_type": Property(),
}
Comment on lines +47 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class seems redundant to the above one. We can just keep one of them.

Personally I think ServiceType could be a better name for this class.



class MetricDefinition(Base):
"""
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-information
"""

id_attribute = "metric"
properties = {
"available_aggregate_functions": Property(),
"dimensions": Property(mutable=True),
"label": Property(),
"is_alertable": Property(),
"metric": Property(),
"metric_type": Property(),
"scrape_interval": Property(),
"unit": Property(),
}
Comment on lines +59 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there is not an endpoint for getting a single metric. In this case, I think we can build it as an json object instead of class. Also personally i think it'd be better to name this type as ServiceMetric

Suggested change
class MetricDefinition(Base):
"""
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-information
"""
id_attribute = "metric"
properties = {
"available_aggregate_functions": Property(),
"dimensions": Property(mutable=True),
"label": Property(),
"is_alertable": Property(),
"metric": Property(),
"metric_type": Property(),
"scrape_interval": Property(),
"unit": Property(),
}
class ServiceMetric(JSONObject):
# .. declare the attributes and their corresponding types



class CreateToken(Base):
"""
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-get-token
"""

properties = {"token": Property(mutable=True)}
Comment on lines +77 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is not necessary to have. You can build a MetricServiceToken as a JSONObject

37 changes: 37 additions & 0 deletions test/fixtures/monitor_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"data": [
{
"created": "2024-10-10T05:01:58",
"id": 1,
"label": "Resource Usage",
"service_type": "dbaas",
"type": "standard",
"updated": "2024-10-10T05:01:58",
"widgets": [
{
"aggregate_function": "sum",
"chart_type": "area",
"color": "default",
"label": "CPU Usage",
"metric": "cpu_usage",
"size": 12,
"unit": "%",
"y_label": "cpu_usage"
},
{
"aggregate_function": "sum",
"chart_type": "area",
"color": "default",
"label": "Disk I/O Write",
"metric": "write_iops",
"size": 6,
"unit": "IOPS",
"y_label": "write_iops"
}
]
}
],
"page": 1,
"pages": 1,
"results": 1
}
30 changes: 30 additions & 0 deletions test/fixtures/monitor_dashboards_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"created": "2024-10-10T05:01:58",
"id": 1,
"label": "Resource Usage",
"service_type": "dbaas",
"type": "standard",
"updated": "2024-10-10T05:01:58",
"widgets": [
{
"aggregate_function": "sum",
"chart_type": "area",
"color": "default",
"label": "CPU Usage",
"metric": "cpu_usage",
"size": 12,
"unit": "%",
"y_label": "cpu_usage"
},
{
"aggregate_function": "sum",
"chart_type": "area",
"color": "default",
"label": "Available Memory",
"metric": "available_memory",
"size": 6,
"unit": "GB",
"y_label": "available_memory"
}
]
}
11 changes: 11 additions & 0 deletions test/fixtures/monitor_services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"data": [
{
"label": "Databases",
"service_type": "dbaas"
}
],
"page": 1,
"pages": 1,
"results": 1
}
11 changes: 11 additions & 0 deletions test/fixtures/monitor_services_dbaas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"data": [
{
"label": "Databases",
"service_type": "dbaas"
}
],
"page": 1,
"pages": 1,
"results": 1
}
37 changes: 37 additions & 0 deletions test/fixtures/monitor_services_dbaas_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"data": [
{
"created": "2024-10-10T05:01:58",
"id": 1,
"label": "Resource Usage",
"service_type": "dbaas",
"type": "standard",
"updated": "2024-10-10T05:01:58",
"widgets": [
{
"aggregate_function": "sum",
"chart_type": "area",
"color": "default",
"label": "CPU Usage",
"metric": "cpu_usage",
"size": 12,
"unit": "%",
"y_label": "cpu_usage"
},
{
"aggregate_function": "sum",
"chart_type": "area",
"color": "default",
"label": "Memory Usage",
"metric": "memory_usage",
"size": 6,
"unit": "%",
"y_label": "memory_usage"
}
]
}
],
"page": 1,
"pages": 1,
"results": 1
}
Loading