ls enhancement: show running stats

Mainly for zabbix, but why not. Format of both json and text output of ls
has changed.
This commit is contained in:
Joel Elkins 2024-02-22 21:56:34 -06:00
parent 13e35e9c4d
commit 139ea8707c
No known key found for this signature in database
GPG Key ID: 133589DC38921AE2
2 changed files with 82 additions and 6 deletions

View File

@ -48,6 +48,20 @@ type lsContainerObj struct {
Running bool `json:"running"`
}
type lsContainerStatsObj struct {
CPU float64 `json:"cpu_usage_pct"`
CPUNano uint64 `json:"cpu_nano"`
MemUsage uint64 `json:"mem_usage_bytes"`
MemLimit uint64 `json:"mem_limit_bytes"`
MemPerc float64 `json:"mem_usage_pct"`
NetInput uint64 `json:"net_input_bytes"`
NetOutput uint64 `json:"net_output_bytes"`
BlockInput uint64 `json:"block_input_bytes"`
BlockOutput uint64 `json:"block_output_bytes"`
UpTime uint64 `json:"uptime_sec"`
lsContainerObj
}
// lsCmd represents the ls command
var lsCmd = &cobra.Command{
Use: "ls",
@ -90,20 +104,41 @@ ccl ls squid`,
}
if lsJsonFormat {
out := make([]lsContainerObj, 0)
out := make([]interface{}, 0)
for _, c := range conts {
run, cre := false, false
if conn != nil {
run, cre = c.IsRunning(), c.IsCreated()
}
out = append(out, lsContainerObj{
baseObj := lsContainerObj{
Category: c.Category,
StartGroup: c.StartGroup,
Name: c.Name,
Image: c.Image,
Running: run,
Created: cre,
})
}
if run {
if stats := c.GetStats(); stats != nil {
out = append(out, lsContainerStatsObj{
CPUNano: stats.CPUNano,
CPU: stats.CPU,
MemUsage: stats.MemUsage,
MemLimit: stats.MemLimit,
MemPerc: stats.MemPerc,
NetInput: stats.NetInput,
NetOutput: stats.NetOutput,
BlockInput: stats.BlockInput,
BlockOutput: stats.BlockOutput,
UpTime: uint64(c.RunningTime().Seconds()),
lsContainerObj: baseObj,
})
} else {
out = append(out, baseObj)
}
} else {
out = append(out, baseObj)
}
}
val, err := json.Marshal(out)
if err != nil {
@ -117,7 +152,8 @@ ccl ls squid`,
defer tw.Flush()
if conn != nil {
titlemsg := "CATEGORY\tGROUP\tNAME\tIMAGE\tCREATED\tRUNNING"
titlemsg := "CATEGORY\tGROUP\tNAME\tIMAGE\tCREATED\t RUNNING\tCPU%\tMEM%"
block := "%s\t%5d\t%s\t%s\t%s\t%s\t%.1f\t%.1f\n"
fmt.Fprintf(tw, "%s\n", titlemsg)
for _, c := range conts {
data := []interface{}{c.Category, c.StartGroup, c.Name, c.Image}
@ -127,11 +163,21 @@ ccl ls squid`,
data = append(data, "")
}
if c.IsRunning() {
data = append(data, " ✓")
raw := int64(c.RunningTime().Seconds())
seconds := raw % 60
minutes := (raw / 60) % 60
hours := (raw / 60) / 60
disp := fmt.Sprintf("%3d:%02d:%02d", hours, minutes, seconds)
data = append(data, disp)
} else {
data = append(data, "")
}
fmt.Fprintf(tw, "%s\t%5d\t%s\t%s\t%s\t%s\n", data...)
if stats := c.GetStats(); c.IsRunning() && stats != nil {
data = append(data, stats.CPU, stats.MemPerc)
} else {
data = append(data, 0.0, 0.0)
}
fmt.Fprintf(tw, block, data...)
}
} else {
titlemsg := "CATEGORY\tGROUP\tNAME\tIMAGE"

View File

@ -28,8 +28,10 @@ import (
"context"
"fmt"
"net"
"os"
"os/exec"
"regexp"
"time"
cmd "gitea.elkins.co/Networking/ccl/internal/pkg/command"
"gitea.elkins.co/Networking/ccl/internal/pkg/network"
@ -390,6 +392,14 @@ func (c *Container) IsCreated() bool {
return true
}
func (c *Container) RunningTime() time.Duration {
cdata := c.getCData()
if cdata == nil || cdata.State == nil {
return 0
}
return time.Since(cdata.State.StartedAt)
}
// UpdateCommands will pull the image (to force updates) and then recreate the
// container. It will be stopped first.
func (c *Container) UpdateCommands() cmd.Set {
@ -503,6 +513,26 @@ func (c *Container) watchCData() {
}
}
func (c *Container) GetStats() *define.ContainerStats {
no := false
reportChan, err := containers.Stats(c.conn, []string{c.getCData().ID}, &containers.StatsOptions{Stream: &no})
if err != nil {
fmt.Fprintf(os.Stderr, "containers.stats returned error (%s): %s\n", c.Name, err)
return nil
}
select {
case report := <-reportChan:
if report.Error != nil {
fmt.Fprintf(os.Stderr, "containers.stats returned error in the channel (%s): %s\n", c.Name, report.Error)
break
}
return &report.Stats[0]
case <-time.After(250 * time.Millisecond):
break
}
return nil
}
// Pid will return the host process id of the main container process (pid
// 1 inside the container)
func (c *Container) Pid() int {