diff --git a/zabbix.go b/zabbix.go new file mode 100644 index 0000000..92b10d4 --- /dev/null +++ b/zabbix.go @@ -0,0 +1,184 @@ +/* +Zabbix sender protocol defenition: + +HEADER: ZBXD\x01, 5 byte +DATALEN: 8 byte, little-endian +DATA: JSON data + +Request packet to zabbix host: +
{ + "request": "sender data", + "data": [ + { + "host": "Host name 1", + "key": "item_key", + "value": "33", + "clock": 1381482894 + }, + { + "host": "Host name 2", + "key": "item_key", + "value": "55", + "clock": 1381482894 + } + ], + "clock": 1381482905 +} + +Response packet: +
{ + "response":"success", + "info":"Processed 2 Failed 0 Total 2 Seconds spent 0.002070" +} +*/ + +package zabbix + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "os" + "time" +) + +// Metric class +type Metric struct { + Host string `json:"host"` + Key string `json:"key"` + Value string `json:"value"` + Clock int64 `json:"clock"` +} + +// Metric class constructor +func NewMetric(host, key, value string, clock ...int64) *Metric { + m := new(Metric) + m.Host = host + m.Key = key + m.Value = value + // use current time, if `clock` is not specified + if m.Clock = time.Now().Unix(); len(clock) > 0 { + m.Clock = int64(clock[0]) + } + return m +} + +// Packet class +type Packet struct { + Request string `json:"request"` + Data []*Metric `json:"data"` + Clock int64 `json:"clock"` +} + +// Packet class cunstructor +func NewPacket(data []*Metric, clock ...int64) *Packet { + p := new(Packet) + p.Request = `sender data` + p.Data = data + // use current time, if `clock` is not specified + if p.Clock = time.Now().Unix(); len(clock) > 0 { + p.Clock = int64(clock[0]) + } + return p +} + +// DataLen. Packet class method, return 8 bytes with packet length in little endian order +func (p Packet) DataLen() []byte { + dataLen := make([]byte, 8) + jsonData, _ := json.Marshal(p) + binary.LittleEndian.PutUint32(dataLen, uint32(len(jsonData))) + return dataLen +} + +// Sender class +type Sender struct { + Host string + Port int +} + +// Sender class constructor +func NewSender(host string, port int) *Sender { + s := new(Sender) + s.Host = host + s.Port = port + return s +} + +// Method Sender class, return zabbix header +func (s Sender) getHeader() []byte { + return []byte("ZBXD\x01") +} + +// Method Sender class, resolve uri by name:port +func (s Sender) getTCPAddr() *net.TCPAddr { + // format: hostname:port + addr := fmt.Sprintf("%s:%d", s.Host, s.Port) + + // Resolve hostname:port to ip:port + iaddr, err := net.ResolveTCPAddr("tcp", addr) + + if err != nil { + fmt.Printf("Connection failed: %s", err.Error()) + os.Exit(1) + } + + return iaddr +} + +// Method Sender class, make connection to uri +func (s Sender) connect() *net.TCPConn { + iaddr := s.getTCPAddr() + + // Open connection to zabbix host + conn, err := net.DialTCP("tcp", nil, iaddr) + + if err != nil { + fmt.Printf("Connection failed: %s", err.Error()) + os.Exit(1) + } + + return conn +} + +// Method Sender class, read data from connection +func (s Sender) read(conn *net.TCPConn) []byte { + res := make([]byte, 1024) + + res, err := ioutil.ReadAll(conn) + if err != nil { + fmt.Printf("Error whule receiving the data: %s", err.Error()) + os.Exit(1) + } + + return res +} + +// Method Sender class, send packet to zabbix +func (s Sender) Send(packet *Packet) { + + conn := s.connect() + defer conn.Close() + + dataPacket, _ := json.Marshal(packet) + + // Make zabbix header + fmt.Printf("HEADER: % x (%s)\n", s.getHeader(), s.getHeader()) + fmt.Printf("DATALEN: % x, %d byte\n", packet.DataLen(), len(packet.DataLen())) + fmt.Printf("BODY: %s\n", string(dataPacket)) + + // Fill buffer + buffer := append(s.getHeader(), packet.DataLen()...) + buffer = append(buffer, dataPacket...) + + // Sent packet to zabbix + _, err := conn.Write(buffer) + if err != nil { + fmt.Printf("Error while sending the data: %s", err.Error()) + os.Exit(1) + } + + res := s.read(conn) + fmt.Printf("RESPONSE: %s\n", string(res)) +}