summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/prometheus
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/prometheus')
-rw-r--r--vendor/github.com/prometheus/common/expfmt/decode.go47
-rw-r--r--vendor/github.com/prometheus/common/expfmt/decode_test.go68
-rw-r--r--vendor/github.com/prometheus/common/expfmt/expfmt.go7
-rw-r--r--vendor/github.com/prometheus/common/log/syslog_formatter.go11
-rw-r--r--vendor/github.com/prometheus/common/log/syslog_formatter_test.go52
-rw-r--r--vendor/github.com/prometheus/common/model/metric.go2
-rw-r--r--vendor/github.com/prometheus/common/route/route.go7
-rw-r--r--vendor/github.com/prometheus/common/route/route_test.go2
-rw-r--r--vendor/github.com/prometheus/procfs/.travis.yml4
-rw-r--r--vendor/github.com/prometheus/procfs/AUTHORS.md1
-rw-r--r--vendor/github.com/prometheus/procfs/fixtures/26231/mountstats19
-rw-r--r--vendor/github.com/prometheus/procfs/mountstats.go552
-rw-r--r--vendor/github.com/prometheus/procfs/mountstats_test.go252
-rw-r--r--vendor/github.com/prometheus/procfs/proc.go12
14 files changed, 1009 insertions, 27 deletions
diff --git a/vendor/github.com/prometheus/common/expfmt/decode.go b/vendor/github.com/prometheus/common/expfmt/decode.go
index 487fdc6cc..a7a42d5ef 100644
--- a/vendor/github.com/prometheus/common/expfmt/decode.go
+++ b/vendor/github.com/prometheus/common/expfmt/decode.go
@@ -31,6 +31,7 @@ type Decoder interface {
Decode(*dto.MetricFamily) error
}
+// DecodeOptions contains options used by the Decoder and in sample extraction.
type DecodeOptions struct {
// Timestamp is added to each value from the stream that has no explicit timestamp set.
Timestamp model.Time
@@ -142,6 +143,8 @@ func (d *textDecoder) Decode(v *dto.MetricFamily) error {
return nil
}
+// SampleDecoder wraps a Decoder to extract samples from the metric families
+// decoded by the wrapped Decoder.
type SampleDecoder struct {
Dec Decoder
Opts *DecodeOptions
@@ -149,37 +152,51 @@ type SampleDecoder struct {
f dto.MetricFamily
}
+// Decode calls the Decode method of the wrapped Decoder and then extracts the
+// samples from the decoded MetricFamily into the provided model.Vector.
func (sd *SampleDecoder) Decode(s *model.Vector) error {
- if err := sd.Dec.Decode(&sd.f); err != nil {
+ err := sd.Dec.Decode(&sd.f)
+ if err != nil {
return err
}
- *s = extractSamples(&sd.f, sd.Opts)
- return nil
+ *s, err = extractSamples(&sd.f, sd.Opts)
+ return err
}
-// Extract samples builds a slice of samples from the provided metric families.
-func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) model.Vector {
- var all model.Vector
+// ExtractSamples builds a slice of samples from the provided metric
+// families. If an error occurs during sample extraction, it continues to
+// extract from the remaining metric families. The returned error is the last
+// error that has occured.
+func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
+ var (
+ all model.Vector
+ lastErr error
+ )
for _, f := range fams {
- all = append(all, extractSamples(f, o)...)
+ some, err := extractSamples(f, o)
+ if err != nil {
+ lastErr = err
+ continue
+ }
+ all = append(all, some...)
}
- return all
+ return all, lastErr
}
-func extractSamples(f *dto.MetricFamily, o *DecodeOptions) model.Vector {
+func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
switch f.GetType() {
case dto.MetricType_COUNTER:
- return extractCounter(o, f)
+ return extractCounter(o, f), nil
case dto.MetricType_GAUGE:
- return extractGauge(o, f)
+ return extractGauge(o, f), nil
case dto.MetricType_SUMMARY:
- return extractSummary(o, f)
+ return extractSummary(o, f), nil
case dto.MetricType_UNTYPED:
- return extractUntyped(o, f)
+ return extractUntyped(o, f), nil
case dto.MetricType_HISTOGRAM:
- return extractHistogram(o, f)
+ return extractHistogram(o, f), nil
}
- panic("expfmt.extractSamples: unknown metric family type")
+ return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
}
func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
diff --git a/vendor/github.com/prometheus/common/expfmt/decode_test.go b/vendor/github.com/prometheus/common/expfmt/decode_test.go
index c27325a9d..82c1130c9 100644
--- a/vendor/github.com/prometheus/common/expfmt/decode_test.go
+++ b/vendor/github.com/prometheus/common/expfmt/decode_test.go
@@ -21,6 +21,9 @@ import (
"strings"
"testing"
+ "github.com/golang/protobuf/proto"
+ dto "github.com/prometheus/client_model/go"
+
"github.com/prometheus/common/model"
)
@@ -365,3 +368,68 @@ func BenchmarkDiscriminatorHTTPHeader(b *testing.B) {
testDiscriminatorHTTPHeader(b)
}
}
+
+func TestExtractSamples(t *testing.T) {
+ var (
+ goodMetricFamily1 = &dto.MetricFamily{
+ Name: proto.String("foo"),
+ Help: proto.String("Help for foo."),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Counter: &dto.Counter{
+ Value: proto.Float64(4711),
+ },
+ },
+ },
+ }
+ goodMetricFamily2 = &dto.MetricFamily{
+ Name: proto.String("bar"),
+ Help: proto.String("Help for bar."),
+ Type: dto.MetricType_GAUGE.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Gauge: &dto.Gauge{
+ Value: proto.Float64(3.14),
+ },
+ },
+ },
+ }
+ badMetricFamily = &dto.MetricFamily{
+ Name: proto.String("bad"),
+ Help: proto.String("Help for bad."),
+ Type: dto.MetricType(42).Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Gauge: &dto.Gauge{
+ Value: proto.Float64(2.7),
+ },
+ },
+ },
+ }
+
+ opts = &DecodeOptions{
+ Timestamp: 42,
+ }
+ )
+
+ got, err := ExtractSamples(opts, goodMetricFamily1, goodMetricFamily2)
+ if err != nil {
+ t.Error("Unexpected error from ExtractSamples:", err)
+ }
+ want := model.Vector{
+ &model.Sample{Metric: model.Metric{model.MetricNameLabel: "foo"}, Value: 4711, Timestamp: 42},
+ &model.Sample{Metric: model.Metric{model.MetricNameLabel: "bar"}, Value: 3.14, Timestamp: 42},
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("unexpected samples extracted, got: %v, want: %v", got, want)
+ }
+
+ got, err = ExtractSamples(opts, goodMetricFamily1, badMetricFamily, goodMetricFamily2)
+ if err == nil {
+ t.Error("Expected error from ExtractSamples")
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("unexpected samples extracted, got: %v, want: %v", got, want)
+ }
+}
diff --git a/vendor/github.com/prometheus/common/expfmt/expfmt.go b/vendor/github.com/prometheus/common/expfmt/expfmt.go
index fae10f6eb..371ac7503 100644
--- a/vendor/github.com/prometheus/common/expfmt/expfmt.go
+++ b/vendor/github.com/prometheus/common/expfmt/expfmt.go
@@ -11,14 +11,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// A package for reading and writing Prometheus metrics.
+// Package expfmt contains tools for reading and writing Prometheus metrics.
package expfmt
+// Format specifies the HTTP content type of the different wire protocols.
type Format string
+// Constants to assemble the Content-Type values for the different wire protocols.
const (
- TextVersion = "0.0.4"
-
+ TextVersion = "0.0.4"
ProtoType = `application/vnd.google.protobuf`
ProtoProtocol = `io.prometheus.client.MetricFamily`
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
diff --git a/vendor/github.com/prometheus/common/log/syslog_formatter.go b/vendor/github.com/prometheus/common/log/syslog_formatter.go
index fd8c6fbee..64f5fdac9 100644
--- a/vendor/github.com/prometheus/common/log/syslog_formatter.go
+++ b/vendor/github.com/prometheus/common/log/syslog_formatter.go
@@ -23,6 +23,8 @@ import (
"github.com/Sirupsen/logrus"
)
+var _ logrus.Formatter = (*syslogger)(nil)
+
func init() {
setSyslogFormatter = func(appname, local string) error {
if appname == "" {
@@ -43,7 +45,7 @@ func init() {
}
}
-var ceeTag = []byte("@cee:")
+var prefixTag []byte
type syslogger struct {
wrap logrus.Formatter
@@ -56,6 +58,11 @@ func newSyslogger(appname string, facility string, fmter logrus.Formatter) (*sys
return nil, err
}
out, err := syslog.New(priority, appname)
+ _, isJSON := fmter.(*logrus.JSONFormatter)
+ if isJSON {
+ // add cee tag to json formatted syslogs
+ prefixTag = []byte("@cee:")
+ }
return &syslogger{
out: out,
wrap: fmter,
@@ -92,7 +99,7 @@ func (s *syslogger) Format(e *logrus.Entry) ([]byte, error) {
}
// only append tag to data sent to syslog (line), not to what
// is returned
- line := string(append(ceeTag, data...))
+ line := string(append(prefixTag, data...))
switch e.Level {
case logrus.PanicLevel:
diff --git a/vendor/github.com/prometheus/common/log/syslog_formatter_test.go b/vendor/github.com/prometheus/common/log/syslog_formatter_test.go
new file mode 100644
index 000000000..b7e68848f
--- /dev/null
+++ b/vendor/github.com/prometheus/common/log/syslog_formatter_test.go
@@ -0,0 +1,52 @@
+// 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.
+
+// +build !windows,!nacl,!plan9
+
+package log
+
+import (
+ "errors"
+ "log/syslog"
+ "testing"
+)
+
+func TestGetFacility(t *testing.T) {
+ testCases := []struct {
+ facility string
+ expectedPriority syslog.Priority
+ expectedErr error
+ }{
+ {"0", syslog.LOG_LOCAL0, nil},
+ {"1", syslog.LOG_LOCAL1, nil},
+ {"2", syslog.LOG_LOCAL2, nil},
+ {"3", syslog.LOG_LOCAL3, nil},
+ {"4", syslog.LOG_LOCAL4, nil},
+ {"5", syslog.LOG_LOCAL5, nil},
+ {"6", syslog.LOG_LOCAL6, nil},
+ {"7", syslog.LOG_LOCAL7, nil},
+ {"8", syslog.LOG_LOCAL0, errors.New("invalid local(8) for syslog")},
+ }
+ for _, tc := range testCases {
+ priority, err := getFacility(tc.facility)
+ if err != tc.expectedErr {
+ if err.Error() != tc.expectedErr.Error() {
+ t.Errorf("want %s, got %s", tc.expectedErr.Error(), err.Error())
+ }
+ }
+
+ if priority != tc.expectedPriority {
+ t.Errorf("want %q, got %q", tc.expectedPriority, priority)
+ }
+ }
+}
diff --git a/vendor/github.com/prometheus/common/model/metric.go b/vendor/github.com/prometheus/common/model/metric.go
index 9dff899cb..f7250909b 100644
--- a/vendor/github.com/prometheus/common/model/metric.go
+++ b/vendor/github.com/prometheus/common/model/metric.go
@@ -44,7 +44,7 @@ func (m Metric) Before(o Metric) bool {
// Clone returns a copy of the Metric.
func (m Metric) Clone() Metric {
- clone := Metric{}
+ clone := make(Metric, len(m))
for k, v := range m {
clone[k] = v
}
diff --git a/vendor/github.com/prometheus/common/route/route.go b/vendor/github.com/prometheus/common/route/route.go
index 930b52d4f..1e5638ed9 100644
--- a/vendor/github.com/prometheus/common/route/route.go
+++ b/vendor/github.com/prometheus/common/route/route.go
@@ -33,18 +33,19 @@ func WithParam(ctx context.Context, p, v string) context.Context {
return context.WithValue(ctx, param(p), v)
}
-type contextFn func(r *http.Request) (context.Context, error)
+// ContextFunc returns a new context for a request.
+type ContextFunc func(r *http.Request) (context.Context, error)
// Router wraps httprouter.Router and adds support for prefixed sub-routers
// and per-request context injections.
type Router struct {
rtr *httprouter.Router
prefix string
- ctxFn contextFn
+ ctxFn ContextFunc
}
// New returns a new Router.
-func New(ctxFn contextFn) *Router {
+func New(ctxFn ContextFunc) *Router {
if ctxFn == nil {
ctxFn = func(r *http.Request) (context.Context, error) {
return context.Background(), nil
diff --git a/vendor/github.com/prometheus/common/route/route_test.go b/vendor/github.com/prometheus/common/route/route_test.go
index 4055d69d5..e7b1cba33 100644
--- a/vendor/github.com/prometheus/common/route/route_test.go
+++ b/vendor/github.com/prometheus/common/route/route_test.go
@@ -29,7 +29,7 @@ func TestRedirect(t *testing.T) {
}
}
-func TestContextFn(t *testing.T) {
+func TestContextFunc(t *testing.T) {
router := New(func(r *http.Request) (context.Context, error) {
return context.WithValue(context.Background(), "testkey", "testvalue"), nil
})
diff --git a/vendor/github.com/prometheus/procfs/.travis.yml b/vendor/github.com/prometheus/procfs/.travis.yml
index 2b4554da5..a9e28bf5d 100644
--- a/vendor/github.com/prometheus/procfs/.travis.yml
+++ b/vendor/github.com/prometheus/procfs/.travis.yml
@@ -1,5 +1,5 @@
sudo: false
language: go
go:
- - 1.5
- - 1.6
+ - 1.6.4
+ - 1.7.4
diff --git a/vendor/github.com/prometheus/procfs/AUTHORS.md b/vendor/github.com/prometheus/procfs/AUTHORS.md
index 0c802dd87..d55863560 100644
--- a/vendor/github.com/prometheus/procfs/AUTHORS.md
+++ b/vendor/github.com/prometheus/procfs/AUTHORS.md
@@ -14,6 +14,7 @@ The following individuals have contributed code to this repository
* Ji-Hoon, Seol <jihoon.seol@gmail.com>
* Jonas Große Sundrup <cherti@letopolis.de>
* Julius Volz <julius.volz@gmail.com>
+* Matt Layher <mdlayher@gmail.com>
* Matthias Rampke <mr@soundcloud.com>
* Nicky Gerritsen <nicky@streamone.nl>
* Rémi Audebert <contact@halfr.net>
diff --git a/vendor/github.com/prometheus/procfs/fixtures/26231/mountstats b/vendor/github.com/prometheus/procfs/fixtures/26231/mountstats
new file mode 100644
index 000000000..a665c33da
--- /dev/null
+++ b/vendor/github.com/prometheus/procfs/fixtures/26231/mountstats
@@ -0,0 +1,19 @@
+device rootfs mounted on / with fstype rootfs
+device sysfs mounted on /sys with fstype sysfs
+device proc mounted on /proc with fstype proc
+device /dev/sda1 mounted on / with fstype ext4
+device 192.168.1.1:/srv/test mounted on /mnt/nfs/test with fstype nfs4 statvers=1.1
+ opts: rw,vers=4.0,rsize=1048576,wsize=1048576,namlen=255,acregmin=3,acregmax=60,acdirmin=30,acdirmax=60,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.1.5,local_lock=none
+ age: 13968
+ caps: caps=0xfff7,wtmult=512,dtsize=32768,bsize=0,namlen=255
+ nfsv4: bm0=0xfdffafff,bm1=0xf9be3e,bm2=0x0,acl=0x0,pnfs=not configured
+ sec: flavor=1,pseudoflavor=1
+ events: 52 226 0 0 1 13 398 0 0 331 0 47 0 0 77 0 0 77 0 0 0 0 0 0 0 0 0
+ bytes: 1207640230 0 0 0 1210214218 0 295483 0
+ RPC iostats version: 1.0 p/v: 100003/4 (nfs)
+ xprt: tcp 832 0 1 0 11 6428 6428 0 12154 0 24 26 5726
+ per-op statistics
+ NULL: 0 0 0 0 0 0 0 0
+ READ: 1298 1298 0 207680 1210292152 6 79386 79407
+ WRITE: 0 0 0 0 0 0 0 0
+
diff --git a/vendor/github.com/prometheus/procfs/mountstats.go b/vendor/github.com/prometheus/procfs/mountstats.go
new file mode 100644
index 000000000..47ab0a744
--- /dev/null
+++ b/vendor/github.com/prometheus/procfs/mountstats.go
@@ -0,0 +1,552 @@
+package procfs
+
+// While implementing parsing of /proc/[pid]/mountstats, this blog was used
+// heavily as a reference:
+// https://utcc.utoronto.ca/~cks/space/blog/linux/NFSMountstatsIndex
+//
+// Special thanks to Chris Siebenmann for all of his posts explaining the
+// various statistics available for NFS.
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Constants shared between multiple functions.
+const (
+ deviceEntryLen = 8
+
+ fieldBytesLen = 8
+ fieldEventsLen = 27
+
+ statVersion10 = "1.0"
+ statVersion11 = "1.1"
+
+ fieldTransport10Len = 10
+ fieldTransport11Len = 13
+)
+
+// A Mount is a device mount parsed from /proc/[pid]/mountstats.
+type Mount struct {
+ // Name of the device.
+ Device string
+ // The mount point of the device.
+ Mount string
+ // The filesystem type used by the device.
+ Type string
+ // If available additional statistics related to this Mount.
+ // Use a type assertion to determine if additional statistics are available.
+ Stats MountStats
+}
+
+// A MountStats is a type which contains detailed statistics for a specific
+// type of Mount.
+type MountStats interface {
+ mountStats()
+}
+
+// A MountStatsNFS is a MountStats implementation for NFSv3 and v4 mounts.
+type MountStatsNFS struct {
+ // The version of statistics provided.
+ StatVersion string
+ // The age of the NFS mount.
+ Age time.Duration
+ // Statistics related to byte counters for various operations.
+ Bytes NFSBytesStats
+ // Statistics related to various NFS event occurrences.
+ Events NFSEventsStats
+ // Statistics broken down by filesystem operation.
+ Operations []NFSOperationStats
+ // Statistics about the NFS RPC transport.
+ Transport NFSTransportStats
+}
+
+// mountStats implements MountStats.
+func (m MountStatsNFS) mountStats() {}
+
+// A NFSBytesStats contains statistics about the number of bytes read and written
+// by an NFS client to and from an NFS server.
+type NFSBytesStats struct {
+ // Number of bytes read using the read() syscall.
+ Read uint64
+ // Number of bytes written using the write() syscall.
+ Write uint64
+ // Number of bytes read using the read() syscall in O_DIRECT mode.
+ DirectRead uint64
+ // Number of bytes written using the write() syscall in O_DIRECT mode.
+ DirectWrite uint64
+ // Number of bytes read from the NFS server, in total.
+ ReadTotal uint64
+ // Number of bytes written to the NFS server, in total.
+ WriteTotal uint64
+ // Number of pages read directly via mmap()'d files.
+ ReadPages uint64
+ // Number of pages written directly via mmap()'d files.
+ WritePages uint64
+}
+
+// A NFSEventsStats contains statistics about NFS event occurrences.
+type NFSEventsStats struct {
+ // Number of times cached inode attributes are re-validated from the server.
+ InodeRevalidate uint64
+ // Number of times cached dentry nodes are re-validated from the server.
+ DnodeRevalidate uint64
+ // Number of times an inode cache is cleared.
+ DataInvalidate uint64
+ // Number of times cached inode attributes are invalidated.
+ AttributeInvalidate uint64
+ // Number of times files or directories have been open()'d.
+ VFSOpen uint64
+ // Number of times a directory lookup has occurred.
+ VFSLookup uint64
+ // Number of times permissions have been checked.
+ VFSAccess uint64
+ // Number of updates (and potential writes) to pages.
+ VFSUpdatePage uint64
+ // Number of pages read directly via mmap()'d files.
+ VFSReadPage uint64
+ // Number of times a group of pages have been read.
+ VFSReadPages uint64
+ // Number of pages written directly via mmap()'d files.
+ VFSWritePage uint64
+ // Number of times a group of pages have been written.
+ VFSWritePages uint64
+ // Number of times directory entries have been read with getdents().
+ VFSGetdents uint64
+ // Number of times attributes have been set on inodes.
+ VFSSetattr uint64
+ // Number of pending writes that have been forcefully flushed to the server.
+ VFSFlush uint64
+ // Number of times fsync() has been called on directories and files.
+ VFSFsync uint64
+ // Number of times locking has been attemped on a file.
+ VFSLock uint64
+ // Number of times files have been closed and released.
+ VFSFileRelease uint64
+ // Unknown. Possibly unused.
+ CongestionWait uint64
+ // Number of times files have been truncated.
+ Truncation uint64
+ // Number of times a file has been grown due to writes beyond its existing end.
+ WriteExtension uint64
+ // Number of times a file was removed while still open by another process.
+ SillyRename uint64
+ // Number of times the NFS server gave less data than expected while reading.
+ ShortRead uint64
+ // Number of times the NFS server wrote less data than expected while writing.
+ ShortWrite uint64
+ // Number of times the NFS server indicated EJUKEBOX; retrieving data from
+ // offline storage.
+ JukeboxDelay uint64
+ // Number of NFS v4.1+ pNFS reads.
+ PNFSRead uint64
+ // Number of NFS v4.1+ pNFS writes.
+ PNFSWrite uint64
+}
+
+// A NFSOperationStats contains statistics for a single operation.
+type NFSOperationStats struct {
+ // The name of the operation.
+ Operation string
+ // Number of requests performed for this operation.
+ Requests uint64
+ // Number of times an actual RPC request has been transmitted for this operation.
+ Transmissions uint64
+ // Number of times a request has had a major timeout.
+ MajorTimeouts uint64
+ // Number of bytes sent for this operation, including RPC headers and payload.
+ BytesSent uint64
+ // Number of bytes received for this operation, including RPC headers and payload.
+ BytesReceived uint64
+ // Duration all requests spent queued for transmission before they were sent.
+ CumulativeQueueTime time.Duration
+ // Duration it took to get a reply back after the request was transmitted.
+ CumulativeTotalResponseTime time.Duration
+ // Duration from when a request was enqueued to when it was completely handled.
+ CumulativeTotalRequestTime time.Duration
+}
+
+// A NFSTransportStats contains statistics for the NFS mount RPC requests and
+// responses.
+type NFSTransportStats struct {
+ // The local port used for the NFS mount.
+ Port uint64
+ // Number of times the client has had to establish a connection from scratch
+ // to the NFS server.
+ Bind uint64
+ // Number of times the client has made a TCP connection to the NFS server.
+ Connect uint64
+ // Duration (in jiffies, a kernel internal unit of time) the NFS mount has
+ // spent waiting for connections to the server to be established.
+ ConnectIdleTime uint64
+ // Duration since the NFS mount last saw any RPC traffic.
+ IdleTime time.Duration
+ // Number of RPC requests for this mount sent to the NFS server.
+ Sends uint64
+ // Number of RPC responses for this mount received from the NFS server.
+ Receives uint64
+ // Number of times the NFS server sent a response with a transaction ID
+ // unknown to this client.
+ BadTransactionIDs uint64
+ // A running counter, incremented on each request as the current difference
+ // ebetween sends and receives.
+ CumulativeActiveRequests uint64
+ // A running counter, incremented on each request by the current backlog
+ // queue size.
+ CumulativeBacklog uint64
+
+ // Stats below only available with stat version 1.1.
+
+ // Maximum number of simultaneously active RPC requests ever used.
+ MaximumRPCSlotsUsed uint64
+ // A running counter, incremented on each request as the current size of the
+ // sending queue.
+ CumulativeSendingQueue uint64
+ // A running counter, incremented on each request as the current size of the
+ // pending queue.
+ CumulativePendingQueue uint64
+}
+
+// parseMountStats parses a /proc/[pid]/mountstats file and returns a slice
+// of Mount structures containing detailed information about each mount.
+// If available, statistics for each mount are parsed as well.
+func parseMountStats(r io.Reader) ([]*Mount, error) {
+ const (
+ device = "device"
+ statVersionPrefix = "statvers="
+
+ nfs3Type = "nfs"
+ nfs4Type = "nfs4"
+ )
+
+ var mounts []*Mount
+
+ s := bufio.NewScanner(r)
+ for s.Scan() {
+ // Only look for device entries in this function
+ ss := strings.Fields(string(s.Bytes()))
+ if len(ss) == 0 || ss[0] != device {
+ continue
+ }
+
+ m, err := parseMount(ss)
+ if err != nil {
+ return nil, err
+ }
+
+ // Does this mount also possess statistics information?
+ if len(ss) > deviceEntryLen {
+ // Only NFSv3 and v4 are supported for parsing statistics
+ if m.Type != nfs3Type && m.Type != nfs4Type {
+ return nil, fmt.Errorf("cannot parse MountStats for fstype %q", m.Type)
+ }
+
+ statVersion := strings.TrimPrefix(ss[8], statVersionPrefix)
+
+ stats, err := parseMountStatsNFS(s, statVersion)
+ if err != nil {
+ return nil, err
+ }
+
+ m.Stats = stats
+ }
+
+ mounts = append(mounts, m)
+ }
+
+ return mounts, s.Err()
+}
+
+// parseMount parses an entry in /proc/[pid]/mountstats in the format:
+// device [device] mounted on [mount] with fstype [type]
+func parseMount(ss []string) (*Mount, error) {
+ if len(ss) < deviceEntryLen {
+ return nil, fmt.Errorf("invalid device entry: %v", ss)
+ }
+
+ // Check for specific words appearing at specific indices to ensure
+ // the format is consistent with what we expect
+ format := []struct {
+ i int
+ s string
+ }{
+ {i: 0, s: "device"},
+ {i: 2, s: "mounted"},
+ {i: 3, s: "on"},
+ {i: 5, s: "with"},
+ {i: 6, s: "fstype"},
+ }
+
+ for _, f := range format {
+ if ss[f.i] != f.s {
+ return nil, fmt.Errorf("invalid device entry: %v", ss)
+ }
+ }
+
+ return &Mount{
+ Device: ss[1],
+ Mount: ss[4],
+ Type: ss[7],
+ }, nil
+}
+
+// parseMountStatsNFS parses a MountStatsNFS by scanning additional information
+// related to NFS statistics.
+func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, error) {
+ // Field indicators for parsing specific types of data
+ const (
+ fieldAge = "age:"
+ fieldBytes = "bytes:"
+ fieldEvents = "events:"
+ fieldPerOpStats = "per-op"
+ fieldTransport = "xprt:"
+ )
+
+ stats := &MountStatsNFS{
+ StatVersion: statVersion,
+ }
+
+ for s.Scan() {
+ ss := strings.Fields(string(s.Bytes()))
+ if len(ss) == 0 {
+ break
+ }
+ if len(ss) < 2 {
+ return nil, fmt.Errorf("not enough information for NFS stats: %v", ss)
+ }
+
+ switch ss[0] {
+ case fieldAge:
+ // Age integer is in seconds
+ d, err := time.ParseDuration(ss[1] + "s")
+ if err != nil {
+ return nil, err
+ }
+
+ stats.Age = d
+ case fieldBytes:
+ bstats, err := parseNFSBytesStats(ss[1:])
+ if err != nil {
+ return nil, err
+ }
+
+ stats.Bytes = *bstats
+ case fieldEvents:
+ estats, err := parseNFSEventsStats(ss[1:])
+ if err != nil {
+ return nil, err
+ }
+
+ stats.Events = *estats
+ case fieldTransport:
+ if len(ss) < 3 {
+ return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss)
+ }
+
+ tstats, err := parseNFSTransportStats(ss[2:], statVersion)
+ if err != nil {
+ return nil, err
+ }
+
+ stats.Transport = *tstats
+ }
+
+ // When encountering "per-operation statistics", we must break this
+ // loop and parse them seperately to ensure we can terminate parsing
+ // before reaching another device entry; hence why this 'if' statement
+ // is not just another switch case
+ if ss[0] == fieldPerOpStats {
+ break
+ }
+ }
+
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+
+ // NFS per-operation stats appear last before the next device entry
+ perOpStats, err := parseNFSOperationStats(s)
+ if err != nil {
+ return nil, err
+ }
+
+ stats.Operations = perOpStats
+
+ return stats, nil
+}
+
+// parseNFSBytesStats parses a NFSBytesStats line using an input set of
+// integer fields.
+func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) {
+ if len(ss) != fieldBytesLen {
+ return nil, fmt.Errorf("invalid NFS bytes stats: %v", ss)
+ }
+
+ ns := make([]uint64, 0, fieldBytesLen)
+ for _, s := range ss {
+ n, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+
+ ns = append(ns, n)
+ }
+
+ return &NFSBytesStats{
+ Read: ns[0],
+ Write: ns[1],
+ DirectRead: ns[2],
+ DirectWrite: ns[3],
+ ReadTotal: ns[4],
+ WriteTotal: ns[5],
+ ReadPages: ns[6],
+ WritePages: ns[7],
+ }, nil
+}
+
+// parseNFSEventsStats parses a NFSEventsStats line using an input set of
+// integer fields.
+func parseNFSEventsStats(ss []string) (*NFSEventsStats, error) {
+ if len(ss) != fieldEventsLen {
+ return nil, fmt.Errorf("invalid NFS events stats: %v", ss)
+ }
+
+ ns := make([]uint64, 0, fieldEventsLen)
+ for _, s := range ss {
+ n, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+
+ ns = append(ns, n)
+ }
+
+ return &NFSEventsStats{
+ InodeRevalidate: ns[0],
+ DnodeRevalidate: ns[1],
+ DataInvalidate: ns[2],
+ AttributeInvalidate: ns[3],
+ VFSOpen: ns[4],
+ VFSLookup: ns[5],
+ VFSAccess: ns[6],
+ VFSUpdatePage: ns[7],
+ VFSReadPage: ns[8],
+ VFSReadPages: ns[9],
+ VFSWritePage: ns[10],
+ VFSWritePages: ns[11],
+ VFSGetdents: ns[12],
+ VFSSetattr: ns[13],
+ VFSFlush: ns[14],
+ VFSFsync: ns[15],
+ VFSLock: ns[16],
+ VFSFileRelease: ns[17],
+ CongestionWait: ns[18],
+ Truncation: ns[19],
+ WriteExtension: ns[20],
+ SillyRename: ns[21],
+ ShortRead: ns[22],
+ ShortWrite: ns[23],
+ JukeboxDelay: ns[24],
+ PNFSRead: ns[25],
+ PNFSWrite: ns[26],
+ }, nil
+}
+
+// parseNFSOperationStats parses a slice of NFSOperationStats by scanning
+// additional information about per-operation statistics until an empty
+// line is reached.
+func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
+ const (
+ // Number of expected fields in each per-operation statistics set
+ numFields = 9
+ )
+
+ var ops []NFSOperationStats
+
+ for s.Scan() {
+ ss := strings.Fields(string(s.Bytes()))
+ if len(ss) == 0 {
+ // Must break when reading a blank line after per-operation stats to
+ // enable top-level function to parse the next device entry
+ break
+ }
+
+ if len(ss) != numFields {
+ return nil, fmt.Errorf("invalid NFS per-operations stats: %v", ss)
+ }
+
+ // Skip string operation name for integers
+ ns := make([]uint64, 0, numFields-1)
+ for _, st := range ss[1:] {
+ n, err := strconv.ParseUint(st, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+
+ ns = append(ns, n)
+ }
+
+ ops = append(ops, NFSOperationStats{
+ Operation: strings.TrimSuffix(ss[0], ":"),
+ Requests: ns[0],
+ Transmissions: ns[1],
+ MajorTimeouts: ns[2],
+ BytesSent: ns[3],
+ BytesReceived: ns[4],
+ CumulativeQueueTime: time.Duration(ns[5]) * time.Millisecond,
+ CumulativeTotalResponseTime: time.Duration(ns[6]) * time.Millisecond,
+ CumulativeTotalRequestTime: time.Duration(ns[7]) * time.Millisecond,
+ })
+ }
+
+ return ops, s.Err()
+}
+
+// parseNFSTransportStats parses a NFSTransportStats line using an input set of
+// integer fields matched to a specific stats version.
+func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats, error) {
+ switch statVersion {
+ case statVersion10:
+ if len(ss) != fieldTransport10Len {
+ return nil, fmt.Errorf("invalid NFS transport stats 1.0 statement: %v", ss)
+ }
+ case statVersion11:
+ if len(ss) != fieldTransport11Len {
+ return nil, fmt.Errorf("invalid NFS transport stats 1.1 statement: %v", ss)
+ }
+ default:
+ return nil, fmt.Errorf("unrecognized NFS transport stats version: %q", statVersion)
+ }
+
+ // Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay
+ // in a v1.0 response
+ ns := make([]uint64, 0, fieldTransport11Len)
+ for _, s := range ss {
+ n, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+
+ ns = append(ns, n)
+ }
+
+ return &NFSTransportStats{
+ Port: ns[0],
+ Bind: ns[1],
+ Connect: ns[2],
+ ConnectIdleTime: ns[3],
+ IdleTime: time.Duration(ns[4]) * time.Second,
+ Sends: ns[5],
+ Receives: ns[6],
+ BadTransactionIDs: ns[7],
+ CumulativeActiveRequests: ns[8],
+ CumulativeBacklog: ns[9],
+ MaximumRPCSlotsUsed: ns[10],
+ CumulativeSendingQueue: ns[11],
+ CumulativePendingQueue: ns[12],
+ }, nil
+}
diff --git a/vendor/github.com/prometheus/procfs/mountstats_test.go b/vendor/github.com/prometheus/procfs/mountstats_test.go
new file mode 100644
index 000000000..e65707939
--- /dev/null
+++ b/vendor/github.com/prometheus/procfs/mountstats_test.go
@@ -0,0 +1,252 @@
+package procfs
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestMountStats(t *testing.T) {
+ tests := []struct {
+ name string
+ s string
+ fs bool
+ mounts []*Mount
+ invalid bool
+ }{
+ {
+ name: "no devices",
+ s: `hello`,
+ },
+ {
+ name: "device has too few fields",
+ s: `device foo`,
+ invalid: true,
+ },
+ {
+ name: "device incorrect format",
+ s: `device rootfs BAD on / with fstype rootfs`,
+ invalid: true,
+ },
+ {
+ name: "device incorrect format",
+ s: `device rootfs mounted BAD / with fstype rootfs`,
+ invalid: true,
+ },
+ {
+ name: "device incorrect format",
+ s: `device rootfs mounted on / BAD fstype rootfs`,
+ invalid: true,
+ },
+ {
+ name: "device incorrect format",
+ s: `device rootfs mounted on / with BAD rootfs`,
+ invalid: true,
+ },
+ {
+ name: "device rootfs cannot have stats",
+ s: `device rootfs mounted on / with fstype rootfs stats`,
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with too little info",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nhello",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad bytes",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nbytes: 0",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad events",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nevents: 0",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad per-op stats",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nper-op statistics\nFOO 0",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad transport stats",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nxprt: tcp",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad transport version",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=foo\nxprt: tcp 0",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad transport stats version 1.0",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.0\nxprt: tcp 0 0 0 0 0 0 0 0 0 0 0 0 0",
+ invalid: true,
+ },
+ {
+ name: "NFSv4 device with bad transport stats version 1.1",
+ s: "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nxprt: tcp 0 0 0 0 0 0 0 0 0 0",
+ invalid: true,
+ },
+ {
+ name: "device rootfs OK",
+ s: `device rootfs mounted on / with fstype rootfs`,
+ mounts: []*Mount{{
+ Device: "rootfs",
+ Mount: "/",
+ Type: "rootfs",
+ }},
+ },
+ {
+ name: "NFSv3 device with minimal stats OK",
+ s: `device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1`,
+ mounts: []*Mount{{
+ Device: "192.168.1.1:/srv",
+ Mount: "/mnt/nfs",
+ Type: "nfs",
+ Stats: &MountStatsNFS{
+ StatVersion: "1.1",
+ },
+ }},
+ },
+ {
+ name: "fixtures OK",
+ fs: true,
+ mounts: []*Mount{
+ {
+ Device: "rootfs",
+ Mount: "/",
+ Type: "rootfs",
+ },
+ {
+ Device: "sysfs",
+ Mount: "/sys",
+ Type: "sysfs",
+ },
+ {
+ Device: "proc",
+ Mount: "/proc",
+ Type: "proc",
+ },
+ {
+ Device: "/dev/sda1",
+ Mount: "/",
+ Type: "ext4",
+ },
+ {
+ Device: "192.168.1.1:/srv/test",
+ Mount: "/mnt/nfs/test",
+ Type: "nfs4",
+ Stats: &MountStatsNFS{
+ StatVersion: "1.1",
+ Age: 13968 * time.Second,
+ Bytes: NFSBytesStats{
+ Read: 1207640230,
+ ReadTotal: 1210214218,
+ ReadPages: 295483,
+ },
+ Events: NFSEventsStats{
+ InodeRevalidate: 52,
+ DnodeRevalidate: 226,
+ VFSOpen: 1,
+ VFSLookup: 13,
+ VFSAccess: 398,
+ VFSReadPages: 331,
+ VFSWritePages: 47,
+ VFSFlush: 77,
+ VFSFileRelease: 77,
+ },
+ Operations: []NFSOperationStats{
+ {
+ Operation: "NULL",
+ },
+ {
+ Operation: "READ",
+ Requests: 1298,
+ Transmissions: 1298,
+ BytesSent: 207680,
+ BytesReceived: 1210292152,
+ CumulativeQueueTime: 6 * time.Millisecond,
+ CumulativeTotalResponseTime: 79386 * time.Millisecond,
+ CumulativeTotalRequestTime: 79407 * time.Millisecond,
+ },
+ {
+ Operation: "WRITE",
+ },
+ },
+ Transport: NFSTransportStats{
+ Port: 832,
+ Connect: 1,
+ IdleTime: 11 * time.Second,
+ Sends: 6428,
+ Receives: 6428,
+ CumulativeActiveRequests: 12154,
+ MaximumRPCSlotsUsed: 24,
+ CumulativeSendingQueue: 26,
+ CumulativePendingQueue: 5726,
+ },
+ },
+ },
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ t.Logf("[%02d] test %q", i, tt.name)
+
+ var mounts []*Mount
+ var err error
+
+ if tt.s != "" {
+ mounts, err = parseMountStats(strings.NewReader(tt.s))
+ }
+ if tt.fs {
+ proc, err := FS("fixtures").NewProc(26231)
+ if err != nil {
+ t.Fatalf("failed to create proc: %v", err)
+ }
+
+ mounts, err = proc.MountStats()
+ }
+
+ if tt.invalid && err == nil {
+ t.Error("expected an error, but none occurred")
+ }
+ if !tt.invalid && err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+
+ if want, have := tt.mounts, mounts; !reflect.DeepEqual(want, have) {
+ t.Errorf("mounts:\nwant:\n%v\nhave:\n%v", mountsStr(want), mountsStr(have))
+ }
+ }
+}
+
+func mountsStr(mounts []*Mount) string {
+ var out string
+ for i, m := range mounts {
+ out += fmt.Sprintf("[%d] %q on %q (%q)", i, m.Device, m.Mount, m.Type)
+
+ stats, ok := m.Stats.(*MountStatsNFS)
+ if !ok {
+ out += "\n"
+ continue
+ }
+
+ out += fmt.Sprintf("\n\t- v%s, age: %s", stats.StatVersion, stats.Age)
+ out += fmt.Sprintf("\n\t- bytes: %v", stats.Bytes)
+ out += fmt.Sprintf("\n\t- events: %v", stats.Events)
+ out += fmt.Sprintf("\n\t- transport: %v", stats.Transport)
+ out += fmt.Sprintf("\n\t- per-operation stats:")
+
+ for _, o := range stats.Operations {
+ out += fmt.Sprintf("\n\t\t- %v", o)
+ }
+
+ out += "\n"
+ }
+
+ return out
+}
diff --git a/vendor/github.com/prometheus/procfs/proc.go b/vendor/github.com/prometheus/procfs/proc.go
index 0d0a6a90f..8717e1fe0 100644
--- a/vendor/github.com/prometheus/procfs/proc.go
+++ b/vendor/github.com/prometheus/procfs/proc.go
@@ -192,6 +192,18 @@ func (p Proc) FileDescriptorsLen() (int, error) {
return len(fds), nil
}
+// MountStats retrieves statistics and configuration for mount points in a
+// process's namespace.
+func (p Proc) MountStats() ([]*Mount, error) {
+ f, err := os.Open(p.path("mountstats"))
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ return parseMountStats(f)
+}
+
func (p Proc) fileDescriptors() ([]string, error) {
d, err := os.Open(p.path("fd"))
if err != nil {