Merge pull request #976 from jimmidyson/net-stats-from-proc

Fix Devicemapper output parsing
This commit is contained in:
Jimmi Dyson 2015-11-25 19:29:14 +00:00
commit 060609f899
2 changed files with 108 additions and 20 deletions

View File

@ -19,7 +19,6 @@ package fs
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"os"
@ -337,6 +336,8 @@ func dockerStatusValue(status [][]string, target string) string {
return ""
}
// Devicemapper thin provisioning is detailed at
// https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt
func dockerDMDevice(driverStatus string) (string, uint, uint, uint, error) {
var config [][]string
err := json.Unmarshal([]byte(driverStatus), &config)
@ -348,46 +349,77 @@ func dockerDMDevice(driverStatus string) (string, uint, uint, uint, error) {
return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name")
}
dmTable, err := exec.Command("dmsetup", "table", poolName).Output()
out, err := exec.Command("dmsetup", "table", poolName).Output()
if err != nil {
return "", 0, 0, 0, err
}
var (
major, minor, dataBlkSize, bkt uint
bkts string
)
_, err = fmt.Fscanf(bytes.NewReader(dmTable),
"%d %d %s %d:%d %d:%d %d %d %d %s",
&bkt, &bkt, &bkts, &bkt, &bkt, &major, &minor, &dataBlkSize, &bkt, &bkt, &bkts)
major, minor, dataBlkSize, err := parseDMTable(string(out))
if err != nil {
return "", 0, 0, 0, err
}
return poolName, major, minor, dataBlkSize, nil
}
func parseDMTable(dmTable string) (uint, uint, uint, error) {
dmTable = strings.Replace(dmTable, ":", " ", -1)
dmFields := strings.Fields(dmTable)
if len(dmFields) < 8 {
return 0, 0, 0, fmt.Errorf("Invalid dmsetup status output: %s", dmTable)
}
major, err := strconv.ParseUint(dmFields[5], 10, 32)
if err != nil {
return 0, 0, 0, err
}
minor, err := strconv.ParseUint(dmFields[6], 10, 32)
if err != nil {
return 0, 0, 0, err
}
dataBlkSize, err := strconv.ParseUint(dmFields[7], 10, 32)
if err != nil {
return 0, 0, 0, err
}
return uint(major), uint(minor), uint(dataBlkSize), nil
}
func getDMStats(poolName string, dataBlkSize uint) (uint64, uint64, uint64, error) {
dmStatus, err := exec.Command("dmsetup", "status", poolName).Output()
out, err := exec.Command("dmsetup", "status", poolName).Output()
if err != nil {
return 0, 0, 0, err
}
var (
total, used, bkt uint64
bkts string
)
_, err = fmt.Fscanf(bytes.NewReader(dmStatus),
"%d %d %s %d %d/%d %d/%d %s %s %s %s",
&bkt, &bkt, &bkts, &bkt, &bkt, &bkt, &used, &total, &bkts, &bkts, &bkts, &bkts)
used, total, err := parseDMStatus(string(out))
if err != nil {
return 0, 0, 0, err
}
total *= 512 * uint64(dataBlkSize)
used *= 512 * uint64(dataBlkSize)
total *= 512 * uint64(dataBlkSize)
free := total - used
return total, free, free, nil
}
func parseDMStatus(dmStatus string) (uint64, uint64, error) {
dmStatus = strings.Replace(dmStatus, "/", " ", -1)
dmFields := strings.Fields(dmStatus)
if len(dmFields) < 8 {
return 0, 0, fmt.Errorf("Invalid dmsetup status output: %s", dmStatus)
}
used, err := strconv.ParseUint(dmFields[6], 10, 64)
if err != nil {
return 0, 0, err
}
total, err := strconv.ParseUint(dmFields[7], 10, 64)
if err != nil {
return 0, 0, err
}
return used, total, nil
}

View File

@ -100,3 +100,59 @@ func TestDirUsage(t *testing.T) {
as.NoError(err)
as.True(expectedSize <= size, "expected dir size to be at-least %d; got size: %d", expectedSize, size)
}
var dmStatusTests = []struct {
dmStatus string
used uint64
total uint64
errExpected bool
}{
{`0 409534464 thin-pool 64085 3705/4161600 88106/3199488 - rw no_discard_passdown queue_if_no_space -`, 88106, 3199488, false},
{`0 209715200 thin-pool 707 1215/524288 30282/1638400 - rw discard_passdown`, 30282, 1638400, false},
{`Invalid status line`, 0, 0, false},
}
func TestParseDMStatus(t *testing.T) {
for _, tt := range dmStatusTests {
used, total, err := parseDMStatus(tt.dmStatus)
if tt.errExpected && err != nil {
t.Errorf("parseDMStatus(%q) expected error", tt.dmStatus)
}
if used != tt.used {
t.Errorf("parseDMStatus(%q) wrong used value => %q, want %q", tt.dmStatus, used, tt.used)
}
if total != tt.total {
t.Errorf("parseDMStatus(%q) wrong total value => %q, want %q", tt.dmStatus, total, tt.total)
}
}
}
var dmTableTests = []struct {
dmTable string
major uint
minor uint
dataBlkSize uint
errExpected bool
}{
{`0 409534464 thin-pool 253:6 253:7 128 32768 1 skip_block_zeroing`, 253, 7, 128, false},
{`0 409534464 thin-pool 253:6 258:9 512 32768 1 skip_block_zeroing otherstuff`, 258, 9, 512, false},
{`Invalid status line`, 0, 0, 0, false},
}
func TestParseDMTable(t *testing.T) {
for _, tt := range dmTableTests {
major, minor, dataBlkSize, err := parseDMTable(tt.dmTable)
if tt.errExpected && err != nil {
t.Errorf("parseDMTable(%q) expected error", tt.dmTable)
}
if major != tt.major {
t.Errorf("parseDMTable(%q) wrong major value => %q, want %q", tt.dmTable, major, tt.major)
}
if minor != tt.minor {
t.Errorf("parseDMTable(%q) wrong minor value => %q, want %q", tt.dmTable, minor, tt.minor)
}
if dataBlkSize != tt.dataBlkSize {
t.Errorf("parseDMTable(%q) wrong dataBlkSize value => %q, want %q", tt.dmTable, dataBlkSize, tt.dataBlkSize)
}
}
}