mirror of
https://github.com/containers/podman.git
synced 2025-06-20 00:51:16 +08:00
@ -206,7 +206,7 @@ func ps(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
if err := tmpl.Execute(w, responses); err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
|
244
cmd/podman/containers/stats.go
Normal file
244
cmd/podman/containers/stats.go
Normal file
@ -0,0 +1,244 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
tm "github.com/buger/goterm"
|
||||
"github.com/containers/libpod/cmd/podman/registry"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/cgroups"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/libpod/utils"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var (
|
||||
statsDescription = "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers."
|
||||
statsCommand = &cobra.Command{
|
||||
Use: "stats [flags] [CONTAINER...]",
|
||||
Short: "Display a live stream of container resource usage statistics",
|
||||
Long: statsDescription,
|
||||
RunE: stats,
|
||||
Args: checkStatOptions,
|
||||
Example: `podman stats --all --no-stream
|
||||
podman stats ctrID
|
||||
podman stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`,
|
||||
}
|
||||
|
||||
containerStatsCommand = &cobra.Command{
|
||||
Use: statsCommand.Use,
|
||||
Short: statsCommand.Short,
|
||||
Long: statsCommand.Long,
|
||||
RunE: statsCommand.RunE,
|
||||
Args: checkStatOptions,
|
||||
Example: `podman container stats --all --no-stream
|
||||
podman container stats ctrID
|
||||
podman container stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
statsOptions entities.ContainerStatsOptions
|
||||
defaultStatsRow = "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\n"
|
||||
defaultStatsHeader = "ID\tNAME\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET IO\tBLOCK IO\tPIDS\n"
|
||||
)
|
||||
|
||||
func statFlags(flags *pflag.FlagSet) {
|
||||
flags.BoolVarP(&statsOptions.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false")
|
||||
flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template")
|
||||
flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Act on the latest container Podman is aware of")
|
||||
flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals")
|
||||
flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false")
|
||||
if registry.IsRemote() {
|
||||
_ = flags.MarkHidden("latest")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: statsCommand,
|
||||
})
|
||||
flags := statsCommand.Flags()
|
||||
statFlags(flags)
|
||||
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: containerStatsCommand,
|
||||
Parent: containerCmd,
|
||||
})
|
||||
|
||||
containerStatsFlags := containerStatsCommand.Flags()
|
||||
statFlags(containerStatsFlags)
|
||||
}
|
||||
|
||||
// stats is different in that it will assume running containers if
|
||||
// no input is given, so we need to validate differently
|
||||
func checkStatOptions(cmd *cobra.Command, args []string) error {
|
||||
opts := 0
|
||||
if statsOptions.All {
|
||||
opts += 1
|
||||
}
|
||||
if statsOptions.Latest {
|
||||
opts += 1
|
||||
}
|
||||
if len(args) > 0 {
|
||||
opts += 1
|
||||
}
|
||||
if opts > 1 {
|
||||
return errors.Errorf("--all, --latest and containers cannot be used together")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stats(cmd *cobra.Command, args []string) error {
|
||||
if rootless.IsRootless() {
|
||||
unified, err := cgroups.IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !unified {
|
||||
return errors.New("stats is not supported in rootless mode without cgroups v2")
|
||||
}
|
||||
}
|
||||
statsOptions.StatChan = make(chan []*define.ContainerStats, 1)
|
||||
go func() {
|
||||
for reports := range statsOptions.StatChan {
|
||||
if err := outputStats(reports); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return registry.ContainerEngine().ContainerStats(registry.Context(), args, statsOptions)
|
||||
}
|
||||
|
||||
func outputStats(reports []*define.ContainerStats) error {
|
||||
if len(statsOptions.Format) < 1 && !statsOptions.NoReset {
|
||||
tm.Clear()
|
||||
tm.MoveCursor(1, 1)
|
||||
tm.Flush()
|
||||
}
|
||||
var stats []*containerStats
|
||||
for _, r := range reports {
|
||||
stats = append(stats, &containerStats{r})
|
||||
}
|
||||
if statsOptions.Format == "json" {
|
||||
return outputJSON(stats)
|
||||
}
|
||||
format := defaultStatsRow
|
||||
if len(statsOptions.Format) > 0 {
|
||||
format = statsOptions.Format
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
}
|
||||
format = "{{range . }}" + format + "{{end}}"
|
||||
if len(statsOptions.Format) < 1 {
|
||||
format = defaultStatsHeader + format
|
||||
}
|
||||
tmpl, err := template.New("stats").Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
|
||||
if err := tmpl.Execute(w, stats); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type containerStats struct {
|
||||
*define.ContainerStats
|
||||
}
|
||||
|
||||
func (s *containerStats) ID() string {
|
||||
return s.ContainerID[0:12]
|
||||
}
|
||||
|
||||
func (s *containerStats) CPUPerc() string {
|
||||
return floatToPercentString(s.CPU)
|
||||
}
|
||||
|
||||
func (s *containerStats) MemPerc() string {
|
||||
return floatToPercentString(s.ContainerStats.MemPerc)
|
||||
}
|
||||
|
||||
func (s *containerStats) NetIO() string {
|
||||
return combineHumanValues(s.NetInput, s.NetOutput)
|
||||
}
|
||||
|
||||
func (s *containerStats) BlockIO() string {
|
||||
return combineHumanValues(s.BlockInput, s.BlockOutput)
|
||||
}
|
||||
|
||||
func (s *containerStats) PIDS() string {
|
||||
if s.PIDs == 0 {
|
||||
// If things go bazinga, return a safe value
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%d", s.PIDs)
|
||||
}
|
||||
func (s *containerStats) MemUsage() string {
|
||||
return combineHumanValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit)
|
||||
}
|
||||
|
||||
func floatToPercentString(f float64) string {
|
||||
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
|
||||
if err != nil || strippedFloat == 0 {
|
||||
// If things go bazinga, return a safe value
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%.2f", strippedFloat) + "%"
|
||||
}
|
||||
|
||||
func combineHumanValues(a, b uint64) string {
|
||||
if a == 0 && b == 0 {
|
||||
return "-- / --"
|
||||
}
|
||||
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
|
||||
}
|
||||
|
||||
func outputJSON(stats []*containerStats) error {
|
||||
type jstat struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CpuPercent string `json:"cpu_percent"`
|
||||
MemUsage string `json:"mem_usage"`
|
||||
MemPerc string `json:"mem_percent"`
|
||||
NetIO string `json:"net_io"`
|
||||
BlockIO string `json:"block_io"`
|
||||
Pids string `json:"pids"`
|
||||
}
|
||||
var jstats []jstat
|
||||
for _, j := range stats {
|
||||
jstats = append(jstats, jstat{
|
||||
Id: j.ID(),
|
||||
Name: j.Name,
|
||||
CpuPercent: j.CPUPerc(),
|
||||
MemUsage: j.MemPerc(),
|
||||
MemPerc: j.MemUsage(),
|
||||
NetIO: j.NetIO(),
|
||||
BlockIO: j.BlockIO(),
|
||||
Pids: j.PIDS(),
|
||||
})
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(jstats, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
@ -112,3 +112,22 @@ func (s ContainerExecStatus) String() string {
|
||||
return "bad state"
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerStats contains the statistics information for a running container
|
||||
type ContainerStats struct {
|
||||
ContainerID string
|
||||
Name string
|
||||
PerCPU []uint64
|
||||
CPU float64
|
||||
CPUNano uint64
|
||||
CPUSystemNano uint64
|
||||
SystemNano uint64
|
||||
MemUsage uint64
|
||||
MemLimit uint64
|
||||
MemPerc float64
|
||||
NetInput uint64
|
||||
NetOutput uint64
|
||||
BlockInput uint64
|
||||
BlockOutput uint64
|
||||
PIDs uint64
|
||||
}
|
||||
|
@ -247,14 +247,14 @@ func (p *Pod) InfraContainerID() (string, error) {
|
||||
// PodContainerStats is an organization struct for pods and their containers
|
||||
type PodContainerStats struct {
|
||||
Pod *Pod
|
||||
ContainerStats map[string]*ContainerStats
|
||||
ContainerStats map[string]*define.ContainerStats
|
||||
}
|
||||
|
||||
// GetPodStats returns the stats for each of its containers
|
||||
func (p *Pod) GetPodStats(previousContainerStats map[string]*ContainerStats) (map[string]*ContainerStats, error) {
|
||||
func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerStats) (map[string]*define.ContainerStats, error) {
|
||||
var (
|
||||
ok bool
|
||||
prevStat *ContainerStats
|
||||
prevStat *define.ContainerStats
|
||||
)
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
@ -266,10 +266,10 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*ContainerStats) (ma
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newContainerStats := make(map[string]*ContainerStats)
|
||||
newContainerStats := make(map[string]*define.ContainerStats)
|
||||
for _, c := range containers {
|
||||
if prevStat, ok = previousContainerStats[c.ID()]; !ok {
|
||||
prevStat = &ContainerStats{}
|
||||
prevStat = &define.ContainerStats{}
|
||||
}
|
||||
newStats, err := c.GetContainerStats(prevStat)
|
||||
// If the container wasn't running, don't include it
|
||||
|
@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
// GetContainerStats gets the running stats for a given container
|
||||
func (c *Container) GetContainerStats(previousStats *ContainerStats) (*ContainerStats, error) {
|
||||
stats := new(ContainerStats)
|
||||
func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) {
|
||||
stats := new(define.ContainerStats)
|
||||
stats.ContainerID = c.ID()
|
||||
stats.Name = c.Name()
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
package libpod
|
||||
|
||||
// ContainerStats contains the statistics information for a running container
|
||||
type ContainerStats struct {
|
||||
ContainerID string
|
||||
Name string
|
||||
PerCPU []uint64
|
||||
CPU float64
|
||||
CPUNano uint64
|
||||
CPUSystemNano uint64
|
||||
SystemNano uint64
|
||||
MemUsage uint64
|
||||
MemLimit uint64
|
||||
MemPerc float64
|
||||
NetInput uint64
|
||||
NetOutput uint64
|
||||
BlockInput uint64
|
||||
BlockOutput uint64
|
||||
PIDs uint64
|
||||
}
|
@ -5,6 +5,6 @@ package libpod
|
||||
import "github.com/containers/libpod/libpod/define"
|
||||
|
||||
// GetContainerStats gets the running stats for a given container
|
||||
func (c *Container) GetContainerStats(previousStats *ContainerStats) (*ContainerStats, error) {
|
||||
func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) {
|
||||
return nil, define.ErrOSNotSupported
|
||||
}
|
||||
|
@ -9,12 +9,10 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/utils"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
@ -36,24 +34,6 @@ func FuncTimer(funcName string) {
|
||||
fmt.Printf("%s executed in %d ms\n", funcName, elapsed)
|
||||
}
|
||||
|
||||
// RemoveScientificNotationFromFloat returns a float without any
|
||||
// scientific notation if the number has any.
|
||||
// golang does not handle conversion of float64s that have scientific
|
||||
// notation in them and otherwise stinks. please replace this if you have
|
||||
// a better implementation.
|
||||
func RemoveScientificNotationFromFloat(x float64) (float64, error) {
|
||||
bigNum := strconv.FormatFloat(x, 'g', -1, 64)
|
||||
breakPoint := strings.IndexAny(bigNum, "Ee")
|
||||
if breakPoint > 0 {
|
||||
bigNum = bigNum[:breakPoint]
|
||||
}
|
||||
result, err := strconv.ParseFloat(bigNum, 64)
|
||||
if err != nil {
|
||||
return x, errors.Wrapf(err, "unable to remove scientific number from calculations")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MountExists returns true if dest exists in the list of mounts
|
||||
func MountExists(specMounts []spec.Mount, dest string) bool {
|
||||
for _, m := range specMounts {
|
||||
|
@ -3,6 +3,7 @@ package libpod
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/libpod/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -10,7 +11,7 @@ func TestRemoveScientificNotationFromFloat(t *testing.T) {
|
||||
numbers := []float64{0.0, .5, 1.99999932, 1.04e+10}
|
||||
results := []float64{0.0, .5, 1.99999932, 1.04}
|
||||
for i, x := range numbers {
|
||||
result, err := RemoveScientificNotationFromFloat(x)
|
||||
result, err := utils.RemoveScientificNotationFromFloat(x)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, results[i])
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
stats, err := ctnr.GetContainerStats(&libpod.ContainerStats{})
|
||||
stats, err := ctnr.GetContainerStats(&define.ContainerStats{})
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain Container %s stats", name))
|
||||
return
|
||||
|
@ -367,3 +367,14 @@ type ContainerCpOptions struct {
|
||||
// ContainerCpReport describes the output from a cp operation
|
||||
type ContainerCpReport struct {
|
||||
}
|
||||
|
||||
// ContainerStatsOptions describes input options for getting
|
||||
// stats on containers
|
||||
type ContainerStatsOptions struct {
|
||||
All bool
|
||||
Format string
|
||||
Latest bool
|
||||
NoReset bool
|
||||
NoStream bool
|
||||
StatChan chan []*define.ContainerStats
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ type ContainerEngine interface {
|
||||
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
|
||||
ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error)
|
||||
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
|
||||
ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) error
|
||||
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
|
||||
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
|
||||
ContainerUnmount(ctx context.Context, nameOrIds []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error)
|
||||
|
@ -8,8 +8,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
lpfilters "github.com/containers/libpod/libpod/filters"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/common/pkg/config"
|
||||
@ -17,8 +16,10 @@ import (
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/libpod/events"
|
||||
lpfilters "github.com/containers/libpod/libpod/filters"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/libpod/logs"
|
||||
"github.com/containers/libpod/pkg/cgroups"
|
||||
"github.com/containers/libpod/pkg/checkpoint"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
||||
@ -1003,3 +1004,76 @@ func (ic *ContainerEngine) Shutdown(_ context.Context) {
|
||||
_ = ic.Libpod.Shutdown(false)
|
||||
})
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) error {
|
||||
containerFunc := ic.Libpod.GetRunningContainers
|
||||
switch {
|
||||
case len(namesOrIds) > 0:
|
||||
containerFunc = func() ([]*libpod.Container, error) { return ic.Libpod.GetContainersByList(namesOrIds) }
|
||||
case options.Latest:
|
||||
containerFunc = func() ([]*libpod.Container, error) {
|
||||
lastCtr, err := ic.Libpod.GetLatestContainer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*libpod.Container{lastCtr}, nil
|
||||
}
|
||||
case options.All:
|
||||
containerFunc = ic.Libpod.GetAllContainers
|
||||
}
|
||||
|
||||
ctrs, err := containerFunc()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get list of containers")
|
||||
}
|
||||
containerStats := map[string]*define.ContainerStats{}
|
||||
for _, ctr := range ctrs {
|
||||
initialStats, err := ctr.GetContainerStats(&define.ContainerStats{})
|
||||
if err != nil {
|
||||
// when doing "all", don't worry about containers that are not running
|
||||
cause := errors.Cause(err)
|
||||
if options.All && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) {
|
||||
continue
|
||||
}
|
||||
if cause == cgroups.ErrCgroupV1Rootless {
|
||||
err = cause
|
||||
}
|
||||
return err
|
||||
}
|
||||
containerStats[ctr.ID()] = initialStats
|
||||
}
|
||||
for {
|
||||
reportStats := []*define.ContainerStats{}
|
||||
for _, ctr := range ctrs {
|
||||
id := ctr.ID()
|
||||
if _, ok := containerStats[ctr.ID()]; !ok {
|
||||
initialStats, err := ctr.GetContainerStats(&define.ContainerStats{})
|
||||
if errors.Cause(err) == define.ErrCtrRemoved || errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrStateInvalid {
|
||||
// skip dealing with a container that is gone
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerStats[id] = initialStats
|
||||
}
|
||||
stats, err := ctr.GetContainerStats(containerStats[id])
|
||||
if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
|
||||
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
|
||||
}
|
||||
options.StatChan <- reportStats
|
||||
if options.NoStream {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/containers/libpod/pkg/cgroups"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/libpod/utils"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -68,7 +69,7 @@ func combineHumanValues(a, b uint64) string {
|
||||
}
|
||||
|
||||
func floatToPercentString(f float64) string {
|
||||
strippedFloat, err := libpod.RemoveScientificNotationFromFloat(f)
|
||||
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
|
||||
if err != nil || strippedFloat == 0 {
|
||||
// If things go bazinga, return a safe value
|
||||
return "--"
|
||||
|
@ -387,3 +387,7 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string,
|
||||
// Shutdown Libpod engine
|
||||
func (ic *ContainerEngine) Shutdown(_ context.Context) {
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
@ -331,7 +331,7 @@ func (i *VarlinkAPI) GetContainerStats(call iopodman.VarlinkCall, name string) e
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name, err.Error())
|
||||
}
|
||||
containerStats, err := ctr.GetContainerStats(&libpod.ContainerStats{})
|
||||
containerStats, err := ctr.GetContainerStats(&define.ContainerStats{})
|
||||
if err != nil {
|
||||
if errors.Cause(err) == define.ErrCtrStateInvalid {
|
||||
return call.ReplyNoContainerRunning()
|
||||
|
@ -8,12 +8,12 @@ import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
iopodman "github.com/containers/libpod/pkg/varlink"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
iopodman "github.com/containers/libpod/pkg/varlink"
|
||||
)
|
||||
|
||||
// CreatePod ...
|
||||
@ -263,7 +263,7 @@ func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
|
||||
if err != nil {
|
||||
return call.ReplyPodNotFound(name, err.Error())
|
||||
}
|
||||
prevStats := make(map[string]*libpod.ContainerStats)
|
||||
prevStats := make(map[string]*define.ContainerStats)
|
||||
podStats, err := pod.GetPodStats(prevStats)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
|
@ -3,14 +3,14 @@
|
||||
package varlinkapi
|
||||
|
||||
import (
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
iopodman "github.com/containers/libpod/pkg/varlink"
|
||||
)
|
||||
|
||||
// ContainerStatsToLibpodContainerStats converts the varlink containerstats to a libpod
|
||||
// container stats
|
||||
func ContainerStatsToLibpodContainerStats(stats iopodman.ContainerStats) libpod.ContainerStats {
|
||||
cstats := libpod.ContainerStats{
|
||||
func ContainerStatsToLibpodContainerStats(stats iopodman.ContainerStats) define.ContainerStats {
|
||||
cstats := define.ContainerStats{
|
||||
ContainerID: stats.Id,
|
||||
Name: stats.Name,
|
||||
CPU: stats.Cpu,
|
||||
|
@ -21,7 +21,6 @@ var _ = Describe("Podman stats", func() {
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
Skip(v2fail)
|
||||
cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
@ -125,3 +126,21 @@ func Tar(source string) (io.ReadCloser, error) {
|
||||
logrus.Debugf("creating tarball of %s", source)
|
||||
return archive.Tar(source, archive.Uncompressed)
|
||||
}
|
||||
|
||||
// RemoveScientificNotationFromFloat returns a float without any
|
||||
// scientific notation if the number has any.
|
||||
// golang does not handle conversion of float64s that have scientific
|
||||
// notation in them and otherwise stinks. please replace this if you have
|
||||
// a better implementation.
|
||||
func RemoveScientificNotationFromFloat(x float64) (float64, error) {
|
||||
bigNum := strconv.FormatFloat(x, 'g', -1, 64)
|
||||
breakPoint := strings.IndexAny(bigNum, "Ee")
|
||||
if breakPoint > 0 {
|
||||
bigNum = bigNum[:breakPoint]
|
||||
}
|
||||
result, err := strconv.ParseFloat(bigNum, 64)
|
||||
if err != nil {
|
||||
return x, errors.Wrapf(err, "unable to remove scientific number from calculations")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user