summaryrefslogtreecommitdiff
path: root/pkg/domain/infra/abi/pods_stats.go
blob: 4cab6bea601888642fe1c0cd24d7d46a8fd9de81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package abi

import (
	"context"
	"fmt"

	"github.com/containers/libpod/v2/libpod"
	"github.com/containers/libpod/v2/pkg/cgroups"
	"github.com/containers/libpod/v2/pkg/domain/entities"
	"github.com/containers/libpod/v2/pkg/rootless"
	"github.com/containers/libpod/v2/utils"
	"github.com/docker/go-units"
	"github.com/pkg/errors"
)

// PodStats implements printing stats about pods.
func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) {
	// Cgroups v2 check for rootless.
	if rootless.IsRootless() {
		unified, err := cgroups.IsCgroup2UnifiedMode()
		if err != nil {
			return nil, err
		}
		if !unified {
			return nil, errors.New("pod stats is not supported in rootless mode without cgroups v2")
		}
	}
	// Get the (running) pods and convert them to the entities format.
	pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
	if err != nil {
		return nil, errors.Wrap(err, "unable to get list of pods")
	}
	return ic.podsToStatsReport(pods)
}

// podsToStatsReport converts a slice of pods into a corresponding slice of stats reports.
func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) {
	reports := []*entities.PodStatsReport{}
	for i := range pods { // Access by index to prevent potential loop-variable leaks.
		podStats, err := pods[i].GetPodStats(nil)
		if err != nil {
			return nil, err
		}
		podID := pods[i].ID()[:12]
		for j := range podStats {
			r := entities.PodStatsReport{
				CPU:      floatToPercentString(podStats[j].CPU),
				MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit),
				Mem:      floatToPercentString(podStats[j].MemPerc),
				NetIO:    combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput),
				BlockIO:  combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput),
				PIDS:     pidsToString(podStats[j].PIDs),
				CID:      podStats[j].ContainerID[:12],
				Name:     podStats[j].Name,
				Pod:      podID,
			}
			reports = append(reports, &r)
		}
	}

	return reports, nil
}

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 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 pidsToString(pid uint64) string {
	if pid == 0 {
		// If things go bazinga, return a safe value
		return "--"
	}
	return fmt.Sprintf("%d", pid)
}