Merge pull request #1235 from hashicorp/stats-cli

Rework the node-status and alloc-status cli
This commit is contained in:
Diptanu Choudhury 2016-06-06 16:11:30 -07:00
commit 38b3894667
5 changed files with 147 additions and 221 deletions

View file

@ -656,30 +656,38 @@ func (r *TaskRunner) Destroy() {
// emitStats emits resource usage stats of tasks to remote metrics collector
// sinks
func (r *TaskRunner) emitStats(ru *cstructs.TaskResourceUsage) {
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "rss"}, float32(ru.ResourceUsage.MemoryStats.RSS))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "cache"}, float32(ru.ResourceUsage.MemoryStats.Cache))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "swap"}, float32(ru.ResourceUsage.MemoryStats.Swap))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "max_usage"}, float32(ru.ResourceUsage.MemoryStats.MaxUsage))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "kernel_usage"}, float32(ru.ResourceUsage.MemoryStats.KernelUsage))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "kernel_max_usage"}, float32(ru.ResourceUsage.MemoryStats.KernelMaxUsage))
if ru.ResourceUsage.MemoryStats != nil {
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "rss"}, float32(ru.ResourceUsage.MemoryStats.RSS))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "cache"}, float32(ru.ResourceUsage.MemoryStats.Cache))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "swap"}, float32(ru.ResourceUsage.MemoryStats.Swap))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "max_usage"}, float32(ru.ResourceUsage.MemoryStats.MaxUsage))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "kernel_usage"}, float32(ru.ResourceUsage.MemoryStats.KernelUsage))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "memory", "kernel_max_usage"}, float32(ru.ResourceUsage.MemoryStats.KernelMaxUsage))
}
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "percent"}, float32(ru.ResourceUsage.CpuStats.Percent))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "system"}, float32(ru.ResourceUsage.CpuStats.SystemMode))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "user"}, float32(ru.ResourceUsage.CpuStats.UserMode))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "throttled_time"}, float32(ru.ResourceUsage.CpuStats.ThrottledTime))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "throttled_periods"}, float32(ru.ResourceUsage.CpuStats.ThrottledPeriods))
if ru.ResourceUsage.CpuStats != nil {
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "percent"}, float32(ru.ResourceUsage.CpuStats.Percent))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "system"}, float32(ru.ResourceUsage.CpuStats.SystemMode))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "user"}, float32(ru.ResourceUsage.CpuStats.UserMode))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "throttled_time"}, float32(ru.ResourceUsage.CpuStats.ThrottledTime))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, "cpu", "throttled_periods"}, float32(ru.ResourceUsage.CpuStats.ThrottledPeriods))
}
for pid, pidStats := range ru.Pids {
// Not emitting max, kernel usages since we never get them on a per-pid
// basis
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "memory", "rss"}, float32(pidStats.MemoryStats.RSS))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "memory", "cache"}, float32(pidStats.MemoryStats.Cache))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "memory", "swap"}, float32(pidStats.MemoryStats.Swap))
if pidStats.MemoryStats != nil {
// Not emitting max, kernel usages since we never get them on a per-pid
// basis
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "memory", "rss"}, float32(pidStats.MemoryStats.RSS))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "memory", "cache"}, float32(pidStats.MemoryStats.Cache))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "memory", "swap"}, float32(pidStats.MemoryStats.Swap))
}
// Not emitting throttled time and periods since we never get them on a
// per pid basis
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "cpu", "percent"}, float32(pidStats.CpuStats.Percent))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "cpu", "system"}, float32(pidStats.CpuStats.SystemMode))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "cpu", "user"}, float32(pidStats.CpuStats.UserMode))
if pidStats.CpuStats != nil {
// Not emitting throttled time and periods since we never get them on a
// per pid basis
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "cpu", "percent"}, float32(pidStats.CpuStats.Percent))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "cpu", "system"}, float32(pidStats.CpuStats.SystemMode))
metrics.EmitKey([]string{r.alloc.Job.Name, r.alloc.Name, r.alloc.ID, r.task.Name, pid, "cpu", "user"}, float32(pidStats.CpuStats.UserMode))
}
}
}

View file

@ -2,17 +2,22 @@ package command
import (
"fmt"
"math"
"sort"
"strconv"
"strings"
"time"
"github.com/dustin/go-humanize"
"github.com/mitchellh/colorstring"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/client"
)
type AllocStatusCommand struct {
Meta
color *colorstring.Colorize
}
func (c *AllocStatusCommand) Help() string {
@ -33,6 +38,9 @@ Alloc Status Options:
-short
Display short output. Shows only the most recent task event.
-stats
Display detailed resource usage statistics
-verbose
Show full information.
`
@ -45,12 +53,13 @@ func (c *AllocStatusCommand) Synopsis() string {
}
func (c *AllocStatusCommand) Run(args []string) int {
var short, verbose bool
var short, displayStats, verbose bool
flags := c.Meta.FlagSet("alloc-status", FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&short, "short", false, "")
flags.BoolVar(&verbose, "verbose", false, "")
flags.BoolVar(&displayStats, "stats", false, "")
if err := flags.Parse(args); err != nil {
return 1
@ -147,7 +156,7 @@ func (c *AllocStatusCommand) Run(args []string) int {
c.Ui.Output(formatKV(basic))
if !short {
c.taskResources(alloc, stats)
c.taskResources(alloc, stats, displayStats)
}
// Print the state of each task.
@ -309,7 +318,7 @@ func (c *AllocStatusCommand) allocResources(alloc *api.Allocation) {
}
// taskResources prints out the tasks current resource usage
func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[string]*api.TaskResourceUsage) {
func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[string]*api.TaskResourceUsage, displayStats bool) {
if len(alloc.TaskResources) == 0 {
return
}
@ -326,12 +335,12 @@ func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[stri
for _, task := range tasks {
resource := alloc.TaskResources[task]
header := fmt.Sprintf("\nTask: %q", task)
header := fmt.Sprintf("\n[bold]Task: %q[reset]", task)
if firstLine {
header = fmt.Sprintf("Task: %q", task)
header = fmt.Sprintf("[bold]Task: %q[reset]", task)
firstLine = false
}
c.Ui.Output(header)
c.Ui.Output(c.Colorize().Color(header))
var addr []string
for _, nw := range resource.Networks {
ports := append(nw.DynamicPorts, nw.ReservedPorts...)
@ -340,7 +349,7 @@ func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[stri
}
}
var resourcesOutput []string
resourcesOutput = append(resourcesOutput, "CPU|Memory MB|Disk MB|IOPS|Addresses")
resourcesOutput = append(resourcesOutput, "CPU|Memory|Disk|IOPS|Addresses")
firstAddr := ""
if len(addr) > 0 {
firstAddr = addr[0]
@ -348,12 +357,12 @@ func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[stri
cpuUsage := strconv.Itoa(resource.CPU)
memUsage := strconv.Itoa(resource.MemoryMB)
if ru, ok := stats[task]; ok && ru != nil && ru.ResourceUsage != nil {
cpuStats := ru.ResourceUsage.CpuStats
cpuTicksConsumed := (ru.ResourceUsage.CpuStats.Percent / 100) * float64(resource.CPU)
memoryStats := ru.ResourceUsage.MemoryStats
cpuUsage = fmt.Sprintf("%v/%v", (cpuStats.SystemMode + cpuStats.UserMode), resource.CPU)
cpuUsage = fmt.Sprintf("%v/%v", math.Floor(cpuTicksConsumed), resource.CPU)
memUsage = fmt.Sprintf("%v/%v", memoryStats.RSS/(1024*1024), resource.MemoryMB)
}
resourcesOutput = append(resourcesOutput, fmt.Sprintf("%v|%v|%v|%v|%v",
resourcesOutput = append(resourcesOutput, fmt.Sprintf("%v|%v MB|%v MB|%v|%v",
cpuUsage,
memUsage,
resource.DiskMB,
@ -363,5 +372,37 @@ func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[stri
resourcesOutput = append(resourcesOutput, fmt.Sprintf("||||%v", addr[i]))
}
c.Ui.Output(formatListWithSpaces(resourcesOutput))
if ru, ok := stats[task]; ok && ru != nil && displayStats && ru.ResourceUsage != nil {
c.Ui.Output("")
c.printTaskResourceUsage(task, ru.ResourceUsage)
}
}
}
func (c *AllocStatusCommand) printTaskResourceUsage(task string, resourceUsage *api.ResourceUsage) {
memoryStats := resourceUsage.MemoryStats
cpuStats := resourceUsage.CpuStats
c.Ui.Output("Memory Stats")
out := make([]string, 2)
out[0] = "RSS|Cache|Swap|Max Usage|Kernel Usage|Kernel Max Usage"
out[1] = fmt.Sprintf("%v|%v|%v|%v|%v|%v",
humanize.Bytes(memoryStats.RSS),
humanize.Bytes(memoryStats.Cache),
humanize.Bytes(memoryStats.Swap),
humanize.Bytes(memoryStats.MaxUsage),
humanize.Bytes(memoryStats.KernelUsage),
humanize.Bytes(memoryStats.KernelMaxUsage),
)
c.Ui.Output(formatList(out))
c.Ui.Output("")
c.Ui.Output("CPU Stats")
out = make([]string, 2)
out[0] = "Percent|Throttled Periods|Throttled Time"
percent := strconv.FormatFloat(cpuStats.Percent, 'f', 2, 64)
out[1] = fmt.Sprintf("%v %|%v|%v", percent,
cpuStats.ThrottledPeriods, cpuStats.ThrottledTime)
c.Ui.Output(formatList(out))
}

View file

@ -8,12 +8,14 @@ import (
"time"
"github.com/dustin/go-humanize"
"github.com/mitchellh/colorstring"
"github.com/hashicorp/nomad/api"
)
type NodeStatusCommand struct {
Meta
color *colorstring.Colorize
}
func (c *NodeStatusCommand) Help() string {
@ -42,6 +44,9 @@ Node Status Options:
-verbose
Display full information.
-stats
Display detailed resource usage statistics
-self
Query the status of the local node.
@ -56,7 +61,7 @@ func (c *NodeStatusCommand) Synopsis() string {
}
func (c *NodeStatusCommand) Run(args []string) int {
var short, verbose, list_allocs, self bool
var short, verbose, list_allocs, self, stats bool
var hostStats *api.HostStats
flags := c.Meta.FlagSet("node-status", FlagSetClient)
@ -65,6 +70,7 @@ func (c *NodeStatusCommand) Run(args []string) int {
flags.BoolVar(&verbose, "verbose", false, "")
flags.BoolVar(&list_allocs, "allocs", false, "")
flags.BoolVar(&self, "self", false, "")
flags.BoolVar(&stats, "stats", false, "")
if err := flags.Parse(args); err != nil {
return 1
@ -204,7 +210,7 @@ func (c *NodeStatusCommand) Run(args []string) int {
// Format the output
basic := []string{
fmt.Sprintf("ID|%s", limit(node.ID, length)),
fmt.Sprintf("[bold]Node ID[reset]|%s", limit(node.ID, length)),
fmt.Sprintf("Name|%s", node.Name),
fmt.Sprintf("Class|%s", node.NodeClass),
fmt.Sprintf("DC|%s", node.Datacenter),
@ -215,22 +221,29 @@ func (c *NodeStatusCommand) Run(args []string) int {
uptime := time.Duration(hostStats.Uptime * uint64(time.Second))
basic = append(basic, fmt.Sprintf("Uptime|%s", uptime.String()))
}
c.Ui.Output(formatKV(basic))
c.Ui.Output(c.Colorize().Color(formatKV(basic)))
if !short {
resources, err := getResources(client, node)
allocatedResources, err := getAllocatedResources(client, node)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error querying node resources: %s", err))
return 1
}
c.Ui.Output("\n==> Resource Utilization")
c.Ui.Output(formatList(resources))
if hostStats != nil {
c.Ui.Output("\n===> Node CPU Stats")
c.Ui.Output(c.Colorize().Color("\n[bold]==> Resource Utilization (Allocated)[reset]"))
c.Ui.Output(formatList(allocatedResources))
actualResources, err := getActualResources(hostStats, node)
if err == nil {
c.Ui.Output(c.Colorize().Color("\n[bold]==> Resource Utilization (Actual)[reset]"))
c.Ui.Output(formatList(actualResources))
}
if hostStats != nil && stats {
c.Ui.Output(c.Colorize().Color("\n===> [bold]Detailed CPU Stats[reset]"))
c.printCpuStats(hostStats)
c.Ui.Output("\n===> Node Memory Stats")
c.Ui.Output(c.Colorize().Color("\n===> [bold]Detailed Memory Stats[reset]"))
c.printMemoryStats(hostStats)
c.Ui.Output("\n===> Node Disk Stats")
c.Ui.Output(c.Colorize().Color("\n===> [bold]Detailed Disk Stats[reset]"))
c.printDiskStats(hostStats)
}
@ -271,9 +284,9 @@ func (c *NodeStatusCommand) printCpuStats(hostStats *api.HostStats) {
for _, cpuStat := range hostStats.CPU {
cpuStatsAttr := make([]string, 4)
cpuStatsAttr[0] = fmt.Sprintf("CPU|%v", cpuStat.CPU)
cpuStatsAttr[1] = fmt.Sprintf("User|%v", formatFloat64(cpuStat.User))
cpuStatsAttr[2] = fmt.Sprintf("System|%v", formatFloat64(cpuStat.System))
cpuStatsAttr[3] = fmt.Sprintf("Idle|%v", formatFloat64(cpuStat.Idle))
cpuStatsAttr[1] = fmt.Sprintf("User|%v %%", formatFloat64(cpuStat.User))
cpuStatsAttr[2] = fmt.Sprintf("System|%v %%", formatFloat64(cpuStat.System))
cpuStatsAttr[3] = fmt.Sprintf("Idle|%v %%", formatFloat64(cpuStat.Idle))
c.Ui.Output(formatKV(cpuStatsAttr))
c.Ui.Output("")
}
@ -297,8 +310,8 @@ func (c *NodeStatusCommand) printDiskStats(hostStats *api.HostStats) {
diskStatsAttr[2] = fmt.Sprintf("Size|%s", humanize.Bytes(diskStat.Size))
diskStatsAttr[3] = fmt.Sprintf("Used|%s", humanize.Bytes(diskStat.Used))
diskStatsAttr[4] = fmt.Sprintf("Available|%s", humanize.Bytes(diskStat.Available))
diskStatsAttr[5] = fmt.Sprintf("Used Percent|%s", formatFloat64(diskStat.UsedPercent))
diskStatsAttr[6] = fmt.Sprintf("Inodes Percent|%s", formatFloat64(diskStat.InodesUsedPercent))
diskStatsAttr[5] = fmt.Sprintf("Used Percent|%v %%", formatFloat64(diskStat.UsedPercent))
diskStatsAttr[6] = fmt.Sprintf("Inodes Percent|%v %%", formatFloat64(diskStat.InodesUsedPercent))
c.Ui.Output(formatKV(diskStatsAttr))
c.Ui.Output("")
}
@ -339,8 +352,8 @@ func getAllocs(client *api.Client, node *api.Node, length int) ([]string, error)
return allocs, err
}
// getResources returns the resource usage of the node.
func getResources(client *api.Client, node *api.Node) ([]string, error) {
// getAllocatedResources returns the resource usage of the node.
func getAllocatedResources(client *api.Client, node *api.Node) ([]string, error) {
var resources []string
var cpu, mem, disk, iops int
var totalCpu, totalMem, totalDisk, totalIops int
@ -382,6 +395,43 @@ func getResources(client *api.Client, node *api.Node) ([]string, error) {
return resources, err
}
// getActualResources returns the actual resource usage of the node.
func getActualResources(hostStats *api.HostStats, node *api.Node) ([]string, error) {
if hostStats == nil {
return nil, fmt.Errorf("actual resource usage not present")
}
var resources []string
// Calculate cpu usage
usedCPUPercent := 0.0
for _, cpu := range hostStats.CPU {
usedCPUPercent += (cpu.User + cpu.System)
}
usedCPUTicks := (usedCPUPercent / 100) * float64(node.Resources.CPU)
// calculate disk usage
storageDevice := node.Attributes["unique.storage.volume"]
var diskUsed, diskSize uint64
for _, disk := range hostStats.DiskStats {
if disk.Device == storageDevice {
diskUsed = disk.Used
diskSize = disk.Size
}
}
resources = make([]string, 2)
resources[0] = "CPU|Memory|Disk"
resources[1] = fmt.Sprintf("%v/%v|%v/%v|%v/%v",
int64(usedCPUTicks),
node.Resources.CPU,
humanize.Bytes(hostStats.Memory.Used),
humanize.Bytes(hostStats.Memory.Total),
humanize.Bytes(diskUsed),
humanize.Bytes(diskSize),
)
return resources, nil
}
func formatFloat64(val float64) string {
return strconv.FormatFloat(val, 'f', 2, 64)
}

View file

@ -1,168 +0,0 @@
package command
import (
"fmt"
"strconv"
"strings"
"github.com/dustin/go-humanize"
"github.com/hashicorp/nomad/api"
)
type StatsCommand struct {
Meta
}
func (f *StatsCommand) Help() string {
helpText := `
Usage: nomad stats [options] <alloc-id>
Displays statistics related to resource usage of tasks in an allocation. Use
the -task flag to query statistics of an individual task running in an
allocation.
General Options:
` + generalOptionsUsage() + `
Node Stats Options:
-task
Display statistics for a specific task in an allocation.
`
return strings.TrimSpace(helpText)
}
func (f *StatsCommand) Synopsis() string {
return "Dispalys resource usage stats of an allocation or a task running on a nomad client"
}
func (f *StatsCommand) Run(args []string) int {
var verbose bool
var task string
flags := f.Meta.FlagSet("stats", FlagSetClient)
flags.BoolVar(&verbose, "verbose", false, "")
flags.StringVar(&task, "task", "", "")
flags.Usage = func() { f.Ui.Output(f.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
args = flags.Args()
if len(args) < 1 {
f.Ui.Error("allocation id is a required parameter")
return 1
}
client, err := f.Meta.Client()
if err != nil {
f.Ui.Error(fmt.Sprintf("Error initializing client: %v", err))
return 1
}
var allocID string
allocID = strings.TrimSpace(args[0])
// Truncate the id unless full length is requested
length := shortId
if verbose {
length = fullId
}
// Query the allocation info
if len(allocID) == 1 {
f.Ui.Error(fmt.Sprintf("Alloc ID must contain at least two characters."))
return 1
}
if len(allocID)%2 == 1 {
// Identifiers must be of even length, so we strip off the last byte
// to provide a consistent user experience.
allocID = allocID[:len(allocID)-1]
}
allocs, _, err := client.Allocations().PrefixList(allocID)
if err != nil {
f.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
return 1
}
if len(allocs) == 0 {
f.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
return 1
}
if len(allocs) > 1 {
// Format the allocs
out := make([]string, len(allocs)+1)
out[0] = "ID|Eval ID|Job ID|Task Group|Desired Status|Client Status"
for i, alloc := range allocs {
out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s",
limit(alloc.ID, length),
limit(alloc.EvalID, length),
alloc.JobID,
alloc.TaskGroup,
alloc.DesiredStatus,
alloc.ClientStatus,
)
}
f.Ui.Output(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", formatList(out)))
return 0
}
// Prefix lookup matched a single allocation
alloc, _, err := client.Allocations().Info(allocs[0].ID, nil)
if err != nil {
f.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
return 1
}
stats, err := client.Allocations().Stats(alloc, nil)
if err != nil {
f.Ui.Error(fmt.Sprintf("unable to get stats: %v", err))
return 1
}
if task == "" {
f.printAllocResourceUsage(alloc, stats)
} else {
f.printTaskResourceUsage(task, stats)
}
return 0
}
func (f *StatsCommand) printTaskResourceUsage(task string, resourceUsage map[string]*api.TaskResourceUsage) {
tu, ok := resourceUsage[task]
if !ok {
return
}
memoryStats := tu.ResourceUsage.MemoryStats
cpuStats := tu.ResourceUsage.CpuStats
f.Ui.Output(fmt.Sprintf("===> Task: %q", task))
f.Ui.Output("Memory Stats")
out := make([]string, 2)
out[0] = "RSS|Cache|Swap|Max Usage|Kernel Usage|KernelMaxUsage"
out[1] = fmt.Sprintf("%v|%v|%v|%v|%v|%v",
humanize.Bytes(memoryStats.RSS),
humanize.Bytes(memoryStats.Cache),
humanize.Bytes(memoryStats.Swap),
humanize.Bytes(memoryStats.MaxUsage),
humanize.Bytes(memoryStats.KernelUsage),
humanize.Bytes(memoryStats.KernelMaxUsage),
)
f.Ui.Output(formatList(out))
f.Ui.Output("")
f.Ui.Output("CPU Stats")
out = make([]string, 2)
out[0] = "Percent|Throttled Periods|Throttled Time"
percent := strconv.FormatFloat(cpuStats.Percent, 'f', 2, 64)
out[1] = fmt.Sprintf("%v|%v|%v", percent,
cpuStats.ThrottledPeriods, cpuStats.ThrottledTime)
f.Ui.Output(formatList(out))
}
func (f *StatsCommand) printAllocResourceUsage(alloc *api.Allocation, resourceUsage map[string]*api.TaskResourceUsage) {
f.Ui.Output(fmt.Sprintf("Resource Usage of Tasks running in Allocation %q", alloc.ID))
for task, _ := range alloc.TaskStates {
f.printTaskResourceUsage(task, resourceUsage)
}
}

View file

@ -121,11 +121,6 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
Meta: meta,
}, nil
},
"stats": func() (cli.Command, error) {
return &command.StatsCommand{
Meta: meta,
}, nil
},
"status": func() (cli.Command, error) {
return &command.StatusCommand{
Meta: meta,