Skip to content

Commit cfc7281

Browse files
mwdd146980buraizu
andauthored
Add metric ibm_mq.channel.conns to ibm mq integration, add channel and connection metric tests (DataDog#20519)
* add metric ibm_mq.channel.conns, add channel and connection metric tests * Add changelog entry * run black formatting * apply ruff check --config ../pyproject.toml --fix * fix(ibm_mq): use correct metrics for channel status collection - Update channel metric collector to use channel_status_metrics() instead of channel_metrics() for discovered channels to properly collect buffers_rcvd metric - Update test assertions to match actual tags being sent in gauge calls - Fix unit tests in test_channel_metric_collector.py to pass This change ensures that channel status metrics like buffers_rcvd are properly collected and reported by the integration. * reformat test_channel_metric_collector.py using black * Fix channel metric collection logic and update unit tests - Updated get_pcf_channel_metrics to submit configuration metrics instead of status metrics. - Modified unit tests to verify that configuration metrics are collected for channels with empty or no connections. - Added a new test test_channel_status_metrics to ensure status metrics and connection metrics are correctly submitted. * add ibm_mq.channel.conns to metadata.csv * sort metadata.csv by metric name * - rename ibm_mq.channel.conns to ibm_mq.channel.conn_status - create new metric ibm_mq.channel.connections_active which represents total num of active conns per channel * Test commit signing * remove test_signing.txt * reformat with black * sorted metadata.csv with ddev validate metadata ibm_mq --sync * fix and sort metadata.csv; many metrics had been removed accidentally * shorten changelog entry * add config option collect_connection_metrics so that metric ibm_mq.channel.conn_status is only collected if this flag is enabled * Update ibm_mq/assets/configuration/spec.yaml Co-authored-by: Bryce Eadie <bryce.eadie@datadoghq.com> * Update ibm_mq/datadog_checks/ibm_mq/data/conf.yaml.example Co-authored-by: Bryce Eadie <bryce.eadie@datadoghq.com> * fix formatting with ddev test --fmt * format with ddev test --fmt * reformat with ddev test --fmt after upgrading ddev * move simulate_mq_conn.py, add description, add licensing comment * fix license header with ddev validate license-headers ibm_mq/tests/agent_scripts/ --fix --------- Co-authored-by: Bryce Eadie <bryce.eadie@datadoghq.com>
1 parent a1c490c commit cfc7281

13 files changed

Lines changed: 319 additions & 1 deletion

File tree

ibm_mq/assets/configuration/spec.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,17 @@ files:
187187
value:
188188
example: true
189189
type: boolean
190+
- name: collect_connection_metrics
191+
description: |
192+
Collect connection-related metrics. Metrics collected are:
193+
- connection status metrics (ibm_mq.channel.conn_status)
194+
195+
Note: Enabling this option increases tag cardinality, as the ibm_mq.channel.conn_status
196+
metric creates a new `connection` tag for each unique connection. This can lead to high
197+
cardinality if you have many active connections.
198+
value:
199+
example: false
200+
type: boolean
190201
- name: mqcd_version
191202
description: |
192203
Which channel definition version to use. Supported values are 1 to 9 including.

ibm_mq/changelog.d/20519.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add ibm_mq.channel.conn_status and ibm_mq.channel.connections_active metrics with channel and connection metric tests

ibm_mq/datadog_checks/ibm_mq/collectors/channel_metric_collector.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ def _submit_channel_status(self, queue_manager, search_channel_name, tags, chann
146146
)
147147
self.log.warning("Error getting CHANNEL status for channel %s: %s", search_channel_name, e)
148148
else:
149+
# Count active connections per channel
150+
channel_active_counts = {}
149151
for channel_info in response:
150152
channel_name = to_string(channel_info[pymqi.CMQCFC.MQCACH_CHANNEL_NAME]).strip()
151153
if channel_name in channels_to_skip:
@@ -159,6 +161,18 @@ def _submit_channel_status(self, queue_manager, search_channel_name, tags, chann
159161
channel_status = channel_info[pymqi.CMQCFC.MQIACH_CHANNEL_STATUS]
160162
self._submit_channel_count(channel_name, channel_status, channel_tags)
161163
self._submit_status_check(channel_name, channel_status, channel_tags)
164+
165+
# Count as active connection if status is MQCHS_RUNNING
166+
if channel_status == pymqi.CMQCFC.MQCHS_RUNNING:
167+
channel_active_counts[channel_name] = channel_active_counts.get(channel_name, 0) + 1
168+
# Submit the total active connections for each channel
169+
for channel_name, active_count in channel_active_counts.items():
170+
self.gauge(
171+
'{}.channel.connections_active'.format(metrics.METRIC_PREFIX),
172+
active_count,
173+
tags=tags + ["channel:{}".format(channel_name)],
174+
hostname=self.config.hostname,
175+
)
162176
finally:
163177
if pcf is not None:
164178
pcf.disconnect()
@@ -170,6 +184,19 @@ def _submit_metrics_from_properties(self, channel_info, channel_name, metrics_ma
170184
if pymqi_type not in channel_info:
171185
self.log.debug("metric '%s' not found in channel: %s", metric_name, channel_name)
172186
continue
187+
188+
# Special handling for connection metric
189+
if metric_name == 'conn_status':
190+
if not self.config.collect_connection_metrics:
191+
continue
192+
connection_name = to_string(channel_info[pymqi_type]).strip()
193+
if not connection_name:
194+
continue
195+
connection_tags = tags + ["connection:{}".format(connection_name)]
196+
self.gauge(metric_full_name, 1, tags=connection_tags, hostname=self.config.hostname)
197+
continue
198+
199+
# Regular metric handling
173200
metric_value = int(channel_info[pymqi_type])
174201
self.gauge(metric_full_name, metric_value, tags=tags, hostname=self.config.hostname)
175202

ibm_mq/datadog_checks/ibm_mq/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def __init__(self, instance, init_config):
8383

8484
self.collect_statistics_metrics = is_affirmative(instance.get('collect_statistics_metrics', False)) # type: bool
8585
self.collect_reset_queue_metrics = is_affirmative(instance.get('collect_reset_queue_metrics', True))
86+
self.collect_connection_metrics = is_affirmative(instance.get('collect_connection_metrics', True))
8687
if int(self.auto_discover_queues) + int(bool(self.queue_patterns)) + int(bool(self.queue_regex)) > 1:
8788
self.log.warning(
8889
"Configurations auto_discover_queues, queue_patterns and queue_regex are not intended to be used "

ibm_mq/datadog_checks/ibm_mq/config_models/defaults.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def instance_auto_discover_queues():
2020
return False
2121

2222

23+
def instance_collect_connection_metrics():
24+
return False
25+
26+
2327
def instance_collect_reset_queue_metrics():
2428
return True
2529

ibm_mq/datadog_checks/ibm_mq/config_models/instance.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class InstanceConfig(BaseModel):
4040
channel: str = Field(..., min_length=1)
4141
channel_status_mapping: Optional[MappingProxyType[str, Any]] = None
4242
channels: Optional[tuple[str, ...]] = None
43+
collect_connection_metrics: Optional[bool] = None
4344
collect_reset_queue_metrics: Optional[bool] = None
4445
collect_statistics_metrics: Optional[bool] = None
4546
connection_name: Optional[str] = Field(None, min_length=1)

ibm_mq/datadog_checks/ibm_mq/data/conf.yaml.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ instances:
167167
#
168168
# collect_reset_queue_metrics: true
169169

170+
## @param collect_connection_metrics - boolean - optional - default: false
171+
## Collect connection-related metrics. Metrics collected are:
172+
## - connection status metrics (ibm_mq.channel.conn_status)
173+
##
174+
## Note: Enabling this option increases tag cardinality, as the ibm_mq.channel.conn_status
175+
## metric creates a new `connection` tag for each unique connection. This can lead to high
176+
## cardinality if you have many active connections.
177+
#
178+
# collect_connection_metrics: false
179+
170180
## @param mqcd_version - number - optional - default: 6
171181
## Which channel definition version to use. Supported values are 1 to 9 including.
172182
## If you're having connection issues make sure it matches your MQ version.

ibm_mq/datadog_checks/ibm_mq/metrics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def channel_status_metrics():
109109
'batches': pymqi.CMQCFC.MQIACH_BATCHES,
110110
'current_msgs': pymqi.CMQCFC.MQIACH_CURRENT_MSGS,
111111
'indoubt_status': pymqi.CMQCFC.MQIACH_INDOUBT_STATUS,
112+
'conn_status': pymqi.CMQCFC.MQCACH_CONNECTION_NAME,
112113
}
113114

114115

ibm_mq/metadata.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ ibm_mq.channel.bytes_rcvd,gauge,,byte,,This attribute specifies the number of by
88
ibm_mq.channel.bytes_sent,gauge,,byte,,This attribute specifies the number of bytes sent (parameter identifier: `MQIACH_BYTES_SENT`).,0,ibm_mq,bytes sent,
99
ibm_mq.channel.channel_status,gauge,,,,This attribute specifies the channel status (parameter identifier: `MQIACH_CHANNEL_STATUS`).,0,ibm_mq,channel status,
1010
ibm_mq.channel.channels,gauge,,resource,,The number of active channels.,0,ibm_mq,active channel count,
11+
ibm_mq.channel.conn_status,gauge,,connection,,The connection status for the channel (parameter identifier: `MQIACH_CONNS`).,0,ibm_mq,conn status,
12+
ibm_mq.channel.connections_active,gauge,,connection,,The total number of active channel connections (instances) per channel.,0,ibm_mq,connections active,
1113
ibm_mq.channel.count,gauge,,,,Sum by status to count channels. Filter by channel and status tags to create notifications.,0,ibm_mq,channel count,
1214
ibm_mq.channel.current_msgs,gauge,,message,,This attribute specifies the number of messages in-doubt (parameter identifier: `MQIACH_CURRENT_MSGS`).,0,ibm_mq,current msgs,
1315
ibm_mq.channel.disc_interval,gauge,,second,,"This attribute is the length of time after which a channel closes down, if no message arrives during that period (parameter identifier: `DISCINT`).",0,ibm_mq,disc interval,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# (C) Datadog, Inc. 2025-present
2+
# All rights reserved
3+
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
"""
5+
IBM MQ Connection Simulation Script
6+
7+
This script simulates an active connection to an IBM MQ queue manager for manual testing purposes.
8+
It connects to a specified queue manager, puts a message to a queue, and maintains the connection
9+
for 120 seconds to simulate an active channel connection.
10+
11+
This script was used for manual testing of the IBM MQ integration PR that adds connection-related
12+
metrics (ibm_mq.channel.conn_status and ibm_mq.channel.connections_active). By creating active
13+
connections, we can verify that the new metrics properly detect and count channel connections.
14+
15+
Usage: python simulate_mq_conn.py <QMGR> <CHANNEL> <HOST> <PORT> <QUEUE> [<MESSAGE>] [<USER>] [<PASSWORD>]
16+
Example: python simulate_mq_conn.py QM1 GCP.A localhost 11414 APP.QUEUE.1 "Hello from script" admin passw0rd
17+
"""
18+
19+
import sys
20+
import time
21+
22+
import pymqi
23+
24+
# Usage: python simulate_mq_conn.py <QMGR> <CHANNEL> <HOST> <PORT> <QUEUE> [<MESSAGE>] [<USER>] [<PASSWORD>]
25+
# Example: python simulate_mq_conn.py QM1 GCP.A localhost 11414 APP.QUEUE.1 "Hello from script" admin passw0rd
26+
27+
qmgr_name = sys.argv[1] if len(sys.argv) > 1 else "QM1"
28+
channel = sys.argv[2] if len(sys.argv) > 2 else "DEV.ADMIN.SVRCONN"
29+
host = sys.argv[3] if len(sys.argv) > 3 else "localhost"
30+
port = sys.argv[4] if len(sys.argv) > 4 else "11414"
31+
queue_name = sys.argv[5] if len(sys.argv) > 5 else "APP.QUEUE.1"
32+
message = sys.argv[6] if len(sys.argv) > 6 else "Hello from pymqi!"
33+
user = sys.argv[7] if len(sys.argv) > 7 else "admin"
34+
password = sys.argv[8] if len(sys.argv) > 8 else "passw0rd"
35+
36+
conn_info = f"{host}({port})"
37+
38+
print(f"Connecting to {qmgr_name} on {host}:{port} via channel {channel} as {user}...")
39+
40+
qmgr = pymqi.connect(qmgr_name, channel, conn_info, user, password)
41+
queue = pymqi.Queue(qmgr, queue_name)
42+
43+
print(f"Putting message: {message}")
44+
queue.put(message)
45+
46+
print("Sleeping for 120 seconds to keep the connection open...")
47+
time.sleep(120)
48+
49+
queue.close()
50+
qmgr.disconnect()
51+
print("Connection closed.")

0 commit comments

Comments
 (0)