/
storage.go
139 lines (124 loc) · 3.76 KB
/
storage.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//go:build !no_logs
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
package docker
import (
"errors"
"fmt"
"log"
"math"
"regexp"
"strconv"
"strings"
"github.com/docker/docker/api/types"
)
var (
// ErrStorageStatsNotAvailable is returned if the storage stats are not in the docker info.
ErrStorageStatsNotAvailable = errors.New("docker storage stats not available")
diskBytesRe = regexp.MustCompile("([0-9.]+)\\s?([a-zA-Z]+)")
diskUnits = map[string]uint64{
"b": 1,
"kb": 1000,
"mb": 1000000,
"gb": 1000000000,
"tb": 1000000000000,
}
)
const (
// DataStorageName represent diskmapper data stats
DataStorageName = "data"
// MetadataStorageName represent diskmapper metadata stats
MetadataStorageName = "metadata"
)
// StorageStats holds the available stats for a given storage type.
// Non available stats will result in nil pointer, user has to check
// for nil before using the value.
type StorageStats struct {
Name string
Free *uint64
Used *uint64
Total *uint64
}
// GetPercentUsed computes the used percent (from 0 to 100), even if
// only two of three stats are available. If only one is available
// or total is 0, Nan is returned.
func (s *StorageStats) GetPercentUsed() float64 {
total := s.Total
if s.Total != nil && s.Used != nil && s.Free != nil {
if *s.Total < *s.Used+*s.Free {
log.Println("total lower than free+used, re-computing total")
totalValue := *s.Used + *s.Free
total = &totalValue
}
}
if s.Used != nil && total != nil {
return (100.0 * float64(*s.Used) / float64(*total))
}
if s.Free != nil && total != nil {
return 100.0 - (100.0 * float64(*s.Free) / float64(*total))
}
if s.Used != nil && s.Free != nil {
return (100.0 * float64(*s.Used) / float64(*s.Used+*s.Free))
}
return math.NaN()
}
// parseStorageStatsFromInfo converts the [][2]string DriverStatus from docker
// info into a reliable StorageStats struct. It only supports DeviceMapper
// stats for now.
func parseStorageStatsFromInfo(info types.Info) ([]*StorageStats, error) {
statsArray := []*StorageStats{}
statsPerName := make(map[string]*StorageStats)
if len(info.DriverStatus) == 0 {
return statsArray, ErrStorageStatsNotAvailable
}
for _, entry := range info.DriverStatus {
key := entry[0]
valueString := entry[1]
fields := strings.Fields(key)
if len(fields) != 3 || strings.ToLower(fields[1]) != "space" {
log.Println("ignoring invalid storage stat: ", key)
continue
}
valueInt, err := parseDiskQuantity(valueString)
if err != nil {
log.Printf("ignoring invalid value %s for stat %s: %s\n", valueString, key, err)
continue
}
storageType := strings.ToLower(fields[0])
stats, found := statsPerName[storageType]
if !found {
stats = &StorageStats{
Name: storageType,
}
statsPerName[storageType] = stats
statsArray = append(statsArray, stats)
}
switch strings.ToLower(fields[2]) {
case "available":
stats.Free = &valueInt
case "used":
stats.Used = &valueInt
case "total":
stats.Total = &valueInt
}
}
return statsArray, nil
}
// parseDiskQuantity parses a string from docker into a bytes quantity,
func parseDiskQuantity(text string) (uint64, error) {
match := diskBytesRe.FindStringSubmatch(text)
if match == nil {
return 0, fmt.Errorf("parsing error: invalid format")
}
multi, found := diskUnits[strings.ToLower(match[2])]
if !found {
return 0, fmt.Errorf("parsing error: unknown unit %s", match[2])
}
value, err := strconv.ParseFloat(match[1], 64)
if err != nil {
return 0, fmt.Errorf("parsing error: %s", err)
}
return uint64(value * float64(multi)), nil
}