diff options
Diffstat (limited to 'cmd/podman/system/df.go')
-rw-r--r-- | cmd/podman/system/df.go | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go new file mode 100644 index 000000000..7caa8e39a --- /dev/null +++ b/cmd/podman/system/df.go @@ -0,0 +1,282 @@ +package system + +import ( + "fmt" + "html/template" + "io" + "os" + "strings" + "text/tabwriter" + "time" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/validate" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/docker/go-units" + "github.com/spf13/cobra" +) + +var ( + dfSystemDescription = ` + podman system df + + Show podman disk usage + ` + dfSystemCommand = &cobra.Command{ + Use: "df", + Args: validate.NoArgs, + Short: "Show podman disk usage", + Long: dfSystemDescription, + RunE: df, + } +) + +var ( + dfOptions entities.SystemDfOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: dfSystemCommand, + Parent: systemCmd, + }) + flags := dfSystemCommand.Flags() + flags.BoolVarP(&dfOptions.Verbose, "verbose", "v", false, "Show detailed information on disk usage") + flags.StringVar(&dfOptions.Format, "format", "", "Pretty-print images using a Go template") +} + +func df(cmd *cobra.Command, args []string) error { + reports, err := registry.ContainerEngine().SystemDf(registry.Context(), dfOptions) + if err != nil { + return err + } + if dfOptions.Verbose { + return printVerbose(reports) + } + return printSummary(reports, dfOptions.Format) +} + +func printSummary(reports *entities.SystemDfReport, userFormat string) error { + + var ( + dfSummaries []*dfSummary + active int + size, reclaimable int64 + format string = "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n" + w io.Writer = os.Stdout + ) + + // Images + if len(userFormat) > 0 { + format = userFormat + } + + for _, i := range reports.Images { + if i.Containers > 0 { + active += 1 + } + size += i.Size + if i.Containers < 1 { + reclaimable += i.Size + } + } + + imageSummary := dfSummary{ + Type: "Images", + Total: len(reports.Images), + Active: active, + size: size, + reclaimable: reclaimable, + } + dfSummaries = append(dfSummaries, &imageSummary) + + // Containers + + var ( + conActive int + conSize, conReclaimable int64 + ) + for _, c := range reports.Containers { + if c.Status == "running" { + conActive += 1 + } else { + conReclaimable += c.RWSize + } + conSize += c.RWSize + } + + containerSummary := dfSummary{ + Type: "Containers", + Total: len(reports.Containers), + Active: conActive, + size: conSize, + reclaimable: conReclaimable, + } + + dfSummaries = append(dfSummaries, &containerSummary) + + // Volumes + var ( + activeVolumes int + volumesSize, volumesReclaimable int64 + ) + + for _, v := range reports.Volumes { + activeVolumes += v.Links + volumesSize += v.Size + volumesReclaimable += v.Size + } + volumeSummary := dfSummary{ + Type: "Local Volumes", + Total: len(reports.Volumes), + Active: activeVolumes, + size: volumesSize, + reclaimable: volumesReclaimable, + } + + dfSummaries = append(dfSummaries, &volumeSummary) + + headers := "TYPE\tTOTAL\tACTIVE\tSIZE\tRECLAIMABLE\n" + format = "{{range . }}" + format + "{{end}}" + if len(userFormat) == 0 { + format = headers + format + } + return writeTemplate(w, format, dfSummaries) +} + +func printVerbose(reports *entities.SystemDfReport) error { + var ( + dfImages []*dfImage + dfContainers []*dfContainer + dfVolumes []*dfVolume + w io.Writer = os.Stdout + ) + + // Images + fmt.Print("\nImages space usage:\n\n") + // convert to dfImage for output + for _, d := range reports.Images { + dfImages = append(dfImages, &dfImage{SystemDfImageReport: d}) + } + imageHeaders := "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE\tSHARED SIZE\tUNIQUE SIZE\tCONTAINERS\n" + imageRow := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n" + format := imageHeaders + "{{range . }}" + imageRow + "{{end}}" + if err := writeTemplate(w, format, dfImages); err != nil { + return nil + } + + // Containers + fmt.Print("\nContainers space usage:\n\n") + + // convert to dfContainers for output + for _, d := range reports.Containers { + dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d}) + } + containerHeaders := "CONTAINER ID\tIMAGE\tCOMMAND\tLOCAL VOLUMES\tSIZE\tCREATED\tSTATUS\tNAMES\n" + containerRow := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n" + format = containerHeaders + "{{range . }}" + containerRow + "{{end}}" + if err := writeTemplate(w, format, dfContainers); err != nil { + return nil + } + + // Volumes + fmt.Print("\nLocal Volumes space usage:\n\n") + + // convert to dfVolume for output + for _, d := range reports.Volumes { + dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d}) + } + volumeHeaders := "VOLUME NAME\tLINKS\tSIZE\n" + volumeRow := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n" + format = volumeHeaders + "{{range . }}" + volumeRow + "{{end}}" + return writeTemplate(w, format, dfVolumes) +} + +func writeTemplate(w io.Writer, format string, output interface{}) error { + tmpl, err := template.New("dfout").Parse(format) + if err != nil { + return err + } + w = tabwriter.NewWriter(w, 8, 2, 2, ' ', 0) //nolint + if err := tmpl.Execute(w, output); err != nil { + return err + } + if flusher, ok := w.(interface{ Flush() error }); ok { + return flusher.Flush() + } + return nil +} + +type dfImage struct { + *entities.SystemDfImageReport +} + +func (d *dfImage) ImageID() string { + return d.SystemDfImageReport.ImageID[0:12] +} + +func (d *dfImage) Created() string { + return units.HumanDuration(time.Since(d.SystemDfImageReport.Created)) +} + +func (d *dfImage) Size() string { + return units.HumanSize(float64(d.SystemDfImageReport.Size)) +} + +func (d *dfImage) SharedSize() string { + return units.HumanSize(float64(d.SystemDfImageReport.SharedSize)) +} + +func (d *dfImage) UniqueSize() string { + return units.HumanSize(float64(d.SystemDfImageReport.UniqueSize)) +} + +type dfContainer struct { + *entities.SystemDfContainerReport +} + +func (d *dfContainer) ContainerID() string { + return d.SystemDfContainerReport.ContainerID[0:12] +} + +func (d *dfContainer) Image() string { + return d.SystemDfContainerReport.Image[0:12] +} + +func (d *dfContainer) Command() string { + return strings.Join(d.SystemDfContainerReport.Command, " ") +} + +func (d *dfContainer) Size() string { + return units.HumanSize(float64(d.SystemDfContainerReport.Size)) +} + +func (d *dfContainer) Created() string { + return units.HumanDuration(time.Since(d.SystemDfContainerReport.Created)) +} + +type dfVolume struct { + *entities.SystemDfVolumeReport +} + +func (d *dfVolume) Size() string { + return units.HumanSize(float64(d.SystemDfVolumeReport.Size)) +} + +type dfSummary struct { + Type string + Total int + Active int + size int64 + reclaimable int64 +} + +func (d *dfSummary) Size() string { + return units.HumanSize(float64(d.size)) +} + +func (d *dfSummary) Reclaimable() string { + percent := int(float64(d.reclaimable)/float64(d.size)) * 100 + return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.reclaimable)), percent) +} |