disk_usage.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package daemon
  2. import (
  3. "fmt"
  4. "sync/atomic"
  5. "golang.org/x/net/context"
  6. "github.com/docker/docker/api/types"
  7. "github.com/docker/docker/api/types/filters"
  8. "github.com/docker/docker/layer"
  9. "github.com/docker/docker/pkg/directory"
  10. "github.com/docker/docker/volume"
  11. "github.com/opencontainers/go-digest"
  12. "github.com/sirupsen/logrus"
  13. )
  14. func (daemon *Daemon) getLayerRefs(platform string) map[layer.ChainID]int {
  15. tmpImages := daemon.stores[platform].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.stores[platform].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. // TODO @jhowardmsft LCOW. This may need revisiting
  48. allImages, err := daemon.Images(filters.NewArgs(), false, true)
  49. if err != nil {
  50. return nil, fmt.Errorf("failed to retrieve image list: %v", err)
  51. }
  52. // Get all local volumes
  53. allVolumes := []*types.Volume{}
  54. getLocalVols := func(v volume.Volume) error {
  55. select {
  56. case <-ctx.Done():
  57. return ctx.Err()
  58. default:
  59. if d, ok := v.(volume.DetailedVolume); ok {
  60. // skip local volumes with mount options since these could have external
  61. // mounted filesystems that will be slow to enumerate.
  62. if len(d.Options()) > 0 {
  63. return nil
  64. }
  65. }
  66. name := v.Name()
  67. refs := daemon.volumes.Refs(v)
  68. tv := volumeToAPIType(v)
  69. sz, err := directory.Size(v.Path())
  70. if err != nil {
  71. logrus.Warnf("failed to determine size of volume %v", name)
  72. sz = -1
  73. }
  74. tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))}
  75. allVolumes = append(allVolumes, tv)
  76. }
  77. return nil
  78. }
  79. err = daemon.traverseLocalVolumes(getLocalVols)
  80. if err != nil {
  81. return nil, err
  82. }
  83. // Get total layers size on disk
  84. var allLayersSize int64
  85. for platform := range daemon.stores {
  86. layerRefs := daemon.getLayerRefs(platform)
  87. allLayers := daemon.stores[platform].layerStore.Map()
  88. for _, l := range allLayers {
  89. select {
  90. case <-ctx.Done():
  91. return nil, ctx.Err()
  92. default:
  93. size, err := l.DiffSize()
  94. if err == nil {
  95. if _, ok := layerRefs[l.ChainID()]; ok {
  96. allLayersSize += size
  97. } else {
  98. logrus.Warnf("found leaked image layer %v platform %s", l.ChainID(), platform)
  99. }
  100. } else {
  101. logrus.Warnf("failed to get diff size for layer %v %s", l.ChainID(), platform)
  102. }
  103. }
  104. }
  105. }
  106. return &types.DiskUsage{
  107. LayersSize: allLayersSize,
  108. Containers: allContainers,
  109. Volumes: allVolumes,
  110. Images: allImages,
  111. }, nil
  112. }