diff --git a/linode_api4/groups/__init__.py b/linode_api4/groups/__init__.py index e50eeab66..3842042ad 100644 --- a/linode_api4/groups/__init__.py +++ b/linode_api4/groups/__init__.py @@ -10,6 +10,7 @@ from .lke import * from .lke_tier import * from .longview import * +from .monitor import * from .networking import * from .nodebalancer import * from .object_storage import * diff --git a/linode_api4/groups/monitor.py b/linode_api4/groups/monitor.py new file mode 100644 index 000000000..908b4e819 --- /dev/null +++ b/linode_api4/groups/monitor.py @@ -0,0 +1,153 @@ +__all__ = [ + "MonitorGroup", +] +from typing import Any, Optional + +from linode_api4 import ( + PaginatedList, +) +from linode_api4.errors import UnexpectedResponseError +from linode_api4.groups import Group +from linode_api4.objects import ( + MonitorDashboard, + MonitorMetricsDefinition, + MonitorService, + MonitorServiceToken, +) + + +class MonitorGroup(Group): + """ + Encapsulates Monitor-related methods of the :any:`LinodeClient`. + + This group contains all features beneath the `/monitor` group in the API v4. + """ + + def dashboards( + self, *filters, service_type: Optional[str] = None + ) -> PaginatedList: + """ + Returns a list of dashboards. If `service_type` is provided, it fetches dashboards + for the specific service type. If None, it fetches all dashboards. + + dashboards = client.monitor.dashboards() + dashboard = client.load(MonitorDashboard, 1) + dashboards_by_service = client.monitor.dashboards(service_type="dbaas") + + .. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`. + + API Documentation: + - All Dashboards: https://techdocs.akamai.com/linode-api/reference/get-dashboards-all + - Dashboards by Service: https://techdocs.akamai.com/linode-api/reference/get-dashboards + + :param service_type: The service type to get dashboards for. + :type service_type: Optional[str] + :param filters: Any number of filters to apply to this query. + See :doc:`Filtering Collections` + for more details on filtering. + + :returns: A list of Dashboards. + :rtype: PaginatedList of Dashboard + """ + endpoint = ( + f"/monitor/services/{service_type}/dashboards" + if service_type + else "/monitor/dashboards" + ) + + return self.client._get_and_filter( + MonitorDashboard, + *filters, + endpoint=endpoint, + ) + + def services( + self, *filters, service_type: Optional[str] = None + ) -> list[MonitorService]: + """ + Lists services supported by ACLP. + supported_services = client.monitor.services() + service_details = client.monitor.services(service_type="dbaas") + + .. 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 + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services-for-service-type + + :param service_type: The service type to get details for. + :type service_type: Optional[str] + :param filters: Any number of filters to apply to this query. + See :doc:`Filtering Collections` + for more details on filtering. + + :returns: Lists monitor services by a given service_type + :rtype: PaginatedList of the Services + """ + endpoint = ( + f"/monitor/services/{service_type}" + if service_type + else "/monitor/services" + ) + return self.client._get_and_filter( + MonitorService, + *filters, + endpoint=endpoint, + ) + + def metric_definitions( + self, service_type: str, *filters + ) -> list[MonitorMetricsDefinition]: + """ + Returns metrics for a specific service type. + + metrics = client.monitor.list_metric_definitions(service_type="dbaas") + .. 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 service_type: The service type to get metrics for. + :type service_type: str + :param filters: Any number of filters to apply to this query. + See :doc:`Filtering Collections` + for more details on filtering. + + :returns: Returns a List of metrics for a service + :rtype: PaginatedList of metrics + """ + return self.client._get_and_filter( + MonitorMetricsDefinition, + *filters, + endpoint=f"/monitor/services/{service_type}/metric-definitions", + ) + + def create_token( + self, service_type: str, entity_ids: list[Any] + ) -> MonitorServiceToken: + """ + Returns a JWE Token for a specific service type. + token = client.monitor.create_token(service_type="dbaas", entity_ids=[1234]) + + .. 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 service_type: The service type to create token for. + :type service_type: str + :param entity_ids: The list of entity IDs for which the token is valid. + :type entity_ids: any + + :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 MonitorServiceToken(token=result["token"]) diff --git a/linode_api4/linode_client.py b/linode_api4/linode_client.py index 19e6f3900..e71f1563e 100644 --- a/linode_api4/linode_client.py +++ b/linode_api4/linode_client.py @@ -19,6 +19,7 @@ LinodeGroup, LKEGroup, LongviewGroup, + MonitorGroup, NetworkingGroup, NodeBalancerGroup, ObjectStorageGroup, @@ -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( diff --git a/linode_api4/objects/__init__.py b/linode_api4/objects/__init__.py index b13fac51a..7f1542d2a 100644 --- a/linode_api4/objects/__init__.py +++ b/linode_api4/objects/__init__.py @@ -21,3 +21,4 @@ from .vpc import * from .beta import * from .placement import * +from .monitor import * diff --git a/linode_api4/objects/monitor.py b/linode_api4/objects/monitor.py new file mode 100644 index 000000000..f518e641d --- /dev/null +++ b/linode_api4/objects/monitor.py @@ -0,0 +1,180 @@ +__all__ = [ + "MonitorDashboard", + "MonitorMetricsDefinition", + "MonitorService", + "MonitorServiceToken", +] +from dataclasses import dataclass, field +from typing import List, Optional + +from linode_api4.objects import Base, JSONObject, Property, StrEnum + + +class AggregateFunction(StrEnum): + """ + Enum for supported aggregate functions. + """ + + min = "min" + max = "max" + avg = "avg" + sum = "sum" + count = "count" + rate = "rate" + increase = "increase" + last = "last" + + +class ChartType(StrEnum): + """ + Enum for supported chart types. + """ + + line = "line" + area = "area" + + +class ServiceType(StrEnum): + """ + Enum for supported service types. + """ + + dbaas = "dbaas" + linode = "linode" + lke = "lke" + vpc = "vpc" + nodebalancer = "nodebalancer" + firewall = "firewall" + object_storage = "object_storage" + aclb = "aclb" + + +class MetricType(StrEnum): + """ + Enum for supported metric type + """ + + gauge = "gauge" + counter = "counter" + histogram = "histogram" + summary = "summary" + + +class MetricUnit(StrEnum): + """ + Enum for supported metric units. + """ + + COUNT = "count" + PERCENT = "percent" + BYTE = "byte" + SECOND = "second" + BITS_PER_SECOND = "bits_per_second" + MILLISECOND = "millisecond" + KB = "KB" + MB = "MB" + GB = "GB" + RATE = "rate" + BYTES_PER_SECOND = "bytes_per_second" + PERCENTILE = "percentile" + RATIO = "ratio" + OPS_PER_SECOND = "ops_per_second" + IOPS = "iops" + + +class DashboardType(StrEnum): + """ + Enum for supported dashboard types. + """ + + standard = "standard" + custom = "custom" + + +@dataclass +class DashboardWidget(JSONObject): + """ + Represents a single widget in the widgets list. + """ + + metric: str = "" + unit: MetricUnit = "" + label: str = "" + color: str = "" + size: int = 0 + chart_type: ChartType = "" + y_label: str = "" + aggregate_function: AggregateFunction = "" + + +@dataclass +class Dimension(JSONObject): + """ + Represents a single dimension in the dimensions list. + """ + + dimension_label: Optional[str] = None + label: Optional[str] = None + values: Optional[List[str]] = None + + +@dataclass +class MonitorMetricsDefinition(JSONObject): + """ + Represents a single metric definition in the metrics definition list. + + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-information + """ + + metric: str = "" + label: str = "" + metric_type: MetricType = "" + unit: MetricUnit = "" + scrape_interval: int = 0 + is_alertable: bool = False + dimensions: Optional[List[Dimension]] = None + available_aggregate_functions: List[AggregateFunction] = field( + default_factory=list + ) + + +class MonitorDashboard(Base): + """ + Dashboard details. + + 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(ServiceType), + "type": Property(DashboardType), + "widgets": Property(List[DashboardWidget]), + "updated": Property(is_datetime=True), + } + + +@dataclass +class MonitorService(JSONObject): + """ + Represents a single service type. + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-monitor-services + + """ + + service_type: ServiceType = "" + label: str = "" + + +@dataclass +class MonitorServiceToken(JSONObject): + """ + A token for the requested service_type. + + API Documentation: https://techdocs.akamai.com/linode-api/reference/post-get-token + """ + + token: str = "" diff --git a/test/fixtures/monitor_dashboards.json b/test/fixtures/monitor_dashboards.json new file mode 100644 index 000000000..42de92b55 --- /dev/null +++ b/test/fixtures/monitor_dashboards.json @@ -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 + } \ No newline at end of file diff --git a/test/fixtures/monitor_dashboards_1.json b/test/fixtures/monitor_dashboards_1.json new file mode 100644 index 000000000..b78bf3447 --- /dev/null +++ b/test/fixtures/monitor_dashboards_1.json @@ -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" + } + ] + } \ No newline at end of file diff --git a/test/fixtures/monitor_services.json b/test/fixtures/monitor_services.json new file mode 100644 index 000000000..7a568866c --- /dev/null +++ b/test/fixtures/monitor_services.json @@ -0,0 +1,11 @@ +{ + "data": [ + { + "label": "Databases", + "service_type": "dbaas" + } + ], + "page": 1, + "pages": 1, + "results": 1 + } \ No newline at end of file diff --git a/test/fixtures/monitor_services_dbaas.json b/test/fixtures/monitor_services_dbaas.json new file mode 100644 index 000000000..7a568866c --- /dev/null +++ b/test/fixtures/monitor_services_dbaas.json @@ -0,0 +1,11 @@ +{ + "data": [ + { + "label": "Databases", + "service_type": "dbaas" + } + ], + "page": 1, + "pages": 1, + "results": 1 + } \ No newline at end of file diff --git a/test/fixtures/monitor_services_dbaas_dashboards.json b/test/fixtures/monitor_services_dbaas_dashboards.json new file mode 100644 index 000000000..5fbb7e9db --- /dev/null +++ b/test/fixtures/monitor_services_dbaas_dashboards.json @@ -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 + } \ No newline at end of file diff --git a/test/fixtures/monitor_services_dbaas_metric-definitions.json b/test/fixtures/monitor_services_dbaas_metric-definitions.json new file mode 100644 index 000000000..c493b23a3 --- /dev/null +++ b/test/fixtures/monitor_services_dbaas_metric-definitions.json @@ -0,0 +1,55 @@ +{ + "data": [ + { + "available_aggregate_functions": [ + "max", + "avg", + "min", + "sum" + ], + "dimensions": [ + { + "dimension_label": "node_type", + "label": "Node Type", + "values": [ + "primary", + "secondary" + ] + } + ], + "is_alertable": true, + "label": "CPU Usage", + "metric": "cpu_usage", + "metric_type": "gauge", + "scrape_interval": "60s", + "unit": "percent" + }, + { + "available_aggregate_functions": [ + "max", + "avg", + "min", + "sum" + ], + "dimensions": [ + { + "dimension_label": "node_type", + "label": "Node Type", + "values": [ + "primary", + "secondary" + ] + } + ], + "is_alertable": true, + "label": "Disk I/O Read", + "metric": "read_iops", + "metric_type": "gauge", + "scrape_interval": "60s", + "unit": "iops" + } + ], + "page": 1, + "pages": 1, + "results": 2 + } \ No newline at end of file diff --git a/test/fixtures/monitor_services_dbaas_token.json b/test/fixtures/monitor_services_dbaas_token.json new file mode 100644 index 000000000..b1aa0d786 --- /dev/null +++ b/test/fixtures/monitor_services_dbaas_token.json @@ -0,0 +1,3 @@ +{ + "token": "abcdefhjigkfghh" +} \ No newline at end of file diff --git a/test/fixtures/monitor_services_linode_token.json b/test/fixtures/monitor_services_linode_token.json new file mode 100644 index 000000000..b1aa0d786 --- /dev/null +++ b/test/fixtures/monitor_services_linode_token.json @@ -0,0 +1,3 @@ +{ + "token": "abcdefhjigkfghh" +} \ No newline at end of file diff --git a/test/integration/helpers.py b/test/integration/helpers.py index 0ee9810a8..9777d5950 100644 --- a/test/integration/helpers.py +++ b/test/integration/helpers.py @@ -43,7 +43,7 @@ def send_request_when_resource_available( timeout: int, func: Callable, *args, **kwargs ) -> object: start_time = time.time() - retry_statuses = {400, 500} + retry_statuses = {400, 500, 503} while True: try: diff --git a/test/integration/models/monitor/test_monitor.py b/test/integration/models/monitor/test_monitor.py new file mode 100644 index 000000000..5fb9626b3 --- /dev/null +++ b/test/integration/models/monitor/test_monitor.py @@ -0,0 +1,109 @@ +from test.integration.helpers import ( + get_test_label, + send_request_when_resource_available, + wait_for_condition, +) + +import pytest + +from linode_api4 import LinodeClient +from linode_api4.objects import ( + MonitorDashboard, + MonitorMetricsDefinition, + MonitorService, + MonitorServiceToken, +) + + +# List all dashboards +def test_get_all_dashboards(test_linode_client): + client = test_linode_client + dashboards = client.monitor.dashboards() + assert isinstance(dashboards[0], MonitorDashboard) + + dashboard_get = dashboards[0] + get_service_type = dashboard_get.service_type + + # Fetch Dashboard by ID + dashboard_by_id = client.load(MonitorDashboard, 1) + assert isinstance(dashboard_by_id, MonitorDashboard) + assert dashboard_by_id.id == 1 + + # #Fetch Dashboard by service_type + dashboards_by_svc = client.monitor.dashboards(service_type=get_service_type) + assert isinstance(dashboards_by_svc[0], MonitorDashboard) + assert dashboards_by_svc[0].service_type == get_service_type + + +# List supported services +def test_get_supported_services(test_linode_client): + client = test_linode_client + supported_services = client.monitor.services() + assert isinstance(supported_services[0], MonitorService) + + get_supported_service = supported_services[0].service_type + + # Get details for a particular service + service_details = client.monitor.services( + service_type=get_supported_service + ) + assert isinstance(service_details[0], MonitorService) + assert service_details[0].service_type == get_supported_service + + # Get Metric definition details for that particular service + metric_definitions = client.monitor.metric_definitions( + service_type=get_supported_service + ) + assert isinstance(metric_definitions[0], MonitorMetricsDefinition) + + +# Test Helpers +def get_db_engine_id(client: LinodeClient, engine: str): + engines = client.database.engines() + engine_id = "" + for e in engines: + if e.engine == engine: + engine_id = e.id + + return str(engine_id) + + +@pytest.fixture(scope="session") +def test_create_and_test_db(test_linode_client): + client = test_linode_client + label = get_test_label() + "-sqldb" + region = "us-ord" + engine_id = get_db_engine_id(client, "mysql") + dbtype = "g6-standard-1" + + db = client.database.mysql_create( + label=label, + region=region, + engine=engine_id, + ltype=dbtype, + cluster_size=None, + ) + + def get_db_status(): + return db.status == "active" + + # TAKES 15-30 MINUTES TO FULLY PROVISION DB + wait_for_condition(60, 2000, get_db_status) + + yield db + send_request_when_resource_available(300, db.delete) + + +def test_my_db_functionality(test_linode_client, test_create_and_test_db): + client = test_linode_client + assert test_create_and_test_db.status == "active" + + entity_id = test_create_and_test_db.id + + # create token for the particular service + token = client.monitor.create_token( + service_type="dbaas", entity_ids=[entity_id] + ) + assert isinstance(token, MonitorServiceToken) + assert len(token.token) > 0, "Token should not be empty" + assert hasattr(token, "token"), "Response object has no 'token' attribute" diff --git a/test/integration/models/vpc/test_vpc.py b/test/integration/models/vpc/test_vpc.py index 5dd14b502..0e9d27aff 100644 --- a/test/integration/models/vpc/test_vpc.py +++ b/test/integration/models/vpc/test_vpc.py @@ -56,7 +56,6 @@ def test_fails_create_vpc_invalid_data(test_linode_client): description="test description", ) assert excinfo.value.status == 400 - assert "Label must include only ASCII" in str(excinfo.value.json) def test_get_all_vpcs(test_linode_client, create_multiple_vpcs): @@ -78,7 +77,6 @@ def test_fails_update_vpc_invalid_data(create_vpc): vpc.save() assert excinfo.value.status == 400 - assert "Label must include only ASCII" in str(excinfo.value.json) def test_fails_create_subnet_invalid_data(create_vpc): @@ -88,7 +86,6 @@ def test_fails_create_subnet_invalid_data(create_vpc): create_vpc.subnet_create("test-subnet", ipv4=invalid_ipv4) assert excinfo.value.status == 400 - assert "ipv4 must be an IPv4 network" in str(excinfo.value.json) def test_fails_update_subnet_invalid_data(create_vpc_with_subnet): @@ -100,4 +97,3 @@ def test_fails_update_subnet_invalid_data(create_vpc_with_subnet): subnet.save() assert excinfo.value.status == 400 - assert "Label must include only ASCII" in str(excinfo.value.json) diff --git a/test/unit/groups/database_test.py b/test/unit/groups/database_test.py index d1939aec7..9647fed82 100644 --- a/test/unit/groups/database_test.py +++ b/test/unit/groups/database_test.py @@ -73,65 +73,6 @@ def test_database_instance(self): self.assertTrue(isinstance(db_translated, MySQLDatabase)) self.assertEqual(db_translated.ssl_connection, True) - -class MySQLDatabaseTest(ClientBaseCase): - """ - Tests methods of the MySQLDatabase class - """ - - def test_get_instances(self): - """ - Test that database types are properly handled - """ - dbs = self.client.database.mysql_instances() - - self.assertEqual(len(dbs), 1) - self.assertEqual(dbs[0].allow_list[1], "192.0.1.0/24") - self.assertEqual(dbs[0].cluster_size, 3) - self.assertEqual(dbs[0].encrypted, False) - self.assertEqual(dbs[0].engine, "mysql") - self.assertEqual( - dbs[0].hosts.primary, - "lin-123-456-mysql-mysql-primary.servers.linodedb.net", - ) - self.assertEqual( - dbs[0].hosts.secondary, - "lin-123-456-mysql-primary-private.servers.linodedb.net", - ) - self.assertEqual(dbs[0].id, 123) - self.assertEqual(dbs[0].region, "us-east") - self.assertEqual(dbs[0].updates.duration, 3) - self.assertEqual(dbs[0].version, "8.0.26") - - def test_create(self): - """ - Test that MySQL databases can be created - """ - - with self.mock_post("/databases/mysql/instances") as m: - # We don't care about errors here; we just want to - # validate the request. - try: - self.client.database.mysql_create( - "cool", - "us-southeast", - "mysql/8.0.26", - "g6-standard-1", - cluster_size=3, - ) - except Exception as e: - logger.warning( - "An error occurred while validating the request: %s", e - ) - - self.assertEqual(m.method, "post") - self.assertEqual(m.call_url, "/databases/mysql/instances") - self.assertEqual(m.call_data["label"], "cool") - self.assertEqual(m.call_data["region"], "us-southeast") - self.assertEqual(m.call_data["engine"], "mysql/8.0.26") - self.assertEqual(m.call_data["type"], "g6-standard-1") - self.assertEqual(m.call_data["cluster_size"], 3) - def test_mysql_config_options(self): """ Test that MySQL configuration options can be retrieved @@ -1320,15 +1261,86 @@ def test_postgresql_config_options(self): self.assertFalse(config["work_mem"]["requires_restart"]) self.assertEqual("integer", config["work_mem"]["type"]) + def test_get_mysql_instances(self): + """ + Test that mysql instances can be retrieved properly + """ + dbs = self.client.database.mysql_instances() -class PostgreSQLDatabaseTest(ClientBaseCase): - """ - Tests methods of the PostgreSQLDatabase class - """ + self.assertEqual(len(dbs), 1) + self.assertEqual(dbs[0].allow_list[1], "192.0.1.0/24") + self.assertEqual(dbs[0].cluster_size, 3) + self.assertEqual(dbs[0].encrypted, False) + self.assertEqual(dbs[0].engine, "mysql") + self.assertEqual( + dbs[0].hosts.primary, + "lin-123-456-mysql-mysql-primary.servers.linodedb.net", + ) + self.assertEqual( + dbs[0].hosts.secondary, + "lin-123-456-mysql-primary-private.servers.linodedb.net", + ) + self.assertEqual(dbs[0].id, 123) + self.assertEqual(dbs[0].region, "us-east") + self.assertEqual(dbs[0].updates.duration, 3) + self.assertEqual(dbs[0].version, "8.0.26") + self.assertEqual(dbs[0].engine_config.binlog_retention_period, 600) + self.assertEqual(dbs[0].engine_config.mysql.connect_timeout, 10) + self.assertEqual(dbs[0].engine_config.mysql.default_time_zone, "+03:00") + self.assertEqual(dbs[0].engine_config.mysql.group_concat_max_len, 1024) + self.assertEqual( + dbs[0].engine_config.mysql.information_schema_stats_expiry, 86400 + ) + self.assertEqual( + dbs[0].engine_config.mysql.innodb_change_buffer_max_size, 30 + ) + self.assertEqual(dbs[0].engine_config.mysql.innodb_flush_neighbors, 0) + self.assertEqual(dbs[0].engine_config.mysql.innodb_ft_min_token_size, 3) + self.assertEqual( + dbs[0].engine_config.mysql.innodb_ft_server_stopword_table, + "db_name/table_name", + ) + self.assertEqual( + dbs[0].engine_config.mysql.innodb_lock_wait_timeout, 50 + ) + self.assertEqual( + dbs[0].engine_config.mysql.innodb_log_buffer_size, 16777216 + ) + self.assertEqual( + dbs[0].engine_config.mysql.innodb_online_alter_log_max_size, + 134217728, + ) + self.assertEqual(dbs[0].engine_config.mysql.innodb_read_io_threads, 10) + self.assertTrue(dbs[0].engine_config.mysql.innodb_rollback_on_timeout) + self.assertEqual( + dbs[0].engine_config.mysql.innodb_thread_concurrency, 10 + ) + self.assertEqual(dbs[0].engine_config.mysql.innodb_write_io_threads, 10) + self.assertEqual(dbs[0].engine_config.mysql.interactive_timeout, 3600) + self.assertEqual( + dbs[0].engine_config.mysql.internal_tmp_mem_storage_engine, + "TempTable", + ) + self.assertEqual( + dbs[0].engine_config.mysql.max_allowed_packet, 67108864 + ) + self.assertEqual( + dbs[0].engine_config.mysql.max_heap_table_size, 16777216 + ) + self.assertEqual(dbs[0].engine_config.mysql.net_buffer_length, 16384) + self.assertEqual(dbs[0].engine_config.mysql.net_read_timeout, 30) + self.assertEqual(dbs[0].engine_config.mysql.net_write_timeout, 30) + self.assertEqual(dbs[0].engine_config.mysql.sort_buffer_size, 262144) + self.assertEqual( + dbs[0].engine_config.mysql.sql_mode, "ANSI,TRADITIONAL" + ) + self.assertTrue(dbs[0].engine_config.mysql.sql_require_primary_key) + self.assertEqual(dbs[0].engine_config.mysql.tmp_table_size, 16777216) + self.assertEqual(dbs[0].engine_config.mysql.wait_timeout, 28800) - def test_get_instances(self): + def test_get_postgresql_instances(self): """ - Test that database types are properly handled + Test that postgresql instances can be retrieved properly """ dbs = self.client.database.postgresql_instances() @@ -1350,31 +1362,93 @@ def test_get_instances(self): self.assertEqual(dbs[0].updates.duration, 3) self.assertEqual(dbs[0].version, "13.2") - def test_create(self): - """ - Test that PostgreSQL databases can be created - """ + print(dbs[0].engine_config.pg.__dict__) - with self.mock_post("/databases/postgresql/instances") as m: - # We don't care about errors here; we just want to - # validate the request. - try: - self.client.database.postgresql_create( - "cool", - "us-southeast", - "postgresql/13.2", - "g6-standard-1", - cluster_size=3, - ) - except Exception as e: - logger.warning( - "An error occurred while validating the request: %s", e - ) - - self.assertEqual(m.method, "post") - self.assertEqual(m.call_url, "/databases/postgresql/instances") - self.assertEqual(m.call_data["label"], "cool") - self.assertEqual(m.call_data["region"], "us-southeast") - self.assertEqual(m.call_data["engine"], "postgresql/13.2") - self.assertEqual(m.call_data["type"], "g6-standard-1") - self.assertEqual(m.call_data["cluster_size"], 3) + self.assertTrue(dbs[0].engine_config.pg_stat_monitor_enable) + self.assertEqual( + dbs[0].engine_config.pglookout.max_failover_replication_time_lag, + 1000, + ) + self.assertEqual(dbs[0].engine_config.shared_buffers_percentage, 41.5) + self.assertEqual(dbs[0].engine_config.work_mem, 4) + self.assertEqual( + dbs[0].engine_config.pg.autovacuum_analyze_scale_factor, 0.5 + ) + self.assertEqual( + dbs[0].engine_config.pg.autovacuum_analyze_threshold, 100 + ) + self.assertEqual(dbs[0].engine_config.pg.autovacuum_max_workers, 10) + self.assertEqual(dbs[0].engine_config.pg.autovacuum_naptime, 100) + self.assertEqual( + dbs[0].engine_config.pg.autovacuum_vacuum_cost_delay, 50 + ) + self.assertEqual( + dbs[0].engine_config.pg.autovacuum_vacuum_cost_limit, 100 + ) + self.assertEqual( + dbs[0].engine_config.pg.autovacuum_vacuum_scale_factor, 0.5 + ) + self.assertEqual( + dbs[0].engine_config.pg.autovacuum_vacuum_threshold, 100 + ) + self.assertEqual(dbs[0].engine_config.pg.bgwriter_delay, 200) + self.assertEqual(dbs[0].engine_config.pg.bgwriter_flush_after, 512) + self.assertEqual(dbs[0].engine_config.pg.bgwriter_lru_maxpages, 100) + self.assertEqual(dbs[0].engine_config.pg.bgwriter_lru_multiplier, 2.0) + self.assertEqual(dbs[0].engine_config.pg.deadlock_timeout, 1000) + self.assertEqual( + dbs[0].engine_config.pg.default_toast_compression, "lz4" + ) + self.assertEqual( + dbs[0].engine_config.pg.idle_in_transaction_session_timeout, 100 + ) + self.assertTrue(dbs[0].engine_config.pg.jit) + self.assertEqual(dbs[0].engine_config.pg.max_files_per_process, 100) + self.assertEqual(dbs[0].engine_config.pg.max_locks_per_transaction, 100) + self.assertEqual( + dbs[0].engine_config.pg.max_logical_replication_workers, 32 + ) + self.assertEqual(dbs[0].engine_config.pg.max_parallel_workers, 64) + self.assertEqual( + dbs[0].engine_config.pg.max_parallel_workers_per_gather, 64 + ) + self.assertEqual( + dbs[0].engine_config.pg.max_pred_locks_per_transaction, 1000 + ) + self.assertEqual(dbs[0].engine_config.pg.max_replication_slots, 32) + self.assertEqual(dbs[0].engine_config.pg.max_slot_wal_keep_size, 100) + self.assertEqual(dbs[0].engine_config.pg.max_stack_depth, 3507152) + self.assertEqual( + dbs[0].engine_config.pg.max_standby_archive_delay, 1000 + ) + self.assertEqual( + dbs[0].engine_config.pg.max_standby_streaming_delay, 1000 + ) + self.assertEqual(dbs[0].engine_config.pg.max_wal_senders, 32) + self.assertEqual(dbs[0].engine_config.pg.max_worker_processes, 64) + self.assertEqual( + dbs[0].engine_config.pg.password_encryption, "scram-sha-256" + ) + self.assertEqual(dbs[0].engine_config.pg.pg_partman_bgw_interval, 3600) + self.assertEqual( + dbs[0].engine_config.pg.pg_partman_bgw_role, "myrolename" + ) + self.assertFalse( + dbs[0].engine_config.pg.pg_stat_monitor_pgsm_enable_query_plan + ) + self.assertEqual( + dbs[0].engine_config.pg.pg_stat_monitor_pgsm_max_buckets, 10 + ) + self.assertEqual( + dbs[0].engine_config.pg.pg_stat_statements_track, "top" + ) + self.assertEqual(dbs[0].engine_config.pg.temp_file_limit, 5000000) + self.assertEqual(dbs[0].engine_config.pg.timezone, "Europe/Helsinki") + self.assertEqual( + dbs[0].engine_config.pg.track_activity_query_size, 1024 + ) + self.assertEqual(dbs[0].engine_config.pg.track_commit_timestamp, "off") + self.assertEqual(dbs[0].engine_config.pg.track_functions, "all") + self.assertEqual(dbs[0].engine_config.pg.track_io_timing, "off") + self.assertEqual(dbs[0].engine_config.pg.wal_sender_timeout, 60000) + self.assertEqual(dbs[0].engine_config.pg.wal_writer_delay, 50) diff --git a/test/unit/objects/database_test.py b/test/unit/objects/database_test.py index 8605e43c5..c5abe3a58 100644 --- a/test/unit/objects/database_test.py +++ b/test/unit/objects/database_test.py @@ -13,156 +13,11 @@ logger = logging.getLogger(__name__) -class DatabaseTest(ClientBaseCase): - """ - Tests methods of the DatabaseGroup class - """ - - def test_get_types(self): - """ - Test that database types are properly handled - """ - types = self.client.database.types() - - self.assertEqual(len(types), 1) - self.assertEqual(types[0].type_class, "nanode") - self.assertEqual(types[0].id, "g6-nanode-1") - self.assertEqual(types[0].engines.mysql[0].price.monthly, 20) - - def test_get_engines(self): - """ - Test that database engines are properly handled - """ - engines = self.client.database.engines() - - self.assertEqual(len(engines), 2) - - self.assertEqual(engines[0].engine, "mysql") - self.assertEqual(engines[0].id, "mysql/8.0.26") - self.assertEqual(engines[0].version, "8.0.26") - - self.assertEqual(engines[1].engine, "postgresql") - self.assertEqual(engines[1].id, "postgresql/10.14") - self.assertEqual(engines[1].version, "10.14") - - def test_get_databases(self): - """ - Test that databases are properly handled - """ - dbs = self.client.database.instances() - - self.assertEqual(len(dbs), 1) - self.assertEqual(dbs[0].allow_list[1], "192.0.1.0/24") - self.assertEqual(dbs[0].cluster_size, 3) - self.assertEqual(dbs[0].encrypted, False) - self.assertEqual(dbs[0].engine, "mysql") - self.assertEqual( - dbs[0].hosts.primary, - "lin-123-456-mysql-mysql-primary.servers.linodedb.net", - ) - self.assertEqual( - dbs[0].hosts.secondary, - "lin-123-456-mysql-primary-private.servers.linodedb.net", - ) - self.assertEqual(dbs[0].id, 123) - self.assertEqual(dbs[0].region, "us-east") - self.assertEqual(dbs[0].updates.duration, 3) - self.assertEqual(dbs[0].version, "8.0.26") - - def test_database_instance(self): - """ - Ensures that the .instance attribute properly translates database types - """ - - dbs = self.client.database.instances() - db_translated = dbs[0].instance - - self.assertTrue(isinstance(db_translated, MySQLDatabase)) - self.assertEqual(db_translated.ssl_connection, True) - - class MySQLDatabaseTest(ClientBaseCase): """ Tests methods of the MySQLDatabase class """ - def test_get_instances(self): - """ - Test that database types are properly handled - """ - dbs = self.client.database.mysql_instances() - - self.assertEqual(len(dbs), 1) - self.assertEqual(dbs[0].allow_list[1], "192.0.1.0/24") - self.assertEqual(dbs[0].cluster_size, 3) - self.assertEqual(dbs[0].encrypted, False) - self.assertEqual(dbs[0].engine, "mysql") - self.assertEqual( - dbs[0].hosts.primary, - "lin-123-456-mysql-mysql-primary.servers.linodedb.net", - ) - self.assertEqual( - dbs[0].hosts.secondary, - "lin-123-456-mysql-primary-private.servers.linodedb.net", - ) - self.assertEqual(dbs[0].id, 123) - self.assertEqual(dbs[0].region, "us-east") - self.assertEqual(dbs[0].updates.duration, 3) - self.assertEqual(dbs[0].version, "8.0.26") - self.assertEqual(dbs[0].engine_config.binlog_retention_period, 600) - self.assertEqual(dbs[0].engine_config.mysql.connect_timeout, 10) - self.assertEqual(dbs[0].engine_config.mysql.default_time_zone, "+03:00") - self.assertEqual(dbs[0].engine_config.mysql.group_concat_max_len, 1024) - self.assertEqual( - dbs[0].engine_config.mysql.information_schema_stats_expiry, 86400 - ) - self.assertEqual( - dbs[0].engine_config.mysql.innodb_change_buffer_max_size, 30 - ) - self.assertEqual(dbs[0].engine_config.mysql.innodb_flush_neighbors, 0) - self.assertEqual(dbs[0].engine_config.mysql.innodb_ft_min_token_size, 3) - self.assertEqual( - dbs[0].engine_config.mysql.innodb_ft_server_stopword_table, - "db_name/table_name", - ) - self.assertEqual( - dbs[0].engine_config.mysql.innodb_lock_wait_timeout, 50 - ) - self.assertEqual( - dbs[0].engine_config.mysql.innodb_log_buffer_size, 16777216 - ) - self.assertEqual( - dbs[0].engine_config.mysql.innodb_online_alter_log_max_size, - 134217728, - ) - self.assertEqual(dbs[0].engine_config.mysql.innodb_read_io_threads, 10) - self.assertTrue(dbs[0].engine_config.mysql.innodb_rollback_on_timeout) - self.assertEqual( - dbs[0].engine_config.mysql.innodb_thread_concurrency, 10 - ) - self.assertEqual(dbs[0].engine_config.mysql.innodb_write_io_threads, 10) - self.assertEqual(dbs[0].engine_config.mysql.interactive_timeout, 3600) - self.assertEqual( - dbs[0].engine_config.mysql.internal_tmp_mem_storage_engine, - "TempTable", - ) - self.assertEqual( - dbs[0].engine_config.mysql.max_allowed_packet, 67108864 - ) - self.assertEqual( - dbs[0].engine_config.mysql.max_heap_table_size, 16777216 - ) - self.assertEqual(dbs[0].engine_config.mysql.net_buffer_length, 16384) - self.assertEqual(dbs[0].engine_config.mysql.net_read_timeout, 30) - self.assertEqual(dbs[0].engine_config.mysql.net_write_timeout, 30) - self.assertEqual(dbs[0].engine_config.mysql.sort_buffer_size, 262144) - self.assertEqual( - dbs[0].engine_config.mysql.sql_mode, "ANSI,TRADITIONAL" - ) - self.assertTrue(dbs[0].engine_config.mysql.sql_require_primary_key) - self.assertEqual(dbs[0].engine_config.mysql.tmp_table_size, 16777216) - self.assertEqual(dbs[0].engine_config.mysql.wait_timeout, 28800) - def test_create(self): """ Test that MySQL databases can be created @@ -378,121 +233,6 @@ class PostgreSQLDatabaseTest(ClientBaseCase): Tests methods of the PostgreSQLDatabase class """ - def test_get_instances(self): - """ - Test that database types are properly handled - """ - dbs = self.client.database.postgresql_instances() - - self.assertEqual(len(dbs), 1) - self.assertEqual(dbs[0].allow_list[1], "192.0.1.0/24") - self.assertEqual(dbs[0].cluster_size, 3) - self.assertEqual(dbs[0].encrypted, False) - self.assertEqual(dbs[0].engine, "postgresql") - self.assertEqual( - dbs[0].hosts.primary, - "lin-0000-000-pgsql-primary.servers.linodedb.net", - ) - self.assertEqual( - dbs[0].hosts.secondary, - "lin-0000-000-pgsql-primary-private.servers.linodedb.net", - ) - self.assertEqual(dbs[0].id, 123) - self.assertEqual(dbs[0].region, "us-east") - self.assertEqual(dbs[0].updates.duration, 3) - self.assertEqual(dbs[0].version, "13.2") - - print(dbs[0].engine_config.pg.__dict__) - - self.assertTrue(dbs[0].engine_config.pg_stat_monitor_enable) - self.assertEqual( - dbs[0].engine_config.pglookout.max_failover_replication_time_lag, - 1000, - ) - self.assertEqual(dbs[0].engine_config.shared_buffers_percentage, 41.5) - self.assertEqual(dbs[0].engine_config.work_mem, 4) - self.assertEqual( - dbs[0].engine_config.pg.autovacuum_analyze_scale_factor, 0.5 - ) - self.assertEqual( - dbs[0].engine_config.pg.autovacuum_analyze_threshold, 100 - ) - self.assertEqual(dbs[0].engine_config.pg.autovacuum_max_workers, 10) - self.assertEqual(dbs[0].engine_config.pg.autovacuum_naptime, 100) - self.assertEqual( - dbs[0].engine_config.pg.autovacuum_vacuum_cost_delay, 50 - ) - self.assertEqual( - dbs[0].engine_config.pg.autovacuum_vacuum_cost_limit, 100 - ) - self.assertEqual( - dbs[0].engine_config.pg.autovacuum_vacuum_scale_factor, 0.5 - ) - self.assertEqual( - dbs[0].engine_config.pg.autovacuum_vacuum_threshold, 100 - ) - self.assertEqual(dbs[0].engine_config.pg.bgwriter_delay, 200) - self.assertEqual(dbs[0].engine_config.pg.bgwriter_flush_after, 512) - self.assertEqual(dbs[0].engine_config.pg.bgwriter_lru_maxpages, 100) - self.assertEqual(dbs[0].engine_config.pg.bgwriter_lru_multiplier, 2.0) - self.assertEqual(dbs[0].engine_config.pg.deadlock_timeout, 1000) - self.assertEqual( - dbs[0].engine_config.pg.default_toast_compression, "lz4" - ) - self.assertEqual( - dbs[0].engine_config.pg.idle_in_transaction_session_timeout, 100 - ) - self.assertTrue(dbs[0].engine_config.pg.jit) - self.assertEqual(dbs[0].engine_config.pg.max_files_per_process, 100) - self.assertEqual(dbs[0].engine_config.pg.max_locks_per_transaction, 100) - self.assertEqual( - dbs[0].engine_config.pg.max_logical_replication_workers, 32 - ) - self.assertEqual(dbs[0].engine_config.pg.max_parallel_workers, 64) - self.assertEqual( - dbs[0].engine_config.pg.max_parallel_workers_per_gather, 64 - ) - self.assertEqual( - dbs[0].engine_config.pg.max_pred_locks_per_transaction, 1000 - ) - self.assertEqual(dbs[0].engine_config.pg.max_replication_slots, 32) - self.assertEqual(dbs[0].engine_config.pg.max_slot_wal_keep_size, 100) - self.assertEqual(dbs[0].engine_config.pg.max_stack_depth, 3507152) - self.assertEqual( - dbs[0].engine_config.pg.max_standby_archive_delay, 1000 - ) - self.assertEqual( - dbs[0].engine_config.pg.max_standby_streaming_delay, 1000 - ) - self.assertEqual(dbs[0].engine_config.pg.max_wal_senders, 32) - self.assertEqual(dbs[0].engine_config.pg.max_worker_processes, 64) - self.assertEqual( - dbs[0].engine_config.pg.password_encryption, "scram-sha-256" - ) - self.assertEqual(dbs[0].engine_config.pg.pg_partman_bgw_interval, 3600) - self.assertEqual( - dbs[0].engine_config.pg.pg_partman_bgw_role, "myrolename" - ) - self.assertFalse( - dbs[0].engine_config.pg.pg_stat_monitor_pgsm_enable_query_plan - ) - self.assertEqual( - dbs[0].engine_config.pg.pg_stat_monitor_pgsm_max_buckets, 10 - ) - self.assertEqual( - dbs[0].engine_config.pg.pg_stat_statements_track, "top" - ) - self.assertEqual(dbs[0].engine_config.pg.temp_file_limit, 5000000) - self.assertEqual(dbs[0].engine_config.pg.timezone, "Europe/Helsinki") - self.assertEqual( - dbs[0].engine_config.pg.track_activity_query_size, 1024 - ) - self.assertEqual(dbs[0].engine_config.pg.track_commit_timestamp, "off") - self.assertEqual(dbs[0].engine_config.pg.track_functions, "all") - self.assertEqual(dbs[0].engine_config.pg.track_io_timing, "off") - self.assertEqual(dbs[0].engine_config.pg.wal_sender_timeout, 60000) - self.assertEqual(dbs[0].engine_config.pg.wal_writer_delay, 50) - def test_create(self): """ Test that PostgreSQL databases can be created diff --git a/test/unit/objects/monitor_test.py b/test/unit/objects/monitor_test.py new file mode 100644 index 000000000..385eaf462 --- /dev/null +++ b/test/unit/objects/monitor_test.py @@ -0,0 +1,123 @@ +import datetime +from test.unit.base import ClientBaseCase + +from linode_api4.objects import MonitorDashboard + + +class MonitorTest(ClientBaseCase): + """ + Tests the methods of MonitorServiceSupported class + """ + + def test_supported_services(self): + """ + Test the services supported by monitor + """ + service = self.client.monitor.services() + self.assertEqual(len(service), 1) + self.assertEqual(service[0].label, "Databases") + self.assertEqual(service[0].service_type, "dbaas") + + def test_dashboard_by_ID(self): + """ + Test the dashboard by ID API + """ + dashboard = self.client.load(MonitorDashboard, 1) + self.assertEqual(dashboard.type, "standard") + self.assertEqual( + dashboard.created, datetime.datetime(2024, 10, 10, 5, 1, 58) + ) + 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) + ) + self.assertEqual(dashboard.widgets[0].aggregate_function, "sum") + self.assertEqual(dashboard.widgets[0].chart_type, "area") + self.assertEqual(dashboard.widgets[0].color, "default") + self.assertEqual(dashboard.widgets[0].label, "CPU Usage") + self.assertEqual(dashboard.widgets[0].metric, "cpu_usage") + self.assertEqual(dashboard.widgets[0].size, 12) + self.assertEqual(dashboard.widgets[0].unit, "%") + self.assertEqual(dashboard.widgets[0].y_label, "cpu_usage") + + 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) + ) + 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) + ) + self.assertEqual(dashboards[0].widgets[0].aggregate_function, "sum") + 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") + self.assertEqual(dashboards[0].widgets[0].metric, "cpu_usage") + 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") + + def test_get_all_dashboards(self): + dashboards = self.client.monitor.dashboards() + self.assertEqual(dashboards[0].type, "standard") + self.assertEqual( + dashboards[0].created, datetime.datetime(2024, 10, 10, 5, 1, 58) + ) + 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) + ) + self.assertEqual(dashboards[0].widgets[0].aggregate_function, "sum") + 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") + self.assertEqual(dashboards[0].widgets[0].metric, "cpu_usage") + 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") + + def test_specific_service_details(self): + data = self.client.monitor.services(service_type="dbaas") + self.assertEqual(data[0].label, "Databases") + self.assertEqual(data[0].service_type, "dbaas") + + def test_metric_definitions(self): + + metrics = self.client.monitor.metric_definitions(service_type="dbaas") + self.assertEqual( + metrics[0].available_aggregate_functions, + ["max", "avg", "min", "sum"], + ) + self.assertEqual(metrics[0].is_alertable, True) + self.assertEqual(metrics[0].label, "CPU Usage") + self.assertEqual(metrics[0].metric, "cpu_usage") + self.assertEqual(metrics[0].metric_type, "gauge") + self.assertEqual(metrics[0].scrape_interval, "60s") + self.assertEqual(metrics[0].unit, "percent") + self.assertEqual(metrics[0].dimensions[0].dimension_label, "node_type") + self.assertEqual(metrics[0].dimensions[0].label, "Node Type") + self.assertEqual( + metrics[0].dimensions[0].values, ["primary", "secondary"] + ) + + def test_create_token(self): + + with self.mock_post("/monitor/services/dbaas/token") as m: + self.client.monitor.create_token( + service_type="dbaas", entity_ids=[189690, 188020] + ) + self.assertEqual(m.return_dct["token"], "abcdefhjigkfghh") + + with self.mock_post("/monitor/services/linode/token") as m: + self.client.monitor.create_token( + service_type="linode", entity_ids=["compute-instance-1"] + ) + self.assertEqual(m.return_dct["token"], "abcdefhjigkfghh")