xmtop/widgets/net.go

167 lines
4.4 KiB
Go
Raw Normal View History

2018-02-19 15:25:02 +08:00
package widgets
import (
"fmt"
"log"
2019-06-13 05:28:52 +08:00
"strings"
2018-02-19 15:25:02 +08:00
"time"
"github.com/VictoriaMetrics/metrics"
2019-01-31 07:20:09 +08:00
psNet "github.com/shirou/gopsutil/net"
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
)
2019-06-08 06:03:00 +08:00
const (
2020-04-28 09:33:41 +08:00
// NetInterfaceAll enables all network interfaces
NetInterfaceAll = "all"
// NetInterfaceVpn is the VPN interface
NetInterfaceVpn = "tun0"
2019-06-08 06:03:00 +08:00
)
2019-03-01 08:29:52 +08:00
type NetWidget struct {
*ui.SparklineGroup
updateInterval time.Duration
2019-01-31 07:20:09 +08:00
2018-02-23 16:42:39 +08:00
// used to calculate recent network activity
2019-03-01 08:29:52 +08:00
totalBytesRecv uint64
totalBytesSent uint64
2019-06-13 05:28:52 +08:00
NetInterface []string
sentMetric *metrics.Counter
recvMetric *metrics.Counter
Mbps bool
2018-02-19 15:25:02 +08:00
}
2020-02-21 08:56:52 +08:00
// TODO: state:merge #169 % option for network use (jrswab/networkPercentage)
2019-05-15 00:09:17 +08:00
func NewNetWidget(netInterface string) *NetWidget {
2019-03-01 08:29:52 +08:00
recvSparkline := ui.NewSparkline()
recvSparkline.Data = []int{}
2018-02-19 15:25:02 +08:00
2019-03-01 08:29:52 +08:00
sentSparkline := ui.NewSparkline()
sentSparkline.Data = []int{}
2018-02-19 15:25:02 +08:00
2019-03-01 08:29:52 +08:00
spark := ui.NewSparklineGroup(recvSparkline, sentSparkline)
self := &NetWidget{
SparklineGroup: spark,
updateInterval: time.Second,
2019-06-13 05:28:52 +08:00
NetInterface: strings.Split(netInterface, ","),
2018-03-04 09:05:52 +08:00
}
2019-01-01 08:55:50 +08:00
self.Title = " Network Usage "
2019-05-15 00:09:17 +08:00
if netInterface != "all" {
self.Title = fmt.Sprintf(" Network Usage: %s ", netInterface)
2019-05-15 00:09:17 +08:00
}
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 (net *NetWidget) EnableMetric() {
net.recvMetric = metrics.NewCounter(makeName("net", "recv"))
net.sentMetric = metrics.NewCounter(makeName("net", "sent"))
}
2020-04-28 09:33:41 +08:00
func (net *NetWidget) update() {
interfaces, err := psNet.IOCounters(true)
if err != nil {
log.Printf("failed to get network activity from gopsutil: %v", err)
2018-12-14 13:59:45 +08:00
return
}
2019-03-01 08:29:52 +08:00
var totalBytesRecv uint64
var totalBytesSent uint64
2019-06-13 05:28:52 +08:00
interfaceMap := make(map[string]bool)
// Default behaviour
2020-04-28 09:33:41 +08:00
interfaceMap[NetInterfaceAll] = true
interfaceMap[NetInterfaceVpn] = false
2019-06-13 05:28:52 +08:00
// Build a map with wanted status for each interfaces.
2020-04-28 09:33:41 +08:00
for _, iface := range net.NetInterface {
2019-06-13 05:28:52 +08:00
if strings.HasPrefix(iface, "!") {
interfaceMap[strings.TrimPrefix(iface, "!")] = false
} else {
// if we specify a wanted interface, remove capture on all.
2020-04-28 09:33:41 +08:00
delete(interfaceMap, NetInterfaceAll)
2019-06-13 05:28:52 +08:00
interfaceMap[iface] = true
}
}
for _, _interface := range interfaces {
2019-06-13 05:28:52 +08:00
wanted, ok := interfaceMap[_interface.Name]
if wanted && ok { // Simple case
totalBytesRecv += _interface.BytesRecv
totalBytesSent += _interface.BytesSent
} else if ok { // Present but unwanted
continue
2020-04-28 09:33:41 +08:00
} else if interfaceMap[NetInterfaceAll] { // Capture other
2019-03-01 08:29:52 +08:00
totalBytesRecv += _interface.BytesRecv
totalBytesSent += _interface.BytesSent
}
}
2018-02-19 15:25:02 +08:00
2019-03-01 08:29:52 +08:00
var recentBytesRecv uint64
var recentBytesSent uint64
2020-04-28 09:33:41 +08:00
if net.totalBytesRecv != 0 { // if this isn't the first update
recentBytesRecv = totalBytesRecv - net.totalBytesRecv
recentBytesSent = totalBytesSent - net.totalBytesSent
2018-02-19 15:25:02 +08:00
2019-03-01 08:29:52 +08:00
if int(recentBytesRecv) < 0 {
log.Printf("error: negative value for recently received network data from gopsutil. recentBytesRecv: %v", recentBytesRecv)
// recover from error
2019-03-01 08:29:52 +08:00
recentBytesRecv = 0
}
2019-03-01 08:29:52 +08:00
if int(recentBytesSent) < 0 {
log.Printf("error: negative value for recently sent network data from gopsutil. recentBytesSent: %v", recentBytesSent)
// recover from error
2019-03-01 08:29:52 +08:00
recentBytesSent = 0
}
2020-04-28 09:33:41 +08:00
net.Lines[0].Data = append(net.Lines[0].Data, int(recentBytesRecv))
net.Lines[1].Data = append(net.Lines[1].Data, int(recentBytesSent))
if net.sentMetric != nil {
net.sentMetric.Add(int(recentBytesSent))
net.recvMetric.Add(int(recentBytesRecv))
}
2018-02-19 15:25:02 +08:00
}
2018-03-04 09:05:52 +08:00
// used in later calls to update
2020-04-28 09:33:41 +08:00
net.totalBytesRecv = totalBytesRecv
net.totalBytesSent = totalBytesSent
rx, tx := "RX/s", "TX/s"
2020-04-28 09:33:41 +08:00
if net.Mbps {
rx, tx = "mbps", "mbps"
}
format := " %s: %9.1f %2s/s"
var total, recent uint64
var label, unitRecent, rate string
var recentConverted float64
2018-05-22 07:02:31 +08:00
// render widget titles
for i := 0; i < 2; i++ {
if i == 0 {
total, label, rate, recent = totalBytesRecv, "RX", rx, recentBytesRecv
} else {
total, label, rate, recent = totalBytesSent, "TX", tx, recentBytesSent
}
totalConverted, unitTotal := utils.ConvertBytes(total)
2020-04-28 09:33:41 +08:00
if net.Mbps {
recentConverted, unitRecent, format = float64(recent)*0.000008, "", " %s: %11.3f %2s"
} else {
recentConverted, unitRecent = utils.ConvertBytes(recent)
}
2020-04-28 09:33:41 +08:00
net.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", label, totalConverted, unitTotal)
net.Lines[i].Title2 = fmt.Sprintf(format, rate, recentConverted, unitRecent)
}
2018-02-19 15:25:02 +08:00
}