Compare commits
1 Commits
0.11.1
...
0.10.3-win
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0074696e67 |
64
CHANGELOG.md
64
CHANGELOG.md
@@ -1,75 +1,13 @@
|
|||||||
## v0.11.1 [unreleased]
|
## v0.10.4 [unreleased]
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#747](https://github.com/influxdata/telegraf/pull/747): Start telegraf on install & remove on uninstall. Thanks @pierref!
|
|
||||||
- [#794](https://github.com/influxdata/telegraf/pull/794): Add service reload ability. Thanks @entertainyou!
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#852](https://github.com/influxdata/telegraf/issues/852): Windows zip package fix
|
|
||||||
- [#859](https://github.com/influxdata/telegraf/issues/859): httpjson plugin panic
|
|
||||||
|
|
||||||
## v0.11.0 [2016-03-15]
|
|
||||||
|
|
||||||
### Release Notes
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [#692](https://github.com/influxdata/telegraf/pull/770): Support InfluxDB retention policies
|
|
||||||
- [#771](https://github.com/influxdata/telegraf/pull/771): Default timeouts for input plugns. Thanks @PierreF!
|
|
||||||
- [#758](https://github.com/influxdata/telegraf/pull/758): UDP Listener input plugin, thanks @whatyouhide!
|
|
||||||
- [#769](https://github.com/influxdata/telegraf/issues/769): httpjson plugin: allow specifying SSL configuration.
|
|
||||||
- [#735](https://github.com/influxdata/telegraf/pull/735): SNMP Table feature. Thanks @titilambert!
|
|
||||||
- [#754](https://github.com/influxdata/telegraf/pull/754): docker plugin: adding `docker info` metrics to output. Thanks @titilambert!
|
|
||||||
- [#788](https://github.com/influxdata/telegraf/pull/788): -input-list and -output-list command-line options. Thanks @ebookbug!
|
|
||||||
- [#778](https://github.com/influxdata/telegraf/pull/778): Adding a TCP input listener.
|
|
||||||
- [#797](https://github.com/influxdata/telegraf/issues/797): Provide option for persistent MQTT consumer client sessions.
|
|
||||||
- [#799](https://github.com/influxdata/telegraf/pull/799): Add number of threads for procstat input plugin. Thanks @titilambert!
|
|
||||||
- [#776](https://github.com/influxdata/telegraf/pull/776): Add Zookeeper chroot option to kafka_consumer. Thanks @prune998!
|
|
||||||
- [#811](https://github.com/influxdata/telegraf/pull/811): Add processes plugin for classifying total procs on system. Thanks @titilambert!
|
|
||||||
- [#235](https://github.com/influxdata/telegraf/issues/235): Add number of users to the `system` input plugin.
|
|
||||||
- [#826](https://github.com/influxdata/telegraf/pull/826): "kernel" linux plugin for /proc/stat metrics (context switches, interrupts, etc.)
|
|
||||||
- [#847](https://github.com/influxdata/telegraf/pull/847): `ntpq`: Input plugin for running ntp query executable and gathering metrics.
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#748](https://github.com/influxdata/telegraf/issues/748): Fix sensor plugin split on ":"
|
|
||||||
- [#722](https://github.com/influxdata/telegraf/pull/722): Librato output plugin fixes. Thanks @chrusty!
|
|
||||||
- [#745](https://github.com/influxdata/telegraf/issues/745): Fix Telegraf toml parse panic on large config files. Thanks @titilambert!
|
|
||||||
- [#781](https://github.com/influxdata/telegraf/pull/781): Fix mqtt_consumer username not being set. Thanks @chaton78!
|
|
||||||
- [#786](https://github.com/influxdata/telegraf/pull/786): Fix mqtt output username not being set. Thanks @msangoi!
|
|
||||||
- [#773](https://github.com/influxdata/telegraf/issues/773): Fix duplicate measurements in snmp plugin. Thanks @titilambert!
|
|
||||||
- [#708](https://github.com/influxdata/telegraf/issues/708): packaging: build ARM package
|
|
||||||
- [#713](https://github.com/influxdata/telegraf/issues/713): packaging: insecure permissions error on log directory
|
|
||||||
- [#816](https://github.com/influxdata/telegraf/issues/816): Fix phpfpm panic if fcgi endpoint unreachable.
|
|
||||||
- [#828](https://github.com/influxdata/telegraf/issues/828): fix net_response plugin overwriting host tag.
|
|
||||||
- [#821](https://github.com/influxdata/telegraf/issues/821): Remove postgres password from server tag. Thanks @menardorama!
|
|
||||||
|
|
||||||
## v0.10.4.1
|
|
||||||
|
|
||||||
### Release Notes
|
|
||||||
- Bug in the build script broke deb and rpm packages.
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
- [#750](https://github.com/influxdata/telegraf/issues/750): deb package broken
|
|
||||||
- [#752](https://github.com/influxdata/telegraf/issues/752): rpm package broken
|
|
||||||
|
|
||||||
## v0.10.4 [2016-02-24]
|
|
||||||
|
|
||||||
### Release Notes
|
|
||||||
- The pass/drop parameters have been renamed to fielddrop/fieldpass parameters,
|
|
||||||
to more accurately indicate their purpose.
|
|
||||||
- There are also now namedrop/namepass parameters for passing/dropping based
|
|
||||||
on the metric _name_.
|
|
||||||
- Experimental windows builds now available.
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [#727](https://github.com/influxdata/telegraf/pull/727): riak input, thanks @jcoene!
|
- [#727](https://github.com/influxdata/telegraf/pull/727): riak input, thanks @jcoene!
|
||||||
- [#694](https://github.com/influxdata/telegraf/pull/694): DNS Query input, thanks @mjasion!
|
- [#694](https://github.com/influxdata/telegraf/pull/694): DNS Query input, thanks @mjasion!
|
||||||
- [#724](https://github.com/influxdata/telegraf/pull/724): username matching for procstat input, thanks @zorel!
|
- [#724](https://github.com/influxdata/telegraf/pull/724): username matching for procstat input, thanks @zorel!
|
||||||
- [#736](https://github.com/influxdata/telegraf/pull/736): Ignore dummy filesystems from disk plugin. Thanks @PierreF!
|
- [#736](https://github.com/influxdata/telegraf/pull/736): Ignore dummy filesystems from disk plugin. Thanks @PierreF!
|
||||||
- [#737](https://github.com/influxdata/telegraf/pull/737): Support multiple fields for statsd input. Thanks @mattheath!
|
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
- [#701](https://github.com/influxdata/telegraf/pull/701): output write count shouldnt print in quiet mode.
|
- [#701](https://github.com/influxdata/telegraf/pull/701): output write count shouldnt print in quiet mode.
|
||||||
- [#746](https://github.com/influxdata/telegraf/pull/746): httpjson plugin: Fix HTTP GET parameters.
|
|
||||||
|
|
||||||
## v0.10.3 [2016-02-18]
|
## v0.10.3 [2016-02-18]
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func (s *Simple) SampleConfig() string {
|
|||||||
return "ok = true # indicate if everything is fine"
|
return "ok = true # indicate if everything is fine"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Simple) Gather(acc telegraf.Accumulator) error {
|
func (s *Simple) Gather(acc inputs.Accumulator) error {
|
||||||
if s.Ok {
|
if s.Ok {
|
||||||
acc.Add("state", "pretty good", nil)
|
acc.Add("state", "pretty good", nil)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
65
Godeps
65
Godeps
@@ -1,54 +1,53 @@
|
|||||||
git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git 617c801af238c3af2d9e72c5d4a0f02edad03ce5
|
git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git 617c801af238c3af2d9e72c5d4a0f02edad03ce5
|
||||||
github.com/Shopify/sarama 8aadb476e66ca998f2f6bb3c993e9a2daa3666b9
|
github.com/Shopify/sarama d37c73f2b2bce85f7fa16b6a550d26c5372892ef
|
||||||
github.com/Sirupsen/logrus 219c8cb75c258c552e999735be6df753ffc7afdc
|
github.com/Sirupsen/logrus f7f79f729e0fbe2fcc061db48a9ba0263f588252
|
||||||
github.com/amir/raidman 53c1b967405155bfc8758557863bf2e14f814687
|
github.com/amir/raidman 6a8e089bbe32e6b907feae5ba688841974b3c339
|
||||||
github.com/aws/aws-sdk-go 13a12060f716145019378a10e2806c174356b857
|
github.com/aws/aws-sdk-go 87b1e60a50b09e4812dee560b33a238f67305804
|
||||||
github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
github.com/beorn7/perks b965b613227fddccbfffe13eae360ed3fa822f8d
|
||||||
github.com/cenkalti/backoff 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
github.com/cenkalti/backoff 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
||||||
github.com/dancannon/gorethink e7cac92ea2bc52638791a021f212145acfedb1fc
|
github.com/dancannon/gorethink 6f088135ff288deb9d5546f4c71919207f891a70
|
||||||
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||||
github.com/eapache/go-resiliency b86b1ec0dd4209a588dc1285cdd471e73525c0b3
|
github.com/eapache/go-resiliency b86b1ec0dd4209a588dc1285cdd471e73525c0b3
|
||||||
github.com/eapache/queue ded5959c0d4e360646dc9e9908cff48666781367
|
github.com/eapache/queue ded5959c0d4e360646dc9e9908cff48666781367
|
||||||
github.com/fsouza/go-dockerclient a49c8269a6899cae30da1f8a4b82e0ce945f9967
|
github.com/fsouza/go-dockerclient 7b651349f9479f5114913eefbfd3c4eeddd79ab4
|
||||||
github.com/go-ini/ini 776aa739ce9373377cd16f526cdf06cb4c89b40f
|
github.com/go-ini/ini afbd495e5aaea13597b5e14fe514ddeaa4d76fc3
|
||||||
github.com/go-sql-driver/mysql 1fca743146605a172a266e1654e01e5cd5669bee
|
github.com/go-sql-driver/mysql 7c7f556282622f94213bc028b4d0a7b6151ba239
|
||||||
github.com/golang/protobuf 552c7b9542c194800fd493123b3798ef0a832032
|
github.com/golang/protobuf 6aaa8d47701fa6cf07e914ec01fde3d4a1fe79c3
|
||||||
github.com/golang/snappy 427fb6fc07997f43afa32f35e850833760e489a7
|
github.com/golang/snappy 723cc1e459b8eea2dea4583200fd60757d40097a
|
||||||
github.com/gonuts/go-shellquote e842a11b24c6abfb3dd27af69a17f482e4b483c2
|
github.com/gonuts/go-shellquote e842a11b24c6abfb3dd27af69a17f482e4b483c2
|
||||||
github.com/gorilla/context 1ea25387ff6f684839d82767c1733ff4d4d15d0a
|
github.com/gorilla/context 1c83b3eabd45b6d76072b66b746c20815fb2872d
|
||||||
github.com/gorilla/mux c9e326e2bdec29039a3761c07bece13133863e1e
|
github.com/gorilla/mux 26a6070f849969ba72b72256e9f14cf519751690
|
||||||
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
||||||
github.com/influxdata/config b79f6829346b8d6e78ba73544b1e1038f1f1c9da
|
github.com/influxdata/config bae7cb98197d842374d3b8403905924094930f24
|
||||||
github.com/influxdata/influxdb e3fef5593c21644f2b43af55d6e17e70910b0e48
|
github.com/influxdata/influxdb ef571fc104dc24b77cd3710c156cd95e5cfd7aa5
|
||||||
github.com/influxdata/toml af4df43894b16e3fd2b788d01bd27ad0776ef2d0
|
github.com/jmespath/go-jmespath c01cf91b011868172fdcd9f41838e80c9d716264
|
||||||
github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
|
github.com/klauspost/crc32 999f3125931f6557b991b2f8472172bdfa578d38
|
||||||
github.com/klauspost/crc32 19b0b332c9e4516a6370a0456e6182c3b5036720
|
github.com/lib/pq 8ad2b298cadd691a77015666a5372eae5dbfac8f
|
||||||
github.com/lib/pq e182dc4027e2ded4b19396d638610f2653295f36
|
|
||||||
github.com/matttproud/golang_protobuf_extensions d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
github.com/matttproud/golang_protobuf_extensions d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
||||||
github.com/miekg/dns cce6c130cdb92c752850880fd285bea1d64439dd
|
|
||||||
github.com/mreiferson/go-snappystream 028eae7ab5c4c9e2d1cb4c4ca1e53259bbe7e504
|
github.com/mreiferson/go-snappystream 028eae7ab5c4c9e2d1cb4c4ca1e53259bbe7e504
|
||||||
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
||||||
github.com/nats-io/nats b13fc9d12b0b123ebc374e6b808c6228ae4234a3
|
github.com/naoina/toml 751171607256bb66e64c9f0220c00662420c38e9
|
||||||
github.com/nats-io/nuid 4f84f5f3b2786224e336af2e13dba0a0a80b76fa
|
github.com/nats-io/nats 6a83f1a633cfbfd90aa648ac99fb38c06a8b40df
|
||||||
github.com/nsqio/go-nsq 0b80d6f05e15ca1930e0c5e1d540ed627e299980
|
github.com/nsqio/go-nsq 2118015c120962edc5d03325c680daf3163a8b5f
|
||||||
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
||||||
github.com/prometheus/client_golang 18acf9993a863f4c4b40612e19cdd243e7c86831
|
github.com/prometheus/client_golang 67994f177195311c3ea3d4407ed0175e34a4256f
|
||||||
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||||
github.com/prometheus/common e8eabff8812b05acf522b45fdcd725a785188e37
|
github.com/prometheus/common 14ca1097bbe21584194c15e391a9dab95ad42a59
|
||||||
github.com/prometheus/procfs 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
github.com/prometheus/procfs 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
||||||
github.com/samuel/go-zookeeper 218e9c81c0dd8b3b18172b2bbfad92cc7d6db55f
|
github.com/samuel/go-zookeeper 218e9c81c0dd8b3b18172b2bbfad92cc7d6db55f
|
||||||
github.com/shirou/gopsutil 1de1357e7737a536c7f4ff6be7bd27977db4d2cb
|
github.com/shirou/gopsutil e77438504d45b9985c99a75730fe65220ceea00e
|
||||||
github.com/soniah/gosnmp b1b4f885b12c5dcbd021c5cee1c904110de6db7d
|
github.com/soniah/gosnmp b1b4f885b12c5dcbd021c5cee1c904110de6db7d
|
||||||
github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744
|
github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744
|
||||||
github.com/stretchr/objx 1a9d0bb9f541897e62256577b352fdbc1fb4fd94
|
github.com/stretchr/objx 1a9d0bb9f541897e62256577b352fdbc1fb4fd94
|
||||||
github.com/stretchr/testify 1f4a1643a57e798696635ea4c126e9127adb7d3c
|
github.com/stretchr/testify f390dcf405f7b83c997eac1b06768bb9f44dec18
|
||||||
github.com/wvanbergen/kafka 1a8639a45164fcc245d5c7b4bd3ccfbd1a0ffbf3
|
github.com/wvanbergen/kafka 1a8639a45164fcc245d5c7b4bd3ccfbd1a0ffbf3
|
||||||
github.com/wvanbergen/kazoo-go 0f768712ae6f76454f987c3356177e138df258f8
|
github.com/wvanbergen/kazoo-go 0f768712ae6f76454f987c3356177e138df258f8
|
||||||
github.com/zensqlmonitor/go-mssqldb ffe5510c6fa5e15e6d983210ab501c815b56b363
|
github.com/zensqlmonitor/go-mssqldb ffe5510c6fa5e15e6d983210ab501c815b56b363
|
||||||
golang.org/x/crypto 5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3
|
golang.org/x/crypto 1f22c0103821b9390939b6776727195525381532
|
||||||
golang.org/x/net 6acef71eb69611914f7a30939ea9f6e194c78172
|
golang.org/x/net 04b9de9b512f58addf28c9853d50ebef61c3953e
|
||||||
golang.org/x/text a71fd10341b064c10f4a81ceac72bcf70f26ea34
|
golang.org/x/text 6d3c22c4525a4da167968fa2479be5524d2e8bd0
|
||||||
gopkg.in/dancannon/gorethink.v1 7d1af5be49cb5ecc7b177bf387d232050299d6ef
|
gopkg.in/dancannon/gorethink.v1 6f088135ff288deb9d5546f4c71919207f891a70
|
||||||
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
||||||
gopkg.in/mgo.v2 d90005c5262a3463800497ea5a89aed5fe22c886
|
gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64
|
||||||
gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4
|
gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||||
|
github.com/miekg/dns e0d84d97e59bcb6561eae269c4e94d25b66822cb
|
||||||
@@ -1,53 +1,56 @@
|
|||||||
git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git 617c801af238c3af2d9e72c5d4a0f02edad03ce5
|
git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git 617c801af238c3af2d9e72c5d4a0f02edad03ce5
|
||||||
github.com/Shopify/sarama 8aadb476e66ca998f2f6bb3c993e9a2daa3666b9
|
github.com/Shopify/sarama d37c73f2b2bce85f7fa16b6a550d26c5372892ef
|
||||||
github.com/Sirupsen/logrus 219c8cb75c258c552e999735be6df753ffc7afdc
|
github.com/Sirupsen/logrus f7f79f729e0fbe2fcc061db48a9ba0263f588252
|
||||||
github.com/StackExchange/wmi f3e2bae1e0cb5aef83e319133eabfee30013a4a5
|
github.com/StackExchange/wmi f3e2bae1e0cb5aef83e319133eabfee30013a4a5
|
||||||
github.com/amir/raidman 53c1b967405155bfc8758557863bf2e14f814687
|
github.com/amir/raidman 6a8e089bbe32e6b907feae5ba688841974b3c339
|
||||||
github.com/aws/aws-sdk-go 13a12060f716145019378a10e2806c174356b857
|
github.com/aws/aws-sdk-go 87b1e60a50b09e4812dee560b33a238f67305804
|
||||||
github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
github.com/beorn7/perks b965b613227fddccbfffe13eae360ed3fa822f8d
|
||||||
github.com/cenkalti/backoff 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
github.com/cenkalti/backoff 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
||||||
github.com/dancannon/gorethink e7cac92ea2bc52638791a021f212145acfedb1fc
|
github.com/dancannon/gorethink 6f088135ff288deb9d5546f4c71919207f891a70
|
||||||
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||||
github.com/eapache/go-resiliency b86b1ec0dd4209a588dc1285cdd471e73525c0b3
|
github.com/eapache/go-resiliency b86b1ec0dd4209a588dc1285cdd471e73525c0b3
|
||||||
github.com/eapache/queue ded5959c0d4e360646dc9e9908cff48666781367
|
github.com/eapache/queue ded5959c0d4e360646dc9e9908cff48666781367
|
||||||
github.com/fsouza/go-dockerclient a49c8269a6899cae30da1f8a4b82e0ce945f9967
|
github.com/fsouza/go-dockerclient 7b651349f9479f5114913eefbfd3c4eeddd79ab4
|
||||||
|
github.com/go-ini/ini afbd495e5aaea13597b5e14fe514ddeaa4d76fc3
|
||||||
github.com/go-ole/go-ole 50055884d646dd9434f16bbb5c9801749b9bafe4
|
github.com/go-ole/go-ole 50055884d646dd9434f16bbb5c9801749b9bafe4
|
||||||
github.com/go-sql-driver/mysql 1fca743146605a172a266e1654e01e5cd5669bee
|
github.com/go-sql-driver/mysql 7c7f556282622f94213bc028b4d0a7b6151ba239
|
||||||
github.com/golang/protobuf 552c7b9542c194800fd493123b3798ef0a832032
|
github.com/golang/protobuf 6aaa8d47701fa6cf07e914ec01fde3d4a1fe79c3
|
||||||
github.com/golang/snappy 427fb6fc07997f43afa32f35e850833760e489a7
|
github.com/golang/snappy 723cc1e459b8eea2dea4583200fd60757d40097a
|
||||||
github.com/gonuts/go-shellquote e842a11b24c6abfb3dd27af69a17f482e4b483c2
|
github.com/gonuts/go-shellquote e842a11b24c6abfb3dd27af69a17f482e4b483c2
|
||||||
github.com/gorilla/context 1ea25387ff6f684839d82767c1733ff4d4d15d0a
|
github.com/gorilla/context 1c83b3eabd45b6d76072b66b746c20815fb2872d
|
||||||
github.com/gorilla/mux c9e326e2bdec29039a3761c07bece13133863e1e
|
github.com/gorilla/mux 26a6070f849969ba72b72256e9f14cf519751690
|
||||||
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
||||||
github.com/influxdata/config b79f6829346b8d6e78ba73544b1e1038f1f1c9da
|
github.com/influxdata/config bae7cb98197d842374d3b8403905924094930f24
|
||||||
github.com/influxdata/influxdb e3fef5593c21644f2b43af55d6e17e70910b0e48
|
github.com/influxdata/influxdb ef571fc104dc24b77cd3710c156cd95e5cfd7aa5
|
||||||
github.com/influxdata/toml af4df43894b16e3fd2b788d01bd27ad0776ef2d0
|
github.com/jmespath/go-jmespath c01cf91b011868172fdcd9f41838e80c9d716264
|
||||||
github.com/klauspost/crc32 19b0b332c9e4516a6370a0456e6182c3b5036720
|
github.com/klauspost/crc32 999f3125931f6557b991b2f8472172bdfa578d38
|
||||||
github.com/lib/pq e182dc4027e2ded4b19396d638610f2653295f36
|
github.com/lib/pq 8ad2b298cadd691a77015666a5372eae5dbfac8f
|
||||||
github.com/lxn/win 9a7734ea4db26bc593d52f6a8a957afdad39c5c1
|
github.com/lxn/win 9a7734ea4db26bc593d52f6a8a957afdad39c5c1
|
||||||
github.com/matttproud/golang_protobuf_extensions d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
github.com/matttproud/golang_protobuf_extensions d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
||||||
github.com/miekg/dns cce6c130cdb92c752850880fd285bea1d64439dd
|
github.com/miekg/dns e0d84d97e59bcb6561eae269c4e94d25b66822cb
|
||||||
github.com/mreiferson/go-snappystream 028eae7ab5c4c9e2d1cb4c4ca1e53259bbe7e504
|
github.com/mreiferson/go-snappystream 028eae7ab5c4c9e2d1cb4c4ca1e53259bbe7e504
|
||||||
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
||||||
github.com/nats-io/nats b13fc9d12b0b123ebc374e6b808c6228ae4234a3
|
github.com/naoina/toml 751171607256bb66e64c9f0220c00662420c38e9
|
||||||
github.com/nats-io/nuid 4f84f5f3b2786224e336af2e13dba0a0a80b76fa
|
github.com/nats-io/nats 6a83f1a633cfbfd90aa648ac99fb38c06a8b40df
|
||||||
github.com/nsqio/go-nsq 0b80d6f05e15ca1930e0c5e1d540ed627e299980
|
github.com/nsqio/go-nsq 2118015c120962edc5d03325c680daf3163a8b5f
|
||||||
github.com/prometheus/client_golang 18acf9993a863f4c4b40612e19cdd243e7c86831
|
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
||||||
|
github.com/prometheus/client_golang 67994f177195311c3ea3d4407ed0175e34a4256f
|
||||||
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||||
github.com/prometheus/common e8eabff8812b05acf522b45fdcd725a785188e37
|
github.com/prometheus/common 14ca1097bbe21584194c15e391a9dab95ad42a59
|
||||||
github.com/prometheus/procfs 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
github.com/prometheus/procfs 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
||||||
github.com/samuel/go-zookeeper 218e9c81c0dd8b3b18172b2bbfad92cc7d6db55f
|
github.com/samuel/go-zookeeper 218e9c81c0dd8b3b18172b2bbfad92cc7d6db55f
|
||||||
github.com/shirou/gopsutil 1de1357e7737a536c7f4ff6be7bd27977db4d2cb
|
github.com/shirou/gopsutil e77438504d45b9985c99a75730fe65220ceea00e
|
||||||
github.com/shirou/w32 ada3ba68f000aa1b58580e45c9d308fe0b7fc5c5
|
github.com/shirou/w32 ada3ba68f000aa1b58580e45c9d308fe0b7fc5c5
|
||||||
github.com/soniah/gosnmp b1b4f885b12c5dcbd021c5cee1c904110de6db7d
|
github.com/soniah/gosnmp b1b4f885b12c5dcbd021c5cee1c904110de6db7d
|
||||||
github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744
|
github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744
|
||||||
github.com/stretchr/testify 1f4a1643a57e798696635ea4c126e9127adb7d3c
|
github.com/stretchr/objx 1a9d0bb9f541897e62256577b352fdbc1fb4fd94
|
||||||
|
github.com/stretchr/testify f390dcf405f7b83c997eac1b06768bb9f44dec18
|
||||||
github.com/wvanbergen/kafka 1a8639a45164fcc245d5c7b4bd3ccfbd1a0ffbf3
|
github.com/wvanbergen/kafka 1a8639a45164fcc245d5c7b4bd3ccfbd1a0ffbf3
|
||||||
github.com/wvanbergen/kazoo-go 0f768712ae6f76454f987c3356177e138df258f8
|
github.com/wvanbergen/kazoo-go 0f768712ae6f76454f987c3356177e138df258f8
|
||||||
github.com/zensqlmonitor/go-mssqldb ffe5510c6fa5e15e6d983210ab501c815b56b363
|
github.com/zensqlmonitor/go-mssqldb ffe5510c6fa5e15e6d983210ab501c815b56b363
|
||||||
golang.org/x/net 6acef71eb69611914f7a30939ea9f6e194c78172
|
golang.org/x/net 04b9de9b512f58addf28c9853d50ebef61c3953e
|
||||||
golang.org/x/text a71fd10341b064c10f4a81ceac72bcf70f26ea34
|
golang.org/x/text 6d3c22c4525a4da167968fa2479be5524d2e8bd0
|
||||||
gopkg.in/dancannon/gorethink.v1 7d1af5be49cb5ecc7b177bf387d232050299d6ef
|
gopkg.in/dancannon/gorethink.v1 6f088135ff288deb9d5546f4c71919207f891a70
|
||||||
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
||||||
gopkg.in/mgo.v2 d90005c5262a3463800497ea5a89aed5fe22c886
|
gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64
|
||||||
gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4
|
gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -22,8 +22,8 @@ build-windows:
|
|||||||
./cmd/telegraf/telegraf.go
|
./cmd/telegraf/telegraf.go
|
||||||
|
|
||||||
build-for-docker:
|
build-for-docker:
|
||||||
CGO_ENABLED=0 GOOS=linux go build -installsuffix cgo -o telegraf -ldflags \
|
CGO_ENABLED=0 GOOS=linux go -o telegraf -ldflags \
|
||||||
"-s -X main.Version=$(VERSION)" \
|
"-X main.Version=$(VERSION)" \
|
||||||
./cmd/telegraf/telegraf.go
|
./cmd/telegraf/telegraf.go
|
||||||
|
|
||||||
# Build with race detector
|
# Build with race detector
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -27,12 +27,12 @@ the [release blog post](https://influxdata.com/blog/announcing-telegraf-0-10-0/)
|
|||||||
### Linux deb and rpm Packages:
|
### Linux deb and rpm Packages:
|
||||||
|
|
||||||
Latest:
|
Latest:
|
||||||
* http://get.influxdb.org/telegraf/telegraf_0.11.1-1_amd64.deb
|
* http://get.influxdb.org/telegraf/telegraf_0.10.3-1_amd64.deb
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1.x86_64.rpm
|
* http://get.influxdb.org/telegraf/telegraf-0.10.3-1.x86_64.rpm
|
||||||
|
|
||||||
Latest (arm):
|
Latest (arm):
|
||||||
* http://get.influxdb.org/telegraf/telegraf_0.11.1-1_armhf.deb
|
* http://get.influxdb.org/telegraf/telegraf_0.10.3-1_arm.deb
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1.armhf.rpm
|
* http://get.influxdb.org/telegraf/telegraf-0.10.3-1.arm.rpm
|
||||||
|
|
||||||
0.2.x:
|
0.2.x:
|
||||||
* http://get.influxdb.org/telegraf/telegraf_0.2.4_amd64.deb
|
* http://get.influxdb.org/telegraf/telegraf_0.2.4_amd64.deb
|
||||||
@@ -56,9 +56,9 @@ for instructions, replacing the `influxdb` package name with `telegraf`.
|
|||||||
### Linux tarballs:
|
### Linux tarballs:
|
||||||
|
|
||||||
Latest:
|
Latest:
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1_linux_amd64.tar.gz
|
* http://get.influxdb.org/telegraf/telegraf-0.10.3-1_linux_amd64.tar.gz
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1_linux_i386.tar.gz
|
* http://get.influxdb.org/telegraf/telegraf-0.10.3-1_linux_i386.tar.gz
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1_linux_armhf.tar.gz
|
* http://get.influxdb.org/telegraf/telegraf-0.10.3-1_linux_arm.tar.gz
|
||||||
|
|
||||||
0.2.x:
|
0.2.x:
|
||||||
* http://get.influxdb.org/telegraf/telegraf_linux_amd64_0.2.4.tar.gz
|
* http://get.influxdb.org/telegraf/telegraf_linux_amd64_0.2.4.tar.gz
|
||||||
@@ -70,13 +70,13 @@ Latest:
|
|||||||
To install the full directory structure with config file, run:
|
To install the full directory structure with config file, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo tar -C / -zxvf ./telegraf-0.11.1-1_linux_amd64.tar.gz
|
sudo tar -C / -zxvf ./telegraf-0.10.3-1_linux_amd64.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
To extract only the binary, run:
|
To extract only the binary, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
tar -zxvf telegraf-0.11.1-1_linux_amd64.tar.gz --strip-components=3 ./usr/bin/telegraf
|
tar -zxvf telegraf-0.10.3-1_linux_amd64.tar.gz --strip-components=3 ./usr/bin/telegraf
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ansible Role:
|
### Ansible Role:
|
||||||
@@ -90,12 +90,6 @@ brew update
|
|||||||
brew install telegraf
|
brew install telegraf
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windows Binaries (EXPERIMENTAL)
|
|
||||||
|
|
||||||
Latest:
|
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1_windows_amd64.zip
|
|
||||||
* http://get.influxdb.org/telegraf/telegraf-0.11.1-1_windows_i386.zip
|
|
||||||
|
|
||||||
### From Source:
|
### From Source:
|
||||||
|
|
||||||
Telegraf manages dependencies via [gdm](https://github.com/sparrc/gdm),
|
Telegraf manages dependencies via [gdm](https://github.com/sparrc/gdm),
|
||||||
@@ -186,7 +180,6 @@ Currently implemented sources:
|
|||||||
* net_response
|
* net_response
|
||||||
* nginx
|
* nginx
|
||||||
* nsq
|
* nsq
|
||||||
* ntpq
|
|
||||||
* phpfpm
|
* phpfpm
|
||||||
* phusion passenger
|
* phusion passenger
|
||||||
* ping
|
* ping
|
||||||
@@ -215,14 +208,10 @@ Currently implemented sources:
|
|||||||
* disk
|
* disk
|
||||||
* diskio
|
* diskio
|
||||||
* swap
|
* swap
|
||||||
* processes
|
|
||||||
* kernel (/proc/stat)
|
|
||||||
|
|
||||||
Telegraf can also collect metrics via the following service plugins:
|
Telegraf can also collect metrics via the following service plugins:
|
||||||
|
|
||||||
* statsd
|
* statsd
|
||||||
* udp_listener
|
|
||||||
* tcp_listener
|
|
||||||
* mqtt_consumer
|
* mqtt_consumer
|
||||||
* kafka_consumer
|
* kafka_consumer
|
||||||
* nats_consumer
|
* nats_consumer
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ func (ac *accumulator) AddFields(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result[k] = v
|
||||||
|
|
||||||
// Validate uint64 and float64 fields
|
// Validate uint64 and float64 fields
|
||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
@@ -115,7 +116,6 @@ func (ac *accumulator) AddFields(
|
|||||||
} else {
|
} else {
|
||||||
result[k] = int64(9223372036854775807)
|
result[k] = int64(9223372036854775807)
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
case float64:
|
case float64:
|
||||||
// NaNs are invalid values in influxdb, skip measurement
|
// NaNs are invalid values in influxdb, skip measurement
|
||||||
if math.IsNaN(val) || math.IsInf(val, 0) {
|
if math.IsNaN(val) || math.IsInf(val, 0) {
|
||||||
@@ -127,8 +127,6 @@ func (ac *accumulator) AddFields(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result[k] = v
|
|
||||||
}
|
}
|
||||||
fields = nil
|
fields = nil
|
||||||
if len(result) == 0 {
|
if len(result) == 0 {
|
||||||
@@ -170,8 +168,5 @@ func (ac *accumulator) setDefaultTags(tags map[string]string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ac *accumulator) addDefaultTag(key, value string) {
|
func (ac *accumulator) addDefaultTag(key, value string) {
|
||||||
if ac.defaultTags == nil {
|
|
||||||
ac.defaultTags = make(map[string]string)
|
|
||||||
}
|
|
||||||
ac.defaultTags[key] = value
|
ac.defaultTags[key] = value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,302 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/internal/models"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
a.Add("acctest", float64(101), map[string]string{})
|
|
||||||
a.Add("acctest", float64(101), map[string]string{"acc": "test"})
|
|
||||||
a.Add("acctest", float64(101), map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest value=101")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test value=101")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test value=101 %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddDefaultTags(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
a.addDefaultTag("default", "tag")
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
a.Add("acctest", float64(101), map[string]string{})
|
|
||||||
a.Add("acctest", float64(101), map[string]string{"acc": "test"})
|
|
||||||
a.Add("acctest", float64(101), map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,default=tag value=101")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test,default=tag value=101")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test,default=tag value=101 %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddFields(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage": float64(99),
|
|
||||||
}
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest usage=99")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test usage=99")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test usage=99 %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that all Inf fields get dropped, and not added to metrics channel
|
|
||||||
func TestAddInfFields(t *testing.T) {
|
|
||||||
inf := math.Inf(1)
|
|
||||||
ninf := math.Inf(-1)
|
|
||||||
|
|
||||||
a := accumulator{}
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage": inf,
|
|
||||||
"nusage": ninf,
|
|
||||||
}
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
assert.Len(t, a.metrics, 0)
|
|
||||||
|
|
||||||
// test that non-inf fields are kept and not dropped
|
|
||||||
fields["notinf"] = float64(100)
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest notinf=100")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that nan fields are dropped and not added
|
|
||||||
func TestAddNaNFields(t *testing.T) {
|
|
||||||
nan := math.NaN()
|
|
||||||
|
|
||||||
a := accumulator{}
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage": nan,
|
|
||||||
}
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
assert.Len(t, a.metrics, 0)
|
|
||||||
|
|
||||||
// test that non-nan fields are kept and not dropped
|
|
||||||
fields["notnan"] = float64(100)
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest notnan=100")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddUint64Fields(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage": uint64(99),
|
|
||||||
}
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest usage=99i")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test usage=99i")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test usage=99i %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddUint64Overflow(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage": uint64(9223372036854775808),
|
|
||||||
}
|
|
||||||
a.AddFields("acctest", fields, map[string]string{})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"})
|
|
||||||
a.AddFields("acctest", fields, map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest usage=9223372036854775807i")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test usage=9223372036854775807i")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test usage=9223372036854775807i %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddInts(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
a.addDefaultTag("default", "tag")
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
a.Add("acctest", int(101), map[string]string{})
|
|
||||||
a.Add("acctest", int32(101), map[string]string{"acc": "test"})
|
|
||||||
a.Add("acctest", int64(101), map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,default=tag value=101i")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test,default=tag value=101i")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test,default=tag value=101i %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddFloats(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
a.addDefaultTag("default", "tag")
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
a.Add("acctest", float32(101), map[string]string{"acc": "test"})
|
|
||||||
a.Add("acctest", float64(101), map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test,default=tag value=101")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test,default=tag value=101 %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddStrings(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
a.addDefaultTag("default", "tag")
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
a.Add("acctest", "test", map[string]string{"acc": "test"})
|
|
||||||
a.Add("acctest", "foo", map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test,default=tag value=\"test\"")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test,default=tag value=\"foo\" %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddBools(t *testing.T) {
|
|
||||||
a := accumulator{}
|
|
||||||
a.addDefaultTag("default", "tag")
|
|
||||||
now := time.Now()
|
|
||||||
a.metrics = make(chan telegraf.Metric, 10)
|
|
||||||
defer close(a.metrics)
|
|
||||||
a.inputConfig = &internal_models.InputConfig{}
|
|
||||||
|
|
||||||
a.Add("acctest", true, map[string]string{"acc": "test"})
|
|
||||||
a.Add("acctest", false, map[string]string{"acc": "test"}, now)
|
|
||||||
|
|
||||||
testm := <-a.metrics
|
|
||||||
actual := testm.String()
|
|
||||||
assert.Contains(t, actual, "acctest,acc=test,default=tag value=true")
|
|
||||||
|
|
||||||
testm = <-a.metrics
|
|
||||||
actual = testm.String()
|
|
||||||
assert.Equal(t,
|
|
||||||
fmt.Sprintf("acctest,acc=test,default=tag value=false %d", now.UnixNano()),
|
|
||||||
actual)
|
|
||||||
}
|
|
||||||
@@ -11,9 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/influxdata/telegraf/agent"
|
"github.com/influxdata/telegraf/agent"
|
||||||
"github.com/influxdata/telegraf/internal/config"
|
"github.com/influxdata/telegraf/internal/config"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/all"
|
_ "github.com/influxdata/telegraf/plugins/inputs/all"
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/outputs/all"
|
_ "github.com/influxdata/telegraf/plugins/outputs/all"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,14 +30,11 @@ var fSampleConfig = flag.Bool("sample-config", false,
|
|||||||
var fPidfile = flag.String("pidfile", "", "file to write our pid to")
|
var fPidfile = flag.String("pidfile", "", "file to write our pid to")
|
||||||
var fInputFilters = flag.String("input-filter", "",
|
var fInputFilters = flag.String("input-filter", "",
|
||||||
"filter the inputs to enable, separator is :")
|
"filter the inputs to enable, separator is :")
|
||||||
var fInputList = flag.Bool("input-list", false,
|
|
||||||
"print available output plugins.")
|
|
||||||
var fOutputFilters = flag.String("output-filter", "",
|
var fOutputFilters = flag.String("output-filter", "",
|
||||||
"filter the outputs to enable, separator is :")
|
"filter the outputs to enable, separator is :")
|
||||||
var fOutputList = flag.Bool("output-list", false,
|
|
||||||
"print available output plugins.")
|
|
||||||
var fUsage = flag.String("usage", "",
|
var fUsage = flag.String("usage", "",
|
||||||
"print usage for a plugin, ie, 'telegraf -usage mysql'")
|
"print usage for a plugin, ie, 'telegraf -usage mysql'")
|
||||||
|
|
||||||
var fInputFiltersLegacy = flag.String("filter", "",
|
var fInputFiltersLegacy = flag.String("filter", "",
|
||||||
"filter the inputs to enable, separator is :")
|
"filter the inputs to enable, separator is :")
|
||||||
var fOutputFiltersLegacy = flag.String("outputfilter", "",
|
var fOutputFiltersLegacy = flag.String("outputfilter", "",
|
||||||
@@ -63,9 +59,7 @@ The flags are:
|
|||||||
-sample-config print out full sample configuration to stdout
|
-sample-config print out full sample configuration to stdout
|
||||||
-config-directory directory containing additional *.conf files
|
-config-directory directory containing additional *.conf files
|
||||||
-input-filter filter the input plugins to enable, separator is :
|
-input-filter filter the input plugins to enable, separator is :
|
||||||
-input-list print all the plugins inputs
|
|
||||||
-output-filter filter the output plugins to enable, separator is :
|
-output-filter filter the output plugins to enable, separator is :
|
||||||
-output-list print all the available outputs
|
|
||||||
-usage print usage for a plugin, ie, 'telegraf -usage mysql'
|
-usage print usage for a plugin, ie, 'telegraf -usage mysql'
|
||||||
-debug print metrics as they're generated to stdout
|
-debug print metrics as they're generated to stdout
|
||||||
-quiet run in quiet mode
|
-quiet run in quiet mode
|
||||||
@@ -96,9 +90,8 @@ func main() {
|
|||||||
reload <- false
|
reload <- false
|
||||||
flag.Usage = func() { usageExit(0) }
|
flag.Usage = func() { usageExit(0) }
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
args := flag.Args()
|
|
||||||
|
|
||||||
if flag.NFlag() == 0 && len(args) == 0 {
|
if flag.NFlag() == 0 {
|
||||||
usageExit(0)
|
usageExit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,34 +115,6 @@ func main() {
|
|||||||
outputFilters = strings.Split(":"+outputFilter+":", ":")
|
outputFilters = strings.Split(":"+outputFilter+":", ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) > 0 {
|
|
||||||
switch args[0] {
|
|
||||||
case "version":
|
|
||||||
v := fmt.Sprintf("Telegraf - Version %s", Version)
|
|
||||||
fmt.Println(v)
|
|
||||||
return
|
|
||||||
case "config":
|
|
||||||
config.PrintSampleConfig(inputFilters, outputFilters)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if *fOutputList {
|
|
||||||
fmt.Println("Available Output Plugins:")
|
|
||||||
for k, _ := range outputs.Outputs {
|
|
||||||
fmt.Printf(" %s\n", k)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if *fInputList {
|
|
||||||
fmt.Println("Available Input Plugins:")
|
|
||||||
for k, _ := range inputs.Inputs {
|
|
||||||
fmt.Printf(" %s\n", k)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if *fVersion {
|
if *fVersion {
|
||||||
v := fmt.Sprintf("Telegraf - Version %s", Version)
|
v := fmt.Sprintf("Telegraf - Version %s", Version)
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ fields which begin with `time_`.
|
|||||||
percpu = true
|
percpu = true
|
||||||
totalcpu = false
|
totalcpu = false
|
||||||
# filter all fields beginning with 'time_'
|
# filter all fields beginning with 'time_'
|
||||||
fielddrop = ["time_*"]
|
drop = ["time_*"]
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Input Config: tagpass and tagdrop
|
#### Input Config: tagpass and tagdrop
|
||||||
@@ -106,7 +106,7 @@ fields which begin with `time_`.
|
|||||||
[[inputs.cpu]]
|
[[inputs.cpu]]
|
||||||
percpu = true
|
percpu = true
|
||||||
totalcpu = false
|
totalcpu = false
|
||||||
fielddrop = ["cpu_time"]
|
drop = ["cpu_time"]
|
||||||
# Don't collect CPU data for cpu6 & cpu7
|
# Don't collect CPU data for cpu6 & cpu7
|
||||||
[inputs.cpu.tagdrop]
|
[inputs.cpu.tagdrop]
|
||||||
cpu = [ "cpu6", "cpu7" ]
|
cpu = [ "cpu6", "cpu7" ]
|
||||||
@@ -199,7 +199,7 @@ to avoid measurement collisions:
|
|||||||
percpu = true
|
percpu = true
|
||||||
totalcpu = false
|
totalcpu = false
|
||||||
name_override = "percpu_usage"
|
name_override = "percpu_usage"
|
||||||
fielddrop = ["cpu_time*"]
|
drop = ["cpu_time*"]
|
||||||
```
|
```
|
||||||
|
|
||||||
## `[outputs.xxx]` Configuration
|
## `[outputs.xxx]` Configuration
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
## Telegraf will cache metric_buffer_limit metrics for each output, and will
|
## Telegraf will cache metric_buffer_limit metrics for each output, and will
|
||||||
## flush this buffer on a successful write.
|
## flush this buffer on a successful write.
|
||||||
metric_buffer_limit = 1000
|
metric_buffer_limit = 10000
|
||||||
## Flush the buffer whenever full, regardless of flush_interval.
|
## Flush the buffer whenever full, regardless of flush_interval.
|
||||||
flush_buffer_when_full = true
|
flush_buffer_when_full = true
|
||||||
|
|
||||||
@@ -56,17 +56,15 @@
|
|||||||
|
|
||||||
# Configuration for influxdb server to send metrics to
|
# Configuration for influxdb server to send metrics to
|
||||||
[[outputs.influxdb]]
|
[[outputs.influxdb]]
|
||||||
## The full HTTP or UDP endpoint URL for your InfluxDB instance.
|
# The full HTTP or UDP endpoint URL for your InfluxDB instance.
|
||||||
## Multiple urls can be specified as part of the same cluster,
|
# Multiple urls can be specified but it is assumed that they are part of the same
|
||||||
## this means that only ONE of the urls will be written to each interval.
|
# cluster, this means that only ONE of the urls will be written to each interval.
|
||||||
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
||||||
urls = ["http://localhost:8086"] # required
|
urls = ["http://localhost:8086"] # required
|
||||||
## The target database for metrics (telegraf will create it if not exists).
|
# The target database for metrics (telegraf will create it if not exists)
|
||||||
database = "telegraf" # required
|
database = "telegraf" # required
|
||||||
## Retention policy to write to.
|
# Precision of writes, valid values are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||||
retention_policy = "default"
|
# note: using second precision greatly helps InfluxDB compression
|
||||||
## Precision of writes, valid values are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
|
||||||
## note: using "s" precision greatly improves InfluxDB compression.
|
|
||||||
precision = "s"
|
precision = "s"
|
||||||
|
|
||||||
## Write timeout (for the InfluxDB client), formatted as a string.
|
## Write timeout (for the InfluxDB client), formatted as a string.
|
||||||
@@ -74,18 +72,11 @@
|
|||||||
timeout = "5s"
|
timeout = "5s"
|
||||||
# username = "telegraf"
|
# username = "telegraf"
|
||||||
# password = "metricsmetricsmetricsmetrics"
|
# password = "metricsmetricsmetricsmetrics"
|
||||||
## Set the user agent for HTTP POSTs (can be useful for log differentiation)
|
# Set the user agent for HTTP POSTs (can be useful for log differentiation)
|
||||||
# user_agent = "telegraf"
|
# user_agent = "telegraf"
|
||||||
## Set UDP payload size, defaults to InfluxDB UDP Client default (512 bytes)
|
# Set UDP payload size, defaults to InfluxDB UDP Client default (512 bytes)
|
||||||
# udp_payload = 512
|
# udp_payload = 512
|
||||||
|
|
||||||
## Optional SSL Config
|
|
||||||
# ssl_ca = "/etc/telegraf/ca.pem"
|
|
||||||
# ssl_cert = "/etc/telegraf/cert.pem"
|
|
||||||
# ssl_key = "/etc/telegraf/key.pem"
|
|
||||||
## Use SSL but skip chain & host verification
|
|
||||||
# insecure_skip_verify = false
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# INPUTS #
|
# INPUTS #
|
||||||
@@ -119,18 +110,10 @@
|
|||||||
# Uncomment the following line if you do not need disk serial numbers.
|
# Uncomment the following line if you do not need disk serial numbers.
|
||||||
# skip_serial_number = true
|
# skip_serial_number = true
|
||||||
|
|
||||||
# Get kernel statistics from /proc/stat
|
|
||||||
[[inputs.kernel]]
|
|
||||||
# no configuration
|
|
||||||
|
|
||||||
# Read metrics about memory usage
|
# Read metrics about memory usage
|
||||||
[[inputs.mem]]
|
[[inputs.mem]]
|
||||||
# no configuration
|
# no configuration
|
||||||
|
|
||||||
# Get the number of processes and group them by status
|
|
||||||
[[inputs.processes]]
|
|
||||||
# no configuration
|
|
||||||
|
|
||||||
# Read metrics about swap memory usage
|
# Read metrics about swap memory usage
|
||||||
[[inputs.swap]]
|
[[inputs.swap]]
|
||||||
# no configuration
|
# no configuration
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
# Telegraf configuration
|
|
||||||
|
|
||||||
# Telegraf is entirely plugin driven. All metrics are gathered from the
|
|
||||||
# declared inputs, and sent to the declared outputs.
|
|
||||||
|
|
||||||
# Plugins must be declared in here to be active.
|
|
||||||
# To deactivate a plugin, comment out the name and any variables.
|
|
||||||
|
|
||||||
# Use 'telegraf -config telegraf.conf -test' to see what metrics a config
|
|
||||||
# file would generate.
|
|
||||||
|
|
||||||
# Global tags can be specified here in key="value" format.
|
|
||||||
[global_tags]
|
|
||||||
# dc = "us-east-1" # will tag all metrics with dc=us-east-1
|
|
||||||
# rack = "1a"
|
|
||||||
|
|
||||||
# Configuration for telegraf agent
|
|
||||||
[agent]
|
|
||||||
## Default data collection interval for all inputs
|
|
||||||
interval = "10s"
|
|
||||||
## Rounds collection interval to 'interval'
|
|
||||||
## ie, if interval="10s" then always collect on :00, :10, :20, etc.
|
|
||||||
round_interval = true
|
|
||||||
|
|
||||||
## Telegraf will cache metric_buffer_limit metrics for each output, and will
|
|
||||||
## flush this buffer on a successful write.
|
|
||||||
metric_buffer_limit = 1000
|
|
||||||
## Flush the buffer whenever full, regardless of flush_interval.
|
|
||||||
flush_buffer_when_full = true
|
|
||||||
|
|
||||||
## Collection jitter is used to jitter the collection by a random amount.
|
|
||||||
## Each plugin will sleep for a random time within jitter before collecting.
|
|
||||||
## This can be used to avoid many plugins querying things like sysfs at the
|
|
||||||
## same time, which can have a measurable effect on the system.
|
|
||||||
collection_jitter = "0s"
|
|
||||||
|
|
||||||
## Default flushing interval for all outputs. You shouldn't set this below
|
|
||||||
## interval. Maximum flush_interval will be flush_interval + flush_jitter
|
|
||||||
flush_interval = "10s"
|
|
||||||
## Jitter the flush interval by a random amount. This is primarily to avoid
|
|
||||||
## large write spikes for users running a large number of telegraf instances.
|
|
||||||
## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s
|
|
||||||
flush_jitter = "0s"
|
|
||||||
|
|
||||||
## Run telegraf in debug mode
|
|
||||||
debug = false
|
|
||||||
## Run telegraf in quiet mode
|
|
||||||
quiet = false
|
|
||||||
## Override default hostname, if empty use os.Hostname()
|
|
||||||
hostname = ""
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# OUTPUTS #
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
# Configuration for influxdb server to send metrics to
|
|
||||||
[[outputs.influxdb]]
|
|
||||||
# The full HTTP or UDP endpoint URL for your InfluxDB instance.
|
|
||||||
# Multiple urls can be specified but it is assumed that they are part of the same
|
|
||||||
# cluster, this means that only ONE of the urls will be written to each interval.
|
|
||||||
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
|
||||||
urls = ["http://localhost:8086"] # required
|
|
||||||
# The target database for metrics (telegraf will create it if not exists)
|
|
||||||
database = "telegraf" # required
|
|
||||||
# Precision of writes, valid values are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
|
||||||
# note: using second precision greatly helps InfluxDB compression
|
|
||||||
precision = "s"
|
|
||||||
|
|
||||||
## Write timeout (for the InfluxDB client), formatted as a string.
|
|
||||||
## If not provided, will default to 5s. 0s means no timeout (not recommended).
|
|
||||||
timeout = "5s"
|
|
||||||
# username = "telegraf"
|
|
||||||
# password = "metricsmetricsmetricsmetrics"
|
|
||||||
# Set the user agent for HTTP POSTs (can be useful for log differentiation)
|
|
||||||
# user_agent = "telegraf"
|
|
||||||
# Set UDP payload size, defaults to InfluxDB UDP Client default (512 bytes)
|
|
||||||
# udp_payload = 512
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# INPUTS #
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
# Windows Performance Counters plugin.
|
|
||||||
# These are the recommended method of monitoring system metrics on windows,
|
|
||||||
# as the regular system plugins (inputs.cpu, inputs.mem, etc.) rely on WMI,
|
|
||||||
# which utilizes a lot of system resources.
|
|
||||||
#
|
|
||||||
# See more configuration examples at:
|
|
||||||
# https://github.com/influxdata/telegraf/tree/master/plugins/inputs/win_perf_counters
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters]]
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
# Processor usage, alternative to native, reports on a per core.
|
|
||||||
ObjectName = "Processor"
|
|
||||||
Instances = ["*"]
|
|
||||||
Counters = ["% Idle Time", "% Interrupt Time", "% Privileged Time", "% User Time", "% Processor Time"]
|
|
||||||
Measurement = "win_cpu"
|
|
||||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
# Disk times and queues
|
|
||||||
ObjectName = "LogicalDisk"
|
|
||||||
Instances = ["*"]
|
|
||||||
Counters = ["% Idle Time", "% Disk Time","% Disk Read Time", "% Disk Write Time", "% User Time", "Current Disk Queue Length"]
|
|
||||||
Measurement = "win_disk"
|
|
||||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
ObjectName = "System"
|
|
||||||
Counters = ["Context Switches/sec","System Calls/sec"]
|
|
||||||
Instances = ["------"]
|
|
||||||
Measurement = "win_system"
|
|
||||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
|
||||||
|
|
||||||
[[inputs.win_perf_counters.object]]
|
|
||||||
# Example query where the Instance portion must be removed to get data back, such as from the Memory object.
|
|
||||||
ObjectName = "Memory"
|
|
||||||
Counters = ["Available Bytes","Cache Faults/sec","Demand Zero Faults/sec","Page Faults/sec","Pages/sec","Transition Faults/sec","Pool Nonpaged Bytes","Pool Paged Bytes"]
|
|
||||||
Instances = ["------"] # Use 6 x - to remove the Instance bit from the query.
|
|
||||||
Measurement = "win_mem"
|
|
||||||
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
|
|
||||||
|
|
||||||
|
|
||||||
# Windows system plugins using WMI (disabled by default, using
|
|
||||||
# win_perf_counters over WMI is recommended)
|
|
||||||
|
|
||||||
# Read metrics about cpu usage
|
|
||||||
#[[inputs.cpu]]
|
|
||||||
## Whether to report per-cpu stats or not
|
|
||||||
#percpu = true
|
|
||||||
## Whether to report total system cpu stats or not
|
|
||||||
#totalcpu = true
|
|
||||||
## Comment this line if you want the raw CPU time metrics
|
|
||||||
#fielddrop = ["time_*"]
|
|
||||||
|
|
||||||
# Read metrics about disk usage by mount point
|
|
||||||
#[[inputs.disk]]
|
|
||||||
## By default, telegraf gather stats for all mountpoints.
|
|
||||||
## Setting mountpoints will restrict the stats to the specified mountpoints.
|
|
||||||
## mount_points=["/"]
|
|
||||||
|
|
||||||
## Ignore some mountpoints by filesystem type. For example (dev)tmpfs (usually
|
|
||||||
## present on /run, /var/run, /dev/shm or /dev).
|
|
||||||
#ignore_fs = ["tmpfs", "devtmpfs"]
|
|
||||||
|
|
||||||
# Read metrics about disk IO by device
|
|
||||||
#[[inputs.diskio]]
|
|
||||||
## By default, telegraf will gather stats for all devices including
|
|
||||||
## disk partitions.
|
|
||||||
## Setting devices will restrict the stats to the specified devices.
|
|
||||||
## devices = ["sda", "sdb"]
|
|
||||||
## Uncomment the following line if you do not need disk serial numbers.
|
|
||||||
## skip_serial_number = true
|
|
||||||
|
|
||||||
# Read metrics about memory usage
|
|
||||||
#[[inputs.mem]]
|
|
||||||
# no configuration
|
|
||||||
|
|
||||||
# Read metrics about swap memory usage
|
|
||||||
#[[inputs.swap]]
|
|
||||||
# no configuration
|
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"github.com/influxdata/telegraf/plugins/serializers"
|
"github.com/influxdata/telegraf/plugins/serializers"
|
||||||
|
|
||||||
"github.com/influxdata/config"
|
"github.com/influxdata/config"
|
||||||
"github.com/influxdata/toml/ast"
|
"github.com/naoina/toml/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config specifies the URL/user/password for the database that telegraf
|
// Config specifies the URL/user/password for the database that telegraf
|
||||||
@@ -159,7 +159,7 @@ var header = `# Telegraf Configuration
|
|||||||
|
|
||||||
## Telegraf will cache metric_buffer_limit metrics for each output, and will
|
## Telegraf will cache metric_buffer_limit metrics for each output, and will
|
||||||
## flush this buffer on a successful write.
|
## flush this buffer on a successful write.
|
||||||
metric_buffer_limit = 1000
|
metric_buffer_limit = 10000
|
||||||
## Flush the buffer whenever full, regardless of flush_interval.
|
## Flush the buffer whenever full, regardless of flush_interval.
|
||||||
flush_buffer_when_full = true
|
flush_buffer_when_full = true
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Default number of metrics kept between flushes.
|
// Default number of metrics kept between flushes.
|
||||||
DEFAULT_METRIC_BUFFER_LIMIT = 1000
|
DEFAULT_METRIC_BUFFER_LIMIT = 10000
|
||||||
|
|
||||||
// Limit how many full metric buffers are kept due to failed writes.
|
// Limit how many full metric buffers are kept due to failed writes.
|
||||||
FULL_METRIC_BUFFERS_LIMIT = 100
|
FULL_METRIC_BUFFERS_LIMIT = 100
|
||||||
@@ -82,11 +82,9 @@ func (ro *RunningOutput) AddMetric(metric telegraf.Metric) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ro.overwriteI == 0 {
|
log.Printf("WARNING: overwriting cached metrics, you may want to " +
|
||||||
log.Printf("WARNING: overwriting cached metrics, you may want to " +
|
"increase the metric_buffer_limit setting in your [agent] " +
|
||||||
"increase the metric_buffer_limit setting in your [agent] " +
|
"config if you do not wish to overwrite metrics.\n")
|
||||||
"config if you do not wish to overwrite metrics.\n")
|
|
||||||
}
|
|
||||||
if ro.overwriteI == len(ro.metrics) {
|
if ro.overwriteI == len(ro.metrics) {
|
||||||
ro.overwriteI = 0
|
ro.overwriteI = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ The example plugin gathers metrics about example things
|
|||||||
|
|
||||||
### Example Output:
|
### Example Output:
|
||||||
|
|
||||||
|
Give an example `-test` output here
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./telegraf -config telegraf.conf -input-filter example -test
|
$ ./telegraf -config telegraf.conf -input-filter example -test
|
||||||
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
|
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import (
|
|||||||
_ "github.com/influxdata/telegraf/plugins/inputs/net_response"
|
_ "github.com/influxdata/telegraf/plugins/inputs/net_response"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/nginx"
|
_ "github.com/influxdata/telegraf/plugins/inputs/nginx"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/nsq"
|
_ "github.com/influxdata/telegraf/plugins/inputs/nsq"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/ntpq"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/passenger"
|
_ "github.com/influxdata/telegraf/plugins/inputs/passenger"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/phpfpm"
|
_ "github.com/influxdata/telegraf/plugins/inputs/phpfpm"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/ping"
|
_ "github.com/influxdata/telegraf/plugins/inputs/ping"
|
||||||
@@ -48,10 +47,8 @@ import (
|
|||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/system"
|
_ "github.com/influxdata/telegraf/plugins/inputs/system"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/tcp_listener"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/trig"
|
_ "github.com/influxdata/telegraf/plugins/inputs/trig"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/twemproxy"
|
_ "github.com/influxdata/telegraf/plugins/inputs/twemproxy"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/udp_listener"
|
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/win_perf_counters"
|
_ "github.com/influxdata/telegraf/plugins/inputs/win_perf_counters"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/zfs"
|
_ "github.com/influxdata/telegraf/plugins/inputs/zfs"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/zookeeper"
|
_ "github.com/influxdata/telegraf/plugins/inputs/zookeeper"
|
||||||
|
|||||||
@@ -58,10 +58,7 @@ var tr = &http.Transport{
|
|||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = &http.Client{
|
var client = &http.Client{Transport: tr}
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Apache) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
|
func (n *Apache) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
|
||||||
resp, err := client.Get(addr.String())
|
resp, err := client.Get(addr.String())
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Schema:
|
// Schema:
|
||||||
@@ -113,18 +112,9 @@ func (c *CouchDB) Gather(accumulator telegraf.Accumulator) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tr = &http.Transport{
|
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CouchDB) fetchAndInsertData(accumulator telegraf.Accumulator, host string) error {
|
func (c *CouchDB) fetchAndInsertData(accumulator telegraf.Accumulator, host string) error {
|
||||||
|
|
||||||
response, error := client.Get(host)
|
response, error := http.Get(host)
|
||||||
if error != nil {
|
if error != nil {
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
@@ -31,8 +30,6 @@ var sampleConfig = `
|
|||||||
servers = ["localhost"]
|
servers = ["localhost"]
|
||||||
`
|
`
|
||||||
|
|
||||||
var defaultTimeout = 5 * time.Second
|
|
||||||
|
|
||||||
func (r *Disque) SampleConfig() string {
|
func (r *Disque) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
@@ -110,7 +107,7 @@ func (g *Disque) gatherServer(addr *url.URL, acc telegraf.Accumulator) error {
|
|||||||
addr.Host = addr.Host + ":" + defaultPort
|
addr.Host = addr.Host + ":" + defaultPort
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := net.DialTimeout("tcp", addr.Host, defaultTimeout)
|
c, err := net.Dial("tcp", addr.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to connect to disque server '%s': %s", addr.Host, err)
|
return fmt.Errorf("Unable to connect to disque server '%s': %s", addr.Host, err)
|
||||||
}
|
}
|
||||||
@@ -135,9 +132,6 @@ func (g *Disque) gatherServer(addr *url.URL, acc telegraf.Accumulator) error {
|
|||||||
g.c = c
|
g.c = c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extend connection
|
|
||||||
g.c.SetDeadline(time.Now().Add(defaultTimeout))
|
|
||||||
|
|
||||||
g.c.Write([]byte("info\r\n"))
|
g.c.Write([]byte("info\r\n"))
|
||||||
|
|
||||||
r := bufio.NewReader(g.c)
|
r := bufio.NewReader(g.c)
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
package dns_query
|
package dns_query
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var servers = []string{"8.8.8.8"}
|
var servers = []string{"8.8.8.8"}
|
||||||
var domains = []string{"google.com"}
|
var domains = []string{"mjasion.pl"}
|
||||||
|
|
||||||
func TestGathering(t *testing.T) {
|
func TestGathering(t *testing.T) {
|
||||||
var dnsConfig = DnsQuery{
|
var dnsConfig = DnsQuery{
|
||||||
@@ -21,10 +18,8 @@ func TestGathering(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
err := dnsConfig.Gather(&acc)
|
dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
metric, _ := acc.Get("dns_query")
|
||||||
metric, ok := acc.Get("dns_query")
|
|
||||||
require.True(t, ok)
|
|
||||||
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
||||||
|
|
||||||
assert.NotEqual(t, 0, queryTime)
|
assert.NotEqual(t, 0, queryTime)
|
||||||
@@ -38,10 +33,8 @@ func TestGatheringMxRecord(t *testing.T) {
|
|||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
dnsConfig.RecordType = "MX"
|
dnsConfig.RecordType = "MX"
|
||||||
|
|
||||||
err := dnsConfig.Gather(&acc)
|
dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
metric, _ := acc.Get("dns_query")
|
||||||
metric, ok := acc.Get("dns_query")
|
|
||||||
require.True(t, ok)
|
|
||||||
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
||||||
|
|
||||||
assert.NotEqual(t, 0, queryTime)
|
assert.NotEqual(t, 0, queryTime)
|
||||||
@@ -61,10 +54,8 @@ func TestGatheringRootDomain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fields := map[string]interface{}{}
|
fields := map[string]interface{}{}
|
||||||
|
|
||||||
err := dnsConfig.Gather(&acc)
|
dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
metric, _ := acc.Get("dns_query")
|
||||||
metric, ok := acc.Get("dns_query")
|
|
||||||
require.True(t, ok)
|
|
||||||
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
||||||
|
|
||||||
fields["query_time_ms"] = queryTime
|
fields["query_time_ms"] = queryTime
|
||||||
@@ -79,15 +70,13 @@ func TestMetricContainsServerAndDomainAndRecordTypeTags(t *testing.T) {
|
|||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"server": "8.8.8.8",
|
"server": "8.8.8.8",
|
||||||
"domain": "google.com",
|
"domain": "mjasion.pl",
|
||||||
"record_type": "NS",
|
"record_type": "NS",
|
||||||
}
|
}
|
||||||
fields := map[string]interface{}{}
|
fields := map[string]interface{}{}
|
||||||
|
|
||||||
err := dnsConfig.Gather(&acc)
|
dnsConfig.Gather(&acc)
|
||||||
assert.NoError(t, err)
|
metric, _ := acc.Get("dns_query")
|
||||||
metric, ok := acc.Get("dns_query")
|
|
||||||
require.True(t, ok)
|
|
||||||
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
queryTime, _ := metric.Fields["query_time_ms"].(float64)
|
||||||
|
|
||||||
fields["query_time_ms"] = queryTime
|
fields["query_time_ms"] = queryTime
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ on the availability of per-cpu stats on your system.
|
|||||||
- usage_in_usermode
|
- usage_in_usermode
|
||||||
- usage_system
|
- usage_system
|
||||||
- usage_total
|
- usage_total
|
||||||
- usage_percent
|
|
||||||
- docker_net
|
- docker_net
|
||||||
- rx_dropped
|
- rx_dropped
|
||||||
- rx_bytes
|
- rx_bytes
|
||||||
@@ -95,50 +94,18 @@ on the availability of per-cpu stats on your system.
|
|||||||
- io_serviced_recursive_sync
|
- io_serviced_recursive_sync
|
||||||
- io_serviced_recursive_total
|
- io_serviced_recursive_total
|
||||||
- io_serviced_recursive_write
|
- io_serviced_recursive_write
|
||||||
- docker_
|
|
||||||
- n_used_file_descriptors
|
|
||||||
- n_cpus
|
|
||||||
- n_containers
|
|
||||||
- n_images
|
|
||||||
- n_goroutines
|
|
||||||
- n_listener_events
|
|
||||||
- memory_total
|
|
||||||
- pool_blocksize
|
|
||||||
- docker_data
|
|
||||||
- available
|
|
||||||
- total
|
|
||||||
- used
|
|
||||||
- docker_metadata
|
|
||||||
- available
|
|
||||||
- total
|
|
||||||
- used
|
|
||||||
|
|
||||||
|
|
||||||
### Tags:
|
### Tags:
|
||||||
|
|
||||||
- docker (memory_total)
|
- All stats have the following tags:
|
||||||
- unit=bytes
|
|
||||||
- docker (pool_blocksize)
|
|
||||||
- unit=bytes
|
|
||||||
- docker_data
|
|
||||||
- unit=bytes
|
|
||||||
- docker_metadata
|
|
||||||
- unit=bytes
|
|
||||||
|
|
||||||
- docker_cpu specific:
|
|
||||||
- cont_id (container ID)
|
- cont_id (container ID)
|
||||||
- cont_image (container image)
|
- cont_image (container image)
|
||||||
- cont_name (container name)
|
- cont_name (container name)
|
||||||
|
- docker_cpu specific:
|
||||||
- cpu
|
- cpu
|
||||||
- docker_net specific:
|
- docker_net specific:
|
||||||
- cont_id (container ID)
|
|
||||||
- cont_image (container image)
|
|
||||||
- cont_name (container name)
|
|
||||||
- network
|
- network
|
||||||
- docker_blkio specific:
|
- docker_blkio specific:
|
||||||
- cont_id (container ID)
|
|
||||||
- cont_image (container image)
|
|
||||||
- cont_name (container name)
|
|
||||||
- device
|
- device
|
||||||
|
|
||||||
### Example Output:
|
### Example Output:
|
||||||
@@ -146,16 +113,6 @@ on the availability of per-cpu stats on your system.
|
|||||||
```
|
```
|
||||||
% ./telegraf -config ~/ws/telegraf.conf -input-filter docker -test
|
% ./telegraf -config ~/ws/telegraf.conf -input-filter docker -test
|
||||||
* Plugin: docker, Collection 1
|
* Plugin: docker, Collection 1
|
||||||
> docker n_cpus=8i 1456926671065383978
|
|
||||||
> docker n_used_file_descriptors=15i 1456926671065383978
|
|
||||||
> docker n_containers=7i 1456926671065383978
|
|
||||||
> docker n_images=152i 1456926671065383978
|
|
||||||
> docker n_goroutines=36i 1456926671065383978
|
|
||||||
> docker n_listener_events=0i 1456926671065383978
|
|
||||||
> docker,unit=bytes memory_total=18935443456i 1456926671065383978
|
|
||||||
> docker,unit=bytes pool_blocksize=65540i 1456926671065383978
|
|
||||||
> docker_data,unit=bytes available=24340000000i,total=107400000000i,used=14820000000i 1456926671065383978
|
|
||||||
> docker_metadata,unit=bytes available=2126999999i,total=2146999999i,used=20420000i 145692667106538
|
|
||||||
> docker_mem,cont_id=5705ba8ed8fb47527410653d60a8bb2f3af5e62372297c419022a3cc6d45d848,\
|
> docker_mem,cont_id=5705ba8ed8fb47527410653d60a8bb2f3af5e62372297c419022a3cc6d45d848,\
|
||||||
cont_image=spotify/kafka,cont_name=kafka \
|
cont_image=spotify/kafka,cont_name=kafka \
|
||||||
active_anon=52568064i,active_file=6926336i,cache=12038144i,fail_count=0i,\
|
active_anon=52568064i,active_file=6926336i,cache=12038144i,fail_count=0i,\
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -20,29 +17,9 @@ type Docker struct {
|
|||||||
Endpoint string
|
Endpoint string
|
||||||
ContainerNames []string
|
ContainerNames []string
|
||||||
|
|
||||||
client DockerClient
|
client *docker.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type DockerClient interface {
|
|
||||||
// Docker Client wrapper
|
|
||||||
// Useful for test
|
|
||||||
Info() (*docker.Env, error)
|
|
||||||
ListContainers(opts docker.ListContainersOptions) ([]docker.APIContainers, error)
|
|
||||||
Stats(opts docker.StatsOptions) error
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
KB = 1000
|
|
||||||
MB = 1000 * KB
|
|
||||||
GB = 1000 * MB
|
|
||||||
TB = 1000 * GB
|
|
||||||
PB = 1000 * TB
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
## Docker Endpoint
|
## Docker Endpoint
|
||||||
## To use TCP, set endpoint = "tcp://[ip]:[port]"
|
## To use TCP, set endpoint = "tcp://[ip]:[port]"
|
||||||
@@ -81,20 +58,12 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
|
|||||||
d.client = c
|
d.client = c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get daemon info
|
|
||||||
err := d.gatherInfo(acc)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// List containers
|
|
||||||
opts := docker.ListContainersOptions{}
|
opts := docker.ListContainersOptions{}
|
||||||
containers, err := d.client.ListContainers(opts)
|
containers, err := d.client.ListContainers(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get container data
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(containers))
|
wg.Add(len(containers))
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
@@ -112,76 +81,6 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Docker) gatherInfo(acc telegraf.Accumulator) error {
|
|
||||||
// Init vars
|
|
||||||
var driverStatus [][]string
|
|
||||||
dataFields := make(map[string]interface{})
|
|
||||||
metadataFields := make(map[string]interface{})
|
|
||||||
now := time.Now()
|
|
||||||
// Get info from docker daemon
|
|
||||||
info, err := d.client.Info()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"n_cpus": info.GetInt64("NCPU"),
|
|
||||||
"n_used_file_descriptors": info.GetInt64("NFd"),
|
|
||||||
"n_containers": info.GetInt64("Containers"),
|
|
||||||
"n_images": info.GetInt64("Images"),
|
|
||||||
"n_goroutines": info.GetInt64("NGoroutines"),
|
|
||||||
"n_listener_events": info.GetInt64("NEventsListener"),
|
|
||||||
}
|
|
||||||
// Add metrics
|
|
||||||
acc.AddFields("docker",
|
|
||||||
fields,
|
|
||||||
nil,
|
|
||||||
now)
|
|
||||||
acc.AddFields("docker",
|
|
||||||
map[string]interface{}{"memory_total": info.GetInt64("MemTotal")},
|
|
||||||
map[string]string{"unit": "bytes"},
|
|
||||||
now)
|
|
||||||
// Get storage metrics
|
|
||||||
driverStatusRaw := []byte(info.Get("DriverStatus"))
|
|
||||||
json.Unmarshal(driverStatusRaw, &driverStatus)
|
|
||||||
for _, rawData := range driverStatus {
|
|
||||||
// Try to convert string to int (bytes)
|
|
||||||
value, err := parseSize(rawData[1])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := strings.ToLower(strings.Replace(rawData[0], " ", "_", -1))
|
|
||||||
if name == "pool_blocksize" {
|
|
||||||
// pool blocksize
|
|
||||||
acc.AddFields("docker",
|
|
||||||
map[string]interface{}{"pool_blocksize": value},
|
|
||||||
map[string]string{"unit": "bytes"},
|
|
||||||
now)
|
|
||||||
} else if strings.HasPrefix(name, "data_space_") {
|
|
||||||
// data space
|
|
||||||
field_name := strings.TrimPrefix(name, "data_space_")
|
|
||||||
dataFields[field_name] = value
|
|
||||||
} else if strings.HasPrefix(name, "metadata_space_") {
|
|
||||||
// metadata space
|
|
||||||
field_name := strings.TrimPrefix(name, "metadata_space_")
|
|
||||||
metadataFields[field_name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(dataFields) > 0 {
|
|
||||||
acc.AddFields("docker_data",
|
|
||||||
dataFields,
|
|
||||||
map[string]string{"unit": "bytes"},
|
|
||||||
now)
|
|
||||||
}
|
|
||||||
if len(metadataFields) > 0 {
|
|
||||||
acc.AddFields("docker_metadata",
|
|
||||||
metadataFields,
|
|
||||||
map[string]string{"unit": "bytes"},
|
|
||||||
now)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Docker) gatherContainer(
|
func (d *Docker) gatherContainer(
|
||||||
container docker.APIContainers,
|
container docker.APIContainers,
|
||||||
acc telegraf.Accumulator,
|
acc telegraf.Accumulator,
|
||||||
@@ -435,27 +334,6 @@ func sliceContains(in string, sl []string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses the human-readable size string into the amount it represents.
|
|
||||||
func parseSize(sizeStr string) (int64, error) {
|
|
||||||
matches := sizeRegex.FindStringSubmatch(sizeStr)
|
|
||||||
if len(matches) != 4 {
|
|
||||||
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
size, err := strconv.ParseFloat(matches[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
uMap := map[string]int64{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
|
||||||
unitPrefix := strings.ToLower(matches[3])
|
|
||||||
if mul, ok := uMap[unitPrefix]; ok {
|
|
||||||
size *= float64(mul)
|
|
||||||
}
|
|
||||||
|
|
||||||
return int64(size), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("docker", func() telegraf.Input {
|
inputs.Add("docker", func() telegraf.Input {
|
||||||
return &Docker{}
|
return &Docker{}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/fsouza/go-dockerclient"
|
"github.com/fsouza/go-dockerclient"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDockerGatherContainerStats(t *testing.T) {
|
func TestDockerGatherContainerStats(t *testing.T) {
|
||||||
@@ -196,186 +194,3 @@ func testStats() *docker.Stats {
|
|||||||
|
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeDockerClient struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d FakeDockerClient) Info() (*docker.Env, error) {
|
|
||||||
env := docker.Env{"Containers=108", "OomKillDisable=false", "SystemTime=2016-02-24T00:55:09.15073105-05:00", "NEventsListener=0", "ID=5WQQ:TFWR:FDNG:OKQ3:37Y4:FJWG:QIKK:623T:R3ME:QTKB:A7F7:OLHD", "Debug=false", "LoggingDriver=json-file", "KernelVersion=4.3.0-1-amd64", "IndexServerAddress=https://index.docker.io/v1/", "MemTotal=3840757760", "Images=199", "CpuCfsQuota=true", "Name=absol", "SwapLimit=false", "IPv4Forwarding=true", "ExecutionDriver=native-0.2", "InitSha1=23a51f3c916d2b5a3bbb31caf301fd2d14edd518", "ExperimentalBuild=false", "CpuCfsPeriod=true", "RegistryConfig={\"IndexConfigs\":{\"docker.io\":{\"Mirrors\":null,\"Name\":\"docker.io\",\"Official\":true,\"Secure\":true}},\"InsecureRegistryCIDRs\":[\"127.0.0.0/8\"],\"Mirrors\":null}", "OperatingSystem=Linux Mint LMDE (containerized)", "BridgeNfIptables=true", "HttpsProxy=", "Labels=null", "MemoryLimit=false", "DriverStatus=[[\"Pool Name\",\"docker-8:1-1182287-pool\"],[\"Pool Blocksize\",\"65.54 kB\"],[\"Backing Filesystem\",\"extfs\"],[\"Data file\",\"/dev/loop0\"],[\"Metadata file\",\"/dev/loop1\"],[\"Data Space Used\",\"17.3 GB\"],[\"Data Space Total\",\"107.4 GB\"],[\"Data Space Available\",\"36.53 GB\"],[\"Metadata Space Used\",\"20.97 MB\"],[\"Metadata Space Total\",\"2.147 GB\"],[\"Metadata Space Available\",\"2.127 GB\"],[\"Udev Sync Supported\",\"true\"],[\"Deferred Removal Enabled\",\"false\"],[\"Data loop file\",\"/var/lib/docker/devicemapper/devicemapper/data\"],[\"Metadata loop file\",\"/var/lib/docker/devicemapper/devicemapper/metadata\"],[\"Library Version\",\"1.02.115 (2016-01-25)\"]]", "NFd=19", "HttpProxy=", "Driver=devicemapper", "NGoroutines=39", "InitPath=/usr/lib/docker.io/dockerinit", "NCPU=4", "DockerRootDir=/var/lib/docker", "NoProxy=", "BridgeNfIp6tables=true"}
|
|
||||||
return &env, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d FakeDockerClient) ListContainers(opts docker.ListContainersOptions) ([]docker.APIContainers, error) {
|
|
||||||
container1 := docker.APIContainers{
|
|
||||||
ID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
|
||||||
Image: "quay.io/coreos/etcd:v2.2.2",
|
|
||||||
Command: "/etcd -name etcd0 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379",
|
|
||||||
Created: 1455941930,
|
|
||||||
Status: "Up 4 hours",
|
|
||||||
Ports: []docker.APIPort{
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 7001,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 4001,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 2380,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 2379,
|
|
||||||
PublicPort: 2379,
|
|
||||||
Type: "tcp",
|
|
||||||
IP: "0.0.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SizeRw: 0,
|
|
||||||
SizeRootFs: 0,
|
|
||||||
Names: []string{"/etcd"},
|
|
||||||
}
|
|
||||||
container2 := docker.APIContainers{
|
|
||||||
ID: "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
|
||||||
Image: "quay.io/coreos/etcd:v2.2.2",
|
|
||||||
Command: "/etcd -name etcd2 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379",
|
|
||||||
Created: 1455941933,
|
|
||||||
Status: "Up 4 hours",
|
|
||||||
Ports: []docker.APIPort{
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 7002,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 4002,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 2381,
|
|
||||||
PublicPort: 0,
|
|
||||||
Type: "tcp",
|
|
||||||
},
|
|
||||||
docker.APIPort{
|
|
||||||
PrivatePort: 2382,
|
|
||||||
PublicPort: 2382,
|
|
||||||
Type: "tcp",
|
|
||||||
IP: "0.0.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SizeRw: 0,
|
|
||||||
SizeRootFs: 0,
|
|
||||||
Names: []string{"/etcd2"},
|
|
||||||
}
|
|
||||||
|
|
||||||
containers := []docker.APIContainers{container1, container2}
|
|
||||||
return containers, nil
|
|
||||||
|
|
||||||
//#{e6a96c84ca91a5258b7cb752579fb68826b68b49ff957487695cd4d13c343b44 titilambert/snmpsim /bin/sh -c 'snmpsimd --agent-udpv4-endpoint=0.0.0.0:31161 --process-user=root --process-group=user' 1455724831 Up 4 hours [{31161 31161 udp 0.0.0.0}] 0 0 [/snmp] map[]}]2016/02/24 01:05:01 Gathered metrics, (3s interval), from 1 inputs in 1.233836656s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d FakeDockerClient) Stats(opts docker.StatsOptions) error {
|
|
||||||
jsonStat := `{"read":"2016-02-24T11:42:27.472459608-05:00","memory_stats":{"stats":{},"limit":18935443456},"blkio_stats":{"io_service_bytes_recursive":[{"major":252,"minor":1,"op":"Read","value":753664},{"major":252,"minor":1,"op":"Write"},{"major":252,"minor":1,"op":"Sync"},{"major":252,"minor":1,"op":"Async","value":753664},{"major":252,"minor":1,"op":"Total","value":753664}],"io_serviced_recursive":[{"major":252,"minor":1,"op":"Read","value":26},{"major":252,"minor":1,"op":"Write"},{"major":252,"minor":1,"op":"Sync"},{"major":252,"minor":1,"op":"Async","value":26},{"major":252,"minor":1,"op":"Total","value":26}]},"cpu_stats":{"cpu_usage":{"percpu_usage":[17871,4959158,1646137,1231652,11829401,244656,369972,0],"usage_in_usermode":10000000,"total_usage":20298847},"system_cpu_usage":24052607520000000,"throttling_data":{}},"precpu_stats":{"cpu_usage":{"percpu_usage":[17871,4959158,1646137,1231652,11829401,244656,369972,0],"usage_in_usermode":10000000,"total_usage":20298847},"system_cpu_usage":24052599550000000,"throttling_data":{}}}`
|
|
||||||
var stat docker.Stats
|
|
||||||
json.Unmarshal([]byte(jsonStat), &stat)
|
|
||||||
opts.Stats <- &stat
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDockerGatherInfo(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
client := FakeDockerClient{}
|
|
||||||
d := Docker{client: client}
|
|
||||||
|
|
||||||
err := d.Gather(&acc)
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t,
|
|
||||||
"docker",
|
|
||||||
map[string]interface{}{
|
|
||||||
"n_listener_events": int64(0),
|
|
||||||
"n_cpus": int64(4),
|
|
||||||
"n_used_file_descriptors": int64(19),
|
|
||||||
"n_containers": int64(108),
|
|
||||||
"n_images": int64(199),
|
|
||||||
"n_goroutines": int64(39),
|
|
||||||
},
|
|
||||||
map[string]string{},
|
|
||||||
)
|
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t,
|
|
||||||
"docker_data",
|
|
||||||
map[string]interface{}{
|
|
||||||
"used": int64(17300000000),
|
|
||||||
"total": int64(107400000000),
|
|
||||||
"available": int64(36530000000),
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"unit": "bytes",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
acc.AssertContainsTaggedFields(t,
|
|
||||||
"docker_cpu",
|
|
||||||
map[string]interface{}{
|
|
||||||
"usage_total": uint64(1231652),
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"cont_id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
|
||||||
"cont_name": "etcd2",
|
|
||||||
"cont_image": "quay.io/coreos/etcd:v2.2.2",
|
|
||||||
"cpu": "cpu3",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
acc.AssertContainsTaggedFields(t,
|
|
||||||
"docker_mem",
|
|
||||||
map[string]interface{}{
|
|
||||||
"total_pgpgout": uint64(0),
|
|
||||||
"usage_percent": float64(0),
|
|
||||||
"rss": uint64(0),
|
|
||||||
"total_writeback": uint64(0),
|
|
||||||
"active_anon": uint64(0),
|
|
||||||
"total_pgmafault": uint64(0),
|
|
||||||
"total_rss": uint64(0),
|
|
||||||
"total_unevictable": uint64(0),
|
|
||||||
"active_file": uint64(0),
|
|
||||||
"total_mapped_file": uint64(0),
|
|
||||||
"pgpgin": uint64(0),
|
|
||||||
"total_active_file": uint64(0),
|
|
||||||
"total_active_anon": uint64(0),
|
|
||||||
"total_cache": uint64(0),
|
|
||||||
"inactive_anon": uint64(0),
|
|
||||||
"pgmajfault": uint64(0),
|
|
||||||
"total_inactive_anon": uint64(0),
|
|
||||||
"total_rss_huge": uint64(0),
|
|
||||||
"rss_huge": uint64(0),
|
|
||||||
"hierarchical_memory_limit": uint64(0),
|
|
||||||
"pgpgout": uint64(0),
|
|
||||||
"unevictable": uint64(0),
|
|
||||||
"total_inactive_file": uint64(0),
|
|
||||||
"writeback": uint64(0),
|
|
||||||
"total_pgfault": uint64(0),
|
|
||||||
"total_pgpgin": uint64(0),
|
|
||||||
"cache": uint64(0),
|
|
||||||
"mapped_file": uint64(0),
|
|
||||||
"inactive_file": uint64(0),
|
|
||||||
"max_usage": uint64(0),
|
|
||||||
"fail_count": uint64(0),
|
|
||||||
"pgfault": uint64(0),
|
|
||||||
"usage": uint64(0),
|
|
||||||
"limit": uint64(18935443456),
|
|
||||||
},
|
|
||||||
map[string]string{
|
|
||||||
"cont_id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
|
||||||
"cont_name": "etcd2",
|
|
||||||
"cont_image": "quay.io/coreos/etcd:v2.2.2",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
//fmt.Print(info)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ var sampleConfig = `
|
|||||||
domains = []
|
domains = []
|
||||||
`
|
`
|
||||||
|
|
||||||
var defaultTimeout = time.Second * time.Duration(5)
|
|
||||||
|
|
||||||
func (d *Dovecot) SampleConfig() string { return sampleConfig }
|
func (d *Dovecot) SampleConfig() string { return sampleConfig }
|
||||||
|
|
||||||
const defaultPort = "24242"
|
const defaultPort = "24242"
|
||||||
@@ -76,15 +74,12 @@ func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator, doms map[s
|
|||||||
return fmt.Errorf("Error: %s on url %s\n", err, addr)
|
return fmt.Errorf("Error: %s on url %s\n", err, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := net.DialTimeout("tcp", addr, defaultTimeout)
|
c, err := net.Dial("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to connect to dovecot server '%s': %s", addr, err)
|
return fmt.Errorf("Unable to connect to dovecot server '%s': %s", addr, err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
// Extend connection
|
|
||||||
c.SetDeadline(time.Now().Add(defaultTimeout))
|
|
||||||
|
|
||||||
c.Write([]byte("EXPORT\tdomain\n\n"))
|
c.Write([]byte("EXPORT\tdomain\n\n"))
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
io.Copy(&buf, c)
|
io.Copy(&buf, c)
|
||||||
|
|||||||
@@ -81,12 +81,7 @@ type Elasticsearch struct {
|
|||||||
|
|
||||||
// NewElasticsearch return a new instance of Elasticsearch
|
// NewElasticsearch return a new instance of Elasticsearch
|
||||||
func NewElasticsearch() *Elasticsearch {
|
func NewElasticsearch() *Elasticsearch {
|
||||||
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
|
return &Elasticsearch{client: http.DefaultClient}
|
||||||
client := &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
return &Elasticsearch{client: client}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SampleConfig returns sample configuration for this plugin.
|
// SampleConfig returns sample configuration for this plugin.
|
||||||
|
|||||||
@@ -34,9 +34,6 @@ func (t *transportMock) RoundTrip(r *http.Request) (*http.Response, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *transportMock) CancelRequest(_ *http.Request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestElasticsearch(t *testing.T) {
|
func TestElasticsearch(t *testing.T) {
|
||||||
es := NewElasticsearch()
|
es := NewElasticsearch()
|
||||||
es.Servers = []string{"http://example.com:9200"}
|
es.Servers = []string{"http://example.com:9200"}
|
||||||
|
|||||||
@@ -73,17 +73,14 @@ func (gh *GithubWebhooks) Stop() {
|
|||||||
|
|
||||||
// Handles the / route
|
// Handles the / route
|
||||||
func (gh *GithubWebhooks) eventHandler(w http.ResponseWriter, r *http.Request) {
|
func (gh *GithubWebhooks) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer r.Body.Close()
|
|
||||||
eventType := r.Header["X-Github-Event"][0]
|
eventType := r.Header["X-Github-Event"][0]
|
||||||
data, err := ioutil.ReadAll(r.Body)
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
e, err := NewEvent(data, eventType)
|
e, err := NewEvent(data, eventType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
gh.Lock()
|
gh.Lock()
|
||||||
gh.events = append(gh.events, e)
|
gh.events = append(gh.events, e)
|
||||||
|
|||||||
@@ -129,11 +129,8 @@ func (g *haproxy) Gather(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
func (g *haproxy) gatherServer(addr string, acc telegraf.Accumulator) error {
|
func (g *haproxy) gatherServer(addr string, acc telegraf.Accumulator) error {
|
||||||
if g.client == nil {
|
if g.client == nil {
|
||||||
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
|
|
||||||
client := &http.Client{
|
client := &http.Client{}
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
g.client = client
|
g.client = client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package httpjson
|
package httpjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -11,7 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/internal"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
"github.com/influxdata/telegraf/plugins/parsers"
|
||||||
)
|
)
|
||||||
@@ -23,17 +23,7 @@ type HttpJson struct {
|
|||||||
TagKeys []string
|
TagKeys []string
|
||||||
Parameters map[string]string
|
Parameters map[string]string
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
|
client HTTPClient
|
||||||
// Path to CA file
|
|
||||||
SSLCA string `toml:"ssl_ca"`
|
|
||||||
// Path to host cert file
|
|
||||||
SSLCert string `toml:"ssl_cert"`
|
|
||||||
// Path to cert key file
|
|
||||||
SSLKey string `toml:"ssl_key"`
|
|
||||||
// Use SSL but skip chain & host verification
|
|
||||||
InsecureSkipVerify bool
|
|
||||||
|
|
||||||
client HTTPClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPClient interface {
|
type HTTPClient interface {
|
||||||
@@ -46,27 +36,16 @@ type HTTPClient interface {
|
|||||||
// http.Response: HTTP respons object
|
// http.Response: HTTP respons object
|
||||||
// error : Any error that may have occurred
|
// error : Any error that may have occurred
|
||||||
MakeRequest(req *http.Request) (*http.Response, error)
|
MakeRequest(req *http.Request) (*http.Response, error)
|
||||||
|
|
||||||
SetHTTPClient(client *http.Client)
|
|
||||||
HTTPClient() *http.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RealHTTPClient struct {
|
type RealHTTPClient struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
|
func (c RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
|
||||||
return c.client.Do(req)
|
return c.client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
|
|
||||||
c.client = client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RealHTTPClient) HTTPClient() *http.Client {
|
|
||||||
return c.client
|
|
||||||
}
|
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
## NOTE This plugin only reads numerical measurements, strings and booleans
|
## NOTE This plugin only reads numerical measurements, strings and booleans
|
||||||
## will be ignored.
|
## will be ignored.
|
||||||
@@ -98,13 +77,6 @@ var sampleConfig = `
|
|||||||
# [inputs.httpjson.headers]
|
# [inputs.httpjson.headers]
|
||||||
# X-Auth-Token = "my-xauth-token"
|
# X-Auth-Token = "my-xauth-token"
|
||||||
# apiVersion = "v1"
|
# apiVersion = "v1"
|
||||||
|
|
||||||
## Optional SSL Config
|
|
||||||
# ssl_ca = "/etc/telegraf/ca.pem"
|
|
||||||
# ssl_cert = "/etc/telegraf/cert.pem"
|
|
||||||
# ssl_key = "/etc/telegraf/key.pem"
|
|
||||||
## Use SSL but skip chain & host verification
|
|
||||||
# insecure_skip_verify = false
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func (h *HttpJson) SampleConfig() string {
|
func (h *HttpJson) SampleConfig() string {
|
||||||
@@ -119,23 +91,6 @@ func (h *HttpJson) Description() string {
|
|||||||
func (h *HttpJson) Gather(acc telegraf.Accumulator) error {
|
func (h *HttpJson) Gather(acc telegraf.Accumulator) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
if h.client.HTTPClient() == nil {
|
|
||||||
tlsCfg, err := internal.GetTLSConfig(
|
|
||||||
h.SSLCert, h.SSLKey, h.SSLCA, h.InsecureSkipVerify)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tr := &http.Transport{
|
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
|
||||||
TLSClientConfig: tlsCfg,
|
|
||||||
}
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
h.client.SetHTTPClient(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
errorChannel := make(chan error, len(h.Servers))
|
errorChannel := make(chan error, len(h.Servers))
|
||||||
|
|
||||||
for _, server := range h.Servers {
|
for _, server := range h.Servers {
|
||||||
@@ -227,14 +182,15 @@ func (h *HttpJson) sendRequest(serverURL string) (string, float64, error) {
|
|||||||
return "", -1, fmt.Errorf("Invalid server URL \"%s\"", serverURL)
|
return "", -1, fmt.Errorf("Invalid server URL \"%s\"", serverURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case h.Method == "GET":
|
case h.Method == "GET":
|
||||||
params := requestURL.Query()
|
requestURL.RawQuery = params.Encode()
|
||||||
for k, v := range h.Parameters {
|
for k, v := range h.Parameters {
|
||||||
params.Add(k, v)
|
params.Add(k, v)
|
||||||
}
|
}
|
||||||
requestURL.RawQuery = params.Encode()
|
|
||||||
|
|
||||||
case h.Method == "POST":
|
case h.Method == "POST":
|
||||||
requestURL.RawQuery = ""
|
requestURL.RawQuery = ""
|
||||||
@@ -244,8 +200,7 @@ func (h *HttpJson) sendRequest(serverURL string) (string, float64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create + send request
|
// Create + send request
|
||||||
req, err := http.NewRequest(h.Method, requestURL.String(),
|
req, err := http.NewRequest(h.Method, requestURL.String(), bytes.NewBufferString(data.Encode()))
|
||||||
strings.NewReader(data.Encode()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", -1, err
|
return "", -1, err
|
||||||
}
|
}
|
||||||
@@ -289,8 +244,6 @@ func (h *HttpJson) sendRequest(serverURL string) (string, float64, error) {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("httpjson", func() telegraf.Input {
|
inputs.Add("httpjson", func() telegraf.Input {
|
||||||
return &HttpJson{
|
return &HttpJson{client: RealHTTPClient{client: &http.Client{}}}
|
||||||
client: &RealHTTPClient{},
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package httpjson
|
package httpjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -29,75 +27,6 @@ const validJSON = `
|
|||||||
"another_list": [4]
|
"another_list": [4]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const validJSON2 = `{
|
|
||||||
"user":{
|
|
||||||
"hash_rate":0,
|
|
||||||
"expected_24h_rewards":0,
|
|
||||||
"total_rewards":0.000595109232,
|
|
||||||
"paid_rewards":0,
|
|
||||||
"unpaid_rewards":0.000595109232,
|
|
||||||
"past_24h_rewards":0,
|
|
||||||
"total_work":"5172625408",
|
|
||||||
"blocks_found":0
|
|
||||||
},
|
|
||||||
"workers":{
|
|
||||||
"brminer.1":{
|
|
||||||
"hash_rate":0,
|
|
||||||
"hash_rate_24h":0,
|
|
||||||
"valid_shares":"6176",
|
|
||||||
"stale_shares":"0",
|
|
||||||
"invalid_shares":"0",
|
|
||||||
"rewards":4.5506464e-5,
|
|
||||||
"rewards_24h":0,
|
|
||||||
"reset_time":1455409950
|
|
||||||
},
|
|
||||||
"brminer.2":{
|
|
||||||
"hash_rate":0,
|
|
||||||
"hash_rate_24h":0,
|
|
||||||
"valid_shares":"0",
|
|
||||||
"stale_shares":"0",
|
|
||||||
"invalid_shares":"0",
|
|
||||||
"rewards":0,
|
|
||||||
"rewards_24h":0,
|
|
||||||
"reset_time":1455936726
|
|
||||||
},
|
|
||||||
"brminer.3":{
|
|
||||||
"hash_rate":0,
|
|
||||||
"hash_rate_24h":0,
|
|
||||||
"valid_shares":"0",
|
|
||||||
"stale_shares":"0",
|
|
||||||
"invalid_shares":"0",
|
|
||||||
"rewards":0,
|
|
||||||
"rewards_24h":0,
|
|
||||||
"reset_time":1455936733
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pool":{
|
|
||||||
"hash_rate":114100000,
|
|
||||||
"active_users":843,
|
|
||||||
"total_work":"5015346808842682368",
|
|
||||||
"pps_ratio":1.04,
|
|
||||||
"pps_rate":7.655e-9
|
|
||||||
},
|
|
||||||
"network":{
|
|
||||||
"hash_rate":1426117703,
|
|
||||||
"block_number":944895,
|
|
||||||
"time_per_block":156,
|
|
||||||
"difficulty":51825.72835216,
|
|
||||||
"next_difficulty":51916.15249019,
|
|
||||||
"retarget_time":95053
|
|
||||||
},
|
|
||||||
"market":{
|
|
||||||
"ltc_btc":0.00798,
|
|
||||||
"ltc_usd":3.37801,
|
|
||||||
"ltc_eur":3.113,
|
|
||||||
"ltc_gbp":2.32807,
|
|
||||||
"ltc_rub":241.796,
|
|
||||||
"ltc_cny":21.3883,
|
|
||||||
"btc_usd":422.852
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
const validJSONTags = `
|
const validJSONTags = `
|
||||||
{
|
{
|
||||||
"value": 15,
|
"value": 15,
|
||||||
@@ -125,7 +54,7 @@ type mockHTTPClient struct {
|
|||||||
// Mock implementation of MakeRequest. Usually returns an http.Response with
|
// Mock implementation of MakeRequest. Usually returns an http.Response with
|
||||||
// hard-coded responseBody and statusCode. However, if the request uses a
|
// hard-coded responseBody and statusCode. However, if the request uses a
|
||||||
// nonstandard method, it uses status code 405 (method not allowed)
|
// nonstandard method, it uses status code 405 (method not allowed)
|
||||||
func (c *mockHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
|
func (c mockHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
|
||||||
resp := http.Response{}
|
resp := http.Response{}
|
||||||
resp.StatusCode = c.statusCode
|
resp.StatusCode = c.statusCode
|
||||||
|
|
||||||
@@ -147,13 +76,6 @@ func (c *mockHTTPClient) MakeRequest(req *http.Request) (*http.Response, error)
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *mockHTTPClient) SetHTTPClient(_ *http.Client) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockHTTPClient) HTTPClient() *http.Client {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a pointer to an HttpJson object that uses a mock HTTP client.
|
// Generates a pointer to an HttpJson object that uses a mock HTTP client.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// response : Body of the response that the mock HTTP client should return
|
// response : Body of the response that the mock HTTP client should return
|
||||||
@@ -164,7 +86,7 @@ func (c *mockHTTPClient) HTTPClient() *http.Client {
|
|||||||
func genMockHttpJson(response string, statusCode int) []*HttpJson {
|
func genMockHttpJson(response string, statusCode int) []*HttpJson {
|
||||||
return []*HttpJson{
|
return []*HttpJson{
|
||||||
&HttpJson{
|
&HttpJson{
|
||||||
client: &mockHTTPClient{responseBody: response, statusCode: statusCode},
|
client: mockHTTPClient{responseBody: response, statusCode: statusCode},
|
||||||
Servers: []string{
|
Servers: []string{
|
||||||
"http://server1.example.com/metrics/",
|
"http://server1.example.com/metrics/",
|
||||||
"http://server2.example.com/metrics/",
|
"http://server2.example.com/metrics/",
|
||||||
@@ -181,7 +103,7 @@ func genMockHttpJson(response string, statusCode int) []*HttpJson {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
&HttpJson{
|
&HttpJson{
|
||||||
client: &mockHTTPClient{responseBody: response, statusCode: statusCode},
|
client: mockHTTPClient{responseBody: response, statusCode: statusCode},
|
||||||
Servers: []string{
|
Servers: []string{
|
||||||
"http://server3.example.com/metrics/",
|
"http://server3.example.com/metrics/",
|
||||||
"http://server4.example.com/metrics/",
|
"http://server4.example.com/metrics/",
|
||||||
@@ -227,222 +149,6 @@ func TestHttpJson200(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that GET Parameters from the url string are applied properly
|
|
||||||
func TestHttpJsonGET_URL(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
key := r.FormValue("api_key")
|
|
||||||
assert.Equal(t, "mykey", key)
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprintln(w, validJSON2)
|
|
||||||
}))
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
a := HttpJson{
|
|
||||||
Servers: []string{ts.URL + "?api_key=mykey"},
|
|
||||||
Name: "",
|
|
||||||
Method: "GET",
|
|
||||||
client: &RealHTTPClient{client: &http.Client{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := a.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// remove response_time from gathered fields because it's non-deterministic
|
|
||||||
delete(acc.Metrics[0].Fields, "response_time")
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"market_btc_usd": float64(422.852),
|
|
||||||
"market_ltc_btc": float64(0.00798),
|
|
||||||
"market_ltc_cny": float64(21.3883),
|
|
||||||
"market_ltc_eur": float64(3.113),
|
|
||||||
"market_ltc_gbp": float64(2.32807),
|
|
||||||
"market_ltc_rub": float64(241.796),
|
|
||||||
"market_ltc_usd": float64(3.37801),
|
|
||||||
"network_block_number": float64(944895),
|
|
||||||
"network_difficulty": float64(51825.72835216),
|
|
||||||
"network_hash_rate": float64(1.426117703e+09),
|
|
||||||
"network_next_difficulty": float64(51916.15249019),
|
|
||||||
"network_retarget_time": float64(95053),
|
|
||||||
"network_time_per_block": float64(156),
|
|
||||||
"pool_active_users": float64(843),
|
|
||||||
"pool_hash_rate": float64(1.141e+08),
|
|
||||||
"pool_pps_rate": float64(7.655e-09),
|
|
||||||
"pool_pps_ratio": float64(1.04),
|
|
||||||
"user_blocks_found": float64(0),
|
|
||||||
"user_expected_24h_rewards": float64(0),
|
|
||||||
"user_hash_rate": float64(0),
|
|
||||||
"user_paid_rewards": float64(0),
|
|
||||||
"user_past_24h_rewards": float64(0),
|
|
||||||
"user_total_rewards": float64(0.000595109232),
|
|
||||||
"user_unpaid_rewards": float64(0.000595109232),
|
|
||||||
"workers_brminer.1_hash_rate": float64(0),
|
|
||||||
"workers_brminer.1_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.1_reset_time": float64(1.45540995e+09),
|
|
||||||
"workers_brminer.1_rewards": float64(4.5506464e-05),
|
|
||||||
"workers_brminer.1_rewards_24h": float64(0),
|
|
||||||
"workers_brminer.2_hash_rate": float64(0),
|
|
||||||
"workers_brminer.2_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.2_reset_time": float64(1.455936726e+09),
|
|
||||||
"workers_brminer.2_rewards": float64(0),
|
|
||||||
"workers_brminer.2_rewards_24h": float64(0),
|
|
||||||
"workers_brminer.3_hash_rate": float64(0),
|
|
||||||
"workers_brminer.3_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.3_reset_time": float64(1.455936733e+09),
|
|
||||||
"workers_brminer.3_rewards": float64(0),
|
|
||||||
"workers_brminer.3_rewards_24h": float64(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "httpjson", fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that GET Parameters are applied properly
|
|
||||||
func TestHttpJsonGET(t *testing.T) {
|
|
||||||
params := map[string]string{
|
|
||||||
"api_key": "mykey",
|
|
||||||
}
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
key := r.FormValue("api_key")
|
|
||||||
assert.Equal(t, "mykey", key)
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprintln(w, validJSON2)
|
|
||||||
}))
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
a := HttpJson{
|
|
||||||
Servers: []string{ts.URL},
|
|
||||||
Name: "",
|
|
||||||
Method: "GET",
|
|
||||||
Parameters: params,
|
|
||||||
client: &RealHTTPClient{client: &http.Client{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := a.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// remove response_time from gathered fields because it's non-deterministic
|
|
||||||
delete(acc.Metrics[0].Fields, "response_time")
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"market_btc_usd": float64(422.852),
|
|
||||||
"market_ltc_btc": float64(0.00798),
|
|
||||||
"market_ltc_cny": float64(21.3883),
|
|
||||||
"market_ltc_eur": float64(3.113),
|
|
||||||
"market_ltc_gbp": float64(2.32807),
|
|
||||||
"market_ltc_rub": float64(241.796),
|
|
||||||
"market_ltc_usd": float64(3.37801),
|
|
||||||
"network_block_number": float64(944895),
|
|
||||||
"network_difficulty": float64(51825.72835216),
|
|
||||||
"network_hash_rate": float64(1.426117703e+09),
|
|
||||||
"network_next_difficulty": float64(51916.15249019),
|
|
||||||
"network_retarget_time": float64(95053),
|
|
||||||
"network_time_per_block": float64(156),
|
|
||||||
"pool_active_users": float64(843),
|
|
||||||
"pool_hash_rate": float64(1.141e+08),
|
|
||||||
"pool_pps_rate": float64(7.655e-09),
|
|
||||||
"pool_pps_ratio": float64(1.04),
|
|
||||||
"user_blocks_found": float64(0),
|
|
||||||
"user_expected_24h_rewards": float64(0),
|
|
||||||
"user_hash_rate": float64(0),
|
|
||||||
"user_paid_rewards": float64(0),
|
|
||||||
"user_past_24h_rewards": float64(0),
|
|
||||||
"user_total_rewards": float64(0.000595109232),
|
|
||||||
"user_unpaid_rewards": float64(0.000595109232),
|
|
||||||
"workers_brminer.1_hash_rate": float64(0),
|
|
||||||
"workers_brminer.1_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.1_reset_time": float64(1.45540995e+09),
|
|
||||||
"workers_brminer.1_rewards": float64(4.5506464e-05),
|
|
||||||
"workers_brminer.1_rewards_24h": float64(0),
|
|
||||||
"workers_brminer.2_hash_rate": float64(0),
|
|
||||||
"workers_brminer.2_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.2_reset_time": float64(1.455936726e+09),
|
|
||||||
"workers_brminer.2_rewards": float64(0),
|
|
||||||
"workers_brminer.2_rewards_24h": float64(0),
|
|
||||||
"workers_brminer.3_hash_rate": float64(0),
|
|
||||||
"workers_brminer.3_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.3_reset_time": float64(1.455936733e+09),
|
|
||||||
"workers_brminer.3_rewards": float64(0),
|
|
||||||
"workers_brminer.3_rewards_24h": float64(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "httpjson", fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that POST Parameters are applied properly
|
|
||||||
func TestHttpJsonPOST(t *testing.T) {
|
|
||||||
params := map[string]string{
|
|
||||||
"api_key": "mykey",
|
|
||||||
}
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "api_key=mykey", string(body))
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprintln(w, validJSON2)
|
|
||||||
}))
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
a := HttpJson{
|
|
||||||
Servers: []string{ts.URL},
|
|
||||||
Name: "",
|
|
||||||
Method: "POST",
|
|
||||||
Parameters: params,
|
|
||||||
client: &RealHTTPClient{client: &http.Client{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := a.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// remove response_time from gathered fields because it's non-deterministic
|
|
||||||
delete(acc.Metrics[0].Fields, "response_time")
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"market_btc_usd": float64(422.852),
|
|
||||||
"market_ltc_btc": float64(0.00798),
|
|
||||||
"market_ltc_cny": float64(21.3883),
|
|
||||||
"market_ltc_eur": float64(3.113),
|
|
||||||
"market_ltc_gbp": float64(2.32807),
|
|
||||||
"market_ltc_rub": float64(241.796),
|
|
||||||
"market_ltc_usd": float64(3.37801),
|
|
||||||
"network_block_number": float64(944895),
|
|
||||||
"network_difficulty": float64(51825.72835216),
|
|
||||||
"network_hash_rate": float64(1.426117703e+09),
|
|
||||||
"network_next_difficulty": float64(51916.15249019),
|
|
||||||
"network_retarget_time": float64(95053),
|
|
||||||
"network_time_per_block": float64(156),
|
|
||||||
"pool_active_users": float64(843),
|
|
||||||
"pool_hash_rate": float64(1.141e+08),
|
|
||||||
"pool_pps_rate": float64(7.655e-09),
|
|
||||||
"pool_pps_ratio": float64(1.04),
|
|
||||||
"user_blocks_found": float64(0),
|
|
||||||
"user_expected_24h_rewards": float64(0),
|
|
||||||
"user_hash_rate": float64(0),
|
|
||||||
"user_paid_rewards": float64(0),
|
|
||||||
"user_past_24h_rewards": float64(0),
|
|
||||||
"user_total_rewards": float64(0.000595109232),
|
|
||||||
"user_unpaid_rewards": float64(0.000595109232),
|
|
||||||
"workers_brminer.1_hash_rate": float64(0),
|
|
||||||
"workers_brminer.1_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.1_reset_time": float64(1.45540995e+09),
|
|
||||||
"workers_brminer.1_rewards": float64(4.5506464e-05),
|
|
||||||
"workers_brminer.1_rewards_24h": float64(0),
|
|
||||||
"workers_brminer.2_hash_rate": float64(0),
|
|
||||||
"workers_brminer.2_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.2_reset_time": float64(1.455936726e+09),
|
|
||||||
"workers_brminer.2_rewards": float64(0),
|
|
||||||
"workers_brminer.2_rewards_24h": float64(0),
|
|
||||||
"workers_brminer.3_hash_rate": float64(0),
|
|
||||||
"workers_brminer.3_hash_rate_24h": float64(0),
|
|
||||||
"workers_brminer.3_reset_time": float64(1.455936733e+09),
|
|
||||||
"workers_brminer.3_rewards": float64(0),
|
|
||||||
"workers_brminer.3_rewards_24h": float64(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "httpjson", fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test response to HTTP 500
|
// Test response to HTTP 500
|
||||||
func TestHttpJson500(t *testing.T) {
|
func TestHttpJson500(t *testing.T) {
|
||||||
httpjson := genMockHttpJson(validJSON, 500)
|
httpjson := genMockHttpJson(validJSON, 500)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
@@ -71,15 +70,6 @@ type point struct {
|
|||||||
Values map[string]interface{} `json:"values"`
|
Values map[string]interface{} `json:"values"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var tr = &http.Transport{
|
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gathers data from a particular URL
|
// Gathers data from a particular URL
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// acc : The telegraf Accumulator to use
|
// acc : The telegraf Accumulator to use
|
||||||
@@ -91,7 +81,7 @@ func (i *InfluxDB) gatherURL(
|
|||||||
acc telegraf.Accumulator,
|
acc telegraf.Accumulator,
|
||||||
url string,
|
url string,
|
||||||
) error {
|
) error {
|
||||||
resp, err := client.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
@@ -161,11 +160,6 @@ func (j *Jolokia) Gather(acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("jolokia", func() telegraf.Input {
|
inputs.Add("jolokia", func() telegraf.Input {
|
||||||
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
|
return &Jolokia{jClient: &JolokiaClientImpl{client: &http.Client{}}}
|
||||||
client := &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
return &Jolokia{jClient: &JolokiaClientImpl{client: client}}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Kafka struct {
|
type Kafka struct {
|
||||||
ConsumerGroup string
|
ConsumerGroup string
|
||||||
Topics []string
|
Topics []string
|
||||||
ZookeeperPeers []string
|
ZookeeperPeers []string
|
||||||
ZookeeperChroot string
|
Consumer *consumergroup.ConsumerGroup
|
||||||
Consumer *consumergroup.ConsumerGroup
|
|
||||||
|
|
||||||
// Legacy metric buffer support
|
// Legacy metric buffer support
|
||||||
MetricBuffer int
|
MetricBuffer int
|
||||||
@@ -49,8 +48,6 @@ var sampleConfig = `
|
|||||||
topics = ["telegraf"]
|
topics = ["telegraf"]
|
||||||
## an array of Zookeeper connection strings
|
## an array of Zookeeper connection strings
|
||||||
zookeeper_peers = ["localhost:2181"]
|
zookeeper_peers = ["localhost:2181"]
|
||||||
## Zookeeper Chroot
|
|
||||||
zookeeper_chroot = "/"
|
|
||||||
## the name of the consumer group
|
## the name of the consumer group
|
||||||
consumer_group = "telegraf_metrics_consumers"
|
consumer_group = "telegraf_metrics_consumers"
|
||||||
## Offset (must be either "oldest" or "newest")
|
## Offset (must be either "oldest" or "newest")
|
||||||
@@ -83,7 +80,6 @@ func (k *Kafka) Start(acc telegraf.Accumulator) error {
|
|||||||
k.acc = acc
|
k.acc = acc
|
||||||
|
|
||||||
config := consumergroup.NewConfig()
|
config := consumergroup.NewConfig()
|
||||||
config.Zookeeper.Chroot = k.ZookeeperChroot
|
|
||||||
switch strings.ToLower(k.Offset) {
|
switch strings.ToLower(k.Offset) {
|
||||||
case "oldest", "":
|
case "oldest", "":
|
||||||
config.Offsets.Initial = sarama.OffsetOldest
|
config.Offsets.Initial = sarama.OffsetOldest
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -121,10 +120,7 @@ func (a *ChimpAPI) GetReport(campaignID string) (Report, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runChimp(api *ChimpAPI, params ReportsParams) ([]byte, error) {
|
func runChimp(api *ChimpAPI, params ReportsParams) ([]byte, error) {
|
||||||
client := &http.Client{
|
client := &http.Client{Transport: api.Transport}
|
||||||
Transport: api.Transport,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
req, err := http.NewRequest("GET", api.url.String(), &b)
|
req, err := http.NewRequest("GET", api.url.String(), &b)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
@@ -262,15 +261,6 @@ func (m *Mesos) removeGroup(j *map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tr = &http.Transport{
|
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should not belong to the object
|
// This should not belong to the object
|
||||||
func (m *Mesos) gatherMetrics(a string, acc telegraf.Accumulator) error {
|
func (m *Mesos) gatherMetrics(a string, acc telegraf.Accumulator) error {
|
||||||
var jsonOut map[string]interface{}
|
var jsonOut map[string]interface{}
|
||||||
@@ -292,7 +282,7 @@ func (m *Mesos) gatherMetrics(a string, acc telegraf.Accumulator) error {
|
|||||||
|
|
||||||
ts := strconv.Itoa(m.Timeout) + "ms"
|
ts := strconv.Itoa(m.Timeout) + "ms"
|
||||||
|
|
||||||
resp, err := client.Get("http://" + a + "/metrics/snapshot?timeout=" + ts)
|
resp, err := http.Get("http://" + a + "/metrics/snapshot?timeout=" + ts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ type MQTTConsumer struct {
|
|||||||
// Legacy metric buffer support
|
// Legacy metric buffer support
|
||||||
MetricBuffer int
|
MetricBuffer int
|
||||||
|
|
||||||
PersistentSession bool
|
|
||||||
ClientID string `toml:"client_id"`
|
|
||||||
|
|
||||||
// Path to CA file
|
// Path to CA file
|
||||||
SSLCA string `toml:"ssl_ca"`
|
SSLCA string `toml:"ssl_ca"`
|
||||||
// Path to host cert file
|
// Path to host cert file
|
||||||
@@ -60,13 +57,6 @@ var sampleConfig = `
|
|||||||
"sensors/#",
|
"sensors/#",
|
||||||
]
|
]
|
||||||
|
|
||||||
# if true, messages that can't be delivered while the subscriber is offline
|
|
||||||
# will be delivered when it comes back (such as on service restart).
|
|
||||||
# NOTE: if true, client_id MUST be set
|
|
||||||
persistent_session = false
|
|
||||||
# If empty, a random client ID will be generated.
|
|
||||||
client_id = ""
|
|
||||||
|
|
||||||
## username and password to connect MQTT server.
|
## username and password to connect MQTT server.
|
||||||
# username = "telegraf"
|
# username = "telegraf"
|
||||||
# password = "metricsmetricsmetricsmetrics"
|
# password = "metricsmetricsmetricsmetrics"
|
||||||
@@ -101,11 +91,6 @@ func (m *MQTTConsumer) Start(acc telegraf.Accumulator) error {
|
|||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if m.PersistentSession && m.ClientID == "" {
|
|
||||||
return fmt.Errorf("ERROR MQTT Consumer: When using persistent_session" +
|
|
||||||
" = true, you MUST also set client_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
m.acc = acc
|
m.acc = acc
|
||||||
if m.QoS > 2 || m.QoS < 0 {
|
if m.QoS > 2 || m.QoS < 0 {
|
||||||
return fmt.Errorf("MQTT Consumer, invalid QoS value: %d", m.QoS)
|
return fmt.Errorf("MQTT Consumer, invalid QoS value: %d", m.QoS)
|
||||||
@@ -181,11 +166,7 @@ func (m *MQTTConsumer) Gather(acc telegraf.Accumulator) error {
|
|||||||
func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
||||||
opts := mqtt.NewClientOptions()
|
opts := mqtt.NewClientOptions()
|
||||||
|
|
||||||
if m.ClientID == "" {
|
opts.SetClientID("Telegraf-Consumer-" + internal.RandomString(5))
|
||||||
opts.SetClientID("Telegraf-Consumer-" + internal.RandomString(5))
|
|
||||||
} else {
|
|
||||||
opts.SetClientID(m.ClientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsCfg, err := internal.GetTLSConfig(
|
tlsCfg, err := internal.GetTLSConfig(
|
||||||
m.SSLCert, m.SSLKey, m.SSLCA, m.InsecureSkipVerify)
|
m.SSLCert, m.SSLKey, m.SSLCA, m.InsecureSkipVerify)
|
||||||
@@ -200,7 +181,7 @@ func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user := m.Username
|
user := m.Username
|
||||||
if user != "" {
|
if user == "" {
|
||||||
opts.SetUsername(user)
|
opts.SetUsername(user)
|
||||||
}
|
}
|
||||||
password := m.Password
|
password := m.Password
|
||||||
@@ -218,7 +199,6 @@ func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
|||||||
}
|
}
|
||||||
opts.SetAutoReconnect(true)
|
opts.SetAutoReconnect(true)
|
||||||
opts.SetKeepAlive(time.Second * 60)
|
opts.SetKeepAlive(time.Second * 60)
|
||||||
opts.SetCleanSession(!m.PersistentSession)
|
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import (
|
|||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
"github.com/influxdata/telegraf/plugins/parsers"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
|
"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,52 +28,6 @@ func newTestMQTTConsumer() (*MQTTConsumer, chan mqtt.Message) {
|
|||||||
return n, in
|
return n, in
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that default client has random ID
|
|
||||||
func TestRandomClientID(t *testing.T) {
|
|
||||||
m1 := &MQTTConsumer{
|
|
||||||
Servers: []string{"localhost:1883"}}
|
|
||||||
opts, err := m1.createOpts()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
m2 := &MQTTConsumer{
|
|
||||||
Servers: []string{"localhost:1883"}}
|
|
||||||
opts2, err2 := m2.createOpts()
|
|
||||||
assert.NoError(t, err2)
|
|
||||||
|
|
||||||
assert.NotEqual(t, opts.ClientID, opts2.ClientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that default client has random ID
|
|
||||||
func TestClientID(t *testing.T) {
|
|
||||||
m1 := &MQTTConsumer{
|
|
||||||
Servers: []string{"localhost:1883"},
|
|
||||||
ClientID: "telegraf-test",
|
|
||||||
}
|
|
||||||
opts, err := m1.createOpts()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
m2 := &MQTTConsumer{
|
|
||||||
Servers: []string{"localhost:1883"},
|
|
||||||
ClientID: "telegraf-test",
|
|
||||||
}
|
|
||||||
opts2, err2 := m2.createOpts()
|
|
||||||
assert.NoError(t, err2)
|
|
||||||
|
|
||||||
assert.Equal(t, "telegraf-test", opts2.ClientID)
|
|
||||||
assert.Equal(t, "telegraf-test", opts.ClientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Start() fails if client ID is not set but persistent is
|
|
||||||
func TestPersistentClientIDFail(t *testing.T) {
|
|
||||||
m1 := &MQTTConsumer{
|
|
||||||
Servers: []string{"localhost:1883"},
|
|
||||||
PersistentSession: true,
|
|
||||||
}
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
err := m1.Start(&acc)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that the parser parses NATS messages into metrics
|
// Test that the parser parses NATS messages into metrics
|
||||||
func TestRunParser(t *testing.T) {
|
func TestRunParser(t *testing.T) {
|
||||||
n, in := newTestMQTTConsumer()
|
n, in := newTestMQTTConsumer()
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package mysql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
@@ -28,8 +26,6 @@ var sampleConfig = `
|
|||||||
servers = ["tcp(127.0.0.1:3306)/"]
|
servers = ["tcp(127.0.0.1:3306)/"]
|
||||||
`
|
`
|
||||||
|
|
||||||
var defaultTimeout = time.Second * time.Duration(5)
|
|
||||||
|
|
||||||
func (m *Mysql) SampleConfig() string {
|
func (m *Mysql) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
@@ -126,10 +122,6 @@ func (m *Mysql) gatherServer(serv string, acc telegraf.Accumulator) error {
|
|||||||
serv = ""
|
serv = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
serv, err := dsnAddTimeout(serv)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
db, err := sql.Open("mysql", serv)
|
db, err := sql.Open("mysql", serv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -215,27 +207,6 @@ func (m *Mysql) gatherServer(serv string, acc telegraf.Accumulator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dsnAddTimeout(dsn string) (string, error) {
|
|
||||||
|
|
||||||
// DSN "?timeout=5s" is not valid, but "/?timeout=5s" is valid ("" and "/"
|
|
||||||
// are the same DSN)
|
|
||||||
if dsn == "" {
|
|
||||||
dsn = "/"
|
|
||||||
}
|
|
||||||
u, err := url.Parse(dsn)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
v := u.Query()
|
|
||||||
|
|
||||||
// Only override timeout if not already defined
|
|
||||||
if _, ok := v["timeout"]; ok == false {
|
|
||||||
v.Add("timeout", defaultTimeout.String())
|
|
||||||
u.RawQuery = v.Encode()
|
|
||||||
}
|
|
||||||
return u.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("mysql", func() telegraf.Input {
|
inputs.Add("mysql", func() telegraf.Input {
|
||||||
return &Mysql{}
|
return &Mysql{}
|
||||||
|
|||||||
@@ -84,34 +84,3 @@ func TestMysqlParseDSN(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMysqlDNSAddTimeout(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
output string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
"/?timeout=5s",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tcp(192.168.1.1:3306)/",
|
|
||||||
"tcp(192.168.1.1:3306)/?timeout=5s",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"root:passwd@tcp(192.168.1.1:3306)/?tls=false",
|
|
||||||
"root:passwd@tcp(192.168.1.1:3306)/?timeout=5s&tls=false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"root:passwd@tcp(192.168.1.1:3306)/?tls=false&timeout=10s",
|
|
||||||
"root:passwd@tcp(192.168.1.1:3306)/?tls=false&timeout=10s",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
output, _ := dsnAddTimeout(test.input)
|
|
||||||
if output != test.output {
|
|
||||||
t.Errorf("Expected %s, got %s\n", test.output, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ It can also check response text.
|
|||||||
### Tags:
|
### Tags:
|
||||||
|
|
||||||
- All measurements have the following tags:
|
- All measurements have the following tags:
|
||||||
- server
|
- host
|
||||||
- port
|
- port
|
||||||
- protocol
|
- protocol
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ It can also check response text.
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ ./telegraf -config telegraf.conf -input-filter net_response -test
|
$ ./telegraf -config telegraf.conf -input-filter net_response -test
|
||||||
net_response,server=192.168.2.2,port=22,protocol=tcp response_time=0.18070360500000002,string_found=true 1454785464182527094
|
net_response,host=127.0.0.1,port=22,protocol=tcp response_time=0.18070360500000002,string_found=true 1454785464182527094
|
||||||
net_response,server=192.168.2.2,port=2222,protocol=tcp response_time=1.090124776,string_found=false 1454784433658942325
|
net_response,host=127.0.0.1,port=2222,protocol=tcp response_time=1.090124776,string_found=false 1454784433658942325
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ func (c *NetResponse) Gather(acc telegraf.Accumulator) error {
|
|||||||
return errors.New("Bad port")
|
return errors.New("Bad port")
|
||||||
}
|
}
|
||||||
// Prepare data
|
// Prepare data
|
||||||
tags := map[string]string{"server": host, "port": port}
|
tags := map[string]string{"host": host, "port": port}
|
||||||
var fields map[string]interface{}
|
var fields map[string]interface{}
|
||||||
// Gather data
|
// Gather data
|
||||||
if c.Protocol == "tcp" {
|
if c.Protocol == "tcp" {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func TestTCPOK1(t *testing.T) {
|
|||||||
"string_found": true,
|
"string_found": true,
|
||||||
"response_time": 1.0,
|
"response_time": 1.0,
|
||||||
},
|
},
|
||||||
map[string]string{"server": "127.0.0.1",
|
map[string]string{"host": "127.0.0.1",
|
||||||
"port": "2004",
|
"port": "2004",
|
||||||
"protocol": "tcp",
|
"protocol": "tcp",
|
||||||
},
|
},
|
||||||
@@ -109,7 +109,7 @@ func TestTCPOK2(t *testing.T) {
|
|||||||
"string_found": false,
|
"string_found": false,
|
||||||
"response_time": 1.0,
|
"response_time": 1.0,
|
||||||
},
|
},
|
||||||
map[string]string{"server": "127.0.0.1",
|
map[string]string{"host": "127.0.0.1",
|
||||||
"port": "2004",
|
"port": "2004",
|
||||||
"protocol": "tcp",
|
"protocol": "tcp",
|
||||||
},
|
},
|
||||||
@@ -164,7 +164,7 @@ func TestUDPOK1(t *testing.T) {
|
|||||||
"string_found": true,
|
"string_found": true,
|
||||||
"response_time": 1.0,
|
"response_time": 1.0,
|
||||||
},
|
},
|
||||||
map[string]string{"server": "127.0.0.1",
|
map[string]string{"host": "127.0.0.1",
|
||||||
"port": "2004",
|
"port": "2004",
|
||||||
"protocol": "udp",
|
"protocol": "udp",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
# Telegraf Plugin: Nginx
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Read Nginx's basic status information (ngx_http_stub_status_module)
|
|
||||||
[[inputs.nginx]]
|
|
||||||
## An array of Nginx stub_status URI to gather stats.
|
|
||||||
urls = ["http://localhost/server_status"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- Measurement
|
|
||||||
- accepts
|
|
||||||
- active
|
|
||||||
- handled
|
|
||||||
- reading
|
|
||||||
- requests
|
|
||||||
- waiting
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
- All measurements have the following tags:
|
|
||||||
- port
|
|
||||||
- server
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
Using this configuration:
|
|
||||||
```
|
|
||||||
[[inputs.nginx]]
|
|
||||||
## An array of Nginx stub_status URI to gather stats.
|
|
||||||
urls = ["http://localhost/status"]
|
|
||||||
```
|
|
||||||
|
|
||||||
When run with:
|
|
||||||
```
|
|
||||||
./telegraf -config telegraf.conf -input-filter nginx -test
|
|
||||||
```
|
|
||||||
|
|
||||||
It produces:
|
|
||||||
```
|
|
||||||
* Plugin: nginx, Collection 1
|
|
||||||
> nginx,port=80,server=localhost accepts=605i,active=2i,handled=605i,reading=0i,requests=12132i,waiting=1i,writing=1i 1456690994701784331
|
|
||||||
```
|
|
||||||
@@ -58,10 +58,7 @@ var tr = &http.Transport{
|
|||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = &http.Client{
|
var client = &http.Client{Transport: tr}
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Nginx) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
|
func (n *Nginx) gatherUrl(addr *url.URL, acc telegraf.Accumulator) error {
|
||||||
resp, err := client.Get(addr.String())
|
resp, err := client.Get(addr.String())
|
||||||
|
|||||||
@@ -84,10 +84,7 @@ var tr = &http.Transport{
|
|||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = &http.Client{
|
var client = &http.Client{Transport: tr}
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NSQ) gatherEndpoint(e string, acc telegraf.Accumulator) error {
|
func (n *NSQ) gatherEndpoint(e string, acc telegraf.Accumulator) error {
|
||||||
u, err := buildURL(e)
|
u, err := buildURL(e)
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
# ntpq Input Plugin
|
|
||||||
|
|
||||||
Get standard NTP query metrics, requires ntpq executable.
|
|
||||||
|
|
||||||
Below is the documentation of the various headers returned from the NTP query
|
|
||||||
command when running `ntpq -p`.
|
|
||||||
|
|
||||||
- remote – The remote peer or server being synced to. “LOCAL” is this local host
|
|
||||||
(included in case there are no remote peers or servers available);
|
|
||||||
- refid – Where or what the remote peer or server is itself synchronised to;
|
|
||||||
- st (stratum) – The remote peer or server Stratum
|
|
||||||
- t (type) – Type (u: unicast or manycast client, b: broadcast or multicast client,
|
|
||||||
l: local reference clock, s: symmetric peer, A: manycast server,
|
|
||||||
B: broadcast server, M: multicast server, see “Automatic Server Discovery“);
|
|
||||||
- when – When last polled (seconds ago, “h” hours ago, or “d” days ago);
|
|
||||||
- poll – Polling frequency: rfc5905 suggests this ranges in NTPv4 from 4 (16s)
|
|
||||||
to 17 (36h) (log2 seconds), however observation suggests the actual displayed
|
|
||||||
value is seconds for a much smaller range of 64 (26) to 1024 (210) seconds;
|
|
||||||
- reach – An 8-bit left-shift shift register value recording polls (bit set =
|
|
||||||
successful, bit reset = fail) displayed in octal;
|
|
||||||
- delay – Round trip communication delay to the remote peer or server (milliseconds);
|
|
||||||
- offset – Mean offset (phase) in the times reported between this local host and
|
|
||||||
the remote peer or server (RMS, milliseconds);
|
|
||||||
- jitter – Mean deviation (jitter) in the time reported for that remote peer or
|
|
||||||
server (RMS of difference of multiple time samples, milliseconds);
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Get standard NTP query metrics, requires ntpq executable
|
|
||||||
[[inputs.ntpq]]
|
|
||||||
## If false, set the -n ntpq flag. Can reduce metric gather times.
|
|
||||||
dns_lookup = true
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- ntpq
|
|
||||||
- delay (float, milliseconds)
|
|
||||||
- jitter (float, milliseconds)
|
|
||||||
- offset (float, milliseconds)
|
|
||||||
- poll (int, seconds)
|
|
||||||
- reach (int)
|
|
||||||
- when (int, seconds)
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
- All measurements have the following tags:
|
|
||||||
- refid
|
|
||||||
- remote
|
|
||||||
- type
|
|
||||||
- stratum
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ telegraf -config ~/ws/telegraf.conf -input-filter ntpq -test
|
|
||||||
* Plugin: ntpq, Collection 1
|
|
||||||
> ntpq,refid=.GPSs.,remote=*time.apple.com,stratum=1,type=u delay=91.797,jitter=3.735,offset=12.841,poll=64i,reach=377i,when=35i 1457960478909556134
|
|
||||||
```
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package ntpq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"log"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mapping of ntpq header names to tag keys
|
|
||||||
var tagHeaders map[string]string = map[string]string{
|
|
||||||
"remote": "remote",
|
|
||||||
"refid": "refid",
|
|
||||||
"st": "stratum",
|
|
||||||
"t": "type",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping of the ntpq tag key to the index in the command output
|
|
||||||
var tagI map[string]int = map[string]int{
|
|
||||||
"remote": -1,
|
|
||||||
"refid": -1,
|
|
||||||
"stratum": -1,
|
|
||||||
"type": -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping of float metrics to their index in the command output
|
|
||||||
var floatI map[string]int = map[string]int{
|
|
||||||
"delay": -1,
|
|
||||||
"offset": -1,
|
|
||||||
"jitter": -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping of int metrics to their index in the command output
|
|
||||||
var intI map[string]int = map[string]int{
|
|
||||||
"when": -1,
|
|
||||||
"poll": -1,
|
|
||||||
"reach": -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
type NTPQ struct {
|
|
||||||
runQ func() ([]byte, error)
|
|
||||||
|
|
||||||
DNSLookup bool `toml:"dns_lookup"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NTPQ) Description() string {
|
|
||||||
return "Get standard NTP query metrics, requires ntpq executable."
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NTPQ) SampleConfig() string {
|
|
||||||
return `
|
|
||||||
## If false, set the -n ntpq flag. Can reduce metric gather time.
|
|
||||||
dns_lookup = true
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NTPQ) Gather(acc telegraf.Accumulator) error {
|
|
||||||
out, err := n.runQ()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
lineCounter := 0
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(out))
|
|
||||||
for scanner.Scan() {
|
|
||||||
fields := strings.Fields(scanner.Text())
|
|
||||||
if len(fields) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If lineCounter == 0, then this is the header line
|
|
||||||
if lineCounter == 0 {
|
|
||||||
for i, field := range fields {
|
|
||||||
// Check if field is a tag:
|
|
||||||
if tagKey, ok := tagHeaders[field]; ok {
|
|
||||||
tagI[tagKey] = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if field is a float metric:
|
|
||||||
if _, ok := floatI[field]; ok {
|
|
||||||
floatI[field] = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if field is an int metric:
|
|
||||||
if _, ok := intI[field]; ok {
|
|
||||||
intI[field] = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tags := make(map[string]string)
|
|
||||||
mFields := make(map[string]interface{})
|
|
||||||
|
|
||||||
// Get tags from output
|
|
||||||
for key, index := range tagI {
|
|
||||||
if index == -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tags[key] = fields[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get integer metrics from output
|
|
||||||
for key, index := range intI {
|
|
||||||
if index == -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if key == "when" {
|
|
||||||
when := fields[index]
|
|
||||||
switch {
|
|
||||||
case strings.HasSuffix(when, "h"):
|
|
||||||
m, err := strconv.Atoi(strings.TrimSuffix(fields[index], "h"))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR ntpq: parsing int: %s", fields[index])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// seconds in an hour
|
|
||||||
mFields[key] = int64(m) * 360
|
|
||||||
continue
|
|
||||||
case strings.HasSuffix(when, "d"):
|
|
||||||
m, err := strconv.Atoi(strings.TrimSuffix(fields[index], "d"))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR ntpq: parsing int: %s", fields[index])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// seconds in a day
|
|
||||||
mFields[key] = int64(m) * 86400
|
|
||||||
continue
|
|
||||||
case strings.HasSuffix(when, "m"):
|
|
||||||
m, err := strconv.Atoi(strings.TrimSuffix(fields[index], "m"))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR ntpq: parsing int: %s", fields[index])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// seconds in a day
|
|
||||||
mFields[key] = int64(m) * 60
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := strconv.Atoi(fields[index])
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR ntpq: parsing int: %s", fields[index])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mFields[key] = int64(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get float metrics from output
|
|
||||||
for key, index := range floatI {
|
|
||||||
if index == -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := strconv.ParseFloat(fields[index], 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR ntpq: parsing float: %s", fields[index])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mFields[key] = m
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AddFields("ntpq", mFields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
lineCounter++
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NTPQ) runq() ([]byte, error) {
|
|
||||||
bin, err := exec.LookPath("ntpq")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmd *exec.Cmd
|
|
||||||
if n.DNSLookup {
|
|
||||||
cmd = exec.Command(bin, "-p")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command(bin, "-p", "-n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Output()
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("ntpq", func() telegraf.Input {
|
|
||||||
n := &NTPQ{}
|
|
||||||
n.runQ = n.runq
|
|
||||||
return n
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,422 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package ntpq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSingleNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(singleNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(101),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadIntNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(badIntParseNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(101),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadFloatNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(badFloatParseNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(2),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDaysNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(whenDaysNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(172800),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHoursNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(whenHoursNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(720),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMinutesNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(whenMinutesNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(120),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadWhenNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(whenBadNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultiNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(multiNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"delay": float64(54.033),
|
|
||||||
"jitter": float64(449514),
|
|
||||||
"offset": float64(243.426),
|
|
||||||
"poll": int64(1024),
|
|
||||||
"reach": int64(377),
|
|
||||||
"when": int64(740),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"refid": "10.177.80.37",
|
|
||||||
"remote": "83.137.98.96",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
|
|
||||||
fields = map[string]interface{}{
|
|
||||||
"delay": float64(60.785),
|
|
||||||
"jitter": float64(449539),
|
|
||||||
"offset": float64(232.597),
|
|
||||||
"poll": int64(1024),
|
|
||||||
"reach": int64(377),
|
|
||||||
"when": int64(739),
|
|
||||||
}
|
|
||||||
tags = map[string]string{
|
|
||||||
"refid": "10.177.80.37",
|
|
||||||
"remote": "81.7.16.52",
|
|
||||||
"stratum": "2",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadHeaderNTPQ(t *testing.T) {
|
|
||||||
resetVars()
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(badHeaderNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(101),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"delay": float64(51.016),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMissingDelayColumnNTPQ(t *testing.T) {
|
|
||||||
resetVars()
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(missingDelayNTPQ),
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.NoError(t, n.Gather(&acc))
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"when": int64(101),
|
|
||||||
"poll": int64(256),
|
|
||||||
"reach": int64(37),
|
|
||||||
"offset": float64(233.010),
|
|
||||||
"jitter": float64(17.462),
|
|
||||||
}
|
|
||||||
tags := map[string]string{
|
|
||||||
"remote": "*uschi5-ntp-002.",
|
|
||||||
"refid": "10.177.80.46",
|
|
||||||
"type": "u",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "ntpq", fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailedNTPQ(t *testing.T) {
|
|
||||||
tt := tester{
|
|
||||||
ret: []byte(singleNTPQ),
|
|
||||||
err: fmt.Errorf("Test failure"),
|
|
||||||
}
|
|
||||||
n := &NTPQ{
|
|
||||||
runQ: tt.runqTest,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
assert.Error(t, n.Gather(&acc))
|
|
||||||
}
|
|
||||||
|
|
||||||
type tester struct {
|
|
||||||
ret []byte
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tester) runqTest() ([]byte, error) {
|
|
||||||
return t.ret, t.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetVars() {
|
|
||||||
// Mapping of ntpq header names to tag keys
|
|
||||||
tagHeaders = map[string]string{
|
|
||||||
"remote": "remote",
|
|
||||||
"refid": "refid",
|
|
||||||
"st": "stratum",
|
|
||||||
"t": "type",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping of the ntpq tag key to the index in the command output
|
|
||||||
tagI = map[string]int{
|
|
||||||
"remote": -1,
|
|
||||||
"refid": -1,
|
|
||||||
"stratum": -1,
|
|
||||||
"type": -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping of float metrics to their index in the command output
|
|
||||||
floatI = map[string]int{
|
|
||||||
"delay": -1,
|
|
||||||
"offset": -1,
|
|
||||||
"jitter": -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping of int metrics to their index in the command output
|
|
||||||
intI = map[string]int{
|
|
||||||
"when": -1,
|
|
||||||
"poll": -1,
|
|
||||||
"reach": -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var singleNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var badHeaderNTPQ = `remote refid foobar t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var missingDelayNTPQ = `remote refid foobar t when poll reach offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 101 256 37 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var whenDaysNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 2d 256 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var whenHoursNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 2h 256 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var whenMinutesNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 2m 256 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var whenBadNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 2q 256 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var badFloatParseNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 2 256 37 51.016 foobar 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var badIntParseNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
*uschi5-ntp-002. 10.177.80.46 2 u 101 foobar 37 51.016 233.010 17.462
|
|
||||||
`
|
|
||||||
|
|
||||||
var multiNTPQ = ` remote refid st t when poll reach delay offset jitter
|
|
||||||
==============================================================================
|
|
||||||
83.137.98.96 10.177.80.37 2 u 740 1024 377 54.033 243.426 449514.
|
|
||||||
81.7.16.52 10.177.80.37 2 u 739 1024 377 60.785 232.597 449539.
|
|
||||||
131.188.3.221 10.177.80.37 2 u 783 1024 377 111.820 261.921 449528.
|
|
||||||
5.9.29.107 10.177.80.37 2 u 703 1024 377 205.704 160.406 449602.
|
|
||||||
91.189.94.4 10.177.80.37 2 u 673 1024 377 143.047 274.726 449445.
|
|
||||||
`
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package ntpq
|
|
||||||
@@ -1,331 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package phpfpm
|
|
||||||
|
|
||||||
// This file implements FastCGI from the perspective of a child process.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/cgi"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// request holds the state for an in-progress request. As soon as it's complete,
|
|
||||||
// it's converted to an http.Request.
|
|
||||||
type request struct {
|
|
||||||
pw *io.PipeWriter
|
|
||||||
reqId uint16
|
|
||||||
params map[string]string
|
|
||||||
buf [1024]byte
|
|
||||||
rawParams []byte
|
|
||||||
keepConn bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRequest(reqId uint16, flags uint8) *request {
|
|
||||||
r := &request{
|
|
||||||
reqId: reqId,
|
|
||||||
params: map[string]string{},
|
|
||||||
keepConn: flags&flagKeepConn != 0,
|
|
||||||
}
|
|
||||||
r.rawParams = r.buf[:0]
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseParams reads an encoded []byte into Params.
|
|
||||||
func (r *request) parseParams() {
|
|
||||||
text := r.rawParams
|
|
||||||
r.rawParams = nil
|
|
||||||
for len(text) > 0 {
|
|
||||||
keyLen, n := readSize(text)
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text = text[n:]
|
|
||||||
valLen, n := readSize(text)
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text = text[n:]
|
|
||||||
if int(keyLen)+int(valLen) > len(text) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
key := readString(text, keyLen)
|
|
||||||
text = text[keyLen:]
|
|
||||||
val := readString(text, valLen)
|
|
||||||
text = text[valLen:]
|
|
||||||
r.params[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// response implements http.ResponseWriter.
|
|
||||||
type response struct {
|
|
||||||
req *request
|
|
||||||
header http.Header
|
|
||||||
w *bufWriter
|
|
||||||
wroteHeader bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newResponse(c *child, req *request) *response {
|
|
||||||
return &response{
|
|
||||||
req: req,
|
|
||||||
header: http.Header{},
|
|
||||||
w: newWriter(c.conn, typeStdout, req.reqId),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *response) Header() http.Header {
|
|
||||||
return r.header
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *response) Write(data []byte) (int, error) {
|
|
||||||
if !r.wroteHeader {
|
|
||||||
r.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
return r.w.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *response) WriteHeader(code int) {
|
|
||||||
if r.wroteHeader {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.wroteHeader = true
|
|
||||||
if code == http.StatusNotModified {
|
|
||||||
// Must not have body.
|
|
||||||
r.header.Del("Content-Type")
|
|
||||||
r.header.Del("Content-Length")
|
|
||||||
r.header.Del("Transfer-Encoding")
|
|
||||||
} else if r.header.Get("Content-Type") == "" {
|
|
||||||
r.header.Set("Content-Type", "text/html; charset=utf-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.header.Get("Date") == "" {
|
|
||||||
r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
|
|
||||||
r.header.Write(r.w)
|
|
||||||
r.w.WriteString("\r\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *response) Flush() {
|
|
||||||
if !r.wroteHeader {
|
|
||||||
r.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
r.w.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *response) Close() error {
|
|
||||||
r.Flush()
|
|
||||||
return r.w.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
type child struct {
|
|
||||||
conn *conn
|
|
||||||
handler http.Handler
|
|
||||||
|
|
||||||
mu sync.Mutex // protects requests:
|
|
||||||
requests map[uint16]*request // keyed by request ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
|
|
||||||
return &child{
|
|
||||||
conn: newConn(rwc),
|
|
||||||
handler: handler,
|
|
||||||
requests: make(map[uint16]*request),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *child) serve() {
|
|
||||||
defer c.conn.Close()
|
|
||||||
defer c.cleanUp()
|
|
||||||
var rec record
|
|
||||||
for {
|
|
||||||
if err := rec.read(c.conn.rwc); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := c.handleRecord(&rec); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var errCloseConn = errors.New("fcgi: connection should be closed")
|
|
||||||
|
|
||||||
var emptyBody = ioutil.NopCloser(strings.NewReader(""))
|
|
||||||
|
|
||||||
// ErrRequestAborted is returned by Read when a handler attempts to read the
|
|
||||||
// body of a request that has been aborted by the web server.
|
|
||||||
var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
|
|
||||||
|
|
||||||
// ErrConnClosed is returned by Read when a handler attempts to read the body of
|
|
||||||
// a request after the connection to the web server has been closed.
|
|
||||||
var ErrConnClosed = errors.New("fcgi: connection to web server closed")
|
|
||||||
|
|
||||||
func (c *child) handleRecord(rec *record) error {
|
|
||||||
c.mu.Lock()
|
|
||||||
req, ok := c.requests[rec.h.Id]
|
|
||||||
c.mu.Unlock()
|
|
||||||
if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
|
|
||||||
// The spec says to ignore unknown request IDs.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rec.h.Type {
|
|
||||||
case typeBeginRequest:
|
|
||||||
if req != nil {
|
|
||||||
// The server is trying to begin a request with the same ID
|
|
||||||
// as an in-progress request. This is an error.
|
|
||||||
return errors.New("fcgi: received ID that is already in-flight")
|
|
||||||
}
|
|
||||||
|
|
||||||
var br beginRequest
|
|
||||||
if err := br.read(rec.content()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if br.role != roleResponder {
|
|
||||||
c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
req = newRequest(rec.h.Id, br.flags)
|
|
||||||
c.mu.Lock()
|
|
||||||
c.requests[rec.h.Id] = req
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
case typeParams:
|
|
||||||
// NOTE(eds): Technically a key-value pair can straddle the boundary
|
|
||||||
// between two packets. We buffer until we've received all parameters.
|
|
||||||
if len(rec.content()) > 0 {
|
|
||||||
req.rawParams = append(req.rawParams, rec.content()...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
req.parseParams()
|
|
||||||
return nil
|
|
||||||
case typeStdin:
|
|
||||||
content := rec.content()
|
|
||||||
if req.pw == nil {
|
|
||||||
var body io.ReadCloser
|
|
||||||
if len(content) > 0 {
|
|
||||||
// body could be an io.LimitReader, but it shouldn't matter
|
|
||||||
// as long as both sides are behaving.
|
|
||||||
body, req.pw = io.Pipe()
|
|
||||||
} else {
|
|
||||||
body = emptyBody
|
|
||||||
}
|
|
||||||
go c.serveRequest(req, body)
|
|
||||||
}
|
|
||||||
if len(content) > 0 {
|
|
||||||
// TODO(eds): This blocks until the handler reads from the pipe.
|
|
||||||
// If the handler takes a long time, it might be a problem.
|
|
||||||
req.pw.Write(content)
|
|
||||||
} else if req.pw != nil {
|
|
||||||
req.pw.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case typeGetValues:
|
|
||||||
values := map[string]string{"FCGI_MPXS_CONNS": "1"}
|
|
||||||
c.conn.writePairs(typeGetValuesResult, 0, values)
|
|
||||||
return nil
|
|
||||||
case typeData:
|
|
||||||
// If the filter role is implemented, read the data stream here.
|
|
||||||
return nil
|
|
||||||
case typeAbortRequest:
|
|
||||||
c.mu.Lock()
|
|
||||||
delete(c.requests, rec.h.Id)
|
|
||||||
c.mu.Unlock()
|
|
||||||
c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
|
|
||||||
if req.pw != nil {
|
|
||||||
req.pw.CloseWithError(ErrRequestAborted)
|
|
||||||
}
|
|
||||||
if !req.keepConn {
|
|
||||||
// connection will close upon return
|
|
||||||
return errCloseConn
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
b := make([]byte, 8)
|
|
||||||
b[0] = byte(rec.h.Type)
|
|
||||||
c.conn.writeRecord(typeUnknownType, 0, b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *child) serveRequest(req *request, body io.ReadCloser) {
|
|
||||||
r := newResponse(c, req)
|
|
||||||
httpReq, err := cgi.RequestFromMap(req.params)
|
|
||||||
if err != nil {
|
|
||||||
// there was an error reading the request
|
|
||||||
r.WriteHeader(http.StatusInternalServerError)
|
|
||||||
c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
|
|
||||||
} else {
|
|
||||||
httpReq.Body = body
|
|
||||||
c.handler.ServeHTTP(r, httpReq)
|
|
||||||
}
|
|
||||||
r.Close()
|
|
||||||
c.mu.Lock()
|
|
||||||
delete(c.requests, req.reqId)
|
|
||||||
c.mu.Unlock()
|
|
||||||
c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
|
|
||||||
|
|
||||||
// Consume the entire body, so the host isn't still writing to
|
|
||||||
// us when we close the socket below in the !keepConn case,
|
|
||||||
// otherwise we'd send a RST. (golang.org/issue/4183)
|
|
||||||
// TODO(bradfitz): also bound this copy in time. Or send
|
|
||||||
// some sort of abort request to the host, so the host
|
|
||||||
// can properly cut off the client sending all the data.
|
|
||||||
// For now just bound it a little and
|
|
||||||
io.CopyN(ioutil.Discard, body, 100<<20)
|
|
||||||
body.Close()
|
|
||||||
|
|
||||||
if !req.keepConn {
|
|
||||||
c.conn.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *child) cleanUp() {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
for _, req := range c.requests {
|
|
||||||
if req.pw != nil {
|
|
||||||
// race with call to Close in c.serveRequest doesn't matter because
|
|
||||||
// Pipe(Reader|Writer).Close are idempotent
|
|
||||||
req.pw.CloseWithError(ErrConnClosed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve accepts incoming FastCGI connections on the listener l, creating a new
|
|
||||||
// goroutine for each. The goroutine reads requests and then calls handler
|
|
||||||
// to reply to them.
|
|
||||||
// If l is nil, Serve accepts connections from os.Stdin.
|
|
||||||
// If handler is nil, http.DefaultServeMux is used.
|
|
||||||
func Serve(l net.Listener, handler http.Handler) error {
|
|
||||||
if l == nil {
|
|
||||||
var err error
|
|
||||||
l, err = net.FileListener(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer l.Close()
|
|
||||||
}
|
|
||||||
if handler == nil {
|
|
||||||
handler = http.DefaultServeMux
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
rw, err := l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c := newChild(rw, handler)
|
|
||||||
go c.serve()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package phpfpm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create an fcgi client
|
|
||||||
func newFcgiClient(h string, args ...interface{}) (*conn, error) {
|
|
||||||
var con net.Conn
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, errors.New("fcgi: not enough params")
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
switch args[0].(type) {
|
|
||||||
case int:
|
|
||||||
addr := h + ":" + strconv.FormatInt(int64(args[0].(int)), 10)
|
|
||||||
con, err = net.Dial("tcp", addr)
|
|
||||||
case string:
|
|
||||||
laddr := net.UnixAddr{Name: args[0].(string), Net: h}
|
|
||||||
con, err = net.DialUnix(h, nil, &laddr)
|
|
||||||
default:
|
|
||||||
err = errors.New("fcgi: we only accept int (port) or string (socket) params.")
|
|
||||||
}
|
|
||||||
fcgi := &conn{
|
|
||||||
rwc: con,
|
|
||||||
}
|
|
||||||
|
|
||||||
return fcgi, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *conn) Request(
|
|
||||||
env map[string]string,
|
|
||||||
requestData string,
|
|
||||||
) (retout []byte, reterr []byte, err error) {
|
|
||||||
defer client.rwc.Close()
|
|
||||||
var reqId uint16 = 1
|
|
||||||
|
|
||||||
err = client.writeBeginRequest(reqId, uint16(roleResponder), 0)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = client.writePairs(typeParams, reqId, env)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requestData) > 0 {
|
|
||||||
if err = client.writeRecord(typeStdin, reqId, []byte(requestData)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rec := &record{}
|
|
||||||
var err1 error
|
|
||||||
|
|
||||||
// recive untill EOF or FCGI_END_REQUEST
|
|
||||||
READ_LOOP:
|
|
||||||
for {
|
|
||||||
err1 = rec.read(client.rwc)
|
|
||||||
if err1 != nil && strings.Contains(err1.Error(), "use of closed network connection") {
|
|
||||||
if err1 != io.EOF {
|
|
||||||
err = err1
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case rec.h.Type == typeStdout:
|
|
||||||
retout = append(retout, rec.content()...)
|
|
||||||
case rec.h.Type == typeStderr:
|
|
||||||
reterr = append(reterr, rec.content()...)
|
|
||||||
case rec.h.Type == typeEndRequest:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
break READ_LOOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package phpfpm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sizeTests = []struct {
|
|
||||||
size uint32
|
|
||||||
bytes []byte
|
|
||||||
}{
|
|
||||||
{0, []byte{0x00}},
|
|
||||||
{127, []byte{0x7F}},
|
|
||||||
{128, []byte{0x80, 0x00, 0x00, 0x80}},
|
|
||||||
{1000, []byte{0x80, 0x00, 0x03, 0xE8}},
|
|
||||||
{33554431, []byte{0x81, 0xFF, 0xFF, 0xFF}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSize(t *testing.T) {
|
|
||||||
b := make([]byte, 4)
|
|
||||||
for i, test := range sizeTests {
|
|
||||||
n := encodeSize(b, test.size)
|
|
||||||
if !bytes.Equal(b[:n], test.bytes) {
|
|
||||||
t.Errorf("%d expected %x, encoded %x", i, test.bytes, b)
|
|
||||||
}
|
|
||||||
size, n := readSize(test.bytes)
|
|
||||||
if size != test.size {
|
|
||||||
t.Errorf("%d expected %d, read %d", i, test.size, size)
|
|
||||||
}
|
|
||||||
if len(test.bytes) != n {
|
|
||||||
t.Errorf("%d did not consume all the bytes", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var streamTests = []struct {
|
|
||||||
desc string
|
|
||||||
recType recType
|
|
||||||
reqId uint16
|
|
||||||
content []byte
|
|
||||||
raw []byte
|
|
||||||
}{
|
|
||||||
{"single record", typeStdout, 1, nil,
|
|
||||||
[]byte{1, byte(typeStdout), 0, 1, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
// this data will have to be split into two records
|
|
||||||
{"two records", typeStdin, 300, make([]byte, 66000),
|
|
||||||
bytes.Join([][]byte{
|
|
||||||
// header for the first record
|
|
||||||
{1, byte(typeStdin), 0x01, 0x2C, 0xFF, 0xFF, 1, 0},
|
|
||||||
make([]byte, 65536),
|
|
||||||
// header for the second
|
|
||||||
{1, byte(typeStdin), 0x01, 0x2C, 0x01, 0xD1, 7, 0},
|
|
||||||
make([]byte, 472),
|
|
||||||
// header for the empty record
|
|
||||||
{1, byte(typeStdin), 0x01, 0x2C, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
nil),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type nilCloser struct {
|
|
||||||
io.ReadWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *nilCloser) Close() error { return nil }
|
|
||||||
|
|
||||||
func TestStreams(t *testing.T) {
|
|
||||||
var rec record
|
|
||||||
outer:
|
|
||||||
for _, test := range streamTests {
|
|
||||||
buf := bytes.NewBuffer(test.raw)
|
|
||||||
var content []byte
|
|
||||||
for buf.Len() > 0 {
|
|
||||||
if err := rec.read(buf); err != nil {
|
|
||||||
t.Errorf("%s: error reading record: %v", test.desc, err)
|
|
||||||
continue outer
|
|
||||||
}
|
|
||||||
content = append(content, rec.content()...)
|
|
||||||
}
|
|
||||||
if rec.h.Type != test.recType {
|
|
||||||
t.Errorf("%s: got type %d expected %d", test.desc, rec.h.Type, test.recType)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rec.h.Id != test.reqId {
|
|
||||||
t.Errorf("%s: got request ID %d expected %d", test.desc, rec.h.Id, test.reqId)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !bytes.Equal(content, test.content) {
|
|
||||||
t.Errorf("%s: read wrong content", test.desc)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf.Reset()
|
|
||||||
c := newConn(&nilCloser{buf})
|
|
||||||
w := newWriter(c, test.recType, test.reqId)
|
|
||||||
if _, err := w.Write(test.content); err != nil {
|
|
||||||
t.Errorf("%s: error writing record: %v", test.desc, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := w.Close(); err != nil {
|
|
||||||
t.Errorf("%s: error closing stream: %v", test.desc, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !bytes.Equal(buf.Bytes(), test.raw) {
|
|
||||||
t.Errorf("%s: wrote wrong content", test.desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type writeOnlyConn struct {
|
|
||||||
buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *writeOnlyConn) Write(p []byte) (int, error) {
|
|
||||||
c.buf = append(c.buf, p...)
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *writeOnlyConn) Read(p []byte) (int, error) {
|
|
||||||
return 0, errors.New("conn is write-only")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *writeOnlyConn) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetValues(t *testing.T) {
|
|
||||||
var rec record
|
|
||||||
rec.h.Type = typeGetValues
|
|
||||||
|
|
||||||
wc := new(writeOnlyConn)
|
|
||||||
c := newChild(wc, nil)
|
|
||||||
err := c.handleRecord(&rec)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("handleRecord: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const want = "\x01\n\x00\x00\x00\x12\x06\x00" +
|
|
||||||
"\x0f\x01FCGI_MPXS_CONNS1" +
|
|
||||||
"\x00\x00\x00\x00\x00\x00\x01\n\x00\x00\x00\x00\x00\x00"
|
|
||||||
if got := string(wc.buf); got != want {
|
|
||||||
t.Errorf(" got: %q\nwant: %q\n", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nameValuePair11(nameData, valueData string) []byte {
|
|
||||||
return bytes.Join(
|
|
||||||
[][]byte{
|
|
||||||
{byte(len(nameData)), byte(len(valueData))},
|
|
||||||
[]byte(nameData),
|
|
||||||
[]byte(valueData),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeRecord(
|
|
||||||
recordType recType,
|
|
||||||
requestId uint16,
|
|
||||||
contentData []byte,
|
|
||||||
) []byte {
|
|
||||||
requestIdB1 := byte(requestId >> 8)
|
|
||||||
requestIdB0 := byte(requestId)
|
|
||||||
|
|
||||||
contentLength := len(contentData)
|
|
||||||
contentLengthB1 := byte(contentLength >> 8)
|
|
||||||
contentLengthB0 := byte(contentLength)
|
|
||||||
return bytes.Join([][]byte{
|
|
||||||
{1, byte(recordType), requestIdB1, requestIdB0, contentLengthB1,
|
|
||||||
contentLengthB0, 0, 0},
|
|
||||||
contentData,
|
|
||||||
},
|
|
||||||
nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// a series of FastCGI records that start a request and begin sending the
|
|
||||||
// request body
|
|
||||||
var streamBeginTypeStdin = bytes.Join([][]byte{
|
|
||||||
// set up request 1
|
|
||||||
makeRecord(typeBeginRequest, 1,
|
|
||||||
[]byte{0, byte(roleResponder), 0, 0, 0, 0, 0, 0}),
|
|
||||||
// add required parameters to request 1
|
|
||||||
makeRecord(typeParams, 1, nameValuePair11("REQUEST_METHOD", "GET")),
|
|
||||||
makeRecord(typeParams, 1, nameValuePair11("SERVER_PROTOCOL", "HTTP/1.1")),
|
|
||||||
makeRecord(typeParams, 1, nil),
|
|
||||||
// begin sending body of request 1
|
|
||||||
makeRecord(typeStdin, 1, []byte("0123456789abcdef")),
|
|
||||||
},
|
|
||||||
nil)
|
|
||||||
|
|
||||||
var cleanUpTests = []struct {
|
|
||||||
input []byte
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
// confirm that child.handleRecord closes req.pw after aborting req
|
|
||||||
{
|
|
||||||
bytes.Join([][]byte{
|
|
||||||
streamBeginTypeStdin,
|
|
||||||
makeRecord(typeAbortRequest, 1, nil),
|
|
||||||
},
|
|
||||||
nil),
|
|
||||||
ErrRequestAborted,
|
|
||||||
},
|
|
||||||
// confirm that child.serve closes all pipes after error reading record
|
|
||||||
{
|
|
||||||
bytes.Join([][]byte{
|
|
||||||
streamBeginTypeStdin,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
nil),
|
|
||||||
ErrConnClosed,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type nopWriteCloser struct {
|
|
||||||
io.ReadWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nopWriteCloser) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that child.serve closes the bodies of aborted requests and closes the
|
|
||||||
// bodies of all requests before returning. Causes deadlock if either condition
|
|
||||||
// isn't met. See issue 6934.
|
|
||||||
func TestChildServeCleansUp(t *testing.T) {
|
|
||||||
for _, tt := range cleanUpTests {
|
|
||||||
input := make([]byte, len(tt.input))
|
|
||||||
copy(input, tt.input)
|
|
||||||
rc := nopWriteCloser{bytes.NewBuffer(input)}
|
|
||||||
done := make(chan bool)
|
|
||||||
c := newChild(rc, http.HandlerFunc(func(
|
|
||||||
w http.ResponseWriter,
|
|
||||||
r *http.Request,
|
|
||||||
) {
|
|
||||||
// block on reading body of request
|
|
||||||
_, err := io.Copy(ioutil.Discard, r.Body)
|
|
||||||
if err != tt.err {
|
|
||||||
t.Errorf("Expected %#v, got %#v", tt.err, err)
|
|
||||||
}
|
|
||||||
// not reached if body of request isn't closed
|
|
||||||
done <- true
|
|
||||||
}))
|
|
||||||
go c.serve()
|
|
||||||
// wait for body of request to be closed or all goroutines to block
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type rwNopCloser struct {
|
|
||||||
io.Reader
|
|
||||||
io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rwNopCloser) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifies it doesn't crash. Issue 11824.
|
|
||||||
func TestMalformedParams(t *testing.T) {
|
|
||||||
input := []byte{
|
|
||||||
// beginRequest, requestId=1, contentLength=8, role=1, keepConn=1
|
|
||||||
1, 1, 0, 1, 0, 8, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
|
|
||||||
// params, requestId=1, contentLength=10, k1Len=50, v1Len=50 (malformed, wrong length)
|
|
||||||
1, 4, 0, 1, 0, 10, 0, 0, 50, 50, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
||||||
// end of params
|
|
||||||
1, 4, 0, 1, 0, 0, 0, 0,
|
|
||||||
}
|
|
||||||
rw := rwNopCloser{bytes.NewReader(input), ioutil.Discard}
|
|
||||||
c := newChild(rw, http.DefaultServeMux)
|
|
||||||
c.serve()
|
|
||||||
}
|
|
||||||
@@ -112,7 +112,6 @@ func (g *phpfpm) gatherServer(addr string, acc telegraf.Accumulator) error {
|
|||||||
statusPath string
|
statusPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
var err error
|
|
||||||
if strings.HasPrefix(addr, "fcgi://") || strings.HasPrefix(addr, "cgi://") {
|
if strings.HasPrefix(addr, "fcgi://") || strings.HasPrefix(addr, "cgi://") {
|
||||||
u, err := url.Parse(addr)
|
u, err := url.Parse(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -121,7 +120,7 @@ func (g *phpfpm) gatherServer(addr string, acc telegraf.Accumulator) error {
|
|||||||
socketAddr := strings.Split(u.Host, ":")
|
socketAddr := strings.Split(u.Host, ":")
|
||||||
fcgiIp := socketAddr[0]
|
fcgiIp := socketAddr[0]
|
||||||
fcgiPort, _ := strconv.Atoi(socketAddr[1])
|
fcgiPort, _ := strconv.Atoi(socketAddr[1])
|
||||||
fcgi, err = newFcgiClient(fcgiIp, fcgiPort)
|
fcgi, _ = NewClient(fcgiIp, fcgiPort)
|
||||||
} else {
|
} else {
|
||||||
socketAddr := strings.Split(addr, ":")
|
socketAddr := strings.Split(addr, ":")
|
||||||
if len(socketAddr) >= 2 {
|
if len(socketAddr) >= 2 {
|
||||||
@@ -135,13 +134,8 @@ func (g *phpfpm) gatherServer(addr string, acc telegraf.Accumulator) error {
|
|||||||
if _, err := os.Stat(socketPath); os.IsNotExist(err) {
|
if _, err := os.Stat(socketPath); os.IsNotExist(err) {
|
||||||
return fmt.Errorf("Socket doesn't exist '%s': %s", socketPath, err)
|
return fmt.Errorf("Socket doesn't exist '%s': %s", socketPath, err)
|
||||||
}
|
}
|
||||||
fcgi, err = newFcgiClient("unix", socketPath)
|
fcgi, _ = NewClient("unix", socketPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return g.gatherFcgi(fcgi, statusPath, acc)
|
return g.gatherFcgi(fcgi, statusPath, acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// recType is a record type, as defined by
|
// recType is a record type, as defined by
|
||||||
@@ -272,3 +277,74 @@ func (w *streamWriter) Close() error {
|
|||||||
// send empty record to close the stream
|
// send empty record to close the stream
|
||||||
return w.c.writeRecord(w.recType, w.reqId, nil)
|
return w.c.writeRecord(w.recType, w.reqId, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewClient(h string, args ...interface{}) (fcgi *conn, err error) {
|
||||||
|
var con net.Conn
|
||||||
|
if len(args) != 1 {
|
||||||
|
err = errors.New("fcgi: not enough params")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch args[0].(type) {
|
||||||
|
case int:
|
||||||
|
addr := h + ":" + strconv.FormatInt(int64(args[0].(int)), 10)
|
||||||
|
con, err = net.Dial("tcp", addr)
|
||||||
|
case string:
|
||||||
|
laddr := net.UnixAddr{Name: args[0].(string), Net: h}
|
||||||
|
con, err = net.DialUnix(h, nil, &laddr)
|
||||||
|
default:
|
||||||
|
err = errors.New("fcgi: we only accept int (port) or string (socket) params.")
|
||||||
|
}
|
||||||
|
fcgi = &conn{
|
||||||
|
rwc: con,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *conn) Request(env map[string]string, requestData string) (retout []byte, reterr []byte, err error) {
|
||||||
|
defer client.rwc.Close()
|
||||||
|
var reqId uint16 = 1
|
||||||
|
|
||||||
|
err = client.writeBeginRequest(reqId, uint16(roleResponder), 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.writePairs(typeParams, reqId, env)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(requestData) > 0 {
|
||||||
|
if err = client.writeRecord(typeStdin, reqId, []byte(requestData)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rec := &record{}
|
||||||
|
var err1 error
|
||||||
|
|
||||||
|
// recive untill EOF or FCGI_END_REQUEST
|
||||||
|
READ_LOOP:
|
||||||
|
for {
|
||||||
|
err1 = rec.read(client.rwc)
|
||||||
|
if err1 != nil && strings.Contains(err1.Error(), "use of closed network connection") {
|
||||||
|
if err1 != io.EOF {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case rec.h.Type == typeStdout:
|
||||||
|
retout = append(retout, rec.content()...)
|
||||||
|
case rec.h.Type == typeStderr:
|
||||||
|
reterr = append(reterr, rec.content()...)
|
||||||
|
case rec.h.Type == typeEndRequest:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
break READ_LOOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -4,22 +4,20 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Postgresql struct {
|
type Postgresql struct {
|
||||||
Address string
|
Address string
|
||||||
Databases []string
|
Databases []string
|
||||||
OrderedColumns []string
|
OrderedColumns []string
|
||||||
AllColumns []string
|
AllColumns []string
|
||||||
sanitizedAddress string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ignoredColumns = map[string]bool{"datid": true, "datname": true, "stats_reset": true}
|
var ignoredColumns = map[string]bool{"datid": true, "datname": true, "stats_reset": true}
|
||||||
@@ -135,23 +133,6 @@ type scanner interface {
|
|||||||
Scan(dest ...interface{}) error
|
Scan(dest ...interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
var passwordKVMatcher, _ = regexp.Compile("password=\\S+ ?")
|
|
||||||
|
|
||||||
func (p *Postgresql) SanitizedAddress() (_ string, err error) {
|
|
||||||
var canonicalizedAddress string
|
|
||||||
if strings.HasPrefix(p.Address, "postgres://") || strings.HasPrefix(p.Address, "postgresql://") {
|
|
||||||
canonicalizedAddress, err = pq.ParseURL(p.Address)
|
|
||||||
if err != nil {
|
|
||||||
return p.sanitizedAddress, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
canonicalizedAddress = p.Address
|
|
||||||
}
|
|
||||||
p.sanitizedAddress = passwordKVMatcher.ReplaceAllString(canonicalizedAddress, "")
|
|
||||||
|
|
||||||
return p.sanitizedAddress, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Postgresql) accRow(row scanner, acc telegraf.Accumulator) error {
|
func (p *Postgresql) accRow(row scanner, acc telegraf.Accumulator) error {
|
||||||
var columnVars []interface{}
|
var columnVars []interface{}
|
||||||
var dbname bytes.Buffer
|
var dbname bytes.Buffer
|
||||||
@@ -184,13 +165,7 @@ func (p *Postgresql) accRow(row scanner, acc telegraf.Accumulator) error {
|
|||||||
dbname.WriteString("postgres")
|
dbname.WriteString("postgres")
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagAddress string
|
tags := map[string]string{"server": p.Address, "db": dbname.String()}
|
||||||
tagAddress, err = p.SanitizedAddress()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tags := map[string]string{"server": tagAddress, "db": dbname.String()}
|
|
||||||
|
|
||||||
fields := make(map[string]interface{})
|
fields := make(map[string]interface{})
|
||||||
for col, val := range columnMap {
|
for col, val := range columnMap {
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ The above configuration would result in output like:
|
|||||||
# Measurements
|
# Measurements
|
||||||
Note: prefix can be set by the user, per process.
|
Note: prefix can be set by the user, per process.
|
||||||
|
|
||||||
|
|
||||||
Threads related measurement names:
|
|
||||||
- procstat_[prefix_]num_threads value=5
|
|
||||||
|
|
||||||
File descriptor related measurement names:
|
File descriptor related measurement names:
|
||||||
- procstat_[prefix_]num_fds value=4
|
- procstat_[prefix_]num_fds value=4
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ func NewSpecProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *SpecProcessor) pushMetrics() {
|
func (p *SpecProcessor) pushMetrics() {
|
||||||
p.pushNThreadsStats()
|
|
||||||
p.pushFDStats()
|
p.pushFDStats()
|
||||||
p.pushCtxStats()
|
p.pushCtxStats()
|
||||||
p.pushIOStats()
|
p.pushIOStats()
|
||||||
@@ -61,15 +60,6 @@ func (p *SpecProcessor) pushMetrics() {
|
|||||||
p.flush()
|
p.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *SpecProcessor) pushNThreadsStats() error {
|
|
||||||
numThreads, err := p.proc.NumThreads()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("NumThreads error: %s\n", err)
|
|
||||||
}
|
|
||||||
p.add("num_threads", numThreads)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SpecProcessor) pushFDStats() error {
|
func (p *SpecProcessor) pushFDStats() error {
|
||||||
fds, err := p.proc.NumFDs()
|
fds, err := p.proc.NumFDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Prometheus struct {
|
type Prometheus struct {
|
||||||
@@ -52,17 +51,8 @@ func (g *Prometheus) Gather(acc telegraf.Accumulator) error {
|
|||||||
return outerr
|
return outerr
|
||||||
}
|
}
|
||||||
|
|
||||||
var tr = &http.Transport{
|
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Prometheus) gatherURL(url string, acc telegraf.Accumulator) error {
|
func (g *Prometheus) gatherURL(url string, acc telegraf.Accumulator) error {
|
||||||
resp, err := client.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error making HTTP request to %s: %s", url, err)
|
return fmt.Errorf("error making HTTP request to %s: %s", url, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,11 +122,7 @@ func (r *RabbitMQ) Description() string {
|
|||||||
|
|
||||||
func (r *RabbitMQ) Gather(acc telegraf.Accumulator) error {
|
func (r *RabbitMQ) Gather(acc telegraf.Accumulator) error {
|
||||||
if r.Client == nil {
|
if r.Client == nil {
|
||||||
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
|
r.Client = &http.Client{}
|
||||||
r.Client = &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errChan = make(chan error, len(gatherFunctions))
|
var errChan = make(chan error, len(gatherFunctions))
|
||||||
|
|||||||
@@ -177,11 +177,8 @@ func (r *Raindrops) getTags(addr *url.URL) map[string]string {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("raindrops", func() telegraf.Input {
|
inputs.Add("raindrops", func() telegraf.Input {
|
||||||
return &Raindrops{http_client: &http.Client{
|
return &Raindrops{http_client: &http.Client{Transport: &http.Transport{
|
||||||
Transport: &http.Transport{
|
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
||||||
ResponseHeaderTimeout: time.Duration(3 * time.Second),
|
}}}
|
||||||
},
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
# Telegraf Plugin: Redis
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Read Redis's basic status information
|
|
||||||
[[inputs.redis]]
|
|
||||||
## specify servers via a url matching:
|
|
||||||
## [protocol://][:password]@address[:port]
|
|
||||||
## e.g.
|
|
||||||
## tcp://localhost:6379
|
|
||||||
## tcp://:password@192.168.99.100
|
|
||||||
##
|
|
||||||
## If no servers are specified, then localhost is used as the host.
|
|
||||||
## If no port is specified, 6379 is used
|
|
||||||
servers = ["tcp://localhost:6379"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- Measurement
|
|
||||||
- uptime_in_seconds
|
|
||||||
- connected_clients
|
|
||||||
- used_memory
|
|
||||||
- used_memory_rss
|
|
||||||
- used_memory_peak
|
|
||||||
- used_memory_lua
|
|
||||||
- rdb_changes_since_last_save
|
|
||||||
- total_connections_received
|
|
||||||
- total_commands_processed
|
|
||||||
- instantaneous_ops_per_sec
|
|
||||||
- instantaneous_input_kbps
|
|
||||||
- instantaneous_output_kbps
|
|
||||||
- sync_full
|
|
||||||
- sync_partial_ok
|
|
||||||
- sync_partial_err
|
|
||||||
- expired_keys
|
|
||||||
- evicted_keys
|
|
||||||
- keyspace_hits
|
|
||||||
- keyspace_misses
|
|
||||||
- pubsub_channels
|
|
||||||
- pubsub_patterns
|
|
||||||
- latest_fork_usec
|
|
||||||
- connected_slaves
|
|
||||||
- master_repl_offset
|
|
||||||
- repl_backlog_active
|
|
||||||
- repl_backlog_size
|
|
||||||
- repl_backlog_histlen
|
|
||||||
- mem_fragmentation_ratio
|
|
||||||
- used_cpu_sys
|
|
||||||
- used_cpu_user
|
|
||||||
- used_cpu_sys_children
|
|
||||||
- used_cpu_user_children
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
- All measurements have the following tags:
|
|
||||||
- port
|
|
||||||
- server
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
Using this configuration:
|
|
||||||
```
|
|
||||||
[[inputs.nginx]]
|
|
||||||
## specify servers via a url matching:
|
|
||||||
## [protocol://][:password]@address[:port]
|
|
||||||
## e.g.
|
|
||||||
## tcp://localhost:6379
|
|
||||||
## tcp://:password@192.168.99.100
|
|
||||||
##
|
|
||||||
## If no servers are specified, then localhost is used as the host.
|
|
||||||
## If no port is specified, 6379 is used
|
|
||||||
servers = ["tcp://localhost:6379"]
|
|
||||||
```
|
|
||||||
|
|
||||||
When run with:
|
|
||||||
```
|
|
||||||
./telegraf -config telegraf.conf -input-filter redis -test
|
|
||||||
```
|
|
||||||
|
|
||||||
It produces:
|
|
||||||
```
|
|
||||||
* Plugin: redis, Collection 1
|
|
||||||
> redis,port=6379,server=localhost clients=1i,connected_slaves=0i,evicted_keys=0i,expired_keys=0i,instantaneous_ops_per_sec=0i,keyspace_hitrate=0,keyspace_hits=0i,keyspace_misses=2i,latest_fork_usec=0i,master_repl_offset=0i,mem_fragmentation_ratio=3.58,pubsub_channels=0i,pubsub_patterns=0i,rdb_changes_since_last_save=0i,repl_backlog_active=0i,repl_backlog_histlen=0i,repl_backlog_size=1048576i,sync_full=0i,sync_partial_err=0i,sync_partial_ok=0i,total_commands_processed=4i,total_connections_received=2i,uptime=869i,used_cpu_sys=0.07,used_cpu_sys_children=0,used_cpu_user=0.1,used_cpu_user_children=0,used_memory=502048i,used_memory_lua=33792i,used_memory_peak=501128i,used_memory_rss=1798144i 1457052084987848383
|
|
||||||
```
|
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
@@ -31,8 +30,6 @@ var sampleConfig = `
|
|||||||
servers = ["tcp://localhost:6379"]
|
servers = ["tcp://localhost:6379"]
|
||||||
`
|
`
|
||||||
|
|
||||||
var defaultTimeout = 5 * time.Second
|
|
||||||
|
|
||||||
func (r *Redis) SampleConfig() string {
|
func (r *Redis) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
@@ -123,15 +120,12 @@ func (r *Redis) gatherServer(addr *url.URL, acc telegraf.Accumulator) error {
|
|||||||
addr.Host = addr.Host + ":" + defaultPort
|
addr.Host = addr.Host + ":" + defaultPort
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := net.DialTimeout("tcp", addr.Host, defaultTimeout)
|
c, err := net.Dial("tcp", addr.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to connect to redis server '%s': %s", addr.Host, err)
|
return fmt.Errorf("Unable to connect to redis server '%s': %s", addr.Host, err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
// Extend connection
|
|
||||||
c.SetDeadline(time.Now().Add(defaultTimeout))
|
|
||||||
|
|
||||||
if addr.User != nil {
|
if addr.User != nil {
|
||||||
pwd, set := addr.User.Password()
|
pwd, set := addr.User.Password()
|
||||||
if set && pwd != "" {
|
if set && pwd != "" {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
@@ -21,12 +20,7 @@ type Riak struct {
|
|||||||
|
|
||||||
// NewRiak return a new instance of Riak with a default http client
|
// NewRiak return a new instance of Riak with a default http client
|
||||||
func NewRiak() *Riak {
|
func NewRiak() *Riak {
|
||||||
tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)}
|
return &Riak{client: http.DefaultClient}
|
||||||
client := &http.Client{
|
|
||||||
Transport: tr,
|
|
||||||
Timeout: time.Duration(4 * time.Second),
|
|
||||||
}
|
|
||||||
return &Riak{client: client}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type riakStats represents the data that is received from Riak
|
// Type riakStats represents the data that is received from Riak
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (s *Sensors) Gather(acc telegraf.Accumulator) error {
|
|||||||
var found bool
|
var found bool
|
||||||
|
|
||||||
for _, sensor := range s.Sensors {
|
for _, sensor := range s.Sensors {
|
||||||
parts := strings.SplitN(sensor, ":", 2)
|
parts := strings.SplitN(":", sensor, 2)
|
||||||
|
|
||||||
if parts[0] == chipName {
|
if parts[0] == chipName {
|
||||||
if parts[1] == "*" || parts[1] == featureLabel {
|
if parts[1] == "*" || parts[1] == featureLabel {
|
||||||
|
|||||||
@@ -1,549 +0,0 @@
|
|||||||
# SNMP Input Plugin
|
|
||||||
|
|
||||||
The SNMP input plugin gathers metrics from SNMP agents
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
|
|
||||||
#### Very simple example
|
|
||||||
|
|
||||||
In this example, the plugin will gather value of OIDS:
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.4.1`
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Very Simple Example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Simple list of OIDs to get, in addition to "collect"
|
|
||||||
get_oids = [".1.3.6.1.2.1.2.2.1.4.1"]
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Simple example
|
|
||||||
|
|
||||||
In this example, Telegraf gathers value of OIDS:
|
|
||||||
|
|
||||||
- named **ifnumber**
|
|
||||||
- named **interface_speed**
|
|
||||||
|
|
||||||
With **inputs.snmp.get** section the plugin gets the oid number:
|
|
||||||
|
|
||||||
- **ifnumber** => `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- **interface_speed** => *ifSpeed*
|
|
||||||
|
|
||||||
As you can see *ifSpeed* is not a valid OID. In order to get
|
|
||||||
the valid OID, the plugin uses `snmptranslate_file` to match the OID:
|
|
||||||
|
|
||||||
- **ifnumber** => `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- **interface_speed** => *ifSpeed* => `.1.3.6.1.2.1.2.2.1.5`
|
|
||||||
|
|
||||||
Also as the plugin will append `instance` to the corresponding OID:
|
|
||||||
|
|
||||||
- **ifnumber** => `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- **interface_speed** => *ifSpeed* => `.1.3.6.1.2.1.2.2.1.5.1`
|
|
||||||
|
|
||||||
In this example, the plugin will gather value of OIDS:
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.5.1`
|
|
||||||
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Simple example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
## Use 'oids.txt' file to translate oids to names
|
|
||||||
## To generate 'oids.txt' you need to run:
|
|
||||||
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
||||||
## Or if you have an other MIB folder with custom MIBs
|
|
||||||
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
|
||||||
snmptranslate_file = "/tmp/oids.txt"
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Which get/bulk do you want to collect for this host
|
|
||||||
collect = ["ifnumber", "interface_speed"]
|
|
||||||
|
|
||||||
[[inputs.snmp.get]]
|
|
||||||
name = "ifnumber"
|
|
||||||
oid = ".1.3.6.1.2.1.2.1.0"
|
|
||||||
|
|
||||||
[[inputs.snmp.get]]
|
|
||||||
name = "interface_speed"
|
|
||||||
oid = "ifSpeed"
|
|
||||||
instance = "1"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Simple bulk example
|
|
||||||
|
|
||||||
In this example, Telegraf gathers value of OIDS:
|
|
||||||
|
|
||||||
- named **ifnumber**
|
|
||||||
- named **interface_speed**
|
|
||||||
- named **if_out_octets**
|
|
||||||
|
|
||||||
With **inputs.snmp.get** section the plugin gets oid number:
|
|
||||||
|
|
||||||
- **ifnumber** => `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- **interface_speed** => *ifSpeed*
|
|
||||||
|
|
||||||
With **inputs.snmp.bulk** section the plugin gets the oid number:
|
|
||||||
|
|
||||||
- **if_out_octets** => *ifOutOctets*
|
|
||||||
|
|
||||||
As you can see *ifSpeed* and *ifOutOctets* are not a valid OID.
|
|
||||||
In order to get the valid OID, the plugin uses `snmptranslate_file`
|
|
||||||
to match the OID:
|
|
||||||
|
|
||||||
- **ifnumber** => `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- **interface_speed** => *ifSpeed* => `.1.3.6.1.2.1.2.2.1.5`
|
|
||||||
- **if_out_octets** => *ifOutOctets* => `.1.3.6.1.2.1.2.2.1.16`
|
|
||||||
|
|
||||||
Also, the plugin will append `instance` to the corresponding OID:
|
|
||||||
|
|
||||||
- **ifnumber** => `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- **interface_speed** => *ifSpeed* => `.1.3.6.1.2.1.2.2.1.5.1`
|
|
||||||
|
|
||||||
And **if_out_octets** is a bulk request, the plugin will gathers all
|
|
||||||
OIDS in the table.
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.1`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.2`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.3`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.4`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.5`
|
|
||||||
- `...`
|
|
||||||
|
|
||||||
In this example, the plugin will gather value of OIDS:
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.2.1.0`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.5.1`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.1`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.2`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.3`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.4`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.16.5`
|
|
||||||
- `...`
|
|
||||||
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Simple bulk example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
## Use 'oids.txt' file to translate oids to names
|
|
||||||
## To generate 'oids.txt' you need to run:
|
|
||||||
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
||||||
## Or if you have an other MIB folder with custom MIBs
|
|
||||||
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
|
||||||
snmptranslate_file = "/tmp/oids.txt"
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Which get/bulk do you want to collect for this host
|
|
||||||
collect = ["interface_speed", "if_number", "if_out_octets"]
|
|
||||||
|
|
||||||
[[inputs.snmp.get]]
|
|
||||||
name = "interface_speed"
|
|
||||||
oid = "ifSpeed"
|
|
||||||
instance = "1"
|
|
||||||
|
|
||||||
[[inputs.snmp.get]]
|
|
||||||
name = "if_number"
|
|
||||||
oid = "ifNumber"
|
|
||||||
|
|
||||||
[[inputs.snmp.bulk]]
|
|
||||||
name = "if_out_octets"
|
|
||||||
oid = "ifOutOctets"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Table example
|
|
||||||
|
|
||||||
In this example, we remove collect attribute to the host section,
|
|
||||||
but you can still use it in combination of the following part.
|
|
||||||
|
|
||||||
Note: This example is like a bulk request a but using an
|
|
||||||
other configuration
|
|
||||||
|
|
||||||
Telegraf gathers value of OIDS of the table:
|
|
||||||
|
|
||||||
- named **iftable1**
|
|
||||||
|
|
||||||
With **inputs.snmp.table** section the plugin gets oid number:
|
|
||||||
|
|
||||||
- **iftable1** => `.1.3.6.1.2.1.31.1.1.1`
|
|
||||||
|
|
||||||
Also **iftable1** is a table, the plugin will gathers all
|
|
||||||
OIDS in the table and in the subtables
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.2`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.3`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.4`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.2`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.2....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.3`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.3....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.4`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.4....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.5....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6....`
|
|
||||||
- `...`
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Table example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
## Use 'oids.txt' file to translate oids to names
|
|
||||||
## To generate 'oids.txt' you need to run:
|
|
||||||
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
||||||
## Or if you have an other MIB folder with custom MIBs
|
|
||||||
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
|
||||||
snmptranslate_file = "/tmp/oids.txt"
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Which get/bulk do you want to collect for this host
|
|
||||||
# Which table do you want to collect
|
|
||||||
[[inputs.snmp.host.table]]
|
|
||||||
name = "iftable1"
|
|
||||||
|
|
||||||
# table without mapping neither subtables
|
|
||||||
# This is like bulk request
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable1"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Table with subtable example
|
|
||||||
|
|
||||||
In this example, we remove collect attribute to the host section,
|
|
||||||
but you can still use it in combination of the following part.
|
|
||||||
|
|
||||||
Note: This example is like a bulk request a but using an
|
|
||||||
other configuration
|
|
||||||
|
|
||||||
Telegraf gathers value of OIDS of the table:
|
|
||||||
|
|
||||||
- named **iftable2**
|
|
||||||
|
|
||||||
With **inputs.snmp.table** section *AND* **sub_tables** attribute,
|
|
||||||
the plugin will get OIDS from subtables:
|
|
||||||
|
|
||||||
- **iftable2** => `.1.3.6.1.2.1.2.2.1.13`
|
|
||||||
|
|
||||||
Also **iftable2** is a table, the plugin will gathers all
|
|
||||||
OIDS in subtables:
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.13.1`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.13.2`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.13.3`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.13.4`
|
|
||||||
- `.1.3.6.1.2.1.2.2.1.13....`
|
|
||||||
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Table with subtable example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
## Use 'oids.txt' file to translate oids to names
|
|
||||||
## To generate 'oids.txt' you need to run:
|
|
||||||
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
||||||
## Or if you have an other MIB folder with custom MIBs
|
|
||||||
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
|
||||||
snmptranslate_file = "/tmp/oids.txt"
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Which table do you want to collect
|
|
||||||
[[inputs.snmp.host.table]]
|
|
||||||
name = "iftable2"
|
|
||||||
|
|
||||||
# table without mapping but with subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable2"
|
|
||||||
sub_tables = [".1.3.6.1.2.1.2.2.1.13"]
|
|
||||||
# note
|
|
||||||
# oid attribute is useless
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Table with mapping example
|
|
||||||
|
|
||||||
In this example, we remove collect attribute to the host section,
|
|
||||||
but you can still use it in combination of the following part.
|
|
||||||
|
|
||||||
Telegraf gathers value of OIDS of the table:
|
|
||||||
|
|
||||||
- named **iftable3**
|
|
||||||
|
|
||||||
With **inputs.snmp.table** section the plugin gets oid number:
|
|
||||||
|
|
||||||
- **iftable3** => `.1.3.6.1.2.1.31.1.1.1`
|
|
||||||
|
|
||||||
Also **iftable2** is a table, the plugin will gathers all
|
|
||||||
OIDS in the table and in the subtables
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.2`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.3`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.4`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.2`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.2....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.3`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.3....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.4`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.4....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.5....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6....`
|
|
||||||
- `...`
|
|
||||||
|
|
||||||
But the **include_instances** attribute will filter which OIDS
|
|
||||||
will be gathered; As you see, there is an other attribute, `mapping_table`.
|
|
||||||
`include_instances` and `mapping_table` permit to build a hash table
|
|
||||||
to filter only OIDS you want.
|
|
||||||
Let's say, we have the following data on SNMP server:
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.1` has as value: `enp5s0`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.2` has as value: `enp5s1`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.3` has as value: `enp5s2`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.4` has as value: `eth0`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.5` has as value: `eth1`
|
|
||||||
|
|
||||||
The plugin will build the following hash table:
|
|
||||||
|
|
||||||
| instance name | instance id |
|
|
||||||
|---------------|-------------|
|
|
||||||
| `enp5s0` | `1` |
|
|
||||||
| `enp5s1` | `2` |
|
|
||||||
| `enp5s2` | `3` |
|
|
||||||
| `eth0` | `4` |
|
|
||||||
| `eth1` | `5` |
|
|
||||||
|
|
||||||
With the **include_instances** attribute, the plugin will gather
|
|
||||||
the following OIDS:
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.1.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.2.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.2.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.3.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.3.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.4.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.4.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.5.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.5.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.5`
|
|
||||||
- `...`
|
|
||||||
|
|
||||||
Note: the plugin will add instance name as tag *instance*
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Simple table with mapping example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
## Use 'oids.txt' file to translate oids to names
|
|
||||||
## To generate 'oids.txt' you need to run:
|
|
||||||
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
||||||
## Or if you have an other MIB folder with custom MIBs
|
|
||||||
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
|
||||||
snmptranslate_file = "/tmp/oids.txt"
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Which table do you want to collect
|
|
||||||
[[inputs.snmp.host.table]]
|
|
||||||
name = "iftable3"
|
|
||||||
include_instances = ["enp5s0", "eth1"]
|
|
||||||
|
|
||||||
# table with mapping but without subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable3"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
# if empty. get all instances
|
|
||||||
mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
||||||
# if empty, get all subtables
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Table with both mapping and subtable example
|
|
||||||
|
|
||||||
In this example, we remove collect attribute to the host section,
|
|
||||||
but you can still use it in combination of the following part.
|
|
||||||
|
|
||||||
Telegraf gathers value of OIDS of the table:
|
|
||||||
|
|
||||||
- named **iftable4**
|
|
||||||
|
|
||||||
With **inputs.snmp.table** section *AND* **sub_tables** attribute,
|
|
||||||
the plugin will get OIDS from subtables:
|
|
||||||
|
|
||||||
- **iftable4** => `.1.3.6.1.2.1.31.1.1.1`
|
|
||||||
|
|
||||||
Also **iftable2** is a table, the plugin will gathers all
|
|
||||||
OIDS in the table and in the subtables
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.1
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.2`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.3`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.4`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6....`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10.2`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10.3`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10.4`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10....`
|
|
||||||
|
|
||||||
But the **include_instances** attribute will filter which OIDS
|
|
||||||
will be gathered; As you see, there is an other attribute, `mapping_table`.
|
|
||||||
`include_instances` and `mapping_table` permit to build a hash table
|
|
||||||
to filter only OIDS you want.
|
|
||||||
Let's say, we have the following data on SNMP server:
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.1` has as value: `enp5s0`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.2` has as value: `enp5s1`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.3` has as value: `enp5s2`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.4` has as value: `eth0`
|
|
||||||
- OID: `.1.3.6.1.2.1.31.1.1.1.1.5` has as value: `eth1`
|
|
||||||
|
|
||||||
The plugin will build the following hash table:
|
|
||||||
|
|
||||||
| instance name | instance id |
|
|
||||||
|---------------|-------------|
|
|
||||||
| `enp5s0` | `1` |
|
|
||||||
| `enp5s1` | `2` |
|
|
||||||
| `enp5s2` | `3` |
|
|
||||||
| `eth0` | `4` |
|
|
||||||
| `eth1` | `5` |
|
|
||||||
|
|
||||||
With the **include_instances** attribute, the plugin will gather
|
|
||||||
the following OIDS:
|
|
||||||
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.6.5`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10.1`
|
|
||||||
- `.1.3.6.1.2.1.31.1.1.1.10.5`
|
|
||||||
|
|
||||||
Note: the plugin will add instance name as tag *instance*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Table with both mapping and subtable example
|
|
||||||
[[inputs.snmp]]
|
|
||||||
## Use 'oids.txt' file to translate oids to names
|
|
||||||
## To generate 'oids.txt' you need to run:
|
|
||||||
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
||||||
## Or if you have an other MIB folder with custom MIBs
|
|
||||||
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
|
||||||
snmptranslate_file = "/tmp/oids.txt"
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "127.0.0.1:161"
|
|
||||||
# SNMP community
|
|
||||||
community = "public" # default public
|
|
||||||
# SNMP version (1, 2 or 3)
|
|
||||||
# Version 3 not supported yet
|
|
||||||
version = 2 # default 2
|
|
||||||
# Which table do you want to collect
|
|
||||||
[[inputs.snmp.host.table]]
|
|
||||||
name = "iftable4"
|
|
||||||
include_instances = ["enp5s0", "eth1"]
|
|
||||||
|
|
||||||
# table with both mapping and subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable4"
|
|
||||||
# if empty get all instances
|
|
||||||
mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
||||||
# if empty get all subtables
|
|
||||||
# sub_tables could be not "real subtables"
|
|
||||||
sub_tables=[".1.3.6.1.2.1.2.2.1.13", "bytes_recv", "bytes_send"]
|
|
||||||
# note
|
|
||||||
# oid attribute is useless
|
|
||||||
|
|
||||||
# SNMP SUBTABLES
|
|
||||||
[[plugins.snmp.subtable]]
|
|
||||||
name = "bytes_recv"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1.6"
|
|
||||||
unit = "octets"
|
|
||||||
|
|
||||||
[[plugins.snmp.subtable]]
|
|
||||||
name = "bytes_send"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1.10"
|
|
||||||
unit = "octets"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Configuration notes
|
|
||||||
|
|
||||||
- In **plugins.snmp.table** section, the `oid` attribute is useless if
|
|
||||||
the `sub_tables` attributes is defined
|
|
||||||
|
|
||||||
- In **plugins.snmp.subtable** section, you can put a name from `snmptranslate_file`
|
|
||||||
as `oid` attribute instead of a valid OID
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
With the last example (Table with both mapping and subtable example):
|
|
||||||
|
|
||||||
- ifHCOutOctets
|
|
||||||
- ifHCOutOctets
|
|
||||||
- ifInDiscards
|
|
||||||
- ifInDiscards
|
|
||||||
- ifHCInOctets
|
|
||||||
- ifHCInOctets
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
With the last example (Table with both mapping and subtable example):
|
|
||||||
|
|
||||||
- ifHCOutOctets
|
|
||||||
- host
|
|
||||||
- instance
|
|
||||||
- unit
|
|
||||||
- ifInDiscards
|
|
||||||
- host
|
|
||||||
- instance
|
|
||||||
- ifHCInOctets
|
|
||||||
- host
|
|
||||||
- instance
|
|
||||||
- unit
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
With the last example (Table with both mapping and subtable example):
|
|
||||||
|
|
||||||
```
|
|
||||||
ifHCOutOctets,host=127.0.0.1,instance=enp5s0,unit=octets ifHCOutOctets=10565628i 1456878706044462901
|
|
||||||
ifInDiscards,host=127.0.0.1,instance=enp5s0 ifInDiscards=0i 1456878706044510264
|
|
||||||
ifHCInOctets,host=127.0.0.1,instance=enp5s0,unit=octets ifHCInOctets=76351777i 1456878706044531312
|
|
||||||
```
|
|
||||||
@@ -20,16 +20,7 @@ type Snmp struct {
|
|||||||
Host []Host
|
Host []Host
|
||||||
Get []Data
|
Get []Data
|
||||||
Bulk []Data
|
Bulk []Data
|
||||||
Table []Table
|
|
||||||
Subtable []Subtable
|
|
||||||
SnmptranslateFile string
|
SnmptranslateFile string
|
||||||
|
|
||||||
nameToOid map[string]string
|
|
||||||
initNode Node
|
|
||||||
subTableMap map[string]Subtable
|
|
||||||
|
|
||||||
// TODO change as unexportable
|
|
||||||
//OidInstanceMapping map[string]map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Host struct {
|
type Host struct {
|
||||||
@@ -45,54 +36,9 @@ type Host struct {
|
|||||||
Collect []string
|
Collect []string
|
||||||
// easy get oids
|
// easy get oids
|
||||||
GetOids []string
|
GetOids []string
|
||||||
// Table
|
|
||||||
Table []HostTable
|
|
||||||
// Oids
|
// Oids
|
||||||
getOids []Data
|
getOids []Data
|
||||||
bulkOids []Data
|
bulkOids []Data
|
||||||
tables []HostTable
|
|
||||||
// array of processed oids
|
|
||||||
// to skip oid duplication
|
|
||||||
processedOids []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Table struct {
|
|
||||||
// name = "iftable"
|
|
||||||
Name string
|
|
||||||
// oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
Oid string
|
|
||||||
//if empty get all instances
|
|
||||||
//mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
||||||
MappingTable string
|
|
||||||
// if empty get all subtables
|
|
||||||
// sub_tables could be not "real subtables"
|
|
||||||
//sub_tables=[".1.3.6.1.2.1.2.2.1.13", "bytes_recv", "bytes_send"]
|
|
||||||
SubTables []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostTable struct {
|
|
||||||
// name = "iftable"
|
|
||||||
Name string
|
|
||||||
// Includes only these instances
|
|
||||||
// include_instances = ["eth0", "eth1"]
|
|
||||||
IncludeInstances []string
|
|
||||||
// Excludes only these instances
|
|
||||||
// exclude_instances = ["eth20", "eth21"]
|
|
||||||
ExcludeInstances []string
|
|
||||||
// From Table struct
|
|
||||||
oid string
|
|
||||||
mappingTable string
|
|
||||||
subTables []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO find better names
|
|
||||||
type Subtable struct {
|
|
||||||
//name = "bytes_send"
|
|
||||||
Name string
|
|
||||||
//oid = ".1.3.6.1.2.1.31.1.1.1.10"
|
|
||||||
Oid string
|
|
||||||
//unit = "octets"
|
|
||||||
Unit string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
@@ -117,8 +63,13 @@ type Node struct {
|
|||||||
subnodes map[string]Node
|
subnodes map[string]Node
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move this var to snmp struct
|
var initNode = Node{
|
||||||
var OidInstanceMapping = make(map[string]map[string]string)
|
id: "1",
|
||||||
|
name: "",
|
||||||
|
subnodes: make(map[string]Node),
|
||||||
|
}
|
||||||
|
|
||||||
|
var NameToOid = make(map[string]string)
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
## Use 'oids.txt' file to translate oids to names
|
## Use 'oids.txt' file to translate oids to names
|
||||||
@@ -162,7 +113,7 @@ var sampleConfig = `
|
|||||||
[[inputs.snmp.get]]
|
[[inputs.snmp.get]]
|
||||||
name = "interface_speed"
|
name = "interface_speed"
|
||||||
oid = "ifSpeed"
|
oid = "ifSpeed"
|
||||||
instance = "0"
|
instance = 0
|
||||||
|
|
||||||
[[inputs.snmp.get]]
|
[[inputs.snmp.get]]
|
||||||
name = "sysuptime"
|
name = "sysuptime"
|
||||||
@@ -178,52 +129,6 @@ var sampleConfig = `
|
|||||||
name = "ifoutoctets"
|
name = "ifoutoctets"
|
||||||
max_repetition = 127
|
max_repetition = 127
|
||||||
oid = "ifOutOctets"
|
oid = "ifOutOctets"
|
||||||
|
|
||||||
|
|
||||||
[[inputs.snmp.host]]
|
|
||||||
address = "192.168.2.13:161"
|
|
||||||
#address = "127.0.0.1:161"
|
|
||||||
community = "public"
|
|
||||||
version = 2
|
|
||||||
timeout = 2.0
|
|
||||||
retries = 2
|
|
||||||
#collect = ["mybulk", "sysservices", "sysdescr", "systype"]
|
|
||||||
collect = ["sysuptime" ]
|
|
||||||
[[inputs.snmp.host.table]]
|
|
||||||
name = "iftable3"
|
|
||||||
include_instances = ["enp5s0", "eth1"]
|
|
||||||
|
|
||||||
# SNMP TABLEs
|
|
||||||
# table without mapping neither subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable1"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
|
|
||||||
# table without mapping but with subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable2"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
sub_tables = [".1.3.6.1.2.1.2.2.1.13"]
|
|
||||||
|
|
||||||
# table with mapping but without subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable3"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
# if empty. get all instances
|
|
||||||
mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
||||||
# if empty, get all subtables
|
|
||||||
|
|
||||||
# table with both mapping and subtables
|
|
||||||
[[inputs.snmp.table]]
|
|
||||||
name = "iftable4"
|
|
||||||
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
||||||
# if empty get all instances
|
|
||||||
mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
||||||
# if empty get all subtables
|
|
||||||
# sub_tables could be not "real subtables"
|
|
||||||
sub_tables=[".1.3.6.1.2.1.2.2.1.13", "bytes_recv", "bytes_send"]
|
|
||||||
|
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
// SampleConfig returns sample configuration message
|
// SampleConfig returns sample configuration message
|
||||||
@@ -284,24 +189,8 @@ func findnodename(node Node, ids []string) (string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
||||||
// TODO put this in cache on first run
|
|
||||||
// Create subtables mapping
|
|
||||||
if len(s.subTableMap) == 0 {
|
|
||||||
s.subTableMap = make(map[string]Subtable)
|
|
||||||
for _, sb := range s.Subtable {
|
|
||||||
s.subTableMap[sb.Name] = sb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO put this in cache on first run
|
|
||||||
// Create oid tree
|
// Create oid tree
|
||||||
if s.SnmptranslateFile != "" && len(s.initNode.subnodes) == 0 {
|
if s.SnmptranslateFile != "" && len(initNode.subnodes) == 0 {
|
||||||
s.nameToOid = make(map[string]string)
|
|
||||||
s.initNode = Node{
|
|
||||||
id: "1",
|
|
||||||
name: "",
|
|
||||||
subnodes: make(map[string]Node),
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(s.SnmptranslateFile)
|
data, err := ioutil.ReadFile(s.SnmptranslateFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Reading SNMPtranslate file error: %s", err)
|
log.Printf("Reading SNMPtranslate file error: %s", err)
|
||||||
@@ -313,8 +202,8 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
|||||||
if oids[2] != "" {
|
if oids[2] != "" {
|
||||||
oid_name := oids[1]
|
oid_name := oids[1]
|
||||||
oid := oids[2]
|
oid := oids[2]
|
||||||
fillnode(s.initNode, oid_name, strings.Split(string(oid), "."))
|
fillnode(initNode, oid_name, strings.Split(string(oid), "."))
|
||||||
s.nameToOid[oid_name] = oid
|
NameToOid[oid_name] = oid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +227,7 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
|||||||
// Get Easy GET oids
|
// Get Easy GET oids
|
||||||
for _, oidstring := range host.GetOids {
|
for _, oidstring := range host.GetOids {
|
||||||
oid := Data{}
|
oid := Data{}
|
||||||
if val, ok := s.nameToOid[oidstring]; ok {
|
if val, ok := NameToOid[oidstring]; ok {
|
||||||
// TODO should we add the 0 instance ?
|
// TODO should we add the 0 instance ?
|
||||||
oid.Name = oidstring
|
oid.Name = oidstring
|
||||||
oid.Oid = val
|
oid.Oid = val
|
||||||
@@ -359,7 +248,7 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
|||||||
// Get GET oids
|
// Get GET oids
|
||||||
for _, oid := range s.Get {
|
for _, oid := range s.Get {
|
||||||
if oid.Name == oid_name {
|
if oid.Name == oid_name {
|
||||||
if val, ok := s.nameToOid[oid.Oid]; ok {
|
if val, ok := NameToOid[oid.Oid]; ok {
|
||||||
// TODO should we add the 0 instance ?
|
// TODO should we add the 0 instance ?
|
||||||
if oid.Instance != "" {
|
if oid.Instance != "" {
|
||||||
oid.rawOid = "." + val + "." + oid.Instance
|
oid.rawOid = "." + val + "." + oid.Instance
|
||||||
@@ -375,7 +264,7 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
|||||||
// Get GETBULK oids
|
// Get GETBULK oids
|
||||||
for _, oid := range s.Bulk {
|
for _, oid := range s.Bulk {
|
||||||
if oid.Name == oid_name {
|
if oid.Name == oid_name {
|
||||||
if val, ok := s.nameToOid[oid.Oid]; ok {
|
if val, ok := NameToOid[oid.Oid]; ok {
|
||||||
oid.rawOid = "." + val
|
oid.rawOid = "." + val
|
||||||
} else {
|
} else {
|
||||||
oid.rawOid = oid.Oid
|
oid.rawOid = oid.Oid
|
||||||
@@ -384,219 +273,18 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Table
|
|
||||||
for _, hostTable := range host.Table {
|
|
||||||
for _, snmpTable := range s.Table {
|
|
||||||
if hostTable.Name == snmpTable.Name {
|
|
||||||
table := hostTable
|
|
||||||
table.oid = snmpTable.Oid
|
|
||||||
table.mappingTable = snmpTable.MappingTable
|
|
||||||
table.subTables = snmpTable.SubTables
|
|
||||||
host.tables = append(host.tables, table)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Launch Mapping
|
|
||||||
// TODO put this in cache on first run
|
|
||||||
// TODO save mapping and computed oids
|
|
||||||
// to do it only the first time
|
|
||||||
// only if len(s.OidInstanceMapping) == 0
|
|
||||||
if len(OidInstanceMapping) >= 0 {
|
|
||||||
if err := host.SNMPMap(acc, s.nameToOid, s.subTableMap); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Launch Get requests
|
// Launch Get requests
|
||||||
if err := host.SNMPGet(acc, s.initNode); err != nil {
|
if err := host.SNMPGet(acc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := host.SNMPBulk(acc, s.initNode); err != nil {
|
if err := host.SNMPBulk(acc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Host) SNMPMap(acc telegraf.Accumulator, nameToOid map[string]string, subTableMap map[string]Subtable) error {
|
func (h *Host) SNMPGet(acc telegraf.Accumulator) error {
|
||||||
// Get snmp client
|
|
||||||
snmpClient, err := h.GetSNMPClient()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Deconnection
|
|
||||||
defer snmpClient.Conn.Close()
|
|
||||||
// Prepare OIDs
|
|
||||||
for _, table := range h.tables {
|
|
||||||
// We don't have mapping
|
|
||||||
if table.mappingTable == "" {
|
|
||||||
if len(table.subTables) == 0 {
|
|
||||||
// If We don't have mapping table
|
|
||||||
// neither subtables list
|
|
||||||
// This is just a bulk request
|
|
||||||
oid := Data{}
|
|
||||||
oid.Oid = table.oid
|
|
||||||
if val, ok := nameToOid[oid.Oid]; ok {
|
|
||||||
oid.rawOid = "." + val
|
|
||||||
} else {
|
|
||||||
oid.rawOid = oid.Oid
|
|
||||||
}
|
|
||||||
h.bulkOids = append(h.bulkOids, oid)
|
|
||||||
} else {
|
|
||||||
// If We don't have mapping table
|
|
||||||
// but we have subtables
|
|
||||||
// This is a bunch of bulk requests
|
|
||||||
// For each subtable ...
|
|
||||||
for _, sb := range table.subTables {
|
|
||||||
// ... we create a new Data (oid) object
|
|
||||||
oid := Data{}
|
|
||||||
// Looking for more information about this subtable
|
|
||||||
ssb, exists := subTableMap[sb]
|
|
||||||
if exists {
|
|
||||||
// We found a subtable section in config files
|
|
||||||
oid.Oid = ssb.Oid
|
|
||||||
oid.rawOid = ssb.Oid
|
|
||||||
oid.Unit = ssb.Unit
|
|
||||||
} else {
|
|
||||||
// We did NOT find a subtable section in config files
|
|
||||||
oid.Oid = sb
|
|
||||||
oid.rawOid = sb
|
|
||||||
}
|
|
||||||
// TODO check oid validity
|
|
||||||
|
|
||||||
// Add the new oid to getOids list
|
|
||||||
h.bulkOids = append(h.bulkOids, oid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We have a mapping table
|
|
||||||
// We need to query this table
|
|
||||||
// To get mapping between instance id
|
|
||||||
// and instance name
|
|
||||||
oid_asked := table.mappingTable
|
|
||||||
oid_next := oid_asked
|
|
||||||
need_more_requests := true
|
|
||||||
// Set max repetition
|
|
||||||
maxRepetition := uint8(32)
|
|
||||||
// Launch requests
|
|
||||||
for need_more_requests {
|
|
||||||
// Launch request
|
|
||||||
result, err3 := snmpClient.GetBulk([]string{oid_next}, 0, maxRepetition)
|
|
||||||
if err3 != nil {
|
|
||||||
return err3
|
|
||||||
}
|
|
||||||
|
|
||||||
lastOid := ""
|
|
||||||
for _, variable := range result.Variables {
|
|
||||||
lastOid = variable.Name
|
|
||||||
if strings.HasPrefix(variable.Name, oid_asked) {
|
|
||||||
switch variable.Type {
|
|
||||||
// handle instance names
|
|
||||||
case gosnmp.OctetString:
|
|
||||||
// Check if instance is in includes instances
|
|
||||||
getInstances := true
|
|
||||||
if len(table.IncludeInstances) > 0 {
|
|
||||||
getInstances = false
|
|
||||||
for _, instance := range table.IncludeInstances {
|
|
||||||
if instance == string(variable.Value.([]byte)) {
|
|
||||||
getInstances = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if instance is in excludes instances
|
|
||||||
if len(table.ExcludeInstances) > 0 {
|
|
||||||
getInstances = true
|
|
||||||
for _, instance := range table.ExcludeInstances {
|
|
||||||
if instance == string(variable.Value.([]byte)) {
|
|
||||||
getInstances = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We don't want this instance
|
|
||||||
if !getInstances {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove oid table from the complete oid
|
|
||||||
// in order to get the current instance id
|
|
||||||
key := strings.Replace(variable.Name, oid_asked, "", 1)
|
|
||||||
|
|
||||||
if len(table.subTables) == 0 {
|
|
||||||
// We have a mapping table
|
|
||||||
// but no subtables
|
|
||||||
// This is just a bulk request
|
|
||||||
|
|
||||||
// Building mapping table
|
|
||||||
mapping := map[string]string{strings.Trim(key, "."): string(variable.Value.([]byte))}
|
|
||||||
_, exists := OidInstanceMapping[table.oid]
|
|
||||||
if exists {
|
|
||||||
OidInstanceMapping[table.oid][strings.Trim(key, ".")] = string(variable.Value.([]byte))
|
|
||||||
} else {
|
|
||||||
OidInstanceMapping[table.oid] = mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add table oid in bulk oid list
|
|
||||||
oid := Data{}
|
|
||||||
oid.Oid = table.oid
|
|
||||||
if val, ok := nameToOid[oid.Oid]; ok {
|
|
||||||
oid.rawOid = "." + val
|
|
||||||
} else {
|
|
||||||
oid.rawOid = oid.Oid
|
|
||||||
}
|
|
||||||
h.bulkOids = append(h.bulkOids, oid)
|
|
||||||
} else {
|
|
||||||
// We have a mapping table
|
|
||||||
// and some subtables
|
|
||||||
// This is a bunch of get requests
|
|
||||||
// This is the best case :)
|
|
||||||
|
|
||||||
// For each subtable ...
|
|
||||||
for _, sb := range table.subTables {
|
|
||||||
// ... we create a new Data (oid) object
|
|
||||||
oid := Data{}
|
|
||||||
// Looking for more information about this subtable
|
|
||||||
ssb, exists := subTableMap[sb]
|
|
||||||
if exists {
|
|
||||||
// We found a subtable section in config files
|
|
||||||
oid.Oid = ssb.Oid + key
|
|
||||||
oid.rawOid = ssb.Oid + key
|
|
||||||
oid.Unit = ssb.Unit
|
|
||||||
oid.Instance = string(variable.Value.([]byte))
|
|
||||||
} else {
|
|
||||||
// We did NOT find a subtable section in config files
|
|
||||||
oid.Oid = sb + key
|
|
||||||
oid.rawOid = sb + key
|
|
||||||
oid.Instance = string(variable.Value.([]byte))
|
|
||||||
}
|
|
||||||
// TODO check oid validity
|
|
||||||
|
|
||||||
// Add the new oid to getOids list
|
|
||||||
h.getOids = append(h.getOids, oid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Determine if we need more requests
|
|
||||||
if strings.HasPrefix(lastOid, oid_asked) {
|
|
||||||
need_more_requests = true
|
|
||||||
oid_next = lastOid
|
|
||||||
} else {
|
|
||||||
need_more_requests = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Mapping finished
|
|
||||||
|
|
||||||
// Create newoids based on mapping
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Host) SNMPGet(acc telegraf.Accumulator, initNode Node) error {
|
|
||||||
// Get snmp client
|
// Get snmp client
|
||||||
snmpClient, err := h.GetSNMPClient()
|
snmpClient, err := h.GetSNMPClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -629,7 +317,7 @@ func (h *Host) SNMPGet(acc telegraf.Accumulator, initNode Node) error {
|
|||||||
return err3
|
return err3
|
||||||
}
|
}
|
||||||
// Handle response
|
// Handle response
|
||||||
_, err = h.HandleResponse(oidsList, result, acc, initNode)
|
_, err = h.HandleResponse(oidsList, result, acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -637,7 +325,7 @@ func (h *Host) SNMPGet(acc telegraf.Accumulator, initNode Node) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Host) SNMPBulk(acc telegraf.Accumulator, initNode Node) error {
|
func (h *Host) SNMPBulk(acc telegraf.Accumulator) error {
|
||||||
// Get snmp client
|
// Get snmp client
|
||||||
snmpClient, err := h.GetSNMPClient()
|
snmpClient, err := h.GetSNMPClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -672,7 +360,7 @@ func (h *Host) SNMPBulk(acc telegraf.Accumulator, initNode Node) error {
|
|||||||
return err3
|
return err3
|
||||||
}
|
}
|
||||||
// Handle response
|
// Handle response
|
||||||
last_oid, err := h.HandleResponse(oidsList, result, acc, initNode)
|
last_oid, err := h.HandleResponse(oidsList, result, acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -724,19 +412,12 @@ func (h *Host) GetSNMPClient() (*gosnmp.GoSNMP, error) {
|
|||||||
return snmpClient, nil
|
return snmpClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, acc telegraf.Accumulator, initNode Node) (string, error) {
|
func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, acc telegraf.Accumulator) (string, error) {
|
||||||
var lastOid string
|
var lastOid string
|
||||||
for _, variable := range result.Variables {
|
for _, variable := range result.Variables {
|
||||||
lastOid = variable.Name
|
lastOid = variable.Name
|
||||||
nextresult:
|
// Remove unwanted oid
|
||||||
// Get only oid wanted
|
|
||||||
for oid_key, oid := range oids {
|
for oid_key, oid := range oids {
|
||||||
// Skip oids already processed
|
|
||||||
for _, processedOid := range h.processedOids {
|
|
||||||
if variable.Name == processedOid {
|
|
||||||
break nextresult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(variable.Name, oid_key) {
|
if strings.HasPrefix(variable.Name, oid_key) {
|
||||||
switch variable.Type {
|
switch variable.Type {
|
||||||
// handle Metrics
|
// handle Metrics
|
||||||
@@ -750,27 +431,11 @@ func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, a
|
|||||||
// Get name and instance
|
// Get name and instance
|
||||||
var oid_name string
|
var oid_name string
|
||||||
var instance string
|
var instance string
|
||||||
// Get oidname and instance from translate file
|
// Get oidname and instannce from translate file
|
||||||
oid_name, instance = findnodename(initNode,
|
oid_name, instance = findnodename(initNode,
|
||||||
strings.Split(string(variable.Name[1:]), "."))
|
strings.Split(string(variable.Name[1:]), "."))
|
||||||
// Set instance tag
|
|
||||||
// From mapping table
|
if instance != "" {
|
||||||
mapping, inMappingNoSubTable := OidInstanceMapping[oid_key]
|
|
||||||
if inMappingNoSubTable {
|
|
||||||
// filter if the instance in not in
|
|
||||||
// OidInstanceMapping mapping map
|
|
||||||
if instance_name, exists := mapping[instance]; exists {
|
|
||||||
tags["instance"] = instance_name
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if oid.Instance != "" {
|
|
||||||
// From config files
|
|
||||||
tags["instance"] = oid.Instance
|
|
||||||
} else if instance != "" {
|
|
||||||
// Using last id of the current oid, ie:
|
|
||||||
// with .1.3.6.1.2.1.31.1.1.1.10.3
|
|
||||||
// instance is 3
|
|
||||||
tags["instance"] = instance
|
tags["instance"] = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,7 +453,6 @@ func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, a
|
|||||||
fields := make(map[string]interface{})
|
fields := make(map[string]interface{})
|
||||||
fields[string(field_name)] = variable.Value
|
fields[string(field_name)] = variable.Value
|
||||||
|
|
||||||
h.processedOids = append(h.processedOids, variable.Name)
|
|
||||||
acc.AddFields(field_name, fields, tags)
|
acc.AddFields(field_name, fields, tags)
|
||||||
case gosnmp.NoSuchObject, gosnmp.NoSuchInstance:
|
case gosnmp.NoSuchObject, gosnmp.NoSuchInstance:
|
||||||
// Oid not found
|
// Oid not found
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ import (
|
|||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const UDP_PACKET_SIZE int = 1500
|
||||||
UDP_PACKET_SIZE int = 1500
|
|
||||||
|
|
||||||
defaultFieldName = "value"
|
|
||||||
)
|
|
||||||
|
|
||||||
var dropwarn = "ERROR: Message queue full. Discarding line [%s] " +
|
var dropwarn = "ERROR: Message queue full. Discarding line [%s] " +
|
||||||
"You may want to increase allowed_pending_messages in the config\n"
|
"You may want to increase allowed_pending_messages in the config\n"
|
||||||
@@ -117,9 +113,9 @@ type cachedcounter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type cachedtimings struct {
|
type cachedtimings struct {
|
||||||
name string
|
name string
|
||||||
fields map[string]RunningStats
|
stats RunningStats
|
||||||
tags map[string]string
|
tags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ *Statsd) Description() string {
|
func (_ *Statsd) Description() string {
|
||||||
@@ -173,26 +169,16 @@ func (s *Statsd) Gather(acc telegraf.Accumulator) error {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
for _, metric := range s.timings {
|
for _, metric := range s.timings {
|
||||||
// Defining a template to parse field names for timers allows us to split
|
|
||||||
// out multiple fields per timer. In this case we prefix each stat with the
|
|
||||||
// field name and store these all in a single measurement.
|
|
||||||
fields := make(map[string]interface{})
|
fields := make(map[string]interface{})
|
||||||
for fieldName, stats := range metric.fields {
|
fields["mean"] = metric.stats.Mean()
|
||||||
var prefix string
|
fields["stddev"] = metric.stats.Stddev()
|
||||||
if fieldName != defaultFieldName {
|
fields["upper"] = metric.stats.Upper()
|
||||||
prefix = fieldName + "_"
|
fields["lower"] = metric.stats.Lower()
|
||||||
}
|
fields["count"] = metric.stats.Count()
|
||||||
fields[prefix+"mean"] = stats.Mean()
|
for _, percentile := range s.Percentiles {
|
||||||
fields[prefix+"stddev"] = stats.Stddev()
|
name := fmt.Sprintf("%v_percentile", percentile)
|
||||||
fields[prefix+"upper"] = stats.Upper()
|
fields[name] = metric.stats.Percentile(percentile)
|
||||||
fields[prefix+"lower"] = stats.Lower()
|
|
||||||
fields[prefix+"count"] = stats.Count()
|
|
||||||
for _, percentile := range s.Percentiles {
|
|
||||||
name := fmt.Sprintf("%s%v_percentile", prefix, percentile)
|
|
||||||
fields[name] = stats.Percentile(percentile)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acc.AddFields(metric.name, fields, metric.tags, now)
|
acc.AddFields(metric.name, fields, metric.tags, now)
|
||||||
}
|
}
|
||||||
if s.DeleteTimings {
|
if s.DeleteTimings {
|
||||||
@@ -384,6 +370,11 @@ func (s *Statsd) parseStatsdLine(line string) error {
|
|||||||
|
|
||||||
// Parse the name & tags from bucket
|
// Parse the name & tags from bucket
|
||||||
m.name, m.field, m.tags = s.parseName(m.bucket)
|
m.name, m.field, m.tags = s.parseName(m.bucket)
|
||||||
|
// fields are not supported for timings, so if specified combine into
|
||||||
|
// the name
|
||||||
|
if (m.mtype == "ms" || m.mtype == "h") && m.field != "value" {
|
||||||
|
m.name += "_" + m.field
|
||||||
|
}
|
||||||
switch m.mtype {
|
switch m.mtype {
|
||||||
case "c":
|
case "c":
|
||||||
m.tags["metric_type"] = "counter"
|
m.tags["metric_type"] = "counter"
|
||||||
@@ -442,7 +433,7 @@ func (s *Statsd) parseName(bucket string) (string, string, map[string]string) {
|
|||||||
name = strings.Replace(name, "-", "__", -1)
|
name = strings.Replace(name, "-", "__", -1)
|
||||||
}
|
}
|
||||||
if field == "" {
|
if field == "" {
|
||||||
field = defaultFieldName
|
field = "value"
|
||||||
}
|
}
|
||||||
|
|
||||||
return name, field, tags
|
return name, field, tags
|
||||||
@@ -470,32 +461,26 @@ func parseKeyValue(keyvalue string) (string, string) {
|
|||||||
func (s *Statsd) aggregate(m metric) {
|
func (s *Statsd) aggregate(m metric) {
|
||||||
switch m.mtype {
|
switch m.mtype {
|
||||||
case "ms", "h":
|
case "ms", "h":
|
||||||
// Check if the measurement exists
|
|
||||||
cached, ok := s.timings[m.hash]
|
cached, ok := s.timings[m.hash]
|
||||||
if !ok {
|
if !ok {
|
||||||
cached = cachedtimings{
|
cached = cachedtimings{
|
||||||
name: m.name,
|
name: m.name,
|
||||||
fields: make(map[string]RunningStats),
|
tags: m.tags,
|
||||||
tags: m.tags,
|
stats: RunningStats{
|
||||||
}
|
PercLimit: s.PercentileLimit,
|
||||||
}
|
},
|
||||||
// Check if the field exists. If we've not enabled multiple fields per timer
|
|
||||||
// this will be the default field name, eg. "value"
|
|
||||||
field, ok := cached.fields[m.field]
|
|
||||||
if !ok {
|
|
||||||
field = RunningStats{
|
|
||||||
PercLimit: s.PercentileLimit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.samplerate > 0 {
|
if m.samplerate > 0 {
|
||||||
for i := 0; i < int(1.0/m.samplerate); i++ {
|
for i := 0; i < int(1.0/m.samplerate); i++ {
|
||||||
field.AddValue(m.floatvalue)
|
cached.stats.AddValue(m.floatvalue)
|
||||||
}
|
}
|
||||||
|
s.timings[m.hash] = cached
|
||||||
} else {
|
} else {
|
||||||
field.AddValue(m.floatvalue)
|
cached.stats.AddValue(m.floatvalue)
|
||||||
|
s.timings[m.hash] = cached
|
||||||
}
|
}
|
||||||
cached.fields[m.field] = field
|
|
||||||
s.timings[m.hash] = cached
|
|
||||||
case "c":
|
case "c":
|
||||||
// check if the measurement exists
|
// check if the measurement exists
|
||||||
_, ok := s.counters[m.hash]
|
_, ok := s.counters[m.hash]
|
||||||
|
|||||||
@@ -561,12 +561,12 @@ func TestParse_MeasurementsWithMultipleValues(t *testing.T) {
|
|||||||
// A 0 with invalid samplerate will add a single 0,
|
// A 0 with invalid samplerate will add a single 0,
|
||||||
// plus the last bit of value 1
|
// plus the last bit of value 1
|
||||||
// which adds up to 12 individual datapoints to be cached
|
// which adds up to 12 individual datapoints to be cached
|
||||||
if cachedtiming.fields[defaultFieldName].n != 12 {
|
if cachedtiming.stats.n != 12 {
|
||||||
t.Errorf("Expected 11 additions, got %d", cachedtiming.fields[defaultFieldName].n)
|
t.Errorf("Expected 11 additions, got %d", cachedtiming.stats.n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cachedtiming.fields[defaultFieldName].upper != 1 {
|
if cachedtiming.stats.upper != 1 {
|
||||||
t.Errorf("Expected max input to be 1, got %f", cachedtiming.fields[defaultFieldName].upper)
|
t.Errorf("Expected max input to be 1, got %f", cachedtiming.stats.upper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,105 +842,7 @@ func TestParse_Timings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "test_timing", valid)
|
acc.AssertContainsFields(t, "test_timing", valid)
|
||||||
}
|
|
||||||
|
|
||||||
// Tests low-level functionality of timings when multiple fields is enabled
|
|
||||||
// and a measurement template has been defined which can parse field names
|
|
||||||
func TestParse_Timings_MultipleFieldsWithTemplate(t *testing.T) {
|
|
||||||
s := NewStatsd()
|
|
||||||
s.Templates = []string{"measurement.field"}
|
|
||||||
s.Percentiles = []int{90}
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
|
|
||||||
validLines := []string{
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.success:11|ms",
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
"test_timing.error:22|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range validLines {
|
|
||||||
err := s.parseStatsdLine(line)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Parsing line %s should not have resulted in an error\n", line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Gather(acc)
|
|
||||||
|
|
||||||
valid := map[string]interface{}{
|
|
||||||
"success_90_percentile": float64(11),
|
|
||||||
"success_count": int64(5),
|
|
||||||
"success_lower": float64(1),
|
|
||||||
"success_mean": float64(3),
|
|
||||||
"success_stddev": float64(4),
|
|
||||||
"success_upper": float64(11),
|
|
||||||
|
|
||||||
"error_90_percentile": float64(22),
|
|
||||||
"error_count": int64(5),
|
|
||||||
"error_lower": float64(2),
|
|
||||||
"error_mean": float64(6),
|
|
||||||
"error_stddev": float64(8),
|
|
||||||
"error_upper": float64(22),
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "test_timing", valid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests low-level functionality of timings when multiple fields is enabled
|
|
||||||
// but a measurement template hasn't been defined so we can't parse field names
|
|
||||||
// In this case the behaviour should be the same as normal behaviour
|
|
||||||
func TestParse_Timings_MultipleFieldsWithoutTemplate(t *testing.T) {
|
|
||||||
s := NewStatsd()
|
|
||||||
s.Templates = []string{}
|
|
||||||
s.Percentiles = []int{90}
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
|
|
||||||
validLines := []string{
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.success:11|ms",
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.success:1|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
"test_timing.error:22|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
"test_timing.error:2|ms",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range validLines {
|
|
||||||
err := s.parseStatsdLine(line)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Parsing line %s should not have resulted in an error\n", line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Gather(acc)
|
|
||||||
|
|
||||||
expectedSuccess := map[string]interface{}{
|
|
||||||
"90_percentile": float64(11),
|
|
||||||
"count": int64(5),
|
|
||||||
"lower": float64(1),
|
|
||||||
"mean": float64(3),
|
|
||||||
"stddev": float64(4),
|
|
||||||
"upper": float64(11),
|
|
||||||
}
|
|
||||||
expectedError := map[string]interface{}{
|
|
||||||
"90_percentile": float64(22),
|
|
||||||
"count": int64(5),
|
|
||||||
"lower": float64(2),
|
|
||||||
"mean": float64(6),
|
|
||||||
"stddev": float64(8),
|
|
||||||
"upper": float64(22),
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "test_timing_success", expectedSuccess)
|
|
||||||
acc.AssertContainsFields(t, "test_timing_error", expectedError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParse_Timings_Delete(t *testing.T) {
|
func TestParse_Timings_Delete(t *testing.T) {
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
# Kernel Input Plugin
|
|
||||||
|
|
||||||
This plugin is only available on Linux.
|
|
||||||
|
|
||||||
The kernel plugin gathers info about the kernel that doesn't fit into other
|
|
||||||
plugins. In general, it is the statistics available in `/proc/stat` that are
|
|
||||||
not covered by other plugins.
|
|
||||||
|
|
||||||
The metrics are documented in `man proc` under the `/proc/stat` section.
|
|
||||||
|
|
||||||
```
|
|
||||||
/proc/stat
|
|
||||||
kernel/system statistics. Varies with architecture. Common entries include:
|
|
||||||
|
|
||||||
page 5741 1808
|
|
||||||
The number of pages the system paged in and the number that were paged out (from disk).
|
|
||||||
|
|
||||||
swap 1 0
|
|
||||||
The number of swap pages that have been brought in and out.
|
|
||||||
|
|
||||||
intr 1462898
|
|
||||||
This line shows counts of interrupts serviced since boot time, for each of
|
|
||||||
the possible system interrupts. The first column is the total of all
|
|
||||||
interrupts serviced; each subsequent column is the total for a particular interrupt.
|
|
||||||
|
|
||||||
ctxt 115315
|
|
||||||
The number of context switches that the system underwent.
|
|
||||||
|
|
||||||
btime 769041601
|
|
||||||
boot time, in seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
|
|
||||||
|
|
||||||
processes 86031
|
|
||||||
Number of forks since boot.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Get kernel statistics from /proc/stat
|
|
||||||
[[inputs.kernel]]
|
|
||||||
# no configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- kernel
|
|
||||||
- boot_time (integer, seconds since epoch, `btime`)
|
|
||||||
- context_switches (integer, `ctxt`)
|
|
||||||
- disk_pages_in (integer, `page (0)`)
|
|
||||||
- disk_pages_out (integer, `page (1)`)
|
|
||||||
- interrupts (integer, `intr`)
|
|
||||||
- processes_forked (integer, `processes`)
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ telegraf -config ~/ws/telegraf.conf -input-filter kernel -test
|
|
||||||
* Plugin: kernel, Collection 1
|
|
||||||
> kernel boot_time=1457505775i,context_switches=2626618i,disk_pages_in=5741i,disk_pages_out=1808i,interrupts=1472736i,processes_forked=10673i 1457613402960879816
|
|
||||||
```
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# Processes Input Plugin
|
|
||||||
|
|
||||||
This plugin gathers info about the total number of processes and groups
|
|
||||||
them by status (zombie, sleeping, running, etc.)
|
|
||||||
|
|
||||||
On linux this plugin requires access to procfs (/proc), on other OSes
|
|
||||||
it requires access to execute `ps`.
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Get the number of processes and group them by status
|
|
||||||
[[inputs.processes]]
|
|
||||||
# no configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- processes
|
|
||||||
- blocked (aka disk sleep or uninterruptible sleep)
|
|
||||||
- running
|
|
||||||
- sleeping
|
|
||||||
- stopped
|
|
||||||
- total
|
|
||||||
- zombie
|
|
||||||
- wait (freebsd only)
|
|
||||||
- idle (bsd only)
|
|
||||||
- paging (linux only)
|
|
||||||
- total_threads (linux only)
|
|
||||||
|
|
||||||
### Process State Mappings
|
|
||||||
|
|
||||||
Different OSes use slightly different State codes for their processes, these
|
|
||||||
state codes are documented in `man ps`, and I will give a mapping of what major
|
|
||||||
OS state codes correspond to in telegraf metrics:
|
|
||||||
|
|
||||||
```
|
|
||||||
Linux FreeBSD Darwin meaning
|
|
||||||
R R R running
|
|
||||||
S S S sleeping
|
|
||||||
Z Z Z zombie
|
|
||||||
T T T stopped
|
|
||||||
none I I idle (sleeping for longer than about 20 seconds)
|
|
||||||
D D,L U blocked (waiting in uninterruptible sleep, or locked)
|
|
||||||
W W none paging (linux kernel < 2.6 only), wait (freebsd)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ telegraf -config ~/ws/telegraf.conf -input-filter processes -test
|
|
||||||
* Plugin: processes, Collection 1
|
|
||||||
> processes blocked=8i,running=1i,sleeping=265i,stopped=0i,total=274i,zombie=0i,paging=0i,total_threads=687i 1457478636980905042
|
|
||||||
```
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# System Input Plugin
|
|
||||||
|
|
||||||
The system plugin gathers general stats on system load, uptime,
|
|
||||||
and number of users logged in. It is basically equivalent
|
|
||||||
to the unix `uptime` command.
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Read metrics about system load & uptime
|
|
||||||
[[inputs.system]]
|
|
||||||
# no configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
### Measurements & Fields:
|
|
||||||
|
|
||||||
- system
|
|
||||||
- load1 (float)
|
|
||||||
- load15 (float)
|
|
||||||
- load5 (float)
|
|
||||||
- n_users (integer)
|
|
||||||
- uptime (integer, seconds)
|
|
||||||
- uptime_format (string)
|
|
||||||
|
|
||||||
### Tags:
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
### Example Output:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ telegraf -config ~/ws/telegraf.conf -input-filter system -test
|
|
||||||
* Plugin: system, Collection 1
|
|
||||||
> system load1=2.05,load15=2.38,load5=2.03,n_users=4i,uptime=239043i,uptime_format="2 days, 18:24" 1457546165399253452
|
|
||||||
```
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package system
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// /proc/stat file line prefixes to gather stats on:
|
|
||||||
var (
|
|
||||||
interrupts = []byte("intr")
|
|
||||||
context_switches = []byte("ctxt")
|
|
||||||
processes_forked = []byte("processes")
|
|
||||||
disk_pages = []byte("page")
|
|
||||||
boot_time = []byte("btime")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Kernel struct {
|
|
||||||
statFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Kernel) Description() string {
|
|
||||||
return "Get kernel statistics from /proc/stat"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Kernel) SampleConfig() string { return "" }
|
|
||||||
|
|
||||||
func (k *Kernel) Gather(acc telegraf.Accumulator) error {
|
|
||||||
data, err := k.getProcStat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := make(map[string]interface{})
|
|
||||||
|
|
||||||
dataFields := bytes.Fields(data)
|
|
||||||
for i, field := range dataFields {
|
|
||||||
switch {
|
|
||||||
case bytes.Equal(field, interrupts):
|
|
||||||
m, err := strconv.Atoi(string(dataFields[i+1]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fields["interrupts"] = int64(m)
|
|
||||||
case bytes.Equal(field, context_switches):
|
|
||||||
m, err := strconv.Atoi(string(dataFields[i+1]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fields["context_switches"] = int64(m)
|
|
||||||
case bytes.Equal(field, processes_forked):
|
|
||||||
m, err := strconv.Atoi(string(dataFields[i+1]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fields["processes_forked"] = int64(m)
|
|
||||||
case bytes.Equal(field, boot_time):
|
|
||||||
m, err := strconv.Atoi(string(dataFields[i+1]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fields["boot_time"] = int64(m)
|
|
||||||
case bytes.Equal(field, disk_pages):
|
|
||||||
in, err := strconv.Atoi(string(dataFields[i+1]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out, err := strconv.Atoi(string(dataFields[i+2]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fields["disk_pages_in"] = int64(in)
|
|
||||||
fields["disk_pages_out"] = int64(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AddFields("kernel", fields, map[string]string{})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Kernel) getProcStat() ([]byte, error) {
|
|
||||||
if _, err := os.Stat(k.statFile); os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("kernel: %s does not exist!", k.statFile)
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(k.statFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("kernel", func() telegraf.Input {
|
|
||||||
return &Kernel{
|
|
||||||
statFile: "/proc/stat",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package system
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFullProcFile(t *testing.T) {
|
|
||||||
tmpfile := makeFakeStatFile([]byte(statFile_Full))
|
|
||||||
defer os.Remove(tmpfile)
|
|
||||||
|
|
||||||
k := Kernel{
|
|
||||||
statFile: tmpfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
err := k.Gather(&acc)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"boot_time": int64(1457505775),
|
|
||||||
"context_switches": int64(2626618),
|
|
||||||
"disk_pages_in": int64(5741),
|
|
||||||
"disk_pages_out": int64(1808),
|
|
||||||
"interrupts": int64(1472736),
|
|
||||||
"processes_forked": int64(10673),
|
|
||||||
}
|
|
||||||
acc.AssertContainsFields(t, "kernel", fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPartialProcFile(t *testing.T) {
|
|
||||||
tmpfile := makeFakeStatFile([]byte(statFile_Partial))
|
|
||||||
defer os.Remove(tmpfile)
|
|
||||||
|
|
||||||
k := Kernel{
|
|
||||||
statFile: tmpfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
err := k.Gather(&acc)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"boot_time": int64(1457505775),
|
|
||||||
"context_switches": int64(2626618),
|
|
||||||
"disk_pages_in": int64(5741),
|
|
||||||
"disk_pages_out": int64(1808),
|
|
||||||
"interrupts": int64(1472736),
|
|
||||||
}
|
|
||||||
acc.AssertContainsFields(t, "kernel", fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidProcFile1(t *testing.T) {
|
|
||||||
tmpfile := makeFakeStatFile([]byte(statFile_Invalid))
|
|
||||||
defer os.Remove(tmpfile)
|
|
||||||
|
|
||||||
k := Kernel{
|
|
||||||
statFile: tmpfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
err := k.Gather(&acc)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidProcFile2(t *testing.T) {
|
|
||||||
tmpfile := makeFakeStatFile([]byte(statFile_Invalid2))
|
|
||||||
defer os.Remove(tmpfile)
|
|
||||||
|
|
||||||
k := Kernel{
|
|
||||||
statFile: tmpfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
err := k.Gather(&acc)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoProcFile(t *testing.T) {
|
|
||||||
tmpfile := makeFakeStatFile([]byte(statFile_Invalid2))
|
|
||||||
os.Remove(tmpfile)
|
|
||||||
|
|
||||||
k := Kernel{
|
|
||||||
statFile: tmpfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
err := k.Gather(&acc)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
const statFile_Full = `cpu 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
cpu0 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
||||||
ctxt 2626618
|
|
||||||
btime 1457505775
|
|
||||||
processes 10673
|
|
||||||
procs_running 2
|
|
||||||
procs_blocked 0
|
|
||||||
softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545
|
|
||||||
page 5741 1808
|
|
||||||
swap 1 0
|
|
||||||
`
|
|
||||||
|
|
||||||
const statFile_Partial = `cpu 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
cpu0 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
||||||
ctxt 2626618
|
|
||||||
btime 1457505775
|
|
||||||
procs_running 2
|
|
||||||
procs_blocked 0
|
|
||||||
softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545
|
|
||||||
page 5741 1808
|
|
||||||
`
|
|
||||||
|
|
||||||
// missing btime measurement
|
|
||||||
const statFile_Invalid = `cpu 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
cpu0 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
||||||
ctxt 2626618
|
|
||||||
btime
|
|
||||||
processes 10673
|
|
||||||
procs_running 2
|
|
||||||
procs_blocked 0
|
|
||||||
softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545
|
|
||||||
page 5741 1808
|
|
||||||
swap 1 0
|
|
||||||
`
|
|
||||||
|
|
||||||
// missing second page measurement
|
|
||||||
const statFile_Invalid2 = `cpu 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
cpu0 6796 252 5655 10444977 175 0 101 0 0 0
|
|
||||||
intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
||||||
ctxt 2626618
|
|
||||||
processes 10673
|
|
||||||
procs_running 2
|
|
||||||
page 5741
|
|
||||||
procs_blocked 0
|
|
||||||
softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545
|
|
||||||
`
|
|
||||||
|
|
||||||
func makeFakeStatFile(content []byte) string {
|
|
||||||
tmpfile, err := ioutil.TempFile("", "kerneltest")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := tmpfile.Write(content); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := tmpfile.Close(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmpfile.Name()
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package system
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Processes struct {
|
|
||||||
execPS func() ([]byte, error)
|
|
||||||
readProcFile func(statFile string) ([]byte, error)
|
|
||||||
|
|
||||||
forcePS bool
|
|
||||||
forceProc bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Processes) Description() string {
|
|
||||||
return "Get the number of processes and group them by status"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Processes) SampleConfig() string { return "" }
|
|
||||||
|
|
||||||
func (p *Processes) Gather(acc telegraf.Accumulator) error {
|
|
||||||
// Get an empty map of metric fields
|
|
||||||
fields := getEmptyFields()
|
|
||||||
|
|
||||||
// Decide if we will use 'ps' to get stats (use procfs otherwise)
|
|
||||||
usePS := true
|
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
usePS = false
|
|
||||||
}
|
|
||||||
if p.forcePS {
|
|
||||||
usePS = true
|
|
||||||
} else if p.forceProc {
|
|
||||||
usePS = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gather stats from 'ps' or procfs
|
|
||||||
if usePS {
|
|
||||||
if err := p.gatherFromPS(fields); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := p.gatherFromProc(fields); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AddFields("processes", fields, nil)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets empty fields of metrics based on the OS
|
|
||||||
func getEmptyFields() map[string]interface{} {
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"blocked": int64(0),
|
|
||||||
"zombies": int64(0),
|
|
||||||
"stopped": int64(0),
|
|
||||||
"running": int64(0),
|
|
||||||
"sleeping": int64(0),
|
|
||||||
"total": int64(0),
|
|
||||||
}
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "freebsd":
|
|
||||||
fields["idle"] = int64(0)
|
|
||||||
fields["wait"] = int64(0)
|
|
||||||
case "darwin":
|
|
||||||
fields["idle"] = int64(0)
|
|
||||||
case "openbsd":
|
|
||||||
fields["idle"] = int64(0)
|
|
||||||
case "linux":
|
|
||||||
fields["paging"] = int64(0)
|
|
||||||
fields["total_threads"] = int64(0)
|
|
||||||
}
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// exec `ps` to get all process states
|
|
||||||
func (p *Processes) gatherFromPS(fields map[string]interface{}) error {
|
|
||||||
out, err := p.execPS()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, status := range bytes.Fields(out) {
|
|
||||||
if i == 0 && string(status) == "STAT" {
|
|
||||||
// This is a header, skip it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch status[0] {
|
|
||||||
case 'W':
|
|
||||||
fields["wait"] = fields["wait"].(int64) + int64(1)
|
|
||||||
case 'U', 'D', 'L':
|
|
||||||
// Also known as uninterruptible sleep or disk sleep
|
|
||||||
fields["blocked"] = fields["blocked"].(int64) + int64(1)
|
|
||||||
case 'Z':
|
|
||||||
fields["zombies"] = fields["zombies"].(int64) + int64(1)
|
|
||||||
case 'T':
|
|
||||||
fields["stopped"] = fields["stopped"].(int64) + int64(1)
|
|
||||||
case 'R':
|
|
||||||
fields["running"] = fields["running"].(int64) + int64(1)
|
|
||||||
case 'S':
|
|
||||||
fields["sleeping"] = fields["sleeping"].(int64) + int64(1)
|
|
||||||
case 'I':
|
|
||||||
fields["idle"] = fields["idle"].(int64) + int64(1)
|
|
||||||
default:
|
|
||||||
log.Printf("processes: Unknown state [ %s ] from ps",
|
|
||||||
string(status[0]))
|
|
||||||
}
|
|
||||||
fields["total"] = fields["total"].(int64) + int64(1)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get process states from /proc/(pid)/stat files
|
|
||||||
func (p *Processes) gatherFromProc(fields map[string]interface{}) error {
|
|
||||||
files, err := ioutil.ReadDir("/proc")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
if !file.IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
statFile := path.Join("/proc", file.Name(), "stat")
|
|
||||||
data, err := p.readProcFile(statFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if data == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
stats := bytes.Fields(data)
|
|
||||||
if len(stats) < 3 {
|
|
||||||
return fmt.Errorf("Something is terribly wrong with %s", statFile)
|
|
||||||
}
|
|
||||||
switch stats[2][0] {
|
|
||||||
case 'R':
|
|
||||||
fields["running"] = fields["running"].(int64) + int64(1)
|
|
||||||
case 'S':
|
|
||||||
fields["sleeping"] = fields["sleeping"].(int64) + int64(1)
|
|
||||||
case 'D':
|
|
||||||
fields["blocked"] = fields["blocked"].(int64) + int64(1)
|
|
||||||
case 'Z':
|
|
||||||
fields["zombies"] = fields["zombies"].(int64) + int64(1)
|
|
||||||
case 'T', 't':
|
|
||||||
fields["stopped"] = fields["stopped"].(int64) + int64(1)
|
|
||||||
case 'W':
|
|
||||||
fields["paging"] = fields["paging"].(int64) + int64(1)
|
|
||||||
default:
|
|
||||||
log.Printf("processes: Unknown state [ %s ] in file %s",
|
|
||||||
string(stats[2][0]), statFile)
|
|
||||||
}
|
|
||||||
fields["total"] = fields["total"].(int64) + int64(1)
|
|
||||||
|
|
||||||
threads, err := strconv.Atoi(string(stats[19]))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("processes: Error parsing thread count: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields["total_threads"] = fields["total_threads"].(int64) + int64(threads)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readProcFile(statFile string) ([]byte, error) {
|
|
||||||
if _, err := os.Stat(statFile); os.IsNotExist(err) {
|
|
||||||
return nil, nil
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(statFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func execPS() ([]byte, error) {
|
|
||||||
bin, err := exec.LookPath("ps")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := exec.Command(bin, "axo", "state").Output()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("processes", func() telegraf.Input {
|
|
||||||
return &Processes{
|
|
||||||
execPS: execPS,
|
|
||||||
readProcFile: readProcFile,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package system
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProcesses(t *testing.T) {
|
|
||||||
processes := &Processes{
|
|
||||||
execPS: execPS,
|
|
||||||
readProcFile: readProcFile,
|
|
||||||
}
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
err := processes.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.True(t, acc.HasIntField("processes", "running"))
|
|
||||||
assert.True(t, acc.HasIntField("processes", "sleeping"))
|
|
||||||
assert.True(t, acc.HasIntField("processes", "stopped"))
|
|
||||||
assert.True(t, acc.HasIntField("processes", "total"))
|
|
||||||
total, ok := acc.Get("processes")
|
|
||||||
require.True(t, ok)
|
|
||||||
assert.True(t, total.Fields["total"].(int64) > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromPS(t *testing.T) {
|
|
||||||
processes := &Processes{
|
|
||||||
execPS: testExecPS,
|
|
||||||
forcePS: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := processes.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
fields := getEmptyFields()
|
|
||||||
fields["blocked"] = int64(4)
|
|
||||||
fields["zombies"] = int64(1)
|
|
||||||
fields["running"] = int64(4)
|
|
||||||
fields["sleeping"] = int64(34)
|
|
||||||
fields["total"] = int64(43)
|
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromPSError(t *testing.T) {
|
|
||||||
processes := &Processes{
|
|
||||||
execPS: testExecPSError,
|
|
||||||
forcePS: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := processes.Gather(&acc)
|
|
||||||
require.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromProcFiles(t *testing.T) {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
t.Skip("This test only runs on linux")
|
|
||||||
}
|
|
||||||
tester := tester{}
|
|
||||||
processes := &Processes{
|
|
||||||
readProcFile: tester.testProcFile,
|
|
||||||
forceProc: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
err := processes.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
fields := getEmptyFields()
|
|
||||||
fields["sleeping"] = tester.calls
|
|
||||||
fields["total_threads"] = tester.calls * 2
|
|
||||||
fields["total"] = tester.calls
|
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExecPS() ([]byte, error) {
|
|
||||||
return []byte(testPSOut), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// struct for counting calls to testProcFile
|
|
||||||
type tester struct {
|
|
||||||
calls int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tester) testProcFile(_ string) ([]byte, error) {
|
|
||||||
t.calls++
|
|
||||||
return []byte(fmt.Sprintf(testProcStat, "S", "2")), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExecPSError() ([]byte, error) {
|
|
||||||
return []byte(testPSOut), fmt.Errorf("ERROR!")
|
|
||||||
}
|
|
||||||
|
|
||||||
const testPSOut = `
|
|
||||||
STAT
|
|
||||||
S
|
|
||||||
S
|
|
||||||
S
|
|
||||||
S
|
|
||||||
R
|
|
||||||
R
|
|
||||||
S
|
|
||||||
S
|
|
||||||
Ss
|
|
||||||
Ss
|
|
||||||
S
|
|
||||||
SNs
|
|
||||||
Ss
|
|
||||||
Ss
|
|
||||||
S
|
|
||||||
R+
|
|
||||||
S
|
|
||||||
U
|
|
||||||
S
|
|
||||||
S
|
|
||||||
S
|
|
||||||
S
|
|
||||||
Ss
|
|
||||||
S+
|
|
||||||
Ss
|
|
||||||
S
|
|
||||||
S+
|
|
||||||
S+
|
|
||||||
Ss
|
|
||||||
S+
|
|
||||||
Ss
|
|
||||||
S
|
|
||||||
R+
|
|
||||||
Ss
|
|
||||||
S
|
|
||||||
S+
|
|
||||||
S+
|
|
||||||
Ss
|
|
||||||
L
|
|
||||||
U
|
|
||||||
Z
|
|
||||||
D
|
|
||||||
S+
|
|
||||||
`
|
|
||||||
|
|
||||||
const testProcStat = `10 (rcuob/0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
||||||
`
|
|
||||||
@@ -31,17 +31,11 @@ func (_ *SystemStats) Gather(acc telegraf.Accumulator) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := host.Users()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"load1": loadavg.Load1,
|
"load1": loadavg.Load1,
|
||||||
"load5": loadavg.Load5,
|
"load5": loadavg.Load5,
|
||||||
"load15": loadavg.Load15,
|
"load15": loadavg.Load15,
|
||||||
"uptime": hostinfo.Uptime,
|
"uptime": hostinfo.Uptime,
|
||||||
"n_users": len(users),
|
|
||||||
"uptime_format": format_uptime(hostinfo.Uptime),
|
"uptime_format": format_uptime(hostinfo.Uptime),
|
||||||
}
|
}
|
||||||
acc.AddFields("system", fields, nil)
|
acc.AddFields("system", fields, nil)
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
# TCP listener service input plugin
|
|
||||||
|
|
||||||
The TCP listener is a service input plugin that listens for messages on a TCP
|
|
||||||
socket and adds those messages to InfluxDB.
|
|
||||||
The plugin expects messages in the
|
|
||||||
[Telegraf Input Data Formats](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md).
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
This is a sample configuration for the plugin.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Generic TCP listener
|
|
||||||
[[inputs.tcp_listener]]
|
|
||||||
## Address and port to host TCP listener on
|
|
||||||
service_address = ":8094"
|
|
||||||
|
|
||||||
## Number of TCP messages allowed to queue up. Once filled, the
|
|
||||||
## TCP listener will start dropping packets.
|
|
||||||
allowed_pending_messages = 10000
|
|
||||||
|
|
||||||
## Maximum number of concurrent TCP connections to allow
|
|
||||||
max_tcp_connections = 250
|
|
||||||
|
|
||||||
## Data format to consume. This can be "json", "influx" or "graphite"
|
|
||||||
## Each data format has it's own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
|
||||||
data_format = "influx"
|
|
||||||
```
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
package tcp_listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/internal"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TcpListener struct {
|
|
||||||
ServiceAddress string
|
|
||||||
AllowedPendingMessages int
|
|
||||||
MaxTCPConnections int `toml:"max_tcp_connections"`
|
|
||||||
|
|
||||||
sync.Mutex
|
|
||||||
// Lock for preventing a data race during resource cleanup
|
|
||||||
cleanup sync.Mutex
|
|
||||||
wg sync.WaitGroup
|
|
||||||
|
|
||||||
in chan []byte
|
|
||||||
done chan struct{}
|
|
||||||
// accept channel tracks how many active connections there are, if there
|
|
||||||
// is an available bool in accept, then we are below the maximum and can
|
|
||||||
// accept the connection
|
|
||||||
accept chan bool
|
|
||||||
|
|
||||||
// track the listener here so we can close it in Stop()
|
|
||||||
listener *net.TCPListener
|
|
||||||
// track current connections so we can close them in Stop()
|
|
||||||
conns map[string]*net.TCPConn
|
|
||||||
|
|
||||||
parser parsers.Parser
|
|
||||||
acc telegraf.Accumulator
|
|
||||||
}
|
|
||||||
|
|
||||||
var dropwarn = "ERROR: Message queue full. Discarding line [%s] " +
|
|
||||||
"You may want to increase allowed_pending_messages in the config\n"
|
|
||||||
|
|
||||||
const sampleConfig = `
|
|
||||||
## Address and port to host TCP listener on
|
|
||||||
service_address = ":8094"
|
|
||||||
|
|
||||||
## Number of TCP messages allowed to queue up. Once filled, the
|
|
||||||
## TCP listener will start dropping packets.
|
|
||||||
allowed_pending_messages = 10000
|
|
||||||
|
|
||||||
## Maximum number of concurrent TCP connections to allow
|
|
||||||
max_tcp_connections = 250
|
|
||||||
|
|
||||||
## Data format to consume. This can be "json", "influx" or "graphite"
|
|
||||||
## Each data format has it's own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
|
||||||
data_format = "influx"
|
|
||||||
`
|
|
||||||
|
|
||||||
func (t *TcpListener) SampleConfig() string {
|
|
||||||
return sampleConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TcpListener) Description() string {
|
|
||||||
return "Generic TCP listener"
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the work is done in the Start() function, so this is just a dummy
|
|
||||||
// function.
|
|
||||||
func (t *TcpListener) Gather(_ telegraf.Accumulator) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TcpListener) SetParser(parser parsers.Parser) {
|
|
||||||
t.parser = parser
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the tcp listener service.
|
|
||||||
func (t *TcpListener) Start(acc telegraf.Accumulator) error {
|
|
||||||
t.Lock()
|
|
||||||
defer t.Unlock()
|
|
||||||
|
|
||||||
t.acc = acc
|
|
||||||
t.in = make(chan []byte, t.AllowedPendingMessages)
|
|
||||||
t.done = make(chan struct{})
|
|
||||||
t.accept = make(chan bool, t.MaxTCPConnections)
|
|
||||||
t.conns = make(map[string]*net.TCPConn)
|
|
||||||
for i := 0; i < t.MaxTCPConnections; i++ {
|
|
||||||
t.accept <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start listener
|
|
||||||
var err error
|
|
||||||
address, _ := net.ResolveTCPAddr("tcp", t.ServiceAddress)
|
|
||||||
t.listener, err = net.ListenTCP("tcp", address)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ERROR: ListenUDP - %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Println("TCP server listening on: ", t.listener.Addr().String())
|
|
||||||
|
|
||||||
t.wg.Add(2)
|
|
||||||
go t.tcpListen()
|
|
||||||
go t.tcpParser()
|
|
||||||
|
|
||||||
log.Printf("Started TCP listener service on %s\n", t.ServiceAddress)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop cleans up all resources
|
|
||||||
func (t *TcpListener) Stop() {
|
|
||||||
t.Lock()
|
|
||||||
defer t.Unlock()
|
|
||||||
close(t.done)
|
|
||||||
t.listener.Close()
|
|
||||||
|
|
||||||
// Close all open TCP connections
|
|
||||||
// - get all conns from the t.conns map and put into slice
|
|
||||||
// - this is so the forget() function doesnt conflict with looping
|
|
||||||
// over the t.conns map
|
|
||||||
var conns []*net.TCPConn
|
|
||||||
t.cleanup.Lock()
|
|
||||||
for _, conn := range t.conns {
|
|
||||||
conns = append(conns, conn)
|
|
||||||
}
|
|
||||||
t.cleanup.Unlock()
|
|
||||||
for _, conn := range conns {
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
t.wg.Wait()
|
|
||||||
close(t.in)
|
|
||||||
log.Println("Stopped TCP listener service on ", t.ServiceAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tcpListen listens for incoming TCP connections.
|
|
||||||
func (t *TcpListener) tcpListen() error {
|
|
||||||
defer t.wg.Done()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-t.done:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
// Accept connection:
|
|
||||||
conn, err := t.listener.AcceptTCP()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Received TCP Connection from %s", conn.RemoteAddr())
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-t.accept:
|
|
||||||
// not over connection limit, handle the connection properly.
|
|
||||||
t.wg.Add(1)
|
|
||||||
// generate a random id for this TCPConn
|
|
||||||
id := internal.RandomString(6)
|
|
||||||
t.remember(id, conn)
|
|
||||||
go t.handler(conn, id)
|
|
||||||
default:
|
|
||||||
// We are over the connection limit, refuse & close.
|
|
||||||
t.refuser(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// refuser refuses a TCP connection
|
|
||||||
func (t *TcpListener) refuser(conn *net.TCPConn) {
|
|
||||||
// Tell the connection why we are closing.
|
|
||||||
fmt.Fprintf(conn, "Telegraf maximum concurrent TCP connections (%d)"+
|
|
||||||
" reached, closing.\nYou may want to increase max_tcp_connections in"+
|
|
||||||
" the Telegraf tcp listener configuration.\n", t.MaxTCPConnections)
|
|
||||||
conn.Close()
|
|
||||||
log.Printf("Refused TCP Connection from %s", conn.RemoteAddr())
|
|
||||||
log.Printf("WARNING: Maximum TCP Connections reached, you may want to" +
|
|
||||||
" adjust max_tcp_connections")
|
|
||||||
}
|
|
||||||
|
|
||||||
// handler handles a single TCP Connection
|
|
||||||
func (t *TcpListener) handler(conn *net.TCPConn, id string) {
|
|
||||||
// connection cleanup function
|
|
||||||
defer func() {
|
|
||||||
t.wg.Done()
|
|
||||||
conn.Close()
|
|
||||||
log.Printf("Closed TCP Connection from %s", conn.RemoteAddr())
|
|
||||||
// Add one connection potential back to channel when this one closes
|
|
||||||
t.accept <- true
|
|
||||||
t.forget(id)
|
|
||||||
}()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(conn)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-t.done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
if !scanner.Scan() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf := scanner.Bytes()
|
|
||||||
select {
|
|
||||||
case t.in <- buf:
|
|
||||||
default:
|
|
||||||
log.Printf(dropwarn, string(buf))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tcpParser parses the incoming tcp byte packets
|
|
||||||
func (t *TcpListener) tcpParser() error {
|
|
||||||
defer t.wg.Done()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-t.done:
|
|
||||||
return nil
|
|
||||||
case packet := <-t.in:
|
|
||||||
if len(packet) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
metrics, err := t.parser.Parse(packet)
|
|
||||||
if err == nil {
|
|
||||||
t.storeMetrics(metrics)
|
|
||||||
} else {
|
|
||||||
log.Printf("Malformed packet: [%s], Error: %s\n",
|
|
||||||
string(packet), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TcpListener) storeMetrics(metrics []telegraf.Metric) error {
|
|
||||||
t.Lock()
|
|
||||||
defer t.Unlock()
|
|
||||||
for _, m := range metrics {
|
|
||||||
t.acc.AddFields(m.Name(), m.Fields(), m.Tags(), m.Time())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// forget a TCP connection
|
|
||||||
func (t *TcpListener) forget(id string) {
|
|
||||||
t.cleanup.Lock()
|
|
||||||
defer t.cleanup.Unlock()
|
|
||||||
delete(t.conns, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember a TCP connection
|
|
||||||
func (t *TcpListener) remember(id string, conn *net.TCPConn) {
|
|
||||||
t.cleanup.Lock()
|
|
||||||
defer t.cleanup.Unlock()
|
|
||||||
t.conns[id] = conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("tcp_listener", func() telegraf.Input {
|
|
||||||
return &TcpListener{}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
package tcp_listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testMsg = "cpu_load_short,host=server01 value=12.0 1422568543702900257\n"
|
|
||||||
|
|
||||||
testMsgs = `
|
|
||||||
cpu_load_short,host=server02 value=12.0 1422568543702900257
|
|
||||||
cpu_load_short,host=server03 value=12.0 1422568543702900257
|
|
||||||
cpu_load_short,host=server04 value=12.0 1422568543702900257
|
|
||||||
cpu_load_short,host=server05 value=12.0 1422568543702900257
|
|
||||||
cpu_load_short,host=server06 value=12.0 1422568543702900257
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func newTestTcpListener() (*TcpListener, chan []byte) {
|
|
||||||
in := make(chan []byte, 1500)
|
|
||||||
listener := &TcpListener{
|
|
||||||
ServiceAddress: ":8194",
|
|
||||||
AllowedPendingMessages: 10000,
|
|
||||||
MaxTCPConnections: 250,
|
|
||||||
in: in,
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
return listener, in
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnectTCP(t *testing.T) {
|
|
||||||
listener := TcpListener{
|
|
||||||
ServiceAddress: ":8194",
|
|
||||||
AllowedPendingMessages: 10000,
|
|
||||||
MaxTCPConnections: 250,
|
|
||||||
}
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
require.NoError(t, listener.Start(acc))
|
|
||||||
defer listener.Stop()
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
conn, err := net.Dial("tcp", "127.0.0.1:8194")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// send single message to socket
|
|
||||||
fmt.Fprintf(conn, testMsg)
|
|
||||||
time.Sleep(time.Millisecond * 15)
|
|
||||||
acc.AssertContainsTaggedFields(t, "cpu_load_short",
|
|
||||||
map[string]interface{}{"value": float64(12)},
|
|
||||||
map[string]string{"host": "server01"},
|
|
||||||
)
|
|
||||||
|
|
||||||
// send multiple messages to socket
|
|
||||||
fmt.Fprintf(conn, testMsgs)
|
|
||||||
time.Sleep(time.Millisecond * 15)
|
|
||||||
hostTags := []string{"server02", "server03",
|
|
||||||
"server04", "server05", "server06"}
|
|
||||||
for _, hostTag := range hostTags {
|
|
||||||
acc.AssertContainsTaggedFields(t, "cpu_load_short",
|
|
||||||
map[string]interface{}{"value": float64(12)},
|
|
||||||
map[string]string{"host": hostTag},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that MaxTCPConections is respected
|
|
||||||
func TestConcurrentConns(t *testing.T) {
|
|
||||||
listener := TcpListener{
|
|
||||||
ServiceAddress: ":8195",
|
|
||||||
AllowedPendingMessages: 10000,
|
|
||||||
MaxTCPConnections: 2,
|
|
||||||
}
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
require.NoError(t, listener.Start(acc))
|
|
||||||
defer listener.Stop()
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
_, err := net.Dial("tcp", "127.0.0.1:8195")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = net.Dial("tcp", "127.0.0.1:8195")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Connection over the limit:
|
|
||||||
conn, err := net.Dial("tcp", "127.0.0.1:8195")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
net.Dial("tcp", "127.0.0.1:8195")
|
|
||||||
buf := make([]byte, 1500)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t,
|
|
||||||
"Telegraf maximum concurrent TCP connections (2) reached, closing.\n"+
|
|
||||||
"You may want to increase max_tcp_connections in"+
|
|
||||||
" the Telegraf tcp listener configuration.\n",
|
|
||||||
string(buf[:n]))
|
|
||||||
|
|
||||||
_, err = conn.Write([]byte(testMsg))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
time.Sleep(time.Millisecond * 10)
|
|
||||||
assert.Zero(t, acc.NFields())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that MaxTCPConections is respected when max==1
|
|
||||||
func TestConcurrentConns1(t *testing.T) {
|
|
||||||
listener := TcpListener{
|
|
||||||
ServiceAddress: ":8196",
|
|
||||||
AllowedPendingMessages: 10000,
|
|
||||||
MaxTCPConnections: 1,
|
|
||||||
}
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
require.NoError(t, listener.Start(acc))
|
|
||||||
defer listener.Stop()
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
_, err := net.Dial("tcp", "127.0.0.1:8196")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Connection over the limit:
|
|
||||||
conn, err := net.Dial("tcp", "127.0.0.1:8196")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
net.Dial("tcp", "127.0.0.1:8196")
|
|
||||||
buf := make([]byte, 1500)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t,
|
|
||||||
"Telegraf maximum concurrent TCP connections (1) reached, closing.\n"+
|
|
||||||
"You may want to increase max_tcp_connections in"+
|
|
||||||
" the Telegraf tcp listener configuration.\n",
|
|
||||||
string(buf[:n]))
|
|
||||||
|
|
||||||
_, err = conn.Write([]byte(testMsg))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
time.Sleep(time.Millisecond * 10)
|
|
||||||
assert.Zero(t, acc.NFields())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that MaxTCPConections is respected
|
|
||||||
func TestCloseConcurrentConns(t *testing.T) {
|
|
||||||
listener := TcpListener{
|
|
||||||
ServiceAddress: ":8195",
|
|
||||||
AllowedPendingMessages: 10000,
|
|
||||||
MaxTCPConnections: 2,
|
|
||||||
}
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
|
||||||
require.NoError(t, listener.Start(acc))
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
_, err := net.Dial("tcp", "127.0.0.1:8195")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = net.Dial("tcp", "127.0.0.1:8195")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
listener.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParser(t *testing.T) {
|
|
||||||
var testmsg = []byte(testMsg)
|
|
||||||
|
|
||||||
listener, in := newTestTcpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
listener.wg.Add(1)
|
|
||||||
go listener.tcpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
listener.Gather(&acc)
|
|
||||||
|
|
||||||
if a := acc.NFields(); a != 1 {
|
|
||||||
t.Errorf("got %v, expected %v", a, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t, "cpu_load_short",
|
|
||||||
map[string]interface{}{"value": float64(12)},
|
|
||||||
map[string]string{"host": "server01"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParserInvalidMsg(t *testing.T) {
|
|
||||||
var testmsg = []byte("cpu_load_short")
|
|
||||||
|
|
||||||
listener, in := newTestTcpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
listener.wg.Add(1)
|
|
||||||
go listener.tcpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
|
|
||||||
if a := acc.NFields(); a != 0 {
|
|
||||||
t.Errorf("got %v, expected %v", a, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParserGraphiteMsg(t *testing.T) {
|
|
||||||
var testmsg = []byte("cpu.load.graphite 12 1454780029")
|
|
||||||
|
|
||||||
listener, in := newTestTcpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewGraphiteParser("_", []string{}, nil)
|
|
||||||
listener.wg.Add(1)
|
|
||||||
go listener.tcpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
listener.Gather(&acc)
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "cpu_load_graphite",
|
|
||||||
map[string]interface{}{"value": float64(12)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParserJSONMsg(t *testing.T) {
|
|
||||||
var testmsg = []byte("{\"a\": 5, \"b\": {\"c\": 6}}\n")
|
|
||||||
|
|
||||||
listener, in := newTestTcpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewJSONParser("udp_json_test", []string{}, nil)
|
|
||||||
listener.wg.Add(1)
|
|
||||||
go listener.tcpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
listener.Gather(&acc)
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "udp_json_test",
|
|
||||||
map[string]interface{}{
|
|
||||||
"a": float64(5),
|
|
||||||
"b_c": float64(6),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
# UDP listener service input plugin
|
|
||||||
|
|
||||||
The UDP listener is a service input plugin that listens for messages on a UDP
|
|
||||||
socket and adds those messages to InfluxDB.
|
|
||||||
The plugin expects messages in the
|
|
||||||
[Telegraf Input Data Formats](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md).
|
|
||||||
|
|
||||||
### Configuration:
|
|
||||||
|
|
||||||
This is a sample configuration for the plugin.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[inputs.udp_listener]]
|
|
||||||
## Address and port to host UDP listener on
|
|
||||||
service_address = ":8092"
|
|
||||||
|
|
||||||
## Number of UDP messages allowed to queue up. Once filled, the
|
|
||||||
## UDP listener will start dropping packets.
|
|
||||||
allowed_pending_messages = 10000
|
|
||||||
|
|
||||||
## UDP packet size for the server to listen for. This will depend
|
|
||||||
## on the size of the packets that the client is sending, which is
|
|
||||||
## usually 1500 bytes.
|
|
||||||
udp_packet_size = 1500
|
|
||||||
|
|
||||||
## Data format to consume. This can be "json", "influx" or "graphite"
|
|
||||||
## Each data format has it's own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
|
||||||
data_format = "influx"
|
|
||||||
```
|
|
||||||
|
|
||||||
## A Note on UDP OS Buffer Sizes
|
|
||||||
|
|
||||||
Some OSes (most notably, Linux) place very restricive limits on the performance
|
|
||||||
of UDP protocols. It is _highly_ recommended that you increase these OS limits to
|
|
||||||
at least 8MB before trying to run large amounts of UDP traffic to your instance.
|
|
||||||
8MB is just a recommendation, and can be adjusted higher.
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
Check the current UDP/IP receive buffer limit & default by typing the following
|
|
||||||
commands:
|
|
||||||
|
|
||||||
```
|
|
||||||
sysctl net.core.rmem_max
|
|
||||||
sysctl net.core.rmem_default
|
|
||||||
```
|
|
||||||
|
|
||||||
If the values are less than 8388608 bytes you should add the following lines to
|
|
||||||
the /etc/sysctl.conf file:
|
|
||||||
|
|
||||||
```
|
|
||||||
net.core.rmem_max=8388608
|
|
||||||
net.core.rmem_default=8388608
|
|
||||||
```
|
|
||||||
|
|
||||||
Changes to /etc/sysctl.conf do not take effect until reboot.
|
|
||||||
To update the values immediately, type the following commands as root:
|
|
||||||
|
|
||||||
```
|
|
||||||
sysctl -w net.core.rmem_max=8388608
|
|
||||||
sysctl -w net.core.rmem_default=8388608
|
|
||||||
```
|
|
||||||
|
|
||||||
### BSD/Darwin
|
|
||||||
|
|
||||||
On BSD/Darwin systems you need to add about a 15% padding to the kernel limit
|
|
||||||
socket buffer. Meaning if you want an 8MB buffer (8388608 bytes) you need to set
|
|
||||||
the kernel limit to `8388608*1.15 = 9646900`. This is not documented anywhere but
|
|
||||||
happens
|
|
||||||
[in the kernel here.](https://github.com/freebsd/freebsd/blob/master/sys/kern/uipc_sockbuf.c#L63-L64)
|
|
||||||
|
|
||||||
Check the current UDP/IP buffer limit by typing the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
sysctl kern.ipc.maxsockbuf
|
|
||||||
```
|
|
||||||
|
|
||||||
If the value is less than 9646900 bytes you should add the following lines
|
|
||||||
to the /etc/sysctl.conf file (create it if necessary):
|
|
||||||
|
|
||||||
```
|
|
||||||
kern.ipc.maxsockbuf=9646900
|
|
||||||
```
|
|
||||||
|
|
||||||
Changes to /etc/sysctl.conf do not take effect until reboot.
|
|
||||||
To update the values immediately, type the following commands as root:
|
|
||||||
|
|
||||||
```
|
|
||||||
sysctl -w kern.ipc.maxsockbuf=9646900
|
|
||||||
```
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
package udp_listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UdpListener struct {
|
|
||||||
ServiceAddress string
|
|
||||||
UDPPacketSize int `toml:"udp_packet_size"`
|
|
||||||
AllowedPendingMessages int
|
|
||||||
sync.Mutex
|
|
||||||
|
|
||||||
in chan []byte
|
|
||||||
done chan struct{}
|
|
||||||
|
|
||||||
parser parsers.Parser
|
|
||||||
|
|
||||||
// Keep the accumulator in this struct
|
|
||||||
acc telegraf.Accumulator
|
|
||||||
}
|
|
||||||
|
|
||||||
const UDP_PACKET_SIZE int = 1500
|
|
||||||
|
|
||||||
var dropwarn = "ERROR: Message queue full. Discarding line [%s] " +
|
|
||||||
"You may want to increase allowed_pending_messages in the config\n"
|
|
||||||
|
|
||||||
const sampleConfig = `
|
|
||||||
## Address and port to host UDP listener on
|
|
||||||
service_address = ":8092"
|
|
||||||
|
|
||||||
## Number of UDP messages allowed to queue up. Once filled, the
|
|
||||||
## UDP listener will start dropping packets.
|
|
||||||
allowed_pending_messages = 10000
|
|
||||||
|
|
||||||
## UDP packet size for the server to listen for. This will depend
|
|
||||||
## on the size of the packets that the client is sending, which is
|
|
||||||
## usually 1500 bytes, but can be as large as 65,535 bytes.
|
|
||||||
udp_packet_size = 1500
|
|
||||||
|
|
||||||
## Data format to consume. This can be "json", "influx" or "graphite"
|
|
||||||
## Each data format has it's own unique set of configuration options, read
|
|
||||||
## more about them here:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
|
||||||
data_format = "influx"
|
|
||||||
`
|
|
||||||
|
|
||||||
func (u *UdpListener) SampleConfig() string {
|
|
||||||
return sampleConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) Description() string {
|
|
||||||
return "Generic UDP listener"
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the work is done in the Start() function, so this is just a dummy
|
|
||||||
// function.
|
|
||||||
func (u *UdpListener) Gather(_ telegraf.Accumulator) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) SetParser(parser parsers.Parser) {
|
|
||||||
u.parser = parser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) Start(acc telegraf.Accumulator) error {
|
|
||||||
u.Lock()
|
|
||||||
defer u.Unlock()
|
|
||||||
|
|
||||||
u.acc = acc
|
|
||||||
u.in = make(chan []byte, u.AllowedPendingMessages)
|
|
||||||
u.done = make(chan struct{})
|
|
||||||
|
|
||||||
go u.udpListen()
|
|
||||||
go u.udpParser()
|
|
||||||
|
|
||||||
log.Printf("Started UDP listener service on %s\n", u.ServiceAddress)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) Stop() {
|
|
||||||
u.Lock()
|
|
||||||
defer u.Unlock()
|
|
||||||
close(u.done)
|
|
||||||
close(u.in)
|
|
||||||
log.Println("Stopped UDP listener service on ", u.ServiceAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) udpListen() error {
|
|
||||||
address, _ := net.ResolveUDPAddr("udp", u.ServiceAddress)
|
|
||||||
listener, err := net.ListenUDP("udp", address)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ERROR: ListenUDP - %s", err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
log.Println("UDP server listening on: ", listener.LocalAddr().String())
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-u.done:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
buf := make([]byte, u.UDPPacketSize)
|
|
||||||
n, _, err := listener.ReadFromUDP(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR: %s\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case u.in <- buf[:n]:
|
|
||||||
default:
|
|
||||||
log.Printf(dropwarn, string(buf[:n]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) udpParser() error {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-u.done:
|
|
||||||
return nil
|
|
||||||
case packet := <-u.in:
|
|
||||||
metrics, err := u.parser.Parse(packet)
|
|
||||||
if err == nil {
|
|
||||||
u.storeMetrics(metrics)
|
|
||||||
} else {
|
|
||||||
log.Printf("Malformed packet: [%s], Error: %s\n", packet, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UdpListener) storeMetrics(metrics []telegraf.Metric) error {
|
|
||||||
u.Lock()
|
|
||||||
defer u.Unlock()
|
|
||||||
for _, m := range metrics {
|
|
||||||
u.acc.AddFields(m.Name(), m.Fields(), m.Tags(), m.Time())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
inputs.Add("udp_listener", func() telegraf.Input {
|
|
||||||
return &UdpListener{
|
|
||||||
UDPPacketSize: UDP_PACKET_SIZE,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
package udp_listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newTestUdpListener() (*UdpListener, chan []byte) {
|
|
||||||
in := make(chan []byte, 1500)
|
|
||||||
listener := &UdpListener{
|
|
||||||
ServiceAddress: ":8125",
|
|
||||||
UDPPacketSize: 1500,
|
|
||||||
AllowedPendingMessages: 10000,
|
|
||||||
in: in,
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
return listener, in
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParser(t *testing.T) {
|
|
||||||
log.SetOutput(ioutil.Discard)
|
|
||||||
var testmsg = []byte("cpu_load_short,host=server01 value=12.0 1422568543702900257")
|
|
||||||
|
|
||||||
listener, in := newTestUdpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
go listener.udpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
listener.Gather(&acc)
|
|
||||||
|
|
||||||
if a := acc.NFields(); a != 1 {
|
|
||||||
t.Errorf("got %v, expected %v", a, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AssertContainsTaggedFields(t, "cpu_load_short",
|
|
||||||
map[string]interface{}{"value": float64(12)},
|
|
||||||
map[string]string{"host": "server01"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParserInvalidMsg(t *testing.T) {
|
|
||||||
log.SetOutput(ioutil.Discard)
|
|
||||||
var testmsg = []byte("cpu_load_short")
|
|
||||||
|
|
||||||
listener, in := newTestUdpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewInfluxParser()
|
|
||||||
go listener.udpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
|
|
||||||
if a := acc.NFields(); a != 0 {
|
|
||||||
t.Errorf("got %v, expected %v", a, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParserGraphiteMsg(t *testing.T) {
|
|
||||||
log.SetOutput(ioutil.Discard)
|
|
||||||
var testmsg = []byte("cpu.load.graphite 12 1454780029")
|
|
||||||
|
|
||||||
listener, in := newTestUdpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewGraphiteParser("_", []string{}, nil)
|
|
||||||
go listener.udpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
listener.Gather(&acc)
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "cpu_load_graphite",
|
|
||||||
map[string]interface{}{"value": float64(12)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunParserJSONMsg(t *testing.T) {
|
|
||||||
log.SetOutput(ioutil.Discard)
|
|
||||||
var testmsg = []byte("{\"a\": 5, \"b\": {\"c\": 6}}\n")
|
|
||||||
|
|
||||||
listener, in := newTestUdpListener()
|
|
||||||
acc := testutil.Accumulator{}
|
|
||||||
listener.acc = &acc
|
|
||||||
defer close(listener.done)
|
|
||||||
|
|
||||||
listener.parser, _ = parsers.NewJSONParser("udp_json_test", []string{}, nil)
|
|
||||||
go listener.udpParser()
|
|
||||||
|
|
||||||
in <- testmsg
|
|
||||||
time.Sleep(time.Millisecond * 25)
|
|
||||||
listener.Gather(&acc)
|
|
||||||
|
|
||||||
acc.AssertContainsFields(t, "udp_json_test",
|
|
||||||
map[string]interface{}{
|
|
||||||
"a": float64(5),
|
|
||||||
"b_c": float64(6),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -67,9 +67,6 @@ func (z *Zookeeper) gatherServer(address string, acc telegraf.Accumulator) error
|
|||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
// Extend connection
|
|
||||||
c.SetDeadline(time.Now().Add(defaultTimeout))
|
|
||||||
|
|
||||||
fmt.Fprintf(c, "%s\n", "mntr")
|
fmt.Fprintf(c, "%s\n", "mntr")
|
||||||
rdr := bufio.NewReader(c)
|
rdr := bufio.NewReader(c)
|
||||||
scanner := bufio.NewScanner(rdr)
|
scanner := bufio.NewScanner(rdr)
|
||||||
|
|||||||
@@ -18,16 +18,15 @@ import (
|
|||||||
|
|
||||||
type InfluxDB struct {
|
type InfluxDB struct {
|
||||||
// URL is only for backwards compatability
|
// URL is only for backwards compatability
|
||||||
URL string
|
URL string
|
||||||
URLs []string `toml:"urls"`
|
URLs []string `toml:"urls"`
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
Database string
|
Database string
|
||||||
UserAgent string
|
UserAgent string
|
||||||
Precision string
|
Precision string
|
||||||
RetentionPolicy string
|
Timeout internal.Duration
|
||||||
Timeout internal.Duration
|
UDPPayload int `toml:"udp_payload"`
|
||||||
UDPPayload int `toml:"udp_payload"`
|
|
||||||
|
|
||||||
// Path to CA file
|
// Path to CA file
|
||||||
SSLCA string `toml:"ssl_ca"`
|
SSLCA string `toml:"ssl_ca"`
|
||||||
@@ -47,12 +46,10 @@ var sampleConfig = `
|
|||||||
## this means that only ONE of the urls will be written to each interval.
|
## this means that only ONE of the urls will be written to each interval.
|
||||||
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
||||||
urls = ["http://localhost:8086"] # required
|
urls = ["http://localhost:8086"] # required
|
||||||
## The target database for metrics (telegraf will create it if not exists).
|
## The target database for metrics (telegraf will create it if not exists)
|
||||||
database = "telegraf" # required
|
database = "telegraf" # required
|
||||||
## Retention policy to write to.
|
|
||||||
retention_policy = "default"
|
|
||||||
## Precision of writes, valid values are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
## Precision of writes, valid values are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||||
## note: using "s" precision greatly improves InfluxDB compression.
|
## note: using "s" precision greatly improves InfluxDB compression
|
||||||
precision = "s"
|
precision = "s"
|
||||||
|
|
||||||
## Write timeout (for the InfluxDB client), formatted as a string.
|
## Write timeout (for the InfluxDB client), formatted as a string.
|
||||||
@@ -132,7 +129,6 @@ func (i *InfluxDB) Connect() error {
|
|||||||
|
|
||||||
if e != nil {
|
if e != nil {
|
||||||
log.Println("Database creation failed: " + e.Error())
|
log.Println("Database creation failed: " + e.Error())
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conns = append(conns, c)
|
conns = append(conns, c)
|
||||||
@@ -160,16 +156,9 @@ func (i *InfluxDB) Description() string {
|
|||||||
// Choose a random server in the cluster to write to until a successful write
|
// Choose a random server in the cluster to write to until a successful write
|
||||||
// occurs, logging each unsuccessful. If all servers fail, return error.
|
// occurs, logging each unsuccessful. If all servers fail, return error.
|
||||||
func (i *InfluxDB) Write(metrics []telegraf.Metric) error {
|
func (i *InfluxDB) Write(metrics []telegraf.Metric) error {
|
||||||
if len(i.conns) == 0 {
|
|
||||||
err := i.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bp, err := client.NewBatchPoints(client.BatchPointsConfig{
|
bp, err := client.NewBatchPoints(client.BatchPointsConfig{
|
||||||
Database: i.Database,
|
Database: i.Database,
|
||||||
Precision: i.Precision,
|
Precision: i.Precision,
|
||||||
RetentionPolicy: i.RetentionPolicy,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -191,12 +180,6 @@ func (i *InfluxDB) Write(metrics []telegraf.Metric) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all of the writes failed, create a new connection array so that
|
|
||||||
// i.Connect() will be called on the next gather.
|
|
||||||
if err != nil {
|
|
||||||
i.conns = make([]client.Client, 0)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,24 +4,19 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
"github.com/influxdata/telegraf/plugins/outputs"
|
||||||
"github.com/influxdata/telegraf/plugins/serializers/graphite"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Librato struct {
|
type Librato struct {
|
||||||
ApiUser string
|
ApiUser string
|
||||||
ApiToken string
|
ApiToken string
|
||||||
Debug bool
|
SourceTag string
|
||||||
NameFromTags bool
|
Timeout internal.Duration
|
||||||
SourceTag string
|
|
||||||
Timeout internal.Duration
|
|
||||||
|
|
||||||
apiUrl string
|
apiUrl string
|
||||||
client *http.Client
|
client *http.Client
|
||||||
@@ -37,12 +32,9 @@ var sampleConfig = `
|
|||||||
## Librato API token
|
## Librato API token
|
||||||
api_token = "my-secret-token" # required.
|
api_token = "my-secret-token" # required.
|
||||||
|
|
||||||
### Debug
|
## Tag Field to populate source attribute (optional)
|
||||||
# debug = false
|
## This is typically the _hostname_ from which the metric was obtained.
|
||||||
|
source_tag = "hostname"
|
||||||
### Tag Field to populate source attribute (optional)
|
|
||||||
### This is typically the _hostname_ from which the metric was obtained.
|
|
||||||
source_tag = "host"
|
|
||||||
|
|
||||||
## Connection timeout.
|
## Connection timeout.
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
@@ -90,27 +82,17 @@ func (l *Librato) Write(metrics []telegraf.Metric) error {
|
|||||||
for _, gauge := range gauges {
|
for _, gauge := range gauges {
|
||||||
tempGauges = append(tempGauges, gauge)
|
tempGauges = append(tempGauges, gauge)
|
||||||
metricCounter++
|
metricCounter++
|
||||||
if l.Debug {
|
|
||||||
log.Printf("[DEBUG] Got a gauge: %v\n", gauge)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("unable to build Gauge for %s, skipping\n", m.Name())
|
log.Printf("unable to build Gauge for %s, skipping\n", m.Name())
|
||||||
if l.Debug {
|
|
||||||
log.Printf("[DEBUG] Couldn't build gauge: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lmetrics.Gauges = make([]*Gauge, metricCounter)
|
lmetrics.Gauges = make([]*Gauge, metricCounter)
|
||||||
copy(lmetrics.Gauges, tempGauges[0:])
|
copy(lmetrics.Gauges, tempGauges[0:])
|
||||||
metricsBytes, err := json.Marshal(lmetrics)
|
metricsBytes, err := json.Marshal(metrics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to marshal Metrics, %s\n", err.Error())
|
return fmt.Errorf("unable to marshal Metrics, %s\n", err.Error())
|
||||||
} else {
|
|
||||||
if l.Debug {
|
|
||||||
log.Printf("[DEBUG] Librato request: %v\n", string(metricsBytes))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("POST", l.apiUrl, bytes.NewBuffer(metricsBytes))
|
req, err := http.NewRequest("POST", l.apiUrl, bytes.NewBuffer(metricsBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -121,21 +103,8 @@ func (l *Librato) Write(metrics []telegraf.Metric) error {
|
|||||||
|
|
||||||
resp, err := l.client.Do(req)
|
resp, err := l.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if l.Debug {
|
|
||||||
log.Printf("[DEBUG] Error POSTing metrics: %v\n", err.Error())
|
|
||||||
}
|
|
||||||
return fmt.Errorf("error POSTing metrics, %s\n", err.Error())
|
return fmt.Errorf("error POSTing metrics, %s\n", err.Error())
|
||||||
} else {
|
|
||||||
if l.Debug {
|
|
||||||
htmlData, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[DEBUG] Couldn't get response! (%v)\n", err)
|
|
||||||
} else {
|
|
||||||
log.Printf("[DEBUG] Librato response: %v\n", string(htmlData))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
@@ -153,20 +122,11 @@ func (l *Librato) Description() string {
|
|||||||
return "Configuration for Librato API to send metrics to."
|
return "Configuration for Librato API to send metrics to."
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Librato) buildGaugeName(m telegraf.Metric, fieldName string) string {
|
|
||||||
// Use the GraphiteSerializer
|
|
||||||
graphiteSerializer := graphite.GraphiteSerializer{}
|
|
||||||
serializedMetric := graphiteSerializer.SerializeBucketName(m, fieldName)
|
|
||||||
|
|
||||||
// Deal with slash characters:
|
|
||||||
return strings.Replace(serializedMetric, "/", "-", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Librato) buildGauges(m telegraf.Metric) ([]*Gauge, error) {
|
func (l *Librato) buildGauges(m telegraf.Metric) ([]*Gauge, error) {
|
||||||
gauges := []*Gauge{}
|
gauges := []*Gauge{}
|
||||||
for fieldName, value := range m.Fields() {
|
for fieldName, value := range m.Fields() {
|
||||||
gauge := &Gauge{
|
gauge := &Gauge{
|
||||||
Name: l.buildGaugeName(m, fieldName),
|
Name: m.Name() + "_" + fieldName,
|
||||||
MeasureTime: m.Time().Unix(),
|
MeasureTime: m.Time().Unix(),
|
||||||
}
|
}
|
||||||
if err := gauge.setValue(value); err != nil {
|
if err := gauge.setValue(value); err != nil {
|
||||||
@@ -182,10 +142,6 @@ func (l *Librato) buildGauges(m telegraf.Metric) ([]*Gauge, error) {
|
|||||||
l.SourceTag)
|
l.SourceTag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gauges = append(gauges, gauge)
|
|
||||||
}
|
|
||||||
if l.Debug {
|
|
||||||
fmt.Printf("[DEBUG] Built gauges: %v\n", gauges)
|
|
||||||
}
|
}
|
||||||
return gauges, nil
|
return gauges, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/serializers/graphite"
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,14 +28,6 @@ func fakeLibrato() *Librato {
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildTags(t *testing.T) {
|
|
||||||
testMetric := testutil.TestMetric(0.0, "test1")
|
|
||||||
graphiteSerializer := graphite.GraphiteSerializer{}
|
|
||||||
tags, err := graphiteSerializer.Serialize(testMetric)
|
|
||||||
fmt.Printf("Tags: %v", tags)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUriOverride(t *testing.T) {
|
func TestUriOverride(t *testing.T) {
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
@@ -86,7 +78,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric(0.0, "test1"),
|
testutil.TestMetric(0.0, "test1"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test1.value",
|
Name: "test1",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 0.0,
|
Value: 0.0,
|
||||||
},
|
},
|
||||||
@@ -95,7 +87,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric(1.0, "test2"),
|
testutil.TestMetric(1.0, "test2"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test2.value",
|
Name: "test2",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 1.0,
|
Value: 1.0,
|
||||||
},
|
},
|
||||||
@@ -104,7 +96,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric(10, "test3"),
|
testutil.TestMetric(10, "test3"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test3.value",
|
Name: "test3",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 10.0,
|
Value: 10.0,
|
||||||
},
|
},
|
||||||
@@ -113,7 +105,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric(int32(112345), "test4"),
|
testutil.TestMetric(int32(112345), "test4"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test4.value",
|
Name: "test4",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 112345.0,
|
Value: 112345.0,
|
||||||
},
|
},
|
||||||
@@ -122,7 +114,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric(int64(112345), "test5"),
|
testutil.TestMetric(int64(112345), "test5"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test5.value",
|
Name: "test5",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 112345.0,
|
Value: 112345.0,
|
||||||
},
|
},
|
||||||
@@ -131,7 +123,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric(float32(11234.5), "test6"),
|
testutil.TestMetric(float32(11234.5), "test6"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test6.value",
|
Name: "test6",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 11234.5,
|
Value: 11234.5,
|
||||||
},
|
},
|
||||||
@@ -140,7 +132,7 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testutil.TestMetric("11234.5", "test7"),
|
testutil.TestMetric("11234.5", "test7"),
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "value1.test7.value",
|
Name: "test7",
|
||||||
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 11234.5,
|
Value: 11234.5,
|
||||||
},
|
},
|
||||||
@@ -171,13 +163,13 @@ func TestBuildGauge(t *testing.T) {
|
|||||||
func TestBuildGaugeWithSource(t *testing.T) {
|
func TestBuildGaugeWithSource(t *testing.T) {
|
||||||
pt1, _ := telegraf.NewMetric(
|
pt1, _ := telegraf.NewMetric(
|
||||||
"test1",
|
"test1",
|
||||||
map[string]string{"hostname": "192.168.0.1", "tag1": "value1"},
|
map[string]string{"hostname": "192.168.0.1"},
|
||||||
map[string]interface{}{"value": 0.0},
|
map[string]interface{}{"value": 0.0},
|
||||||
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||||
)
|
)
|
||||||
pt2, _ := telegraf.NewMetric(
|
pt2, _ := telegraf.NewMetric(
|
||||||
"test2",
|
"test2",
|
||||||
map[string]string{"hostnam": "192.168.0.1", "tag1": "value1"},
|
map[string]string{"hostnam": "192.168.0.1"},
|
||||||
map[string]interface{}{"value": 1.0},
|
map[string]interface{}{"value": 1.0},
|
||||||
time.Date(2010, time.December, 10, 23, 0, 0, 0, time.UTC),
|
time.Date(2010, time.December, 10, 23, 0, 0, 0, time.UTC),
|
||||||
)
|
)
|
||||||
@@ -190,7 +182,7 @@ func TestBuildGaugeWithSource(t *testing.T) {
|
|||||||
{
|
{
|
||||||
pt1,
|
pt1,
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "192_168_0_1.value1.test1.value",
|
Name: "test1",
|
||||||
MeasureTime: time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 0.0,
|
Value: 0.0,
|
||||||
Source: "192.168.0.1",
|
Source: "192.168.0.1",
|
||||||
@@ -200,7 +192,7 @@ func TestBuildGaugeWithSource(t *testing.T) {
|
|||||||
{
|
{
|
||||||
pt2,
|
pt2,
|
||||||
&Gauge{
|
&Gauge{
|
||||||
Name: "192_168_0_1.value1.test1.value",
|
Name: "test2",
|
||||||
MeasureTime: time.Date(2010, time.December, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
MeasureTime: time.Date(2010, time.December, 10, 23, 0, 0, 0, time.UTC).Unix(),
|
||||||
Value: 1.0,
|
Value: 1.0,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ func (m *MQTT) createOpts() (*paho.ClientOptions, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user := m.Username
|
user := m.Username
|
||||||
if user != "" {
|
if user == "" {
|
||||||
opts.SetUsername(user)
|
opts.SetUsername(user)
|
||||||
}
|
}
|
||||||
password := m.Password
|
password := m.Password
|
||||||
|
|||||||
@@ -14,49 +14,39 @@ type GraphiteSerializer struct {
|
|||||||
|
|
||||||
func (s *GraphiteSerializer) Serialize(metric telegraf.Metric) ([]string, error) {
|
func (s *GraphiteSerializer) Serialize(metric telegraf.Metric) ([]string, error) {
|
||||||
out := []string{}
|
out := []string{}
|
||||||
|
// Get name
|
||||||
|
name := metric.Name()
|
||||||
// Convert UnixNano to Unix timestamps
|
// Convert UnixNano to Unix timestamps
|
||||||
timestamp := metric.UnixNano() / 1000000000
|
timestamp := metric.UnixNano() / 1000000000
|
||||||
|
tag_str := buildTags(metric)
|
||||||
|
|
||||||
for field_name, value := range metric.Fields() {
|
for field_name, value := range metric.Fields() {
|
||||||
// Convert value
|
// Convert value
|
||||||
value_str := fmt.Sprintf("%#v", value)
|
value_str := fmt.Sprintf("%#v", value)
|
||||||
// Write graphite metric
|
// Write graphite metric
|
||||||
var graphitePoint string
|
var graphitePoint string
|
||||||
graphitePoint = fmt.Sprintf("%s %s %d",
|
if name == field_name {
|
||||||
s.SerializeBucketName(metric, field_name),
|
graphitePoint = fmt.Sprintf("%s.%s %s %d",
|
||||||
value_str,
|
tag_str,
|
||||||
timestamp)
|
strings.Replace(name, ".", "_", -1),
|
||||||
|
value_str,
|
||||||
|
timestamp)
|
||||||
|
} else {
|
||||||
|
graphitePoint = fmt.Sprintf("%s.%s.%s %s %d",
|
||||||
|
tag_str,
|
||||||
|
strings.Replace(name, ".", "_", -1),
|
||||||
|
strings.Replace(field_name, ".", "_", -1),
|
||||||
|
value_str,
|
||||||
|
timestamp)
|
||||||
|
}
|
||||||
|
if s.Prefix != "" {
|
||||||
|
graphitePoint = fmt.Sprintf("%s.%s", s.Prefix, graphitePoint)
|
||||||
|
}
|
||||||
out = append(out, graphitePoint)
|
out = append(out, graphitePoint)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GraphiteSerializer) SerializeBucketName(metric telegraf.Metric, field_name string) string {
|
|
||||||
// Get the metric name
|
|
||||||
name := metric.Name()
|
|
||||||
|
|
||||||
// Convert UnixNano to Unix timestamps
|
|
||||||
tag_str := buildTags(metric)
|
|
||||||
|
|
||||||
// Write graphite metric
|
|
||||||
var serializedBucketName string
|
|
||||||
if name == field_name {
|
|
||||||
serializedBucketName = fmt.Sprintf("%s.%s",
|
|
||||||
tag_str,
|
|
||||||
strings.Replace(name, ".", "_", -1))
|
|
||||||
} else {
|
|
||||||
serializedBucketName = fmt.Sprintf("%s.%s.%s",
|
|
||||||
tag_str,
|
|
||||||
strings.Replace(name, ".", "_", -1),
|
|
||||||
strings.Replace(field_name, ".", "_", -1))
|
|
||||||
}
|
|
||||||
if s.Prefix != "" {
|
|
||||||
serializedBucketName = fmt.Sprintf("%s.%s", s.Prefix, serializedBucketName)
|
|
||||||
}
|
|
||||||
return serializedBucketName
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTags(metric telegraf.Metric) string {
|
func buildTags(metric telegraf.Metric) string {
|
||||||
var keys []string
|
var keys []string
|
||||||
tags := metric.Tags()
|
tags := metric.Tags()
|
||||||
|
|||||||
@@ -119,62 +119,3 @@ func TestSerializeMetricPrefix(t *testing.T) {
|
|||||||
sort.Strings(expS)
|
sort.Strings(expS)
|
||||||
assert.Equal(t, expS, mS)
|
assert.Equal(t, expS, mS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSerializeBucketNameNoHost(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
tags := map[string]string{
|
|
||||||
"cpu": "cpu0",
|
|
||||||
"datacenter": "us-west-2",
|
|
||||||
}
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage_idle": float64(91.5),
|
|
||||||
}
|
|
||||||
m, err := telegraf.NewMetric("cpu", tags, fields, now)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
s := GraphiteSerializer{}
|
|
||||||
mS := s.SerializeBucketName(m, "usage_idle")
|
|
||||||
|
|
||||||
expS := fmt.Sprintf("cpu0.us-west-2.cpu.usage_idle")
|
|
||||||
assert.Equal(t, expS, mS)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSerializeBucketNameHost(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
tags := map[string]string{
|
|
||||||
"host": "localhost",
|
|
||||||
"cpu": "cpu0",
|
|
||||||
"datacenter": "us-west-2",
|
|
||||||
}
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage_idle": float64(91.5),
|
|
||||||
}
|
|
||||||
m, err := telegraf.NewMetric("cpu", tags, fields, now)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
s := GraphiteSerializer{}
|
|
||||||
mS := s.SerializeBucketName(m, "usage_idle")
|
|
||||||
|
|
||||||
expS := fmt.Sprintf("localhost.cpu0.us-west-2.cpu.usage_idle")
|
|
||||||
assert.Equal(t, expS, mS)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSerializeBucketNamePrefix(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
tags := map[string]string{
|
|
||||||
"host": "localhost",
|
|
||||||
"cpu": "cpu0",
|
|
||||||
"datacenter": "us-west-2",
|
|
||||||
}
|
|
||||||
fields := map[string]interface{}{
|
|
||||||
"usage_idle": float64(91.5),
|
|
||||||
}
|
|
||||||
m, err := telegraf.NewMetric("cpu", tags, fields, now)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
s := GraphiteSerializer{Prefix: "prefix"}
|
|
||||||
mS := s.SerializeBucketName(m, "usage_idle")
|
|
||||||
|
|
||||||
expS := fmt.Sprintf("prefix.localhost.cpu0.us-west-2.cpu.usage_idle")
|
|
||||||
assert.Equal(t, expS, mS)
|
|
||||||
}
|
|
||||||
|
|||||||
592
scripts/build.py
592
scripts/build.py
@@ -1,4 +1,11 @@
|
|||||||
#!/usr/bin/python -u
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# This is the Telegraf build script.
|
||||||
|
#
|
||||||
|
# Current caveats:
|
||||||
|
# - Does not checkout the correct commit/branch (for now, you will need to do so manually)
|
||||||
|
# - Has external dependencies for packaging (fpm) and uploading (boto)
|
||||||
|
#
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@@ -12,12 +19,7 @@ import re
|
|||||||
|
|
||||||
debug = False
|
debug = False
|
||||||
|
|
||||||
################
|
# PACKAGING VARIABLES
|
||||||
#### Telegraf Variables
|
|
||||||
################
|
|
||||||
|
|
||||||
# Packaging variables
|
|
||||||
PACKAGE_NAME = "telegraf"
|
|
||||||
INSTALL_ROOT_DIR = "/usr/bin"
|
INSTALL_ROOT_DIR = "/usr/bin"
|
||||||
LOG_DIR = "/var/log/telegraf"
|
LOG_DIR = "/var/log/telegraf"
|
||||||
SCRIPT_DIR = "/usr/lib/telegraf/scripts"
|
SCRIPT_DIR = "/usr/lib/telegraf/scripts"
|
||||||
@@ -28,19 +30,8 @@ INIT_SCRIPT = "scripts/init.sh"
|
|||||||
SYSTEMD_SCRIPT = "scripts/telegraf.service"
|
SYSTEMD_SCRIPT = "scripts/telegraf.service"
|
||||||
LOGROTATE_SCRIPT = "etc/logrotate.d/telegraf"
|
LOGROTATE_SCRIPT = "etc/logrotate.d/telegraf"
|
||||||
DEFAULT_CONFIG = "etc/telegraf.conf"
|
DEFAULT_CONFIG = "etc/telegraf.conf"
|
||||||
DEFAULT_WINDOWS_CONFIG = "etc/telegraf_windows.conf"
|
|
||||||
POSTINST_SCRIPT = "scripts/post-install.sh"
|
POSTINST_SCRIPT = "scripts/post-install.sh"
|
||||||
PREINST_SCRIPT = "scripts/pre-install.sh"
|
PREINST_SCRIPT = "scripts/pre-install.sh"
|
||||||
POSTREMOVE_SCRIPT = "scripts/post-remove.sh"
|
|
||||||
PREREMOVE_SCRIPT = "scripts/pre-remove.sh"
|
|
||||||
|
|
||||||
# Default AWS S3 bucket for uploads
|
|
||||||
DEFAULT_BUCKET = "get.influxdb.org/telegraf"
|
|
||||||
|
|
||||||
CONFIGURATION_FILES = [
|
|
||||||
CONFIG_DIR + '/telegraf.conf',
|
|
||||||
LOGROTATE_DIR + '/telegraf',
|
|
||||||
]
|
|
||||||
|
|
||||||
# META-PACKAGE VARIABLES
|
# META-PACKAGE VARIABLES
|
||||||
PACKAGE_LICENSE = "MIT"
|
PACKAGE_LICENSE = "MIT"
|
||||||
@@ -51,8 +42,7 @@ DESCRIPTION = "Plugin-driven server agent for reporting metrics into InfluxDB."
|
|||||||
|
|
||||||
# SCRIPT START
|
# SCRIPT START
|
||||||
prereqs = [ 'git', 'go' ]
|
prereqs = [ 'git', 'go' ]
|
||||||
go_vet_command = "go tool vet -composites=true ./"
|
optional_prereqs = [ 'fpm', 'rpmbuild' ]
|
||||||
optional_prereqs = [ 'gvm', 'fpm', 'rpmbuild' ]
|
|
||||||
|
|
||||||
fpm_common_args = "-f -s dir --log error \
|
fpm_common_args = "-f -s dir --log error \
|
||||||
--vendor {} \
|
--vendor {} \
|
||||||
@@ -63,8 +53,6 @@ fpm_common_args = "-f -s dir --log error \
|
|||||||
--config-files {} \
|
--config-files {} \
|
||||||
--after-install {} \
|
--after-install {} \
|
||||||
--before-install {} \
|
--before-install {} \
|
||||||
--after-remove {} \
|
|
||||||
--before-remove {} \
|
|
||||||
--description \"{}\"".format(
|
--description \"{}\"".format(
|
||||||
VENDOR,
|
VENDOR,
|
||||||
PACKAGE_URL,
|
PACKAGE_URL,
|
||||||
@@ -74,88 +62,31 @@ fpm_common_args = "-f -s dir --log error \
|
|||||||
LOGROTATE_DIR + '/telegraf',
|
LOGROTATE_DIR + '/telegraf',
|
||||||
POSTINST_SCRIPT,
|
POSTINST_SCRIPT,
|
||||||
PREINST_SCRIPT,
|
PREINST_SCRIPT,
|
||||||
POSTREMOVE_SCRIPT,
|
|
||||||
PREREMOVE_SCRIPT,
|
|
||||||
DESCRIPTION)
|
DESCRIPTION)
|
||||||
|
|
||||||
targets = {
|
targets = {
|
||||||
'telegraf' : './cmd/telegraf',
|
'telegraf' : './cmd/telegraf/telegraf.go',
|
||||||
}
|
}
|
||||||
|
|
||||||
supported_builds = {
|
supported_builds = {
|
||||||
"darwin": [ "amd64" ],
|
'darwin': [ "amd64", "i386" ],
|
||||||
"windows": [ "amd64", "i386" ],
|
'windows': [ "amd64", "i386" ],
|
||||||
"linux": [ "amd64", "i386", "armhf", "armel", "arm64" ],
|
'linux': [ "amd64", "i386", "arm" ]
|
||||||
"freebsd": [ "amd64" ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
supported_packages = {
|
supported_packages = {
|
||||||
"darwin": [ "tar", "zip" ],
|
"darwin": [ "tar", "zip" ],
|
||||||
"linux": [ "deb", "rpm", "tar" ],
|
"linux": [ "deb", "rpm", "tar", "zip" ],
|
||||||
"windows": [ "zip" ],
|
"windows": [ "zip" ],
|
||||||
"freebsd": [ "tar" ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
supported_tags = {
|
supported_tags = {
|
||||||
# "linux": {
|
# "linux": {
|
||||||
# "amd64": ["sensors"]
|
# "amd64": ["sensors"]
|
||||||
# }
|
# }
|
||||||
}
|
}
|
||||||
|
|
||||||
prereq_cmds = {
|
prereq_cmds = {
|
||||||
# "linux": "sudo apt-get install lm-sensors libsensors4-dev"
|
# "linux": "sudo apt-get install lm-sensors libsensors4-dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
################
|
|
||||||
#### Telegraf Functions
|
|
||||||
################
|
|
||||||
|
|
||||||
def create_package_fs(build_root):
|
|
||||||
print("Creating a filesystem hierarchy from directory: {}".format(build_root))
|
|
||||||
# Using [1:] for the path names due to them being absolute
|
|
||||||
# (will overwrite previous paths, per 'os.path.join' documentation)
|
|
||||||
dirs = [ INSTALL_ROOT_DIR[1:], LOG_DIR[1:], SCRIPT_DIR[1:], CONFIG_DIR[1:], LOGROTATE_DIR[1:] ]
|
|
||||||
for d in dirs:
|
|
||||||
create_dir(os.path.join(build_root, d))
|
|
||||||
os.chmod(os.path.join(build_root, d), 0o755)
|
|
||||||
|
|
||||||
def package_scripts(build_root, windows=False):
|
|
||||||
print("Copying scripts and sample configuration to build directory")
|
|
||||||
if windows:
|
|
||||||
shutil.copyfile(DEFAULT_WINDOWS_CONFIG, os.path.join(build_root, "telegraf.conf"))
|
|
||||||
os.chmod(os.path.join(build_root, "telegraf.conf"), 0o644)
|
|
||||||
else:
|
|
||||||
shutil.copyfile(INIT_SCRIPT, os.path.join(build_root, SCRIPT_DIR[1:], INIT_SCRIPT.split('/')[1]))
|
|
||||||
os.chmod(os.path.join(build_root, SCRIPT_DIR[1:], INIT_SCRIPT.split('/')[1]), 0o644)
|
|
||||||
shutil.copyfile(SYSTEMD_SCRIPT, os.path.join(build_root, SCRIPT_DIR[1:], SYSTEMD_SCRIPT.split('/')[1]))
|
|
||||||
os.chmod(os.path.join(build_root, SCRIPT_DIR[1:], SYSTEMD_SCRIPT.split('/')[1]), 0o644)
|
|
||||||
shutil.copyfile(LOGROTATE_SCRIPT, os.path.join(build_root, LOGROTATE_DIR[1:], "telegraf"))
|
|
||||||
os.chmod(os.path.join(build_root, LOGROTATE_DIR[1:], "telegraf"), 0o644)
|
|
||||||
shutil.copyfile(DEFAULT_CONFIG, os.path.join(build_root, CONFIG_DIR[1:], "telegraf.conf"))
|
|
||||||
os.chmod(os.path.join(build_root, CONFIG_DIR[1:], "telegraf.conf"), 0o644)
|
|
||||||
|
|
||||||
def run_generate():
|
|
||||||
# NOOP for Telegraf
|
|
||||||
return True
|
|
||||||
|
|
||||||
def go_get(branch, update=False, no_stash=False):
|
|
||||||
if not check_path_for("gdm"):
|
|
||||||
print("Downloading `gdm`...")
|
|
||||||
get_command = "go get github.com/sparrc/gdm"
|
|
||||||
run(get_command)
|
|
||||||
print("Retrieving dependencies with `gdm`...")
|
|
||||||
run("{}/bin/gdm restore -f Godeps_windows".format(os.environ.get("GOPATH")))
|
|
||||||
run("{}/bin/gdm restore".format(os.environ.get("GOPATH")))
|
|
||||||
return True
|
|
||||||
|
|
||||||
def run_tests(race, parallel, timeout, no_vet):
|
|
||||||
# Currently a NOOP for Telegraf
|
|
||||||
return True
|
|
||||||
|
|
||||||
################
|
|
||||||
#### All Telegraf-specific content above this line
|
|
||||||
################
|
|
||||||
|
|
||||||
def run(command, allow_failure=False, shell=False):
|
def run(command, allow_failure=False, shell=False):
|
||||||
out = None
|
out = None
|
||||||
if debug:
|
if debug:
|
||||||
@@ -166,8 +97,6 @@ def run(command, allow_failure=False, shell=False):
|
|||||||
else:
|
else:
|
||||||
out = subprocess.check_output(command.split(), stderr=subprocess.STDOUT)
|
out = subprocess.check_output(command.split(), stderr=subprocess.STDOUT)
|
||||||
out = out.decode("utf8")
|
out = out.decode("utf8")
|
||||||
if debug:
|
|
||||||
print("[DEBUG] command output: {}".format(out))
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print("")
|
print("")
|
||||||
print("")
|
print("")
|
||||||
@@ -197,32 +126,16 @@ def run(command, allow_failure=False, shell=False):
|
|||||||
else:
|
else:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def create_temp_dir(prefix = None):
|
def create_temp_dir(prefix=None):
|
||||||
if prefix is None:
|
if prefix is None:
|
||||||
return tempfile.mkdtemp(prefix="{}-build.".format(PACKAGE_NAME))
|
return tempfile.mkdtemp(prefix="telegraf-build.")
|
||||||
else:
|
else:
|
||||||
return tempfile.mkdtemp(prefix=prefix)
|
return tempfile.mkdtemp(prefix=prefix)
|
||||||
|
|
||||||
def get_current_version_tag():
|
|
||||||
version = run("git describe --always --tags --abbrev=0").strip()
|
|
||||||
return version
|
|
||||||
|
|
||||||
def get_current_version():
|
def get_current_version():
|
||||||
version_tag = get_current_version_tag()
|
command = "git describe --always --tags --abbrev=0"
|
||||||
if version_tag[0] == 'v':
|
out = run(command)
|
||||||
# Remove leading 'v' and possible '-rc\d+'
|
return out.strip()
|
||||||
version = re.sub(r'-rc\d+', '', version_tag[1:])
|
|
||||||
else:
|
|
||||||
version = re.sub(r'-rc\d+', '', version_tag)
|
|
||||||
return version
|
|
||||||
|
|
||||||
def get_current_rc():
|
|
||||||
rc = None
|
|
||||||
version_tag = get_current_version_tag()
|
|
||||||
matches = re.match(r'.*-rc(\d+)', version_tag)
|
|
||||||
if matches:
|
|
||||||
rc, = matches.groups(1)
|
|
||||||
return rc
|
|
||||||
|
|
||||||
def get_current_commit(short=False):
|
def get_current_commit(short=False):
|
||||||
command = None
|
command = None
|
||||||
@@ -267,61 +180,56 @@ def check_path_for(b):
|
|||||||
if os.path.isfile(full_path) and os.access(full_path, os.X_OK):
|
if os.path.isfile(full_path) and os.access(full_path, os.X_OK):
|
||||||
return full_path
|
return full_path
|
||||||
|
|
||||||
def check_environ(build_dir=None):
|
def check_environ(build_dir = None):
|
||||||
print("")
|
print("\nChecking environment:")
|
||||||
print("Checking environment:")
|
|
||||||
for v in [ "GOPATH", "GOBIN", "GOROOT" ]:
|
for v in [ "GOPATH", "GOBIN", "GOROOT" ]:
|
||||||
print("- {} -> {}".format(v, os.environ.get(v)))
|
print("\t- {} -> {}".format(v, os.environ.get(v)))
|
||||||
|
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
if build_dir is None and os.environ.get("GOPATH") and os.environ.get("GOPATH") not in cwd:
|
if build_dir == None and os.environ.get("GOPATH") and os.environ.get("GOPATH") not in cwd:
|
||||||
print("!! WARNING: Your current directory is not under your GOPATH. This may lead to build failures.")
|
print("\n!! WARNING: Your current directory is not under your GOPATH. This may lead to build failures.")
|
||||||
|
|
||||||
def check_prereqs():
|
def check_prereqs():
|
||||||
print("")
|
print("\nChecking for dependencies:")
|
||||||
print("Checking for dependencies:")
|
|
||||||
for req in prereqs:
|
for req in prereqs:
|
||||||
path = check_path_for(req)
|
path = check_path_for(req)
|
||||||
if path:
|
if path is None:
|
||||||
print("- {} -> {}".format(req, path))
|
path = '?'
|
||||||
else:
|
print("\t- {} -> {}".format(req, path))
|
||||||
print("- {} -> ?".format(req))
|
|
||||||
for req in optional_prereqs:
|
for req in optional_prereqs:
|
||||||
path = check_path_for(req)
|
path = check_path_for(req)
|
||||||
if path:
|
if path is None:
|
||||||
print("- {} (optional) -> {}".format(req, path))
|
path = '?'
|
||||||
else:
|
print("\t- {} (optional) -> {}".format(req, path))
|
||||||
print("- {} (optional) -> ?".format(req))
|
|
||||||
print("")
|
print("")
|
||||||
return True
|
|
||||||
|
|
||||||
def upload_packages(packages, bucket_name=None, nightly=False):
|
def upload_packages(packages, bucket_name=None, nightly=False):
|
||||||
if debug:
|
if debug:
|
||||||
print("[DEBUG] upload_packages: {}".format(packages))
|
print("[DEBUG] upload_packags: {}".format(packages))
|
||||||
try:
|
try:
|
||||||
import boto
|
import boto
|
||||||
from boto.s3.key import Key
|
from boto.s3.key import Key
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("!! Cannot upload packages without the 'boto' Python library.")
|
print "!! Cannot upload packages without the 'boto' python library."
|
||||||
return 1
|
return 1
|
||||||
print("Connecting to S3...".format(bucket_name))
|
print("Uploading packages to S3...")
|
||||||
|
print("")
|
||||||
c = boto.connect_s3()
|
c = boto.connect_s3()
|
||||||
if bucket_name is None:
|
if bucket_name is None:
|
||||||
bucket_name = DEFAULT_BUCKET
|
bucket_name = 'get.influxdb.org/telegraf'
|
||||||
bucket = c.get_bucket(bucket_name.split('/')[0])
|
bucket = c.get_bucket(bucket_name.split('/')[0])
|
||||||
print("Using bucket: {}".format(bucket_name))
|
print("\t - Using bucket: {}".format(bucket_name))
|
||||||
for p in packages:
|
for p in packages:
|
||||||
if '/' in bucket_name:
|
if '/' in bucket_name:
|
||||||
# Allow for nested paths within the bucket name (ex:
|
# Allow for nested paths within the bucket name (ex:
|
||||||
# bucket/folder). Assuming forward-slashes as path
|
# bucket/telegraf). Assuming forward-slashes as path
|
||||||
# delimiter.
|
# delimiter.
|
||||||
name = os.path.join('/'.join(bucket_name.split('/')[1:]),
|
name = os.path.join('/'.join(bucket_name.split('/')[1:]),
|
||||||
os.path.basename(p))
|
os.path.basename(p))
|
||||||
else:
|
else:
|
||||||
name = os.path.basename(p)
|
name = os.path.basename(p)
|
||||||
if bucket.get_key(name) is None or nightly:
|
if bucket.get_key(name) is None or nightly:
|
||||||
print("Uploading {}...".format(name))
|
print("\t - Uploading {} to {}...".format(name, bucket_name))
|
||||||
sys.stdout.flush()
|
|
||||||
k = Key(bucket)
|
k = Key(bucket)
|
||||||
k.key = name
|
k.key = name
|
||||||
if nightly:
|
if nightly:
|
||||||
@@ -330,10 +238,8 @@ def upload_packages(packages, bucket_name=None, nightly=False):
|
|||||||
n = k.set_contents_from_filename(p, replace=False)
|
n = k.set_contents_from_filename(p, replace=False)
|
||||||
k.make_public()
|
k.make_public()
|
||||||
else:
|
else:
|
||||||
print("!! Not uploading package {}, as it already exists.".format(p))
|
print("\t - Not uploading {}, already exists.".format(p))
|
||||||
print("")
|
print("")
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def build(version=None,
|
def build(version=None,
|
||||||
branch=None,
|
branch=None,
|
||||||
@@ -344,18 +250,22 @@ def build(version=None,
|
|||||||
rc=None,
|
rc=None,
|
||||||
race=False,
|
race=False,
|
||||||
clean=False,
|
clean=False,
|
||||||
outdir="."):
|
outdir=".",
|
||||||
print("\n-------------------------\n")
|
goarm_version="6"):
|
||||||
print("Build Plan:")
|
print("-------------------------")
|
||||||
print("- version: {}".format(version))
|
print("")
|
||||||
|
print("Build plan:")
|
||||||
|
print("\t- version: {}".format(version))
|
||||||
if rc:
|
if rc:
|
||||||
print("- release candidate: {}".format(rc))
|
print("\t- release candidate: {}".format(rc))
|
||||||
print("- commit: {}".format(get_current_commit(short=True)))
|
print("\t- commit: {}".format(commit))
|
||||||
print("- branch: {}".format(get_current_branch()))
|
print("\t- branch: {}".format(branch))
|
||||||
print("- platform: {}".format(platform))
|
print("\t- platform: {}".format(platform))
|
||||||
print("- arch: {}".format(arch))
|
print("\t- arch: {}".format(arch))
|
||||||
print("- nightly? {}".format(str(nightly).lower()))
|
if arch == 'arm' and goarm_version:
|
||||||
print("- race enabled? {}".format(str(race).lower()))
|
print("\t- ARM version: {}".format(goarm_version))
|
||||||
|
print("\t- nightly? {}".format(str(nightly).lower()))
|
||||||
|
print("\t- race enabled? {}".format(str(race).lower()))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
if not os.path.exists(outdir):
|
if not os.path.exists(outdir):
|
||||||
@@ -369,49 +279,45 @@ def build(version=None,
|
|||||||
# If a release candidate, update the version information accordingly
|
# If a release candidate, update the version information accordingly
|
||||||
version = "{}rc{}".format(version, rc)
|
version = "{}rc{}".format(version, rc)
|
||||||
|
|
||||||
|
# Set architecture to something that Go expects
|
||||||
|
if arch == 'i386':
|
||||||
|
arch = '386'
|
||||||
|
elif arch == 'x86_64':
|
||||||
|
arch = 'amd64'
|
||||||
|
|
||||||
print("Starting build...")
|
print("Starting build...")
|
||||||
tmp_build_dir = create_temp_dir()
|
|
||||||
for b, c in targets.items():
|
for b, c in targets.items():
|
||||||
print("Building '{}'...".format(os.path.join(outdir, b)))
|
|
||||||
build_command = ""
|
|
||||||
if "arm" in arch:
|
|
||||||
build_command += "GOOS={} GOARCH={} ".format(platform, "arm")
|
|
||||||
else:
|
|
||||||
if arch == 'i386':
|
|
||||||
arch = '386'
|
|
||||||
elif arch == 'x86_64':
|
|
||||||
arch = 'amd64'
|
|
||||||
build_command += "GOOS={} GOARCH={} ".format(platform, arch)
|
|
||||||
if "arm" in arch:
|
|
||||||
if arch == "armel":
|
|
||||||
build_command += "GOARM=5 "
|
|
||||||
elif arch == "armhf" or arch == "arm":
|
|
||||||
build_command += "GOARM=6 "
|
|
||||||
elif arch == "arm64":
|
|
||||||
build_command += "GOARM=7 "
|
|
||||||
else:
|
|
||||||
print("!! Invalid ARM architecture specifed: {}".format(arch))
|
|
||||||
print("Please specify either 'armel', 'armhf', or 'arm64'")
|
|
||||||
return 1
|
|
||||||
if platform == 'windows':
|
if platform == 'windows':
|
||||||
build_command += "go build -o {} ".format(os.path.join(outdir, b + '.exe'))
|
b = b + '.exe'
|
||||||
else:
|
print("\t- Building '{}'...".format(os.path.join(outdir, b)))
|
||||||
build_command += "go build -o {} ".format(os.path.join(outdir, b))
|
build_command = ""
|
||||||
|
build_command += "GOOS={} GOARCH={} ".format(platform, arch)
|
||||||
|
if arch == "arm" and goarm_version:
|
||||||
|
if goarm_version not in ["5", "6", "7", "arm64"]:
|
||||||
|
print("!! Invalid ARM build version: {}".format(goarm_version))
|
||||||
|
build_command += "GOARM={} ".format(goarm_version)
|
||||||
|
build_command += "go build -o {} ".format(os.path.join(outdir, b))
|
||||||
if race:
|
if race:
|
||||||
build_command += "-race "
|
build_command += "-race "
|
||||||
|
if platform in supported_tags:
|
||||||
|
if arch in supported_tags[platform]:
|
||||||
|
build_tags = supported_tags[platform][arch]
|
||||||
|
for build_tag in build_tags:
|
||||||
|
build_command += "-tags "+build_tag+" "
|
||||||
go_version = get_go_version()
|
go_version = get_go_version()
|
||||||
if "1.4" in go_version:
|
if "1.4" in go_version:
|
||||||
build_command += "-ldflags=\"-X main.Version {} -X main.Branch {} -X main.Commit {}\" ".format(version,
|
build_command += "-ldflags=\"-X main.buildTime '{}' ".format(datetime.datetime.utcnow().isoformat())
|
||||||
get_current_branch(),
|
build_command += "-X main.Version {} ".format(version)
|
||||||
get_current_commit())
|
build_command += "-X main.Branch {} ".format(get_current_branch())
|
||||||
|
build_command += "-X main.Commit {}\" ".format(get_current_commit())
|
||||||
else:
|
else:
|
||||||
# With Go 1.5, the linker flag arguments changed to 'name=value' from 'name value'
|
build_command += "-ldflags=\"-X main.buildTime='{}' ".format(datetime.datetime.utcnow().isoformat())
|
||||||
build_command += "-ldflags=\"-X main.Version={} -X main.Branch={} -X main.Commit={}\" ".format(version,
|
build_command += "-X main.Version={} ".format(version)
|
||||||
get_current_branch(),
|
build_command += "-X main.Branch={} ".format(get_current_branch())
|
||||||
get_current_commit())
|
build_command += "-X main.Commit={}\" ".format(get_current_commit())
|
||||||
build_command += c
|
build_command += c
|
||||||
run(build_command, shell=True)
|
run(build_command, shell=True)
|
||||||
return 0
|
print("")
|
||||||
|
|
||||||
def create_dir(path):
|
def create_dir(path):
|
||||||
try:
|
try:
|
||||||
@@ -436,12 +342,35 @@ def copy_file(fr, to):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def generate_md5_from_file(path):
|
def create_package_fs(build_root):
|
||||||
m = hashlib.md5()
|
print("\t- Creating a filesystem hierarchy from directory: {}".format(build_root))
|
||||||
with open(path, 'rb') as f:
|
# Using [1:] for the path names due to them being absolute
|
||||||
for chunk in iter(lambda: f.read(4096), b""):
|
# (will overwrite previous paths, per 'os.path.join' documentation)
|
||||||
m.update(chunk)
|
dirs = [ INSTALL_ROOT_DIR[1:], LOG_DIR[1:], SCRIPT_DIR[1:], CONFIG_DIR[1:], LOGROTATE_DIR[1:] ]
|
||||||
return m.hexdigest()
|
for d in dirs:
|
||||||
|
create_dir(os.path.join(build_root, d))
|
||||||
|
os.chmod(os.path.join(build_root, d), 0o755)
|
||||||
|
|
||||||
|
def package_scripts(build_root, windows=False):
|
||||||
|
print("\t- Copying scripts and sample configuration to build directory")
|
||||||
|
if windows:
|
||||||
|
shutil.copyfile(DEFAULT_CONFIG, os.path.join(build_root, "telegraf.conf"))
|
||||||
|
os.chmod(os.path.join(build_root, "telegraf.conf"), 0o644)
|
||||||
|
else:
|
||||||
|
shutil.copyfile(INIT_SCRIPT, os.path.join(build_root, SCRIPT_DIR[1:], INIT_SCRIPT.split('/')[1]))
|
||||||
|
os.chmod(os.path.join(build_root, SCRIPT_DIR[1:], INIT_SCRIPT.split('/')[1]), 0o644)
|
||||||
|
shutil.copyfile(SYSTEMD_SCRIPT, os.path.join(build_root, SCRIPT_DIR[1:], SYSTEMD_SCRIPT.split('/')[1]))
|
||||||
|
os.chmod(os.path.join(build_root, SCRIPT_DIR[1:], SYSTEMD_SCRIPT.split('/')[1]), 0o644)
|
||||||
|
shutil.copyfile(LOGROTATE_SCRIPT, os.path.join(build_root, LOGROTATE_DIR[1:], "telegraf"))
|
||||||
|
os.chmod(os.path.join(build_root, LOGROTATE_DIR[1:], "telegraf"), 0o644)
|
||||||
|
shutil.copyfile(DEFAULT_CONFIG, os.path.join(build_root, CONFIG_DIR[1:], "telegraf.conf"))
|
||||||
|
os.chmod(os.path.join(build_root, CONFIG_DIR[1:], "telegraf.conf"), 0o644)
|
||||||
|
|
||||||
|
def go_get():
|
||||||
|
print("Retrieving Go dependencies...")
|
||||||
|
run("go get github.com/sparrc/gdm")
|
||||||
|
run("gdm restore")
|
||||||
|
run("gdm restore -f Godeps_windows")
|
||||||
|
|
||||||
def generate_md5_from_file(path):
|
def generate_md5_from_file(path):
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
@@ -453,135 +382,98 @@ def generate_md5_from_file(path):
|
|||||||
m.update(data)
|
m.update(data)
|
||||||
return m.hexdigest()
|
return m.hexdigest()
|
||||||
|
|
||||||
def build_packages(build_output, version, nightly=False, rc=None, iteration=1):
|
def build_packages(build_output, version, pkg_arch, nightly=False, rc=None, iteration=1):
|
||||||
outfiles = []
|
outfiles = []
|
||||||
tmp_build_dir = create_temp_dir()
|
tmp_build_dir = create_temp_dir()
|
||||||
if debug:
|
if debug:
|
||||||
print("[DEBUG] build_output = {}".format(build_output))
|
print("[DEBUG] build_output = {}".format(build_output))
|
||||||
try:
|
try:
|
||||||
print("-------------------------\n")
|
print("-------------------------")
|
||||||
|
print("")
|
||||||
print("Packaging...")
|
print("Packaging...")
|
||||||
for platform in build_output:
|
for p in build_output:
|
||||||
# Create top-level folder displaying which platform (linux, etc)
|
# Create top-level folder displaying which platform (linux, etc)
|
||||||
create_dir(os.path.join(tmp_build_dir, platform))
|
create_dir(os.path.join(tmp_build_dir, p))
|
||||||
for arch in build_output[platform]:
|
for a in build_output[p]:
|
||||||
# Create second-level directory displaying the architecture (amd64, etc)
|
current_location = build_output[p][a]
|
||||||
current_location = build_output[platform][arch]
|
# Create second-level directory displaying the architecture (amd64, etc)p
|
||||||
|
build_root = os.path.join(tmp_build_dir, p, a)
|
||||||
# Create directory tree to mimic file system of package
|
# Create directory tree to mimic file system of package
|
||||||
build_root = os.path.join(tmp_build_dir,
|
|
||||||
platform,
|
|
||||||
arch,
|
|
||||||
'{}-{}-{}'.format(PACKAGE_NAME, version, iteration))
|
|
||||||
create_dir(build_root)
|
create_dir(build_root)
|
||||||
|
if p == 'windows':
|
||||||
# Copy packaging scripts to build directory
|
|
||||||
if platform == 'windows':
|
|
||||||
package_scripts(build_root, windows=True)
|
package_scripts(build_root, windows=True)
|
||||||
else:
|
else:
|
||||||
create_package_fs(build_root)
|
create_package_fs(build_root)
|
||||||
|
# Copy in packaging and miscellaneous scripts
|
||||||
package_scripts(build_root)
|
package_scripts(build_root)
|
||||||
|
# Copy newly-built binaries to packaging directory
|
||||||
for binary in targets:
|
for b in targets:
|
||||||
if platform == 'windows':
|
if p == 'windows':
|
||||||
# For windows, we just want to copy the binary into the root directory
|
b = b + '.exe'
|
||||||
binary = binary + '.exe'
|
fr = os.path.join(current_location, b)
|
||||||
# Where the binary should go in the package filesystem
|
to = os.path.join(build_root, b)
|
||||||
to = os.path.join(build_root, binary)
|
print("\t- [{}][{}] - Moving from '{}' to '{}'".format(p, a, fr, to))
|
||||||
# Where the binary currently is located
|
|
||||||
fr = os.path.join(current_location, binary)
|
|
||||||
else:
|
|
||||||
# Where the binary currently is located
|
|
||||||
fr = os.path.join(current_location, binary)
|
|
||||||
# Where the binary should go in the package filesystem
|
|
||||||
to = os.path.join(build_root, INSTALL_ROOT_DIR[1:], binary)
|
|
||||||
|
|
||||||
if debug:
|
|
||||||
print("[{}][{}] - Moving from '{}' to '{}'".format(platform,
|
|
||||||
arch,
|
|
||||||
fr,
|
|
||||||
to))
|
|
||||||
copy_file(fr, to)
|
copy_file(fr, to)
|
||||||
|
# Package the directory structure
|
||||||
for package_type in supported_packages[platform]:
|
for package_type in supported_packages[p]:
|
||||||
# Package the directory structure for each package type for the platform
|
print("\t- Packaging directory '{}' as '{}'...".format(build_root, package_type))
|
||||||
print("Packaging directory '{}' as '{}'...".format(build_root, package_type))
|
name = "telegraf"
|
||||||
name = PACKAGE_NAME
|
|
||||||
# Reset version, iteration, and current location on each run
|
# Reset version, iteration, and current location on each run
|
||||||
# since they may be modified below.
|
# since they may be modified below.
|
||||||
package_version = version
|
package_version = version
|
||||||
package_iteration = iteration
|
package_iteration = iteration
|
||||||
package_build_root = build_root
|
current_location = build_output[p][a]
|
||||||
current_location = build_output[platform][arch]
|
|
||||||
if rc is not None:
|
|
||||||
# Set iteration to 0 since it's a release candidate
|
|
||||||
package_iteration = "0.rc{}".format(rc)
|
|
||||||
|
|
||||||
if package_type in ['zip', 'tar']:
|
if package_type in ['zip', 'tar']:
|
||||||
# For tars and zips, start the packaging one folder above
|
|
||||||
# the build root (to include the package name)
|
|
||||||
package_build_root = os.path.join('/', '/'.join(build_root.split('/')[:-1]))
|
|
||||||
if nightly:
|
if nightly:
|
||||||
name = '{}-nightly_{}_{}'.format(name,
|
name = '{}-nightly_{}_{}'.format(name, p, a)
|
||||||
platform,
|
|
||||||
arch)
|
|
||||||
else:
|
else:
|
||||||
name = '{}-{}-{}_{}_{}'.format(name,
|
name = '{}-{}-{}_{}_{}'.format(name, package_version, package_iteration, p, a)
|
||||||
package_version,
|
if package_type == 'tar':
|
||||||
package_iteration,
|
# Add `tar.gz` to path to reduce package size
|
||||||
platform,
|
current_location = os.path.join(current_location, name + '.tar.gz')
|
||||||
arch)
|
if rc is not None:
|
||||||
|
package_iteration = "0.rc{}".format(rc)
|
||||||
current_location = os.path.join(os.getcwd(), current_location)
|
saved_a = a
|
||||||
if package_type == 'tar':
|
if pkg_arch is not None:
|
||||||
tar_command = "cd {} && tar -cvzf {}.tar.gz ./*".format(build_root, name)
|
a = pkg_arch
|
||||||
run(tar_command, shell=True)
|
if a == '386':
|
||||||
run("mv {}.tar.gz {}".format(os.path.join(build_root, name), current_location), shell=True)
|
a = 'i386'
|
||||||
outfile = os.path.join(current_location, name + ".tar.gz")
|
fpm_command = "fpm {} --name {} -a {} -t {} --version {} --iteration {} -C {} -p {} ".format(
|
||||||
outfiles.append(outfile)
|
fpm_common_args,
|
||||||
print("MD5({}) = {}".format(outfile, generate_md5_from_file(outfile)))
|
name,
|
||||||
elif package_type == 'zip':
|
a,
|
||||||
zip_command = "cd {} && zip -r {}.zip ./*".format(build_root, name)
|
package_type,
|
||||||
run(zip_command, shell=True)
|
package_version,
|
||||||
run("mv {}.zip {}".format(os.path.join(build_root, name), current_location), shell=True)
|
package_iteration,
|
||||||
outfile = os.path.join(current_location, name + ".zip")
|
build_root,
|
||||||
outfiles.append(outfile)
|
current_location)
|
||||||
print("MD5({}) = {}".format(outfile, generate_md5_from_file(outfile)))
|
if pkg_arch is not None:
|
||||||
|
a = saved_a
|
||||||
|
if package_type == "rpm":
|
||||||
|
fpm_command += "--depends coreutils "
|
||||||
|
fpm_command += "--depends lsof"
|
||||||
|
print(fpm_command)
|
||||||
|
out = run(fpm_command, shell=True)
|
||||||
|
matches = re.search(':path=>"(.*)"', out)
|
||||||
|
outfile = None
|
||||||
|
if matches is not None:
|
||||||
|
outfile = matches.groups()[0]
|
||||||
|
if outfile is None:
|
||||||
|
print("[ COULD NOT DETERMINE OUTPUT ]")
|
||||||
else:
|
else:
|
||||||
fpm_command = "fpm {} --name {} -a {} -t {} --version {} --iteration {} -C {} -p {} ".format(fpm_common_args,
|
# Strip nightly version (the unix epoch) from filename
|
||||||
name,
|
if nightly and package_type in ['deb', 'rpm']:
|
||||||
arch,
|
outfile = rename_file(outfile, outfile.replace("{}-{}".format(version, iteration), "nightly"))
|
||||||
package_type,
|
outfiles.append(os.path.join(os.getcwd(), outfile))
|
||||||
package_version,
|
# Display MD5 hash for generated package
|
||||||
package_iteration,
|
print("\t\tMD5 = {}".format(generate_md5_from_file(outfile)))
|
||||||
package_build_root,
|
|
||||||
current_location)
|
|
||||||
if debug:
|
|
||||||
fpm_command += "--verbose "
|
|
||||||
if package_type == "rpm":
|
|
||||||
fpm_command += "--depends coreutils "
|
|
||||||
fpm_command += "--depends lsof "
|
|
||||||
out = run(fpm_command, shell=True)
|
|
||||||
matches = re.search(':path=>"(.*)"', out)
|
|
||||||
outfile = None
|
|
||||||
if matches is not None:
|
|
||||||
outfile = matches.groups()[0]
|
|
||||||
if outfile is None:
|
|
||||||
print("!! Could not determine output from packaging command.")
|
|
||||||
else:
|
|
||||||
# Strip nightly version (the unix epoch) from filename
|
|
||||||
if nightly:
|
|
||||||
outfile = rename_file(outfile, outfile.replace("{}-{}".format(version, iteration), "nightly"))
|
|
||||||
outfiles.append(os.path.join(os.getcwd(), outfile))
|
|
||||||
# Display MD5 hash for generated package
|
|
||||||
print("MD5({}) = {}".format(outfile, generate_md5_from_file(outfile)))
|
|
||||||
print("")
|
print("")
|
||||||
if debug:
|
if debug:
|
||||||
print("[DEBUG] package outfiles: {}".format(outfiles))
|
print("[DEBUG] package outfiles: {}".format(outfiles))
|
||||||
return outfiles
|
return outfiles
|
||||||
finally:
|
finally:
|
||||||
# Cleanup
|
# Cleanup
|
||||||
print("Cleaning up build dir: {}".format(tmp_build_dir))
|
|
||||||
shutil.rmtree(tmp_build_dir)
|
shutil.rmtree(tmp_build_dir)
|
||||||
|
|
||||||
def print_usage():
|
def print_usage():
|
||||||
@@ -589,9 +481,11 @@ def print_usage():
|
|||||||
print("")
|
print("")
|
||||||
print("Options:")
|
print("Options:")
|
||||||
print("\t --outdir=<path> \n\t\t- Send build output to a specified path. Defaults to ./build.")
|
print("\t --outdir=<path> \n\t\t- Send build output to a specified path. Defaults to ./build.")
|
||||||
print("\t --arch=<arch> \n\t\t- Build for specified architecture. Acceptable values: x86_64|amd64, 386|i386, arm, or all")
|
print("\t --arch=<arch> \n\t\t- Build for specified architecture. Acceptable values: x86_64|amd64, 386, arm, or all")
|
||||||
|
print("\t --goarm=<arm version> \n\t\t- Build for specified ARM version (when building for ARM). Default value is: 6")
|
||||||
print("\t --platform=<platform> \n\t\t- Build for specified platform. Acceptable values: linux, windows, darwin, or all")
|
print("\t --platform=<platform> \n\t\t- Build for specified platform. Acceptable values: linux, windows, darwin, or all")
|
||||||
print("\t --version=<version> \n\t\t- Version information to apply to build metadata. If not specified, will be pulled from repo tag.")
|
print("\t --version=<version> \n\t\t- Version information to apply to build metadata. If not specified, will be pulled from repo tag.")
|
||||||
|
print("\t --pkgarch=<package-arch> \n\t\t- Package architecture if different from <arch>")
|
||||||
print("\t --commit=<commit> \n\t\t- Use specific commit for build (currently a NOOP).")
|
print("\t --commit=<commit> \n\t\t- Use specific commit for build (currently a NOOP).")
|
||||||
print("\t --branch=<branch> \n\t\t- Build from a specific branch (currently a NOOP).")
|
print("\t --branch=<branch> \n\t\t- Build from a specific branch (currently a NOOP).")
|
||||||
print("\t --rc=<rc number> \n\t\t- Whether or not the build is a release candidate (affects version information).")
|
print("\t --rc=<rc number> \n\t\t- Whether or not the build is a release candidate (affects version information).")
|
||||||
@@ -599,13 +493,9 @@ def print_usage():
|
|||||||
print("\t --race \n\t\t- Whether the produced build should have race detection enabled.")
|
print("\t --race \n\t\t- Whether the produced build should have race detection enabled.")
|
||||||
print("\t --package \n\t\t- Whether the produced builds should be packaged for the target platform(s).")
|
print("\t --package \n\t\t- Whether the produced builds should be packaged for the target platform(s).")
|
||||||
print("\t --nightly \n\t\t- Whether the produced build is a nightly (affects version information).")
|
print("\t --nightly \n\t\t- Whether the produced build is a nightly (affects version information).")
|
||||||
print("\t --update \n\t\t- Whether dependencies should be updated prior to building.")
|
|
||||||
print("\t --test \n\t\t- Run Go tests. Will not produce a build.")
|
|
||||||
print("\t --parallel \n\t\t- Run Go tests in parallel up to the count specified.")
|
print("\t --parallel \n\t\t- Run Go tests in parallel up to the count specified.")
|
||||||
print("\t --generate \n\t\t- Run `go generate`.")
|
|
||||||
print("\t --timeout \n\t\t- Timeout for Go tests. Defaults to 480s.")
|
print("\t --timeout \n\t\t- Timeout for Go tests. Defaults to 480s.")
|
||||||
print("\t --clean \n\t\t- Clean the build output directory prior to creating build.")
|
print("\t --clean \n\t\t- Clean the build output directory prior to creating build.")
|
||||||
print("\t --no-get \n\t\t- Do not run `go get` before building.")
|
|
||||||
print("\t --bucket=<S3 bucket>\n\t\t- Full path of the bucket to upload packages to (must also specify --upload).")
|
print("\t --bucket=<S3 bucket>\n\t\t- Full path of the bucket to upload packages to (must also specify --upload).")
|
||||||
print("\t --debug \n\t\t- Displays debug output.")
|
print("\t --debug \n\t\t- Displays debug output.")
|
||||||
print("")
|
print("")
|
||||||
@@ -614,18 +504,17 @@ def print_package_summary(packages):
|
|||||||
print(packages)
|
print(packages)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global debug
|
|
||||||
|
|
||||||
# Command-line arguments
|
# Command-line arguments
|
||||||
outdir = "build"
|
outdir = "build"
|
||||||
commit = None
|
commit = None
|
||||||
target_platform = None
|
target_platform = None
|
||||||
target_arch = None
|
target_arch = None
|
||||||
|
package_arch = None
|
||||||
nightly = False
|
nightly = False
|
||||||
race = False
|
race = False
|
||||||
branch = None
|
branch = None
|
||||||
version = get_current_version()
|
version = get_current_version()
|
||||||
rc = get_current_rc()
|
rc = None
|
||||||
package = False
|
package = False
|
||||||
update = False
|
update = False
|
||||||
clean = False
|
clean = False
|
||||||
@@ -635,15 +524,15 @@ def main():
|
|||||||
timeout = None
|
timeout = None
|
||||||
iteration = 1
|
iteration = 1
|
||||||
no_vet = False
|
no_vet = False
|
||||||
|
goarm_version = "6"
|
||||||
run_get = True
|
run_get = True
|
||||||
upload_bucket = None
|
upload_bucket = None
|
||||||
generate = False
|
global debug
|
||||||
no_stash = False
|
|
||||||
|
|
||||||
for arg in sys.argv[1:]:
|
for arg in sys.argv[1:]:
|
||||||
if '--outdir' in arg:
|
if '--outdir' in arg:
|
||||||
# Output directory. If none is specified, then builds will be placed in the same directory.
|
# Output directory. If none is specified, then builds will be placed in the same directory.
|
||||||
outdir = arg.split("=")[1]
|
output_dir = arg.split("=")[1]
|
||||||
if '--commit' in arg:
|
if '--commit' in arg:
|
||||||
# Commit to build from. If none is specified, then it will build from the most recent commit.
|
# Commit to build from. If none is specified, then it will build from the most recent commit.
|
||||||
commit = arg.split("=")[1]
|
commit = arg.split("=")[1]
|
||||||
@@ -659,6 +548,9 @@ def main():
|
|||||||
elif '--version' in arg:
|
elif '--version' in arg:
|
||||||
# Version to assign to this build (0.9.5, etc)
|
# Version to assign to this build (0.9.5, etc)
|
||||||
version = arg.split("=")[1]
|
version = arg.split("=")[1]
|
||||||
|
elif '--pkgarch' in arg:
|
||||||
|
# Package architecture if different from <arch> (armhf, etc)
|
||||||
|
package_arch = arg.split("=")[1]
|
||||||
elif '--rc' in arg:
|
elif '--rc' in arg:
|
||||||
# Signifies that this is a release candidate build.
|
# Signifies that this is a release candidate build.
|
||||||
rc = arg.split("=")[1]
|
rc = arg.split("=")[1]
|
||||||
@@ -668,20 +560,12 @@ def main():
|
|||||||
elif '--package' in arg:
|
elif '--package' in arg:
|
||||||
# Signifies that packages should be built.
|
# Signifies that packages should be built.
|
||||||
package = True
|
package = True
|
||||||
# If packaging do not allow stashing of local changes
|
|
||||||
no_stash = True
|
|
||||||
elif '--nightly' in arg:
|
elif '--nightly' in arg:
|
||||||
# Signifies that this is a nightly build.
|
# Signifies that this is a nightly build.
|
||||||
nightly = True
|
nightly = True
|
||||||
elif '--update' in arg:
|
|
||||||
# Signifies that dependencies should be updated.
|
|
||||||
update = True
|
|
||||||
elif '--upload' in arg:
|
elif '--upload' in arg:
|
||||||
# Signifies that the resulting packages should be uploaded to S3
|
# Signifies that the resulting packages should be uploaded to S3
|
||||||
upload = True
|
upload = True
|
||||||
elif '--test' in arg:
|
|
||||||
# Run tests and exit
|
|
||||||
test = True
|
|
||||||
elif '--parallel' in arg:
|
elif '--parallel' in arg:
|
||||||
# Set parallel for tests.
|
# Set parallel for tests.
|
||||||
parallel = int(arg.split("=")[1])
|
parallel = int(arg.split("=")[1])
|
||||||
@@ -695,19 +579,14 @@ def main():
|
|||||||
iteration = arg.split("=")[1]
|
iteration = arg.split("=")[1]
|
||||||
elif '--no-vet' in arg:
|
elif '--no-vet' in arg:
|
||||||
no_vet = True
|
no_vet = True
|
||||||
elif '--no-get' in arg:
|
elif '--goarm' in arg:
|
||||||
run_get = False
|
# Signifies GOARM flag to pass to build command when compiling for ARM
|
||||||
|
goarm_version = arg.split("=")[1]
|
||||||
elif '--bucket' in arg:
|
elif '--bucket' in arg:
|
||||||
# The bucket to upload the packages to, relies on boto
|
# The bucket to upload the packages to, relies on boto
|
||||||
upload_bucket = arg.split("=")[1]
|
upload_bucket = arg.split("=")[1]
|
||||||
elif '--no-stash' in arg:
|
|
||||||
# Do not stash uncommited changes
|
|
||||||
# Fail if uncommited changes exist
|
|
||||||
no_stash = True
|
|
||||||
elif '--generate' in arg:
|
|
||||||
generate = True
|
|
||||||
elif '--debug' in arg:
|
elif '--debug' in arg:
|
||||||
print("[DEBUG] Using debug output")
|
print "[DEBUG] Using debug output"
|
||||||
debug = True
|
debug = True
|
||||||
elif '--help' in arg:
|
elif '--help' in arg:
|
||||||
print_usage()
|
print_usage()
|
||||||
@@ -717,69 +596,54 @@ def main():
|
|||||||
print_usage()
|
print_usage()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if nightly and rc:
|
|
||||||
print("!! Cannot be both nightly and a release candidate! Stopping.")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if nightly:
|
if nightly:
|
||||||
# In order to cleanly delineate nightly version, we are adding the epoch timestamp
|
if rc:
|
||||||
|
print("!! Cannot be both nightly and a release candidate! Stopping.")
|
||||||
|
return 1
|
||||||
|
# In order to support nightly builds on the repository, we are adding the epoch timestamp
|
||||||
# to the version so that version numbers are always greater than the previous nightly.
|
# to the version so that version numbers are always greater than the previous nightly.
|
||||||
version = "{}~n{}".format(version, int(time.time()))
|
version = "{}.n{}".format(version, int(time.time()))
|
||||||
iteration = 0
|
|
||||||
elif rc:
|
|
||||||
iteration = 0
|
|
||||||
|
|
||||||
# Pre-build checks
|
# Pre-build checks
|
||||||
check_environ()
|
check_environ()
|
||||||
if not check_prereqs():
|
check_prereqs()
|
||||||
return 1
|
|
||||||
|
|
||||||
if not commit:
|
if not commit:
|
||||||
commit = get_current_commit(short=True)
|
commit = get_current_commit(short=True)
|
||||||
if not branch:
|
if not branch:
|
||||||
branch = get_current_branch()
|
branch = get_current_branch()
|
||||||
if not target_arch:
|
if not target_arch:
|
||||||
system_arch = get_system_arch()
|
if 'arm' in get_system_arch():
|
||||||
if 'arm' in system_arch:
|
|
||||||
# Prevent uname from reporting ARM arch (eg 'armv7l')
|
# Prevent uname from reporting ARM arch (eg 'armv7l')
|
||||||
target_arch = "arm"
|
target_arch = "arm"
|
||||||
else:
|
else:
|
||||||
target_arch = system_arch
|
target_arch = get_system_arch()
|
||||||
if target_arch == '386':
|
if not target_platform:
|
||||||
target_arch = 'i386'
|
|
||||||
elif target_arch == 'x86_64':
|
|
||||||
target_arch = 'amd64'
|
|
||||||
if target_platform:
|
|
||||||
if target_platform not in supported_builds and target_platform != 'all':
|
|
||||||
print("! Invalid build platform: {}".format(target_platform))
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
target_platform = get_system_platform()
|
target_platform = get_system_platform()
|
||||||
|
if rc or nightly:
|
||||||
|
# If a release candidate or nightly, set iteration to 0 (instead of 1)
|
||||||
|
iteration = 0
|
||||||
|
|
||||||
|
if target_arch == '386':
|
||||||
|
target_arch = 'i386'
|
||||||
|
elif target_arch == 'x86_64':
|
||||||
|
target_arch = 'amd64'
|
||||||
|
|
||||||
build_output = {}
|
build_output = {}
|
||||||
|
|
||||||
if generate:
|
go_get()
|
||||||
if not run_generate():
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if run_get:
|
|
||||||
if not go_get(branch, update=update, no_stash=no_stash):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if test:
|
|
||||||
if not run_tests(race, parallel, timeout, no_vet):
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
platforms = []
|
platforms = []
|
||||||
single_build = True
|
single_build = True
|
||||||
if target_platform == 'all':
|
if target_platform == 'all':
|
||||||
platforms = supported_builds.keys()
|
platforms = list(supported_builds.keys())
|
||||||
single_build = False
|
single_build = False
|
||||||
else:
|
else:
|
||||||
platforms = [target_platform]
|
platforms = [target_platform]
|
||||||
|
|
||||||
for platform in platforms:
|
for platform in platforms:
|
||||||
|
if platform in prereq_cmds:
|
||||||
|
run(prereq_cmds[platform])
|
||||||
build_output.update( { platform : {} } )
|
build_output.update( { platform : {} } )
|
||||||
archs = []
|
archs = []
|
||||||
if target_arch == "all":
|
if target_arch == "all":
|
||||||
@@ -787,34 +651,32 @@ def main():
|
|||||||
archs = supported_builds.get(platform)
|
archs = supported_builds.get(platform)
|
||||||
else:
|
else:
|
||||||
archs = [target_arch]
|
archs = [target_arch]
|
||||||
|
|
||||||
for arch in archs:
|
for arch in archs:
|
||||||
od = outdir
|
od = outdir
|
||||||
if not single_build:
|
if not single_build:
|
||||||
od = os.path.join(outdir, platform, arch)
|
od = os.path.join(outdir, platform, arch)
|
||||||
if build(version=version,
|
build(version=version,
|
||||||
branch=branch,
|
branch=branch,
|
||||||
commit=commit,
|
commit=commit,
|
||||||
platform=platform,
|
platform=platform,
|
||||||
arch=arch,
|
arch=arch,
|
||||||
nightly=nightly,
|
nightly=nightly,
|
||||||
rc=rc,
|
rc=rc,
|
||||||
race=race,
|
race=race,
|
||||||
clean=clean,
|
clean=clean,
|
||||||
outdir=od):
|
outdir=od,
|
||||||
return 1
|
goarm_version=goarm_version)
|
||||||
build_output.get(platform).update( { arch : od } )
|
build_output.get(platform).update( { arch : od } )
|
||||||
|
|
||||||
# Build packages
|
# Build packages
|
||||||
if package:
|
if package:
|
||||||
if not check_path_for("fpm"):
|
if not check_path_for("fpm"):
|
||||||
print("!! Cannot package without command 'fpm'.")
|
print("!! Cannot package without command 'fpm'. Stopping.")
|
||||||
return 1
|
return 1
|
||||||
|
packages = build_packages(build_output, version, package_arch, nightly=nightly, rc=rc, iteration=iteration)
|
||||||
packages = build_packages(build_output, version, nightly=nightly, rc=rc, iteration=iteration)
|
# Optionally upload to S3
|
||||||
if upload:
|
if upload:
|
||||||
upload_packages(packages, bucket_name=upload_bucket, nightly=nightly)
|
upload_packages(packages, bucket_name=upload_bucket, nightly=nightly)
|
||||||
print("Done!")
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ telegraf -sample-config > $tmpdir/config.toml
|
|||||||
exit_if_fail telegraf -config $tmpdir/config.toml \
|
exit_if_fail telegraf -config $tmpdir/config.toml \
|
||||||
-test -input-filter cpu:mem
|
-test -input-filter cpu:mem
|
||||||
|
|
||||||
cat $GOPATH/bin/telegraf | gzip > $CIRCLE_ARTIFACTS/telegraf.gz
|
mv $GOPATH/bin/telegraf $CIRCLE_ARTIFACTS
|
||||||
|
|
||||||
eval "git describe --exact-match HEAD"
|
eval "git describe --exact-match HEAD"
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
@@ -77,6 +77,5 @@ if [ $? -eq 0 ]; then
|
|||||||
echo $tag
|
echo $tag
|
||||||
exit_if_fail ./scripts/build.py --package --version=$tag --platform=linux --arch=all --upload
|
exit_if_fail ./scripts/build.py --package --version=$tag --platform=linux --arch=all --upload
|
||||||
exit_if_fail ./scripts/build.py --package --version=$tag --platform=windows --arch=all --upload
|
exit_if_fail ./scripts/build.py --package --version=$tag --platform=windows --arch=all --upload
|
||||||
exit_if_fail ./scripts/build.py --package --version=$tag --platform=freebsd --arch=all --upload
|
|
||||||
mv build $CIRCLE_ARTIFACTS
|
mv build $CIRCLE_ARTIFACTS
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -159,22 +159,6 @@ case $1 in
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
reload)
|
|
||||||
# Reload the daemon.
|
|
||||||
if [ -e $pidfile ]; then
|
|
||||||
pidofproc -p $pidfile $daemon > /dev/null 2>&1 && status="0" || status="$?"
|
|
||||||
if [ "$status" = 0 ]; then
|
|
||||||
if killproc -p $pidfile SIGHUP; then
|
|
||||||
log_success_msg "$name process was reloaded"
|
|
||||||
else
|
|
||||||
log_failure_msg "$name failed to reload service"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log_failure_msg "$name process is not running"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
restart)
|
restart)
|
||||||
# Restart the daemon.
|
# Restart the daemon.
|
||||||
$0 stop && sleep 2 && $0 start
|
$0 stop && sleep 2 && $0 start
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ function install_init {
|
|||||||
function install_systemd {
|
function install_systemd {
|
||||||
cp -f $SCRIPT_DIR/telegraf.service /lib/systemd/system/telegraf.service
|
cp -f $SCRIPT_DIR/telegraf.service /lib/systemd/system/telegraf.service
|
||||||
systemctl enable telegraf
|
systemctl enable telegraf
|
||||||
systemctl daemon-reload || true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_update_rcd {
|
function install_update_rcd {
|
||||||
@@ -29,9 +28,7 @@ if [[ $? -ne 0 ]]; then
|
|||||||
useradd --system -U -M telegraf -s /bin/false -d /etc/telegraf
|
useradd --system -U -M telegraf -s /bin/false -d /etc/telegraf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
test -d $LOG_DIR || mkdir -p $LOG_DIR
|
|
||||||
chown -R -L telegraf:telegraf $LOG_DIR
|
chown -R -L telegraf:telegraf $LOG_DIR
|
||||||
chmod 755 $LOG_DIR
|
|
||||||
|
|
||||||
# Remove legacy symlink, if it exists
|
# Remove legacy symlink, if it exists
|
||||||
if [[ -L /etc/init.d/telegraf ]]; then
|
if [[ -L /etc/init.d/telegraf ]]; then
|
||||||
@@ -64,12 +61,10 @@ elif [[ -f /etc/debian_version ]]; then
|
|||||||
which systemctl &>/dev/null
|
which systemctl &>/dev/null
|
||||||
if [[ $? -eq 0 ]]; then
|
if [[ $? -eq 0 ]]; then
|
||||||
install_systemd
|
install_systemd
|
||||||
deb-systemd-invoke restart telegraf.service
|
|
||||||
else
|
else
|
||||||
# Assuming sysv
|
# Assuming sysv
|
||||||
install_init
|
install_init
|
||||||
install_update_rcd
|
install_update_rcd
|
||||||
invoke-rc.d telegraf restart
|
|
||||||
fi
|
fi
|
||||||
elif [[ -f /etc/os-release ]]; then
|
elif [[ -f /etc/os-release ]]; then
|
||||||
source /etc/os-release
|
source /etc/os-release
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function disable_systemd {
|
|
||||||
systemctl disable telegraf
|
|
||||||
rm -f /lib/systemd/system/telegraf.service
|
|
||||||
}
|
|
||||||
|
|
||||||
function disable_update_rcd {
|
|
||||||
update-rc.d -f telegraf remove
|
|
||||||
rm -f /etc/init.d/telegraf
|
|
||||||
}
|
|
||||||
|
|
||||||
function disable_chkconfig {
|
|
||||||
chkconfig --del telegraf
|
|
||||||
rm -f /etc/init.d/telegraf
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ -f /etc/redhat-release ]]; then
|
|
||||||
# RHEL-variant logic
|
|
||||||
if [[ "$1" = "0" ]]; then
|
|
||||||
# InfluxDB is no longer installed, remove from init system
|
|
||||||
rm -f /etc/default/telegraf
|
|
||||||
|
|
||||||
which systemctl &>/dev/null
|
|
||||||
if [[ $? -eq 0 ]]; then
|
|
||||||
disable_systemd
|
|
||||||
else
|
|
||||||
# Assuming sysv
|
|
||||||
disable_chkconfig
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
elif [[ -f /etc/debian_version ]]; then
|
|
||||||
# Debian/Ubuntu logic
|
|
||||||
if [[ "$1" != "upgrade" ]]; then
|
|
||||||
# Remove/purge
|
|
||||||
rm -f /etc/default/telegraf
|
|
||||||
|
|
||||||
which systemctl &>/dev/null
|
|
||||||
if [[ $? -eq 0 ]]; then
|
|
||||||
disable_systemd
|
|
||||||
else
|
|
||||||
# Assuming sysv
|
|
||||||
disable_update_rcd
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
BIN_DIR=/usr/bin
|
|
||||||
|
|
||||||
# Distribution-specific logic
|
|
||||||
if [[ -f /etc/debian_version ]]; then
|
|
||||||
# Debian/Ubuntu logic
|
|
||||||
which systemctl &>/dev/null
|
|
||||||
if [[ $? -eq 0 ]]; then
|
|
||||||
deb-systemd-invoke stop telegraf.service
|
|
||||||
else
|
|
||||||
# Assuming sysv
|
|
||||||
invoke-rc.d telegraf stop
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -7,7 +7,6 @@ After=network.target
|
|||||||
EnvironmentFile=-/etc/default/telegraf
|
EnvironmentFile=-/etc/default/telegraf
|
||||||
User=telegraf
|
User=telegraf
|
||||||
ExecStart=/usr/bin/telegraf -config /etc/telegraf/telegraf.conf -config-directory /etc/telegraf/telegraf.d ${TELEGRAF_OPTS}
|
ExecStart=/usr/bin/telegraf -config /etc/telegraf/telegraf.conf -config-directory /etc/telegraf/telegraf.d ${TELEGRAF_OPTS}
|
||||||
ExecReload=/bin/kill -HUP $MAINPID
|
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
KillMode=process
|
KillMode=process
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,13 @@ func (a *Accumulator) AssertContainsTaggedFields(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if p.Measurement == measurement {
|
if p.Measurement == measurement {
|
||||||
assert.Equal(t, fields, p.Fields)
|
if !reflect.DeepEqual(fields, p.Fields) {
|
||||||
|
pActual, _ := json.MarshalIndent(p.Fields, "", " ")
|
||||||
|
pExp, _ := json.MarshalIndent(fields, "", " ")
|
||||||
|
msg := fmt.Sprintf("Actual:\n%s\n(%T) \nExpected:\n%s\n(%T)",
|
||||||
|
string(pActual), p.Fields, string(pExp), fields)
|
||||||
|
assert.Fail(t, msg)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,7 +156,13 @@ func (a *Accumulator) AssertContainsFields(
|
|||||||
defer a.Unlock()
|
defer a.Unlock()
|
||||||
for _, p := range a.Metrics {
|
for _, p := range a.Metrics {
|
||||||
if p.Measurement == measurement {
|
if p.Measurement == measurement {
|
||||||
assert.Equal(t, fields, p.Fields)
|
if !reflect.DeepEqual(fields, p.Fields) {
|
||||||
|
pActual, _ := json.MarshalIndent(p.Fields, "", " ")
|
||||||
|
pExp, _ := json.MarshalIndent(fields, "", " ")
|
||||||
|
msg := fmt.Sprintf("Actual:\n%s\n(%T) \nExpected:\n%s\n(%T)",
|
||||||
|
string(pActual), p.Fields, string(pExp), fields)
|
||||||
|
assert.Fail(t, msg)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user