@@ -19,6 +19,8 @@ package fs
19
19
20
20
import (
21
21
"bufio"
22
+ "bytes"
23
+ "encoding/json"
22
24
"fmt"
23
25
"os"
24
26
"os/exec"
@@ -44,6 +46,8 @@ type partition struct {
44
46
mountpoint string
45
47
major uint
46
48
minor uint
49
+ fsType string
50
+ blockSize uint
47
51
}
48
52
49
53
type RealFsInfo struct {
@@ -57,6 +61,7 @@ type RealFsInfo struct {
57
61
type Context struct {
58
62
// docker root directory.
59
63
DockerRoot string
64
+ DockerInfo map [string ]string
60
65
}
61
66
62
67
func NewFsInfo (context Context ) (FsInfo , error ) {
@@ -80,7 +85,25 @@ func NewFsInfo(context Context) (FsInfo, error) {
80
85
if _ , ok := partitions [mount .Source ]; ok {
81
86
continue
82
87
}
83
- partitions [mount .Source ] = partition {mount .Mountpoint , uint (mount .Major ), uint (mount .Minor )}
88
+ partitions [mount .Source ] = partition {
89
+ mountpoint : mount .Mountpoint ,
90
+ major : uint (mount .Major ),
91
+ minor : uint (mount .Minor ),
92
+ }
93
+ }
94
+ if storageDriver , ok := context .DockerInfo ["Driver" ]; ok && storageDriver == "devicemapper" {
95
+ dev , major , minor , blockSize , err := dockerDMDevice (context .DockerInfo ["DriverStatus" ])
96
+ if err != nil {
97
+ glog .Warningf ("Could not get Docker devicemapper device: %v" , err )
98
+ } else {
99
+ partitions [dev ] = partition {
100
+ fsType : "devicemapper" ,
101
+ major : major ,
102
+ minor : minor ,
103
+ blockSize : blockSize ,
104
+ }
105
+ fsInfo .labels [LabelDockerImages ] = dev
106
+ }
84
107
}
85
108
glog .Infof ("Filesystem partitions: %+v" , partitions )
86
109
fsInfo .partitions = partitions
@@ -174,9 +197,18 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
174
197
_ , hasMount := mountSet [partition .mountpoint ]
175
198
_ , hasDevice := deviceSet [device ]
176
199
if mountSet == nil || (hasMount && ! hasDevice ) {
177
- total , free , avail , err := getVfsStats (partition .mountpoint )
200
+ var (
201
+ total , free , avail uint64
202
+ err error
203
+ )
204
+ switch partition .fsType {
205
+ case "devicemapper" :
206
+ total , free , avail , err = getDMStats (device , partition .blockSize )
207
+ default :
208
+ total , free , avail , err = getVfsStats (partition .mountpoint )
209
+ }
178
210
if err != nil {
179
- glog .Errorf ("Statvfs failed. Error: %v" , err )
211
+ glog .Errorf ("Stat fs failed. Error: %v" , err )
180
212
} else {
181
213
deviceSet [device ] = struct {}{}
182
214
deviceInfo := DeviceInfo {
@@ -295,3 +327,67 @@ func getVfsStats(path string) (uint64, uint64, uint64, error) {
295
327
avail := uint64 (s .Frsize ) * s .Bavail
296
328
return total , free , avail , nil
297
329
}
330
+
331
+ func dockerStatusValue (status [][]string , target string ) string {
332
+ for _ , v := range status {
333
+ if len (v ) == 2 && strings .ToLower (v [0 ]) == strings .ToLower (target ) {
334
+ return v [1 ]
335
+ }
336
+ }
337
+ return ""
338
+ }
339
+
340
+ func dockerDMDevice (driverStatus string ) (string , uint , uint , uint , error ) {
341
+ var config [][]string
342
+ err := json .Unmarshal ([]byte (driverStatus ), & config )
343
+ if err != nil {
344
+ return "" , 0 , 0 , 0 , err
345
+ }
346
+ poolName := dockerStatusValue (config , "Pool Name" )
347
+ if len (poolName ) == 0 {
348
+ return "" , 0 , 0 , 0 , fmt .Errorf ("Could not get dm pool name" )
349
+ }
350
+
351
+ dmTable , err := exec .Command ("dmsetup" , "table" , poolName ).Output ()
352
+ if err != nil {
353
+ return "" , 0 , 0 , 0 , err
354
+ }
355
+
356
+ var (
357
+ major , minor , dataBlkSize , bkt uint
358
+ bkts string
359
+ )
360
+
361
+ _ , err = fmt .Fscanf (bytes .NewReader (dmTable ),
362
+ "%d %d %s %d:%d %d:%d %d %d %d %s" ,
363
+ & bkt , & bkt , & bkts , & bkt , & bkt , & major , & minor , & dataBlkSize , & bkt , & bkt , & bkts )
364
+ if err != nil {
365
+ return "" , 0 , 0 , 0 , err
366
+ }
367
+ return poolName , major , minor , dataBlkSize , nil
368
+ }
369
+
370
+ func getDMStats (poolName string , dataBlkSize uint ) (uint64 , uint64 , uint64 , error ) {
371
+ dmStatus , err := exec .Command ("dmsetup" , "status" , poolName ).Output ()
372
+ if err != nil {
373
+ return 0 , 0 , 0 , err
374
+ }
375
+
376
+ var (
377
+ total , used , bkt uint64
378
+ bkts string
379
+ )
380
+
381
+ _ , err = fmt .Fscanf (bytes .NewReader (dmStatus ),
382
+ "%d %d %s %d %d/%d %d/%d %s %s %s %s" ,
383
+ & bkt , & bkt , & bkts , & bkt , & bkt , & bkt , & used , & total , & bkts , & bkts , & bkts , & bkts )
384
+ if err != nil {
385
+ return 0 , 0 , 0 , err
386
+ }
387
+
388
+ total *= 512 * uint64 (dataBlkSize )
389
+ used *= 512 * uint64 (dataBlkSize )
390
+ free := total - used
391
+
392
+ return total , free , free , nil
393
+ }
0 commit comments