disk_usage.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package daemon
  2. import (
  3. "fmt"
  4. "sync/atomic"
  5. "golang.org/x/net/context"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/filters"
  9. "github.com/docker/docker/layer"
  10. "github.com/docker/docker/pkg/directory"
  11. "github.com/docker/docker/volume"
  12. "github.com/opencontainers/go-digest"
  13. )
  14. func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int {
  15. tmpImages := daemon.imageStore.Map()
  16. layerRefs := map[layer.ChainID]int{}
  17. for id, img := range tmpImages {
  18. dgst := digest.Digest(id)
  19. if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
  20. continue
  21. }
  22. rootFS := *img.RootFS
  23. rootFS.DiffIDs = nil
  24. for _, id := range img.RootFS.DiffIDs {
  25. rootFS.Append(id)
  26. chid := rootFS.ChainID()
  27. layerRefs[chid]++
  28. }
  29. }
  30. return layerRefs
  31. }
  32. // SystemDiskUsage returns information about the daemon data disk usage
  33. func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) {
  34. if !atomic.CompareAndSwapInt32(&daemon.diskUsageRunning, 0, 1) {
  35. return nil, fmt.Errorf("a disk usage operation is already running")
  36. }
  37. defer atomic.StoreInt32(&daemon.diskUsageRunning, 0)
  38. // Retrieve container list
  39. allContainers, err := daemon.Containers(&types.ContainerListOptions{
  40. Size: true,
  41. All: true,
  42. })
  43. if err != nil {
  44. return nil, fmt.Errorf("failed to retrieve container list: %v", err)
  45. }
  46. // Get all top images with extra attributes
  47. allImages, err := daemon.Images(filters.NewArgs(), false, true)
  48. if err != nil {
  49. return nil, fmt.Errorf("failed to retrieve image list: %v", err)
  50. }
  51. // Get all local volumes
  52. allVolumes := []*types.Volume{}
  53. getLocalVols := func(v volume.Volume) error {
  54. select {
  55. case <-ctx.Done():
  56. return ctx.Err()
  57. default:
  58. if d, ok := v.(volume.DetailedVolume); ok {
  59. // skip local volumes with mount options since these could have external
  60. // mounted filesystems that will be slow to enumerate.
  61. if len(d.Options()) > 0 {
  62. return nil
  63. }
  64. }
  65. name := v.Name()
  66. refs := daemon.volumes.Refs(v)
  67. tv := volumeToAPIType(v)
  68. sz, err := directory.Size(v.Path())
  69. if err != nil {
  70. logrus.Warnf("failed to determine size of volume %v", name)
  71. sz = -1
  72. }
  73. tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))}
  74. allVolumes = append(allVolumes, tv)
  75. }
  76. return nil
  77. }
  78. err = daemon.traverseLocalVolumes(getLocalVols)
  79. if err != nil {
  80. return nil, err
  81. }
  82. // Get total layers size on disk
  83. layerRefs := daemon.getLayerRefs()
  84. allLayers := daemon.layerStore.Map()
  85. var allLayersSize int64
  86. for _, l := range allLayers {
  87. select {
  88. case <-ctx.Done():
  89. return nil, ctx.Err()
  90. default:
  91. size, err := l.DiffSize()
  92. if err == nil {
  93. if _, ok := layerRefs[l.ChainID()]; ok {
  94. allLayersSize += size
  95. } else {
  96. logrus.Warnf("found leaked image layer %v", l.ChainID())
  97. }
  98. } else {
  99. logrus.Warnf("failed to get diff size for layer %v", l.ChainID())
  100. }
  101. }
  102. }
  103. return &types.DiskUsage{
  104. LayersSize: allLayersSize,
  105. Containers: allContainers,
  106. Volumes: allVolumes,
  107. Images: allImages,
  108. }, nil
  109. }