diff --git a/ibm_mq/datadog_checks/ibm_mq/collectors/__init__.py b/ibm_mq/datadog_checks/ibm_mq/collectors/__init__.py index edb02b1d690a7..90b445e1aa9ca 100644 --- a/ibm_mq/datadog_checks/ibm_mq/collectors/__init__.py +++ b/ibm_mq/datadog_checks/ibm_mq/collectors/__init__.py @@ -2,6 +2,7 @@ # All rights reserved # Licensed under a 3-clause BSD style license (see LICENSE) from .channel_metric_collector import ChannelMetricCollector +from .metadata_collector import MetadataCollector from .queue_metric_collector import QueueMetricCollector -__all__ = ['ChannelMetricCollector', 'QueueMetricCollector'] +__all__ = ['ChannelMetricCollector', 'MetadataCollector', 'QueueMetricCollector'] diff --git a/ibm_mq/datadog_checks/ibm_mq/collectors/metadata_collector.py b/ibm_mq/datadog_checks/ibm_mq/collectors/metadata_collector.py new file mode 100644 index 0000000000000..691abf4a51a2e --- /dev/null +++ b/ibm_mq/datadog_checks/ibm_mq/collectors/metadata_collector.py @@ -0,0 +1,54 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) + +# TODO: change to from datadog_checks.base.utils.common import to_native_string when bumping base agent requirement +from datadog_checks.base import to_string as to_native_string + +try: + import pymqi +except ImportError as e: + pymqiException = e + pymqi = None + + +class MetadataCollector(object): + def __init__(self, log): + self.log = log + + def collect_metadata(self, queue_manager): + try: + raw_version = self._get_version(queue_manager) + self.log.debug('IBM MQ version: %s', raw_version) + return raw_version + except Exception as e: + self.log.debug("Version could not be retreived: %s", e) + return + + def _get_version(self, queue_manager): + pcf = pymqi.PCFExecute(queue_manager) + resp = pcf.MQCMD_INQUIRE_Q_MGR({pymqi.CMQCFC.MQIACF_Q_MGR_ATTRS: [pymqi.CMQC.MQCA_VERSION]}) + + try: + version = to_native_string(resp[0][pymqi.CMQC.MQCA_VERSION]) + self.log.debug("IBM MQ version from response: %s", version) + except Exception as e: + self.log.debug("Error collecting IBM MQ version: %s", e) + return None + + if version is None: + return None + return self._parse_version(version) + + @staticmethod + def _parse_version(version): + try: + major, minor, mod, fix = [int(version[i : i + 2]) for i in range(0, len(version), 2)] + return { + 'major': str(int(major)), + 'minor': str(int(minor)), + 'mod': str(int(mod)), + 'fix': str(int(fix)), + } + except Exception: + return None diff --git a/ibm_mq/datadog_checks/ibm_mq/config.py b/ibm_mq/datadog_checks/ibm_mq/config.py index a44b98c8af9d3..dfa22fcd6f628 100644 --- a/ibm_mq/datadog_checks/ibm_mq/config.py +++ b/ibm_mq/datadog_checks/ibm_mq/config.py @@ -19,7 +19,8 @@ try: import pymqi -except ImportError: +except ImportError as e: + pymqiException = e pymqi = None else: # Since pymqi is not be available/installed on win/macOS when running e2e, @@ -157,6 +158,8 @@ def _compile_tag_re(self): @staticmethod def get_channel_status_mapping(channel_status_mapping_raw): + if pymqi is None: + raise pymqiException if channel_status_mapping_raw: custom_mapping = {} for ibm_mq_status_raw, service_check_status_raw in channel_status_mapping_raw.items(): diff --git a/ibm_mq/datadog_checks/ibm_mq/ibm_mq.py b/ibm_mq/datadog_checks/ibm_mq/ibm_mq.py index 4b46f331781a9..853a802ccacef 100644 --- a/ibm_mq/datadog_checks/ibm_mq/ibm_mq.py +++ b/ibm_mq/datadog_checks/ibm_mq/ibm_mq.py @@ -2,7 +2,6 @@ # All rights reserved # Licensed under a 3-clause BSD style license (see LICENSE) - from six import iteritems from datadog_checks.base import AgentCheck @@ -10,7 +9,7 @@ from datadog_checks.ibm_mq.metrics import COUNT, GAUGE from . import connection, errors -from .collectors import ChannelMetricCollector, QueueMetricCollector +from .collectors import ChannelMetricCollector, MetadataCollector, QueueMetricCollector from .config import IBMMQConfig try: @@ -41,6 +40,7 @@ def __init__(self, *args, **kwargs): self.config, self.service_check, self.warning, self.send_metric, self.send_metrics_from_properties, self.log ) self.channel_metric_collector = ChannelMetricCollector(self.config, self.service_check, self.gauge, self.log) + self.metadata_collector = MetadataCollector(self.log) self.stats_collector = StatsCollector(self.config, self.send_metrics_from_properties, self.log) def check(self, _): @@ -52,6 +52,8 @@ def check(self, _): self.service_check(self.SERVICE_CHECK, AgentCheck.CRITICAL, self.config.tags) return + self._collect_metadata(queue_manager) + try: self.channel_metric_collector.get_pcf_channel_metrics(queue_manager) self.queue_metric_collector.collect_queue_metrics(queue_manager) @@ -66,6 +68,19 @@ def send_metric(self, metric_type, metric_name, metric_value, tags): else: self.log.warning("Unknown metric type `%s` for metric `%s`", metric_type, metric_name) + @AgentCheck.metadata_entrypoint + def _collect_metadata(self, queue_manager): + try: + version = self.metadata_collector.collect_metadata(queue_manager) + if version: + raw_version = '{}.{}.{}.{}'.format(version["major"], version["minor"], version["mod"], version["fix"]) + self.set_metadata('version', raw_version, scheme='parts', part_map=version) + self.log.debug('Found ibm_mq version: %s', raw_version) + else: + self.log.debug('Could not retrieve ibm_mq version info') + except Exception as e: + self.log.debug('Could not retrieve ibm_mq version info: %s', e) + def send_metrics_from_properties(self, properties, metrics_map, prefix, tags): for metric_name, (pymqi_type, metric_type) in iteritems(metrics_map): metric_full_name = '{}.{}'.format(prefix, metric_name) diff --git a/ibm_mq/tests/common.py b/ibm_mq/tests/common.py index f55a3816e89a2..2046d8679dcd3 100644 --- a/ibm_mq/tests/common.py +++ b/ibm_mq/tests/common.py @@ -25,6 +25,7 @@ MQ_VERSION = os.environ.get('IBM_MQ_VERSION', '9') MQ_COMPOSE_VERSION = os.environ['IBM_MQ_COMPOSE_VERSION'] +MQ_VERSION_RAW = os.environ.get('IBM_MQ_VERSION_RAW', '9.1.1.0') IS_CLUSTER = 'cluster' in MQ_COMPOSE_VERSION @@ -45,6 +46,18 @@ 'collect_statistics_metrics': True, } +INSTANCE_METADATA = { + 'channel': CHANNEL, + 'queue_manager': QUEUE_MANAGER, + 'host': HOST, + 'port': PORT, + 'username': USERNAME, + 'password': PASSWORD, + 'queues': [QUEUE], + 'channels': [CHANNEL, BAD_CHANNEL], + 'tags': ['foo:bar'], +} + INSTANCE_WITH_CONNECTION_NAME = { 'channel': CHANNEL, 'queue_manager': QUEUE_MANAGER, diff --git a/ibm_mq/tests/test_metadata.py b/ibm_mq/tests/test_metadata.py new file mode 100644 index 0000000000000..7bef8965fbe34 --- /dev/null +++ b/ibm_mq/tests/test_metadata.py @@ -0,0 +1,28 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +import pytest + +from datadog_checks.ibm_mq import IbmMqCheck + +from .common import MQ_VERSION_RAW + + +@pytest.mark.integration +def test_metadata(instance, datadog_agent): + check = IbmMqCheck('ibm_mq', {}, [instance]) + check.check_id = 'test:123' + check.check(instance) + + raw_version = MQ_VERSION_RAW + major, minor, mod, fix = raw_version.split('.') + version_metadata = { + 'version.scheme': 'ibm_mq', + 'version.major': major, + 'version.minor': minor, + 'version.mod': mod, + 'version.fix': fix, + 'version.raw': raw_version, + } + + datadog_agent.assert_metadata('test:123', version_metadata) diff --git a/ibm_mq/tox.ini b/ibm_mq/tox.ini index 436a60d807490..9afa741d6b284 100644 --- a/ibm_mq/tox.ini +++ b/ibm_mq/tox.ini @@ -27,7 +27,9 @@ setenv = LD_LIBRARY_PATH=/opt/mqm/lib64:/opt/mqm/lib:{env:LD_LIBRARY_PATH:none} 8: IBM_MQ_VERSION = 8 8: IBM_MQ_COMPOSE_VERSION = 8 + 8: IBM_MQ_VERSION_RAW = 8.0.0.4 9: IBM_MQ_VERSION = 9 9: IBM_MQ_COMPOSE_VERSION = 9 + 9: IBM_MQ_VERSION_RAW = 9.1.1.0 9cluster: IBM_MQ_VERSION = 9 9cluster: IBM_MQ_COMPOSE_VERSION = 9cluster