|
| 1 | +# Visit our schema definition for additional information on this file format. |
| 2 | +# https://github.com/newrelic/open-install-library/blob/main/docs/recipe-spec/recipe-spec.md#schema-definition |
| 3 | + |
| 4 | +name: nrdot-collector-ibm-mq |
| 5 | +displayName: NRDOT Collector - IBM MQ |
| 6 | +description: New Relic install recipe for IBM MQ monitoring via NRDOT Collector on Debian/Ubuntu. |
| 7 | +repository: https://github.com/newrelic/nrdot-collector-releases |
| 8 | + |
| 9 | +installTargets: |
| 10 | + - type: host |
| 11 | + os: linux |
| 12 | + platformFamily: debian |
| 13 | + - type: host |
| 14 | + os: linux |
| 15 | + platform: ubuntu |
| 16 | + |
| 17 | +keywords: |
| 18 | + - IBM MQ |
| 19 | + - IBMMQ |
| 20 | + - NRDOT |
| 21 | + - OpenTelemetry |
| 22 | + - OTel |
| 23 | + - Messaging |
| 24 | + - Queue |
| 25 | + - Linux |
| 26 | + - Debian |
| 27 | + - Ubuntu |
| 28 | + |
| 29 | +processMatch: [] |
| 30 | + |
| 31 | +preInstall: |
| 32 | + discoveryMode: |
| 33 | + - targeted |
| 34 | + |
| 35 | +inputVars: |
| 36 | + - name: NR_CLI_IBMMQ_EXPORTER_ENDPOINT |
| 37 | + prompt: "IBM MQ Prometheus exporter address (mq-metric-samples, e.g. localhost:9157): " |
| 38 | + default: "localhost:9157" |
| 39 | + - name: NR_CLI_IBMMQ_CLUSTER_NAME |
| 40 | + prompt: "IBM MQ cluster name (optional, press Enter to skip): " |
| 41 | + default: "" |
| 42 | + |
| 43 | +validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'ibmmq_%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" |
| 44 | + |
| 45 | +install: |
| 46 | + version: "3" |
| 47 | + silent: true |
| 48 | + |
| 49 | + vars: |
| 50 | + IS_SYSTEMCTL: |
| 51 | + sh: command -v systemctl | wc -l |
| 52 | + NRDOT_ARCH: |
| 53 | + sh: if [ "$(uname -m)" = "aarch64" ]; then echo "arm64"; else echo "amd64"; fi |
| 54 | + COLLECTOR_DISTRO: "nrdot-collector" |
| 55 | + CONFIG_DIR: "/etc/nrdot-collector" |
| 56 | + |
| 57 | + tasks: |
| 58 | + default: |
| 59 | + cmds: |
| 60 | + - task: write_recipe_metadata |
| 61 | + - task: assert_pre_req |
| 62 | + - task: install_nrdot |
| 63 | + - task: create_collector_config |
| 64 | + - task: configure_service |
| 65 | + - task: restart_nrdot |
| 66 | + - task: assert_nrdot_status_ok |
| 67 | + |
| 68 | + write_recipe_metadata: |
| 69 | + cmds: |
| 70 | + - | |
| 71 | + echo '{"Metadata":{"CapturedCliOutput":"true"}}' | tee {{.NR_CLI_OUTPUT}} > /dev/null |
| 72 | +
|
| 73 | + assert_pre_req: |
| 74 | + cmds: |
| 75 | + - | |
| 76 | + if [ "$(id -u)" != "0" ]; then |
| 77 | + echo "This newrelic install must be run under sudo or root" >&2 |
| 78 | + exit 3 |
| 79 | + fi |
| 80 | + - | |
| 81 | + if ! command -v curl > /dev/null 2>&1; then |
| 82 | + echo "curl is required to run the newrelic install. Please install curl and re-run the installation." >&2 |
| 83 | + exit 16 |
| 84 | + fi |
| 85 | + - | |
| 86 | + if [ "{{.IS_SYSTEMCTL}}" -eq 0 ]; then |
| 87 | + echo "systemd is required for the nrdot-collector service configuration." >&2 |
| 88 | + exit 131 |
| 89 | + fi |
| 90 | + - | |
| 91 | + # Config-only recipe: verify the existing mq_prometheus exporter is serving ibmmq_*. |
| 92 | + ENDPOINT="{{.NR_CLI_IBMMQ_EXPORTER_ENDPOINT}}" |
| 93 | + if ! curl -sf "http://${ENDPOINT}/metrics" 2>/dev/null | grep -q "^ibmmq_"; then |
| 94 | + echo "" >&2 |
| 95 | + echo "Could not read IBM MQ metrics from the exporter at http://${ENDPOINT}/metrics" >&2 |
| 96 | + echo "Make sure the mq-metric-samples (mq_prometheus) exporter is running and serving" >&2 |
| 97 | + echo "ibmmq_* metrics, then re-run. Setup: https://github.com/ibm-messaging/mq-metric-samples" >&2 |
| 98 | + exit 132 |
| 99 | + fi |
| 100 | +
|
| 101 | + install_nrdot: |
| 102 | + cmds: |
| 103 | + - | |
| 104 | + # Resolve the latest release into a variable and fail fast if it is empty. |
| 105 | + # (A pipeline's trailing `|| fallback` only guards the last command, so an |
| 106 | + # empty API response would otherwise yield an empty version and a broken |
| 107 | + # download URL that fails much later at dpkg with a misleading error.) |
| 108 | + NRDOT_VERSION=$(curl -sf "https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest" | grep '"tag_name"' | head -1 | cut -d '"' -f 4 | sed 's/^v//') |
| 109 | + if [ -z "$NRDOT_VERSION" ]; then |
| 110 | + echo "Could not determine the latest nrdot-collector release from GitHub (network issue or API rate limit). Please re-run the installation." >&2 |
| 111 | + exit 1 |
| 112 | + fi |
| 113 | + # Idempotent: skip the download if this version is already installed. |
| 114 | + if dpkg -l | grep -q "^ii.*{{.COLLECTOR_DISTRO}} "; then |
| 115 | + INSTALLED_VERSION=$(dpkg -l {{.COLLECTOR_DISTRO}} | awk '/^ii/{print $3}' | sed 's/^[0-9]*://') |
| 116 | + if [ "$INSTALLED_VERSION" = "$NRDOT_VERSION" ]; then |
| 117 | + echo "{{.COLLECTOR_DISTRO}} $NRDOT_VERSION is already installed. Skipping download." |
| 118 | + exit 0 |
| 119 | + fi |
| 120 | + fi |
| 121 | + echo "Installing NRDOT Collector v$NRDOT_VERSION..." |
| 122 | + NRDOT_DEB="/tmp/{{.COLLECTOR_DISTRO}}_${NRDOT_VERSION}_linux_{{.NRDOT_ARCH}}.deb" |
| 123 | + curl -fL --output "$NRDOT_DEB" \ |
| 124 | + "https://github.com/newrelic/nrdot-collector-releases/releases/download/${NRDOT_VERSION}/{{.COLLECTOR_DISTRO}}_${NRDOT_VERSION}_linux_{{.NRDOT_ARCH}}.deb" |
| 125 | + if [ $? -ne 0 ]; then |
| 126 | + echo "Failed to download the NRDOT Collector package (version ${NRDOT_VERSION}, arch {{.NRDOT_ARCH}})." >&2 |
| 127 | + exit 1 |
| 128 | + fi |
| 129 | + DEBIAN_FRONTEND=noninteractive dpkg -i "$NRDOT_DEB" > /dev/null |
| 130 | + if [ $? -ne 0 ]; then |
| 131 | + apt-get -o DPkg::Lock::Timeout=60 install -f -y > /dev/null |
| 132 | + if [ $? -ne 0 ]; then |
| 133 | + echo "Failed to install NRDOT Collector package." >&2 |
| 134 | + exit 1 |
| 135 | + fi |
| 136 | + fi |
| 137 | + rm -f "$NRDOT_DEB" |
| 138 | +
|
| 139 | + create_collector_config: |
| 140 | + cmds: |
| 141 | + - | |
| 142 | + COLLECTOR_CONFIG="{{.CONFIG_DIR}}/ibmmq-collector-config.yaml" |
| 143 | + ENV_FILE="{{.CONFIG_DIR}}/ibmmq-env.conf" |
| 144 | + mkdir -p "{{.CONFIG_DIR}}" |
| 145 | +
|
| 146 | + # Config uses ${env:...} expansion; values come from the env file below |
| 147 | + # (no in-place edits). target.name is derived from the detected host.id. |
| 148 | + cat > "$COLLECTOR_CONFIG" << 'EOF' |
| 149 | + extensions: |
| 150 | + health_check: |
| 151 | + endpoint: 0.0.0.0:13133 |
| 152 | + receivers: |
| 153 | + prometheus/ibmmq: |
| 154 | + config: |
| 155 | + scrape_configs: |
| 156 | + - job_name: 'ibmmq' |
| 157 | + scrape_interval: 60s |
| 158 | + scrape_timeout: 15s |
| 159 | + static_configs: |
| 160 | + - targets: ['${env:IBMMQ_EXPORTER_ENDPOINT}'] |
| 161 | + processors: |
| 162 | + resourcedetection: |
| 163 | + detectors: [env, ec2, gcp, azure, system] |
| 164 | + system: |
| 165 | + resource_attributes: |
| 166 | + host.id: |
| 167 | + enabled: true |
| 168 | + host.name: |
| 169 | + enabled: true |
| 170 | + filter/ibmmq-overhead: |
| 171 | + metrics: |
| 172 | + exclude: |
| 173 | + match_type: regexp |
| 174 | + metric_names: |
| 175 | + - "^go_.*" |
| 176 | + - "^process_.*" |
| 177 | + - "^promhttp_.*" |
| 178 | + - "^scrape_.*" |
| 179 | + filter/ibmmq-queues: |
| 180 | + metrics: |
| 181 | + datapoint: |
| 182 | + - 'attributes["queue"] != nil and IsMatch(attributes["queue"], "^SYSTEM[.](ADMIN[.]|MQSC[.]|DEFAULT[.]|AUTH[.]|CHANNEL[.]|CHLAUTH[.]|CICS[.]|SYNCPOINT[.]|INTERNAL[.]|PENDING[.]|PROTECTION[.]|BROKER[.]|AMQP[.]|DOTNET[.]|REST[.]|RETAINED[.]|SELECTION[.]|DURABLE[.]|HIERARCHY[.]|DDELAY[.])")' |
| 183 | + - 'attributes["queue"] != nil and IsMatch(attributes["queue"], "^SYSTEM[.]CLUSTER[.](COMMAND|HISTORY)[.]QUEUE$")' |
| 184 | + - 'attributes["queue"] != nil and IsMatch(attributes["queue"], "^AMQ[.]")' |
| 185 | + transform/ibmmq-cleanup: |
| 186 | + metric_statements: |
| 187 | + - context: resource |
| 188 | + statements: |
| 189 | + - set(attributes["target.name"], attributes["host.id"]) where attributes["host.id"] != nil |
| 190 | + - set(attributes["cluster.name"], "${env:IBMMQ_CLUSTER_NAME}") where "${env:IBMMQ_CLUSTER_NAME}" != "" |
| 191 | + - delete_key(attributes, "service.name") |
| 192 | + - delete_key(attributes, "url.scheme") |
| 193 | + - context: datapoint |
| 194 | + statements: |
| 195 | + - delete_key(attributes, "instance") |
| 196 | + - delete_key(attributes, "job") |
| 197 | + memory_limiter/ibmmq: |
| 198 | + check_interval: 1s |
| 199 | + limit_mib: 512 |
| 200 | + spike_limit_mib: 256 |
| 201 | + cumulativetodelta/ibmmq: {} |
| 202 | + batch/ibmmq: |
| 203 | + send_batch_size: 1000 |
| 204 | + timeout: 200ms |
| 205 | + exporters: |
| 206 | + otlp/ibmmq: |
| 207 | + endpoint: ${env:NEW_RELIC_OTLP_ENDPOINT} |
| 208 | + headers: |
| 209 | + api-key: ${env:NEW_RELIC_LICENSE_KEY} |
| 210 | + compression: gzip |
| 211 | + service: |
| 212 | + extensions: [health_check] |
| 213 | + pipelines: |
| 214 | + metrics/ibmmq: |
| 215 | + receivers: [prometheus/ibmmq] |
| 216 | + processors: [memory_limiter/ibmmq, resourcedetection, filter/ibmmq-overhead, filter/ibmmq-queues, transform/ibmmq-cleanup, cumulativetodelta/ibmmq, batch/ibmmq] |
| 217 | + exporters: [otlp/ibmmq] |
| 218 | + EOF |
| 219 | +
|
| 220 | + if [ "{{.NEW_RELIC_REGION}}" = "EU" ]; then |
| 221 | + OTLP_ENDPOINT="https://otlp.eu01.nr-data.net:4317" |
| 222 | + elif [ "{{.NEW_RELIC_REGION}}" = "staging" ]; then |
| 223 | + OTLP_ENDPOINT="https://staging-otlp.nr-data.net:4317" |
| 224 | + elif [ "{{.NEW_RELIC_REGION}}" = "JP" ]; then |
| 225 | + OTLP_ENDPOINT="https://otlp.jp.nr-data.net:4317" |
| 226 | + else |
| 227 | + OTLP_ENDPOINT="https://otlp.nr-data.net:4317" |
| 228 | + fi |
| 229 | +
|
| 230 | + printf "IBMMQ_EXPORTER_ENDPOINT=%s\n" "{{.NR_CLI_IBMMQ_EXPORTER_ENDPOINT}}" > "$ENV_FILE" |
| 231 | + printf "IBMMQ_CLUSTER_NAME=%s\n" "{{.NR_CLI_IBMMQ_CLUSTER_NAME}}" >> "$ENV_FILE" |
| 232 | + printf "NEW_RELIC_OTLP_ENDPOINT=%s\n" "$OTLP_ENDPOINT" >> "$ENV_FILE" |
| 233 | + printf "NEW_RELIC_LICENSE_KEY=%s\n" "{{.NEW_RELIC_LICENSE_KEY}}" >> "$ENV_FILE" |
| 234 | + chmod 600 "$ENV_FILE" |
| 235 | + echo "Collector config written to $COLLECTOR_CONFIG" |
| 236 | +
|
| 237 | + configure_service: |
| 238 | + cmds: |
| 239 | + - | |
| 240 | + DROPIN_DIR="/etc/systemd/system/{{.COLLECTOR_DISTRO}}.service.d" |
| 241 | + mkdir -p "$DROPIN_DIR" |
| 242 | + NRDOT_BIN=$(command -v {{.COLLECTOR_DISTRO}} 2>/dev/null || echo "/usr/bin/{{.COLLECTOR_DISTRO}}") |
| 243 | + { |
| 244 | + printf '[Service]\n' |
| 245 | + printf 'EnvironmentFile={{.CONFIG_DIR}}/ibmmq-env.conf\n' |
| 246 | + printf 'ExecStart=\n' |
| 247 | + printf 'ExecStart=%s --config {{.CONFIG_DIR}}/ibmmq-collector-config.yaml\n' "$NRDOT_BIN" |
| 248 | + } > "$DROPIN_DIR/ibmmq-config.conf" |
| 249 | + echo "Systemd drop-in configured." |
| 250 | +
|
| 251 | + restart_nrdot: |
| 252 | + cmds: |
| 253 | + - | |
| 254 | + systemctl daemon-reload |
| 255 | + systemctl enable {{.COLLECTOR_DISTRO}}.service |
| 256 | + systemctl restart {{.COLLECTOR_DISTRO}}.service |
| 257 | +
|
| 258 | + assert_nrdot_status_ok: |
| 259 | + cmds: |
| 260 | + - | |
| 261 | + MAX_RETRIES=30 |
| 262 | + TRIES=0 |
| 263 | + echo "Waiting for NRDOT Collector to start..." |
| 264 | + while [ $TRIES -lt $MAX_RETRIES ]; do |
| 265 | + TRIES=$((TRIES + 1)) |
| 266 | + if systemctl is-active --quiet {{.COLLECTOR_DISTRO}}.service && curl -sf http://localhost:13133 > /dev/null 2>&1; then |
| 267 | + echo "NRDOT Collector is running and healthy." |
| 268 | + exit 0 |
| 269 | + fi |
| 270 | + sleep 2 |
| 271 | + done |
| 272 | + echo "NRDOT Collector did not start in time." >&2 |
| 273 | + journalctl -u {{.COLLECTOR_DISTRO}}.service --no-pager -n 50 >&2 |
| 274 | + exit 31 |
| 275 | +
|
| 276 | +postInstall: |
| 277 | + info: |2 |
| 278 | + IBM MQ OTel Collector config: /etc/nrdot-collector/ibmmq-collector-config.yaml |
| 279 | + Environment file: /etc/nrdot-collector/ibmmq-env.conf |
| 280 | + Service status: systemctl status nrdot-collector |
| 281 | + View logs: journalctl -u nrdot-collector -f |
0 commit comments