Skip to content

Submit database_hostname with database_instance #18969

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

Merged
merged 27 commits into from
Nov 25, 2024
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
1 change: 1 addition & 0 deletions mysql/changelog.d/18969.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Submit database_hostname with database instance and metrics for MySQL, Postgres, and SQLServer
11 changes: 11 additions & 0 deletions mysql/datadog_checks/mysql/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def __init__(self, name, init_config, instances):
self.is_mariadb = None
self._resolved_hostname = None
self._agent_hostname = None
self._database_hostname = None
self._is_aurora = None
self._config = MySQLConfig(self.instance, init_config)
self.tags = self._config.tags
Expand Down Expand Up @@ -170,7 +171,16 @@ def agent_hostname(self):
self._agent_hostname = datadog_agent.get_hostname()
return self._agent_hostname

@property
def database_hostname(self):
# type: () -> str
if self._database_hostname is None:
self._database_hostname = self.resolve_db_host()
return self._database_hostname

def set_resource_tags(self):
self.tags.append("database_hostname:{}".format(self.database_hostname))

if self.cloud_metadata.get("gcp") is not None:
self.tags.append(
"dd.internal.resource:gcp_sql_database_instance:{}:{}".format(
Expand Down Expand Up @@ -1303,6 +1313,7 @@ def _send_database_instance_metadata(self):
event = {
"host": self.resolved_hostname,
"port": self._config.port,
"database_hostname": self.database_hostname,
"agent_version": datadog_agent.get_version(),
"dbms": "mysql",
"kind": "database_instance",
Expand Down
19 changes: 15 additions & 4 deletions mysql/tests/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,35 @@
# Licensed under a 3-clause BSD style license (see LICENSE)
from . import common

DATABASE_INSTANCE_RESOURCE_TAG = 'dd.internal.resource:database_instance:{hostname}'

def database_instance_resource_tags(hostname):
return [f'dd.internal.resource:database_instance:{hostname}', f'database_hostname:{hostname}']


METRIC_TAGS = ['tag1:value1', 'tag2:value2']
METRIC_TAGS_WITH_RESOURCE = [
'tag1:value1',
'tag2:value2',
DATABASE_INSTANCE_RESOURCE_TAG.format(hostname='stubbed.hostname'),
*database_instance_resource_tags('stubbed.hostname'),
'dbms_flavor:{}'.format(common.MYSQL_FLAVOR.lower()),
]
SC_TAGS = [
'port:' + str(common.PORT),
'tag1:value1',
'tag2:value2',
]
SC_TAGS_MIN = ['port:' + str(common.PORT), DATABASE_INSTANCE_RESOURCE_TAG.format(hostname='stubbed.hostname')]
SC_TAGS_MIN = [
'port:' + str(common.PORT),
*database_instance_resource_tags('stubbed.hostname'),
]
SC_TAGS_REPLICA = [
'port:' + str(common.SLAVE_PORT),
'tag1:value1',
'tag2:value2',
'dd.internal.resource:database_instance:stubbed.hostname',
'database_hostname:stubbed.hostname',
]
SC_FAILURE_TAGS = [
'port:unix_socket',
*database_instance_resource_tags('stubbed.hostname'),
]
SC_FAILURE_TAGS = ['port:unix_socket', DATABASE_INSTANCE_RESOURCE_TAG.format(hostname='stubbed.hostname')]
1 change: 1 addition & 0 deletions mysql/tests/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ def test_collect_schemas(aggregator, dd_run_check, dbm_instance):
assert schema_event.get("dbms_version") is not None
assert (schema_event.get("flavor") == "MariaDB") or (schema_event.get("flavor") == "MySQL")
assert sorted(schema_event["tags"]) == [
'database_hostname:stubbed.hostname',
'dbms_flavor:{}'.format(common.MYSQL_FLAVOR.lower()),
'dd.internal.resource:database_instance:stubbed.hostname',
'port:13306',
Expand Down
16 changes: 8 additions & 8 deletions mysql/tests/test_mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def test_complex_config(aggregator, dd_run_check, instance_complex):

_assert_complex_config(
aggregator,
tags.SC_TAGS + [tags.DATABASE_INSTANCE_RESOURCE_TAG.format(hostname='stubbed.hostname')],
tags.SC_TAGS + tags.database_instance_resource_tags('stubbed.hostname'),
tags.METRIC_TAGS_WITH_RESOURCE,
)
aggregator.assert_metrics_using_metadata(
Expand All @@ -116,8 +116,8 @@ def test_e2e(dd_agent_check, dd_default_hostname, instance_complex):
aggregator = dd_agent_check(instance_complex)
_assert_complex_config(
aggregator,
tags.SC_TAGS + [tags.DATABASE_INSTANCE_RESOURCE_TAG.format(hostname=dd_default_hostname)],
tags.METRIC_TAGS + ['dbms_flavor:{}'.format(MYSQL_FLAVOR.lower())],
tags.SC_TAGS + tags.database_instance_resource_tags(dd_default_hostname),
tags.METRIC_TAGS + [f'database_hostname:{dd_default_hostname}', 'dbms_flavor:{}'.format(MYSQL_FLAVOR.lower())],
hostname=dd_default_hostname,
e2e=True,
)
Expand Down Expand Up @@ -413,12 +413,10 @@ def test_correct_hostname(dbm_enabled, reported_hostname, expected_hostname, agg
with mock.patch('datadog_checks.mysql.MySql.resolve_db_host', return_value='resolved.hostname') as resolve_db_host:
mysql_check = MySql(common.CHECK_NAME, {}, [instance_basic])
dd_run_check(mysql_check)
if reported_hostname:
assert resolve_db_host.called is False, 'Expected resolve_db_host.called to be False'
else:
assert resolve_db_host.called is True
assert resolve_db_host.called is True

expected_tags = [
'database_hostname:{}'.format(mysql_check.database_hostname),
'server:{}'.format(HOST),
'port:{}'.format(PORT),
'dd.internal.resource:database_instance:{}'.format(expected_hostname),
Expand Down Expand Up @@ -709,7 +707,8 @@ def test_set_resources(aggregator, dd_run_check, instance_basic, cloud_metadata,
for m in metric_names:
aggregator.assert_metric_has_tag("mysql.net.connections", m)
aggregator.assert_metric_has_tag(
"mysql.net.connections", tags.DATABASE_INSTANCE_RESOURCE_TAG.format(hostname=mysql_check.resolved_hostname)
"mysql.net.connections",
f'dd.internal.resource:database_instance:{mysql_check.resolved_hostname}',
)


Expand Down Expand Up @@ -792,6 +791,7 @@ def test_propagate_agent_tags(
expected_tags = (
instance_basic.get('tags', [])
+ [
'database_hostname:stubbed.hostname',
'server:{}'.format(HOST),
'port:{}'.format(PORT),
'dd.internal.resource:database_instance:forced_hostname',
Expand Down
2 changes: 2 additions & 0 deletions mysql/tests/test_query_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def _run_blocking(conn):
assert activity['ddsource'] == 'mysql'
assert activity['ddagentversion'], "missing agent version"
assert set(activity['ddtags']) == {
'database_hostname:stubbed.hostname',
'tag1:value1',
'tag2:value2',
'port:13306',
Expand Down Expand Up @@ -525,6 +526,7 @@ def test_events_wait_current_disabled_no_warning_azure_flexible_server(
# directly to metrics-intake, so they should also be properly tagged with a resource
def _expected_dbm_job_err_tags(dbm_instance):
return dbm_instance['tags'] + [
'database_hostname:stubbed.hostname',
'job:query-activity',
'port:{}'.format(PORT),
'dd.internal.resource:database_instance:stubbed.hostname',
Expand Down
2 changes: 2 additions & 0 deletions mysql/tests/test_statements.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,7 @@ def test_async_job_cancel(aggregator, dd_run_check, dbm_instance):

def _expected_dbm_instance_tags(dbm_instance):
return dbm_instance.get('tags', []) + [
'database_hostname:{}'.format('stubbed.hostname'),
'server:{}'.format(common.HOST),
'port:{}'.format(common.PORT),
'dbms_flavor:{}'.format(MYSQL_FLAVOR.lower()),
Expand All @@ -864,6 +865,7 @@ def _expected_dbm_instance_tags(dbm_instance):
# directly to metrics-intake, so they should also be properly tagged with a resource
def _expected_dbm_job_err_tags(dbm_instance):
return dbm_instance['tags'] + [
'database_hostname:{}'.format('stubbed.hostname'),
'port:{}'.format(common.PORT),
'server:{}'.format(common.HOST),
'dd.internal.resource:database_instance:stubbed.hostname',
Expand Down
38 changes: 34 additions & 4 deletions mysql/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,44 @@ def cursor(self):
@pytest.mark.parametrize(
'disable_generic_tags, hostname, expected_tags',
[
(True, None, {'port:unix_socket', 'dd.internal.resource:database_instance:stubbed.hostname'}),
(
True,
None,
{
'port:unix_socket',
'database_hostname:stubbed.hostname',
'dd.internal.resource:database_instance:stubbed.hostname',
},
),
(
False,
None,
{'port:unix_socket', 'server:localhost', 'dd.internal.resource:database_instance:stubbed.hostname'},
{
'port:unix_socket',
'server:localhost',
'database_hostname:stubbed.hostname',
'dd.internal.resource:database_instance:stubbed.hostname',
},
),
(
True,
'foo',
{
'port:unix_socket',
'database_hostname:stubbed.hostname',
'dd.internal.resource:database_instance:stubbed.hostname',
},
),
(
False,
'foo',
{
'port:unix_socket',
'server:foo',
'database_hostname:stubbed.hostname',
'dd.internal.resource:database_instance:stubbed.hostname',
},
),
(True, 'foo', {'port:unix_socket', 'dd.internal.resource:database_instance:stubbed.hostname'}),
(False, 'foo', {'port:unix_socket', 'server:foo', 'dd.internal.resource:database_instance:stubbed.hostname'}),
],
)
def test_service_check(disable_generic_tags, expected_tags, hostname):
Expand Down
1 change: 1 addition & 0 deletions postgres/changelog.d/18969.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Submit database_hostname with database instance and metrics for MySQL, Postgres, and SQLServer
11 changes: 11 additions & 0 deletions postgres/datadog_checks/postgres/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def __init__(self, name, init_config, instances):
super(PostgreSql, self).__init__(name, init_config, instances)
self._resolved_hostname = None
self._agent_hostname = None
self._database_hostname = None
self._db = None
self.version = None
self.raw_version = None
Expand Down Expand Up @@ -168,6 +169,8 @@ def _build_autodiscovery(self):
return discovery

def set_resource_tags(self):
self.tags.append("database_hostname:{}".format(self.database_hostname))

if self.cloud_metadata.get("gcp") is not None:
self.tags.append(
"dd.internal.resource:gcp_sql_database_instance:{}:{}".format(
Expand Down Expand Up @@ -476,6 +479,13 @@ def agent_hostname(self):
self._agent_hostname = datadog_agent.get_hostname()
return self._agent_hostname

@property
def database_hostname(self):
# type: () -> str
if self._database_hostname is None:
self._database_hostname = self.resolve_db_host()
return self._database_hostname

def resolve_db_host(self):
return agent_host_resolver(self._config.host)

Expand Down Expand Up @@ -912,6 +922,7 @@ def _send_database_instance_metadata(self):
event = {
"host": self.resolved_hostname,
"port": self._config.port,
"database_hostname": self.database_hostname,
"agent_version": datadog_agent.get_version(),
"dbms": "postgres",
"kind": "database_instance",
Expand Down
2 changes: 1 addition & 1 deletion postgres/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def _get_expected_tags(
role='master',
**kwargs,
):
base_tags = pg_instance['tags'] + [f'port:{pg_instance["port"]}']
base_tags = pg_instance['tags'] + [f'port:{pg_instance["port"]}'] + [f'database_hostname:{check.database_hostname}']
if role:
base_tags.append(f'replication_role:{role}')
if with_db:
Expand Down
3 changes: 3 additions & 0 deletions postgres/tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# All rights reserved
# Licensed under Simplified BSD License (see LICENSE)

import socket

import pytest

from .common import _get_expected_tags, check_bgw_metrics, check_common_metrics
Expand All @@ -23,6 +25,7 @@ def test_e2e(check, dd_agent_check, pg_instance):
cur.execute("SHOW cluster_name;")
check.cluster_name = cur.fetchone()[0]

check._database_hostname = socket.gethostname().lower()
expected_tags = _get_expected_tags(check, pg_instance, with_host=False)
check_bgw_metrics(aggregator, expected_tags)
check_common_metrics(aggregator, expected_tags=expected_tags, count=None)
7 changes: 2 additions & 5 deletions postgres/tests/test_pg_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ def test_activity_vacuum_excluded(aggregator, integration_check, pg_instance):
thread.join()


@pytest.mark.flaky(max_runs=5)
@pytest.mark.flaky(max_runs=10)
def test_backend_transaction_age(aggregator, integration_check, pg_instance):
pg_instance['collect_activity_metrics'] = True
check = integration_check(pg_instance)
Expand Down Expand Up @@ -731,10 +731,7 @@ def test_correct_hostname(dbm_enabled, reported_hostname, expected_hostname, agg
) as resolve_db_host:
check = PostgreSql('test_instance', {}, [pg_instance])
check.run()
if reported_hostname:
assert resolve_db_host.called is False, 'Expected resolve_db_host.called to be False'
else:
assert resolve_db_host.called is True
assert resolve_db_host.called is True

expected_tags_no_db = _get_expected_tags(check, pg_instance, server=HOST)
expected_tags_with_db = expected_tags_no_db + ['db:datadog_test']
Expand Down
2 changes: 2 additions & 0 deletions postgres/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def test_query_timeout_connection_string(aggregator, integration_check, pg_insta
'port:5432',
'foo:bar',
'dd.internal.resource:database_instance:stubbed.hostname',
'database_hostname:stubbed.hostname',
},
),
(
Expand All @@ -152,6 +153,7 @@ def test_query_timeout_connection_string(aggregator, integration_check, pg_insta
'port:5432',
'server:localhost',
'dd.internal.resource:database_instance:stubbed.hostname',
'database_hostname:stubbed.hostname',
},
),
],
Expand Down
1 change: 1 addition & 0 deletions sqlserver/changelog.d/18969.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Submit database_hostname with database instance and metrics for MySQL, Postgres, and SQLServer
12 changes: 12 additions & 0 deletions sqlserver/datadog_checks/sqlserver/sqlserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def __init__(self, name, init_config, instances):

self._resolved_hostname = None
self._agent_hostname = None
self._database_hostname = None
self.connection = None
self.failed_connections = {}
self.instance_metrics = []
Expand Down Expand Up @@ -202,6 +203,8 @@ def set_resolved_hostname_metadata(self):
self.set_metadata("resolved_hostname", self.resolved_hostname)

def set_resource_tags(self):
self.tags.append("database_hostname:{}".format(self.database_hostname))

if self._config.cloud_metadata.get("gcp") is not None:
self.tags.append(
"dd.internal.resource:gcp_sql_database_instance:{}:{}".format(
Expand Down Expand Up @@ -284,6 +287,14 @@ def set_resolved_hostname(self):
def resolved_hostname(self):
return self._resolved_hostname

@property
def database_hostname(self):
# type: () -> str
if self._database_hostname is None:
host, _ = split_sqlserver_host_port(self.instance.get("host"))
self._database_hostname = resolve_db_host(host)
return self._database_hostname

def load_static_information(self):
engine_edition_reloaded = False
expected_keys = {STATIC_INFO_VERSION, STATIC_INFO_MAJOR_VERSION, STATIC_INFO_ENGINE_EDITION, STATIC_INFO_RDS}
Expand Down Expand Up @@ -959,6 +970,7 @@ def _send_database_instance_metadata(self):
if self.resolved_hostname not in self._database_instance_emitted:
event = {
"host": self.resolved_hostname,
"database_hostname": self.database_hostname,
"agent_version": datadog_agent.get_version(),
"dbms": "sqlserver",
"kind": "database_instance",
Expand Down
Loading