disk_usage.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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. name := v.Name()
  59. refs := daemon.volumes.Refs(v)
  60. tv := volumeToAPIType(v)
  61. sz, err := directory.Size(v.Path())
  62. if err != nil {
  63. logrus.Warnf("failed to determine size of volume %v", name)
  64. sz = -1
  65. }
  66. tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))}
  67. allVolumes = append(allVolumes, tv)
  68. }
  69. return nil
  70. }
  71. err = daemon.traverseLocalVolumes(getLocalVols)
  72. if err != nil {
  73. return nil, err
  74. }
  75. // Get total layers size on disk
  76. layerRefs := daemon.getLayerRefs()
  77. allLayers := daemon.layerStore.Map()
  78. var allLayersSize int64
  79. for _, l := range allLayers {
  80. select {
  81. case <-ctx.Done():
  82. return nil, ctx.Err()
  83. default:
  84. size, err := l.DiffSize()
  85. if err == nil {
  86. if _, ok := layerRefs[l.ChainID()]; ok {
  87. allLayersSize += size
  88. } else {
  89. logrus.Warnf("found leaked image layer %v", l.ChainID())
  90. }
  91. } else {
  92. logrus.Warnf("failed to get diff size for layer %v", l.ChainID())
  93. }
  94. }
  95. }
  96. return &types.DiskUsage{
  97. LayersSize: allLayersSize,
  98. Containers: allContainers,
  99. Volumes: allVolumes,
  100. Images: allImages,
  101. }, nil
  102. }