See www.zabbix.com for the official Zabbix site.

Docs/howto/zabbix get jmx

From Zabbix.org
Jump to: navigation, search
Note: zabbix_get cannot be used to query Java gateway since versions 2.2.17, 3.0.7, 3.2.3 and 3.3.0. See ZBX-11528 for more detail.
Note: The protocol for JMX items has been changed in Zabbix 3.4. See the upgrade notes for more detail.

Intro

While an undocumented feature, you can actually use the zabbix_get command to test out the Zabbix Java gateway. This is very helpful to be able to manually test out items, without having to wait for Zabbix to automatically poll them, just like you can with zabbix_get for items from the agent.

Script

Below is a small Bash script that you can use to wrap the zabbix_get command

#!/usr/bin/env bash

ZBXGET="/usr/bin/zabbix_get"
if [ $# != 5 ]
then
    echo "Usage: $0 <JAVA_GATEWAY_HOST> <JAVA_GATEWAY_PORT> <JMX_SERVER> <JMX_PORT> <KEY>"
    exit;
fi

QUERY="{\"request\": \"java gateway jmx\",\"conn\": \"$3\",\"port\": $4,\"keys\": [\"$5\"]}"

$ZBXGET -s $1 -p $2 -k "$QUERY"

Copy the above code into a new file, make it executable and when you call the script you will see the JSON response from the gateway. Example below:

user@host # ./zabbix_get_jmx localhost 10052 java_application_server 9001 'jmx[\"java.lang:type=Threading\",ThreadCount]'
{"response":"success","data":[{"value":"2516"}]}

NOTE: Its important to correctly escape the quotes in the keys section, or the script will fail with an error, that it was unable to parse the JSON object.

Another script

But why?! Why would you use undocumented features? Here is a script that mimics the one above (including requirement to escape the quotes manually) but does not depend on zabbix_get.

#!/usr/bin/env bash

if [ $# != 5 ]
then
	echo "Usage: $0 <JAVA_GATEWAY_HOST> <JAVA_GATEWAY_PORT> <JMX_SERVER> <JMX_PORT> <KEY>"
	exit;
fi

# create connection
exec 3<>/dev/tcp/$1/$2

# compose message
MSG="{\"request\": \"java gateway jmx\", \"conn\": \"$3\", \"port\": $4, \"keys\": [\"$5\"]}"

# write message length as zero-padded 16-digit hexadecimal number
printf -v LEN '%016x' "${#MSG}"

# prepare message length in little endian representation
BYTES=""
for i in {0..14..2}
do
	BYTES="\\x${LEN:$i:2}$BYTES"
done

# prepend protocol header and message length
printf "ZBXD\\1$BYTES%s" "$MSG" >&3

# output the result skipping 5 bytes of "ZBXD\\1" header and 8 bytes of message length
tail -c+13 <&3

Yet another script -- A Ruby version

Takes the same parameters as the previous scripts but named (using --long-form) and offers a --new-protocol option which is compatible with Zabbix v3.4.

#!/usr/bin/ruby
# vim: set ai ts=2 sw=2 expandtab ft=ruby syn=ruby :
# Author: Nour Sharabash <nour.sharabash@gmail.com>
require 'json'
require 'socket'
require 'getoptlong'

args = GetoptLong.new(
  [ '--java-gateway-host', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--java-gateway-port', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--jmx-server', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--jmx-port', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--key', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--jmx-user', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--jmx-pass', GetoptLong::OPTIONAL_ARGUMENT ],
  [ '--new-protocol', GetoptLong::OPTIONAL_ARGUMENT ]
)

query = {'request': 'java gateway jmx'}
gateway = {}
args.each do |opt, arg|
  case opt
    when '--java-gateway-host'
      gateway['host'] = arg
    when '--java-gateway-port'
      gateway['port'] = arg.to_i
    when '--jmx-server'
      query['conn'] = arg
    when '--jmx-port'
      query['port'] = arg.to_i
    when '--key'
      query['keys'] = [arg]
    when '--jmx-user'
      query['username'] = arg
    when '--jmx-pass'
      query['password'] = arg
    when '--new-protocol'
      conn = query.delete 'conn'
      port = query.delete 'port'
      query['jmx_endpoint'] = "service:jmx:rmi:///jndi/rmi://#{conn}:#{port}/jmxrmi"
  end
end
query_str = JSON.dump(query)
query_len = query_str.length
query_len_hex = '%.16x' % query_len
query_len_bin = query_len_hex.gsub(/^(..)(..)(..)(..)(..)(..)(..)(..)/, '\\x\8\\x\7\\x\6\\x\5\\x\4\\x\3\\x\2\\x\1')
query_bin = eval('"'+ "ZBXD\\x01#{query_len_bin}" '"').bytes.pack('C*') + query_str

s = TCPSocket.new(gateway['host'], gateway['port'])
s.write query_bin
size = 1024
data = s.read(size)
full = ''
started = false
while data and data.length
  if not started
    if data.index('{')
      data = data[data.index('{'), data.length]
      started = true
    else
      data = ''
    end
  end
  if started
    full += data
  end
  data = s.read(size)
end
s.close
puts full

Yet another script -- a Python version

Takes the same parameters as the previous scripts but named (using --long-form) and offers a --new-protocol option which is compatible with Zabbix v3.4.

#!/usr/bin/env python3
# vim: set ai ts=2 sw=2 expandtab :
# Author: Nour Sharabash <nour.sharabash@gmail.com>

import argparse, sys, json, re, socket

args = None
def parse_args():
  global args
  parser = argparse.ArgumentParser()
  parser.add_argument("--java-gateway-host")
  parser.add_argument("--java-gateway-port")
  parser.add_argument("--jmx-server")
  parser.add_argument("--jmx-port")
  parser.add_argument("--key")
  parser.add_argument("--jmx-user")
  parser.add_argument("--jmx-pass")
  parser.add_argument("--new-protocol", default=False, action='store_true')
  args = parser.parse_args(sys.argv[1:])

def main():
  parse_args()
  query = { 'request': 'java gateway jmx',
             'conn': args.jmx_server, 'port': int(args.jmx_port),
             'keys': [args.key] }
  if args.jmx_user and args.jmx_pass:
    query['username'] = args.jmx_user
    query['password'] = args.jmx_pass
  if args.new_protocol:
    conn = query.pop('conn')
    port = query.pop('port')
    query['jmx_endpoint'] = 'service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi' % (conn, port)


  query_str = json.dumps(query)
  query_len = len(query_str)
  query_len_hex = '%.16x' % query_len
  query_len_bin = re.sub(r'^(..)(..)(..)(..)(..)(..)(..)(..)', '\\x\\8\\x\\7\\x\\6\\x\\5\\x\\4\\x\\3\\x\\2\\x\\1', query_len_hex)
  query_bin = "ZBXD\\x01%s" % (query_len_bin)
  query_bin = query_bin.encode('latin-1').decode('unicode_escape')
  query_bin += query_str

  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((args.java_gateway_host, int(args.java_gateway_port)))
    s.send(bytes(query_bin, 'latin-1'))
    size = 1024
    data = s.recv(size)
    full = ''
    started = False
    while len(data):
      data = str(data, 'latin-1')
      if not started:
        if '{' in data:
          data = data[data.index('{'):]
          started = True
        else:
          data = ''
      if started:
        full += data
      data = s.recv(size)
    print(full)

if __name__ == '__main__': main()


Where to go next?

If you are still here you may be interested in voting for ZBXNEXT-3764 to get a zabbix_get-like utility for testing Java gateway out of the box.