aboutsummaryrefslogtreecommitdiff
path: root/pkg/domain/infra/abi/pods_stats.go
blob: a270db769feca428ab84801ff3a294eb4f77b731 (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
87
88
89
90
91
92
93
94
package abi

import (
	"context"
	"errors"
	"fmt"

	"github.com/containers/common/pkg/cgroups"
	"github.com/containers/podman/v4/libpod"
	"github.com/containers/podman/v4/pkg/domain/entities"
	"github.com/containers/podman/v4/pkg/rootless"
	"github.com/containers/podman/v4/utils"
	"github.com/docker/go-units"
)

// 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, fmt.Errorf("unable to get list of pods: %w", err)
	}
	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),
				MemUsageBytes: combineBytesValues(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 combineBytesValues(a, b uint64) string {
	if a == 0 && b == 0 {
		return "-- / --"
	}
	return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(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)
}