mirror of
https://github.com/containers/podman.git
synced 2025-09-16 05:39:43 +08:00

For the AutocompleteFormat function we expect the correct template struct which is used in the golang template. The function can handle both struct and pointer to a struct. Using the reference is more efficient since it doe snot have to copy the whole struct. Also change some structs to use he actual type from the template instead of some nested one to make sure it has to correct fields. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
161 lines
4.5 KiB
Go
161 lines
4.5 KiB
Go
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()
|
|
}
|