// Copyright 2016 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. package collector import ( "errors" "io/ioutil" "os" "regexp" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" ) var ( netLineRE = regexp.MustCompile(`^net \d+ (\d+) (\d+) (\d+)$`) rpcLineRE = regexp.MustCompile(`^rpc (\d+) (\d+) (\d+)$`) procLineRE = regexp.MustCompile(`^proc(\d+) \d+ (\d+( \d+)*)$`) nfsProcedures = map[string][]string{ "2": { "null", "getattr", "setattr", "root", "lookup", "readlink", "read", "writecache", "write", "create", "remove", "rename", "link", "symlink", "mkdir", "rmdir", "readdir", "statfs", }, "3": { "null", "getattr", "setattr", "lookup", "access", "readlink", "read", "write", "create", "mkdir", "symlink", "mknod", "remove", "rmdir", "rename", "link", "readdir", "readdirplus", "fsstat", "fsinfo", "pathconf", "commit", }, "4": { "null", "read", "write", "commit", "open", "open_confirm", "open_noattr", "open_downgrade", "close", "setattr", "fsinfo", "renew", "setclientid", "setclientid_confirm", "lock", "lockt", "locku", "access", "getattr", "lookup", "lookup_root", "remove", "rename", "link", "symlink", "create", "pathconf", "statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl", "setacl", "fs_locations", "release_lockowner", "secinfo", "fsid_present", "exchange_id", "create_session", "destroy_session", "sequence", "get_lease_time", "reclaim_complete", "layoutget", "getdeviceinfo", "layoutcommit", "layoutreturn", "secinfo_no_name", "test_stateid", "free_stateid", "getdevicelist", "bind_conn_to_session", "destroy_clientid", "seek", "allocate", "deallocate", "layoutstats", "clone", "copy", }, } nfsNetReadsDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "nfs", "net_reads_total"), "Number of reads at the network layer.", []string{"protocol"}, nil, ) nfsNetConnectionsDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "nfs", "net_connections_total"), "Number of connections at the network layer.", []string{"protocol"}, nil, ) nfsRPCOperationsDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "nfs", "rpc_operations_total"), "Number of RPCs performed.", nil, nil, ) nfsRPCRetransmissionsDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "nfs", "rpc_retransmissions_total"), "Number of RPC transmissions performed.", nil, nil, ) nfsRPCAuthenticationRefreshesDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "nfs", "rpc_authentication_refreshes_total"), "Number of RPC authentication refreshes performed.", nil, nil, ) nfsProceduresDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "nfs", "procedures_total"), "Number of NFS procedures invoked.", []string{"version", "procedure"}, nil, ) ) type nfsCollector struct{} func init() { registerCollector("nfs", defaultDisabled, NewNfsCollector) } // NewNfsCollector returns a new Collector exposing NFS statistics. func NewNfsCollector() (Collector, error) { return &nfsCollector{}, nil } func (c *nfsCollector) Update(ch chan<- prometheus.Metric) error { statsFile := procFilePath("net/rpc/nfs") content, err := ioutil.ReadFile(statsFile) if err != nil { if os.IsNotExist(err) { log.Debugf("Not collecting NFS statistics, as %q does not exist", statsFile) return nil } return err } for _, line := range strings.Split(string(content), "\n") { if fields := netLineRE.FindStringSubmatch(line); fields != nil { value, _ := strconv.ParseFloat(fields[1], 64) ch <- prometheus.MustNewConstMetric( nfsNetReadsDesc, prometheus.CounterValue, value, "udp") value, _ = strconv.ParseFloat(fields[2], 64) ch <- prometheus.MustNewConstMetric( nfsNetReadsDesc, prometheus.CounterValue, value, "tcp") value, _ = strconv.ParseFloat(fields[3], 64) ch <- prometheus.MustNewConstMetric( nfsNetConnectionsDesc, prometheus.CounterValue, value, "tcp") } else if fields := rpcLineRE.FindStringSubmatch(line); fields != nil { value, _ := strconv.ParseFloat(fields[1], 64) ch <- prometheus.MustNewConstMetric( nfsRPCOperationsDesc, prometheus.CounterValue, value) value, _ = strconv.ParseFloat(fields[2], 64) ch <- prometheus.MustNewConstMetric( nfsRPCRetransmissionsDesc, prometheus.CounterValue, value) value, _ = strconv.ParseFloat(fields[3], 64) ch <- prometheus.MustNewConstMetric( nfsRPCAuthenticationRefreshesDesc, prometheus.CounterValue, value) } else if fields := procLineRE.FindStringSubmatch(line); fields != nil { version := fields[1] for procedure, count := range strings.Split(fields[2], " ") { value, _ := strconv.ParseFloat(count, 64) ch <- prometheus.MustNewConstMetric( nfsProceduresDesc, prometheus.CounterValue, value, version, nfsProcedures[version][procedure]) } } else if line != "" { return errors.New("Failed to parse line: " + line) } } return nil }