pax_global_header00006660000000000000000000000064125720370170014516gustar00rootroot0000000000000052 comment=73eb394844b00bc5a463fbebc1017eda7405f088 prometheus-node-exporter-0.11.0+ds/000077500000000000000000000000001257203701700171635ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/.gitignore000066400000000000000000000004631257203701700211560ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe dependencies-stamp node_exporter .build .deps *.tar.gz prometheus-node-exporter-0.11.0+ds/.travis.yml000066400000000000000000000000431257203701700212710ustar00rootroot00000000000000language: go script: - make test prometheus-node-exporter-0.11.0+ds/AUTHORS.md000066400000000000000000000010561257203701700206340ustar00rootroot00000000000000The Prometheus project was started by Matt T. Proud (emeritus) and Julius Volz in 2012. Maintainers of this repository: * Brian Brazil * Johannes 'fish' Ziemke * Tobias Schmidt The following individuals have contributed code to this repository (listed in alphabetical order): * Björn Rabenstein * Brian Brazil * Johannes 'fish' Ziemke * Julius Volz * Tobias Schmidt prometheus-node-exporter-0.11.0+ds/CHANGELOG.md000066400000000000000000000021441257203701700207750ustar00rootroot00000000000000## 0.11.0 / 2015-07-27 * [FEATURE] Add stats from /proc/net/snmp. * [FEATURE] Add support for FreeBSD. * [FEATURE] Allow netdev devices to be ignored. * [MAINTENANCE] New Dockerfile for unified way to dockerize Prometheus exporters. * [FEATURE] Add device,fstype collection to the filesystem exporter. * [IMPROVEMENT] Make logging of collector executions less verbose. ## 0.10.0 / 2015-06-10 * [CHANGE] Change logging output format and flags. ## 0.9.0 / 2015-05-26 * [BUGFIX] Fix `/proc/net/dev` parsing. * [CLEANUP] Remove the `attributes` collector, use `textfile` instead. * [CLEANUP] Replace last uses of the configuration file with flags. * [IMPROVEMENT] Remove cgo dependency. * [IMPROVEMENT] Sort collector names when printing. * [FEATURE] IPVS stats collector. ## 0.8.1 / 2015-05-17 * [MAINTENANCE] Use the common Prometheus build infrastructure. * [MAINTENANCE] Update former Google Code imports. * [IMPROVEMENT] Log the version at startup. * [FEATURE] TCP stats collector ## 0.8.0 / 2015-03-09 * [CLEANUP] Introduced semantic versioning and changelog. From now on, changes will be reported in this file. prometheus-node-exporter-0.11.0+ds/CONTRIBUTING.md000066400000000000000000000015331257203701700214160ustar00rootroot00000000000000# Contributing Prometheus uses GitHub to manage reviews of pull requests. * If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) one or more of the maintainers (see [AUTHORS.md](AUTHORS.md)) in the description of the pull request. * If you plan to do something more involved, first discuss your ideas on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers). This will avoid unnecessary work and surely give you and us a good deal of inspiration. * Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). prometheus-node-exporter-0.11.0+ds/Dockerfile000066400000000000000000000002211257203701700211500ustar00rootroot00000000000000FROM sdurrheimer/alpine-golang-make-onbuild MAINTAINER The Prometheus Authors EXPOSE 9100 prometheus-node-exporter-0.11.0+ds/LICENSE000066400000000000000000000261351257203701700201770ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. prometheus-node-exporter-0.11.0+ds/Makefile000066400000000000000000000012751257203701700206300ustar00rootroot00000000000000# Copyright 2015 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. VERSION := 0.11.0 TARGET := node_exporter GOFLAGS := -ldflags "-X main.Version $(VERSION)" include Makefile.COMMON prometheus-node-exporter-0.11.0+ds/Makefile.COMMON000066400000000000000000000075451257203701700216250ustar00rootroot00000000000000# Copyright 2015 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # THE AUTHORITATIVE VERSION OF THIS MAKEFILE LIVES IN: # # https://github.com/prometheus/utils # # PLEASE MAKE ANY CHANGES THERE AND PROPAGATE THEM TO ALL PROMETHEUS # REPOSITORIES THAT ARE USING THIS MAKEFILE. # # This file provides common Makefile infrastructure for several Prometheus # components. This includes make tasks for downloading Go, setting up a # self-contained build environment, fetching Go dependencies, building # binaries, running tests, and doing release management. This file is intended # to be included from a project's Makefile, which needs to define the following # variables, at a minimum: # # * VERSION - The current version of the project in question. # * TARGET - The desired name of the built binary. # # Many of the variables defined below are defined conditionally (using '?'), # which allows the project's main Makefile to override any of these settings, if # needed. See also: # # https://www.gnu.org/software/make/manual/html_node/Flavors.html#Flavors. # # The including Makefile may define any number of extra targets that are # specific to that project. VERSION ?= $(error VERSION not set in including Makefile) TARGET ?= $(error TARGET not set in including Makefile) SRC ?= $(shell find . -type f -name "*.go" ! -path "./.build/*") GOOS ?= $(shell uname | tr A-Z a-z) GOARCH ?= $(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m))) ifeq ($(GOOS),darwin) RELEASE_SUFFIX ?= -osx$(shell sw_vers -productVersion) endif GO_VERSION ?= 1.4.2 GOURL ?= https://golang.org/dl GOPKG ?= go$(GO_VERSION).$(GOOS)-$(GOARCH)$(RELEASE_SUFFIX).tar.gz GOPATH := $(CURDIR)/.build/gopath # Check for the correct version of go in the path. If we find it, use it. # Otherwise, prepare to build go locally. ifeq ($(shell command -v "go" >/dev/null && go version | sed -e 's/^[^0-9.]*\([0-9.]*\).*/\1/'), $(GO_VERSION)) GOCC ?= $(shell command -v "go") GOFMT ?= $(shell command -v "gofmt") GO ?= GOPATH=$(GOPATH) $(GOCC) else GOROOT ?= $(CURDIR)/.build/go$(GO_VERSION) GOCC ?= $(GOROOT)/bin/go GOFMT ?= $(GOROOT)/bin/gofmt GO ?= GOROOT=$(GOROOT) GOPATH=$(GOPATH) $(GOCC) endif # Never honor GOBIN, should it be set at all. unexport GOBIN SUFFIX ?= $(GOOS)-$(GOARCH) BINARY ?= $(TARGET) ARCHIVE ?= $(TARGET)-$(VERSION).$(SUFFIX).tar.gz ROOTPKG ?= github.com/prometheus/$(TARGET) SELFLINK ?= $(GOPATH)/src/$(ROOTPKG) default: $(BINARY) $(GOCC): @echo Go version $(GO_VERSION) required but not found in PATH. @echo About to download and install go$(GO_VERSION) to $(GOROOT) @echo Abort now if you want to manually install it system-wide instead. @echo @sleep 5 mkdir -p $(GOROOT) curl -L $(GOURL)/$(GOPKG) | tar -C $(GOROOT) --strip 1 -xz $(SELFLINK): mkdir -p $(dir $@) ln -s $(CURDIR) $@ dependencies-stamp: $(GOCC) $(SRC) | $(SELFLINK) $(GO) get -d touch $@ $(BINARY): $(GOCC) $(SRC) dependencies-stamp Makefile Makefile.COMMON $(GO) build $(GOFLAGS) -o $@ .PHONY: archive archive: $(ARCHIVE) $(ARCHIVE): $(BINARY) tar -czf $@ $< .PHONY: tag tag: git tag $(VERSION) git push --tags .PHONY: test test: $(GOCC) dependencies-stamp $(GO) test ./... .PHONY: format format: $(GOCC) find . -iname '*.go' | egrep -v "^\./\.build|./generated|\./Godeps|\.(l|y)\.go" | xargs -n1 $(GOFMT) -w -s=true .PHONY: clean clean: rm -rf $(BINARY) $(ARCHIVE) .build *-stamp prometheus-node-exporter-0.11.0+ds/NOTICE000066400000000000000000000003051257203701700200650ustar00rootroot00000000000000Configurable modular Prometheus exporter for various node metrics. Copyright 2013-2015 The Prometheus Authors This product includes software developed at SoundCloud Ltd. (http://soundcloud.com/). prometheus-node-exporter-0.11.0+ds/README.md000066400000000000000000000064501257203701700204470ustar00rootroot00000000000000# Node exporter [![Build Status](https://travis-ci.org/prometheus/node_exporter.svg)](https://travis-ci.org/prometheus/node_exporter) Prometheus exporter for machine metrics, written in Go with pluggable metric collectors. ## Building and running make ./node_exporter ## Running tests make test ## Available collectors By default the build will include the native collectors that expose information from `/proc`. Which collectors are used is controlled by the `--collectors.enabled` flag. ### Enabled by default Name | Description ---------|------------ diskstats | Exposes disk I/O statistics from `/proc/diskstats`. filesystem | Exposes filesystem statistics, such as disk space used. loadavg | Exposes load average. meminfo | Exposes memory statistics from `/proc/meminfo`. netdev | Exposes network interface statistics from `/proc/netstat`, such as bytes transferred. netstat | Exposes network statistics from `/proc/net/netstat`. This is the same information as `netstat -s`. stat | Exposes various statistics from `/proc/stat`. This includes CPU usage, boot time, forks and interrupts. textfile | Exposes statistics read from local disk. The `--collector.textfile.directory` flag must be set. time | Exposes the current system time. ### Disabled by default Name | Description ---------|------------ bonding | Exposes the number of configured and active slaves of Linux bonding interfaces. gmond | Exposes statistics from Ganglia. interrupts | Exposes detailed interrupts statistics from `/proc/interrupts`. ipvs | Exposes IPVS status from `/proc/net/ip_vs` and stats from `/proc/net/ip_vs_stats`. lastlogin | Exposes the last time there was a login. megacli | Exposes RAID statistics from MegaCLI. ntp | Exposes time drift from an NTP server. runit | Exposes service status from [runit](http://smarden.org/runit/). tcpstat | Exposes TCP connection status information from `/proc/net/tcp` and `/proc/net/tcp6`. (Warning: the current version has potential performance issues in high load situations.) ## Textfile Collector The textfile collector is similar to the [Pushgateway](https://github.com/prometheus/pushgateway), in that it allows exporting of statistics from batch jobs. It can also be used to export static metrics, such as what role a machine has. The Pushgateway should be used for service-level metrics. The textfile module is for metrics that are tied to a machine. To use it, set the `--collector.textfile.directory` flag on the Node exporter. The collector will parse all files in that directory matching the glob `*.prom` using the [text format](http://prometheus.io/docs/instrumenting/exposition_formats/). To atomically push completion time for a cron job: ``` echo my_batch_job_completion_time $(date +%s) > /path/to/directory/my_batch_job.prom.$$ mv /path/to/directory/my_batch_job.prom.$$ /path/to/directory/my_batch_job.prom ``` To statically set roles for a machine using labels: ``` echo 'role{role="application_server"} 1' > /path/to/directory/role.prom.$$ mv /path/to/directory/role.prom.$$ /path/to/directory/role.prom ``` ## Using Docker You can deploy this exporter using the [prom/node-exporter](https://registry.hub.docker.com/u/prom/node-exporter/) Docker image. For example: ```bash docker pull prom/node-exporter docker run -d -p 9100:9100 --net="host" prom/node-exporter ``` prometheus-node-exporter-0.11.0+ds/collector/000077500000000000000000000000001257203701700211515ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/bonding_linux.go000066400000000000000000000046111257203701700243410ustar00rootroot00000000000000// +build !nobonding package collector import ( "fmt" "io/ioutil" "os" "path" "strings" "github.com/prometheus/client_golang/prometheus" ) const ( sysfsNet = "/sys/class/net" ) type bondingCollector struct { slaves, active *prometheus.GaugeVec } func init() { Factories["bonding"] = NewBondingCollector } // NewBondingCollector returns a newly allocated bondingCollector. // It exposes the number of configured and active slave of linux bonding interfaces. func NewBondingCollector() (Collector, error) { return &bondingCollector{ slaves: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Name: "net_bonding_slaves", Help: "Number of configured slaves per bonding interface.", }, []string{"master"}, ), active: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Name: "net_bonding_slaves_active", Help: "Number of active slaves per bonding interface.", }, []string{"master"}, ), }, nil } // Update reads and exposes bonding states, implements Collector interface. Caution: This works only on linux. func (c *bondingCollector) Update(ch chan<- prometheus.Metric) (err error) { bondingStats, err := readBondingStats(sysfsNet) if err != nil { return err } for master, status := range bondingStats { c.slaves.WithLabelValues(master).Set(float64(status[0])) c.active.WithLabelValues(master).Set(float64(status[1])) } c.slaves.Collect(ch) c.active.Collect(ch) return nil } func readBondingStats(root string) (status map[string][2]int, err error) { status = map[string][2]int{} masters, err := ioutil.ReadFile(path.Join(root, "bonding_masters")) if err != nil { return nil, err } for _, master := range strings.Fields(string(masters)) { slaves, err := ioutil.ReadFile(path.Join(root, master, "bonding", "slaves")) if err != nil { return nil, err } sstat := [2]int{0, 0} for _, slave := range strings.Fields(string(slaves)) { state, err := ioutil.ReadFile(path.Join(root, master, fmt.Sprintf("lower_%s", slave), "operstate")) if os.IsNotExist(err) { // some older? kernels use slave_ prefix state, err = ioutil.ReadFile(path.Join(root, master, fmt.Sprintf("slave_%s", slave), "operstate")) } if err != nil { return nil, err } sstat[0]++ if strings.TrimSpace(string(state)) == "up" { sstat[1]++ } } status[master] = sstat } return status, err } prometheus-node-exporter-0.11.0+ds/collector/bonding_linux_test.go000066400000000000000000000007561257203701700254060ustar00rootroot00000000000000package collector import ( "testing" ) func TestBonding(t *testing.T) { bondingStats, err := readBondingStats("fixtures/bonding") if err != nil { t.Fatal(err) } if bondingStats["bond0"][0] != 0 || bondingStats["bond0"][1] != 0 { t.Fatal("bond0 in unexpected state") } if bondingStats["int"][0] != 2 || bondingStats["int"][1] != 1 { t.Fatal("int in unexpected state") } if bondingStats["dmz"][0] != 2 || bondingStats["dmz"][1] != 2 { t.Fatal("dmz in unexpected state") } } prometheus-node-exporter-0.11.0+ds/collector/collector.go000066400000000000000000000013661257203701700234740ustar00rootroot00000000000000// Exporter is a prometheus exporter using multiple Factories to collect and export system metrics. package collector import ( "github.com/prometheus/client_golang/prometheus" ) const Namespace = "node" var Factories = make(map[string]func() (Collector, error)) // Interface a collector has to implement. type Collector interface { // Get new metrics and expose them via prometheus registry. Update(ch chan<- prometheus.Metric) (err error) } // TODO: Instead of periodically call Update, a Collector could be implemented // as a real prometheus.Collector that only gathers metrics when // scraped. (However, for metric gathering that takes very long, it might // actually be better to do them proactively before scraping to minimize scrape // time.) prometheus-node-exporter-0.11.0+ds/collector/cpu_freebsd.go000066400000000000000000000035771257203701700237750ustar00rootroot00000000000000// +build !nocpu package collector import ( "errors" "os" "strconv" "unsafe" "github.com/prometheus/client_golang/prometheus" ) /* #cgo LDFLAGS: -lkvm #include #include #include #include #include */ import "C" type statCollector struct { cpu *prometheus.CounterVec } func init() { Factories["cpu"] = NewStatCollector } // Takes a prometheus registry and returns a new Collector exposing // CPU stats. func NewStatCollector() (Collector, error) { return &statCollector{ cpu: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Name: "cpu_seconds_total", Help: "Seconds the CPU spent in each mode.", }, []string{"cpu", "mode"}, ), }, nil } // Expose CPU stats using KVM. func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) { if os.Geteuid() != 0 && os.Getegid() != 2 { return errors.New("caller should be either root user or kmem group to access /dev/mem") } var errbuf *C.char kd := C.kvm_open(nil, nil, nil, C.O_RDONLY, errbuf) if errbuf != nil { return errors.New("failed to call kvm_open()") } defer C.kvm_close(kd) ncpus := C.kvm_getncpus(kd) for i := 0; i < int(ncpus); i++ { pcpu := C.kvm_getpcpu(kd, C.int(i)) cp_time := ((*C.struct_pcpu)(unsafe.Pointer(pcpu))).pc_cp_time c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "user"}).Set(float64(cp_time[C.CP_USER])) c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "nice"}).Set(float64(cp_time[C.CP_NICE])) c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "system"}).Set(float64(cp_time[C.CP_SYS])) c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "interrupt"}).Set(float64(cp_time[C.CP_INTR])) c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "idle"}).Set(float64(cp_time[C.CP_IDLE])) } c.cpu.Collect(ch) return err } prometheus-node-exporter-0.11.0+ds/collector/devstat_freebsd.go000066400000000000000000000145341257203701700246530ustar00rootroot00000000000000// +build !nodevstat package collector import ( "errors" "fmt" "github.com/prometheus/client_golang/prometheus" ) /* #cgo LDFLAGS: -ldevstat -lkvm #include #include #include #include #include #include #include typedef struct { uint64_t read; uint64_t write; uint64_t free; } Bytes; typedef struct { uint64_t other; uint64_t read; uint64_t write; uint64_t free; } Transfers; typedef struct { double other; double read; double write; double free; } Duration; typedef struct { char device[DEVSTAT_NAME_LEN]; int unit; Bytes bytes; Transfers transfers; Duration duration; long busyTime; uint64_t blocks; } Stats; int _get_ndevs() { struct statinfo current; int num_devices; current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); if (current.dinfo == NULL) return -2; devstat_checkversion(NULL); if (devstat_getdevs(NULL, ¤t) == -1) return -1; return current.dinfo->numdevs; } Stats _get_stats(int i) { struct statinfo current; int num_devices; current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); devstat_getdevs(NULL, ¤t); num_devices = current.dinfo->numdevs; Stats stats; uint64_t bytes_read, bytes_write, bytes_free; uint64_t transfers_other, transfers_read, transfers_write, transfers_free; long double duration_other, duration_read, duration_write, duration_free; long double busy_time; uint64_t blocks; strcpy(stats.device, current.dinfo->devices[i].device_name); stats.unit = current.dinfo->devices[i].unit_number; devstat_compute_statistics(¤t.dinfo->devices[i], NULL, 1.0, DSM_TOTAL_BYTES_READ, &bytes_read, DSM_TOTAL_BYTES_WRITE, &bytes_write, DSM_TOTAL_BYTES_FREE, &bytes_free, DSM_TOTAL_TRANSFERS_OTHER, &transfers_other, DSM_TOTAL_TRANSFERS_READ, &transfers_read, DSM_TOTAL_TRANSFERS_WRITE, &transfers_write, DSM_TOTAL_TRANSFERS_FREE, &transfers_free, DSM_TOTAL_DURATION_OTHER, &duration_other, DSM_TOTAL_DURATION_READ, &duration_read, DSM_TOTAL_DURATION_WRITE, &duration_write, DSM_TOTAL_DURATION_FREE, &duration_free, DSM_TOTAL_BUSY_TIME, &busy_time, DSM_TOTAL_BLOCKS, &blocks, DSM_NONE); stats.bytes.read = bytes_read; stats.bytes.write = bytes_write; stats.bytes.free = bytes_free; stats.transfers.other = transfers_other; stats.transfers.read = transfers_read; stats.transfers.write = transfers_write; stats.transfers.free = transfers_free; stats.duration.other = duration_other; stats.duration.read = duration_read; stats.duration.write = duration_write; stats.duration.free = duration_free; stats.busyTime = busy_time; stats.blocks = blocks; return stats; } */ import "C" const ( devstatSubsystem = "devstat" ) type devstatCollector struct { bytes *prometheus.CounterVec bytes_total *prometheus.CounterVec transfers *prometheus.CounterVec duration *prometheus.CounterVec busyTime *prometheus.CounterVec blocks *prometheus.CounterVec } func init() { Factories["devstat"] = NewDevstatCollector } // Takes a prometheus registry and returns a new Collector exposing // Device stats. func NewDevstatCollector() (Collector, error) { return &devstatCollector{ bytes: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: devstatSubsystem, Name: "bytes_total", Help: "The total number of bytes in transactions.", }, []string{"device", "type"}, ), transfers: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: devstatSubsystem, Name: "transfers_total", Help: "The total number of transactions.", }, []string{"device", "type"}, ), duration: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: devstatSubsystem, Name: "duration_seconds_total", Help: "The total duration of transactions in seconds.", }, []string{"device", "type"}, ), busyTime: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: devstatSubsystem, Name: "busy_time_seconds_total", Help: "Total time the device had one or more transactions outstanding in seconds.", }, []string{"device"}, ), blocks: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: devstatSubsystem, Name: "blocks_transferred_total", Help: "The total number of blocks transferred.", }, []string{"device"}, ), }, nil } func (c *devstatCollector) Update(ch chan<- prometheus.Metric) (err error) { count := C._get_ndevs() if count == -1 { return errors.New("devstat_getdevs() failed") } if count == -2 { return errors.New("calloc() failed") } for i := C.int(0); i < count; i++ { stats := C._get_stats(i) device := fmt.Sprintf("%s%d", C.GoString(&stats.device[0]), stats.unit) // Free metrics are disabled for now, please see PR #88 for more details. c.bytes.With(prometheus.Labels{"device": device, "type": "read"}).Set(float64(stats.bytes.read)) c.bytes.With(prometheus.Labels{"device": device, "type": "write"}).Set(float64(stats.bytes.write)) //c.bytes.With(prometheus.Labels{"device": device, "type": "free"}).Set(float64(stats.bytes.free)) c.transfers.With(prometheus.Labels{"device": device, "type": "other"}).Set(float64(stats.transfers.other)) c.transfers.With(prometheus.Labels{"device": device, "type": "read"}).Set(float64(stats.transfers.read)) c.transfers.With(prometheus.Labels{"device": device, "type": "write"}).Set(float64(stats.transfers.write)) //c.transfers.With(prometheus.Labels{"device": device, "type": "free"}).Set(float64(stats.transfers.free)) c.duration.With(prometheus.Labels{"device": device, "type": "other"}).Set(float64(stats.duration.other)) c.duration.With(prometheus.Labels{"device": device, "type": "read"}).Set(float64(stats.duration.read)) c.duration.With(prometheus.Labels{"device": device, "type": "write"}).Set(float64(stats.duration.write)) //c.duration.With(prometheus.Labels{"device": device, "type": "free"}).Set(float64(stats.duration.free)) c.busyTime.With(prometheus.Labels{"device": device}).Set(float64(stats.busyTime)) c.blocks.With(prometheus.Labels{"device": device}).Set(float64(stats.blocks)) } c.bytes.Collect(ch) c.transfers.Collect(ch) c.duration.Collect(ch) c.busyTime.Collect(ch) c.blocks.Collect(ch) return err } prometheus-node-exporter-0.11.0+ds/collector/diskstats_linux.go000066400000000000000000000126451257203701700247400ustar00rootroot00000000000000// +build !nodiskstats package collector import ( "bufio" "flag" "fmt" "io" "os" "regexp" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) const ( procDiskStats = "/proc/diskstats" diskSubsystem = "disk" ) var ( ignoredDevices = flag.String("collector.diskstats.ignored-devices", "^(ram|loop|fd|(h|s|v|xv)d[a-z])\\d+$", "Regexp of devices to ignore for diskstats.") ) type diskstatsCollector struct { ignoredDevicesPattern *regexp.Regexp metrics []prometheus.Collector } func init() { Factories["diskstats"] = NewDiskstatsCollector } // Takes a prometheus registry and returns a new Collector exposing // disk device stats. func NewDiskstatsCollector() (Collector, error) { var diskLabelNames = []string{"device"} return &diskstatsCollector{ ignoredDevicesPattern: regexp.MustCompile(*ignoredDevices), // Docs from https://www.kernel.org/doc/Documentation/iostats.txt metrics: []prometheus.Collector{ prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "reads_completed", Help: "The total number of reads completed successfully.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "reads_merged", Help: "The number of reads merged. See https://www.kernel.org/doc/Documentation/iostats.txt.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "sectors_read", Help: "The total number of sectors read successfully.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "read_time_ms", Help: "The total number of milliseconds spent by all reads.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "writes_completed", Help: "The total number of writes completed successfully.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "writes_merged", Help: "The number of writes merged. See https://www.kernel.org/doc/Documentation/iostats.txt.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "sectors_written", Help: "The total number of sectors written successfully.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "write_time_ms", Help: "This is the total number of milliseconds spent by all writes.", }, diskLabelNames, ), prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "io_now", Help: "The number of I/Os currently in progress.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "io_time_ms", Help: "Milliseconds spent doing I/Os.", }, diskLabelNames, ), prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: diskSubsystem, Name: "io_time_weighted", Help: "The weighted # of milliseconds spent doing I/Os. See https://www.kernel.org/doc/Documentation/iostats.txt.", }, diskLabelNames, ), }, }, nil } func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) (err error) { diskStats, err := getDiskStats() if err != nil { return fmt.Errorf("couldn't get diskstats: %s", err) } for dev, stats := range diskStats { if c.ignoredDevicesPattern.MatchString(dev) { log.Debugf("Ignoring device: %s", dev) continue } if len(stats) != len(c.metrics) { return fmt.Errorf("invalid line for %s for %s", procDiskStats, dev) } for k, value := range stats { v, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("invalid value %s in diskstats: %s", value, err) } if counter, ok := c.metrics[k].(*prometheus.CounterVec); ok { counter.WithLabelValues(dev).Set(v) } else if gauge, ok := c.metrics[k].(*prometheus.GaugeVec); ok { gauge.WithLabelValues(dev).Set(v) } else { return fmt.Errorf("unexpected collector %d", k) } } } for _, c := range c.metrics { c.Collect(ch) } return err } func getDiskStats() (map[string]map[int]string, error) { file, err := os.Open(procDiskStats) if err != nil { return nil, err } defer file.Close() return parseDiskStats(file) } func parseDiskStats(r io.Reader) (map[string]map[int]string, error) { var ( diskStats = map[string]map[int]string{} scanner = bufio.NewScanner(r) ) for scanner.Scan() { parts := strings.Fields(string(scanner.Text())) if len(parts) < 4 { // we strip major, minor and dev return nil, fmt.Errorf("invalid line in %s: %s", procDiskStats, scanner.Text()) } dev := parts[2] diskStats[dev] = map[int]string{} for i, v := range parts[3:] { diskStats[dev][i] = v } } return diskStats, nil } prometheus-node-exporter-0.11.0+ds/collector/diskstats_linux_test.go000066400000000000000000000010031257203701700257610ustar00rootroot00000000000000package collector import ( "os" "testing" ) func TestDiskStats(t *testing.T) { file, err := os.Open("fixtures/diskstats") if err != nil { t.Fatal(err) } defer file.Close() diskStats, err := parseDiskStats(file) if err != nil { t.Fatal(err) } if want, got := "25353629", diskStats["sda4"][0]; want != got { t.Errorf("want diskstats sda4 %s, got %s", want, got) } if want, got := "68", diskStats["mmcblk0p2"][10]; want != got { t.Errorf("want diskstats mmcblk0p2 %s, got %s", want, got) } } prometheus-node-exporter-0.11.0+ds/collector/filesystem_freebsd.go000066400000000000000000000062241257203701700253620ustar00rootroot00000000000000// +build !nofilesystem package collector import ( "errors" "flag" "regexp" "unsafe" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) /* #include #include #include #include */ import "C" const ( filesystemSubsystem = "filesystem" ) var ( ignoredMountPoints = flag.String("collector.filesystem.ignored-mount-points", "^/(dev)($|/)", "Regexp of mount points to ignore for filesystem collector.") ) type filesystemCollector struct { ignoredMountPointsPattern *regexp.Regexp size, free, avail, files, filesFree *prometheus.GaugeVec } func init() { Factories["filesystem"] = NewFilesystemCollector } // Takes a prometheus registry and returns a new Collector exposing // Filesystems stats. func NewFilesystemCollector() (Collector, error) { var filesystemLabelNames = []string{"filesystem"} return &filesystemCollector{ ignoredMountPointsPattern: regexp.MustCompile(*ignoredMountPoints), size: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "size_bytes", Help: "Filesystem size in bytes.", }, filesystemLabelNames, ), free: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "free_bytes", Help: "Filesystem free space in bytes.", }, filesystemLabelNames, ), avail: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "avail_bytes", Help: "Filesystem space available to non-root users in bytes.", }, filesystemLabelNames, ), files: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "file_nodes", Help: "Filesystem total file nodes.", }, filesystemLabelNames, ), filesFree: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "file_free_nodes", Help: "Filesystem total free file nodes.", }, filesystemLabelNames, ), }, nil } // Expose filesystem fullness. func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) (err error) { var mntbuf *C.struct_statfs count := C.getmntinfo(&mntbuf, C.MNT_NOWAIT) if count == 0 { return errors.New("getmntinfo() failed") } mnt := (*[1 << 30]C.struct_statfs)(unsafe.Pointer(mntbuf)) for i := 0; i < int(count); i++ { name := C.GoString(&mnt[i].f_mntonname[0]) if c.ignoredMountPointsPattern.MatchString(name) { log.Debugf("Ignoring mount point: %s", name) continue } c.size.WithLabelValues(name).Set(float64(mnt[i].f_blocks) * float64(mnt[i].f_bsize)) c.free.WithLabelValues(name).Set(float64(mnt[i].f_bfree) * float64(mnt[i].f_bsize)) c.avail.WithLabelValues(name).Set(float64(mnt[i].f_bavail) * float64(mnt[i].f_bsize)) c.files.WithLabelValues(name).Set(float64(mnt[i].f_files)) c.filesFree.WithLabelValues(name).Set(float64(mnt[i].f_ffree)) } c.size.Collect(ch) c.free.Collect(ch) c.avail.Collect(ch) c.files.Collect(ch) c.filesFree.Collect(ch) return err } prometheus-node-exporter-0.11.0+ds/collector/filesystem_linux.go000066400000000000000000000073471257203701700251160ustar00rootroot00000000000000// +build !nofilesystem package collector import ( "bufio" "flag" "fmt" "os" "regexp" "strings" "syscall" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) const ( procMounts = "/proc/mounts" filesystemSubsystem = "filesystem" ) var ( ignoredMountPoints = flag.String("collector.filesystem.ignored-mount-points", "^/(sys|proc|dev)($|/)", "Regexp of mount points to ignore for filesystem collector.") ) type filesystemDetails struct { device string mountPoint string fsType string } type filesystemCollector struct { ignoredMountPointsPattern *regexp.Regexp size, free, avail, files, filesFree *prometheus.GaugeVec } func init() { Factories["filesystem"] = NewFilesystemCollector } // Takes a prometheus registry and returns a new Collector exposing // network device filesystems. func NewFilesystemCollector() (Collector, error) { var filesystemLabelNames = []string{"device", "mountpoint", "fstype"} return &filesystemCollector{ ignoredMountPointsPattern: regexp.MustCompile(*ignoredMountPoints), size: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "size", Help: "Filesystem size in bytes.", }, filesystemLabelNames, ), free: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "free", Help: "Filesystem free space in bytes.", }, filesystemLabelNames, ), avail: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "avail", Help: "Filesystem space available to non-root users in bytes.", }, filesystemLabelNames, ), files: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "files", Help: "Filesystem total file nodes.", }, filesystemLabelNames, ), filesFree: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: filesystemSubsystem, Name: "files_free", Help: "Filesystem total free file nodes.", }, filesystemLabelNames, ), }, nil } // Expose filesystem fullness. func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) (err error) { mpds, err := mountPointDetails() if err != nil { return err } for _, mpd := range mpds { if c.ignoredMountPointsPattern.MatchString(mpd.mountPoint) { log.Debugf("Ignoring mount point: %s", mpd.mountPoint) continue } buf := new(syscall.Statfs_t) err := syscall.Statfs(mpd.mountPoint, buf) if err != nil { return fmt.Errorf("Statfs on %s returned %s", mpd.mountPoint, err) } c.size.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Blocks) * float64(buf.Bsize)) c.free.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Bfree) * float64(buf.Bsize)) c.avail.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Bavail) * float64(buf.Bsize)) c.files.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Files)) c.filesFree.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Ffree)) } c.size.Collect(ch) c.free.Collect(ch) c.avail.Collect(ch) c.files.Collect(ch) c.filesFree.Collect(ch) return err } func mountPointDetails() ([]filesystemDetails, error) { file, err := os.Open(procMounts) if err != nil { return nil, err } defer file.Close() filesystems := []filesystemDetails{} scanner := bufio.NewScanner(file) for scanner.Scan() { parts := strings.Fields(scanner.Text()) filesystems = append(filesystems, filesystemDetails{parts[0], parts[1], parts[2]}) } return filesystems, nil } prometheus-node-exporter-0.11.0+ds/collector/fixtures/000077500000000000000000000000001257203701700230225ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/000077500000000000000000000000001257203701700244425ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/bond0/000077500000000000000000000000001257203701700254445ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/bond0/bonding/000077500000000000000000000000001257203701700270645ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/bond0/bonding/slaves000066400000000000000000000000001257203701700302720ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/bonding_masters000066400000000000000000000000161257203701700275400ustar00rootroot00000000000000bond0 dmz int prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/000077500000000000000000000000001257203701700252345ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/bonding/000077500000000000000000000000001257203701700266545ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/bonding/slaves000066400000000000000000000000121257203701700300650ustar00rootroot00000000000000eth0 eth4 prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/slave_eth0/000077500000000000000000000000001257203701700272665ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/slave_eth0/operstate000066400000000000000000000000031257203701700312100ustar00rootroot00000000000000up prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/slave_eth4/000077500000000000000000000000001257203701700272725ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/dmz/slave_eth4/operstate000066400000000000000000000000031257203701700312140ustar00rootroot00000000000000up prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/000077500000000000000000000000001257203701700252345ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/bonding/000077500000000000000000000000001257203701700266545ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/bonding/slaves000066400000000000000000000000121257203701700300650ustar00rootroot00000000000000eth5 eth1 prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/slave_eth1/000077500000000000000000000000001257203701700272675ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/slave_eth1/operstate000066400000000000000000000000051257203701700312130ustar00rootroot00000000000000down prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/slave_eth5/000077500000000000000000000000001257203701700272735ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/bonding/int/slave_eth5/operstate000066400000000000000000000000031257203701700312150ustar00rootroot00000000000000up prometheus-node-exporter-0.11.0+ds/collector/fixtures/diskstats000066400000000000000000000043431257203701700247620ustar00rootroot00000000000000 1 0 ram0 0 0 0 0 0 0 0 0 0 0 0 1 1 ram1 0 0 0 0 0 0 0 0 0 0 0 1 2 ram2 0 0 0 0 0 0 0 0 0 0 0 1 3 ram3 0 0 0 0 0 0 0 0 0 0 0 1 4 ram4 0 0 0 0 0 0 0 0 0 0 0 1 5 ram5 0 0 0 0 0 0 0 0 0 0 0 1 6 ram6 0 0 0 0 0 0 0 0 0 0 0 1 7 ram7 0 0 0 0 0 0 0 0 0 0 0 1 8 ram8 0 0 0 0 0 0 0 0 0 0 0 1 9 ram9 0 0 0 0 0 0 0 0 0 0 0 1 10 ram10 0 0 0 0 0 0 0 0 0 0 0 1 11 ram11 0 0 0 0 0 0 0 0 0 0 0 1 12 ram12 0 0 0 0 0 0 0 0 0 0 0 1 13 ram13 0 0 0 0 0 0 0 0 0 0 0 1 14 ram14 0 0 0 0 0 0 0 0 0 0 0 1 15 ram15 0 0 0 0 0 0 0 0 0 0 0 7 0 loop0 0 0 0 0 0 0 0 0 0 0 0 7 1 loop1 0 0 0 0 0 0 0 0 0 0 0 7 2 loop2 0 0 0 0 0 0 0 0 0 0 0 7 3 loop3 0 0 0 0 0 0 0 0 0 0 0 7 4 loop4 0 0 0 0 0 0 0 0 0 0 0 7 5 loop5 0 0 0 0 0 0 0 0 0 0 0 7 6 loop6 0 0 0 0 0 0 0 0 0 0 0 7 7 loop7 0 0 0 0 0 0 0 0 0 0 0 8 0 sda 25354637 34367663 1003346126 18492372 28444756 11134226 505697032 63877960 0 9653880 82621804 8 1 sda1 250 0 2000 36 0 0 0 0 0 36 36 8 2 sda2 246 0 1968 32 0 0 0 0 0 32 32 8 3 sda3 340 13 2818 52 11 8 152 8 0 56 60 8 4 sda4 25353629 34367650 1003337964 18492232 27448755 11134218 505696880 61593380 0 7576432 80332428 252 0 dm-0 59910002 0 1003337218 46229572 39231014 0 505696880 1158557800 0 11325968 1206301256 252 1 dm-1 388 0 3104 84 74 0 592 0 0 76 84 252 2 dm-2 11571 0 308350 6536 153522 0 5093416 122884 0 65400 129416 252 3 dm-3 3870 0 3870 104 0 0 0 0 0 16 104 252 4 dm-4 392 0 1034 28 38 0 137 16 0 24 44 252 5 dm-5 3729 0 84279 924 98918 0 1151688 104684 0 58848 105632 179 0 mmcblk0 192 3 1560 156 0 0 0 0 0 136 156 179 1 mmcblk0p1 17 3 160 24 0 0 0 0 0 24 24 179 2 mmcblk0p2 95 0 760 68 0 0 0 0 0 68 68 2 0 fd0 2 0 16 80 0 0 0 0 0 80 80 254 0 vda 1775784 15386 32670882 8655768 6038856 20711856 213637440 2069221364 0 41614592 2077872228 254 1 vda1 668 85 5984 956 207 4266 35784 32772 0 8808 33720 254 2 vda2 1774936 15266 32663262 8654692 5991028 20707590 213601656 2069152216 0 41607628 2077801992 11 0 sr0 0 0 0 0 0 0 0 0 0 0 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/interrupts000066400000000000000000000042751257203701700251740ustar00rootroot00000000000000 CPU0 CPU1 CPU2 CPU3 0: 18 0 0 0 IR-IO-APIC-edge timer 1: 17960 105 28 28 IR-IO-APIC-edge i8042 8: 1 0 0 0 IR-IO-APIC-edge rtc0 9: 398553 2320 824 863 IR-IO-APIC-fasteoi acpi 12: 380847 1021 240 198 IR-IO-APIC-edge i8042 16: 328511 322879 293782 351412 IR-IO-APIC-fasteoi ehci_hcd:usb1, mmc0 23: 1451445 3333499 1092032 2644609 IR-IO-APIC-fasteoi ehci_hcd:usb2 40: 0 0 0 0 DMAR_MSI-edge dmar0 41: 0 0 0 0 DMAR_MSI-edge dmar1 42: 378324 1734637 440240 2434308 IR-PCI-MSI-edge xhci_hcd 43: 7434032 8092205 6478877 7492252 IR-PCI-MSI-edge ahci 44: 140636 226313 347 633 IR-PCI-MSI-edge i915 45: 4 22 0 0 IR-PCI-MSI-edge mei_me 46: 43078464 130 460171 290 IR-PCI-MSI-edge iwlwifi 47: 350 224 0 0 IR-PCI-MSI-edge snd_hda_intel NMI: 47 5031 6211 4968 Non-maskable interrupts LOC: 174326351 135776678 168393257 130980079 Local timer interrupts SPU: 0 0 0 0 Spurious interrupts PMI: 47 5031 6211 4968 Performance monitoring interrupts IWI: 1509379 2411776 1512975 2428828 IRQ work interrupts RTR: 0 0 0 0 APIC ICR read retries RES: 10847134 9111507 15999335 7457260 Rescheduling interrupts CAL: 148554 157441 142912 155528 Function call interrupts TLB: 10460334 9918429 10494258 10345022 TLB shootdowns TRM: 0 0 0 0 Thermal event interrupts THR: 0 0 0 0 Threshold APIC interrupts MCE: 0 0 0 0 Machine check exceptions MCP: 2406 2399 2399 2399 Machine check polls ERR: 0 MIS: 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/megacli_adapter.txt000066400000000000000000000221341257203701700266660ustar00rootroot00000000000000Adapter #0 ============================================================================== Versions ================ Product Name : PERC 6/i Integrated Serial No : 1234567890123456 FW Package Build: 6.3.3.0002 Mfg. Data ================ Mfg. Date : 06/24/08 Rework Date : 06/24/08 Revision No : Battery FRU : N/A Image Versions in Flash: ================ FW Version : 1.22.52-1909 BIOS Version : 2.04.00 WebBIOS Version : 1.1-46-e_15-Rel Ctrl-R Version : 1.02-015B Preboot CLI Version: 01.00-022:#%00005 Boot Block Version : 1.00.00.01-0011 Pending Images in Flash ================ None PCI Info ================ Controller Id : 0000 Vendor Id : 1000 Device Id : 0060 SubVendorId : 1028 SubDeviceId : 1f0c Host Interface : PCIE Link Speed : 0 Number of Frontend Port: 0 Device Interface : PCIE Number of Backend Port: 8 Port : Address 0 5000c50028f2083d 1 5000c50023cb3f39 2 5000c50023cea805 3 5000c50029124491 4 0000000000000000 5 0000000000000000 6 0000000000000000 7 0000000000000000 HW Configuration ================ SAS Address : 5a4badb01e219100 BBU : Present Alarm : Absent NVRAM : Present Serial Debugger : Present Memory : Present Flash : Present Memory Size : 256MB TPM : Absent On board Expander: Absent Upgrade Key : Absent Temperature sensor for ROC : Absent Temperature sensor for controller : Absent Settings ================ Current Time : 14:55:23 7/4, 2014 Predictive Fail Poll Interval : 300sec Interrupt Throttle Active Count : 16 Interrupt Throttle Completion : 50us Rebuild Rate : 30% PR Rate : 30% BGI Rate : 30% Check Consistency Rate : 30% Reconstruction Rate : 30% Cache Flush Interval : 4s Max Drives to Spinup at One Time : 2 Delay Among Spinup Groups : 12s Physical Drive Coercion Mode : 128MB Cluster Mode : Disabled Alarm : Disabled Auto Rebuild : Enabled Battery Warning : Enabled Ecc Bucket Size : 15 Ecc Bucket Leak Rate : 1440 Minutes Restore HotSpare on Insertion : Disabled Expose Enclosure Devices : Disabled Maintain PD Fail History : Disabled Host Request Reordering : Enabled Auto Detect BackPlane Enabled : SGPIO/i2c SEP Load Balance Mode : Auto Use FDE Only : No Security Key Assigned : No Security Key Failed : No Security Key Not Backedup : No Default LD PowerSave Policy : Controller Defined Maximum number of direct attached drives to spin up in 1 min : 0 Auto Enhanced Import : No Any Offline VD Cache Preserved : No Allow Boot with Preserved Cache : No Disable Online Controller Reset : No PFK in NVRAM : No Use disk activity for locate : No POST delay : 90 seconds BIOS Error Handling : Stop On Errors Current Boot Mode :Normal Capabilities ================ RAID Level Supported : RAID0, RAID1, RAID5, RAID6, RAID00, RAID10, RAID50, RAID60, PRL 11, PRL 11 with spanning, SRL 3 supported, PRL11-RLQ0 DDF layout with no span, PRL11-RLQ0 DDF layout with span Supported Drives : SAS, SATA Allowed Mixing: Mix in Enclosure Allowed Status ================ ECC Bucket Count : 0 Limitations ================ Max Arms Per VD : 32 Max Spans Per VD : 8 Max Arrays : 128 Max Number of VDs : 64 Max Parallel Commands : 1008 Max SGE Count : 80 Max Data Transfer Size : 8192 sectors Max Strips PerIO : 42 Max LD per array : 16 Min Strip Size : 8 KB Max Strip Size : 1.0 MB Max Configurable CacheCade Size: 0 GB Current Size of CacheCade : 0 GB Current Size of FW Cache : 0 MB Device Present ================ Virtual Drives : 1 Degraded : 0 Offline : 0 Physical Devices : 5 Disks : 4 Critical Disks : 0 Failed Disks : 0 Supported Adapter Operations ================ Rebuild Rate : Yes CC Rate : Yes BGI Rate : Yes Reconstruct Rate : Yes Patrol Read Rate : Yes Alarm Control : Yes Cluster Support : No BBU : Yes Spanning : Yes Dedicated Hot Spare : Yes Revertible Hot Spares : Yes Foreign Config Import : Yes Self Diagnostic : Yes Allow Mixed Redundancy on Array : No Global Hot Spares : Yes Deny SCSI Passthrough : No Deny SMP Passthrough : No Deny STP Passthrough : No Support Security : No Snapshot Enabled : No Support the OCE without adding drives : No Support PFK : No Support PI : No Support Boot Time PFK Change : No Disable Online PFK Change : No Support Shield State : No Block SSD Write Disk Cache Change: No Supported VD Operations ================ Read Policy : Yes Write Policy : Yes IO Policy : Yes Access Policy : Yes Disk Cache Policy : Yes Reconstruction : Yes Deny Locate : No Deny CC : No Allow Ctrl Encryption: No Enable LDBBM : No Support Breakmirror : No Power Savings : No Supported PD Operations ================ Force Online : Yes Force Offline : Yes Force Rebuild : Yes Deny Force Failed : No Deny Force Good/Bad : No Deny Missing Replace : No Deny Clear : No Deny Locate : No Support Temperature : No NCQ : No Disable Copyback : No Enable JBOD : No Enable Copyback on SMART : No Enable Copyback to SSD on SMART Error : No Enable SSD Patrol Read : No PR Correct Unconfigured Areas : Yes Error Counters ================ Memory Correctable Errors : 0 Memory Uncorrectable Errors : 0 Cluster Information ================ Cluster Permitted : No Cluster Active : No Default Settings ================ Phy Polarity : 0 Phy PolaritySplit : 0 Background Rate : 30 Strip Size : 64kB Flush Time : 4 seconds Write Policy : WB Read Policy : None Cache When BBU Bad : Disabled Cached IO : No SMART Mode : Mode 6 Alarm Disable : No Coercion Mode : 128MB ZCR Config : Unknown Dirty LED Shows Drive Activity : No BIOS Continue on Error : 0 Spin Down Mode : None Allowed Device Type : SAS/SATA Mix Allow Mix in Enclosure : Yes Allow HDD SAS/SATA Mix in VD : No Allow SSD SAS/SATA Mix in VD : No Allow HDD/SSD Mix in VD : No Allow SATA in Cluster : No Max Chained Enclosures : 1 Disable Ctrl-R : No Enable Web BIOS : No Direct PD Mapping : Yes BIOS Enumerate VDs : Yes Restore Hot Spare on Insertion : No Expose Enclosure Devices : No Maintain PD Fail History : No Disable Puncturing : No Zero Based Enclosure Enumeration : Yes PreBoot CLI Enabled : No LED Show Drive Activity : No Cluster Disable : Yes SAS Disable : No Auto Detect BackPlane Enable : SGPIO/i2c SEP Use FDE Only : No Enable Led Header : No Delay during POST : 0 EnableCrashDump : No Disable Online Controller Reset : No EnableLDBBM : No Un-Certified Hard Disk Drives : Block Treat Single span R1E as R10 : No Max LD per array : 16 Power Saving option : All power saving options are enabled Default spin down time in minutes: 0 Enable JBOD : No Time taken to detect CME : 60s Exit Code: 0x00 prometheus-node-exporter-0.11.0+ds/collector/fixtures/megacli_disks.txt000066400000000000000000000111771257203701700263700ustar00rootroot00000000000000 Adapter #0 Enclosure Device ID: 32 Slot Number: 0 Drive's position: DiskGroup: 0, Span: 0, Arm: 0 Enclosure position: N/A Device Id: 0 WWN: Sequence Number: 2 Media Error Count: 0 Other Error Count: 0 Predictive Failure Count: 0 Last Predictive Failure Event Seq Number: 0 PD Type: SAS Raw Size: 419.186 GB [0x3465f870 Sectors] Non Coerced Size: 418.686 GB [0x3455f870 Sectors] Coerced Size: 418.625 GB [0x34540000 Sectors] Sector Size: 0 Firmware state: Online, Spun Up Device Firmware Level: ES64 Shield Counter: 0 Successful diagnostics completion on : N/A SAS Address(0): 0x5000c50028f2083d SAS Address(1): 0x0 Connected Port Number: 0(path0) Inquiry Data: SEAGATE ST3450857SS ES643SK26856 FDE Capable: Not Capable FDE Enable: Disable Secured: Unsecured Locked: Unlocked Needs EKM Attention: No Foreign State: None Device Speed: Unknown Link Speed: Unknown Media Type: Hard Disk Device Drive Temperature :37C (98.60 F) PI Eligibility: No Drive is formatted for PI information: No PI: No PI Port-0 : Port status: Active Port's Linkspeed: Unknown Port-1 : Port status: Active Port's Linkspeed: Unknown Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID: 32 Slot Number: 1 Drive's position: DiskGroup: 0, Span: 0, Arm: 1 Enclosure position: N/A Device Id: 1 WWN: Sequence Number: 2 Media Error Count: 0 Other Error Count: 0 Predictive Failure Count: 0 Last Predictive Failure Event Seq Number: 0 PD Type: SAS Raw Size: 419.186 GB [0x3465f870 Sectors] Non Coerced Size: 418.686 GB [0x3455f870 Sectors] Coerced Size: 418.625 GB [0x34540000 Sectors] Sector Size: 0 Firmware state: Online, Spun Up Device Firmware Level: ES62 Shield Counter: 0 Successful diagnostics completion on : N/A SAS Address(0): 0x5000c50023cb3f39 SAS Address(1): 0x0 Connected Port Number: 1(path0) Inquiry Data: SEAGATE ST3450857SS ES623SK16HLC FDE Capable: Not Capable FDE Enable: Disable Secured: Unsecured Locked: Unlocked Needs EKM Attention: No Foreign State: None Device Speed: Unknown Link Speed: Unknown Media Type: Hard Disk Device Drive Temperature :37C (98.60 F) PI Eligibility: No Drive is formatted for PI information: No PI: No PI Port-0 : Port status: Active Port's Linkspeed: Unknown Port-1 : Port status: Active Port's Linkspeed: Unknown Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID: 32 Slot Number: 2 Drive's position: DiskGroup: 0, Span: 1, Arm: 0 Enclosure position: N/A Device Id: 2 WWN: Sequence Number: 2 Media Error Count: 0 Other Error Count: 0 Predictive Failure Count: 0 Last Predictive Failure Event Seq Number: 0 PD Type: SAS Raw Size: 419.186 GB [0x3465f870 Sectors] Non Coerced Size: 418.686 GB [0x3455f870 Sectors] Coerced Size: 418.625 GB [0x34540000 Sectors] Sector Size: 0 Firmware state: Online, Spun Up Device Firmware Level: ES62 Shield Counter: 0 Successful diagnostics completion on : N/A SAS Address(0): 0x5000c50023cea805 SAS Address(1): 0x0 Connected Port Number: 2(path0) Inquiry Data: SEAGATE ST3450857SS ES623SK189BR FDE Capable: Not Capable FDE Enable: Disable Secured: Unsecured Locked: Unlocked Needs EKM Attention: No Foreign State: None Device Speed: Unknown Link Speed: Unknown Media Type: Hard Disk Device Drive Temperature :39C (102.20 F) PI Eligibility: No Drive is formatted for PI information: No PI: No PI Port-0 : Port status: Active Port's Linkspeed: Unknown Port-1 : Port status: Active Port's Linkspeed: Unknown Drive has flagged a S.M.A.R.T alert : No Enclosure Device ID: 32 Slot Number: 3 Drive's position: DiskGroup: 0, Span: 1, Arm: 1 Enclosure position: N/A Device Id: 3 WWN: Sequence Number: 2 Media Error Count: 0 Other Error Count: 0 Predictive Failure Count: 23 Last Predictive Failure Event Seq Number: 0 PD Type: SAS Raw Size: 419.186 GB [0x3465f870 Sectors] Non Coerced Size: 418.686 GB [0x3455f870 Sectors] Coerced Size: 418.625 GB [0x34540000 Sectors] Sector Size: 0 Firmware state: Online, Spun Up Device Firmware Level: ES64 Shield Counter: 0 Successful diagnostics completion on : N/A SAS Address(0): 0x5000c50029124491 SAS Address(1): 0x0 Connected Port Number: 3(path0) Inquiry Data: SEAGATE ST3450857SS ES643SK27GQ9 FDE Capable: Not Capable FDE Enable: Disable Secured: Unsecured Locked: Unlocked Needs EKM Attention: No Foreign State: None Device Speed: Unknown Link Speed: Unknown Media Type: Hard Disk Device Drive Temperature :38C (100.40 F) PI Eligibility: No Drive is formatted for PI information: No PI: No PI Port-0 : Port status: Active Port's Linkspeed: Unknown Port-1 : Port status: Active Port's Linkspeed: Unknown Drive has flagged a S.M.A.R.T alert : No Exit Code: 0x00 prometheus-node-exporter-0.11.0+ds/collector/fixtures/meminfo000066400000000000000000000022221257203701700243750ustar00rootroot00000000000000MemTotal: 3742148 kB MemFree: 225472 kB Buffers: 22040 kB Cached: 930888 kB SwapCached: 192504 kB Active: 2233416 kB Inactive: 1028728 kB Active(anon): 2020004 kB Inactive(anon): 883052 kB Active(file): 213412 kB Inactive(file): 145676 kB Unevictable: 32 kB Mlocked: 32 kB SwapTotal: 4194300 kB SwapFree: 3155360 kB Dirty: 1052 kB Writeback: 0 kB AnonPages: 2244172 kB Mapped: 239220 kB Shmem: 593840 kB Slab: 98932 kB SReclaimable: 44772 kB SUnreclaim: 54160 kB KernelStack: 5800 kB PageTables: 75212 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 6065372 kB Committed_AS: 7835436 kB VmallocTotal: 34359738367 kB VmallocUsed: 352840 kB VmallocChunk: 34359338876 kB HardwareCorrupted: 0 kB AnonHugePages: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 185660 kB DirectMap2M: 3698688 kB prometheus-node-exporter-0.11.0+ds/collector/fixtures/net-dev000066400000000000000000000021001257203701700243000ustar00rootroot00000000000000Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed tun0: 1888 24 0 0 0 0 0 0 67120 934 0 0 0 0 0 0 veth4B09XN: 648 8 0 0 0 0 0 0 1943284 10640 0 0 0 0 0 0 lo: 435303245 1832522 0 0 0 0 0 0 435303245 1832522 0 0 0 0 0 0 eth0:68210035552 520993275 0 0 0 0 0 0 9315587528 43451486 0 0 0 0 0 0 lxcbr0: 0 0 0 0 0 0 0 0 2630299 28339 0 0 0 0 0 0 wlan0: 10437182923 13899359 0 0 0 0 0 0 2851649360 11726200 0 0 0 0 0 0 docker0: 64910168 1065585 0 0 0 0 0 0 2681662018 1929779 0 0 0 0 0 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/net/000077500000000000000000000000001257203701700236105ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/fixtures/net/ip_vs000066400000000000000000000013021257203701700246470ustar00rootroot00000000000000IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP C0A80016:0CEA wlc -> C0A85216:0CEA Tunnel 100 248 2 -> C0A85318:0CEA Tunnel 100 248 2 -> C0A85315:0CEA Tunnel 100 248 1 TCP C0A80039:0CEA wlc -> C0A85416:0CEA Tunnel 0 0 0 -> C0A85215:0CEA Tunnel 100 1499 0 -> C0A83215:0CEA Tunnel 100 1498 0 TCP C0A80037:0CEA wlc -> C0A8321A:0CEA Tunnel 0 0 0 -> C0A83120:0CEA Tunnel 100 0 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/net/ip_vs_result.txt000066400000000000000000000112501257203701700270660ustar00rootroot00000000000000# HELP node_ipvs_backend_connections_active The current active connections by local and remote address. # TYPE node_ipvs_backend_connections_active gauge node_ipvs_backend_connections_active{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.82.22",remote_port="3306"} 248 node_ipvs_backend_connections_active{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.83.21",remote_port="3306"} 248 node_ipvs_backend_connections_active{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.83.24",remote_port="3306"} 248 node_ipvs_backend_connections_active{local_address="192.168.0.55",local_port="3306",proto="TCP",remote_address="192.168.49.32",remote_port="3306"} 0 node_ipvs_backend_connections_active{local_address="192.168.0.55",local_port="3306",proto="TCP",remote_address="192.168.50.26",remote_port="3306"} 0 node_ipvs_backend_connections_active{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.50.21",remote_port="3306"} 1498 node_ipvs_backend_connections_active{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.82.21",remote_port="3306"} 1499 node_ipvs_backend_connections_active{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.84.22",remote_port="3306"} 0 # HELP node_ipvs_backend_connections_inactive The current inactive connections by local and remote address. # TYPE node_ipvs_backend_connections_inactive gauge node_ipvs_backend_connections_inactive{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.82.22",remote_port="3306"} 2 node_ipvs_backend_connections_inactive{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.83.21",remote_port="3306"} 1 node_ipvs_backend_connections_inactive{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.83.24",remote_port="3306"} 2 node_ipvs_backend_connections_inactive{local_address="192.168.0.55",local_port="3306",proto="TCP",remote_address="192.168.49.32",remote_port="3306"} 0 node_ipvs_backend_connections_inactive{local_address="192.168.0.55",local_port="3306",proto="TCP",remote_address="192.168.50.26",remote_port="3306"} 0 node_ipvs_backend_connections_inactive{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.50.21",remote_port="3306"} 0 node_ipvs_backend_connections_inactive{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.82.21",remote_port="3306"} 0 node_ipvs_backend_connections_inactive{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.84.22",remote_port="3306"} 0 # HELP node_ipvs_backend_weight The current backend weight by local and remote address. # TYPE node_ipvs_backend_weight gauge node_ipvs_backend_weight{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.82.22",remote_port="3306"} 100 node_ipvs_backend_weight{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.83.21",remote_port="3306"} 100 node_ipvs_backend_weight{local_address="192.168.0.22",local_port="3306",proto="TCP",remote_address="192.168.83.24",remote_port="3306"} 100 node_ipvs_backend_weight{local_address="192.168.0.55",local_port="3306",proto="TCP",remote_address="192.168.49.32",remote_port="3306"} 100 node_ipvs_backend_weight{local_address="192.168.0.55",local_port="3306",proto="TCP",remote_address="192.168.50.26",remote_port="3306"} 0 node_ipvs_backend_weight{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.50.21",remote_port="3306"} 100 node_ipvs_backend_weight{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.82.21",remote_port="3306"} 100 node_ipvs_backend_weight{local_address="192.168.0.57",local_port="3306",proto="TCP",remote_address="192.168.84.22",remote_port="3306"} 0 # HELP node_ipvs_connections_total The total number of connections made. # TYPE node_ipvs_connections_total counter node_ipvs_connections_total 2.3765872e+07 # HELP node_ipvs_incoming_bytes_total The total amount of incoming data. # TYPE node_ipvs_incoming_bytes_total counter node_ipvs_incoming_bytes_total 8.9991519156915e+13 # HELP node_ipvs_incoming_packets_total The total number of incoming packets. # TYPE node_ipvs_incoming_packets_total counter node_ipvs_incoming_packets_total 3.811989221e+09 # HELP node_ipvs_outgoing_bytes_total The total amount of outgoing data. # TYPE node_ipvs_outgoing_bytes_total counter node_ipvs_outgoing_bytes_total 0 # HELP node_ipvs_outgoing_packets_total The total number of outgoing packets. # TYPE node_ipvs_outgoing_packets_total counter node_ipvs_outgoing_packets_total 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/net/ip_vs_stats000066400000000000000000000004621257203701700260730ustar00rootroot00000000000000 Total Incoming Outgoing Incoming Outgoing Conns Packets Packets Bytes Bytes 16AA370 E33656E5 0 51D8C8883AB3 0 Conns/s Pkts/s Pkts/s Bytes/s Bytes/s 4 1FB3C 0 1282A8F 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/netstat000066400000000000000000000032321257203701700244270ustar00rootroot00000000000000TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSPassive PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPPrequeued TCPDirectCopyFromBacklog TCPDirectCopyFromPrequeue TCPPrequeueDropped TCPHPHits TCPHPHitsToUser TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLoss TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPForwardRetrans TCPSlowStartRetrans TCPTimeouts TCPRenoRecoveryFail TCPSackRecoveryFail TCPSchedulerFailed TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPChallengeACK TCPSYNChallenge TcpExt: 0 0 2 0 0 0 0 0 0 0 388812 0 0 0 0 6 102471 17 9 0 0 80568 0 168808 0 4471289 26 1433940 3744565 0 1 0 0 0 0 0 0 0 0 48 0 0 0 1 0 1 0 1 115 0 0 0 0 9 0 5 0 41 4 0 0 0 0 0 0 0 1 0 0 0 0 2 5 0 0 0 0 0 0 0 2 2 IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets IpExt: 0 0 0 0 0 0 6286396970 2786264347 0 0 0 0 prometheus-node-exporter-0.11.0+ds/collector/fixtures/tcpstat000066400000000000000000000007021257203701700244260ustar00rootroot00000000000000 sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 2740 1 ffff88003d3af3c0 100 0 0 10 0 1: 0F02000A:0016 0202000A:8B6B 01 00000000:00000000 02:000AC99B 00000000 0 0 3652 4 ffff88003d3ae040 21 4 31 47 46 prometheus-node-exporter-0.11.0+ds/collector/ganglia/000077500000000000000000000000001257203701700225535ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/collector/ganglia/format.go000066400000000000000000000030421257203701700243710ustar00rootroot00000000000000// Types for unmarshalling gmond's XML output. // // Not used elements in gmond's XML output are commented. // In case you want to use them, please change the names so that one // can understand without needing to know what the acronym stands for. package ganglia import "encoding/xml" type ExtraElement struct { Name string `xml:"NAME,attr"` Val string `xml:"VAL,attr"` } type ExtraData struct { ExtraElements []ExtraElement `xml:"EXTRA_ELEMENT"` } type Metric struct { Name string `xml:"NAME,attr"` Value float64 `xml:"VAL,attr"` /* Unit string `xml:"UNITS,attr"` Slope string `xml:"SLOPE,attr"` Tn int `xml:"TN,attr"` Tmax int `xml:"TMAX,attr"` Dmax int `xml:"DMAX,attr"` */ ExtraData ExtraData `xml:"EXTRA_DATA"` } type Host struct { Name string `xml:"NAME,attr"` /* Ip string `xml:"IP,attr"` Tags string `xml:"TAGS,attr"` Reported int `xml:"REPORTED,attr"` Tn int `xml:"TN,attr"` Tmax int `xml:"TMAX,attr"` Dmax int `xml:"DMAX,attr"` Location string `xml:"LOCATION,attr"` GmondStarted int `xml:"GMOND_STARTED",attr"` */ Metrics []Metric `xml:"METRIC"` } type Cluster struct { Name string `xml:"NAME,attr"` /* Owner string `xml:"OWNER,attr"` LatLong string `xml:"LATLONG,attr"` Url string `xml:"URL,attr"` Localtime int `xml:"LOCALTIME,attr"` */ Hosts []Host `xml:"HOST"` } type Ganglia struct { XMLNAME xml.Name `xml:"GANGLIA_XML"` Clusters []Cluster `xml:"CLUSTER"` } prometheus-node-exporter-0.11.0+ds/collector/gmond.go000066400000000000000000000046071257203701700226130ustar00rootroot00000000000000// +build !nogmond package collector import ( "bufio" "encoding/xml" "fmt" "io" "net" "regexp" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" "github.com/prometheus/node_exporter/collector/ganglia" ) const ( gangliaAddress = "127.0.0.1:8649" gangliaProto = "tcp" gangliaTimeout = 30 * time.Second gangliaNamespace = "ganglia" ) type gmondCollector struct { metrics map[string]*prometheus.GaugeVec } func init() { Factories["gmond"] = NewGmondCollector } var illegalCharsRE = regexp.MustCompile(`[^a-zA-Z0-9_]`) // Takes a prometheus registry and returns a new Collector scraping ganglia. func NewGmondCollector() (Collector, error) { c := gmondCollector{ metrics: map[string]*prometheus.GaugeVec{}, } return &c, nil } func (c *gmondCollector) Update(ch chan<- prometheus.Metric) (err error) { conn, err := net.Dial(gangliaProto, gangliaAddress) log.Debugf("gmondCollector Update") if err != nil { return fmt.Errorf("Can't connect to gmond: %s", err) } conn.SetDeadline(time.Now().Add(gangliaTimeout)) ganglia := ganglia.Ganglia{} decoder := xml.NewDecoder(bufio.NewReader(conn)) decoder.CharsetReader = toUtf8 err = decoder.Decode(&ganglia) if err != nil { return fmt.Errorf("Couldn't parse xml: %s", err) } for _, cluster := range ganglia.Clusters { for _, host := range cluster.Hosts { for _, metric := range host.Metrics { name := illegalCharsRE.ReplaceAllString(metric.Name, "_") c.setMetric(name, cluster.Name, metric) } } } for _, m := range c.metrics { m.Collect(ch) } return err } func (c *gmondCollector) setMetric(name, cluster string, metric ganglia.Metric) { if _, ok := c.metrics[name]; !ok { var desc string var title string for _, element := range metric.ExtraData.ExtraElements { switch element.Name { case "DESC": desc = element.Val case "TITLE": title = element.Val } if title != "" && desc != "" { break } } log.Debugf("Register %s: %s", name, desc) c.metrics[name] = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: gangliaNamespace, Name: name, Help: desc, }, []string{"cluster"}, ) } log.Debugf("Set %s{cluster=%q}: %f", name, cluster, metric.Value) c.metrics[name].WithLabelValues(cluster).Set(metric.Value) } func toUtf8(charset string, input io.Reader) (io.Reader, error) { return input, nil //FIXME } prometheus-node-exporter-0.11.0+ds/collector/helper.go000066400000000000000000000005571257203701700227660ustar00rootroot00000000000000package collector import ( "fmt" "strconv" "strings" ) func splitToInts(str string, sep string) (ints []int, err error) { for _, part := range strings.Split(str, sep) { i, err := strconv.Atoi(part) if err != nil { return nil, fmt.Errorf("Could not split '%s' because %s is no int: %s", str, part, err) } ints = append(ints, i) } return ints, nil } prometheus-node-exporter-0.11.0+ds/collector/interrupts_linux.go000066400000000000000000000050241257203701700251370ustar00rootroot00000000000000// +build !nointerrupts package collector import ( "bufio" "fmt" "io" "os" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" ) const ( procInterrupts = "/proc/interrupts" ) type interruptsCollector struct { metric *prometheus.CounterVec } func init() { Factories["interrupts"] = NewInterruptsCollector } // Takes a prometheus registry and returns a new Collector exposing // interrupts stats func NewInterruptsCollector() (Collector, error) { return &interruptsCollector{ metric: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Name: "interrupts", Help: "Interrupt details from /proc/interrupts.", }, []string{"CPU", "type", "info", "devices"}, ), }, nil } func (c *interruptsCollector) Update(ch chan<- prometheus.Metric) (err error) { interrupts, err := getInterrupts() if err != nil { return fmt.Errorf("Couldn't get interrupts: %s", err) } for name, interrupt := range interrupts { for cpuNo, value := range interrupt.values { fv, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("Invalid value %s in interrupts: %s", value, err) } labels := prometheus.Labels{ "CPU": strconv.Itoa(cpuNo), "type": name, "info": interrupt.info, "devices": interrupt.devices, } c.metric.With(labels).Set(fv) } } c.metric.Collect(ch) return err } type interrupt struct { info string devices string values []string } func getInterrupts() (map[string]interrupt, error) { file, err := os.Open(procInterrupts) if err != nil { return nil, err } defer file.Close() return parseInterrupts(file) } func parseInterrupts(r io.Reader) (map[string]interrupt, error) { var ( interrupts = map[string]interrupt{} scanner = bufio.NewScanner(r) ) if !scanner.Scan() { return nil, fmt.Errorf("%s empty", procInterrupts) } cpuNum := len(strings.Fields(string(scanner.Text()))) // one header per cpu for scanner.Scan() { line := scanner.Text() parts := strings.Fields(string(line)) if len(parts) < cpuNum+2 { // irq + one column per cpu + details, continue // we ignore ERR and MIS for now } intName := parts[0][:len(parts[0])-1] // remove trailing : intr := interrupt{ values: parts[1:cpuNum], } if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt intr.info = parts[cpuNum+1] intr.devices = strings.Join(parts[cpuNum+2:], " ") } else { intr.info = strings.Join(parts[cpuNum+1:], " ") } interrupts[intName] = intr } return interrupts, nil } prometheus-node-exporter-0.11.0+ds/collector/interrupts_linux_test.go000066400000000000000000000006041257203701700261750ustar00rootroot00000000000000package collector import ( "os" "testing" ) func TestInterrupts(t *testing.T) { file, err := os.Open("fixtures/interrupts") if err != nil { t.Fatal(err) } defer file.Close() interrupts, err := parseInterrupts(file) if err != nil { t.Fatal(err) } if want, got := "5031", interrupts["NMI"].values[1]; want != got { t.Errorf("want interrupts %s, got %s", want, got) } } prometheus-node-exporter-0.11.0+ds/collector/ipvs.go000066400000000000000000000107241257203701700224650ustar00rootroot00000000000000// +build !noipvs package collector import ( "flag" "fmt" "strconv" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) var ( ipvsProcfsMountPoint = flag.String("collector.ipvs.procfs", procfs.DefaultMountPoint, "procfs mountpoint.") ) type ipvsCollector struct { Collector fs procfs.FS backendConnectionsActive, backendConnectionsInact, backendWeight *prometheus.GaugeVec connections, incomingPackets, outgoingPackets, incomingBytes, outgoingBytes prometheus.Counter } func init() { Factories["ipvs"] = NewIPVSCollector } // NewIPVSCollector sets up a new collector for IPVS metrics. It accepts the // "procfs" config parameter to override the default proc location (/proc). func NewIPVSCollector() (Collector, error) { return newIPVSCollector() } func newIPVSCollector() (*ipvsCollector, error) { var ( ipvsBackendLabelNames = []string{ "local_address", "local_port", "remote_address", "remote_port", "proto", } c ipvsCollector err error subsystem = "ipvs" ) c.fs, err = procfs.NewFS(*ipvsProcfsMountPoint) if err != nil { return nil, err } c.connections = prometheus.NewCounter( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "connections_total", Help: "The total number of connections made.", }, ) c.incomingPackets = prometheus.NewCounter( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "incoming_packets_total", Help: "The total number of incoming packets.", }, ) c.outgoingPackets = prometheus.NewCounter( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "outgoing_packets_total", Help: "The total number of outgoing packets.", }, ) c.incomingBytes = prometheus.NewCounter( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "incoming_bytes_total", Help: "The total amount of incoming data.", }, ) c.outgoingBytes = prometheus.NewCounter( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "outgoing_bytes_total", Help: "The total amount of outgoing data.", }, ) c.backendConnectionsActive = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "backend_connections_active", Help: "The current active connections by local and remote address.", }, ipvsBackendLabelNames, ) c.backendConnectionsInact = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "backend_connections_inactive", Help: "The current inactive connections by local and remote address.", }, ipvsBackendLabelNames, ) c.backendWeight = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "backend_weight", Help: "The current backend weight by local and remote address.", }, ipvsBackendLabelNames, ) return &c, nil } func (c *ipvsCollector) Update(ch chan<- prometheus.Metric) error { ipvsStats, err := c.fs.NewIPVSStats() if err != nil { return fmt.Errorf("could not get IPVS stats: %s", err) } c.connections.Set(float64(ipvsStats.Connections)) c.incomingPackets.Set(float64(ipvsStats.IncomingPackets)) c.outgoingPackets.Set(float64(ipvsStats.OutgoingPackets)) c.incomingBytes.Set(float64(ipvsStats.IncomingBytes)) c.outgoingBytes.Set(float64(ipvsStats.OutgoingBytes)) c.connections.Collect(ch) c.incomingPackets.Collect(ch) c.outgoingPackets.Collect(ch) c.incomingBytes.Collect(ch) c.outgoingBytes.Collect(ch) backendStats, err := c.fs.NewIPVSBackendStatus() if err != nil { return fmt.Errorf("could not get backend status: %s", err) } for _, backend := range backendStats { labelValues := []string{ backend.LocalAddress.String(), strconv.FormatUint(uint64(backend.LocalPort), 10), backend.RemoteAddress.String(), strconv.FormatUint(uint64(backend.RemotePort), 10), backend.Proto, } c.backendConnectionsActive.WithLabelValues(labelValues...).Set(float64(backend.ActiveConn)) c.backendConnectionsInact.WithLabelValues(labelValues...).Set(float64(backend.InactConn)) c.backendWeight.WithLabelValues(labelValues...).Set(float64(backend.Weight)) } c.backendConnectionsActive.Collect(ch) c.backendConnectionsInact.Collect(ch) c.backendWeight.Collect(ch) return nil } prometheus-node-exporter-0.11.0+ds/collector/ipvs_test.go000066400000000000000000000130311257203701700235160ustar00rootroot00000000000000package collector import ( "flag" "io/ioutil" "net" "net/http" "net/http/httptest" "strconv" "strings" "testing" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) var ( expectedIPVSStats = procfs.IPVSStats{ Connections: 23765872, IncomingPackets: 3811989221, OutgoingPackets: 0, IncomingBytes: 89991519156915, OutgoingBytes: 0, } expectedIPVSBackendStatuses = []procfs.IPVSBackendStatus{ procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.22"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.82.22"), RemotePort: 3306, Proto: "TCP", Weight: 100, ActiveConn: 248, InactConn: 2, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.22"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.83.24"), RemotePort: 3306, Proto: "TCP", Weight: 100, ActiveConn: 248, InactConn: 2, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.22"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.83.21"), RemotePort: 3306, Proto: "TCP", Weight: 100, ActiveConn: 248, InactConn: 1, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.57"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.84.22"), RemotePort: 3306, Proto: "TCP", Weight: 0, ActiveConn: 0, InactConn: 0, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.57"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.82.21"), RemotePort: 3306, Proto: "TCP", Weight: 100, ActiveConn: 1499, InactConn: 0, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.57"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.50.21"), RemotePort: 3306, Proto: "TCP", Weight: 100, ActiveConn: 1498, InactConn: 0, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.55"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.50.26"), RemotePort: 3306, Proto: "TCP", Weight: 0, ActiveConn: 0, InactConn: 0, }, procfs.IPVSBackendStatus{ LocalAddress: net.ParseIP("192.168.0.55"), LocalPort: 3306, RemoteAddress: net.ParseIP("192.168.49.32"), RemotePort: 3306, Proto: "TCP", Weight: 100, ActiveConn: 0, InactConn: 0, }, } ) func TestIPVSCollector(t *testing.T) { if err := flag.Set("collector.ipvs.procfs", "fixtures"); err != nil { t.Fatal(err) } collector, err := newIPVSCollector() if err != nil { t.Fatal(err) } sink := make(chan prometheus.Metric) go func() { for { <-sink } }() err = collector.Update(sink) if err != nil { t.Fatal(err) } for _, expect := range expectedIPVSBackendStatuses { labels := prometheus.Labels{ "local_address": expect.LocalAddress.String(), "local_port": strconv.FormatUint(uint64(expect.LocalPort), 10), "remote_address": expect.RemoteAddress.String(), "remote_port": strconv.FormatUint(uint64(expect.RemotePort), 10), "proto": expect.Proto, } // TODO: Pending prometheus/client_golang#58, check the actual numbers _, err = collector.backendConnectionsActive.GetMetricWith(labels) if err != nil { t.Errorf("Missing active connections metric for label combination: %+v", labels) } _, err = collector.backendConnectionsInact.GetMetricWith(labels) if err != nil { t.Errorf("Missing inactive connections metric for label combination: %+v", labels) } _, err = collector.backendWeight.GetMetricWith(labels) if err != nil { t.Errorf("Missing weight metric for label combination: %+v", labels) } } } // mock collector type miniCollector struct { c Collector } func (c miniCollector) Collect(ch chan<- prometheus.Metric) { c.c.Update(ch) } func (c miniCollector) Describe(ch chan<- *prometheus.Desc) { prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "fake", Subsystem: "fake", Name: "fake", Help: "fake", }).Describe(ch) } func TestIPVSCollectorResponse(t *testing.T) { if err := flag.Set("collector.ipvs.procfs", "fixtures"); err != nil { t.Fatal(err) } collector, err := NewIPVSCollector() if err != nil { t.Fatal(err) } prometheus.MustRegister(miniCollector{c: collector}) rw := httptest.NewRecorder() prometheus.Handler().ServeHTTP(rw, &http.Request{}) metricsFile := "fixtures/net/ip_vs_result.txt" wantMetrics, err := ioutil.ReadFile(metricsFile) if err != nil { t.Fatalf("unable to read input test file %s: %s", metricsFile, err) } wantLines := strings.Split(string(wantMetrics), "\n") gotLines := strings.Split(string(rw.Body.String()), "\n") gotLinesIdx := 0 t.Log(gotLines) // Until the Prometheus Go client library offers better testability // (https://github.com/prometheus/client_golang/issues/58), we simply compare // verbatim text-format metrics outputs, but ignore any lines we don't have // in the fixture. Put differently, we are only testing that each line from // the fixture is present, in the order given. wantLoop: for _, want := range wantLines { for _, got := range gotLines[gotLinesIdx:] { if want == got { // this is a line we are interested in, and it is correct continue wantLoop } else { gotLinesIdx++ } } // if this point is reached, the line we want was missing t.Fatalf("Missing expected output line(s), first missing line is %s", want) } } prometheus-node-exporter-0.11.0+ds/collector/lastlogin_linux.go000066400000000000000000000041511257203701700247140ustar00rootroot00000000000000// +build !nolastlogin package collector import ( "bufio" "fmt" "io" "os/exec" "strings" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) const lastLoginSubsystem = "last_login" type lastLoginCollector struct { metric prometheus.Gauge } func init() { Factories["lastlogin"] = NewLastLoginCollector } // Takes a prometheus registry and returns a new Collector exposing // load, seconds since last login and a list of tags as specified by config. func NewLastLoginCollector() (Collector, error) { return &lastLoginCollector{ metric: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: lastLoginSubsystem, Name: "time", Help: "The time of the last login.", }), }, nil } func (c *lastLoginCollector) Update(ch chan<- prometheus.Metric) (err error) { last, err := getLastLoginTime() if err != nil { return fmt.Errorf("Couldn't get last seen: %s", err) } log.Debugf("Set node_last_login_time: %f", last) c.metric.Set(last) c.metric.Collect(ch) return err } func getLastLoginTime() (float64, error) { who := exec.Command("who", "/var/log/wtmp", "-l", "-u", "-s") output, err := who.StdoutPipe() if err != nil { return 0, err } err = who.Start() if err != nil { return 0, err } reader := bufio.NewReader(output) var last time.Time for { line, isPrefix, err := reader.ReadLine() if err == io.EOF { break } if isPrefix { return 0, fmt.Errorf("line to long: %s(...)", line) } fields := strings.Fields(string(line)) lastDate := fields[2] lastTime := fields[3] dateParts, err := splitToInts(lastDate, "-") // 2013-04-16 if err != nil { return 0, fmt.Errorf("Couldn't parse date in line '%s': %s", fields, err) } timeParts, err := splitToInts(lastTime, ":") // 11:33 if err != nil { return 0, fmt.Errorf("Couldn't parse time in line '%s': %s", fields, err) } last_t := time.Date(dateParts[0], time.Month(dateParts[1]), dateParts[2], timeParts[0], timeParts[1], 0, 0, time.UTC) last = last_t } err = who.Wait() if err != nil { return 0, err } return float64(last.Unix()), nil } prometheus-node-exporter-0.11.0+ds/collector/loadavg.go000066400000000000000000000021411257203701700231130ustar00rootroot00000000000000// +build !noloadavg // +build !linux package collector import ( "errors" "fmt" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) // #include import "C" type loadavgCollector struct { metric prometheus.Gauge } func init() { Factories["loadavg"] = NewLoadavgCollector } // Takes a prometheus registry and returns a new Collector exposing // load1 stat. func NewLoadavgCollector() (Collector, error) { return &loadavgCollector{ metric: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Name: "load1", Help: "1m load average.", }), }, nil } func (c *loadavgCollector) Update(ch chan<- prometheus.Metric) (err error) { load, err := getLoad1() if err != nil { return fmt.Errorf("Couldn't get load: %s", err) } log.Debugf("Set node_load: %f", load) c.metric.Set(load) c.metric.Collect(ch) return err } func getLoad1() (float64, error) { var loadavg [1]C.double samples := C.getloadavg(&loadavg[0], 1) if samples > 0 { return float64(loadavg[0]), nil } else { return 0, errors.New("failed to get load average") } } prometheus-node-exporter-0.11.0+ds/collector/loadavg_linux.go000066400000000000000000000025131257203701700243350ustar00rootroot00000000000000// +build !noloadavg package collector import ( "fmt" "io/ioutil" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) const ( procLoad = "/proc/loadavg" ) type loadavgCollector struct { metric prometheus.Gauge } func init() { Factories["loadavg"] = NewLoadavgCollector } // Takes a prometheus registry and returns a new Collector exposing // load, seconds since last login and a list of tags as specified by config. func NewLoadavgCollector() (Collector, error) { return &loadavgCollector{ metric: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Name: "load1", Help: "1m load average.", }), }, nil } func (c *loadavgCollector) Update(ch chan<- prometheus.Metric) (err error) { load, err := getLoad1() if err != nil { return fmt.Errorf("Couldn't get load: %s", err) } log.Debugf("Set node_load: %f", load) c.metric.Set(load) c.metric.Collect(ch) return err } func getLoad1() (float64, error) { data, err := ioutil.ReadFile(procLoad) if err != nil { return 0, err } return parseLoad(string(data)) } func parseLoad(data string) (float64, error) { parts := strings.Fields(data) load, err := strconv.ParseFloat(parts[0], 64) if err != nil { return 0, fmt.Errorf("Could not parse load '%s': %s", parts[0], err) } return load, nil } prometheus-node-exporter-0.11.0+ds/collector/loadavg_linux_test.go000066400000000000000000000003621257203701700253740ustar00rootroot00000000000000package collector import "testing" func TestLoad(t *testing.T) { load, err := parseLoad("0.21 0.37 0.39 1/719 19737") if err != nil { t.Fatal(err) } if want := 0.21; want != load { t.Fatalf("want load %f, got %f", want, load) } } prometheus-node-exporter-0.11.0+ds/collector/megacli.go000066400000000000000000000116271257203701700231100ustar00rootroot00000000000000// +build !nomegacli package collector import ( "bufio" "flag" "io" "os/exec" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" ) const ( defaultMegaCli = "megacli" adapterHeaderSep = "================" ) var ( megacliCommand = flag.String("collector.megacli.command", defaultMegaCli, "Command to run megacli.") ) type megaCliCollector struct { cli string driveTemperature *prometheus.GaugeVec driveCounters *prometheus.CounterVec drivePresence *prometheus.GaugeVec } func init() { Factories["megacli"] = NewMegaCliCollector } // Takes a prometheus registry and returns a new Collector exposing // RAID status through megacli. func NewMegaCliCollector() (Collector, error) { return &megaCliCollector{ cli: *megacliCommand, driveTemperature: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: Namespace, Name: "megacli_drive_temperature_celsius", Help: "megacli: drive temperature", }, []string{"enclosure", "slot"}), driveCounters: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: Namespace, Name: "megacli_drive_count", Help: "megacli: drive error and event counters", }, []string{"enclosure", "slot", "type"}), drivePresence: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: Namespace, Name: "megacli_adapter_disk_presence", Help: "megacli: disk presence per adapter", }, []string{"type"}), }, nil } func (c *megaCliCollector) Update(ch chan<- prometheus.Metric) (err error) { err = c.updateAdapter() if err != nil { return err } err = c.updateDisks() c.driveTemperature.Collect(ch) c.driveCounters.Collect(ch) c.drivePresence.Collect(ch) return err } func parseMegaCliDisks(r io.Reader) (map[int]map[int]map[string]string, error) { var ( stats = map[int]map[int]map[string]string{} scanner = bufio.NewScanner(r) curEnc = -1 curSlot = -1 ) for scanner.Scan() { var err error text := strings.TrimSpace(scanner.Text()) parts := strings.SplitN(text, ":", 2) if len(parts) != 2 { // Adapter #X continue } key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) switch { case key == "Enclosure Device ID": curEnc, err = strconv.Atoi(value) if err != nil { return nil, err } case key == "Slot Number": curSlot, err = strconv.Atoi(value) if err != nil { return nil, err } case curSlot != -1 && curEnc != -1: if _, ok := stats[curEnc]; !ok { stats[curEnc] = map[int]map[string]string{} } if _, ok := stats[curEnc][curSlot]; !ok { stats[curEnc][curSlot] = map[string]string{} } stats[curEnc][curSlot][key] = value } } return stats, nil } func parseMegaCliAdapter(r io.Reader) (map[string]map[string]string, error) { var ( raidStats = map[string]map[string]string{} scanner = bufio.NewScanner(r) header = "" last = "" ) for scanner.Scan() { text := strings.TrimSpace(scanner.Text()) if text == adapterHeaderSep { header = last raidStats[header] = map[string]string{} continue } last = text if header == "" { // skip Adapter #X and separator continue } parts := strings.SplitN(text, ":", 2) if len(parts) != 2 { // these section never include anything we are interested in continue } key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) raidStats[header][key] = value } return raidStats, nil } func (c *megaCliCollector) updateAdapter() error { cmd := exec.Command(c.cli, "-AdpAllInfo", "-aALL") pipe, err := cmd.StdoutPipe() if err != nil { return err } if err := cmd.Start(); err != nil { return err } stats, err := parseMegaCliAdapter(pipe) if err != nil { return err } if err := cmd.Wait(); err != nil { return err } for k, v := range stats["Device Present"] { value, err := strconv.ParseFloat(v, 64) if err != nil { return err } c.drivePresence.WithLabelValues(k).Set(value) } return nil } func (c *megaCliCollector) updateDisks() error { var counters = []string{"Media Error Count", "Other Error Count", "Predictive Failure Count"} cmd := exec.Command(c.cli, "-PDList", "-aALL") pipe, err := cmd.StdoutPipe() if err != nil { return err } if err := cmd.Start(); err != nil { return err } stats, err := parseMegaCliDisks(pipe) if err != nil { return err } if err := cmd.Wait(); err != nil { return err } for enc, encStats := range stats { for slot, slotStats := range encStats { tStr := slotStats["Drive Temperature"] tStr = tStr[:strings.Index(tStr, "C")] t, err := strconv.ParseFloat(tStr, 64) if err != nil { return err } encStr := strconv.Itoa(enc) slotStr := strconv.Itoa(slot) c.driveTemperature.WithLabelValues(encStr, slotStr).Set(t) for _, i := range counters { counter, err := strconv.ParseFloat(slotStats[i], 64) if err != nil { return err } c.driveCounters.WithLabelValues(encStr, slotStr, i).Set(counter) } } } return nil } prometheus-node-exporter-0.11.0+ds/collector/megacli_test.go000066400000000000000000000021541257203701700241420ustar00rootroot00000000000000// +build !nomegacli package collector import ( "os" "testing" ) const ( testMegaCliAdapter = "fixtures/megacli_adapter.txt" testMegaCliDisks = "fixtures/megacli_disks.txt" physicalDevicesExpected = "5" virtualDevicesDegraded = "0" ) func TestMegaCliAdapter(t *testing.T) { data, err := os.Open(testMegaCliAdapter) if err != nil { t.Fatal(err) } stats, err := parseMegaCliAdapter(data) if err != nil { t.Fatal(err) } if stats["Device Present"]["Physical Devices"] != physicalDevicesExpected { t.Fatalf("Unexpected device count: %d != %d", stats["Device Present"]["Physical Devices"], physicalDevicesExpected) } if stats["Device Present"]["Degraded"] != virtualDevicesDegraded { t.Fatal() } } func TestMegaCliDisks(t *testing.T) { data, err := os.Open(testMegaCliDisks) if err != nil { t.Fatal(err) } stats, err := parseMegaCliDisks(data) if err != nil { t.Fatal(err) } if stats[32][0]["Drive Temperature"] != "37C (98.60 F)" { t.Fatalf("Unexpected drive temperature: %s", stats[32][0]["Drive Temperature"]) } if stats[32][3]["Predictive Failure Count"] != "23" { t.Fatal() } } prometheus-node-exporter-0.11.0+ds/collector/meminfo_freebsd.go000066400000000000000000000046401257203701700246300ustar00rootroot00000000000000// +build !nomeminfo package collector import ( "errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) /* #include #include int _sysctl(const char* name) { int val; size_t size = sizeof(val); int res = sysctlbyname(name, &val, &size, NULL, 0); if (res == -1) { return -1; } if (size != sizeof(val)) { return -2; } return val; } */ import "C" const ( memInfoSubsystem = "memory" ) type meminfoCollector struct { metrics map[string]prometheus.Gauge } func init() { Factories["meminfo"] = NewMeminfoCollector } // Takes a prometheus registry and returns a new Collector exposing // Memory stats. func NewMeminfoCollector() (Collector, error) { return &meminfoCollector{ metrics: map[string]prometheus.Gauge{}, }, nil } func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) (err error) { var pages map[string]C.int pages = make(map[string]C.int) size := C._sysctl(C.CString("vm.stats.vm.v_page_size")) if size == -1 { return errors.New("sysctl(vm.stats.vm.v_page_size) failed") } if size == -2 { return errors.New("sysctl(vm.stats.vm.v_page_size) failed, wrong buffer size") } pages["active"] = C._sysctl(C.CString("vm.stats.vm.v_active_count")) pages["inactive"] = C._sysctl(C.CString("vm.stats.vm.v_inactive_count")) pages["wire"] = C._sysctl(C.CString("vm.stats.vm.v_wire_count")) pages["cache"] = C._sysctl(C.CString("vm.stats.vm.v_cache_count")) pages["free"] = C._sysctl(C.CString("vm.stats.vm.v_free_count")) pages["swappgsin"] = C._sysctl(C.CString("vm.stats.vm.v_swappgsin")) pages["swappgsout"] = C._sysctl(C.CString("vm.stats.vm.v_swappgsout")) pages["total"] = C._sysctl(C.CString("vm.stats.vm.v_page_count")) for key := range pages { if pages[key] == -1 { return errors.New("sysctl() failed for " + key) } if pages[key] == -2 { return errors.New("sysctl() failed for " + key + ", wrong buffer size") } } log.Debugf("Set node_mem: %#v", pages) for k, v := range pages { if _, ok := c.metrics[k]; !ok { c.metrics[k] = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: memInfoSubsystem, Name: k, Help: k + " from sysctl()", }) } // Convert metrics to kB (same as Linux meminfo). c.metrics[k].Set(float64(v) * float64(size)) c.metrics[k].Collect(ch) } return err } prometheus-node-exporter-0.11.0+ds/collector/meminfo_linux.go000066400000000000000000000040601257203701700243510ustar00rootroot00000000000000// +build !nomeminfo package collector import ( "bufio" "fmt" "io" "os" "regexp" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) const ( procMemInfo = "/proc/meminfo" memInfoSubsystem = "memory" ) type meminfoCollector struct { metrics map[string]prometheus.Gauge } func init() { Factories["meminfo"] = NewMeminfoCollector } // Takes a prometheus registry and returns a new Collector exposing // memory stats. func NewMeminfoCollector() (Collector, error) { return &meminfoCollector{ metrics: map[string]prometheus.Gauge{}, }, nil } func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) (err error) { memInfo, err := getMemInfo() if err != nil { return fmt.Errorf("Couldn't get meminfo: %s", err) } log.Debugf("Set node_mem: %#v", memInfo) for k, v := range memInfo { if _, ok := c.metrics[k]; !ok { c.metrics[k] = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: memInfoSubsystem, Name: k, Help: k + " from /proc/meminfo.", }) } c.metrics[k].Set(v) c.metrics[k].Collect(ch) } return err } func getMemInfo() (map[string]float64, error) { file, err := os.Open(procMemInfo) if err != nil { return nil, err } defer file.Close() return parseMemInfo(file) } func parseMemInfo(r io.Reader) (map[string]float64, error) { var ( memInfo = map[string]float64{} scanner = bufio.NewScanner(r) re = regexp.MustCompile("\\((.*)\\)") ) for scanner.Scan() { line := scanner.Text() parts := strings.Fields(string(line)) fv, err := strconv.ParseFloat(parts[1], 64) if err != nil { return nil, fmt.Errorf("Invalid value in meminfo: %s", err) } switch len(parts) { case 2: // no unit case 3: // has unit, we presume kB fv *= 1024 default: return nil, fmt.Errorf("Invalid line in %s: %s", procMemInfo, line) } key := parts[0][:len(parts[0])-1] // remove trailing : from key // Active(anon) -> Active_anon key = re.ReplaceAllString(key, "_${1}") memInfo[key] = fv } return memInfo, nil } prometheus-node-exporter-0.11.0+ds/collector/meminfo_linux_test.go000066400000000000000000000007751257203701700254210ustar00rootroot00000000000000package collector import ( "os" "testing" ) func TestMemInfo(t *testing.T) { file, err := os.Open("fixtures/meminfo") if err != nil { t.Fatal(err) } defer file.Close() memInfo, err := parseMemInfo(file) if err != nil { t.Fatal(err) } if want, got := 3831959552.0, memInfo["MemTotal"]; want != got { t.Errorf("want memory total %f, got %f", want, got) } if want, got := 3787456512.0, memInfo["DirectMap2M"]; want != got { t.Errorf("want memory directMap2M %f, got %f", want, got) } } prometheus-node-exporter-0.11.0+ds/collector/netdev_freebsd.go000066400000000000000000000055531257203701700244670ustar00rootroot00000000000000// +build !nonetdev package collector import ( "errors" "fmt" "strconv" "github.com/prometheus/client_golang/prometheus" ) /* #cgo CFLAGS: -D_IFI_OQDROPS #include #include #include #include #include */ import "C" const ( netDevSubsystem = "network" ) type netDevCollector struct { metrics map[string]*prometheus.CounterVec } func init() { Factories["netdev"] = NewNetDevCollector } // Takes a prometheus registry and returns a new Collector exposing // Network device stats. func NewNetDevCollector() (Collector, error) { return &netDevCollector{ metrics: map[string]*prometheus.CounterVec{}, }, nil } func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { netDev, err := getNetDevStats() if err != nil { return fmt.Errorf("couldn't get netstats: %s", err) } for direction, devStats := range netDev { for dev, stats := range devStats { for t, value := range stats { key := direction + "_" + t if _, ok := c.metrics[key]; !ok { c.metrics[key] = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Subsystem: netDevSubsystem, Name: key, Help: fmt.Sprintf("%s %s from getifaddrs().", t, direction), }, []string{"device"}, ) } v, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("invalid value %s in netstats: %s", value, err) } c.metrics[key].WithLabelValues(dev).Set(v) } } } for _, m := range c.metrics { m.Collect(ch) } return err } func getNetDevStats() (map[string]map[string]map[string]string, error) { netDev := map[string]map[string]map[string]string{} netDev["transmit"] = map[string]map[string]string{} netDev["receive"] = map[string]map[string]string{} var ifap, ifa *C.struct_ifaddrs if C.getifaddrs(&ifap) == -1 { return nil, errors.New("getifaddrs() failed") } defer C.freeifaddrs(ifap) for ifa = ifap; ifa != nil; ifa = ifa.ifa_next { if ifa.ifa_addr.sa_family == C.AF_LINK { receive := map[string]string{} transmit := map[string]string{} data := (*C.struct_if_data)(ifa.ifa_data) receive["packets"] = strconv.Itoa(int(data.ifi_ipackets)) transmit["packets"] = strconv.Itoa(int(data.ifi_opackets)) receive["errs"] = strconv.Itoa(int(data.ifi_ierrors)) transmit["errs"] = strconv.Itoa(int(data.ifi_oerrors)) receive["bytes"] = strconv.Itoa(int(data.ifi_ibytes)) transmit["bytes"] = strconv.Itoa(int(data.ifi_obytes)) receive["multicast"] = strconv.Itoa(int(data.ifi_imcasts)) transmit["multicast"] = strconv.Itoa(int(data.ifi_omcasts)) receive["drop"] = strconv.Itoa(int(data.ifi_iqdrops)) transmit["drop"] = strconv.Itoa(int(data.ifi_oqdrops)) netDev["receive"][C.GoString(ifa.ifa_name)] = receive netDev["transmit"][C.GoString(ifa.ifa_name)] = transmit } } return netDev, nil } prometheus-node-exporter-0.11.0+ds/collector/netdev_linux.go000066400000000000000000000067711257203701700242170ustar00rootroot00000000000000// +build !nonetdev package collector import ( "bufio" "flag" "fmt" "io" "os" "regexp" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) const ( procNetDev = "/proc/net/dev" netDevSubsystem = "network" ) var ( fieldSep = regexp.MustCompile("[ :] *") netdevIgnoredDevices = flag.String("collector.netdev.ignored-devices", "^$", "Regexp of net devices to ignore for netdev collector.") ) type netDevCollector struct { ignoredDevicesPattern *regexp.Regexp metrics map[string]*prometheus.GaugeVec } func init() { Factories["netdev"] = NewNetDevCollector } // Takes a prometheus registry and returns a new Collector exposing // network device stats. func NewNetDevCollector() (Collector, error) { return &netDevCollector{ ignoredDevicesPattern: regexp.MustCompile(*netdevIgnoredDevices), metrics: map[string]*prometheus.GaugeVec{}, }, nil } func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { netDev, err := getNetDevStats(c.ignoredDevicesPattern) if err != nil { return fmt.Errorf("Couldn't get netstats: %s", err) } for direction, devStats := range netDev { for dev, stats := range devStats { for t, value := range stats { key := direction + "_" + t if _, ok := c.metrics[key]; !ok { c.metrics[key] = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: netDevSubsystem, Name: key, Help: fmt.Sprintf("%s %s from /proc/net/dev.", t, direction), }, []string{"device"}, ) } v, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("Invalid value %s in netstats: %s", value, err) } c.metrics[key].WithLabelValues(dev).Set(v) } } } for _, m := range c.metrics { m.Collect(ch) } return err } func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]map[string]string, error) { file, err := os.Open(procNetDev) if err != nil { return nil, err } defer file.Close() return parseNetDevStats(file, ignore) } func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string]map[string]string, error) { netDev := map[string]map[string]map[string]string{} netDev["transmit"] = map[string]map[string]string{} netDev["receive"] = map[string]map[string]string{} scanner := bufio.NewScanner(r) scanner.Scan() // skip first header scanner.Scan() parts := strings.Split(string(scanner.Text()), "|") if len(parts) != 3 { // interface + receive + transmit return nil, fmt.Errorf("Invalid header line in %s: %s", procNetDev, scanner.Text()) } header := strings.Fields(parts[1]) for scanner.Scan() { line := strings.TrimLeft(string(scanner.Text()), " ") parts := fieldSep.Split(line, -1) if len(parts) != 2*len(header)+1 { return nil, fmt.Errorf("Invalid line in %s: %s", procNetDev, scanner.Text()) } dev := parts[0][:len(parts[0])] receive, err := parseNetDevLine(parts[1:len(header)+1], header) if err != nil { return nil, err } transmit, err := parseNetDevLine(parts[len(header)+1:], header) if err != nil { return nil, err } if ignore.MatchString(dev) { log.Debugf("Ignoring device: %s", dev) continue } netDev["transmit"][dev] = transmit netDev["receive"][dev] = receive } return netDev, nil } func parseNetDevLine(parts []string, header []string) (map[string]string, error) { devStats := map[string]string{} for i, v := range parts { devStats[header[i]] = v } return devStats, nil } prometheus-node-exporter-0.11.0+ds/collector/netdev_linux_test.go000066400000000000000000000017641257203701700252530ustar00rootroot00000000000000package collector import ( "os" "regexp" "testing" ) func TestNetDevStats(t *testing.T) { file, err := os.Open("fixtures/net-dev") if err != nil { t.Fatal(err) } defer file.Close() netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth")) if err != nil { t.Fatal(err) } if want, got := "10437182923", netStats["receive"]["wlan0"]["bytes"]; want != got { t.Errorf("want netstat wlan0 bytes %s, got %s", want, got) } if want, got := "68210035552", netStats["receive"]["eth0"]["bytes"]; want != got { t.Errorf("want netstat eth0 bytes %s, got %s", want, got) } if want, got := "934", netStats["transmit"]["tun0"]["packets"]; want != got { t.Errorf("want netstat tun0 packets %s, got %s", want, got) } if want, got := 6, len(netStats["transmit"]); want != got { t.Errorf("want count of devices to be %d, got %d", want, got) } if _, ok := netStats["transmit"]["veth4B09XN"]; ok != false { t.Error("want fixture interface veth4B09XN to not exist, but it does") } } prometheus-node-exporter-0.11.0+ds/collector/netstat_linux.go000066400000000000000000000052171257203701700244060ustar00rootroot00000000000000// +build !nonetstat package collector import ( "bufio" "fmt" "io" "os" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" ) const ( procNetStat = "/proc/net/netstat" procSNMPStat = "/proc/net/snmp" netStatsSubsystem = "netstat" ) type netStatCollector struct { metrics map[string]prometheus.Gauge } func init() { Factories["netstat"] = NewNetStatCollector } // NewNetStatCollector takes a returns // a new Collector exposing network stats. func NewNetStatCollector() (Collector, error) { return &netStatCollector{ metrics: map[string]prometheus.Gauge{}, }, nil } func (c *netStatCollector) Update(ch chan<- prometheus.Metric) (err error) { netStats, err := getNetStats(procNetStat) if err != nil { return fmt.Errorf("couldn't get netstats: %s", err) } snmpStats, err := getNetStats(procSNMPStat) if err != nil { return fmt.Errorf("couldn't get SNMP stats: %s", err) } // Merge the results of snmpStats into netStats (collisions are possible, but // we know that the keys are always unique for the given use case) for k, v := range snmpStats { netStats[k] = v } for protocol, protocolStats := range netStats { for name, value := range protocolStats { key := protocol + "_" + name if _, ok := c.metrics[key]; !ok { c.metrics[key] = prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: netStatsSubsystem, Name: key, Help: fmt.Sprintf("%s %s from /proc/net/{netstat,snmp}.", protocol, name), }, ) } v, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("invalid value %s in netstats: %s", value, err) } c.metrics[key].Set(v) } } for _, m := range c.metrics { m.Collect(ch) } return err } func getNetStats(fileName string) (map[string]map[string]string, error) { file, err := os.Open(fileName) if err != nil { return nil, err } defer file.Close() return parseNetStats(file, fileName) } func parseNetStats(r io.Reader, fileName string) (map[string]map[string]string, error) { var ( netStats = map[string]map[string]string{} scanner = bufio.NewScanner(r) ) for scanner.Scan() { nameParts := strings.Split(string(scanner.Text()), " ") scanner.Scan() valueParts := strings.Split(string(scanner.Text()), " ") // Remove trailing :. protocol := nameParts[0][:len(nameParts[0])-1] netStats[protocol] = map[string]string{} if len(nameParts) != len(valueParts) { return nil, fmt.Errorf("mismatch field count mismatch in %s: %s", fileName, protocol) } for i := 1; i < len(nameParts); i++ { netStats[protocol][nameParts[i]] = valueParts[i] } } return netStats, nil } prometheus-node-exporter-0.11.0+ds/collector/netstat_linux_test.go000066400000000000000000000011021257203701700254320ustar00rootroot00000000000000package collector import ( "os" "testing" ) var fileName = "fixtures/netstat" func TestNetStats(t *testing.T) { file, err := os.Open(fileName) if err != nil { t.Fatal(err) } defer file.Close() netStats, err := parseNetStats(file, fileName) if err != nil { t.Fatal(err) } if want, got := "102471", netStats["TcpExt"]["DelayedACKs"]; want != got { t.Errorf("want netstat TCP DelayedACKs %s, got %s", want, got) } if want, got := "2786264347", netStats["IpExt"]["OutOctets"]; want != got { t.Errorf("want netstat IP OutOctets %s, got %s", want, got) } } prometheus-node-exporter-0.11.0+ds/collector/ntp.go000066400000000000000000000022431257203701700223020ustar00rootroot00000000000000// +build !nontp package collector import ( "flag" "fmt" "time" "github.com/beevik/ntp" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) var ( ntpServer = flag.String("collector.ntp.server", "", "NTP server to use for ntp collector.") ) type ntpCollector struct { drift prometheus.Gauge } func init() { Factories["ntp"] = NewNtpCollector } // Takes a prometheus registry and returns a new Collector exposing // the offset between ntp and the current system time. func NewNtpCollector() (Collector, error) { if *ntpServer == "" { return nil, fmt.Errorf("No NTP server specifies, see --ntpServer") } return &ntpCollector{ drift: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Name: "ntp_drift_seconds", Help: "Time between system time and ntp time.", }), }, nil } func (c *ntpCollector) Update(ch chan<- prometheus.Metric) (err error) { t, err := ntp.Time(*ntpServer) if err != nil { return fmt.Errorf("Couldn't get ntp drift: %s", err) } drift := t.Sub(time.Now()) log.Debugf("Set ntp_drift_seconds: %f", drift.Seconds()) c.drift.Set(drift.Seconds()) c.drift.Collect(ch) return err } prometheus-node-exporter-0.11.0+ds/collector/runit.go000066400000000000000000000041061257203701700226420ustar00rootroot00000000000000// +build !norunit package collector import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" "github.com/soundcloud/go-runit/runit" ) type runitCollector struct { state, stateDesired, stateNormal *prometheus.GaugeVec } func init() { Factories["runit"] = NewRunitCollector } func NewRunitCollector() (Collector, error) { var ( subsystem = "service" constLabels = prometheus.Labels{"supervisor": "runit"} labelNames = []string{"service"} ) return &runitCollector{ state: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "state", Help: "state of runit service.", ConstLabels: constLabels, }, labelNames, ), stateDesired: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "desired_state", Help: "desired state of runit service.", ConstLabels: constLabels, }, labelNames, ), stateNormal: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Subsystem: subsystem, Name: "normal_state", Help: "normal state of runit service.", ConstLabels: constLabels, }, labelNames, ), }, nil } func (c *runitCollector) Update(ch chan<- prometheus.Metric) error { services, err := runit.GetServices("/etc/service") if err != nil { return err } for _, service := range services { status, err := service.Status() if err != nil { log.Debugf("Couldn't get status for %s: %s, skipping...", service.Name, err) continue } log.Debugf("%s is %d on pid %d for %d seconds", service.Name, status.State, status.Pid, status.Duration) c.state.WithLabelValues(service.Name).Set(float64(status.State)) c.stateDesired.WithLabelValues(service.Name).Set(float64(status.Want)) if status.NormallyUp { c.stateNormal.WithLabelValues(service.Name).Set(1) } else { c.stateNormal.WithLabelValues(service.Name).Set(0) } } c.state.Collect(ch) c.stateDesired.Collect(ch) c.stateNormal.Collect(ch) return nil } prometheus-node-exporter-0.11.0+ds/collector/stat_linux.go000066400000000000000000000101171257203701700236720ustar00rootroot00000000000000// +build !nostat package collector import ( "bufio" "os" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" ) const ( procStat = "/proc/stat" userHz = 100 ) type statCollector struct { cpu *prometheus.CounterVec intr prometheus.Counter ctxt prometheus.Counter forks prometheus.Counter btime prometheus.Gauge procsRunning prometheus.Gauge procsBlocked prometheus.Gauge } func init() { Factories["stat"] = NewStatCollector } // Takes a prometheus registry and returns a new Collector exposing // network device stats. func NewStatCollector() (Collector, error) { return &statCollector{ cpu: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, Name: "cpu", Help: "Seconds the cpus spent in each mode.", }, []string{"cpu", "mode"}, ), intr: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: Namespace, Name: "intr", Help: "Total number of interrupts serviced.", }), ctxt: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: Namespace, Name: "context_switches", Help: "Total number of context switches.", }), forks: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: Namespace, Name: "forks", Help: "Total number of forks.", }), btime: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Name: "boot_time", Help: "Node boot time, in unixtime.", }), procsRunning: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Name: "procs_running", Help: "Number of processes in runnable state.", }), procsBlocked: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: Namespace, Name: "procs_blocked", Help: "Number of processes blocked waiting for I/O to complete.", }), }, nil } // Expose a variety of stats from /proc/stats. func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) { file, err := os.Open(procStat) if err != nil { return err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { parts := strings.Fields(scanner.Text()) if len(parts) == 0 { continue } switch { case strings.HasPrefix(parts[0], "cpu"): // Export only per-cpu stats, it can be aggregated up in prometheus. if parts[0] == "cpu" { break } // Only some of these may be present, depending on kernel version. cpuFields := []string{"user", "nice", "system", "idle", "iowait", "irq", "softirq", "steal", "guest"} // OpenVZ guests lack the "guest" CPU field, which needs to be ignored. expectedFieldNum := len(cpuFields) + 1 if expectedFieldNum > len(parts) { expectedFieldNum = len(parts) } for i, v := range parts[1:expectedFieldNum] { value, err := strconv.ParseFloat(v, 64) if err != nil { return err } // Convert from ticks to seconds value /= userHz c.cpu.With(prometheus.Labels{"cpu": parts[0], "mode": cpuFields[i]}).Set(value) } case parts[0] == "intr": // Only expose the overall number, use the 'interrupts' collector for more detail. value, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } c.intr.Set(value) case parts[0] == "ctxt": value, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } c.ctxt.Set(value) case parts[0] == "processes": value, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } c.forks.Set(value) case parts[0] == "btime": value, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } c.btime.Set(value) case parts[0] == "procs_running": value, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } c.procsRunning.Set(value) case parts[0] == "procs_blocked": value, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } c.procsBlocked.Set(value) } } c.cpu.Collect(ch) c.ctxt.Collect(ch) c.intr.Collect(ch) c.forks.Collect(ch) c.btime.Collect(ch) c.procsRunning.Collect(ch) c.procsBlocked.Collect(ch) return err } prometheus-node-exporter-0.11.0+ds/collector/tcpstat_linux.go000066400000000000000000000053241257203701700244050ustar00rootroot00000000000000// +build !notcpstat package collector import ( "bufio" "fmt" "io" "os" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" ) const ( procTCPStat = "/proc/net/tcp" procTCP6Stat = "/proc/net/tcp6" ) type TCPConnectionState int const ( TCP_ESTABLISHED TCPConnectionState = iota + 1 TCP_SYN_SENT TCP_SYN_RECV TCP_FIN_WAIT1 TCP_FIN_WAIT2 TCP_TIME_WAIT TCP_CLOSE TCP_CLOSE_WAIT TCP_LAST_ACK TCP_LISTEN TCP_CLOSING ) type tcpStatCollector struct { metric *prometheus.GaugeVec } func init() { Factories["tcpstat"] = NewTCPStatCollector } // NewTCPStatCollector takes a returns // a new Collector exposing network stats. func NewTCPStatCollector() (Collector, error) { return &tcpStatCollector{ metric: prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, Name: "tcp_connection_states", Help: "Number of connection states.", }, []string{"state"}, ), }, nil } func (c *tcpStatCollector) Update(ch chan<- prometheus.Metric) (err error) { tcpStats, err := getTCPStats(procTCPStat) if err != nil { return fmt.Errorf("couldn't get tcpstats: %s", err) } // if enabled ipv6 system if _, hasIPv6 := os.Stat(procTCP6Stat); hasIPv6 == nil { tcp6Stats, err := getTCPStats(procTCP6Stat) if err != nil { return fmt.Errorf("couldn't get tcp6stats: %s", err) } for st, value := range tcp6Stats { tcpStats[st] += value } } for st, value := range tcpStats { c.metric.WithLabelValues(st.String()).Set(value) } c.metric.Collect(ch) return err } func getTCPStats(statsFile string) (map[TCPConnectionState]float64, error) { file, err := os.Open(statsFile) if err != nil { return nil, err } defer file.Close() return parseTCPStats(file) } func parseTCPStats(r io.Reader) (map[TCPConnectionState]float64, error) { var ( tcpStats = map[TCPConnectionState]float64{} scanner = bufio.NewScanner(r) ) for scanner.Scan() { parts := strings.Fields(scanner.Text()) if len(parts) == 0 { continue } if strings.HasPrefix(parts[0], "sl") { continue } st, err := strconv.ParseInt(parts[3], 16, 8) if err != nil { return nil, err } tcpStats[TCPConnectionState(st)]++ } return tcpStats, nil } func (st TCPConnectionState) String() string { switch st { case TCP_ESTABLISHED: return "established" case TCP_SYN_SENT: return "syn_sent" case TCP_SYN_RECV: return "syn_recv" case TCP_FIN_WAIT1: return "fin_wait1" case TCP_FIN_WAIT2: return "fin_wait2" case TCP_TIME_WAIT: return "time_wait" case TCP_CLOSE: return "close" case TCP_CLOSE_WAIT: return "close_wait" case TCP_LAST_ACK: return "last_ack" case TCP_LISTEN: return "listen" case TCP_CLOSING: return "closing" default: return "unknown" } } prometheus-node-exporter-0.11.0+ds/collector/tcpstat_linux_test.go000066400000000000000000000010321257203701700254340ustar00rootroot00000000000000package collector import ( "os" "testing" ) func TestTCPStat(t *testing.T) { file, err := os.Open("fixtures/tcpstat") if err != nil { t.Fatal(err) } defer file.Close() tcpStats, err := parseTCPStats(file) if err != nil { t.Fatal(err) } if want, got := 1, int(tcpStats[TCP_ESTABLISHED]); want != got { t.Errorf("want tcpstat number of established state %d, got %d", want, got) } if want, got := 1, int(tcpStats[TCP_LISTEN]); want != got { t.Errorf("want tcpstat number of listen state %d, got %d", want, got) } } prometheus-node-exporter-0.11.0+ds/collector/textfile.go000066400000000000000000000062441257203701700233320ustar00rootroot00000000000000// +build !notextfile package collector import ( "flag" "fmt" "io/ioutil" "os" "path/filepath" "strings" "time" dto "github.com/prometheus/client_model/go" "github.com/golang/protobuf/proto" "github.com/prometheus/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/text" ) var ( textFileDirectory = flag.String("collector.textfile.directory", "", "Directory to read text files with metrics from.") ) type textFileCollector struct { } func init() { Factories["textfile"] = NewTextFileCollector } // Takes a registers a // SetMetricFamilyInjectionHook. func NewTextFileCollector() (Collector, error) { if *textFileDirectory == "" { // This collector is enabled by default, so do not fail if // the flag is not passed. log.Infof("No directory specified, see --textfile.directory") } else { prometheus.SetMetricFamilyInjectionHook(parseTextFiles) } return &textFileCollector{}, nil } // textFile collector works via SetMetricFamilyInjectionHook in parseTextFiles. func (c *textFileCollector) Update(ch chan<- prometheus.Metric) (err error) { return nil } func parseTextFiles() []*dto.MetricFamily { var parser text.Parser error := 0.0 metricFamilies := make([]*dto.MetricFamily, 0) mtimes := map[string]time.Time{} // Iterate over files and accumulate their metrics. files, _ := ioutil.ReadDir(*textFileDirectory) for _, f := range files { if !strings.HasSuffix(f.Name(), ".prom") { continue } path := filepath.Join(*textFileDirectory, f.Name()) file, err := os.Open(path) if err != nil { log.Errorf("Error opening %s: %v", path, err) error = 1.0 continue } parsedFamilies, err := parser.TextToMetricFamilies(file) if err != nil { log.Errorf("Error parsing %s: %v", path, err) error = 1.0 continue } // Only set this once it has been parsed, so that // a failure does not appear fresh. mtimes[f.Name()] = f.ModTime() for _, mf := range parsedFamilies { if mf.Help == nil { help := fmt.Sprintf("Metric read from %s", path) mf.Help = &help } metricFamilies = append(metricFamilies, mf) } } // Export the mtimes of the successful files. if len(mtimes) > 0 { mtimeMetricFamily := dto.MetricFamily{ Name: proto.String("node_textfile_mtime"), Help: proto.String("Unixtime mtime of textfiles successfully read."), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{}, } for name, mtime := range mtimes { mtimeMetricFamily.Metric = append(mtimeMetricFamily.Metric, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("file"), Value: &name, }, }, Gauge: &dto.Gauge{Value: proto.Float64(float64(mtime.UnixNano()) / 1e9)}, }, ) } metricFamilies = append(metricFamilies, &mtimeMetricFamily) } // Export if there were errors. metricFamilies = append(metricFamilies, &dto.MetricFamily{ Name: proto.String("node_textfile_scrape_error"), Help: proto.String("1 if there was an error opening or reading a file, 0 otherwise"), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Gauge: &dto.Gauge{Value: &error}, }, }, }) return metricFamilies } prometheus-node-exporter-0.11.0+ds/collector/time.go000066400000000000000000000014671257203701700224460ustar00rootroot00000000000000// +build !notime package collector import ( "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" ) type timeCollector struct { metric prometheus.Counter } func init() { Factories["time"] = NewTimeCollector } // Takes a prometheus registry and returns a new Collector exposing // the current system time in seconds since epoch. func NewTimeCollector() (Collector, error) { return &timeCollector{ metric: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: Namespace, Name: "time", Help: "System time in seconds since epoch (1970).", }), }, nil } func (c *timeCollector) Update(ch chan<- prometheus.Metric) (err error) { now := time.Now() log.Debugf("Set time: %f", now.Unix()) c.metric.Set(float64(now.Unix())) c.metric.Collect(ch) return err } prometheus-node-exporter-0.11.0+ds/deps/000077500000000000000000000000001257203701700201165ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/000077500000000000000000000000001257203701700221555ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/beevik/000077500000000000000000000000001257203701700234225ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/beevik/ntp/000077500000000000000000000000001257203701700242235ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/beevik/ntp/LICENSE000066400000000000000000000024101257203701700252250ustar00rootroot00000000000000Copyright 2015 Brett Vickers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. prometheus-node-exporter-0.11.0+ds/deps/github.com/beevik/ntp/README.md000066400000000000000000000005341257203701700255040ustar00rootroot00000000000000ntp === The ntp package is a small implementation of a limited NTP client. It requests the current time from a remote NTP server according to version 4 of the NTP protocol in RFC 5905. The approach was inspired by a post to the go-nuts mailing list by Michael Hofmann: https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ prometheus-node-exporter-0.11.0+ds/deps/github.com/beevik/ntp/ntp.go000066400000000000000000000042511257203701700253550ustar00rootroot00000000000000// Copyright 2015 Brett Vickers. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package ntp provides a simple mechanism for querying the current time // from a remote NTP server. This package only supports NTP client mode // behavior and version 4 of the NTP protocol. See RFC 5905. // Approach inspired by go-nuts post by Michael Hofmann: // https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ package ntp import ( "encoding/binary" "net" "time" ) type mode byte const ( reserved mode = 0 + iota symmetricActive symmetricPassive client server broadcast controlMessage reservedPrivate ) type ntpTime struct { Seconds uint32 Fraction uint32 } func (t ntpTime) UTC() time.Time { nsec := uint64(t.Seconds)*1e9 + (uint64(t.Fraction) * 1e9 >> 32) return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nsec)) } type msg struct { LiVnMode byte // Leap Indicator (2) + Version (3) + Mode (3) Stratum byte Poll byte Precision byte RootDelay uint32 RootDispersion uint32 ReferenceId uint32 ReferenceTime ntpTime OriginTime ntpTime ReceiveTime ntpTime TransmitTime ntpTime } // SetVersion sets the NTP protocol version on the message. func (m *msg) SetVersion(v byte) { m.LiVnMode = (m.LiVnMode & 0xc7) | v<<3 } // SetMode sets the NTP protocol mode on the message. func (m *msg) SetMode(md mode) { m.LiVnMode = (m.LiVnMode & 0xf8) | byte(md) } // Time returns the "receive time" from the remote NTP server // specifed as host. NTP client mode is used. func Time(host string) (time.Time, error) { raddr, err := net.ResolveUDPAddr("udp", host+":123") if err != nil { return time.Now(), err } con, err := net.DialUDP("udp", nil, raddr) if err != nil { return time.Now(), err } defer con.Close() con.SetDeadline(time.Now().Add(5 * time.Second)) m := new(msg) m.SetMode(client) m.SetVersion(4) err = binary.Write(con, binary.BigEndian, m) if err != nil { return time.Now(), err } err = binary.Read(con, binary.BigEndian, m) if err != nil { return time.Now(), err } t := m.ReceiveTime.UTC().Local() return t, nil } prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/000077500000000000000000000000001257203701700243345ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/000077500000000000000000000000001257203701700261005ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/LICENSE000066400000000000000000000020721257203701700271060ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 SoundCloud Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/README.md000066400000000000000000000000741257203701700273600ustar00rootroot00000000000000go-runit ======== go library wrapping runit service status prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/000077500000000000000000000000001257203701700272415ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/000077500000000000000000000000001257203701700311125ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/000077500000000000000000000000001257203701700325525ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foo/000077500000000000000000000000001257203701700333355ustar00rootroot00000000000000log/000077500000000000000000000000001257203701700340375ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foosupervise/000077500000000000000000000000001257203701700360645ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foo/logstat000066400000000000000000000000041257203701700367540ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foo/log/superviserun status000066400000000000000000000000241257203701700373260ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foo/log/supervise@Rzܙusupervise/000077500000000000000000000000001257203701700353035ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foostat000066400000000000000000000000041257203701700361730ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foo/superviserun status000066400000000000000000000000241257203701700365450ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/service/foo/supervise@Rt{usymlink-service000077700000000000000000000000001257203701700410172../symlink-serviceustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/servicesymlink-service/000077500000000000000000000000001257203701700341575ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtureslog/000077500000000000000000000000001257203701700347405ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-servicesupervise/000077500000000000000000000000001257203701700367655ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-service/logstat000066400000000000000000000000041257203701700376550ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-service/log/superviserun status000066400000000000000000000000241257203701700402270ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-service/log/supervise@Rzܙusupervise/000077500000000000000000000000001257203701700362045ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-servicestat000066400000000000000000000000041257203701700370740ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-service/superviserun status000066400000000000000000000000241257203701700374460ustar00rootroot00000000000000prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/fixtures/symlink-service/supervise@Rt{uprometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/runit.go000066400000000000000000000053431257203701700307360ustar00rootroot00000000000000package runit import ( "errors" "fmt" "io/ioutil" "os" "syscall" "time" ) const ( defaultServiceDir = "/etc/service" taiOffset = 4611686018427387914 statusLen = 20 posTimeStart = 0 posTimeEnd = 7 posPidStart = 12 posPidEnd = 15 posWant = 17 posState = 19 StateDown = 0 StateUp = 1 StateFinish = 2 ) var ( ENoRunsv = errors.New("runsv not running") StateToString = map[int]string{ StateDown: "down", StateUp: "up", StateFinish: "finish", } ) type SvStatus struct { Pid int Duration int Timestamp time.Time State int NormallyUp bool Want int } type service struct { Name string ServiceDir string } func GetServices(dir string) ([]*service, error) { if dir == "" { dir = defaultServiceDir } files, err := ioutil.ReadDir(dir) if err != nil { return nil, err } services := []*service{} for _, file := range files { if file.Mode()&os.ModeSymlink == os.ModeSymlink || file.IsDir() { services = append(services, GetService(file.Name(), dir)) } } return services, nil } func GetService(name string, dir string) *service { if dir == "" { dir = defaultServiceDir } r := service{Name: name, ServiceDir: dir} return &r } func (s *service) file(file string) string { return fmt.Sprintf("%s/%s/supervise/%s", s.ServiceDir, s.Name, file) } func (s *service) runsvRunning() (bool, error) { file, err := os.OpenFile(s.file("ok"), os.O_WRONLY, 0) if err != nil { if err == syscall.ENXIO { return false, nil } return false, err } file.Close() return true, nil } func (s *service) status() ([]byte, error) { file, err := os.Open(s.file("status")) if err != nil { return nil, err } defer file.Close() status := make([]byte, statusLen) _, err = file.Read(status) return status, err } func (s *service) NormallyUp() bool { _, err := os.Stat(s.file("down")) return err != nil } func (s *service) Status() (*SvStatus, error) { status, err := s.status() if err != nil { return nil, err } var pid int pid = int(status[posPidEnd]) for i := posPidEnd - 1; i >= posPidStart; i-- { pid <<= 8 pid += int(status[i]) } tai := int64(status[posTimeStart]) for i := posTimeStart + 1; i <= posTimeEnd; i++ { tai <<= 8 tai += int64(status[i]) } state := status[posState] // 0: down, 1: run, 2: finish tv := &syscall.Timeval{} if err := syscall.Gettimeofday(tv); err != nil { return nil, err } sS := SvStatus{ Pid: pid, Timestamp: time.Unix(tai-taiOffset, 0), // FIXME: do we just select the wrong slice? Duration: int(int64(tv.Sec) - (tai - taiOffset)), State: int(state), NormallyUp: s.NormallyUp(), } switch status[posWant] { case 'u': sS.Want = StateUp case 'd': sS.Want = StateDown } return &sS, nil } prometheus-node-exporter-0.11.0+ds/deps/github.com/soundcloud/go-runit/runit/runit_test.go000066400000000000000000000015511257203701700317720ustar00rootroot00000000000000package runit import ( "testing" "time" ) const expectedServiceNum = 2 var fooTimeStamp = time.Date(2013, 11, 2, 11, 02, 53, 0, time.FixedZone("CET", 3600)) func TestGetServices(t *testing.T) { services, err := GetServices("fixtures/service") if err != nil { t.Fatal(err) } if len(services) != expectedServiceNum { t.Fatalf("Expected to find %d services but found %d", expectedServiceNum, len(services)) } for _, service := range services { status, err := service.Status() if err != nil { t.Fatal(err) } switch service.Name { case "foo", "symlink-service": if status.Pid != 123 || status.Timestamp == fooTimeStamp || status.State != StateUp || !status.NormallyUp || status.Want != StateUp { t.Fatalf("Service '%s' in unexpected state", service.Name) } default: t.Fatalf("Unexpected service '%s'", service.Name) } } } prometheus-node-exporter-0.11.0+ds/node_exporter.conf000066400000000000000000000002361257203701700227100ustar00rootroot00000000000000{ "attributes" : { "default" : "1", "web_server" : "1", "zone" : "a" }, "config" : { "megacli_command" : "megacli.sh" } } prometheus-node-exporter-0.11.0+ds/node_exporter.go000066400000000000000000000113671257203701700223770ustar00rootroot00000000000000package main import ( "flag" "fmt" "net/http" _ "net/http/pprof" "os" "os/signal" "sort" "strings" "sync" "syscall" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/log" "github.com/prometheus/node_exporter/collector" ) const subsystem = "exporter" var ( // set at build time Version = "0.0.0.dev" memProfile = flag.String("debug.memprofile-file", "", "Write memory profile to this file upon receipt of SIGUSR1.") listenAddress = flag.String("web.listen-address", ":9100", "Address on which to expose metrics and web interface.") metricsPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.") enabledCollectors = flag.String("collectors.enabled", "diskstats,filesystem,loadavg,meminfo,stat,textfile,time,netdev,netstat", "Comma-separated list of collectors to use.") printCollectors = flag.Bool("collectors.print", false, "If true, print available collectors and exit.") authUser = flag.String("auth.user", "", "Username for basic auth.") authPass = flag.String("auth.pass", "", "Password for basic auth.") collectorLabelNames = []string{"collector", "result"} scrapeDurations = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Namespace: collector.Namespace, Subsystem: subsystem, Name: "scrape_duration_seconds", Help: "node_exporter: Duration of a scrape job.", }, collectorLabelNames, ) ) // Implements Collector. type NodeCollector struct { collectors map[string]collector.Collector } // Implements Collector. func (n NodeCollector) Describe(ch chan<- *prometheus.Desc) { scrapeDurations.Describe(ch) } // Implements Collector. func (n NodeCollector) Collect(ch chan<- prometheus.Metric) { wg := sync.WaitGroup{} wg.Add(len(n.collectors)) for name, c := range n.collectors { go func(name string, c collector.Collector) { Execute(name, c, ch) wg.Done() }(name, c) } wg.Wait() scrapeDurations.Collect(ch) } type basicAuthHandler struct { handler http.HandlerFunc user string password string } func (h *basicAuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { user, password, ok := r.BasicAuth() if !ok || password != h.password || user != h.user { w.Header().Set("WWW-Authenticate", "Basic realm=\"metrics\"") http.Error(w, "Invalid username or password", http.StatusUnauthorized) return } h.handler(w, r) return } func Execute(name string, c collector.Collector, ch chan<- prometheus.Metric) { begin := time.Now() err := c.Update(ch) duration := time.Since(begin) var result string if err != nil { log.Errorf("ERROR: %s collector failed after %fs: %s", name, duration.Seconds(), err) result = "error" } else { log.Debugf("OK: %s collector succeeded after %fs.", name, duration.Seconds()) result = "success" } scrapeDurations.WithLabelValues(name, result).Observe(duration.Seconds()) } func loadCollectors() (map[string]collector.Collector, error) { collectors := map[string]collector.Collector{} for _, name := range strings.Split(*enabledCollectors, ",") { fn, ok := collector.Factories[name] if !ok { return nil, fmt.Errorf("collector '%s' not available", name) } c, err := fn() if err != nil { return nil, err } collectors[name] = c } return collectors, nil } func main() { flag.Parse() if *printCollectors { collectorNames := make(sort.StringSlice, 0, len(collector.Factories)) for n, _ := range collector.Factories { collectorNames = append(collectorNames, n) } collectorNames.Sort() fmt.Printf("Available collectors:\n") for _, n := range collectorNames { fmt.Printf(" - %s\n", n) } return } collectors, err := loadCollectors() if err != nil { log.Fatalf("Couldn't load collectors: %s", err) } log.Infof("Enabled collectors:") for n, _ := range collectors { log.Infof(" - %s", n) } nodeCollector := NodeCollector{collectors: collectors} prometheus.MustRegister(nodeCollector) sigUsr1 := make(chan os.Signal) signal.Notify(sigUsr1, syscall.SIGUSR1) handler := prometheus.Handler() if *authUser != "" || *authPass != "" { if *authUser == "" || *authPass == "" { log.Fatal("You need to specify -auth.user and -auth.pass to enable basic auth") } handler = &basicAuthHandler{ handler: prometheus.Handler().ServeHTTP, user: *authUser, password: *authPass, } } http.Handle(*metricsPath, handler) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` Node Exporter

Node Exporter

Metrics

`)) }) log.Infof("Starting node_exporter v%s at %s", Version, *listenAddress) err = http.ListenAndServe(*listenAddress, nil) if err != nil { log.Fatal(err) } }