Add 'MemUsageBytes' format option

Although storage is more human-readable when expressed in SI units,
IEC/JEDEC (Bytes) units are more pertinent for memory-related values
(and match the format of the --memory* command-line options).

(To prevent possible compatibility issues, the default SI display is
left unchanged)

See https://github.com/containers/podman/issues/8945

Signed-off-by: Stuart Shelton <stuart@shelton.me>
This commit is contained in:
Stuart Shelton 2021-01-12 23:37:24 +00:00
parent 265ec914d3
commit a6af56f5b4
6 changed files with 76 additions and 51 deletions

View File

@ -147,13 +147,14 @@ func stats(cmd *cobra.Command, args []string) error {
func outputStats(reports []define.ContainerStats) error { func outputStats(reports []define.ContainerStats) error {
headers := report.Headers(define.ContainerStats{}, map[string]string{ headers := report.Headers(define.ContainerStats{}, map[string]string{
"ID": "ID", "ID": "ID",
"CPUPerc": "CPU %", "CPUPerc": "CPU %",
"MemUsage": "MEM USAGE / LIMIT", "MemUsage": "MEM USAGE / LIMIT",
"MemPerc": "MEM %", "MemUsageBytes": "MEM USAGE / LIMIT",
"NetIO": "NET IO", "MemPerc": "MEM %",
"BlockIO": "BLOCK IO", "NetIO": "NET IO",
"PIDS": "PIDS", "BlockIO": "BLOCK IO",
"PIDS": "PIDS",
}) })
if !statsOptions.NoReset { if !statsOptions.NoReset {
tm.Clear() tm.Clear()
@ -222,10 +223,15 @@ func (s *containerStats) PIDS() string {
} }
return fmt.Sprintf("%d", s.PIDs) return fmt.Sprintf("%d", s.PIDs)
} }
func (s *containerStats) MemUsage() string { func (s *containerStats) MemUsage() string {
return combineHumanValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit) return combineHumanValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit)
} }
func (s *containerStats) MemUsageBytes() string {
return combineBytesValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit)
}
func floatToPercentString(f float64) string { func floatToPercentString(f float64) string {
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f) strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
if err != nil || strippedFloat == 0 { if err != nil || strippedFloat == 0 {
@ -242,6 +248,13 @@ func combineHumanValues(a, b uint64) string {
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) 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 outputJSON(stats []containerStats) error { func outputJSON(stats []containerStats) error {
type jstat struct { type jstat struct {
Id string `json:"id"` // nolint Id string `json:"id"` // nolint

View File

@ -75,11 +75,12 @@ func stats(cmd *cobra.Command, args []string) error {
doJSON := report.IsJSON(row) doJSON := report.IsJSON(row)
headers := report.Headers(entities.PodStatsReport{}, map[string]string{ headers := report.Headers(entities.PodStatsReport{}, map[string]string{
"CPU": "CPU %", "CPU": "CPU %",
"MemUsage": "MEM USAGE/ LIMIT", "MemUsage": "MEM USAGE/ LIMIT",
"MEM": "MEM %", "MemUsageBytes": "MEM USAGE/ LIMIT",
"NET IO": "NET IO", "MEM": "MEM %",
"BlockIO": "BLOCK IO", "NET IO": "NET IO",
"BlockIO": "BLOCK IO",
}) })
for ; ; time.Sleep(time.Second) { for ; ; time.Sleep(time.Second) {

View File

@ -35,17 +35,18 @@ Pretty-print container statistics to JSON or using a Go template
Valid placeholders for the Go template are listed below: Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** | | **Placeholder** | **Description** |
| --------------- | --------------- | | --------------- | ------------------ |
| .Pod | Pod ID | | .Pod | Pod ID |
| .CID | Container ID | | .CID | Container ID |
| .Name | Container Name | | .Name | Container Name |
| .CPU | CPU percentage | | .CPU | CPU percentage |
| .MemUsage | Memory usage | | .MemUsage | Memory usage |
| .Mem | Memory percentage | | .MemUsageBytes | Memory usage (IEC) |
| .NetIO | Network IO | | .Mem | Memory percentage |
| .BlockIO | Block IO | | .NetIO | Network IO |
| .PIDS | Number of PIDs | | .BlockIO | Block IO |
| .PIDS | Number of PIDs |
When using a GO template, you may precede the format with `table` to print headers. When using a GO template, you may precede the format with `table` to print headers.
## EXAMPLE ## EXAMPLE

View File

@ -45,16 +45,17 @@ Pretty-print container statistics to JSON or using a Go template
Valid placeholders for the Go template are listed below: Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** | | **Placeholder** | **Description** |
| --------------- | --------------- | | --------------- | ------------------ |
| .ID | Container ID | | .ID | Container ID |
| .Name | Container Name | | .Name | Container Name |
| .CPUPerc | CPU percentage | | .CPUPerc | CPU percentage |
| .MemUsage | Memory usage | | .MemUsage | Memory usage |
| .MemPerc | Memory percentage | | .MemUsageBytes | Memory usage (IEC) |
| .NetIO | Network IO | | .MemPerc | Memory percentage |
| .BlockIO | Block IO | | .NetIO | Network IO |
| .PIDS | Number of PIDs | | .BlockIO | Block IO |
| .PIDS | Number of PIDs |
When using a GO template, you may precede the format with `table` to print headers. When using a GO template, you may precede the format with `table` to print headers.

View File

@ -212,15 +212,16 @@ type PodStatsOptions struct {
// PodStatsReport includes pod-resource statistics data. // PodStatsReport includes pod-resource statistics data.
type PodStatsReport struct { type PodStatsReport struct {
CPU string CPU string
MemUsage string MemUsage string
Mem string MemUsageBytes string
NetIO string Mem string
BlockIO string NetIO string
PIDS string BlockIO string
Pod string PIDS string
CID string Pod string
Name string CID string
Name string
} }
// ValidatePodStatsOptions validates the specified slice and options. Allows // ValidatePodStatsOptions validates the specified slice and options. Allows

View File

@ -44,15 +44,16 @@ func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.Po
podID := pods[i].ID()[:12] podID := pods[i].ID()[:12]
for j := range podStats { for j := range podStats {
r := entities.PodStatsReport{ r := entities.PodStatsReport{
CPU: floatToPercentString(podStats[j].CPU), CPU: floatToPercentString(podStats[j].CPU),
MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit),
Mem: floatToPercentString(podStats[j].MemPerc), MemUsageBytes: combineBytesValues(podStats[j].MemUsage, podStats[j].MemLimit),
NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), Mem: floatToPercentString(podStats[j].MemPerc),
BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput),
PIDS: pidsToString(podStats[j].PIDs), BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput),
CID: podStats[j].ContainerID[:12], PIDS: pidsToString(podStats[j].PIDs),
Name: podStats[j].Name, CID: podStats[j].ContainerID[:12],
Pod: podID, Name: podStats[j].Name,
Pod: podID,
} }
reports = append(reports, &r) reports = append(reports, &r)
} }
@ -68,6 +69,13 @@ func combineHumanValues(a, b uint64) string {
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) 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 { func floatToPercentString(f float64) string {
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f) strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
if err != nil || strippedFloat == 0 { if err != nil || strippedFloat == 0 {