Add bandwidth monitoring.

This commit is contained in:
Patrick Fairbank
2015-06-20 23:54:14 -07:00
parent 676d3b7787
commit 3e25f1334c
13 changed files with 157 additions and 28 deletions

108
bandwidth_monitor.go Normal file
View File

@@ -0,0 +1,108 @@
// Copyright 2015 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Methods for monitoring team bandwidth usage across a managed switch.
package main
import (
"fmt"
"github.com/cdevr/WapSNMP"
"log"
"time"
)
const (
monitoringIntervalMs = 1000
toRobotBytesOid = ".1.3.6.1.2.1.2.2.1.10"
fromRobotBytesOid = ".1.3.6.1.2.1.2.2.1.16"
red1Port = 2
red2Port = 4
red3Port = 6
blue1Port = 8
blue2Port = 10
blue3Port = 12
)
type BandwidthMonitor struct {
snmpClient *wapsnmp.WapSNMP
toRobotOid wapsnmp.Oid
fromRobotOid wapsnmp.Oid
lastToRobotBytes map[string]interface{}
lastFromRobotBytes map[string]interface{}
lastBytesTime time.Time
}
// Loops indefinitely to query the managed switch via SNMP (Simple Network Management Protocol).
func MonitorBandwidth() {
monitor := BandwidthMonitor{toRobotOid: wapsnmp.MustParseOid(toRobotBytesOid),
fromRobotOid: wapsnmp.MustParseOid(fromRobotBytesOid)}
for {
if eventSettings.NetworkSecurityEnabled && eventSettings.BandwidthMonitoringEnabled {
err := monitor.updateBandwidth()
if err != nil {
log.Printf("Bandwidth monitoring error: %s", err)
}
}
time.Sleep(time.Millisecond * monitoringIntervalMs)
}
}
func (monitor *BandwidthMonitor) updateBandwidth() error {
if monitor.snmpClient != nil && monitor.snmpClient.Target != eventSettings.SwitchAddress {
// Switch address has changed; must re-create the SNMP client.
monitor.snmpClient.Close()
monitor.snmpClient = nil
}
if monitor.snmpClient == nil {
var err error
monitor.snmpClient, err = wapsnmp.NewWapSNMP(eventSettings.SwitchAddress, eventSettings.SwitchPassword,
wapsnmp.SNMPv2c, 2*time.Second, 0)
if err != nil {
return err
}
}
// Retrieve total number of bytes sent/received per port.
toRobotBytes, err := monitor.snmpClient.GetTable(monitor.toRobotOid)
if err != nil {
return err
}
fromRobotBytes, err := monitor.snmpClient.GetTable(monitor.fromRobotOid)
if err != nil {
return err
}
// Calculate the bandwidth usage over time.
monitor.updateStationBandwidth("R1", red1Port, toRobotBytes, fromRobotBytes)
monitor.updateStationBandwidth("R2", red2Port, toRobotBytes, fromRobotBytes)
monitor.updateStationBandwidth("R3", red3Port, toRobotBytes, fromRobotBytes)
monitor.updateStationBandwidth("B1", blue1Port, toRobotBytes, fromRobotBytes)
monitor.updateStationBandwidth("B2", blue2Port, toRobotBytes, fromRobotBytes)
monitor.updateStationBandwidth("B3", blue3Port, toRobotBytes, fromRobotBytes)
monitor.lastToRobotBytes = toRobotBytes
monitor.lastFromRobotBytes = fromRobotBytes
monitor.lastBytesTime = time.Now()
return nil
}
func (monitor *BandwidthMonitor) updateStationBandwidth(station string, port int, toRobotBytes map[string]interface{},
fromRobotBytes map[string]interface{}) {
dsConn := mainArena.AllianceStations[station].DsConn
if dsConn == nil {
// No team assigned; just skip it.
return
}
dsStatus := dsConn.DriverStationStatus
secondsSinceLast := time.Now().Sub(monitor.lastBytesTime).Seconds()
toRobotBytesForPort := uint32(toRobotBytes[fmt.Sprintf("%s.%d", toRobotBytesOid, port)].(wapsnmp.Counter))
lastToRobotBytesForPort := uint32(monitor.lastToRobotBytes[fmt.Sprintf("%s.%d", toRobotBytesOid, port)].(wapsnmp.Counter))
dsStatus.MBpsToRobot = float64(toRobotBytesForPort-lastToRobotBytesForPort) / 1024 / 1024 / secondsSinceLast
fromRobotBytesForPort := uint32(fromRobotBytes[fmt.Sprintf("%s.%d", fromRobotBytesOid, port)].(wapsnmp.Counter))
lastFromRobotBytesForPort := uint32(monitor.lastFromRobotBytes[fmt.Sprintf("%s.%d", fromRobotBytesOid, port)].(wapsnmp.Counter))
dsStatus.MBpsFromRobot = float64(fromRobotBytesForPort-lastFromRobotBytesForPort) / 1024 / 1024 / secondsSinceLast
}

View File

@@ -19,7 +19,8 @@ CREATE TABLE event_settings (
apusername VARCHAR(255),
appassword VARCHAR(255),
switchaddress VARCHAR(255),
switchpassword VARCHAR(255)
switchpassword VARCHAR(255),
bandwidthmonitoringenabled bool
);
-- +goose Down

View File

@@ -32,6 +32,8 @@ type DriverStationStatus struct {
PacketCount int
MissedPacketCount int
DsRobotTripTimeMs int
MBpsToRobot float64
MBpsFromRobot float64
}
type DriverStationConnection struct {
@@ -71,6 +73,8 @@ func (dsConn *DriverStationConnection) Update() error {
dsConn.DriverStationStatus.DsLinked = false
dsConn.DriverStationStatus.RobotLinked = false
dsConn.DriverStationStatus.BatteryVoltage = 0
dsConn.DriverStationStatus.MBpsToRobot = 0
dsConn.DriverStationStatus.MBpsFromRobot = 0
}
return nil

View File

@@ -6,26 +6,27 @@
package main
type EventSettings struct {
Id int
Name string
Code string
DisplayBackgroundColor string
NumElimAlliances int
SelectionRound2Order string
SelectionRound3Order string
TeamInfoDownloadEnabled bool
RedGoalLightsAddress string
BlueGoalLightsAddress string
TbaPublishingEnabled bool
TbaEventCode string
TbaSecretId string
TbaSecret string
NetworkSecurityEnabled bool
ApAddress string
ApUsername string
ApPassword string
SwitchAddress string
SwitchPassword string
Id int
Name string
Code string
DisplayBackgroundColor string
NumElimAlliances int
SelectionRound2Order string
SelectionRound3Order string
TeamInfoDownloadEnabled bool
RedGoalLightsAddress string
BlueGoalLightsAddress string
TbaPublishingEnabled bool
TbaEventCode string
TbaSecretId string
TbaSecret string
NetworkSecurityEnabled bool
ApAddress string
ApUsername string
ApPassword string
SwitchAddress string
SwitchPassword string
BandwidthMonitoringEnabled bool
}
const eventSettingsId = 0

View File

@@ -24,6 +24,7 @@ func main() {
listener, err := DsPacketListener()
checkErr(err)
go ListenForDsPackets(listener)
go MonitorBandwidth()
mainArena.Setup()
mainArena.Run()
}

View File

@@ -55,6 +55,7 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
eventSettings.ApPassword = r.PostFormValue("apPassword")
eventSettings.SwitchAddress = r.PostFormValue("switchAddress")
eventSettings.SwitchPassword = r.PostFormValue("switchPassword")
eventSettings.BandwidthMonitoringEnabled = r.PostFormValue("bandwidthMonitoringEnabled") == "on"
err := db.SaveEventSettings(eventSettings)
if err != nil {
handleWebErr(w, err)

View File

@@ -52,7 +52,7 @@
border: 1px solid #999;
border-radius: 4px;
padding: 0px;
width: 45px;
width: 50px;
height: 27px;
line-height: 25px;
font-size: 14px;

View File

@@ -14,6 +14,7 @@ var handleStatus = function(data) {
$("#status" + station + " .team").text(stationStatus.DsConn.TeamId);
var dsStatus = stationStatus.DsConn.DriverStationStatus;
$("#status" + station + " .ds-status").attr("data-status-ok", dsStatus.DsLinked);
$("#status" + station + " .ds-status").text(dsStatus.MBpsToRobot.toFixed(1) + "/" + dsStatus.MBpsFromRobot.toFixed(1));
$("#status" + station + " .robot-status").attr("data-status-ok", dsStatus.RobotLinked);
if (stationStatus.DsConn.SecondsSinceLastRobotLink > 1 && stationStatus.DsConn.SecondsSinceLastRobotLink < 1000) {
$("#status" + station + " .robot-status").text(stationStatus.DsConn.SecondsSinceLastRobotLink.toFixed());
@@ -33,6 +34,7 @@ var handleStatus = function(data) {
$("#status" + station + " .packet-loss").text(dsStatus.MissedPacketCount);
} else {
$("#status" + station + " .ds-status").attr("data-status-ok", "");
$("#status" + station + " .ds-status").text("");
$("#status" + station + " .robot-status").attr("data-status-ok", "");
$("#status" + station + " .robot-status").text("");
$("#status" + station + " .battery-status").attr("data-status-ok", "");

View File

@@ -64,6 +64,7 @@ var handleStatus = function(data) {
if (stationStatus.DsConn) {
var dsStatus = stationStatus.DsConn.DriverStationStatus;
$("#status" + station + " .ds-status").attr("data-status-ok", dsStatus.DsLinked);
$("#status" + station + " .ds-status").text(dsStatus.MBpsToRobot.toFixed(1) + "/" + dsStatus.MBpsFromRobot.toFixed(1));
$("#status" + station + " .robot-status").attr("data-status-ok", dsStatus.RobotLinked);
if (stationStatus.DsConn.SecondsSinceLastRobotLink > 1 && stationStatus.DsConn.SecondsSinceLastRobotLink < 1000) {
$("#status" + station + " .robot-status").text(stationStatus.DsConn.SecondsSinceLastRobotLink.toFixed());
@@ -79,6 +80,7 @@ var handleStatus = function(data) {
$("#status" + station + " .battery-status").text(dsStatus.BatteryVoltage.toFixed(1) + "V");
} else {
$("#status" + station + " .ds-status").attr("data-status-ok", "");
$("#status" + station + " .ds-status").text("");
$("#status" + station + " .robot-status").attr("data-status-ok", "");
$("#status" + station + " .robot-status").text("");
$("#status" + station + " .battery-status").attr("data-status-ok", "");

View File

@@ -125,10 +125,11 @@ interface FastEthernet0/24
switchport mode access
!
interface GigabitEthernet0/1
switchport mode dynamic desirable
switchport trunk encapsulation dot1q
switchport mode trunk
!
interface GigabitEthernet0/2
switchport mode dynamic desirable
switchport mode access
!
interface Vlan1
ip address 10.0.0.61 255.255.255.0
@@ -170,6 +171,8 @@ access-list 114 permit ip 10.0.4.0 0.0.0.255 host 10.0.100.50
access-list 115 permit ip 10.0.5.0 0.0.0.255 host 10.0.100.50
access-list 116 permit ip 10.0.6.0 0.0.0.255 host 10.0.100.50
!
snmp-server community 1234Five RO
!
line con 0
exec-timeout 0 0
line vty 0 4

View File

@@ -12,7 +12,7 @@
<div class="col-lg-6 well well-darkblue">
<div class="row form-group">
<div class="col-xs-3">Blue Teams</div>
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station">DS</div>
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
<div class="col-xs-1" data-toggle="tooltip" title="Robot Status/Time Since Last Link">R</div>
<div class="col-xs-1" data-toggle="tooltip" title="Battery">Bat</div>
<div class="col-xs-1" data-toggle="tooltip" title="Bypassed/Disabled">Byp</div>
@@ -26,7 +26,7 @@
<div class="col-lg-6 well well-darkred">
<div class="row form-group">
<div class="col-xs-3">Red Teams</div>
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station">DS</div>
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
<div class="col-xs-1" data-toggle="tooltip" title="Robot Status/Time Since Last Link">R</div>
<div class="col-xs-1" data-toggle="tooltip" title="Battery">Bat</div>
<div class="col-xs-1" data-toggle="tooltip" title="Bypassed/Disabled">Byp</div>

View File

@@ -63,7 +63,7 @@
<div class="col-lg-6 well well-darkblue">
<div class="row form-group">
<div class="col-lg-4">Blue Teams</div>
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station">DS</div>
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
<div class="col-lg-2" data-toggle="tooltip" title="Robot">R</div>
<div class="col-lg-2" data-toggle="tooltip" title="Battery">B</div>
<div class="col-lg-2" data-toggle="tooltip" title="Bypass/Disable">Byp</div>
@@ -75,7 +75,7 @@
<div class="col-lg-6 well well-darkred">
<div class="row form-group">
<div class="col-lg-4">Red Teams</div>
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station">DS</div>
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
<div class="col-lg-2" data-toggle="tooltip" title="Robot">R</div>
<div class="col-lg-2" data-toggle="tooltip" title="Battery">B</div>
<div class="col-lg-2" data-toggle="tooltip" title="Bypass/Disable">Byp</div>

View File

@@ -179,6 +179,12 @@
<input type="password" class="form-control" name="switchPassword" value="{{.SwitchPassword}}">
</div>
</div>
<div class="form-group">
<label class="col-lg-7 control-label">Enable bandwidth monitoring</label>
<div class="col-lg-1 checkbox">
<input type="checkbox" name="bandwidthMonitoringEnabled"{{if .BandwidthMonitoringEnabled}} checked{{end}}>
</div>
</div>
</fieldset>
<div class="form-group">
<div class="col-lg-7 col-lg-offset-5">