Skip to content

Commit be583e3

Browse files
authored
Add option to use configured host as hostname for esxi hosts (#17544)
* Add option to use configured hostname as hostname for esxi * Add changelog * Move logic to main function and add external host tag test
1 parent 3b512ed commit be583e3

File tree

7 files changed

+62
-11
lines changed

7 files changed

+62
-11
lines changed

esxi/assets/configuration/spec.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ files:
4242
value:
4343
type: boolean
4444
example: false
45+
- name: use_configured_hostname
46+
description: |
47+
If true, the check will use the configured `host` parameter for ESXi hostnames instead of the Host name.
48+
You may need to use this if you install both the vSphere check and ESXi check to avoid duplicate entries
49+
for hosts in the web application UI.
50+
value:
51+
type: boolean
52+
example: true
4553
- name: collect_per_instance_filters
4654
description: |
4755
Use this option to collect metrics tagged with instance values.

esxi/changelog.d/17544.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add option to use configured host as hostname.

esxi/datadog_checks/esxi/check.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def __init__(self, name, init_config, instances):
4444
self.host = self.instance.get("host")
4545
self.username = self.instance.get("username")
4646
self.password = self.instance.get("password")
47-
self.use_guest_hostname = self.instance.get("use_guest_hostname", False)
47+
self.use_guest_hostname = is_affirmative(self.instance.get("use_guest_hostname", False))
48+
self.use_configured_hostname = is_affirmative(self.instance.get("use_configured_hostname", False))
4849
self.excluded_host_tags = self._validate_excluded_host_tags(self.instance.get("excluded_host_tags", []))
4950
self.collect_per_instance_filters = self._parse_metric_regex_filters(
5051
self.instance.get("collect_per_instance_filters", {})
@@ -238,7 +239,7 @@ def get_available_metric_ids_for_entity(self, entity):
238239
metric_ids = [vim.PerformanceManager.MetricId(counterId=counter, instance="") for counter in counter_ids]
239240
return counter_keys_and_names, metric_ids
240241

241-
def collect_metrics_for_entity(self, metric_ids, counter_keys_and_names, entity, entity_name, metric_tags):
242+
def collect_metrics_for_entity(self, metric_ids, counter_keys_and_names, entity, hostname, metric_tags):
242243
resource_type = type(entity)
243244
resource_name = RESOURCE_TYPE_TO_NAME[resource_type]
244245
for metric_id in metric_ids:
@@ -297,7 +298,7 @@ def collect_metrics_for_entity(self, metric_ids, counter_keys_and_names, entity,
297298
self.log.debug(
298299
"Skipping metric %s for %s because no value was returned by the %s",
299300
metric_name,
300-
entity_name,
301+
hostname,
301302
resource_name,
302303
)
303304
continue
@@ -308,7 +309,7 @@ def collect_metrics_for_entity(self, metric_ids, counter_keys_and_names, entity,
308309
"Skipping metric %s for %s, because the value returned by the %s"
309310
" is negative (i.e. the metric is not yet available). values: %s",
310311
metric_name,
311-
entity_name,
312+
hostname,
312313
resource_name,
313314
list(metric_result.value),
314315
)
@@ -321,10 +322,10 @@ def collect_metrics_for_entity(self, metric_ids, counter_keys_and_names, entity,
321322
"Submit metric: name=`%s`, value=`%s`, hostname=`%s`, tags=`%s`",
322323
metric_name,
323324
most_recent_val,
324-
entity_name,
325+
hostname,
325326
all_tags,
326327
)
327-
self.gauge(metric_name, most_recent_val, hostname=entity_name, tags=all_tags)
328+
self.gauge(metric_name, most_recent_val, hostname=hostname, tags=all_tags)
328329

329330
def set_version_metadata(self):
330331
esxi_version = self.content.about.version
@@ -388,6 +389,8 @@ def check(self, _):
388389
}
389390

390391
for resource_obj, resource_props in all_resources_with_metrics.items():
392+
resource_type = type(resource_obj)
393+
resource_type_name = RESOURCE_TYPE_TO_NAME[resource_type]
391394

392395
if not is_resource_collected_by_filters(resource_obj, all_resources_with_metrics, self.resource_filters):
393396
self.log.debug(
@@ -397,16 +400,18 @@ def check(self, _):
397400

398401
hostname = resource_props.get("name")
399402

400-
resource_type = RESOURCE_TYPE_TO_NAME[type(resource_obj)]
401-
if resource_type == "vm" and self.use_guest_hostname:
403+
if resource_type == VM_RESOURCE and self.use_guest_hostname:
402404
hostname = resource_props.get("guest.hostName", hostname)
403405

406+
if resource_type == HOST_RESOURCE and self.use_configured_hostname:
407+
hostname = self.host
408+
404409
self.log.debug("Collect metrics and host tags for hostname: %s, object: %s", hostname, resource_obj)
405410

406411
tags = []
407412
parent = resource_props.get('parent')
408413

409-
if resource_type == "vm":
414+
if resource_type == VM_RESOURCE:
410415
runtime_host = resource_props.get('runtime.host')
411416
runtime_host_props = {}
412417
if runtime_host:
@@ -430,7 +435,7 @@ def check(self, _):
430435
if parent is not None:
431436
tags.extend(get_tags_recursively(parent, resource_map))
432437

433-
tags.append('esxi_type:{}'.format(resource_type))
438+
tags.append('esxi_type:{}'.format(resource_type_name))
434439

435440
metric_tags = self.tags
436441
if self.excluded_host_tags:
@@ -444,7 +449,7 @@ def check(self, _):
444449
else:
445450
self.log.debug("No host name found for %s; skipping external tag submission", resource_obj)
446451

447-
self.count(f"{resource_type}.count", 1, tags=tags, hostname=None)
452+
self.count(f"{resource_type_name}.count", 1, tags=tags, hostname=None)
448453

449454
counter_keys_and_names, metric_ids = self.get_available_metric_ids_for_entity(resource_obj)
450455
self.collect_metrics_for_entity(metric_ids, counter_keys_and_names, resource_obj, hostname, metric_tags)

esxi/datadog_checks/esxi/config_models/defaults.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ def instance_ssl_verify():
2828
return True
2929

3030

31+
def instance_use_configured_hostname():
32+
return True
33+
34+
3135
def instance_use_guest_hostname():
3236
return False

esxi/datadog_checks/esxi/config_models/instance.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class InstanceConfig(BaseModel):
7878
ssl_capath: Optional[str] = None
7979
ssl_verify: Optional[bool] = None
8080
tags: Optional[tuple[str, ...]] = None
81+
use_configured_hostname: Optional[bool] = None
8182
use_guest_hostname: Optional[bool] = None
8283
username: str
8384

esxi/datadog_checks/esxi/data/conf.yaml.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ instances:
3939
#
4040
# use_guest_hostname: false
4141

42+
## @param use_configured_hostname - boolean - optional - default: true
43+
## If true, the check will use the configured `host` parameter for ESXi hostnames instead of the Host name.
44+
## You may need to use this if you install both the vSphere check and ESXi check to avoid duplicate entries
45+
## for hosts in the web application UI.
46+
#
47+
# use_configured_hostname: true
48+
4249
## @param collect_per_instance_filters - mapping - optional
4350
## Use this option to collect metrics tagged with instance values.
4451
## Some ESXi metrics can be tagged with instance values.

esxi/tests/test_unit.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,3 +1198,28 @@ def test_vm_metrics_filters(vcsim_instance, dd_run_check, metric_filters, expect
11981198
aggregator.assert_metric('esxi.host.count')
11991199
aggregator.assert_metric('esxi.vm.count')
12001200
aggregator.assert_all_metrics_covered()
1201+
1202+
1203+
@pytest.mark.usefixtures("service_instance")
1204+
def test_use_configured_hostname(vcsim_instance, dd_run_check, aggregator, datadog_agent):
1205+
instance = copy.deepcopy(vcsim_instance)
1206+
instance['use_configured_hostname'] = True
1207+
check = EsxiCheck('esxi', {}, [instance])
1208+
dd_run_check(check)
1209+
1210+
base_tags = ["esxi_url:127.0.0.1:8989"]
1211+
aggregator.assert_metric("esxi.cpu.usage.avg", value=26, tags=base_tags, hostname="127.0.0.1:8989")
1212+
aggregator.assert_metric("esxi.mem.granted.avg", value=80, tags=base_tags, hostname="127.0.0.1:8989")
1213+
aggregator.assert_metric("esxi.host.can_connect", 1, count=1, tags=base_tags)
1214+
1215+
datadog_agent.assert_external_tags(
1216+
'127.0.0.1:8989',
1217+
{
1218+
'esxi': [
1219+
'esxi_datacenter:dc2',
1220+
'esxi_folder:folder_1',
1221+
'esxi_type:host',
1222+
'esxi_url:127.0.0.1:8989',
1223+
]
1224+
},
1225+
)

0 commit comments

Comments
 (0)