Zabbix Sender:
Zabbix provides a binary executable called `zabbix_sender` that can be used to send data to the Zabbix server. The specific usage is as follows:
>>> zabbix_sender -z 10.0.0.1 -p 10051 -s "rs_solrmaster" -k "get_number_of_solr_index_update" -o 4 Note that get_number_of_solr_index_update is a key that must be created in advance on the host rs_solrmaster, and it must be of type zabbix trapper. #usage: zabbix_sender [-Vhv] {[-zpsI] -ko | [-zpI] -T -i -r} [-c ] #Options: # -c --config Absolute path to the configuration file # -z --zabbix-server IP address of the Zabbix server or Zabbix proxy # -p --port Port of the Zabbix server or Zabbix proxy, default is 10051 # -s --host Host name, referring to the host name configured in Zabbix # -I --source-address Source IP address # -k --key Name of the Zabbix trapper key # -o --value Value of the item to be sent # -i --input-file <input type="text" /> Batch send data via a file, with one record per line in the format hostname,key,value separated by spaces. If the hostname contains spaces, use double quotes. >>> cat trapper_key.txt "zabbix server" key1 10 "zabbix proxy" key2 20 10.0.0.2 key3 30 >>> zabbix_sender -z 10.0.0.1 -i trapper_key.txt
Thus, we can send monitoring data to Zabbix anytime and anywhere. Since Zabbix only provides the `zabbix_sender` binary for sending data, calling a binary executable directly within a program is considered poor practice. Is there a more elegant solution? (Note that the Zabbix API documentation does not cover trapper keys). Yes, pay attention to what comes next.
The communication between `zabbix_sender` and `zabbix_server` uses the TCP protocol. This means `zabbix_sender` sends a TCP packet with a fixed-format DATA segment to `zabbix_server`, which then parses and processes the request. Therefore, if we can determine the data format of this DATA segment, we can simulate `zabbix_sender` using a program.
Let’s look at two articles first:
Research on Zabbix Sender Protocol | itnihao
Zabbix Sender 2.0 Protocol Documentation
From these, we can derive the complete format of a `zabbix_sender` request packet as follows:
| Source Port | Destination Port | ||||||||||||||||||||||||||||||
| 32-bit Sequence Number | |||||||||||||||||||||||||||||||
| 32-bit Acknowledgment Number | |||||||||||||||||||||||||||||||
| 4-bit Header Length | Reserved (6 bits) | URG | ACK | PSH | RST | SYN | FIN | 16-bit Window Size | |||||||||||||||||||||||
| 16-bit Checksum | 16-bit Urgent Pointer | ||||||||||||||||||||||||||||||
| Options | |||||||||||||||||||||||||||||||
| Z | B | X | D | Protocol Version | 8-bit Data Length | JSON data for request or response | |||||||||||||||||||||||||
As seen, the basic Zabbix sender data protocol consists of ‘Z”B”X”D’ + 1-byte protocol version number + 8-byte data length + JSON data. According to the documentation, the JSON data format for the request is:
{ "request":"sender data", "data":[ { "host":"Host name 1", "key":"item_key", "value":"33"}, { "host":"Host name 2", "key":"item_key", "value":"55" } ] }
This gives us the basic program logic, which is:
♠ Construct a data packet conforming to the Zabbix sender protocol, with the format ‘4sBqns’ (where n is the length of the JSON string)
♠ Send the data packet to the Zabbix server port
♠ Process the response
Below is an example implementation in Python:
#!/usr/bin/env python import struct import json import socket class ZabbixSender: def __init__(self,zbx_server_host,zbx_server_port): self.zbx_server_host=zbx_server_host self.zbx_server_port=zbx_server_port self.zbx_header='ZBXD' self.zbx_protocols_version=1 self.zbx_send_value={'request':'sender data','data':[]} def add_data(self,host,key,value): add_data={'host':host,'key':key,'value':value} self.zbx_send_value['data'].append(add_data) # Encapsulate data packet according to protocol def make_send_data(self): zbx_send_json=json.dumps(self.zbx_send_value) zbx_send_json_len=len(zbx_send_json) self.zbx_send_data=struct.pack("<4sBq"+str(zbx_send_json_len)+"s",'ZBXD',1,zbx_send_json_len,zbx_send_json) def send(self): self.make_send_data() zbx_server_socket=socket.socket() zbx_server_socket.connect((self.zbx_server_host,self.zbx_server_port)) zbx_server_write_df=zbx_server_socket.makefile('wb') zbx_server_write_df.write(self.zbx_send_data) zbx_server_write_df.close() zbx_server_read_df=zbx_server_socket.makefile('rb') zbx_response_package=zbx_server_read_df.read() zbx_server_read_df.close() # Decode data packet according to protocol zbx_response_data=struct.unpack("<4sBq"+str(len(zbx_response_package) - struct.calcsize("<4sBq"))+"s",zbx_response_package) return zbx_response_data[3] if __name__ == '__main__': zabbix_sender=ZabbixSender('10.0.0.1',10051) zabbix_sender.add_data('solrmaster','get_number_of_solr_index_update',60) response=zabbix_sender.send() print response