github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/abi/pods_stats.go (about)

     1  package abi
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/containers/common/pkg/cgroups"
     8  	"github.com/hanks177/podman/v4/libpod"
     9  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    10  	"github.com/hanks177/podman/v4/pkg/rootless"
    11  	"github.com/hanks177/podman/v4/utils"
    12  	"github.com/docker/go-units"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // PodStats implements printing stats about pods.
    17  func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) {
    18  	// Cgroups v2 check for rootless.
    19  	if rootless.IsRootless() {
    20  		unified, err := cgroups.IsCgroup2UnifiedMode()
    21  		if err != nil {
    22  			return nil, err
    23  		}
    24  		if !unified {
    25  			return nil, errors.New("pod stats is not supported in rootless mode without cgroups v2")
    26  		}
    27  	}
    28  	// Get the (running) pods and convert them to the entities format.
    29  	pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
    30  	if err != nil {
    31  		return nil, errors.Wrap(err, "unable to get list of pods")
    32  	}
    33  	return ic.podsToStatsReport(pods)
    34  }
    35  
    36  // podsToStatsReport converts a slice of pods into a corresponding slice of stats reports.
    37  func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) {
    38  	reports := []*entities.PodStatsReport{}
    39  	for i := range pods { // Access by index to prevent potential loop-variable leaks.
    40  		podStats, err := pods[i].GetPodStats(nil)
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		podID := pods[i].ID()[:12]
    45  		for j := range podStats {
    46  			r := entities.PodStatsReport{
    47  				CPU:           floatToPercentString(podStats[j].CPU),
    48  				MemUsage:      combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit),
    49  				MemUsageBytes: combineBytesValues(podStats[j].MemUsage, podStats[j].MemLimit),
    50  				Mem:           floatToPercentString(podStats[j].MemPerc),
    51  				NetIO:         combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput),
    52  				BlockIO:       combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput),
    53  				PIDS:          pidsToString(podStats[j].PIDs),
    54  				CID:           podStats[j].ContainerID[:12],
    55  				Name:          podStats[j].Name,
    56  				Pod:           podID,
    57  			}
    58  			reports = append(reports, &r)
    59  		}
    60  	}
    61  
    62  	return reports, nil
    63  }
    64  
    65  func combineHumanValues(a, b uint64) string {
    66  	if a == 0 && b == 0 {
    67  		return "-- / --"
    68  	}
    69  	return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
    70  }
    71  
    72  func combineBytesValues(a, b uint64) string {
    73  	if a == 0 && b == 0 {
    74  		return "-- / --"
    75  	}
    76  	return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b)))
    77  }
    78  
    79  func floatToPercentString(f float64) string {
    80  	strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
    81  	if err != nil || strippedFloat == 0 {
    82  		// If things go bazinga, return a safe value
    83  		return "--"
    84  	}
    85  	return fmt.Sprintf("%.2f", strippedFloat) + "%"
    86  }
    87  
    88  func pidsToString(pid uint64) string {
    89  	if pid == 0 {
    90  		// If things go bazinga, return a safe value
    91  		return "--"
    92  	}
    93  	return fmt.Sprintf("%d", pid)
    94  }