From deed04c2f757090b3d07d6f006f8263bb87ee98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patric=20Kanngie=C3=9Fer?= Date: Wed, 1 Nov 2017 21:28:00 +0100 Subject: [PATCH] Add Teamspeak 3 input plugin (#3315) --- Godeps | 2 + docs/LICENSE_OF_DEPENDENCIES.md | 2 + plugins/inputs/all/all.go | 1 + plugins/inputs/teamspeak/README.md | 45 ++++++++++ plugins/inputs/teamspeak/teamspeak.go | 100 +++++++++++++++++++++ plugins/inputs/teamspeak/teamspeak_test.go | 87 ++++++++++++++++++ 6 files changed, 237 insertions(+) create mode 100644 plugins/inputs/teamspeak/README.md create mode 100644 plugins/inputs/teamspeak/teamspeak.go create mode 100644 plugins/inputs/teamspeak/teamspeak_test.go diff --git a/Godeps b/Godeps index 0802675ba..02e20755e 100644 --- a/Godeps +++ b/Godeps @@ -40,6 +40,8 @@ github.com/kballard/go-shellquote d8ec1a69a250a17bb0e419c386eac1f3711dc142 github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c github.com/Microsoft/go-winio ce2922f643c8fd76b46cadc7f404a06282678b34 github.com/miekg/dns 99f84ae56e75126dd77e5de4fae2ea034a468ca1 +github.com/mitchellh/mapstructure d0303fe809921458f417bcf828397a65db30a7e4 +github.com/multiplay/go-ts3 07477f49b8dfa3ada231afc7b7b17617d42afe8e github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b github.com/nats-io/go-nats ea9585611a4ab58a205b9b125ebd74c389a6b898 github.com/nats-io/nats ea9585611a4ab58a205b9b125ebd74c389a6b898 diff --git a/docs/LICENSE_OF_DEPENDENCIES.md b/docs/LICENSE_OF_DEPENDENCIES.md index 3774d34ce..ffd9751d7 100644 --- a/docs/LICENSE_OF_DEPENDENCIES.md +++ b/docs/LICENSE_OF_DEPENDENCIES.md @@ -82,6 +82,8 @@ following works: - github.com/streadway/amqp [BSD](https://github.com/streadway/amqp/blob/master/LICENSE) - github.com/stretchr/objx [MIT](https://github.com/stretchr/objx/blob/master/LICENSE.md) - github.com/stretchr/testify [MIT](https://github.com/stretchr/testify/blob/master/LICENCE.txt) +- github.com/mitchellh/mapstructure [MIT](https://github.com/mitchellh/mapstructure/blob/master/LICENSE) +- github.com/multiplay/go-ts3 [BSD](https://github.com/multiplay/go-ts3/blob/master/LICENSE) - github.com/vjeantet/grok [APACHE](https://github.com/vjeantet/grok/blob/master/LICENSE) - github.com/wvanbergen/kafka [MIT](https://github.com/wvanbergen/kafka/blob/master/LICENSE) - github.com/wvanbergen/kazoo-go [MIT](https://github.com/wvanbergen/kazoo-go/blob/master/MIT-LICENSE) diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index 1f4d3825c..235169c86 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -86,6 +86,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/system" _ "github.com/influxdata/telegraf/plugins/inputs/tail" _ "github.com/influxdata/telegraf/plugins/inputs/tcp_listener" + _ "github.com/influxdata/telegraf/plugins/inputs/teamspeak" _ "github.com/influxdata/telegraf/plugins/inputs/tomcat" _ "github.com/influxdata/telegraf/plugins/inputs/trig" _ "github.com/influxdata/telegraf/plugins/inputs/twemproxy" diff --git a/plugins/inputs/teamspeak/README.md b/plugins/inputs/teamspeak/README.md new file mode 100644 index 000000000..84c4297e8 --- /dev/null +++ b/plugins/inputs/teamspeak/README.md @@ -0,0 +1,45 @@ +# Teamspeak 3 Input Plugin + +This plugin uses the Teamspeak 3 ServerQuery interface of the Teamspeak server to collect statistics of one or more +virtual servers. If you are querying an external Teamspeak server, make sure to add the host which is running Telegraf +to query_ip_whitelist.txt in the Teamspeak Server directory. For information about how to configure the server take a look +the [Teamspeak 3 ServerQuery Manual](http://media.teamspeak.com/ts3_literature/TeamSpeak%203%20Server%20Query%20Manual.pdf) + +### Configuration: + +``` +# Reads metrics from a Teamspeak 3 Server via ServerQuery +[[inputs.teamspeak]] + ## Server address for Teamspeak 3 ServerQuery + # server = "127.0.0.1:10011" + ## Username for ServerQuery + username = "serverqueryuser" + ## Password for ServerQuery + password = "secret" + ## Array of virtual servers + # virtual_servers = [1] +``` + +### Measurements: + +- teamspeak + - uptime + - clients_online + - total_ping + - total_packet_loss + - packets_sent_total + - packets_received_total + - bytes_sent_total + - bytes_received_total + +### Tags: + +- The following tags are used: + - virtual_server + - name + +### Example output: + +``` +teamspeak,virtual_server=1,name=LeopoldsServer,host=vm01 bytes_received_total=29638202639i,uptime=13567846i,total_ping=26.89,total_packet_loss=0,packets_sent_total=415821252i,packets_received_total=237069900i,bytes_sent_total=55309568252i,clients_online=11i 1507406561000000000 +``` \ No newline at end of file diff --git a/plugins/inputs/teamspeak/teamspeak.go b/plugins/inputs/teamspeak/teamspeak.go new file mode 100644 index 000000000..91fdf1135 --- /dev/null +++ b/plugins/inputs/teamspeak/teamspeak.go @@ -0,0 +1,100 @@ +package teamspeak + +import ( + "github.com/multiplay/go-ts3" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" + "strconv" +) + +type Teamspeak struct { + Server string + Username string + Password string + VirtualServers []int `toml:"virtual_servers"` + + client *ts3.Client + connected bool +} + +func (ts *Teamspeak) Description() string { + return "Reads metrics from a Teamspeak 3 Server via ServerQuery" +} + +const sampleConfig = ` + ## Server address for Teamspeak 3 ServerQuery + # server = "127.0.0.1:10011" + ## Username for ServerQuery + username = "serverqueryuser" + ## Password for ServerQuery + password = "secret" + ## Array of virtual servers + # virtual_servers = [1] +` + +func (ts *Teamspeak) SampleConfig() string { + return sampleConfig +} + +func (ts *Teamspeak) Gather(acc telegraf.Accumulator) error { + var err error + + if !ts.connected { + ts.client, err = ts3.NewClient(ts.Server) + if err != nil { + return err + } + + err = ts.client.Login(ts.Username, ts.Password) + if err != nil { + return err + } + + ts.connected = true + } + + for _, vserver := range ts.VirtualServers { + ts.client.Use(vserver) + + sm, err := ts.client.Server.Info() + if err != nil { + ts.connected = false + return err + } + + sc, err := ts.client.Server.ServerConnectionInfo() + if err != nil { + ts.connected = false + return err + } + + tags := map[string]string{ + "virtual_server": strconv.Itoa(sm.ID), + "name": sm.Name, + } + + fields := map[string]interface{}{ + "uptime": sm.Uptime, + "clients_online": sm.ClientsOnline, + "total_ping": sm.TotalPing, + "total_packet_loss": sm.TotalPacketLossTotal, + "packets_sent_total": sc.PacketsSentTotal, + "packets_received_total": sc.PacketsReceivedTotal, + "bytes_sent_total": sc.BytesSentTotal, + "bytes_received_total": sc.BytesReceivedTotal, + } + + acc.AddFields("teamspeak", fields, tags) + } + return nil +} + +func init() { + inputs.Add("teamspeak", func() telegraf.Input { + return &Teamspeak{ + Server: "127.0.0.1:10011", + VirtualServers: []int{1}, + } + }) +} diff --git a/plugins/inputs/teamspeak/teamspeak_test.go b/plugins/inputs/teamspeak/teamspeak_test.go new file mode 100644 index 000000000..b66948f28 --- /dev/null +++ b/plugins/inputs/teamspeak/teamspeak_test.go @@ -0,0 +1,87 @@ +package teamspeak + +import ( + "bufio" + "net" + "strings" + "testing" + + "github.com/influxdata/telegraf/testutil" +) + +const welcome = `Welcome to the TeamSpeak 3 ServerQuery interface, type "help" for a list of commands and "help " for information on a specific command.` +const ok = `error id=0 msg=ok` +const errorMsg = `error id=256 msg=command\snot\sfound` + +var cmd = map[string]string{ + "login": "", + "use": "", + "serverinfo": `virtualserver_unique_identifier=a1vn9PLF8CMIU virtualserver_name=Testserver virtualserver_welcomemessage=Test virtualserver_platform=Linux virtualserver_version=3.0.13.8\s[Build:\s1500452811] virtualserver_maxclients=32 virtualserver_password virtualserver_clientsonline=2 virtualserver_channelsonline=1 virtualserver_created=1507400243 virtualserver_uptime=148 virtualserver_codec_encryption_mode=0 virtualserver_hostmessage virtualserver_hostmessage_mode=0 virtualserver_filebase=files\/virtualserver_1 virtualserver_default_server_group=8 virtualserver_default_channel_group=8 virtualserver_flag_password=0 virtualserver_default_channel_admin_group=5 virtualserver_max_download_total_bandwidth=18446744073709551615 virtualserver_max_upload_total_bandwidth=18446744073709551615 virtualserver_hostbanner_url virtualserver_hostbanner_gfx_url virtualserver_hostbanner_gfx_interval=0 virtualserver_complain_autoban_count=5 virtualserver_complain_autoban_time=1200 virtualserver_complain_remove_time=3600 virtualserver_min_clients_in_channel_before_forced_silence=100 virtualserver_priority_speaker_dimm_modificator=-18.0000 virtualserver_id=1 virtualserver_antiflood_points_tick_reduce=5 virtualserver_antiflood_points_needed_command_block=150 virtualserver_antiflood_points_needed_ip_block=250 virtualserver_client_connections=1 virtualserver_query_client_connections=1 virtualserver_hostbutton_tooltip virtualserver_hostbutton_url virtualserver_hostbutton_gfx_url virtualserver_queryclientsonline=1 virtualserver_download_quota=18446744073709551615 virtualserver_upload_quota=18446744073709551615 virtualserver_month_bytes_downloaded=0 virtualserver_month_bytes_uploaded=0 virtualserver_total_bytes_downloaded=0 virtualserver_total_bytes_uploaded=0 virtualserver_port=9987 virtualserver_autostart=1 virtualserver_machine_id virtualserver_needed_identity_security_level=8 virtualserver_log_client=0 virtualserver_log_query=0 virtualserver_log_channel=0 virtualserver_log_permissions=1 virtualserver_log_server=0 virtualserver_log_filetransfer=0 virtualserver_min_client_version=1445512488 virtualserver_name_phonetic virtualserver_icon_id=0 virtualserver_reserved_slots=0 virtualserver_total_packetloss_speech=0.0000 virtualserver_total_packetloss_keepalive=0.0000 virtualserver_total_packetloss_control=0.0000 virtualserver_total_packetloss_total=0.0000 virtualserver_total_ping=1.0000 virtualserver_ip=0.0.0.0,\s:: virtualserver_weblist_enabled=1 virtualserver_ask_for_privilegekey=0 virtualserver_hostbanner_mode=0 virtualserver_channel_temp_delete_delay_default=0 virtualserver_min_android_version=1407159763 virtualserver_min_ios_version=1407159763 virtualserver_status=online connection_filetransfer_bandwidth_sent=0 connection_filetransfer_bandwidth_received=0 connection_filetransfer_bytes_sent_total=0 connection_filetransfer_bytes_received_total=0 connection_packets_sent_speech=0 connection_bytes_sent_speech=0 connection_packets_received_speech=0 connection_bytes_received_speech=0 connection_packets_sent_keepalive=261 connection_bytes_sent_keepalive=10701 connection_packets_received_keepalive=261 connection_bytes_received_keepalive=10961 connection_packets_sent_control=54 connection_bytes_sent_control=15143 connection_packets_received_control=55 connection_bytes_received_control=4239 connection_packets_sent_total=315 connection_bytes_sent_total=25844 connection_packets_received_total=316 connection_bytes_received_total=15200 connection_bandwidth_sent_last_second_total=81 connection_bandwidth_sent_last_minute_total=141 connection_bandwidth_received_last_second_total=83 connection_bandwidth_received_last_minute_total=98`, + "serverrequestconnectioninfo": `connection_filetransfer_bandwidth_sent=0 connection_filetransfer_bandwidth_received=0 connection_filetransfer_bytes_sent_total=0 connection_filetransfer_bytes_received_total=0 connection_packets_sent_total=369 connection_bytes_sent_total=28058 connection_packets_received_total=370 connection_bytes_received_total=17468 connection_bandwidth_sent_last_second_total=81 connection_bandwidth_sent_last_minute_total=109 connection_bandwidth_received_last_second_total=83 connection_bandwidth_received_last_minute_total=94 connection_connected_time=174 connection_packetloss_total=0.0000 connection_ping=1.0000`, +} + +func TestGather(t *testing.T) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal("Initializing test server failed") + } + defer l.Close() + + go handleRequest(l, t) + + var acc testutil.Accumulator + testConfig := Teamspeak{ + Server: l.Addr().String(), + Username: "serveradmin", + Password: "test", + VirtualServers: []int{1}, + } + err = testConfig.Gather(&acc) + + if err != nil { + t.Fatalf("Gather returned error. Error: %s\n", err) + } + + fields := map[string]interface{}{ + "uptime": int(148), + "clients_online": int(2), + "total_ping": float32(1.0000), + "total_packet_loss": float64(0.0000), + "packets_sent_total": uint64(369), + "packets_received_total": uint64(370), + "bytes_sent_total": uint64(28058), + "bytes_received_total": uint64(17468), + } + + acc.AssertContainsFields(t, "teamspeak", fields) +} + +func handleRequest(l net.Listener, t *testing.T) { + c, err := l.Accept() + if err != nil { + t.Fatal("Error accepting test connection") + } + c.Write([]byte("TS3\n\r" + welcome + "\n\r")) + for { + msg, _, err := bufio.NewReader(c).ReadLine() + if err != nil { + return + } + r, exists := cmd[strings.Split(string(msg), " ")[0]] + + if exists { + switch r { + case "": + c.Write([]byte(ok + "\n\r")) + case "quit": + c.Write([]byte(ok + "\n\r")) + c.Close() + return + default: + c.Write([]byte(r + "\n\r" + ok + "\n\r")) + } + } else { + c.Write([]byte(errorMsg + "\n\r")) + } + } +}