diff options
author | Daniel J Walsh <dwalsh@redhat.com> | 2017-12-15 16:58:36 -0500 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2017-12-18 16:46:05 +0000 |
commit | 5770dc2640c216525ab84031e3712fcc46b3b087 (patch) | |
tree | 8a1c5c4e4a6ce6a35a3767247623a62bfd698f77 /cmd/podman/stats.go | |
parent | de3468e120d489d046c08dad72ba2262e222ccb1 (diff) | |
download | podman-5770dc2640c216525ab84031e3712fcc46b3b087.tar.gz podman-5770dc2640c216525ab84031e3712fcc46b3b087.tar.bz2 podman-5770dc2640c216525ab84031e3712fcc46b3b087.zip |
Rename all references to kpod to podman
The decision is in, kpod is going to be named podman.
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
Closes: #145
Approved by: umohnani8
Diffstat (limited to 'cmd/podman/stats.go')
-rw-r--r-- | cmd/podman/stats.go | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go new file mode 100644 index 000000000..cf54a8bfe --- /dev/null +++ b/cmd/podman/stats.go @@ -0,0 +1,226 @@ +package main + +import ( + "fmt" + "reflect" + "strings" + "time" + + tm "github.com/buger/goterm" + "github.com/docker/go-units" + "github.com/pkg/errors" + "github.com/projectatomic/libpod/cmd/podman/formats" + "github.com/projectatomic/libpod/libpod" + "github.com/urfave/cli" +) + +type statsOutputParams struct { + Container string `json:"name"` + ID string `json:"id"` + CPUPerc string `json:"cpu_percent"` + MemUsage string `json:"mem_usage"` + MemPerc string `json:"mem_percent"` + NetIO string `json:"netio"` + BlockIO string `json:"blocki"` + PIDS uint64 `json:"pids"` +} + +var ( + statsFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "show all containers. Only running containers are shown by default. The default is false", + }, + cli.BoolFlag{ + Name: "no-stream", + Usage: "disable streaming stats and only pull the first result, default setting is false", + }, + cli.StringFlag{ + Name: "format", + Usage: "pretty-print container statistics using a Go template", + }, + cli.BoolFlag{ + Name: "no-reset", + Usage: "disable resetting the screen between intervals", + }, + } + + statsDescription = "display a live stream of one or more containers' resource usage statistics" + statsCommand = cli.Command{ + Name: "stats", + Usage: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers", + Description: statsDescription, + Flags: statsFlags, + Action: statsCmd, + ArgsUsage: "", + } +) + +func statsCmd(c *cli.Context) error { + if err := validateFlags(c, statsFlags); err != nil { + return err + } + + runtime, err := getRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + times := -1 + if c.Bool("no-stream") { + times = 1 + } + + var format string + var ctrs []*libpod.Container + var containerFunc func() ([]*libpod.Container, error) + all := c.Bool("all") + + if c.IsSet("format") { + format = c.String("format") + } else { + format = genStatsFormat() + } + + if len(c.Args()) > 0 { + containerFunc = func() ([]*libpod.Container, error) { return runtime.GetContainersByList(c.Args()) } + } else if all { + containerFunc = runtime.GetAllContainers + } else { + containerFunc = runtime.GetRunningContainers + } + + ctrs, err = containerFunc() + if err != nil { + return errors.Wrapf(err, "unable to get list of containers") + } + + containerStats := map[string]*libpod.ContainerStats{} + for _, ctr := range ctrs { + initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) + if err != nil { + return err + } + containerStats[ctr.ID()] = initialStats + } + step := 1 + if times == -1 { + times = 1 + step = 0 + } + for i := 0; i < times; i += step { + reportStats := []*libpod.ContainerStats{} + for _, ctr := range ctrs { + id := ctr.ID() + if _, ok := containerStats[ctr.ID()]; !ok { + initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) + if err != nil { + return err + } + containerStats[id] = initialStats + } + stats, err := ctr.GetContainerStats(containerStats[id]) + if err != nil { + return err + } + // replace the previous measurement with the current one + containerStats[id] = stats + reportStats = append(reportStats, stats) + } + ctrs, err = containerFunc() + if err != nil { + return err + } + if strings.ToLower(format) != formats.JSONString && !c.Bool("no-reset") { + tm.Clear() + tm.MoveCursor(1, 1) + tm.Flush() + } + outputStats(reportStats, format) + time.Sleep(time.Second) + } + return nil +} + +func outputStats(stats []*libpod.ContainerStats, format string) error { + var out formats.Writer + var outputStats []statsOutputParams + for _, s := range stats { + outputStats = append(outputStats, getStatsOutputParams(s)) + } + if len(outputStats) == 0 { + return nil + } + if strings.ToLower(format) == formats.JSONString { + out = formats.JSONStructArray{Output: statsToGeneric(outputStats, []statsOutputParams{})} + } else { + out = formats.StdoutTemplateArray{Output: statsToGeneric(outputStats, []statsOutputParams{}), Template: format, Fields: outputStats[0].headerMap()} + } + return formats.Writer(out).Out() +} + +func genStatsFormat() (format string) { + return "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}" +} + +// imagesToGeneric creates an empty array of interfaces for output +func statsToGeneric(templParams []statsOutputParams, JSONParams []statsOutputParams) (genericParams []interface{}) { + if len(templParams) > 0 { + for _, v := range templParams { + genericParams = append(genericParams, interface{}(v)) + } + return + } + for _, v := range JSONParams { + genericParams = append(genericParams, interface{}(v)) + } + return +} + +// generate the header based on the template provided +func (i *statsOutputParams) headerMap() map[string]string { + v := reflect.Indirect(reflect.ValueOf(i)) + values := make(map[string]string) + + for i := 0; i < v.NumField(); i++ { + key := v.Type().Field(i).Name + value := key + switch value { + case "CPUPerc": + value = "CPU%" + case "MemUsage": + value = "MemUsage/Limit" + case "MemPerc": + value = "Mem%" + } + values[key] = strings.ToUpper(splitCamelCase(value)) + } + return values +} + +func combineHumanValues(a, b uint64) string { + return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) +} + +func floatToPercentString(f float64) string { + strippedFloat, err := libpod.RemoveScientificNotationFromFloat(f) + if err != nil { + // If things go bazinga, return a safe value + return "0.00 %" + } + return fmt.Sprintf("%.2f", strippedFloat) + "%" +} + +func getStatsOutputParams(stats *libpod.ContainerStats) statsOutputParams { + return statsOutputParams{ + Container: stats.ContainerID[:12], + ID: stats.ContainerID, + CPUPerc: floatToPercentString(stats.CPU), + MemUsage: combineHumanValues(stats.MemUsage, stats.MemLimit), + MemPerc: floatToPercentString(stats.MemPerc), + NetIO: combineHumanValues(stats.NetInput, stats.NetOutput), + BlockIO: combineHumanValues(stats.BlockInput, stats.BlockOutput), + PIDS: stats.PIDs, + } +} |