osctl top enhancements (#568)
* feat(osctl): Automatic sizing of top window Signed-off-by: Brad Beam <brad.beam@talos-systems.com> * feat(osctl): Format top output in proper columns Signed-off-by: Brad Beam <brad.beam@talos-systems.com> * feat(osctl): Add sort by cpu/rss options Signed-off-by: Brad Beam <brad.beam@talos-systems.com> * feat(osctl): Add ability to run once (no gui) Signed-off-by: Brad Beam <brad.beam@talos-systems.com>
This commit is contained in:
parent
68c2a2735d
commit
1a5be8da47
@ -10,13 +10,16 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/bytefmt"
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
"github.com/ryanuber/columnize"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/talos-systems/talos/cmd/osctl/pkg/client"
|
||||
"github.com/talos-systems/talos/cmd/osctl/pkg/helpers"
|
||||
"github.com/talos-systems/talos/internal/pkg/constants"
|
||||
"github.com/talos-systems/talos/internal/pkg/proc"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
@ -34,6 +37,18 @@ var topCmd = &cobra.Command{
|
||||
helpers.Fatalf("error constructing client: %s", err)
|
||||
}
|
||||
|
||||
if oneTime {
|
||||
var output string
|
||||
output, err = topOutput(c)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Note this is unlimited output of process lines
|
||||
// we arent artificially limited by the box we would otherwise draw
|
||||
fmt.Println(output)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ui.Init(); err != nil {
|
||||
log.Fatalf("failed to initialize termui: %v", err)
|
||||
}
|
||||
@ -43,42 +58,43 @@ var topCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var sortMethod string
|
||||
var oneTime bool
|
||||
|
||||
func init() {
|
||||
topCmd.Flags().StringVarP(&sortMethod, "sort", "s", "rss", "Column to sort output by. [rss|cpu]")
|
||||
topCmd.Flags().BoolVarP(&oneTime, "once", "1", false, "Print the current top output ( no gui/auto refresh )")
|
||||
rootCmd.AddCommand(topCmd)
|
||||
}
|
||||
|
||||
func topUI(c *client.Client) {
|
||||
|
||||
l := widgets.NewList()
|
||||
l := widgets.NewParagraph()
|
||||
l.Title = "Top"
|
||||
// TODO see if we can dynamically get this
|
||||
// x, y, w, h
|
||||
l.SetRect(0, 0, 65, 30)
|
||||
l.TextStyle.Fg = ui.ColorYellow
|
||||
|
||||
draw := func(procs []proc.ProcessList) {
|
||||
rss := func(p1, p2 *proc.ProcessList) bool {
|
||||
// Reverse sort ( Descending )
|
||||
return p1.ResidentMemory > p2.ResidentMemory
|
||||
draw := func(output string) {
|
||||
l.Text = output
|
||||
|
||||
// Attempt to get terminal dimensions
|
||||
// Since we're getting this data on each call
|
||||
// we'll be able to handle terminal window resizing
|
||||
w, h, err := terminal.GetSize(0)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to determine terminal size")
|
||||
}
|
||||
by(rss).sort(procs)
|
||||
s := make([]string, 0, len(procs))
|
||||
s = append(s, fmt.Sprintf("%s %s %s %s %s %s %s %s", "PID", "State", "Threads", "CPU Time", "VirtMem", "ResMem", "Command", "Exec/Args"))
|
||||
for _, p := range procs {
|
||||
//log.Printf("Sorted RSS: %+v", p)
|
||||
s = append(s, p.String())
|
||||
}
|
||||
l.Rows = s
|
||||
// x, y, w, h
|
||||
l.SetRect(0, 0, w, h)
|
||||
|
||||
ui.Render(l)
|
||||
}
|
||||
|
||||
procs, err := c.Top()
|
||||
procs, err := topOutput(c)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
draw(procs)
|
||||
|
||||
uiEvents := ui.PollEvents()
|
||||
ticker := time.NewTicker(time.Second).C
|
||||
for {
|
||||
@ -87,9 +103,13 @@ func topUI(c *client.Client) {
|
||||
switch e.ID {
|
||||
case "q", "<C-c>":
|
||||
return
|
||||
case "r", "m":
|
||||
sortMethod = "rss"
|
||||
case "c":
|
||||
sortMethod = "cpu"
|
||||
}
|
||||
case <-ticker:
|
||||
procs, err := c.Top()
|
||||
procs, err := topOutput(c)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
@ -128,3 +148,41 @@ func (s *procSorter) Swap(i, j int) {
|
||||
func (s *procSorter) Less(i, j int) bool {
|
||||
return s.by(&s.procs[i], &s.procs[j])
|
||||
}
|
||||
|
||||
// Sort Methods
|
||||
var rss = func(p1, p2 *proc.ProcessList) bool {
|
||||
// Reverse sort ( Descending )
|
||||
return p1.ResidentMemory > p2.ResidentMemory
|
||||
}
|
||||
|
||||
var cpu = func(p1, p2 *proc.ProcessList) bool {
|
||||
// Reverse sort ( Descending )
|
||||
return p1.CPUTime > p2.CPUTime
|
||||
}
|
||||
|
||||
func topOutput(c *client.Client) (output string, err error) {
|
||||
procs, err := c.Top()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch sortMethod {
|
||||
case "cpu":
|
||||
by(cpu).sort(procs)
|
||||
default:
|
||||
by(rss).sort(procs)
|
||||
}
|
||||
|
||||
s := make([]string, 0, len(procs))
|
||||
s = append(s, "PID | State | Threads | CPU Time | VirtMem | ResMem | Command | Exec/Args")
|
||||
for _, p := range procs {
|
||||
s = append(s,
|
||||
fmt.Sprintf("%6d | %1s | %4d | %8.2f | %7s | %7s | %s | %s",
|
||||
p.Pid, p.State, p.NumThreads, p.CPUTime, bytefmt.ByteSize(p.VirtualMemory), bytefmt.ByteSize(p.ResidentMemory), p.Command, p.Executable))
|
||||
}
|
||||
|
||||
output = columnize.SimpleFormat(s)
|
||||
|
||||
return
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@ -50,6 +50,7 @@ require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible
|
||||
github.com/sirupsen/logrus v1.0.6 // indirect
|
||||
github.com/spf13/afero v1.2.0 // indirect
|
||||
github.com/spf13/cobra v0.0.3
|
||||
@ -62,6 +63,7 @@ require (
|
||||
github.com/vishvananda/netlink v1.0.0
|
||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
||||
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313
|
||||
golang.org/x/text v0.3.0
|
||||
|
2
go.sum
2
go.sum
@ -118,6 +118,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045 h1:Raos9GP+3BlCBicScEQ+SjTLpYYac34fZMoeqj9McSM=
|
||||
github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/spf13/afero v1.2.0 h1:O9FblXGxoTc51M+cqr74Bm2Tmt4PvkA5iu/j8HrkNuY=
|
||||
|
Loading…
x
Reference in New Issue
Block a user