package pods import ( "context" "fmt" "os" "time" "github.com/buger/goterm" "github.com/containers/common/pkg/report" "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/validate" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/spf13/cobra" ) type podStatsOptionsWrapper struct { entities.PodStatsOptions // Format - pretty-print to JSON or a go template. Format string // NoReset - do not reset the screen when streaming. NoReset bool // NoStream - do not stream stats but write them once. NoStream bool } var ( statsOptions = podStatsOptionsWrapper{} statsDescription = `Display the containers' resource-usage statistics of one or more running pod` // Command: podman pod _pod_ statsCmd = &cobra.Command{ Use: "stats [options] [POD...]", Short: "Display a live stream of resource usage statistics for the containers in one or more pods", Long: statsDescription, RunE: stats, ValidArgsFunction: common.AutocompletePodsRunning, Example: `podman pod stats podman pod stats a69b23034235 named-pod podman pod stats --latest podman pod stats --all`, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: statsCmd, Parent: podCmd, }) flags := statsCmd.Flags() flags.BoolVarP(&statsOptions.All, "all", "a", false, "Provide stats for all pods") formatFlagName := "format" flags.StringVar(&statsOptions.Format, formatFlagName, "", "Pretty-print container statistics to JSON or using a Go template") _ = statsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.PodStatsReport{})) flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen when streaming") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result") validate.AddLatestFlag(statsCmd, &statsOptions.Latest) } func stats(cmd *cobra.Command, args []string) error { // Validate input. if err := entities.ValidatePodStatsOptions(args, &statsOptions.PodStatsOptions); err != nil { return err } rpt := report.New(os.Stdout, cmd.Name()) defer rpt.Flush() var err error doJSON := report.IsJSON(statsOptions.Format) if !doJSON { if cmd.Flags().Changed("format") { rpt, err = rpt.Parse(report.OriginUser, statsOptions.Format) if err != nil { return err } } else { rpt = rpt.Init(os.Stdout, 12, 2, 2, ' ', 0) rpt.Origin = report.OriginPodman } } for ; ; time.Sleep(time.Second) { reports, err := registry.ContainerEngine().PodStats(context.Background(), args, statsOptions.PodStatsOptions) if err != nil { return err } // Print the stats in the requested format and configuration. if doJSON { err = printJSONPodStats(reports) } else { if !statsOptions.NoReset { goterm.Clear() goterm.MoveCursor(1, 1) goterm.Flush() } if report.OriginUser == rpt.Origin { err = userTemplate(rpt, reports) } else { err = defaultTemplate(rpt, reports) } } if err != nil { return err } if statsOptions.NoStream { break } } return nil } func printJSONPodStats(stats []*entities.PodStatsReport) error { b, err := json.MarshalIndent(&stats, "", " ") if err != nil { return err } fmt.Fprintf(os.Stdout, "%s\n", string(b)) return nil } func defaultTemplate(rpt *report.Formatter, stats []*entities.PodStatsReport) error { outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" fmt.Fprintf(rpt.Writer(), outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS") if len(stats) == 0 { fmt.Fprintf(rpt.Writer(), outFormat, "--", "--", "--", "--", "--", "--", "--", "--", "--") } else { for _, i := range stats { fmt.Fprintf(rpt.Writer(), outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS) } } return rpt.Flush() } func userTemplate(rpt *report.Formatter, stats []*entities.PodStatsReport) error { if len(stats) == 0 { return nil } headers := report.Headers(entities.PodStatsReport{}, map[string]string{ "CPU": "CPU %", "MemUsage": "MEM USAGE/ LIMIT", "MemUsageBytes": "MEM USAGE/ LIMIT", "MEM": "MEM %", "NET IO": "NET IO", "BlockIO": "BLOCK IO", }) if err := rpt.Execute(headers); err != nil { return err } if err := rpt.Execute(stats); err != nil { return err } return rpt.Flush() }