xmtop/widgets/disk.go

186 lines
5.4 KiB
Go
Raw Normal View History

2018-02-19 15:25:02 +08:00
package widgets
import (
"fmt"
"log"
2018-05-22 07:02:31 +08:00
"sort"
"strings"
2018-02-19 15:25:02 +08:00
"time"
"github.com/prometheus/client_golang/prometheus"
2019-01-31 07:20:09 +08:00
psDisk "github.com/shirou/gopsutil/disk"
2020-04-24 03:07:08 +08:00
ui "github.com/xxxserxxx/gotop/v4/termui"
"github.com/xxxserxxx/gotop/v4/utils"
2018-02-19 15:25:02 +08:00
)
2018-05-22 07:02:31 +08:00
type Partition struct {
2019-03-01 08:29:52 +08:00
Device string
MountPoint string
BytesRead uint64
BytesWritten uint64
BytesReadRecently string
BytesWrittenRecently string
UsedPercent uint32
Free string
2018-05-22 07:02:31 +08:00
}
2019-03-01 08:29:52 +08:00
type DiskWidget struct {
2018-05-22 07:02:31 +08:00
*ui.Table
2019-03-01 08:29:52 +08:00
updateInterval time.Duration
Partitions map[string]*Partition
metric map[string]prometheus.Gauge
2018-02-19 15:25:02 +08:00
}
func NewDiskWidget() *DiskWidget {
2019-03-01 08:29:52 +08:00
self := &DiskWidget{
Table: ui.NewTable(),
updateInterval: time.Second,
Partitions: make(map[string]*Partition),
2018-03-04 09:05:52 +08:00
}
2019-01-01 08:55:50 +08:00
self.Title = " Disk Usage "
2018-05-22 07:02:31 +08:00
self.Header = []string{"Disk", "Mount", "Used", "Free", "R/s", "W/s"}
2019-03-01 08:29:52 +08:00
self.ColGap = 2
self.ColResizer = func() {
self.ColWidths = []int{
utils.MaxInt(4, (self.Inner.Dx()-29)/2),
utils.MaxInt(5, (self.Inner.Dx()-29)/2),
4, 5, 5, 5,
}
}
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() {
2019-03-01 08:29:52 +08:00
for range time.NewTicker(self.updateInterval).C {
self.Lock()
2018-03-28 05:27:23 +08:00
self.update()
self.Unlock()
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
}
2020-04-28 09:33:41 +08:00
func (disk *DiskWidget) EnableMetric() {
disk.metric = make(map[string]prometheus.Gauge)
for key, part := range disk.Partitions {
gauge := prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "gotop",
Subsystem: "disk",
Name: strings.ReplaceAll(key, "/", ":"),
//Name: strings.Replace(strings.Replace(part.Device, "/dev/", "", -1), "mapper/", "", -1),
})
gauge.Set(float64(part.UsedPercent) / 100.0)
prometheus.MustRegister(gauge)
2020-04-28 09:33:41 +08:00
disk.metric[key] = gauge
}
}
2020-04-28 09:33:41 +08:00
func (disk *DiskWidget) update() {
2019-03-01 08:29:52 +08:00
partitions, err := psDisk.Partitions(false)
if err != nil {
log.Printf("failed to get disk partitions from gopsutil: %v", err)
2018-12-14 13:59:45 +08:00
return
}
2018-05-22 07:02:31 +08:00
// add partition if it's new
2019-03-01 08:29:52 +08:00
for _, partition := range partitions {
2018-12-03 17:55:39 +08:00
// don't show loop devices
2019-03-01 08:29:52 +08:00
if strings.HasPrefix(partition.Device, "/dev/loop") {
2018-12-03 17:55:39 +08:00
continue
}
// don't show docker container filesystems
2019-03-01 08:29:52 +08:00
if strings.HasPrefix(partition.Mountpoint, "/var/lib/docker/") {
continue
}
// check if partition doesn't already exist in our list
2020-04-28 09:33:41 +08:00
if _, ok := disk.Partitions[partition.Device]; !ok {
disk.Partitions[partition.Device] = &Partition{
2019-03-01 08:29:52 +08:00
Device: partition.Device,
MountPoint: partition.Mountpoint,
2018-05-22 07:02:31 +08:00
}
}
}
// delete a partition if it no longer exists
2019-03-01 08:29:52 +08:00
toDelete := []string{}
2020-04-28 09:33:41 +08:00
for device := range disk.Partitions {
2018-05-22 07:02:31 +08:00
exists := false
2019-03-01 08:29:52 +08:00
for _, partition := range partitions {
if device == partition.Device {
2018-05-22 07:02:31 +08:00
exists = true
break
}
}
if !exists {
2019-03-01 08:29:52 +08:00
toDelete = append(toDelete, device)
2018-05-22 07:02:31 +08:00
}
}
2019-03-01 08:29:52 +08:00
for _, device := range toDelete {
2020-04-28 09:33:41 +08:00
delete(disk.Partitions, device)
2018-05-22 07:02:31 +08:00
}
// updates partition info. We add 0.5 to all values to make sure the truncation rounds
2020-04-28 09:33:41 +08:00
for _, partition := range disk.Partitions {
2019-03-01 08:29:52 +08:00
usage, err := psDisk.Usage(partition.MountPoint)
if err != nil {
2019-03-01 08:29:52 +08:00
log.Printf("failed to get partition usage statistics from gopsutil: %v. partition: %v", err, partition)
continue
}
partition.UsedPercent = uint32(usage.UsedPercent + 0.5)
2019-03-01 08:29:52 +08:00
bytesFree, magnitudeFree := utils.ConvertBytes(usage.Free)
partition.Free = fmt.Sprintf("%3d%s", uint64(bytesFree+0.5), magnitudeFree)
2018-05-22 07:02:31 +08:00
2019-03-01 08:29:52 +08:00
ioCounters, err := psDisk.IOCounters(partition.Device)
if err != nil {
2019-03-01 08:29:52 +08:00
log.Printf("failed to get partition read/write info from gopsutil: %v. partition: %v", err, partition)
continue
}
2019-03-01 08:29:52 +08:00
ioCounter := ioCounters[strings.Replace(partition.Device, "/dev/", "", -1)]
bytesRead, bytesWritten := ioCounter.ReadBytes, ioCounter.WriteBytes
if partition.BytesRead != 0 { // if this isn't the first update
bytesReadRecently := bytesRead - partition.BytesRead
bytesWrittenRecently := bytesWritten - partition.BytesWritten
readFloat, readMagnitude := utils.ConvertBytes(bytesReadRecently)
writeFloat, writeMagnitude := utils.ConvertBytes(bytesWrittenRecently)
bytesReadRecently, bytesWrittenRecently = uint64(readFloat+0.5), uint64(writeFloat+0.5)
2019-03-01 08:29:52 +08:00
partition.BytesReadRecently = fmt.Sprintf("%d%s", bytesReadRecently, readMagnitude)
partition.BytesWrittenRecently = fmt.Sprintf("%d%s", bytesWrittenRecently, writeMagnitude)
2018-05-22 07:02:31 +08:00
} else {
2019-03-01 08:29:52 +08:00
partition.BytesReadRecently = fmt.Sprintf("%d%s", 0, "B")
partition.BytesWrittenRecently = fmt.Sprintf("%d%s", 0, "B")
2018-05-22 07:02:31 +08:00
}
2019-03-01 08:29:52 +08:00
partition.BytesRead, partition.BytesWritten = bytesRead, bytesWritten
2018-05-22 07:02:31 +08:00
}
// converts self.Partitions into self.Rows which is a [][]String
2019-03-01 08:29:52 +08:00
2018-05-22 07:02:31 +08:00
sortedPartitions := []string{}
2020-04-28 09:33:41 +08:00
for seriesName := range disk.Partitions {
2018-05-22 07:02:31 +08:00
sortedPartitions = append(sortedPartitions, seriesName)
}
sort.Strings(sortedPartitions)
2020-04-28 09:33:41 +08:00
disk.Rows = make([][]string, len(disk.Partitions))
2019-03-01 08:29:52 +08:00
2018-05-22 07:02:31 +08:00
for i, key := range sortedPartitions {
2020-04-28 09:33:41 +08:00
partition := disk.Partitions[key]
disk.Rows[i] = make([]string, 6)
disk.Rows[i][0] = strings.Replace(strings.Replace(partition.Device, "/dev/", "", -1), "mapper/", "", -1)
disk.Rows[i][1] = partition.MountPoint
disk.Rows[i][2] = fmt.Sprintf("%d%%", partition.UsedPercent)
disk.Rows[i][3] = partition.Free
disk.Rows[i][4] = partition.BytesReadRecently
disk.Rows[i][5] = partition.BytesWrittenRecently
if disk.metric != nil {
if disk.metric[key] == nil {
log.Printf("ERROR: missing metric %s", key)
} else {
2020-04-28 09:33:41 +08:00
disk.metric[key].Set(float64(partition.UsedPercent) / 100.0)
}
}
2018-05-22 07:02:31 +08:00
}
2018-02-19 15:25:02 +08:00
}