mirror of
https://github.com/bcicen/ctop.git
synced 2025-12-06 23:26:45 +08:00
Compare commits
25 Commits
v0.4.1-dep
...
v0.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a87bdce0fe | ||
|
|
2228188ebf | ||
|
|
e94a9c0cc2 | ||
|
|
e82d77ecb0 | ||
|
|
50b4181866 | ||
|
|
1285288b9e | ||
|
|
2a709577bd | ||
|
|
38599bbd19 | ||
|
|
b3cdb33efc | ||
|
|
0ac70c96eb | ||
|
|
36a5bbdfe1 | ||
|
|
3553b0af9d | ||
|
|
ca61ec712e | ||
|
|
06c4b24212 | ||
|
|
12fa716825 | ||
|
|
8327406069 | ||
|
|
2134110224 | ||
|
|
77c3d00e67 | ||
|
|
85eb5228ae | ||
|
|
3a3950e395 | ||
|
|
eaac079b15 | ||
|
|
ab1ccb3cd8 | ||
|
|
dbaebe0192 | ||
|
|
d5ef818c8d | ||
|
|
8203d0b883 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ctop
|
||||||
|
.idea
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
FROM quay.io/vektorcloud/glibc:latest
|
FROM quay.io/vektorcloud/glibc:latest
|
||||||
|
|
||||||
RUN ctop_url=$(wget -q -O - https://api.github.com/repos/bcicen/ctop/releases/latest | grep 'browser_' | cut -d\" -f4 |grep 'linux-amd64') && \
|
ARG CTOP_VERSION=0.5
|
||||||
wget -q $ctop_url -O /ctop && \
|
ENV CTOP_URL https://github.com/bcicen/ctop/releases/download/v${CTOP_VERSION}/ctop-${CTOP_VERSION}-linux-amd64
|
||||||
|
|
||||||
|
RUN echo $CTOP_URL && \
|
||||||
|
wget -q $CTOP_URL -O /ctop && \
|
||||||
chmod +x /ctop
|
chmod +x /ctop
|
||||||
|
|
||||||
ENTRYPOINT ["/ctop"]
|
ENTRYPOINT ["/ctop"]
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -17,7 +17,7 @@ Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your pla
|
|||||||
#### Linux
|
#### Linux
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/bcicen/ctop/releases/download/v0.4.1/ctop-0.4.1-linux-amd64 -O ctop
|
wget https://github.com/bcicen/ctop/releases/download/v0.5/ctop-0.5-linux-amd64 -O ctop
|
||||||
sudo mv ctop /usr/local/bin/
|
sudo mv ctop /usr/local/bin/
|
||||||
sudo chmod +x /usr/local/bin/ctop
|
sudo chmod +x /usr/local/bin/ctop
|
||||||
```
|
```
|
||||||
@@ -25,18 +25,29 @@ sudo chmod +x /usr/local/bin/ctop
|
|||||||
#### OS X
|
#### OS X
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -Lo ctop https://github.com/bcicen/ctop/releases/download/v0.4.1/ctop-0.4.1-darwin-amd64
|
curl -Lo ctop https://github.com/bcicen/ctop/releases/download/v0.5/ctop-0.5-darwin-amd64
|
||||||
sudo mv ctop /usr/local/bin/
|
sudo mv ctop /usr/local/bin/
|
||||||
sudo chmod +x /usr/local/bin/ctop
|
sudo chmod +x /usr/local/bin/ctop
|
||||||
```
|
```
|
||||||
|
|
||||||
or run via Docker:
|
or run via Docker:
|
||||||
```bash
|
```bash
|
||||||
docker run -ti -v /var/run/docker.sock:/var/run/docker.sock quay.io/vektorlab/ctop:latest
|
docker run -ti --name ctop --rm -v /var/run/docker.sock:/var/run/docker.sock quay.io/vektorlab/ctop:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
`ctop` is also available for Arch in the [AUR](https://aur.archlinux.org/packages/ctop/)
|
`ctop` is also available for Arch in the [AUR](https://aur.archlinux.org/packages/ctop/)
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To build `ctop` from source, ensure you have a recent version of [glide](http://glide.sh/) installed and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/bcicen/ctop.git $GOPATH/src/github.com/bcicen/ctop && \
|
||||||
|
cd $GOPATH/src/github.com/bcicen/ctop && \
|
||||||
|
glide install && \
|
||||||
|
go build
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
`ctop` requires no arguments and will configure itself using the `DOCKER_HOST` environment variable
|
`ctop` requires no arguments and will configure itself using the `DOCKER_HOST` environment variable
|
||||||
@@ -45,12 +56,24 @@ export DOCKER_HOST=tcp://127.0.0.1:4243
|
|||||||
ctop
|
ctop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
Option | Description
|
||||||
|
--- | ---
|
||||||
|
-a | show active containers only
|
||||||
|
-f <string> | set an initial filter string
|
||||||
|
-h | display help dialog
|
||||||
|
-i | invert default colors
|
||||||
|
-r | reverse container sort order
|
||||||
|
-s | select initial container sort field
|
||||||
|
-v | output version information and exit
|
||||||
|
|
||||||
### Keybindings
|
### Keybindings
|
||||||
|
|
||||||
Key | Action
|
Key | Action
|
||||||
--- | ---
|
--- | ---
|
||||||
a | Toggle display of all (running and non-running) containers
|
a | Toggle display of all (running and non-running) containers
|
||||||
f | Filter displayed containers
|
f | Filter displayed containers (`esc` to clear when open)
|
||||||
H | Toggle ctop header
|
H | Toggle ctop header
|
||||||
h | Open help dialog
|
h | Open help dialog
|
||||||
s | Select container sort field
|
s | Select container sort field
|
||||||
|
|||||||
15
circle.yml
15
circle.yml
@@ -1,24 +1,23 @@
|
|||||||
machine:
|
machine:
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
environment:
|
||||||
|
IMAGE_NAME: quay.io/vektorlab/ctop
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
- docker info
|
- docker info
|
||||||
- |
|
- docker build --build-arg CTOP_VERSION=$(cat VERSION) -t ctop .
|
||||||
if [[ "$CIRCLE_BRANCH" == "master" ]]; then
|
|
||||||
docker build -t quay.io/vektorlab/ctop:latest .
|
|
||||||
else
|
|
||||||
docker build -t quay.io/vektorlab/ctop:${CIRCLE_BRANCH} .
|
|
||||||
fi
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
- docker run -t --entrypoint /bin/sh quay.io/vektorlab/ctop:latest -v
|
- docker run -ti ctop -v
|
||||||
|
|
||||||
deployment:
|
deployment:
|
||||||
hub:
|
hub:
|
||||||
branch: master
|
branch: master
|
||||||
commands:
|
commands:
|
||||||
|
- docker tag ctop ${IMAGE_NAME}:latest
|
||||||
|
- docker tag ctop ${IMAGE_NAME}:$(cat VERSION)
|
||||||
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS quay.io
|
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS quay.io
|
||||||
- docker push quay.io/vektorlab/ctop:latest
|
- docker push ${IMAGE_NAME}
|
||||||
|
|||||||
17
colors.go
17
colors.go
@@ -1,6 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import ui "github.com/gizak/termui"
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
ui "github.com/gizak/termui"
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Valid colors:
|
Valid colors:
|
||||||
@@ -38,6 +42,17 @@ var ColorMap = map[string]ui.Attribute{
|
|||||||
"mbarchart.text.fg": ui.ColorWhite,
|
"mbarchart.text.fg": ui.ColorWhite,
|
||||||
"par.text.fg": ui.ColorWhite,
|
"par.text.fg": ui.ColorWhite,
|
||||||
"par.text.bg": ui.ColorDefault,
|
"par.text.bg": ui.ColorDefault,
|
||||||
|
"par.text.hi": ui.ColorBlack,
|
||||||
"sparkline.line.fg": ui.ColorGreen,
|
"sparkline.line.fg": ui.ColorGreen,
|
||||||
"sparkline.title.fg": ui.ColorWhite,
|
"sparkline.title.fg": ui.ColorWhite,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InvertColorMap() {
|
||||||
|
re := regexp.MustCompile(".*.fg")
|
||||||
|
for k, _ := range ColorMap {
|
||||||
|
if re.FindAllString(k, 1) != nil {
|
||||||
|
ColorMap[k] = ui.ColorBlack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorMap["par.text.hi"] = ui.ColorWhite
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var header = NewCompactHeader()
|
var header *CompactHeader
|
||||||
|
|
||||||
type CompactGrid struct {
|
type CompactGrid struct {
|
||||||
ui.GridBufferer
|
ui.GridBufferer
|
||||||
@@ -16,6 +16,7 @@ type CompactGrid struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewCompactGrid() *CompactGrid {
|
func NewCompactGrid() *CompactGrid {
|
||||||
|
header = NewCompactHeader() // init column header
|
||||||
return &CompactGrid{}
|
return &CompactGrid{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type CompactHeader struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewCompactHeader() *CompactHeader {
|
func NewCompactHeader() *CompactHeader {
|
||||||
fields := []string{"", "NAME", "CID", "CPU", "MEM", "NET RX/TX"}
|
fields := []string{"", "NAME", "CID", "CPU", "MEM", "NET RX/TX", "IO R/W", "PIDS"}
|
||||||
ch := &CompactHeader{}
|
ch := &CompactHeader{}
|
||||||
ch.Height = 2
|
ch.Height = 2
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
@@ -27,13 +27,13 @@ func (ch *CompactHeader) GetHeight() int {
|
|||||||
|
|
||||||
func (ch *CompactHeader) SetWidth(w int) {
|
func (ch *CompactHeader) SetWidth(w int) {
|
||||||
x := ch.X
|
x := ch.X
|
||||||
autoWidth := calcWidth(w, 5)
|
autoWidth := calcWidth(w)
|
||||||
for n, col := range ch.pars {
|
for n, col := range ch.pars {
|
||||||
// set status column to static width
|
// set column to static width
|
||||||
if n == 0 {
|
if colWidths[n] != 0 {
|
||||||
col.SetX(x)
|
col.SetX(x)
|
||||||
col.SetWidth(statusWidth)
|
col.SetWidth(colWidths[n])
|
||||||
x += statusWidth
|
x += colWidths[n]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
col.SetX(x)
|
col.SetX(x)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ type Compact struct {
|
|||||||
Cpu *GaugeCol
|
Cpu *GaugeCol
|
||||||
Memory *GaugeCol
|
Memory *GaugeCol
|
||||||
Net *TextCol
|
Net *TextCol
|
||||||
|
IO *TextCol
|
||||||
|
Pids *TextCol
|
||||||
X, Y int
|
X, Y int
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
@@ -32,6 +34,8 @@ func NewCompact(id string) *Compact {
|
|||||||
Cpu: NewGaugeCol(),
|
Cpu: NewGaugeCol(),
|
||||||
Memory: NewGaugeCol(),
|
Memory: NewGaugeCol(),
|
||||||
Net: NewTextCol("-"),
|
Net: NewTextCol("-"),
|
||||||
|
IO: NewTextCol("-"),
|
||||||
|
Pids: NewTextCol("-"),
|
||||||
X: 1,
|
X: 1,
|
||||||
Height: 1,
|
Height: 1,
|
||||||
}
|
}
|
||||||
@@ -59,6 +63,8 @@ func (row *Compact) SetMetrics(m metrics.Metrics) {
|
|||||||
row.SetCPU(m.CPUUtil)
|
row.SetCPU(m.CPUUtil)
|
||||||
row.SetNet(m.NetRx, m.NetTx)
|
row.SetNet(m.NetRx, m.NetTx)
|
||||||
row.SetMem(m.MemUsage, m.MemLimit, m.MemPercent)
|
row.SetMem(m.MemUsage, m.MemLimit, m.MemPercent)
|
||||||
|
row.SetIO(m.IOBytesRead, m.IOBytesWrite)
|
||||||
|
row.SetPids(m.Pids)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set gauges, counters to default unread values
|
// Set gauges, counters to default unread values
|
||||||
@@ -66,6 +72,8 @@ func (row *Compact) Reset() {
|
|||||||
row.Cpu.Reset()
|
row.Cpu.Reset()
|
||||||
row.Memory.Reset()
|
row.Memory.Reset()
|
||||||
row.Net.Reset()
|
row.Net.Reset()
|
||||||
|
row.IO.Reset()
|
||||||
|
row.Pids.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (row *Compact) GetHeight() int {
|
func (row *Compact) GetHeight() int {
|
||||||
@@ -91,13 +99,12 @@ func (row *Compact) SetWidth(width int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
x := row.X
|
x := row.X
|
||||||
autoWidth := calcWidth(width, 5)
|
autoWidth := calcWidth(width)
|
||||||
for n, col := range row.all() {
|
for n, col := range row.all() {
|
||||||
// set status column to static width
|
if colWidths[n] != 0 {
|
||||||
if n == 0 {
|
|
||||||
col.SetX(x)
|
col.SetX(x)
|
||||||
col.SetWidth(statusWidth)
|
col.SetWidth(colWidths[n])
|
||||||
x += statusWidth
|
x += colWidths[n]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
col.SetX(x)
|
col.SetX(x)
|
||||||
@@ -116,7 +123,8 @@ func (row *Compact) Buffer() ui.Buffer {
|
|||||||
buf.Merge(row.Cpu.Buffer())
|
buf.Merge(row.Cpu.Buffer())
|
||||||
buf.Merge(row.Memory.Buffer())
|
buf.Merge(row.Memory.Buffer())
|
||||||
buf.Merge(row.Net.Buffer())
|
buf.Merge(row.Net.Buffer())
|
||||||
|
buf.Merge(row.IO.Buffer())
|
||||||
|
buf.Merge(row.Pids.Buffer())
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,5 +136,7 @@ func (row *Compact) all() []ui.GridBufferer {
|
|||||||
row.Cpu,
|
row.Cpu,
|
||||||
row.Memory,
|
row.Memory,
|
||||||
row.Net,
|
row.Net,
|
||||||
|
row.IO,
|
||||||
|
row.Pids,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ func (row *Compact) SetNet(rx int64, tx int64) {
|
|||||||
row.Net.Set(label)
|
row.Net.Set(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (row *Compact) SetIO(read int64, write int64) {
|
||||||
|
label := fmt.Sprintf("%s / %s", cwidgets.ByteFormat(read), cwidgets.ByteFormat(write))
|
||||||
|
row.IO.Set(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row *Compact) SetPids(val int) {
|
||||||
|
label := fmt.Sprintf("%s", strconv.Itoa(val))
|
||||||
|
row.Pids.Set(label)
|
||||||
|
}
|
||||||
|
|
||||||
func (row *Compact) SetCPU(val int) {
|
func (row *Compact) SetCPU(val int) {
|
||||||
row.Cpu.BarColor = colorScale(val)
|
row.Cpu.BarColor = colorScale(val)
|
||||||
row.Cpu.Label = fmt.Sprintf("%s%%", strconv.Itoa(val))
|
row.Cpu.Label = fmt.Sprintf("%s%%", strconv.Itoa(val))
|
||||||
@@ -20,6 +30,9 @@ func (row *Compact) SetCPU(val int) {
|
|||||||
val = 5
|
val = 5
|
||||||
row.Cpu.BarColor = ui.ThemeAttr("gauge.bar.bg")
|
row.Cpu.BarColor = ui.ThemeAttr("gauge.bar.bg")
|
||||||
}
|
}
|
||||||
|
if val > 100 {
|
||||||
|
val = 100
|
||||||
|
}
|
||||||
row.Cpu.Percent = val
|
row.Cpu.Percent = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func NewTextCol(s string) *TextCol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *TextCol) Highlight() {
|
func (w *TextCol) Highlight() {
|
||||||
w.TextFgColor = ui.ColorBlack
|
w.TextFgColor = ui.ThemeAttr("par.text.hi")
|
||||||
w.TextBgColor = ui.ThemeAttr("par.text.fg")
|
w.TextBgColor = ui.ThemeAttr("par.text.fg")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,29 @@ import (
|
|||||||
|
|
||||||
const colSpacing = 1
|
const colSpacing = 1
|
||||||
|
|
||||||
// Calculate per-column width, given total width and number of items
|
// per-column width. 0 == auto width
|
||||||
func calcWidth(width, items int) int {
|
var colWidths = []int{
|
||||||
spacing := colSpacing * items
|
3, // status
|
||||||
return (width - statusWidth - spacing) / items
|
0, // name
|
||||||
|
0, // cid
|
||||||
|
0, // cpu
|
||||||
|
0, // memory
|
||||||
|
0, // net
|
||||||
|
0, // io
|
||||||
|
4, // pids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate per-column width, given total width
|
||||||
|
func calcWidth(width int) int {
|
||||||
|
spacing := colSpacing * len(colWidths)
|
||||||
|
var staticCols int
|
||||||
|
for _, w := range colWidths {
|
||||||
|
width -= w
|
||||||
|
if w == 0 {
|
||||||
|
staticCols += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (width - spacing) / staticCols
|
||||||
}
|
}
|
||||||
|
|
||||||
func centerParText(p *ui.Par) {
|
func centerParText(p *ui.Par) {
|
||||||
|
|||||||
51
cwidgets/expanded/io.go
Normal file
51
cwidgets/expanded/io.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package expanded
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bcicen/ctop/cwidgets"
|
||||||
|
ui "github.com/gizak/termui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IO struct {
|
||||||
|
*ui.Sparklines
|
||||||
|
readHist *DiffHist
|
||||||
|
writeHist *DiffHist
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIO() *IO {
|
||||||
|
io := &IO{ui.NewSparklines(), NewDiffHist(60), NewDiffHist(60)}
|
||||||
|
io.BorderLabel = "IO"
|
||||||
|
io.Height = 6
|
||||||
|
io.Width = colWidth[0]
|
||||||
|
io.X = 0
|
||||||
|
io.Y = 24
|
||||||
|
|
||||||
|
read := ui.NewSparkline()
|
||||||
|
read.Title = "READ"
|
||||||
|
read.Height = 1
|
||||||
|
read.Data = io.readHist.Data
|
||||||
|
read.LineColor = ui.ColorGreen
|
||||||
|
|
||||||
|
write := ui.NewSparkline()
|
||||||
|
write.Title = "WRITE"
|
||||||
|
write.Height = 1
|
||||||
|
write.Data = io.writeHist.Data
|
||||||
|
write.LineColor = ui.ColorYellow
|
||||||
|
|
||||||
|
io.Lines = []ui.Sparkline{read, write}
|
||||||
|
return io
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *IO) Update(read int64, write int64) {
|
||||||
|
var rate string
|
||||||
|
|
||||||
|
w.readHist.Append(int(read))
|
||||||
|
rate = strings.ToLower(cwidgets.ByteFormatInt(w.readHist.Val))
|
||||||
|
w.Lines[0].Title = fmt.Sprintf("read [%s/s]", rate)
|
||||||
|
|
||||||
|
w.writeHist.Append(int(write))
|
||||||
|
rate = strings.ToLower(cwidgets.ByteFormatInt(w.writeHist.Val))
|
||||||
|
w.Lines[1].Title = fmt.Sprintf("write [%s/s]", rate)
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ type Expanded struct {
|
|||||||
Net *Net
|
Net *Net
|
||||||
Cpu *Cpu
|
Cpu *Cpu
|
||||||
Mem *Mem
|
Mem *Mem
|
||||||
|
IO *IO
|
||||||
|
X, Y int
|
||||||
Width int
|
Width int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,30 +31,59 @@ func NewExpanded(id string) *Expanded {
|
|||||||
Net: NewNet(),
|
Net: NewNet(),
|
||||||
Cpu: NewCpu(),
|
Cpu: NewCpu(),
|
||||||
Mem: NewMem(),
|
Mem: NewMem(),
|
||||||
|
IO: NewIO(),
|
||||||
Width: ui.TermWidth(),
|
Width: ui.TermWidth(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Expanded) SetWidth(w int) {
|
func (e *Expanded) Up() {
|
||||||
e.Width = w
|
if e.Y < 0 {
|
||||||
|
e.Y++
|
||||||
|
e.Align()
|
||||||
|
ui.Render(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Expanded) SetMeta(k, v string) {
|
func (e *Expanded) Down() {
|
||||||
e.Info.Set(k, v)
|
if e.Y > (ui.TermHeight() - e.GetHeight()) {
|
||||||
|
e.Y--
|
||||||
|
e.Align()
|
||||||
|
ui.Render(e)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Expanded) SetWidth(w int) { e.Width = w }
|
||||||
|
func (e *Expanded) SetMeta(k, v string) { e.Info.Set(k, v) }
|
||||||
|
|
||||||
func (e *Expanded) SetMetrics(m metrics.Metrics) {
|
func (e *Expanded) SetMetrics(m metrics.Metrics) {
|
||||||
e.Cpu.Update(m.CPUUtil)
|
e.Cpu.Update(m.CPUUtil)
|
||||||
e.Net.Update(m.NetRx, m.NetTx)
|
e.Net.Update(m.NetRx, m.NetTx)
|
||||||
e.Mem.Update(int(m.MemUsage), int(m.MemLimit))
|
e.Mem.Update(int(m.MemUsage), int(m.MemLimit))
|
||||||
|
e.IO.Update(m.IOBytesRead, m.IOBytesWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return total column height
|
||||||
|
func (e *Expanded) GetHeight() (h int) {
|
||||||
|
h += e.Info.Height
|
||||||
|
h += e.Net.Height
|
||||||
|
h += e.Cpu.Height
|
||||||
|
h += e.Mem.Height
|
||||||
|
h += e.IO.Height
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Expanded) Align() {
|
func (e *Expanded) Align() {
|
||||||
y := 0
|
// reset offset if needed
|
||||||
|
if e.GetHeight() <= ui.TermHeight() {
|
||||||
|
e.Y = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
y := e.Y
|
||||||
for _, i := range e.all() {
|
for _, i := range e.all() {
|
||||||
i.SetY(y)
|
i.SetY(y)
|
||||||
y += i.GetHeight()
|
y += i.GetHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Width > colWidth[0] {
|
if e.Width > colWidth[0] {
|
||||||
colWidth[1] = e.Width - (colWidth[0] + 1)
|
colWidth[1] = e.Width - (colWidth[0] + 1)
|
||||||
}
|
}
|
||||||
@@ -74,6 +105,7 @@ func (e *Expanded) Buffer() ui.Buffer {
|
|||||||
buf.Merge(e.Cpu.Buffer())
|
buf.Merge(e.Cpu.Buffer())
|
||||||
buf.Merge(e.Mem.Buffer())
|
buf.Merge(e.Mem.Buffer())
|
||||||
buf.Merge(e.Net.Buffer())
|
buf.Merge(e.Net.Buffer())
|
||||||
|
buf.Merge(e.IO.Buffer())
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +115,7 @@ func (e *Expanded) all() []ui.GridBufferer {
|
|||||||
e.Cpu,
|
e.Cpu,
|
||||||
e.Mem,
|
e.Mem,
|
||||||
e.Net,
|
e.Net,
|
||||||
|
e.IO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
85
glide.lock
generated
Normal file
85
glide.lock
generated
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
hash: c13011881e895378f374b68596a59a0ea6def372b4f5239b2d8aa342eaa46a4b
|
||||||
|
updated: 2017-03-12T09:53:35.212073637+07:00
|
||||||
|
imports:
|
||||||
|
- name: github.com/Azure/go-ansiterm
|
||||||
|
version: fa152c58bc15761d0200cb75fe958b89a9d4888e
|
||||||
|
subpackages:
|
||||||
|
- winterm
|
||||||
|
- name: github.com/docker/docker
|
||||||
|
version: ce07fb6b0f1b8765b92022e45f96bd4349812e06
|
||||||
|
subpackages:
|
||||||
|
- api/types
|
||||||
|
- api/types/blkiodev
|
||||||
|
- api/types/container
|
||||||
|
- api/types/filters
|
||||||
|
- api/types/mount
|
||||||
|
- api/types/network
|
||||||
|
- api/types/registry
|
||||||
|
- api/types/strslice
|
||||||
|
- api/types/swarm
|
||||||
|
- api/types/versions
|
||||||
|
- opts
|
||||||
|
- pkg/archive
|
||||||
|
- pkg/fileutils
|
||||||
|
- pkg/homedir
|
||||||
|
- pkg/idtools
|
||||||
|
- pkg/ioutils
|
||||||
|
- pkg/jsonlog
|
||||||
|
- pkg/jsonmessage
|
||||||
|
- pkg/longpath
|
||||||
|
- pkg/pools
|
||||||
|
- pkg/promise
|
||||||
|
- pkg/stdcopy
|
||||||
|
- pkg/system
|
||||||
|
- pkg/term
|
||||||
|
- pkg/term/windows
|
||||||
|
- name: github.com/docker/go-connections
|
||||||
|
version: a2afab9802043837035592f1c24827fb70766de9
|
||||||
|
subpackages:
|
||||||
|
- nat
|
||||||
|
- name: github.com/docker/go-units
|
||||||
|
version: 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
||||||
|
- name: github.com/fsouza/go-dockerclient
|
||||||
|
version: 318513eb1ab27495afbc67f671ba1080513d8aa0
|
||||||
|
- name: github.com/gizak/termui
|
||||||
|
version: ea10e6ccee219e572ffad0ac1909f1a17f6db7d6
|
||||||
|
repo: https://github.com/bcicen/termui
|
||||||
|
vcs: git
|
||||||
|
- name: github.com/hashicorp/go-cleanhttp
|
||||||
|
version: 3573b8b52aa7b37b9358d966a898feb387f62437
|
||||||
|
- name: github.com/jgautheron/codename-generator
|
||||||
|
version: 16d037c7cc3c9b552fe4af9828b7338d752dbaf9
|
||||||
|
- name: github.com/maruel/panicparse
|
||||||
|
version: 25bcac0d793cf4109483505a0d66e066a3a90a80
|
||||||
|
subpackages:
|
||||||
|
- stack
|
||||||
|
- name: github.com/mattn/go-runewidth
|
||||||
|
version: 14207d285c6c197daabb5c9793d63e7af9ab2d50
|
||||||
|
- name: github.com/Microsoft/go-winio
|
||||||
|
version: fff283ad5116362ca252298cfc9b95828956d85d
|
||||||
|
- name: github.com/mitchellh/go-wordwrap
|
||||||
|
version: ad45545899c7b13c020ea92b2072220eefad42b8
|
||||||
|
- name: github.com/nsf/termbox-go
|
||||||
|
version: 91bae1bb5fa9ee504905ecbe7043fa30e92feaa3
|
||||||
|
- name: github.com/nu7hatch/gouuid
|
||||||
|
version: 179d4d0c4d8d407a32af483c2354df1d2c91e6c3
|
||||||
|
- name: github.com/op/go-logging
|
||||||
|
version: b2cb9fa56473e98db8caba80237377e83fe44db5
|
||||||
|
- name: github.com/opencontainers/runc
|
||||||
|
version: 31980a53ae7887b2c8f8715d13c3eb486c27b6cf
|
||||||
|
subpackages:
|
||||||
|
- libcontainer/system
|
||||||
|
- libcontainer/user
|
||||||
|
- name: github.com/Sirupsen/logrus
|
||||||
|
version: 1deb2db2a6fff8a35532079061b903c3a25eed52
|
||||||
|
- name: golang.org/x/net
|
||||||
|
version: a6577fac2d73be281a500b310739095313165611
|
||||||
|
subpackages:
|
||||||
|
- context
|
||||||
|
- context/ctxhttp
|
||||||
|
- name: golang.org/x/sys
|
||||||
|
version: 99f16d856c9836c42d24e7ab64ea72916925fa97
|
||||||
|
subpackages:
|
||||||
|
- unix
|
||||||
|
- windows
|
||||||
|
testImports: []
|
||||||
11
glide.yaml
Normal file
11
glide.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package: github.com/bcicen/ctop
|
||||||
|
import:
|
||||||
|
- package: github.com/fsouza/go-dockerclient
|
||||||
|
- package: github.com/gizak/termui
|
||||||
|
version: barchart-numfmt
|
||||||
|
repo: https://github.com/bcicen/termui
|
||||||
|
vcs: git
|
||||||
|
- package: github.com/jgautheron/codename-generator
|
||||||
|
- package: github.com/nu7hatch/gouuid
|
||||||
|
- package: github.com/op/go-logging
|
||||||
|
version: ^1.0.0
|
||||||
32
grid.go
32
grid.go
@@ -44,19 +44,19 @@ func ExpandView(c *Container) {
|
|||||||
|
|
||||||
ex.Align()
|
ex.Align()
|
||||||
ui.Render(ex)
|
ui.Render(ex)
|
||||||
ui.Handle("/timer/1s", func(ui.Event) {
|
|
||||||
ui.Render(ex)
|
HandleKeys("up", ex.Up)
|
||||||
})
|
HandleKeys("down", ex.Down)
|
||||||
|
ui.Handle("/sys/kbd/", func(ui.Event) { ui.StopLoop() })
|
||||||
|
|
||||||
|
ui.Handle("/timer/1s", func(ui.Event) { ui.Render(ex) })
|
||||||
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
||||||
ex.SetWidth(ui.TermWidth())
|
ex.SetWidth(ui.TermWidth())
|
||||||
ex.Align()
|
ex.Align()
|
||||||
log.Infof("resize: width=%v max-rows=%v", ex.Width, cGrid.MaxRows())
|
log.Infof("resize: width=%v max-rows=%v", ex.Width, cGrid.MaxRows())
|
||||||
})
|
})
|
||||||
ui.Handle("/sys/kbd/", func(ui.Event) {
|
|
||||||
ui.StopLoop()
|
|
||||||
})
|
|
||||||
ui.Loop()
|
|
||||||
|
|
||||||
|
ui.Loop()
|
||||||
c.SetUpdater(c.Widgets)
|
c.SetUpdater(c.Widgets)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,16 +77,18 @@ func Display() bool {
|
|||||||
cursor.RefreshContainers()
|
cursor.RefreshContainers()
|
||||||
RedrawRows(true)
|
RedrawRows(true)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/<up>", func(ui.Event) { cursor.Up() })
|
HandleKeys("up", cursor.Up)
|
||||||
ui.Handle("/sys/kbd/<down>", func(ui.Event) { cursor.Down() })
|
HandleKeys("down", cursor.Down)
|
||||||
|
HandleKeys("exit", ui.StopLoop)
|
||||||
|
HandleKeys("help", func() {
|
||||||
|
menu = HelpMenu
|
||||||
|
ui.StopLoop()
|
||||||
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
||||||
expand = true
|
expand = true
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) { ui.StopLoop() })
|
|
||||||
ui.Handle("/sys/kbd/C-c", func(ui.Event) { ui.StopLoop() })
|
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/a", func(ui.Event) {
|
ui.Handle("/sys/kbd/a", func(ui.Event) {
|
||||||
config.Toggle("allContainers")
|
config.Toggle("allContainers")
|
||||||
RefreshDisplay()
|
RefreshDisplay()
|
||||||
@@ -98,10 +100,6 @@ func Display() bool {
|
|||||||
menu = FilterMenu
|
menu = FilterMenu
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
ui.Handle("/sys/kbd/h", func(ui.Event) {
|
|
||||||
menu = HelpMenu
|
|
||||||
ui.StopLoop()
|
|
||||||
})
|
|
||||||
ui.Handle("/sys/kbd/H", func(ui.Event) {
|
ui.Handle("/sys/kbd/H", func(ui.Event) {
|
||||||
config.Toggle("enableHeader")
|
config.Toggle("enableHeader")
|
||||||
RedrawRows(true)
|
RedrawRows(true)
|
||||||
|
|||||||
32
keys.go
Normal file
32
keys.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
ui "github.com/gizak/termui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Common action keybindings
|
||||||
|
var keyMap = map[string][]string{
|
||||||
|
"up": []string{
|
||||||
|
"/sys/kbd/<up>",
|
||||||
|
"/sys/kbd/k",
|
||||||
|
},
|
||||||
|
"down": []string{
|
||||||
|
"/sys/kbd/<down>",
|
||||||
|
"/sys/kbd/j",
|
||||||
|
},
|
||||||
|
"exit": []string{
|
||||||
|
"/sys/kbd/q",
|
||||||
|
"/sys/kbd/C-c",
|
||||||
|
},
|
||||||
|
"help": []string{
|
||||||
|
"/sys/kbd/h",
|
||||||
|
"/sys/kbd/?",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a common handler function to all given keys
|
||||||
|
func HandleKeys(i string, f func()) {
|
||||||
|
for _, k := range keyMap[i] {
|
||||||
|
ui.Handle(k, func(ui.Event) { f() })
|
||||||
|
}
|
||||||
|
}
|
||||||
79
main.go
79
main.go
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -22,25 +23,65 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
readArgs()
|
|
||||||
defer panicExit()
|
defer panicExit()
|
||||||
|
|
||||||
// init ui
|
|
||||||
ui.ColorMap = ColorMap // override default colormap
|
|
||||||
if err := ui.Init(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer ui.Close()
|
|
||||||
|
|
||||||
// init global config
|
// init global config
|
||||||
config.Init()
|
config.Init()
|
||||||
|
|
||||||
|
// parse command line arguments
|
||||||
|
var versionFlag = flag.Bool("v", false, "output version information and exit")
|
||||||
|
var helpFlag = flag.Bool("h", false, "display this help dialog")
|
||||||
|
var filterFlag = flag.String("f", "", "filter containers")
|
||||||
|
var activeOnlyFlag = flag.Bool("a", false, "show active containers only")
|
||||||
|
var sortFieldFlag = flag.String("s", "", "select container sort field")
|
||||||
|
var reverseSortFlag = flag.Bool("r", false, "reverse container sort order")
|
||||||
|
var invertFlag = flag.Bool("i", false, "invert default colors")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *versionFlag {
|
||||||
|
printVersion()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *helpFlag {
|
||||||
|
printHelp()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// override default config values with command line flags
|
||||||
|
if *filterFlag != "" {
|
||||||
|
config.Update("filterStr", *filterFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *activeOnlyFlag {
|
||||||
|
config.Toggle("allContainers")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *sortFieldFlag != "" {
|
||||||
|
validSort(*sortFieldFlag)
|
||||||
|
config.Update("sortField", *sortFieldFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *reverseSortFlag {
|
||||||
|
config.Toggle("sortReversed")
|
||||||
|
}
|
||||||
|
|
||||||
// init logger
|
// init logger
|
||||||
log = logging.Init()
|
log = logging.Init()
|
||||||
if config.GetSwitchVal("loggingEnabled") {
|
if config.GetSwitchVal("loggingEnabled") {
|
||||||
logging.StartServer()
|
logging.StartServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init ui
|
||||||
|
if *invertFlag {
|
||||||
|
InvertColorMap()
|
||||||
|
}
|
||||||
|
ui.ColorMap = ColorMap // override default colormap
|
||||||
|
if err := ui.Init(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer ui.Close()
|
||||||
|
|
||||||
// init grid, cursor, header
|
// init grid, cursor, header
|
||||||
cursor = NewGridCursor()
|
cursor = NewGridCursor()
|
||||||
cGrid = compact.NewCompactGrid()
|
cGrid = compact.NewCompactGrid()
|
||||||
@@ -56,24 +97,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readArgs() {
|
// ensure a given sort field is valid
|
||||||
if len(os.Args) < 2 {
|
func validSort(s string) {
|
||||||
return
|
if _, ok := Sorters[s]; !ok {
|
||||||
}
|
fmt.Printf("invalid sort field: %s\n", s)
|
||||||
for _, arg := range os.Args[1:] {
|
|
||||||
switch arg {
|
|
||||||
case "-v", "version":
|
|
||||||
printVersion()
|
|
||||||
os.Exit(0)
|
|
||||||
case "-h", "help":
|
|
||||||
printHelp()
|
|
||||||
os.Exit(0)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid option or argument: \"%s\"\n", arg)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func panicExit() {
|
func panicExit() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -88,12 +118,11 @@ var helpMsg = `ctop - container metric viewer
|
|||||||
usage: ctop [options]
|
usage: ctop [options]
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h display this help dialog
|
|
||||||
-v output version information and exit
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func printHelp() {
|
func printHelp() {
|
||||||
fmt.Println(helpMsg)
|
fmt.Println(helpMsg)
|
||||||
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
|
|||||||
13
menus.go
13
menus.go
@@ -39,6 +39,7 @@ func FilterMenu() {
|
|||||||
i := widgets.NewInput()
|
i := widgets.NewInput()
|
||||||
i.BorderLabel = "Filter"
|
i.BorderLabel = "Filter"
|
||||||
i.SetY(ui.TermHeight() - i.Height)
|
i.SetY(ui.TermHeight() - i.Height)
|
||||||
|
i.Data = config.GetVal("filterStr")
|
||||||
ui.Render(i)
|
ui.Render(i)
|
||||||
|
|
||||||
// refresh container rows on input
|
// refresh container rows on input
|
||||||
@@ -52,6 +53,10 @@ func FilterMenu() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
i.InputHandlers()
|
i.InputHandlers()
|
||||||
|
ui.Handle("/sys/kbd/<escape>", func(ui.Event) {
|
||||||
|
config.Update("filterStr", "")
|
||||||
|
ui.StopLoop()
|
||||||
|
})
|
||||||
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
||||||
config.Update("filterStr", i.Data)
|
config.Update("filterStr", i.Data)
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
@@ -76,11 +81,15 @@ func SortMenu() {
|
|||||||
// set cursor position to current sort field
|
// set cursor position to current sort field
|
||||||
m.SetCursor(config.GetVal("sortField"))
|
m.SetCursor(config.GetVal("sortField"))
|
||||||
|
|
||||||
ui.Render(m)
|
HandleKeys("up", m.Up)
|
||||||
m.NavigationHandlers()
|
HandleKeys("down", m.Down)
|
||||||
|
HandleKeys("exit", ui.StopLoop)
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
||||||
config.Update("sortField", m.SelectedItem().Val)
|
config.Update("sortField", m.SelectedItem().Val)
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ui.Render(m)
|
||||||
ui.Loop()
|
ui.Loop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ func (c *Docker) Start() {
|
|||||||
c.ReadCPU(s)
|
c.ReadCPU(s)
|
||||||
c.ReadMem(s)
|
c.ReadMem(s)
|
||||||
c.ReadNet(s)
|
c.ReadNet(s)
|
||||||
|
c.ReadIO(s)
|
||||||
c.stream <- c.Metrics
|
c.stream <- c.Metrics
|
||||||
}
|
}
|
||||||
log.Infof("collector stopped for container: %s", c.id)
|
log.Infof("collector stopped for container: %s", c.id)
|
||||||
@@ -79,6 +80,7 @@ func (c *Docker) ReadCPU(stats *api.Stats) {
|
|||||||
c.CPUUtil = round((cpudiff / syscpudiff * 100) * ncpus)
|
c.CPUUtil = round((cpudiff / syscpudiff * 100) * ncpus)
|
||||||
c.lastCpu = total
|
c.lastCpu = total
|
||||||
c.lastSysCpu = system
|
c.lastSysCpu = system
|
||||||
|
c.Pids = int(stats.PidsStats.Current)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Docker) ReadMem(stats *api.Stats) {
|
func (c *Docker) ReadMem(stats *api.Stats) {
|
||||||
@@ -95,3 +97,16 @@ func (c *Docker) ReadNet(stats *api.Stats) {
|
|||||||
}
|
}
|
||||||
c.NetRx, c.NetTx = rx, tx
|
c.NetRx, c.NetTx = rx, tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Docker) ReadIO(stats *api.Stats) {
|
||||||
|
var read, write int64
|
||||||
|
for _, blk := range stats.BlkioStats.IOServiceBytesRecursive {
|
||||||
|
if blk.Op == "Read" {
|
||||||
|
read = int64(blk.Value)
|
||||||
|
}
|
||||||
|
if blk.Op == "Write" {
|
||||||
|
write = int64(blk.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.IOBytesRead, c.IOBytesWrite = read, write
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ type Metrics struct {
|
|||||||
MemLimit int64
|
MemLimit int64
|
||||||
MemPercent int
|
MemPercent int
|
||||||
MemUsage int64
|
MemUsage int64
|
||||||
|
IOBytesRead int64
|
||||||
|
IOBytesWrite int64
|
||||||
|
Pids int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMetrics() Metrics {
|
func NewMetrics() Metrics {
|
||||||
@@ -24,6 +27,9 @@ func NewMetrics() Metrics {
|
|||||||
NetRx: -1,
|
NetRx: -1,
|
||||||
MemUsage: -1,
|
MemUsage: -1,
|
||||||
MemPercent: -1,
|
MemPercent: -1,
|
||||||
|
IOBytesRead: -1,
|
||||||
|
IOBytesWrite: -1,
|
||||||
|
Pids: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
sort.go
18
sort.go
@@ -53,6 +53,22 @@ var Sorters = map[string]sortMethod{
|
|||||||
}
|
}
|
||||||
return sum1 > sum2
|
return sum1 > sum2
|
||||||
},
|
},
|
||||||
|
"pids": func(c1, c2 *Container) bool {
|
||||||
|
// Use secondary sort method if equal values
|
||||||
|
if c1.Pids == c2.Pids {
|
||||||
|
return nameSorter(c1, c2)
|
||||||
|
}
|
||||||
|
return c1.Pids > c2.Pids
|
||||||
|
},
|
||||||
|
"io": func(c1, c2 *Container) bool {
|
||||||
|
sum1 := sumIO(c1)
|
||||||
|
sum2 := sumIO(c2)
|
||||||
|
// Use secondary sort method if equal values
|
||||||
|
if sum1 == sum2 {
|
||||||
|
return nameSorter(c1, c2)
|
||||||
|
}
|
||||||
|
return sum1 > sum2
|
||||||
|
},
|
||||||
"state": func(c1, c2 *Container) bool {
|
"state": func(c1, c2 *Container) bool {
|
||||||
// Use secondary sort method if equal values
|
// Use secondary sort method if equal values
|
||||||
c1state := c1.GetMeta("state")
|
c1state := c1.GetMeta("state")
|
||||||
@@ -101,3 +117,5 @@ func (a Containers) Filter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sumNet(c *Container) int64 { return c.NetRx + c.NetTx }
|
func sumNet(c *Container) int64 { return c.NetRx + c.NetTx }
|
||||||
|
|
||||||
|
func sumIO(c *Container) int64 { return c.IOBytesRead + c.IOBytesWrite }
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
input_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_."
|
input_chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_."
|
||||||
)
|
)
|
||||||
|
|
||||||
type Padding [2]int // x,y padding
|
type Padding [2]int // x,y padding
|
||||||
|
|||||||
@@ -100,27 +100,20 @@ func (m *Menu) Buffer() ui.Buffer {
|
|||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Up(ui.Event) {
|
func (m *Menu) Up() {
|
||||||
if m.cursorPos > 0 {
|
if m.cursorPos > 0 {
|
||||||
m.cursorPos--
|
m.cursorPos--
|
||||||
ui.Render(m)
|
ui.Render(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Down(ui.Event) {
|
func (m *Menu) Down() {
|
||||||
if m.cursorPos < (len(m.items) - 1) {
|
if m.cursorPos < (len(m.items) - 1) {
|
||||||
m.cursorPos++
|
m.cursorPos++
|
||||||
ui.Render(m)
|
ui.Render(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup some default handlers for menu navigation
|
|
||||||
func (m *Menu) NavigationHandlers() {
|
|
||||||
ui.Handle("/sys/kbd/<up>", m.Up)
|
|
||||||
ui.Handle("/sys/kbd/<down>", m.Down)
|
|
||||||
ui.Handle("/sys/kbd/q", func(ui.Event) { ui.StopLoop() })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set width and height based on menu items
|
// Set width and height based on menu items
|
||||||
func (m *Menu) calcSize() {
|
func (m *Menu) calcSize() {
|
||||||
m.Width = 7 // minimum width
|
m.Width = 7 // minimum width
|
||||||
|
|||||||
Reference in New Issue
Block a user