go-zabbix-sender/zabbix.go

171 lines
3.6 KiB
Go
Raw Normal View History

2015-02-26 12:58:27 +03:00
// Package implement zabbix sender protocol for send metrics to zabbix.
2015-02-26 12:47:00 +03:00
package zabbix
import (
"encoding/binary"
"encoding/json"
2015-02-26 12:58:27 +03:00
"fmt"
2015-02-26 12:47:00 +03:00
"io/ioutil"
"net"
"time"
)
2015-02-26 12:58:27 +03:00
// Metric class.
2015-02-26 12:47:00 +03:00
type Metric struct {
Host string `json:"host"`
Key string `json:"key"`
Value string `json:"value"`
Clock int64 `json:"clock"`
}
2015-02-26 12:58:27 +03:00
// Metric class constructor.
2015-02-26 12:47:00 +03:00
func NewMetric(host, key, value string, clock ...int64) *Metric {
2015-03-02 01:01:04 +03:00
m := &Metric{Host: host, Key: key, Value: value}
2015-02-26 12:47:00 +03:00
// use current time, if `clock` is not specified
if m.Clock = time.Now().Unix(); len(clock) > 0 {
m.Clock = int64(clock[0])
}
return m
}
2015-02-26 12:58:27 +03:00
// Packet class.
2015-02-26 12:47:00 +03:00
type Packet struct {
Request string `json:"request"`
Data []*Metric `json:"data"`
Clock int64 `json:"clock"`
}
2015-02-26 12:58:27 +03:00
// Packet class cunstructor.
2015-02-26 12:47:00 +03:00
func NewPacket(data []*Metric, clock ...int64) *Packet {
2015-03-02 01:01:04 +03:00
p := &Packet{Request: `sender data`, Data: data}
2015-02-26 12:47:00 +03:00
// use current time, if `clock` is not specified
if p.Clock = time.Now().Unix(); len(clock) > 0 {
p.Clock = int64(clock[0])
}
2015-02-26 12:58:27 +03:00
return p
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// DataLen Packet class method, return 8 bytes with packet length in little endian order.
2015-03-02 01:01:04 +03:00
func (p *Packet) DataLen() []byte {
2015-02-26 12:47:00 +03:00
dataLen := make([]byte, 8)
2015-02-26 12:58:27 +03:00
JSONData, _ := json.Marshal(p)
binary.LittleEndian.PutUint32(dataLen, uint32(len(JSONData)))
return dataLen
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// Sender class.
2015-02-26 12:47:00 +03:00
type Sender struct {
2015-02-26 12:58:27 +03:00
Host string
Port int
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// Sender class constructor.
2015-02-26 12:47:00 +03:00
func NewSender(host string, port int) *Sender {
2015-03-02 01:01:04 +03:00
s := &Sender{Host: host, Port: port}
2015-02-26 12:58:27 +03:00
return s
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// Method Sender class, return zabbix header.
2015-03-02 01:01:04 +03:00
func (s *Sender) getHeader() []byte {
2015-02-26 12:47:00 +03:00
return []byte("ZBXD\x01")
}
2015-02-26 12:58:27 +03:00
// Method Sender class, resolve uri by name:port.
2017-01-11 13:50:26 +03:00
func (s *Sender) getTCPAddr() (iaddr *net.TCPAddr, err error) {
2015-02-26 12:58:27 +03:00
// format: hostname:port
addr := fmt.Sprintf("%s:%d", s.Host, s.Port)
2015-02-26 12:47:00 +03:00
2015-02-26 12:58:27 +03:00
// Resolve hostname:port to ip:port
2017-01-11 13:50:26 +03:00
iaddr, err = net.ResolveTCPAddr("tcp", addr)
2015-02-26 12:58:27 +03:00
if err != nil {
2017-01-11 13:50:26 +03:00
err = fmt.Errorf("Connection failed: %s", err.Error())
return
2015-02-26 12:47:00 +03:00
}
2017-01-11 13:50:26 +03:00
return
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// Method Sender class, make connection to uri.
2017-01-11 13:50:26 +03:00
func (s *Sender) connect() (conn *net.TCPConn, err error) {
type DialResp struct {
Conn *net.TCPConn
Error error
}
2015-02-26 12:47:00 +03:00
2017-01-11 13:50:26 +03:00
// Open connection to zabbix host
iaddr, err := s.getTCPAddr()
2015-02-26 12:58:27 +03:00
if err != nil {
2017-01-11 13:50:26 +03:00
return
2015-02-26 12:47:00 +03:00
}
2017-01-11 13:50:26 +03:00
// dial tcp and handle timeouts
ch := make(chan DialResp)
go func() {
conn, err = net.DialTCP("tcp", nil, iaddr)
ch <- DialResp{Conn: conn, Error: err}
}()
select {
case <-time.After(5 * time.Second):
err = fmt.Errorf("Connection Timeout")
case resp := <-ch:
if resp.Error != nil {
err = resp.Error
break
}
conn = resp.Conn
}
return
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// Method Sender class, read data from connection.
2017-01-11 13:50:26 +03:00
func (s *Sender) read(conn *net.TCPConn) (res []byte, err error) {
res = make([]byte, 1024)
res, err = ioutil.ReadAll(conn)
2015-02-26 12:47:00 +03:00
if err != nil {
2017-01-11 13:50:26 +03:00
err = fmt.Errorf("Error whule receiving the data: %s", err.Error())
return
2015-02-26 12:47:00 +03:00
}
2017-01-11 13:50:26 +03:00
return
2015-02-26 12:47:00 +03:00
}
2015-02-26 12:58:27 +03:00
// Method Sender class, send packet to zabbix.
2017-01-11 13:50:26 +03:00
func (s *Sender) Send(packet *Packet) (res []byte, err error) {
conn, err := s.connect()
if err != nil {
return
}
2015-02-26 12:47:00 +03:00
defer conn.Close()
2015-02-26 12:58:27 +03:00
dataPacket, _ := json.Marshal(packet)
2015-02-26 12:47:00 +03:00
2015-02-26 12:58:27 +03:00
/*
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))
*/
2015-02-26 12:47:00 +03:00
2015-02-26 12:58:27 +03:00
// Fill buffer
buffer := append(s.getHeader(), packet.DataLen()...)
buffer = append(buffer, dataPacket...)
2015-02-26 12:47:00 +03:00
2015-02-26 12:58:27 +03:00
// Sent packet to zabbix
2017-01-11 13:50:26 +03:00
_, err = conn.Write(buffer)
2015-02-26 12:47:00 +03:00
if err != nil {
2017-01-11 13:50:26 +03:00
err = fmt.Errorf("Error while sending the data: %s", err.Error())
return
2015-02-26 12:47:00 +03:00
}
2017-01-11 13:50:26 +03:00
res, err = s.read(conn)
2015-02-26 12:58:27 +03:00
/*
fmt.Printf("RESPONSE: %s\n", string(res))
*/
2017-01-11 13:50:26 +03:00
return
2015-02-26 12:47:00 +03:00
}