Several Considerations for Load Balancing, Collection of Bulk ServerStatus
- added a number of global status collections - this requires wedging into things.. - removing direct address to come functions putting them though serverStatus
This commit is contained in:
parent
057ec00f82
commit
ba67cebb29
|
@ -1,4 +1,5 @@
|
||||||
rvpn:
|
rvpn:
|
||||||
|
serverName: rvpn1
|
||||||
wssdomain: localhost.daplie.me
|
wssdomain: localhost.daplie.me
|
||||||
admindomain: rvpn.daplie.invalid
|
admindomain: rvpn.daplie.invalid
|
||||||
genericlistener: 9999
|
genericlistener: 9999
|
||||||
|
@ -14,6 +15,6 @@ rvpn:
|
||||||
test3.daplie.me:
|
test3.daplie.me:
|
||||||
secret: abc123
|
secret: abc123
|
||||||
loadbalancing:
|
loadbalancing:
|
||||||
defaultmethod: 'round-robin'
|
defaultmethod: round-robin
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
</div>
|
</div>
|
||||||
<nav class="collapse navbar-collapse" id="bs-navbar">
|
<nav class="collapse navbar-collapse" id="bs-navbar">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
|
<li> <a href="/admin/status">Status</a> </li>
|
||||||
<li> <a href="/admin/servers">Servers</a> </li>
|
<li> <a href="/admin/servers">Servers</a> </li>
|
||||||
<li> <a href="/admin/#domains">Domains</a> </li>
|
<li> <a href="/admin/#domains">Domains</a> </li>
|
||||||
<li> <a href="/admin/#connections">Connections</a> </li>
|
<li> <a href="/admin/#connections">Connections</a> </li>
|
||||||
|
|
|
@ -4,15 +4,23 @@ var app = angular.module("rvpnApp", ["ngRoute", "angular-duration-format"]);
|
||||||
|
|
||||||
app.config(function($routeProvider, $locationProvider) {
|
app.config(function($routeProvider, $locationProvider) {
|
||||||
$routeProvider
|
$routeProvider
|
||||||
|
|
||||||
|
.when("/admin/status/", {
|
||||||
|
templateUrl : "admin/partials/status.html"
|
||||||
|
})
|
||||||
|
|
||||||
.when("/admin/index.html", {
|
.when("/admin/index.html", {
|
||||||
templateUrl : "admin/partials/servers.html"
|
templateUrl : "admin/partials/servers.html"
|
||||||
})
|
})
|
||||||
|
|
||||||
.when("/admin/servers/", {
|
.when("/admin/servers/", {
|
||||||
templateUrl : "admin/partials/servers.html"
|
templateUrl : "admin/partials/servers.html"
|
||||||
})
|
})
|
||||||
|
|
||||||
.when("/admin/#domains", {
|
.when("/admin/#domains", {
|
||||||
templateUrl : "green.htm"
|
templateUrl : "green.htm"
|
||||||
})
|
})
|
||||||
|
|
||||||
.when("/blue", {
|
.when("/blue", {
|
||||||
templateUrl : "blue.htm"
|
templateUrl : "blue.htm"
|
||||||
});
|
});
|
||||||
|
@ -81,7 +89,7 @@ app.controller('serverController', function ($scope, $http) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.triggerDetail = function(id) {
|
$scope.triggerDetail = function(id) {
|
||||||
console.log("triggerDetail ", id, $scope.servers_trigger_details[id])
|
//console.log("triggerDetail ", id, $scope.servers_trigger_details[id])
|
||||||
if ($scope.servers_trigger_details[id] == true) {
|
if ($scope.servers_trigger_details[id] == true) {
|
||||||
$scope.servers_trigger_details[id] = false;
|
$scope.servers_trigger_details[id] = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,7 +98,7 @@ app.controller('serverController', function ($scope, $http) {
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.checkDetail = function(id) {
|
$scope.checkDetail = function(id) {
|
||||||
console.log("checkDetail ", id, $scope.servers_trigger_details[id])
|
//console.log("checkDetail ", id, $scope.servers_trigger_details[id])
|
||||||
if ($scope.servers_trigger_details[id] == true) {
|
if ($scope.servers_trigger_details[id] == true) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
<th width="10%">Name</th>
|
<th width="10%">Name</th>
|
||||||
<th width="10%">Address</th>
|
<th width="10%">Address</th>
|
||||||
<th width="10%">Xfer (in/out)</th>
|
<th width="10%">Xfer (in/out)</th>
|
||||||
|
<th width="10%">Req/Resp</th>
|
||||||
<th width="10%">Time</th>
|
<th width="10%">Time</th>
|
||||||
<th width="10%">Idle</th>
|
<th width="10%">Idle</th>
|
||||||
<th width="1%"><center><span class="glyphicon glyphicon-option-vertical" aria-hidden="true"></span></center></th>
|
<th width="1%"><center><span class="glyphicon glyphicon-option-vertical" aria-hidden="true"></span></center></th>
|
||||||
|
@ -46,16 +47,34 @@
|
||||||
<td>
|
<td>
|
||||||
{{ s.bytes_in | bytes }}/{{ s.bytes_out | bytes }}
|
{{ s.bytes_in | bytes }}/{{ s.bytes_out | bytes }}
|
||||||
<div ng-hide="checkDetail(s.server_id)">
|
<div ng-hide="checkDetail(s.server_id)">
|
||||||
domains({{ s.domains.length}})
|
 
|
||||||
<div ng-repeat="d in s.domains | orderBy:'domain_name'">
|
<div ng-repeat="d in s.domains | orderBy:'domain_name'">
|
||||||
   {{ d.bytes_in | bytes }}/{{ d.bytes_out | bytes }}
|
   {{ d.bytes_in | bytes }}/{{ d.bytes_out | bytes }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ s.requests }}/{{ s.responses }}
|
||||||
|
<div ng-hide="checkDetail(s.server_id)">
|
||||||
|
 
|
||||||
|
<div ng-repeat="d in s.domains | orderBy:'domain_name'">
|
||||||
|
   {{ d.requests }}/{{ d.responses }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td>{{ s.duration | hfcduration }}</td>
|
<td>{{ s.duration | hfcduration }}</td>
|
||||||
<td>{{ s.idle | hfcduration }}</td>
|
<td>{{ s.idle | hfcduration }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="glyphicon glyphicon-zoom-in" title="Detail" aria-hidden="false" ng-click="triggerDetail(s.server_id)"></span>
|
<span class="glyphicon glyphicon-zoom-in" title="Detail" aria-hidden="false" ng-click="triggerDetail(s.server_id)"></span>
|
||||||
|
<div ng-hide="checkDetail(s.server_id)">
|
||||||
|
 
|
||||||
|
<div ng-repeat="d in s.domains | orderBy:'domain_name'">
|
||||||
|
<span class="glyphicon glyphicon-zoom-in" title="Detail" aria-hidden="false" ng-click="triggerDetail(s.server_id+d.domain_name)"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="panel panel-default" data-ng-controller="serverController">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="panel-title">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
Status
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form class="form-inline pull-right">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="search">Search:</label>
|
||||||
|
<input type="text" class="form-control" id="search" data-ng-model="servers_search">
|
||||||
|
</div>
|
||||||
|
<button type="button" title="Refresh" class="btn btn-default" aria-label="Refresh">
|
||||||
|
<span class="glyphicon glyphicon-refresh" title="Refresh" aria-hidden="false" ng-click="updateView()"></span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
24
main.go
24
main.go
|
@ -33,6 +33,7 @@ var (
|
||||||
dwell int
|
dwell int
|
||||||
cancelcheck int
|
cancelcheck int
|
||||||
lbDefaultMethod string
|
lbDefaultMethod string
|
||||||
|
serverName string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -48,7 +49,7 @@ func main() {
|
||||||
viper.AddConfigPath("./")
|
viper.AddConfigPath("./")
|
||||||
err := viper.ReadInConfig()
|
err := viper.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
panic(fmt.Errorf("fatal error config file: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.IntVar(&argDeadTime, "dead-time-counter", 5, "deadtime counter in seconds")
|
flag.IntVar(&argDeadTime, "dead-time-counter", 5, "deadtime counter in seconds")
|
||||||
|
@ -61,6 +62,7 @@ func main() {
|
||||||
dwell = deadtime.(map[string]interface{})["dwell"].(int)
|
dwell = deadtime.(map[string]interface{})["dwell"].(int)
|
||||||
cancelcheck = deadtime.(map[string]interface{})["cancelcheck"].(int)
|
cancelcheck = deadtime.(map[string]interface{})["cancelcheck"].(int)
|
||||||
lbDefaultMethod = viper.Get("rvpn.loadbalancing.defaultmethod").(string)
|
lbDefaultMethod = viper.Get("rvpn.loadbalancing.defaultmethod").(string)
|
||||||
|
serverName = viper.Get("rvpn.serverName").(string)
|
||||||
|
|
||||||
loginfo.Println("startup")
|
loginfo.Println("startup")
|
||||||
|
|
||||||
|
@ -78,6 +80,14 @@ func main() {
|
||||||
ctx, cancelContext := context.WithCancel(context.Background())
|
ctx, cancelContext := context.WithCancel(context.Background())
|
||||||
defer cancelContext()
|
defer cancelContext()
|
||||||
|
|
||||||
|
serverStatus := genericlistener.NewStatus(ctx)
|
||||||
|
serverStatus.AdminDomain = adminHostName
|
||||||
|
serverStatus.WssDomain = wssHostName
|
||||||
|
serverStatus.Name = serverName
|
||||||
|
serverStatus.StartTime = time.Now()
|
||||||
|
serverStatus.DeadTime = genericlistener.NewStatusDeadTime(dwell, idle, cancelcheck)
|
||||||
|
serverStatus.LoadbalanceDefaultMethod = lbDefaultMethod
|
||||||
|
|
||||||
// Setup for GenericListenServe.
|
// Setup for GenericListenServe.
|
||||||
// - establish context for the generic listener
|
// - establish context for the generic listener
|
||||||
// - startup listener
|
// - startup listener
|
||||||
|
@ -88,17 +98,17 @@ func main() {
|
||||||
// - match protocol
|
// - match protocol
|
||||||
|
|
||||||
connectionTracking := genericlistener.NewTracking()
|
connectionTracking := genericlistener.NewTracking()
|
||||||
|
serverStatus.ConnectionTracking = connectionTracking
|
||||||
go connectionTracking.Run(ctx)
|
go connectionTracking.Run(ctx)
|
||||||
|
|
||||||
connectionTable = genericlistener.NewTable(dwell, idle)
|
connectionTable = genericlistener.NewTable(dwell, idle)
|
||||||
|
serverStatus.ConnectionTable = connectionTable
|
||||||
go connectionTable.Run(ctx)
|
go connectionTable.Run(ctx)
|
||||||
|
|
||||||
genericListeners := genericlistener.NewGenerListeners(ctx, connectionTable, connectionTracking, secretKey, certbundle, wssHostName,
|
genericListeners := genericlistener.NewGenerListeners(ctx, secretKey, certbundle, serverStatus)
|
||||||
adminHostName, cancelcheck, lbDefaultMethod)
|
serverStatus.GenericListeners = genericListeners
|
||||||
|
|
||||||
genericListeners.Run(ctx, argGenericBinding)
|
go genericListeners.Run(ctx, argGenericBinding)
|
||||||
|
|
||||||
//Run for 10 minutes and then shutdown cleanly
|
select {}
|
||||||
time.Sleep(6000 * time.Second)
|
|
||||||
cancelContext()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ type DomainsAPI struct {
|
||||||
ServerID int64 `json:"server_id"`
|
ServerID int64 `json:"server_id"`
|
||||||
BytesIn int64 `json:"bytes_in"`
|
BytesIn int64 `json:"bytes_in"`
|
||||||
BytesOut int64 `json:"bytes_out"`
|
BytesOut int64 `json:"bytes_out"`
|
||||||
|
Requests int64 `json:"requests"`
|
||||||
|
Responses int64 `json:"responses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewDomainsAPI - Constructor
|
//NewDomainsAPI - Constructor
|
||||||
|
@ -15,6 +17,8 @@ func NewDomainsAPI(c *Connection, d *DomainTrack) (s *DomainsAPI) {
|
||||||
s.ServerID = c.ConnectionID()
|
s.ServerID = c.ConnectionID()
|
||||||
s.BytesIn = d.BytesIn()
|
s.BytesIn = d.BytesIn()
|
||||||
s.BytesOut = d.BytesOut()
|
s.BytesOut = d.BytesOut()
|
||||||
|
s.Requests = d.requests
|
||||||
|
s.Responses = d.responses
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,16 +41,20 @@ type DomainAPI struct {
|
||||||
ServerID int64 `json:"server_id"`
|
ServerID int64 `json:"server_id"`
|
||||||
BytesIn int64 `json:"bytes_in"`
|
BytesIn int64 `json:"bytes_in"`
|
||||||
BytesOut int64 `json:"bytes_out"`
|
BytesOut int64 `json:"bytes_out"`
|
||||||
|
Requests int64 `json:"requests"`
|
||||||
|
Responses int64 `json:"responses"`
|
||||||
Source string `json:"source_addr"`
|
Source string `json:"source_addr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewDomainsAPI - Constructor
|
//NewDomainAPI - Constructor
|
||||||
func NewDomainAPI(c *Connection, d *DomainTrack) (s *DomainAPI) {
|
func NewDomainAPI(c *Connection, d *DomainTrack) (s *DomainAPI) {
|
||||||
s = new(DomainAPI)
|
s = new(DomainAPI)
|
||||||
s.DomainName = d.DomainName
|
s.DomainName = d.DomainName
|
||||||
s.ServerID = c.ConnectionID()
|
s.ServerID = c.ConnectionID()
|
||||||
s.BytesIn = d.BytesIn()
|
s.BytesIn = d.BytesIn()
|
||||||
s.BytesOut = d.BytesOut()
|
s.BytesOut = d.BytesOut()
|
||||||
|
s.Requests = d.requests
|
||||||
|
s.Responses = d.responses
|
||||||
s.Source = c.Source()
|
s.Source = c.Source()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ type ServersAPI struct {
|
||||||
Idle float64 `json:"idle"`
|
Idle float64 `json:"idle"`
|
||||||
BytesIn int64 `json:"bytes_in"`
|
BytesIn int64 `json:"bytes_in"`
|
||||||
BytesOut int64 `json:"bytes_out"`
|
BytesOut int64 `json:"bytes_out"`
|
||||||
|
Requests int64 `json:"requests"`
|
||||||
|
Responses int64 `json:"responses"`
|
||||||
Source string `json:"source_address"`
|
Source string `json:"source_address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +29,8 @@ func NewServersAPI(c *Connection) (s *ServersAPI) {
|
||||||
s.Idle = time.Since(c.LastUpdate()).Seconds()
|
s.Idle = time.Since(c.LastUpdate()).Seconds()
|
||||||
s.BytesIn = c.BytesIn()
|
s.BytesIn = c.BytesIn()
|
||||||
s.BytesOut = c.BytesOut()
|
s.BytesOut = c.BytesOut()
|
||||||
|
s.Requests = c.requests
|
||||||
|
s.Responses = c.responses
|
||||||
s.Source = c.Source()
|
s.Source = c.Source()
|
||||||
|
|
||||||
for d := range c.DomainTrack {
|
for d := range c.DomainTrack {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
//StatusAPI -- Structure to support the server API
|
||||||
|
type StatusAPI struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Uptime float64 `json:"uptime"`
|
||||||
|
WssDomain string `json:"wss_domain"`
|
||||||
|
AdminDomain string `json:"admin_domain"`
|
||||||
|
LoadbalanceDefaultMethod string `json:"loadbalance_default_method"`
|
||||||
|
DeadTime *StatusDeadTimeAPI `json:"dead_time"`
|
||||||
|
AdminStats *TrafficAPI `json:"admin_traffic"`
|
||||||
|
TrafficStats *TrafficAPI `json:"traffic"`
|
||||||
|
ExtConnections *ConnectionStats
|
||||||
|
WSSConnections *ConnectionStats
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewStatusAPI - Constructor
|
||||||
|
func NewStatusAPI(c *Status) (s *StatusAPI) {
|
||||||
|
s = new(StatusAPI)
|
||||||
|
s.Name = c.Name
|
||||||
|
s.Uptime = time.Since(c.StartTime).Seconds()
|
||||||
|
s.WssDomain = c.WssDomain
|
||||||
|
s.AdminDomain = c.AdminDomain
|
||||||
|
s.LoadbalanceDefaultMethod = c.LoadbalanceDefaultMethod
|
||||||
|
s.DeadTime = NewStatusDeadTimeAPI(c.DeadTime.dwell, c.DeadTime.idle, c.DeadTime.cancelcheck)
|
||||||
|
s.AdminStats = NewTrafficAPI(c.AdminStats.Requests, c.AdminStats.Responses, c.AdminStats.BytesIn, c.AdminStats.BytesOut)
|
||||||
|
s.TrafficStats = NewTrafficAPI(c.TrafficStats.Requests, c.TrafficStats.Responses, c.TrafficStats.BytesIn, c.TrafficStats.BytesOut)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
//StatusDeadTimeAPI -- structure for deadtime configuration
|
||||||
|
type StatusDeadTimeAPI struct {
|
||||||
|
Dwell int `json:"dwell"`
|
||||||
|
Idle int `json:"idle"`
|
||||||
|
Cancelcheck int `json:"cancel_check"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewStatusDeadTimeAPI -- constructor
|
||||||
|
func NewStatusDeadTimeAPI(dwell int, idle int, cancelcheck int) (p *StatusDeadTimeAPI) {
|
||||||
|
p = new(StatusDeadTimeAPI)
|
||||||
|
p.Dwell = dwell
|
||||||
|
p.Idle = idle
|
||||||
|
p.Cancelcheck = cancelcheck
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
//TrafficStats --
|
||||||
|
type TrafficAPI struct {
|
||||||
|
Requests int64
|
||||||
|
Responses int64
|
||||||
|
BytesIn int64
|
||||||
|
BytesOut int64
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewTrafficStats -- Consttuctor
|
||||||
|
func NewTrafficAPI(requests int64, responses int64, bytes_in int64, bytes_out int64) (p *TrafficAPI) {
|
||||||
|
p = new(TrafficAPI)
|
||||||
|
p.Requests = requests
|
||||||
|
p.Responses = responses
|
||||||
|
p.BytesIn = bytes_in
|
||||||
|
p.BytesOut = bytes_out
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -16,12 +16,16 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var connectionTable *Table
|
var connectionTable *Table
|
||||||
|
var serverStatusAPI *Status
|
||||||
|
|
||||||
//handleAdminClient -
|
//handleAdminClient -
|
||||||
// - expecting an existing oneConnListener with a qualified wss client connected.
|
// - expecting an existing oneConnListener with a qualified wss client connected.
|
||||||
// - auth will happen again since we were just peeking at the token.
|
// - auth will happen again since we were just peeking at the token.
|
||||||
func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
|
func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
|
||||||
connectionTable = ctx.Value(ctxConnectionTable).(*Table)
|
serverStatus := ctx.Value(ctxServerStatus).(*Status)
|
||||||
|
|
||||||
|
connectionTable = serverStatus.ConnectionTable
|
||||||
|
serverStatusAPI = serverStatus
|
||||||
router := mux.NewRouter().StrictSlash(true)
|
router := mux.NewRouter().StrictSlash(true)
|
||||||
|
|
||||||
router.PathPrefix("/admin/").Handler(http.StripPrefix("/admin/", http.FileServer(http.Dir("html/admin"))))
|
router.PathPrefix("/admin/").Handler(http.StripPrefix("/admin/", http.FileServer(http.Dir("html/admin"))))
|
||||||
|
@ -46,6 +50,7 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
|
||||||
router.HandleFunc(endPointPrefix+"servers", getServersEndpoint).Methods("GET")
|
router.HandleFunc(endPointPrefix+"servers", getServersEndpoint).Methods("GET")
|
||||||
router.HandleFunc(endPointPrefix+"server/", getServerEndpoint).Methods("GET")
|
router.HandleFunc(endPointPrefix+"server/", getServerEndpoint).Methods("GET")
|
||||||
router.HandleFunc(endPointPrefix+"server/{server-id}", getServerEndpoint).Methods("GET")
|
router.HandleFunc(endPointPrefix+"server/{server-id}", getServerEndpoint).Methods("GET")
|
||||||
|
router.HandleFunc(endPointPrefix+"status/", getStatusEndpoint).Methods("GET")
|
||||||
|
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: ":80",
|
Addr: ":80",
|
||||||
|
@ -64,6 +69,20 @@ func handleAdminClient(ctx context.Context, oneConn *oneConnListener) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getStatusEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
pc, _, _, _ := runtime.Caller(0)
|
||||||
|
loginfo.Println(runtime.FuncForPC(pc).Name())
|
||||||
|
|
||||||
|
statusContainer := NewStatusAPI(serverStatusAPI)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||||
|
|
||||||
|
env := envelope.NewEnvelope("domains/GET")
|
||||||
|
env.Result = statusContainer
|
||||||
|
env.GenerateWriter(w)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func getDomainsEndpoint(w http.ResponseWriter, r *http.Request) {
|
func getDomainsEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
pc, _, _, _ := runtime.Caller(0)
|
pc, _, _, _ := runtime.Caller(0)
|
||||||
loginfo.Println(runtime.FuncForPC(pc).Name())
|
loginfo.Println(runtime.FuncForPC(pc).Name())
|
||||||
|
|
|
@ -50,6 +50,12 @@ type Connection struct {
|
||||||
// bytes out
|
// bytes out
|
||||||
bytesOut int64
|
bytesOut int64
|
||||||
|
|
||||||
|
// requests
|
||||||
|
requests int64
|
||||||
|
|
||||||
|
// response
|
||||||
|
responses int64
|
||||||
|
|
||||||
// Connect Time
|
// Connect Time
|
||||||
connectTime time.Time
|
connectTime time.Time
|
||||||
|
|
||||||
|
@ -79,6 +85,8 @@ func NewConnection(connectionTable *Table, conn *websocket.Conn, remoteAddress s
|
||||||
p.source = remoteAddress
|
p.source = remoteAddress
|
||||||
p.bytesIn = 0
|
p.bytesIn = 0
|
||||||
p.bytesOut = 0
|
p.bytesOut = 0
|
||||||
|
p.requests = 0
|
||||||
|
p.responses = 0
|
||||||
p.send = make(chan *SendTrack)
|
p.send = make(chan *SendTrack)
|
||||||
p.connectTime = time.Now()
|
p.connectTime = time.Now()
|
||||||
p.initialDomains = initialDomains
|
p.initialDomains = initialDomains
|
||||||
|
@ -143,6 +151,14 @@ func (c *Connection) addOut(num int64) {
|
||||||
c.bytesOut = c.bytesOut + num
|
c.bytesOut = c.bytesOut + num
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Connection) addRequests() {
|
||||||
|
c.requests = c.requests + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) addResponse() {
|
||||||
|
c.responses = c.responses + 1
|
||||||
|
}
|
||||||
|
|
||||||
//ConnectionTable -- property
|
//ConnectionTable -- property
|
||||||
func (c *Connection) ConnectionTable() (table *Table) {
|
func (c *Connection) ConnectionTable() (table *Table) {
|
||||||
table = c.connectionTable
|
table = c.connectionTable
|
||||||
|
@ -251,12 +267,14 @@ func (c *Connection) Reader(ctx context.Context) {
|
||||||
//Support for tracking outbound traffic based on domain.
|
//Support for tracking outbound traffic based on domain.
|
||||||
if domainTrack, ok := c.DomainTrack[track.domain]; ok {
|
if domainTrack, ok := c.DomainTrack[track.domain]; ok {
|
||||||
//if ok then add to structure, else warn there is something wrong
|
//if ok then add to structure, else warn there is something wrong
|
||||||
domainTrack.AddIn(int64(len(message)))
|
domainTrack.AddOut(int64(len(message)))
|
||||||
|
domainTrack.AddResponses()
|
||||||
}
|
}
|
||||||
|
|
||||||
track.conn.Write(p.Data.Data())
|
track.conn.Write(p.Data.Data())
|
||||||
|
|
||||||
c.addIn(int64(len(message)))
|
c.addIn(int64(len(message)))
|
||||||
|
c.addResponse()
|
||||||
loginfo.Println("end of read")
|
loginfo.Println("end of read")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,11 +308,13 @@ func (c *Connection) Writer() {
|
||||||
messageLen := int64(len(message.data))
|
messageLen := int64(len(message.data))
|
||||||
|
|
||||||
c.addOut(messageLen)
|
c.addOut(messageLen)
|
||||||
|
c.addRequests()
|
||||||
|
|
||||||
//Support for tracking outbound traffic based on domain.
|
//Support for tracking outbound traffic based on domain.
|
||||||
if domainTrack, ok := c.DomainTrack[message.domain]; ok {
|
if domainTrack, ok := c.DomainTrack[message.domain]; ok {
|
||||||
//if ok then add to structure, else warn there is something wrong
|
//if ok then add to structure, else warn there is something wrong
|
||||||
domainTrack.AddOut(messageLen)
|
domainTrack.AddIn(messageLen)
|
||||||
|
domainTrack.AddRequests()
|
||||||
loginfo.Println("adding ", messageLen, " to ", message.domain)
|
loginfo.Println("adding ", messageLen, " to ", message.domain)
|
||||||
} else {
|
} else {
|
||||||
logdebug.Println("attempting to add bytes to ", message.domain, "it does not exist")
|
logdebug.Println("attempting to add bytes to ", message.domain, "it does not exist")
|
||||||
|
|
|
@ -5,6 +5,8 @@ type DomainTrack struct {
|
||||||
DomainName string
|
DomainName string
|
||||||
bytesIn int64
|
bytesIn int64
|
||||||
bytesOut int64
|
bytesOut int64
|
||||||
|
requests int64
|
||||||
|
responses int64
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewDomainTrack -- Constructor
|
//NewDomainTrack -- Constructor
|
||||||
|
@ -13,6 +15,8 @@ func NewDomainTrack(domainName string) (p *DomainTrack) {
|
||||||
p.DomainName = domainName
|
p.DomainName = domainName
|
||||||
p.bytesIn = 0
|
p.bytesIn = 0
|
||||||
p.bytesOut = 0
|
p.bytesOut = 0
|
||||||
|
p.requests = 0
|
||||||
|
p.responses = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +32,7 @@ func (c *DomainTrack) BytesOut() (b int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//AddIn - Porperty
|
//AddIn - Property
|
||||||
func (c *DomainTrack) AddIn(num int64) {
|
func (c *DomainTrack) AddIn(num int64) {
|
||||||
c.bytesIn = c.bytesIn + num
|
c.bytesIn = c.bytesIn + num
|
||||||
}
|
}
|
||||||
|
@ -37,3 +41,13 @@ func (c *DomainTrack) AddIn(num int64) {
|
||||||
func (c *DomainTrack) AddOut(num int64) {
|
func (c *DomainTrack) AddOut(num int64) {
|
||||||
c.bytesOut = c.bytesOut + num
|
c.bytesOut = c.bytesOut + num
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//AddRequests - Property
|
||||||
|
func (c *DomainTrack) AddRequests() {
|
||||||
|
c.requests = c.requests + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
//AddResponses - Property
|
||||||
|
func (c *DomainTrack) AddResponses() {
|
||||||
|
c.responses = c.responses + 1
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@ type contextKey string
|
||||||
//CtxConnectionTrack
|
//CtxConnectionTrack
|
||||||
const (
|
const (
|
||||||
ctxSecretKey contextKey = "secretKey"
|
ctxSecretKey contextKey = "secretKey"
|
||||||
ctxConnectionTable contextKey = "connectionTable"
|
ctxServerStatus contextKey = "serverstatus"
|
||||||
|
|
||||||
|
//ctxConnectionTable contextKey = "connectionTable"
|
||||||
|
|
||||||
ctxConfig contextKey = "config"
|
ctxConfig contextKey = "config"
|
||||||
ctxListenerRegistration contextKey = "listenerRegistration"
|
ctxListenerRegistration contextKey = "listenerRegistration"
|
||||||
ctxConnectionTrack contextKey = "connectionTrack"
|
ctxConnectionTrack contextKey = "connectionTrack"
|
||||||
|
@ -271,16 +274,16 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
||||||
//handleExternalHTTPRequest -
|
//handleExternalHTTPRequest -
|
||||||
// - get a wConn and start processing requests
|
// - get a wConn and start processing requests
|
||||||
func handleExternalHTTPRequest(ctx context.Context, extConn *WedgeConn, hostname string, service string) {
|
func handleExternalHTTPRequest(ctx context.Context, extConn *WedgeConn, hostname string, service string) {
|
||||||
connectionTracking := ctx.Value(ctxConnectionTrack).(*Tracking)
|
//connectionTracking := ctx.Value(ctxConnectionTrack).(*Tracking)
|
||||||
|
serverStatus := ctx.Value(ctxServerStatus).(*Status)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
connectionTracking.unregister <- extConn
|
serverStatus.ExtConnectionUnregister(extConn)
|
||||||
extConn.Close()
|
extConn.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
connectionTable := ctx.Value(ctxConnectionTable).(*Table)
|
|
||||||
//find the connection by domain name
|
//find the connection by domain name
|
||||||
conn, ok := connectionTable.ConnByDomain(hostname)
|
conn, ok := serverStatus.ConnectionTable.ConnByDomain(hostname)
|
||||||
if !ok {
|
if !ok {
|
||||||
//matching connection can not be found based on ConnByDomain
|
//matching connection can not be found based on ConnByDomain
|
||||||
loginfo.Println("unable to match ", hostname, " to an existing connection")
|
loginfo.Println("unable to match ", hostname, " to an existing connection")
|
||||||
|
@ -289,7 +292,7 @@ func handleExternalHTTPRequest(ctx context.Context, extConn *WedgeConn, hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
track := NewTrack(extConn, hostname)
|
track := NewTrack(extConn, hostname)
|
||||||
connectionTracking.register <- track
|
serverStatus.ExtConnectionRegister(track)
|
||||||
|
|
||||||
loginfo.Println("Domain Accepted", hostname, extConn.RemoteAddr().String())
|
loginfo.Println("Domain Accepted", hostname, extConn.RemoteAddr().String())
|
||||||
|
|
||||||
|
@ -322,10 +325,11 @@ func handleExternalHTTPRequest(ctx context.Context, extConn *WedgeConn, hostname
|
||||||
p.Data.AppendBytes(buffer[0:cnt])
|
p.Data.AppendBytes(buffer[0:cnt])
|
||||||
buf := p.PackV1()
|
buf := p.PackV1()
|
||||||
|
|
||||||
loginfo.Println(hex.Dump(buf.Bytes()))
|
//loginfo.Println(hex.Dump(buf.Bytes()))
|
||||||
|
|
||||||
|
//Bundle up the send request and dispatch
|
||||||
sendTrack := NewSendTrack(buf.Bytes(), hostname)
|
sendTrack := NewSendTrack(buf.Bytes(), hostname)
|
||||||
conn.SendCh() <- sendTrack
|
serverStatus.SendExtRequest(conn, sendTrack)
|
||||||
|
|
||||||
_, err = extConn.Discard(cnt)
|
_, err = extConn.Discard(cnt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -341,7 +345,9 @@ func handleExternalHTTPRequest(ctx context.Context, extConn *WedgeConn, hostname
|
||||||
// - auth will happen again since we were just peeking at the token.
|
// - auth will happen again since we were just peeking at the token.
|
||||||
func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
|
func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
|
||||||
secretKey := ctx.Value(ctxSecretKey).(string)
|
secretKey := ctx.Value(ctxSecretKey).(string)
|
||||||
connectionTable := ctx.Value(ctxConnectionTable).(*Table)
|
serverStatus := ctx.Value(ctxServerStatus).(*Status)
|
||||||
|
|
||||||
|
//connectionTable := ctx.Value(ctxConnectionTable).(*Table)
|
||||||
|
|
||||||
router := mux.NewRouter().StrictSlash(true)
|
router := mux.NewRouter().StrictSlash(true)
|
||||||
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -380,9 +386,10 @@ func handleWssClient(ctx context.Context, oneConn *oneConnListener) {
|
||||||
|
|
||||||
//newConnection := connection.NewConnection(connectionTable, conn, r.RemoteAddr, domains)
|
//newConnection := connection.NewConnection(connectionTable, conn, r.RemoteAddr, domains)
|
||||||
|
|
||||||
connectionTrack := ctx.Value(ctxConnectionTrack).(*Tracking)
|
//connectionTrack := ctx.Value(ctxConnectionTrack).(*Tracking)
|
||||||
newRegistration := NewRegistration(conn, r.RemoteAddr, domains, connectionTrack)
|
newRegistration := NewRegistration(conn, r.RemoteAddr, domains, serverStatus.ConnectionTracking)
|
||||||
connectionTable.Register() <- newRegistration
|
serverStatus.WSSConnectionRegister(newRegistration)
|
||||||
|
|
||||||
ok = <-newRegistration.CommCh()
|
ok = <-newRegistration.CommCh()
|
||||||
if !ok {
|
if !ok {
|
||||||
loginfo.Println("connection registration failed ", newRegistration)
|
loginfo.Println("connection registration failed ", newRegistration)
|
||||||
|
|
|
@ -57,23 +57,24 @@ type GenericListeners struct {
|
||||||
adminHostName string
|
adminHostName string
|
||||||
cancelCheck int
|
cancelCheck int
|
||||||
lbDefaultMethod string
|
lbDefaultMethod string
|
||||||
|
serverStatus *Status
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewGenerListeners --
|
//NewGenerListeners --
|
||||||
func NewGenerListeners(ctx context.Context, connectionTable *Table, connectionTrack *Tracking, secretKey string, certbundle tls.Certificate,
|
func NewGenerListeners(ctx context.Context, secretKey string, certbundle tls.Certificate, serverStatus *Status) (p *GenericListeners) {
|
||||||
wssHostName string, adminHostName string, cancelCheck int, lbDefaultMethod string) (p *GenericListeners) {
|
|
||||||
p = new(GenericListeners)
|
p = new(GenericListeners)
|
||||||
p.listeners = make(map[*net.Listener]int)
|
p.listeners = make(map[*net.Listener]int)
|
||||||
p.ctx = ctx
|
p.ctx = ctx
|
||||||
p.connnectionTable = connectionTable
|
p.connnectionTable = serverStatus.ConnectionTable
|
||||||
p.connectionTracking = connectionTrack
|
p.connectionTracking = serverStatus.ConnectionTracking
|
||||||
p.secretKey = secretKey
|
p.secretKey = secretKey
|
||||||
p.certbundle = certbundle
|
p.certbundle = certbundle
|
||||||
p.register = make(chan *ListenerRegistration)
|
p.register = make(chan *ListenerRegistration)
|
||||||
p.wssHostName = wssHostName
|
p.wssHostName = serverStatus.WssDomain
|
||||||
p.adminHostName = adminHostName
|
p.adminHostName = serverStatus.AdminDomain
|
||||||
p.cancelCheck = cancelCheck
|
p.cancelCheck = serverStatus.DeadTime.cancelcheck
|
||||||
p.lbDefaultMethod = lbDefaultMethod
|
p.lbDefaultMethod = serverStatus.LoadbalanceDefaultMethod
|
||||||
|
p.serverStatus = serverStatus
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +87,6 @@ func (gl *GenericListeners) Run(ctx context.Context, initialPort int) {
|
||||||
config := &tls.Config{Certificates: []tls.Certificate{gl.certbundle}}
|
config := &tls.Config{Certificates: []tls.Certificate{gl.certbundle}}
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, ctxSecretKey, gl.secretKey)
|
ctx = context.WithValue(ctx, ctxSecretKey, gl.secretKey)
|
||||||
ctx = context.WithValue(ctx, ctxConnectionTable, gl.connnectionTable)
|
|
||||||
|
|
||||||
loginfo.Println(gl.connectionTracking)
|
loginfo.Println(gl.connectionTracking)
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ func (gl *GenericListeners) Run(ctx context.Context, initialPort int) {
|
||||||
ctx = context.WithValue(ctx, ctxAdminHostName, gl.adminHostName)
|
ctx = context.WithValue(ctx, ctxAdminHostName, gl.adminHostName)
|
||||||
ctx = context.WithValue(ctx, ctxCancelCheck, gl.cancelCheck)
|
ctx = context.WithValue(ctx, ctxCancelCheck, gl.cancelCheck)
|
||||||
ctx = context.WithValue(ctx, ctxLoadbalanceDefaultMethod, gl.lbDefaultMethod)
|
ctx = context.WithValue(ctx, ctxLoadbalanceDefaultMethod, gl.lbDefaultMethod)
|
||||||
|
ctx = context.WithValue(ctx, ctxServerStatus, gl.serverStatus)
|
||||||
|
|
||||||
go func(ctx context.Context) {
|
go func(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Status --
|
||||||
|
type Status struct {
|
||||||
|
ctx context.Context
|
||||||
|
Name string
|
||||||
|
StartTime time.Time
|
||||||
|
WssDomain string
|
||||||
|
AdminDomain string
|
||||||
|
DeadTime *StatusDeadTime
|
||||||
|
ConnectionTracking *Tracking
|
||||||
|
ConnectionTable *Table
|
||||||
|
GenericListeners *GenericListeners
|
||||||
|
LoadbalanceDefaultMethod string
|
||||||
|
AdminStats *TrafficStats
|
||||||
|
TrafficStats *TrafficStats
|
||||||
|
ExtConnections *ConnectionStats
|
||||||
|
WSSConnections *ConnectionStats
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewStatus --
|
||||||
|
func NewStatus(ctx context.Context) (p *Status) {
|
||||||
|
p = new(Status)
|
||||||
|
p.ctx = ctx
|
||||||
|
p.AdminStats = new(TrafficStats)
|
||||||
|
p.TrafficStats = new(TrafficStats)
|
||||||
|
p.ExtConnections = new(ConnectionStats)
|
||||||
|
p.WSSConnections = new(ConnectionStats)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// South Facing Functions
|
||||||
|
|
||||||
|
//WSSConnectionRegister --
|
||||||
|
func (p *Status) WSSConnectionRegister(newRegistration *Registration) {
|
||||||
|
p.ConnectionTable.Register() <- newRegistration
|
||||||
|
p.WSSConnections.IncConnections()
|
||||||
|
}
|
||||||
|
|
||||||
|
//WSSConnectionUnregister --
|
||||||
|
//unregisters a south facing connection
|
||||||
|
//intercept and update global statistics
|
||||||
|
func (p *Status) WSSConnectionUnregister() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// External Facing Functions
|
||||||
|
|
||||||
|
//ExtConnectionRegister --
|
||||||
|
//registers an ext facing connection
|
||||||
|
//intercept and update global statistics
|
||||||
|
func (p *Status) ExtConnectionRegister(newTrack *Track) {
|
||||||
|
p.ConnectionTracking.register <- newTrack
|
||||||
|
p.ExtConnections.IncConnections()
|
||||||
|
}
|
||||||
|
|
||||||
|
//ExtConnectionUnregister --
|
||||||
|
//unregisters an ext facing connection
|
||||||
|
//intercept and update global statistics
|
||||||
|
func (p *Status) ExtConnectionUnregister(extConn *WedgeConn) {
|
||||||
|
p.ConnectionTracking.unregister <- extConn
|
||||||
|
p.ExtConnections.DecConnections()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//SendExtRequest --
|
||||||
|
//sends a request to a south facing connection
|
||||||
|
//intercept the send, update our global stats
|
||||||
|
func (p *Status) SendExtRequest(conn *Connection, sendTrack *SendTrack) {
|
||||||
|
p.TrafficStats.IncRequests()
|
||||||
|
p.TrafficStats.AddBytesOut(int64(len(sendTrack.data)))
|
||||||
|
conn.SendCh() <- sendTrack
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
//StatusDeadTime -- structure for deadtime configuration
|
||||||
|
type StatusDeadTime struct {
|
||||||
|
dwell int
|
||||||
|
idle int
|
||||||
|
cancelcheck int
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewStatusDeadTime -- constructor
|
||||||
|
func NewStatusDeadTime(dwell int, idle int, cancelcheck int) (p *StatusDeadTime) {
|
||||||
|
p = new(StatusDeadTime)
|
||||||
|
p.dwell = dwell
|
||||||
|
p.idle = idle
|
||||||
|
p.cancelcheck = cancelcheck
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
//ConnectionStats --
|
||||||
|
type ConnectionStats struct {
|
||||||
|
Connections int64
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewConnectionStats -- Consttuctor
|
||||||
|
func NewConnectionStats() (p *ConnectionStats) {
|
||||||
|
p = new(ConnectionStats)
|
||||||
|
p.Connections = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//IncConnections --
|
||||||
|
func (p *ConnectionStats) IncConnections() {
|
||||||
|
p.Connections++
|
||||||
|
}
|
||||||
|
|
||||||
|
//DecConnections --
|
||||||
|
func (p *ConnectionStats) DecConnections() {
|
||||||
|
p.Connections--
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package genericlistener
|
||||||
|
|
||||||
|
//TrafficStats --
|
||||||
|
type TrafficStats struct {
|
||||||
|
Requests int64
|
||||||
|
Responses int64
|
||||||
|
BytesIn int64
|
||||||
|
BytesOut int64
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewTrafficStats -- Consttuctor
|
||||||
|
func NewTrafficStats() (p *TrafficStats) {
|
||||||
|
p = new(TrafficStats)
|
||||||
|
p.Requests = 0
|
||||||
|
p.Responses = 0
|
||||||
|
p.BytesIn = 0
|
||||||
|
p.BytesOut = 0
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//IncRequests --
|
||||||
|
func (p *TrafficStats) IncRequests() {
|
||||||
|
p.Requests++
|
||||||
|
}
|
||||||
|
|
||||||
|
//IncResponses --
|
||||||
|
func (p *TrafficStats) IncResponses() {
|
||||||
|
p.Responses++
|
||||||
|
}
|
||||||
|
|
||||||
|
//AddBytesIn --
|
||||||
|
func (p *TrafficStats) AddBytesIn(count int64) {
|
||||||
|
p.BytesIn = p.BytesIn + count
|
||||||
|
}
|
||||||
|
|
||||||
|
//AddBytesOut --
|
||||||
|
func (p *TrafficStats) AddBytesOut(count int64) {
|
||||||
|
p.BytesOut = p.BytesOut + count
|
||||||
|
}
|
Loading…
Reference in New Issue