Repository: prometheus/procfs
Pull request #130 (2019-01-14 19:46:09)

Parse mountaddr from mountstats
This PR will export the mountaddr from the opts field. The test case is a little weird, I wasn't sure how much of the opts field to add considering only 1 value is being parsed out of it, but I wanted to have some other fields to prove that it properly grabbed the right field.

 I have a use case for this with: https://github.com/prometheus/node_exporter/pull/994 

Signed-off-by: Mark Knapp <mknapp@hudson-trading.com>

mknapphrt: I'm not sure sure this will work anymore. If this is an optional field then can it be used as a label for the mountstats? I was hoping to use it for the deduplication problem in the linked issue but I would need some kind of default value when the mountaddr isn't there. Which seems like bad practice, especially for a field that isn't just a number that could default to 0.
mknapphrt: Right, I was mostly worried about having an empty string for a label, whereas an int value would default  to "0" at least. But now that I think about it more adding a label wouldn't introduce any more duplication possibilities than already exist, it can only serve to distinguish two mounts that are already identical. Cool, I'll make fix that nit and squash the commits. Thanks!

Patch:
diff --git a/mountstats.go b/mountstats.go
index 7a8a1e0..fc385af 100644
--- a/mountstats.go
+++ b/mountstats.go
@@ -69,6 +69,8 @@ type MountStats interface {
 type MountStatsNFS struct {
 	// The version of statistics provided.
 	StatVersion string
+	// The optional mountaddr of the NFS mount.
+	MountAddress string
 	// The age of the NFS mount.
 	Age time.Duration
 	// Statistics related to byte counters for various operations.
@@ -317,6 +319,7 @@ func parseMount(ss []string) (*Mount, error) {
 func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, error) {
 	// Field indicators for parsing specific types of data
 	const (
+		fieldOpts       = "opts:"
 		fieldAge        = "age:"
 		fieldBytes      = "bytes:"
 		fieldEvents     = "events:"
@@ -338,6 +341,13 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
 		}
 
 		switch ss[0] {
+		case fieldOpts:
+			for _, opt := range strings.Split(ss[1], ",") {
+				split := strings.Split(opt, "=")
+				if len(split) == 2 && split[0] == "mountaddr" {
+					stats.MountAddress = split[1]
+				}
+			}
 		case fieldAge:
 			// Age integer is in seconds
 			d, err := time.ParseDuration(ss[1] + "s")


Test patch:
diff --git a/mountstats_test.go b/mountstats_test.go
index 9ae3b02..ca5f7f5 100644
--- a/mountstats_test.go
+++ b/mountstats_test.go
@@ -219,6 +219,19 @@ func TestMountStats(t *testing.T) {
 				},
 			}},
 		},
+		{
+			name: "NFSv3 device with mountaddr OK",
+			s:    "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nopts: rw,vers=3,mountaddr=192.168.1.1,proto=udp\n",
+			mounts: []*Mount{{
+				Device: "192.168.1.1:/srv",
+				Mount:  "/mnt/nfs",
+				Type:   "nfs",
+				Stats: &MountStatsNFS{
+					StatVersion:  "1.1",
+					MountAddress: "192.168.1.1",
+				},
+			}},
+		},
 		{
 			name: "device rootfs OK",
 			s:    `device rootfs mounted on / with fstype rootfs`,
@@ -363,6 +376,7 @@ func mountsStr(mounts []*Mount) string {
 			continue
 		}
 
+		out += fmt.Sprintf("\n\t- mountaddr: %s", stats.MountAddress)
 		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)