diff --git a/linode_api4/objects/monitor.py b/linode_api4/objects/monitor.py index 7e0f4ae4d..fd1577169 100644 --- a/linode_api4/objects/monitor.py +++ b/linode_api4/objects/monitor.py @@ -62,7 +62,6 @@ class ServiceType(StrEnum): aclb = "aclb" net_load_balancer = "netloadbalancer" - class MetricType(StrEnum): """ Enum for supported metric type @@ -229,6 +228,7 @@ class MonitorDashboard(Base): "label": Property(), "service_type": Property(ServiceType), "type": Property(DashboardType), + "group_by": Property(), "widgets": Property(json_object=DashboardWidget), "updated": Property(is_datetime=True), } diff --git a/test/fixtures/monitor_dashboards.json b/test/fixtures/monitor_dashboards.json index 5e56923a1..996a3d400 100644 --- a/test/fixtures/monitor_dashboards.json +++ b/test/fixtures/monitor_dashboards.json @@ -1,41 +1,404 @@ { "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", - "group_by": ["entity_id"], - "filters": null - }, - { - "aggregate_function": "sum", - "chart_type": "area", - "color": "default", - "label": "Disk I/O Write", - "metric": "write_iops", - "size": 6, - "unit": "IOPS", - "y_label": "write_iops", - "group_by": ["entity_id"], - "filters": null - } - ] - } + { + "id": 1, + "type": "standard", + "service_type": "dbaas", + "label": "Resource Usage", + "group_by": [ + "entity_id" + ], + "created": "2025-02-27T07:59:40", + "updated": "2025-10-07T01:16:40", + "widgets": [ + { + "metric": "cpu_usage", + "unit": "%", + "label": "CPU Usage", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "cpu_usage", + "aggregate_function": "avg" + }, + { + "metric": "memory_usage", + "unit": "%", + "label": "Memory Usage", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "memory_usage", + "aggregate_function": "avg" + }, + { + "metric": "available_memory", + "unit": "GB", + "label": "Available Memory", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "available_memory", + "aggregate_function": "avg" + }, + { + "metric": "disk_usage", + "unit": "%", + "label": "Disk Space Usage", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "disk_usage", + "aggregate_function": "avg" + }, + { + "metric": "available_disk", + "unit": "GB", + "label": "Available Disk Space", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "available_disk", + "aggregate_function": "avg" + }, + { + "metric": "read_iops", + "unit": "IOPS", + "label": "Disk I/O Read", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "read_iops", + "aggregate_function": "avg" + }, + { + "metric": "write_iops", + "unit": "IOPS", + "label": "Disk I/O Write", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "write_iops", + "aggregate_function": "avg" + } + ] + }, + { + "id": 2, + "type": "standard", + "service_type": "linode", + "label": "Overview", + "group_by": [ + "entity_id" + ], + "created": "2025-05-15T07:05:56", + "updated": "2026-04-08T04:48:43", + "widgets": [ + { + "metric": "vm_cpu_time_total", + "unit": "%", + "label": "CPU Usage by Instance", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "vm_cpu_time_total", + "aggregate_function": "avg" + }, + { + "metric": "vm_local_disk_iops_total", + "unit": "IOPS", + "label": "Local Disk I/O by Instance", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "vm_local_disk_iops_total", + "aggregate_function": "avg" + }, + { + "metric": "vm_network_bytes_total", + "unit": "Kbps", + "label": "Network Traffic In by Instance", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "vm_network_bytes_total", + "aggregate_function": "avg", + "filters": [ + { + "dimension_label": "pattern", + "operator": "in", + "value": "publicin,privatein" + } + ] + }, + { + "metric": "vm_network_bytes_total", + "unit": "Kbps", + "label": "Network Traffic Out by Instance", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "vm_network_bytes_total", + "aggregate_function": "avg", + "filters": [ + { + "dimension_label": "pattern", + "operator": "in", + "value": "publicout,privateout" + } + ] + } + ] + }, + { + "id": 3, + "type": "standard", + "service_type": "nodebalancer", + "label": "Traffic Overview", + "group_by": [ + "entity_id" + ], + "created": "2025-06-05T05:02:14", + "updated": "2026-04-08T04:48:43", + "widgets": [ + { + "metric": "nb_ingress_traffic_rate", + "unit": "Bps", + "label": "Ingress Traffic Rate", + "color": "default", + "size": 12, + "chart_type": "line", + "y_label": "nb_ingress_traffic_rate", + "aggregate_function": "sum" + }, + { + "metric": "nb_egress_traffic_rate", + "unit": "Bps", + "label": "Egress Traffic Rate", + "color": "default", + "size": 12, + "chart_type": "line", + "y_label": "nb_egress_traffic_rate", + "aggregate_function": "sum" + } + ] + }, + { + "id": 4, + "type": "standard", + "service_type": "firewall", + "label": "Connection Metrics", + "group_by": [ + "entity_id", + "linode_id", + "interface_id" + ], + "created": "2025-06-25T01:24:14", + "updated": "2026-04-08T04:48:43", + "widgets": [ + { + "metric": "fw_active_connections", + "unit": "Count", + "label": "Current Connections", + "color": "default", + "size": 12, + "chart_type": "line", + "y_label": "fw_active_connections", + "aggregate_function": "avg" + } + ] + }, + { + "id": 5, + "type": "standard", + "service_type": "netloadbalancer", + "label": "Traffic Overview", + "group_by": [ + "entity_id" + ], + "created": "2025-06-25T01:25:37", + "updated": "2026-04-08T04:48:43", + "widgets": [ + { + "metric": "nlb_ingress_traffic", + "unit": "Bps", + "label": "Ingress Traffic Rate", + "color": "default", + "size": 12, + "chart_type": "line", + "y_label": "nlb_ingress_traffic", + "aggregate_function": "sum" + }, + { + "metric": "nlb_backend_ingress_traffic", + "unit": "Bps", + "label": "Ingress Traffic Rate Per Backend Node", + "color": "default", + "size": 12, + "chart_type": "line", + "y_label": "nlb_backend_ingress_traffic", + "group_by": [ + "node_id" + ], + "aggregate_function": "sum" + } + ] + }, + { + "id": 6, + "type": "standard", + "service_type": "objectstorage", + "label": "Bucket Activity", + "group_by": [ + "entity_id" + ], + "created": "2025-09-08T06:54:54", + "updated": "2026-04-08T06:01:02", + "widgets": [ + { + "metric": "obj_bucket_size", + "unit": "Bytes", + "label": "Content Stored", + "color": "default", + "size": 6, + "chart_type": "line", + "y_label": "obj_bucket_size", + "aggregate_function": "sum" + }, + { + "metric": "obj_requests_rps", + "unit": "Count", + "label": "Requests per second", + "color": "default", + "size": 6, + "chart_type": "line", + "y_label": "obj_requests_rps", + "group_by": [ + "request_type" + ], + "aggregate_function": "sum" + } + ] + }, + { + "id": 7, + "type": "standard", + "service_type": "blockstorage", + "label": "Storage Performance", + "group_by": [ + "entity_id", + "linode_id" + ], + "created": "2025-09-30T03:34:57", + "updated": "2026-04-08T04:48:43", + "widgets": [ + { + "metric": "volume_read_ops", + "unit": "Count", + "label": "Volume Read Operations", + "color": "default", + "size": 6, + "chart_type": "line", + "y_label": "volume_read_ops", + "aggregate_function": "sum" + } + ] + }, + { + "id": 8, + "type": "standard", + "service_type": "firewall", + "label": "Ingress Activity", + "group_by": [ + "entity_id", + "nodebalancer_id" + ], + "created": "2025-10-07T01:11:59", + "updated": "2026-04-08T04:48:43", + "widgets": [ + { + "metric": "nb_ingress_bytes_accepted", + "unit": "Bps", + "label": "Accepted Bytes", + "color": "default", + "size": 12, + "chart_type": "line", + "y_label": "nb_ingress_bytes_accepted", + "aggregate_function": "sum" + } + ] + }, + { + "id": 9, + "type": "standard", + "service_type": "lke", + "label": "Cluster status", + "group_by": [ + "entity_id" + ], + "created": "2025-11-07T08:51:48", + "updated": "2025-11-07T08:51:48", + "widgets": [ + { + "metric": "lke_e_ready_worker_nodes", + "unit": "Count", + "label": "Ready Worker Nodes", + "color": "default", + "size": 6, + "chart_type": "line", + "y_label": "lke_e_ready_worker_nodes", + "aggregate_function": "max" + } + ] + }, + { + "id": 10, + "type": "standard", + "service_type": "objectstorage", + "label": "Endpoint Activity", + "group_by": [ + "endpoint" + ], + "created": "2025-11-19T06:15:28", + "updated": "2026-04-08T06:01:03", + "widgets": [ + { + "metric": "obj_bucket_size", + "unit": "Bytes", + "label": "Content Stored", + "color": "default", + "size": 6, + "chart_type": "line", + "y_label": "obj_bucket_size", + "aggregate_function": "sum" + } + ] + }, + { + "id": 11, + "type": "standard", + "service_type": "logs", + "label": "Log Delivery Status", + "group_by": [ + "entity_id" + ], + "created": "2026-03-12T04:24:34", + "updated": "2026-04-07T05:54:21", + "widgets": [ + { + "metric": "success_upload_count", + "unit": "Count", + "label": "Successful Uploads", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "success_upload_count", + "aggregate_function": "sum" + } + ] + } ], "page": 1, "pages": 1, - "results": 1 - } \ No newline at end of file + "results": 11 +} diff --git a/test/fixtures/monitor_dashboards_1.json b/test/fixtures/monitor_dashboards_1.json index afb5d71ee..32e20fe1f 100644 --- a/test/fixtures/monitor_dashboards_1.json +++ b/test/fixtures/monitor_dashboards_1.json @@ -1,34 +1,83 @@ { - "created": "2024-10-10T05:01:58", "id": 1, - "label": "Resource Usage", - "service_type": "dbaas", "type": "standard", - "updated": "2024-10-10T05:01:58", + "service_type": "dbaas", + "label": "Resource Usage", + "group_by": [ + "entity_id" + ], + "created": "2025-02-27T07:59:40", + "updated": "2025-10-07T01:16:40", "widgets": [ - { - "aggregate_function": "sum", - "chart_type": "area", - "color": "default", - "label": "CPU Usage", - "metric": "cpu_usage", - "size": 12, - "unit": "%", - "y_label": "cpu_usage", - "group_by": ["entity_id"], - "filters": null - }, - { - "aggregate_function": "sum", - "chart_type": "area", - "color": "default", - "label": "Available Memory", - "metric": "available_memory", - "size": 6, - "unit": "GB", - "y_label": "available_memory", - "group_by": ["entity_id"], - "filters": null - } + { + "metric": "cpu_usage", + "unit": "%", + "label": "CPU Usage", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "cpu_usage", + "aggregate_function": "avg" + }, + { + "metric": "memory_usage", + "unit": "%", + "label": "Memory Usage", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "memory_usage", + "aggregate_function": "avg" + }, + { + "metric": "available_memory", + "unit": "GB", + "label": "Available Memory", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "available_memory", + "aggregate_function": "avg" + }, + { + "metric": "disk_usage", + "unit": "%", + "label": "Disk Space Usage", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "disk_usage", + "aggregate_function": "avg" + }, + { + "metric": "available_disk", + "unit": "GB", + "label": "Available Disk Space", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "available_disk", + "aggregate_function": "avg" + }, + { + "metric": "read_iops", + "unit": "IOPS", + "label": "Disk I/O Read", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "read_iops", + "aggregate_function": "avg" + }, + { + "metric": "write_iops", + "unit": "IOPS", + "label": "Disk I/O Write", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "write_iops", + "aggregate_function": "avg" + } ] - } \ No newline at end of file +} diff --git a/test/fixtures/monitor_services_dbaas_dashboards.json b/test/fixtures/monitor_services_dbaas_dashboards.json index e39a231b2..1620c4453 100644 --- a/test/fixtures/monitor_services_dbaas_dashboards.json +++ b/test/fixtures/monitor_services_dbaas_dashboards.json @@ -1,48 +1,90 @@ { "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", - "group_by": ["entity_id"], - "filters": null - }, - { - "aggregate_function": "sum", - "chart_type": "area", - "color": "default", - "label": "Memory Usage", - "metric": "memory_usage", - "size": 6, - "unit": "%", - "y_label": "memory_usage", - "group_by": ["entity_id"], - "filters": [ - { - "dimension_label": "pattern", - "operator": "in", - "value": "publicout,privateout" - } + { + "id": 1, + "type": "standard", + "service_type": "dbaas", + "label": "Resource Usage", + "group_by": [ + "entity_id" + ], + "created": "2025-02-27T07:59:40", + "updated": "2025-10-07T01:16:40", + "widgets": [ + { + "metric": "cpu_usage", + "unit": "%", + "label": "CPU Usage", + "color": "default", + "size": 12, + "chart_type": "area", + "y_label": "cpu_usage", + "aggregate_function": "avg" + }, + { + "metric": "memory_usage", + "unit": "%", + "label": "Memory Usage", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "memory_usage", + "aggregate_function": "avg" + }, + { + "metric": "available_memory", + "unit": "GB", + "label": "Available Memory", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "available_memory", + "aggregate_function": "avg" + }, + { + "metric": "disk_usage", + "unit": "%", + "label": "Disk Space Usage", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "disk_usage", + "aggregate_function": "avg" + }, + { + "metric": "available_disk", + "unit": "GB", + "label": "Available Disk Space", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "available_disk", + "aggregate_function": "avg" + }, + { + "metric": "read_iops", + "unit": "IOPS", + "label": "Disk I/O Read", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "read_iops", + "aggregate_function": "avg" + }, + { + "metric": "write_iops", + "unit": "IOPS", + "label": "Disk I/O Write", + "color": "default", + "size": 6, + "chart_type": "area", + "y_label": "write_iops", + "aggregate_function": "avg" + } ] - - } - ] - } + } ], "page": 1, "pages": 1, "results": 1 - } \ No newline at end of file +} diff --git a/test/integration/models/monitor/test_monitor.py b/test/integration/models/monitor/test_monitor.py index ceb9fdc3a..911b6654d 100644 --- a/test/integration/models/monitor/test_monitor.py +++ b/test/integration/models/monitor/test_monitor.py @@ -39,7 +39,6 @@ def test_get_all_dashboards(test_linode_client): assert isinstance(dashboards_by_svc[0], MonitorDashboard) assert dashboards_by_svc[0].service_type == get_service_type - def test_filter_and_group_by(test_linode_client): client = test_linode_client dashboards_by_svc = client.monitor.dashboards(service_type="linode") diff --git a/test/unit/objects/monitor_test.py b/test/unit/objects/monitor_test.py index 5913b3b28..1dce9aeed 100644 --- a/test/unit/objects/monitor_test.py +++ b/test/unit/objects/monitor_test.py @@ -25,15 +25,17 @@ def test_dashboard_by_ID(self): dashboard = self.client.load(MonitorDashboard, 1) self.assertEqual(dashboard.type, "standard") self.assertEqual( - dashboard.created, datetime.datetime(2024, 10, 10, 5, 1, 58) + dashboard.created, datetime.datetime(2025, 2, 27, 7, 59, 40) ) self.assertEqual(dashboard.id, 1) self.assertEqual(dashboard.label, "Resource Usage") self.assertEqual(dashboard.service_type, "dbaas") self.assertEqual( - dashboard.updated, datetime.datetime(2024, 10, 10, 5, 1, 58) + dashboard.updated, datetime.datetime(2025, 10, 7, 1, 16, 40) ) - self.assertEqual(dashboard.widgets[0].aggregate_function, "sum") + self.assertEqual(dashboard.group_by, ["entity_id"]) + self.assertEqual(len(dashboard.widgets), 7) + self.assertEqual(dashboard.widgets[0].aggregate_function, "avg") self.assertEqual(dashboard.widgets[0].chart_type, "area") self.assertEqual(dashboard.widgets[0].color, "default") self.assertEqual(dashboard.widgets[0].label, "CPU Usage") @@ -41,22 +43,24 @@ def test_dashboard_by_ID(self): self.assertEqual(dashboard.widgets[0].size, 12) self.assertEqual(dashboard.widgets[0].unit, "%") self.assertEqual(dashboard.widgets[0].y_label, "cpu_usage") - self.assertEqual(dashboard.widgets[0].group_by, ["entity_id"]) + self.assertIsNone(dashboard.widgets[0].group_by) self.assertIsNone(dashboard.widgets[0].filters) def test_dashboard_by_service_type(self): dashboards = self.client.monitor.dashboards(service_type="dbaas") self.assertEqual(dashboards[0].type, "standard") self.assertEqual( - dashboards[0].created, datetime.datetime(2024, 10, 10, 5, 1, 58) + dashboards[0].created, datetime.datetime(2025, 2, 27, 7, 59, 40) ) self.assertEqual(dashboards[0].id, 1) self.assertEqual(dashboards[0].label, "Resource Usage") self.assertEqual(dashboards[0].service_type, "dbaas") self.assertEqual( - dashboards[0].updated, datetime.datetime(2024, 10, 10, 5, 1, 58) + dashboards[0].updated, datetime.datetime(2025, 10, 7, 1, 16, 40) ) - self.assertEqual(dashboards[0].widgets[0].aggregate_function, "sum") + self.assertEqual(dashboards[0].group_by, ["entity_id"]) + self.assertEqual(len(dashboards[0].widgets), 7) + self.assertEqual(dashboards[0].widgets[0].aggregate_function, "avg") self.assertEqual(dashboards[0].widgets[0].chart_type, "area") self.assertEqual(dashboards[0].widgets[0].color, "default") self.assertEqual(dashboards[0].widgets[0].label, "CPU Usage") @@ -64,35 +68,30 @@ def test_dashboard_by_service_type(self): self.assertEqual(dashboards[0].widgets[0].size, 12) self.assertEqual(dashboards[0].widgets[0].unit, "%") self.assertEqual(dashboards[0].widgets[0].y_label, "cpu_usage") - self.assertEqual(dashboards[0].widgets[0].group_by, ["entity_id"]) + self.assertIsNone(dashboards[0].widgets[0].group_by) self.assertIsNone(dashboards[0].widgets[0].filters) - # Test the second widget which has filters + # Test the second widget (memory_usage, no filters) self.assertEqual(dashboards[0].widgets[1].label, "Memory Usage") - self.assertEqual(dashboards[0].widgets[1].group_by, ["entity_id"]) - self.assertIsNotNone(dashboards[0].widgets[1].filters) - self.assertEqual(len(dashboards[0].widgets[1].filters), 1) - self.assertEqual( - dashboards[0].widgets[1].filters[0].dimension_label, "pattern" - ) - self.assertEqual(dashboards[0].widgets[1].filters[0].operator, "in") - self.assertEqual( - dashboards[0].widgets[1].filters[0].value, "publicout,privateout" - ) + self.assertEqual(dashboards[0].widgets[1].aggregate_function, "avg") + self.assertIsNone(dashboards[0].widgets[1].group_by) + self.assertIsNone(dashboards[0].widgets[1].filters) def test_get_all_dashboards(self): dashboards = self.client.monitor.dashboards() + self.assertEqual(len(dashboards), 11) self.assertEqual(dashboards[0].type, "standard") self.assertEqual( - dashboards[0].created, datetime.datetime(2024, 10, 10, 5, 1, 58) + dashboards[0].created, datetime.datetime(2025, 2, 27, 7, 59, 40) ) self.assertEqual(dashboards[0].id, 1) self.assertEqual(dashboards[0].label, "Resource Usage") self.assertEqual(dashboards[0].service_type, "dbaas") self.assertEqual( - dashboards[0].updated, datetime.datetime(2024, 10, 10, 5, 1, 58) + dashboards[0].updated, datetime.datetime(2025, 10, 7, 1, 16, 40) ) - self.assertEqual(dashboards[0].widgets[0].aggregate_function, "sum") + self.assertEqual(dashboards[0].group_by, ["entity_id"]) + self.assertEqual(dashboards[0].widgets[0].aggregate_function, "avg") self.assertEqual(dashboards[0].widgets[0].chart_type, "area") self.assertEqual(dashboards[0].widgets[0].color, "default") self.assertEqual(dashboards[0].widgets[0].label, "CPU Usage") @@ -100,8 +99,11 @@ def test_get_all_dashboards(self): self.assertEqual(dashboards[0].widgets[0].size, 12) self.assertEqual(dashboards[0].widgets[0].unit, "%") self.assertEqual(dashboards[0].widgets[0].y_label, "cpu_usage") - self.assertEqual(dashboards[0].widgets[0].group_by, ["entity_id"]) + self.assertIsNone(dashboards[0].widgets[0].group_by) self.assertIsNone(dashboards[0].widgets[0].filters) + # Verify a dashboard with multiple group_by values (id=4, firewall) + self.assertEqual(dashboards[3].id, 4) + self.assertEqual(dashboards[3].group_by, ["entity_id", "linode_id", "interface_id"]) def test_specific_service_details(self): data = self.client.load(MonitorService, "dbaas")