2018-02-19 15:25:02 +08:00
|
|
|
package widgets
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-05-22 07:02:31 +08:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2018-02-19 15:25:02 +08:00
|
|
|
"time"
|
|
|
|
|
2018-05-11 12:23:20 +08:00
|
|
|
"github.com/cjbassi/gotop/src/utils"
|
2018-03-30 06:48:43 +08:00
|
|
|
ui "github.com/cjbassi/termui"
|
2018-03-04 09:05:52 +08:00
|
|
|
psDisk "github.com/shirou/gopsutil/disk"
|
2018-02-19 15:25:02 +08:00
|
|
|
)
|
|
|
|
|
2018-05-22 07:02:31 +08:00
|
|
|
type Partition struct {
|
|
|
|
Device string
|
|
|
|
Mount string
|
|
|
|
TotalRead uint64
|
|
|
|
TotalWrite uint64
|
|
|
|
CurRead string
|
|
|
|
CurWrite string
|
|
|
|
UsedPercent int
|
|
|
|
Free string
|
|
|
|
}
|
|
|
|
|
2018-02-19 15:25:02 +08:00
|
|
|
type Disk struct {
|
2018-05-22 07:02:31 +08:00
|
|
|
*ui.Table
|
|
|
|
interval time.Duration
|
|
|
|
Partitions map[string]*Partition
|
2018-02-19 15:25:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewDisk() *Disk {
|
2018-03-28 05:27:23 +08:00
|
|
|
self := &Disk{
|
2018-05-22 07:02:31 +08:00
|
|
|
Table: ui.NewTable(),
|
|
|
|
interval: time.Second,
|
|
|
|
Partitions: make(map[string]*Partition),
|
2018-03-04 09:05:52 +08:00
|
|
|
}
|
2018-03-28 05:27:23 +08:00
|
|
|
self.Label = "Disk Usage"
|
2018-05-22 07:02:31 +08:00
|
|
|
self.Header = []string{"Disk", "Mount", "Used", "Free", "R/s", "W/s"}
|
|
|
|
self.Gap = 2
|
|
|
|
self.ColResizer = self.ColResize
|
2018-02-19 15:25:02 +08:00
|
|
|
|
2018-04-13 11:00:34 +08:00
|
|
|
self.update()
|
|
|
|
|
2018-02-19 15:25:02 +08:00
|
|
|
go func() {
|
2018-11-30 10:17:13 +08:00
|
|
|
ticker := time.NewTicker(self.interval)
|
2018-02-19 15:25:02 +08:00
|
|
|
for range ticker.C {
|
2018-03-28 05:27:23 +08:00
|
|
|
self.update()
|
2018-02-19 15:25:02 +08:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-03-28 05:27:23 +08:00
|
|
|
return self
|
2018-02-19 15:25:02 +08:00
|
|
|
}
|
|
|
|
|
2018-03-28 05:27:23 +08:00
|
|
|
func (self *Disk) update() {
|
2018-05-22 07:02:31 +08:00
|
|
|
Partitions, _ := psDisk.Partitions(false)
|
|
|
|
|
|
|
|
// add partition if it's new
|
|
|
|
for _, Part := range Partitions {
|
|
|
|
device := strings.Replace(Part.Device, "/dev/", "", -1)
|
2018-12-03 17:55:39 +08:00
|
|
|
// don't show loop devices
|
|
|
|
if strings.HasPrefix(device, "loop") {
|
|
|
|
continue
|
|
|
|
}
|
2018-05-22 07:02:31 +08:00
|
|
|
if _, ok := self.Partitions[device]; !ok {
|
2018-11-16 02:07:32 +08:00
|
|
|
// https://github.com/shirou/gopsutil/issues/555
|
|
|
|
// have to remove artifacts produced by gopsutil when there's a space in the mount path
|
|
|
|
mountPoint := strings.Replace(Part.Mountpoint, "\\040", " ", -1)
|
2018-05-22 07:02:31 +08:00
|
|
|
self.Partitions[device] = &Partition{
|
|
|
|
Device: device,
|
2018-11-16 02:07:32 +08:00
|
|
|
Mount: mountPoint,
|
2018-05-22 07:02:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete a partition if it no longer exists
|
|
|
|
todelete := []string{}
|
|
|
|
for key, _ := range self.Partitions {
|
|
|
|
exists := false
|
|
|
|
for _, Part := range Partitions {
|
|
|
|
device := strings.Replace(Part.Device, "/dev/", "", -1)
|
|
|
|
if key == device {
|
|
|
|
exists = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
todelete = append(todelete, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, val := range todelete {
|
|
|
|
delete(self.Partitions, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
// updates partition info
|
|
|
|
for _, Part := range self.Partitions {
|
|
|
|
usage, _ := psDisk.Usage(Part.Mount)
|
|
|
|
Part.UsedPercent = int(usage.UsedPercent)
|
|
|
|
|
|
|
|
Free, Mag := utils.ConvertBytes(usage.Free)
|
|
|
|
Part.Free = fmt.Sprintf("%3d%s", uint64(Free), Mag)
|
|
|
|
|
|
|
|
ret, _ := psDisk.IOCounters("/dev/" + Part.Device)
|
|
|
|
data := ret[Part.Device]
|
|
|
|
curRead, curWrite := data.ReadBytes, data.WriteBytes
|
|
|
|
if Part.TotalRead != 0 { // if this isn't the first update
|
|
|
|
readRecent := curRead - Part.TotalRead
|
|
|
|
writeRecent := curWrite - Part.TotalWrite
|
|
|
|
|
|
|
|
readFloat, unitRead := utils.ConvertBytes(readRecent)
|
|
|
|
writeFloat, unitWrite := utils.ConvertBytes(writeRecent)
|
|
|
|
readRecent, writeRecent = uint64(readFloat), uint64(writeFloat)
|
|
|
|
Part.CurRead = fmt.Sprintf("%d%s", readRecent, unitRead)
|
|
|
|
Part.CurWrite = fmt.Sprintf("%d%s", writeRecent, unitWrite)
|
|
|
|
} else {
|
|
|
|
Part.CurRead = fmt.Sprintf("%d%s", 0, "B")
|
|
|
|
Part.CurWrite = fmt.Sprintf("%d%s", 0, "B")
|
|
|
|
}
|
|
|
|
Part.TotalRead, Part.TotalWrite = curRead, curWrite
|
|
|
|
}
|
|
|
|
|
|
|
|
// converts self.Partitions into self.Rows which is a [][]String
|
|
|
|
sortedPartitions := []string{}
|
|
|
|
for seriesName := range self.Partitions {
|
|
|
|
sortedPartitions = append(sortedPartitions, seriesName)
|
|
|
|
}
|
|
|
|
sort.Strings(sortedPartitions)
|
|
|
|
|
|
|
|
self.Rows = make([][]string, len(self.Partitions))
|
|
|
|
for i, key := range sortedPartitions {
|
|
|
|
Part := self.Partitions[key]
|
|
|
|
self.Rows[i] = make([]string, 6)
|
|
|
|
self.Rows[i][0] = Part.Device
|
|
|
|
self.Rows[i][1] = Part.Mount
|
|
|
|
self.Rows[i][2] = fmt.Sprintf("%d%%", Part.UsedPercent)
|
|
|
|
self.Rows[i][3] = Part.Free
|
|
|
|
self.Rows[i][4] = Part.CurRead
|
|
|
|
self.Rows[i][5] = Part.CurWrite
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ColResize overrides the default ColResize in the termui table.
|
|
|
|
func (self *Disk) ColResize() {
|
|
|
|
self.ColWidths = []int{
|
|
|
|
4,
|
|
|
|
utils.Max(5, self.X-33),
|
|
|
|
4, 5, 5, 5,
|
|
|
|
}
|
|
|
|
|
|
|
|
self.CellXPos = []int{}
|
|
|
|
cur := 1
|
|
|
|
for _, w := range self.ColWidths {
|
|
|
|
self.CellXPos = append(self.CellXPos, cur)
|
|
|
|
cur += w
|
|
|
|
cur += self.Gap
|
|
|
|
}
|
2018-02-19 15:25:02 +08:00
|
|
|
}
|