Merge 'Connections' and 'Connection Details' in node details pane

* Now the Connections table appears at the end of the pane

* Connection details are expandable (still not implemented in the UI but backwards
compatible).
This commit is contained in:
Alfonso Acosta
2015-08-20 15:59:13 +00:00
parent c78f9d812c
commit 37307c26b9
2 changed files with 81 additions and 104 deletions

View File

@@ -13,13 +13,11 @@ import (
const (
mb = 1 << 20
connectionsRank = 100
containerImageRank = 4
containerRank = 3
processRank = 2
hostRank = 1
endpointRank = 0 // this is the least important table, so sort to bottom
addressRank = 0 // also least important; never merged with endpoints
connectionsRank = 0 // keep connections at the bottom until they are expandable in the UI
)
// DetailedNode is the data type that's yielded to the JavaScript layer when
@@ -46,14 +44,14 @@ type Row struct {
Key string `json:"key"` // e.g. Ingress
ValueMajor string `json:"value_major"` // e.g. 25
ValueMinor string `json:"value_minor,omitempty"` // e.g. KB/s
Expandable bool `json:expandable,omitempty` // Whether it can be expanded (hidden by default)
Expandable bool `json:"expandable,omitempty"` // Whether it can be expanded (hidden by default)
}
type rows []Row
type sortableRows []Row
func (r rows) Len() int { return len(r) }
func (r rows) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r rows) Less(i, j int) bool {
func (r sortableRows) Len() int { return len(r) }
func (r sortableRows) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r sortableRows) Less(i, j int) bool {
switch {
case r[i].Key != r[j].Key:
return r[i].Key < r[j].Key
@@ -75,6 +73,37 @@ func (t tables) Less(i, j int) bool { return t[i].Rank > t[j].Rank }
// MakeDetailedNode transforms a renderable node to a detailed node. It uses
// aggregate metadata, plus the set of origin node IDs, to produce tables.
func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
tables := tables{}
// RenderableNode may be the result of merge operation(s), and so may have
// multiple origins. The ultimate goal here is to generate tables to view
// in the UI, so we skip the intermediate representations, but we could
// add them later.
connections := []Row{}
for _, id := range n.Origins {
if table, ok := OriginTable(r, id); ok {
tables = append(tables, table)
} else if _, ok := r.Endpoint.NodeMetadatas[id]; ok {
connections = append(connections, connectionDetailsRows(r.Endpoint, id)...)
} else if _, ok := r.Address.NodeMetadatas[id]; ok {
connections = append(connections, connectionDetailsRows(r.Address, id)...)
}
}
addConnectionsTable(&tables, connections, r, n)
// Sort tables by rank
sort.Sort(tables)
return DetailedNode{
ID: n.ID,
LabelMajor: n.LabelMajor,
LabelMinor: n.LabelMinor,
Pseudo: n.Pseudo,
Tables: tables,
}
}
func addConnectionsTable(tables *tables, connections []Row, r report.Report, n RenderableNode) {
sec := r.Window.Seconds()
rate := func(u *uint64) (float64, bool) {
if u == nil {
@@ -96,59 +125,31 @@ func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
}
}
tables := tables{}
{
rows := []Row{}
if n.EdgeMetadata.MaxConnCountTCP != nil {
rows = append(rows, Row{"TCP connections", strconv.FormatUint(*n.EdgeMetadata.MaxConnCountTCP, 10), "", false})
}
if rate, ok := rate(n.EdgeMetadata.EgressPacketCount); ok {
rows = append(rows, Row{"Egress packet rate", fmt.Sprintf("%.0f", rate), "packets/sec", false})
}
if rate, ok := rate(n.EdgeMetadata.IngressPacketCount); ok {
rows = append(rows, Row{"Ingress packet rate", fmt.Sprintf("%.0f", rate), "packets/sec", false})
}
if rate, ok := rate(n.EdgeMetadata.EgressByteCount); ok {
s, unit := shortenByteRate(rate)
rows = append(rows, Row{"Egress byte rate", s, unit, false})
}
if rate, ok := rate(n.EdgeMetadata.IngressByteCount); ok {
s, unit := shortenByteRate(rate)
rows = append(rows, Row{"Ingress byte rate", s, unit, false})
}
if len(rows) > 0 {
tables = append(tables, Table{"Connections", true, connectionsRank, rows})
}
rows := []Row{}
if n.EdgeMetadata.MaxConnCountTCP != nil {
rows = append(rows, Row{"TCP connections", strconv.FormatUint(*n.EdgeMetadata.MaxConnCountTCP, 10), "", false})
}
// RenderableNode may be the result of merge operation(s), and so may have
// multiple origins. The ultimate goal here is to generate tables to view
// in the UI, so we skip the intermediate representations, but we could
// add them later.
connections := []Row{}
for _, id := range n.Origins {
if table, ok := OriginTable(r, id); ok {
tables = append(tables, table)
} else if _, ok := r.Endpoint.NodeMetadatas[id]; ok {
connections = append(connections, connectionDetailsRows(r.Endpoint, id)...)
} else if _, ok := r.Address.NodeMetadatas[id]; ok {
connections = append(connections, connectionDetailsRows(r.Address, id)...)
}
if rate, ok := rate(n.EdgeMetadata.EgressPacketCount); ok {
rows = append(rows, Row{"Egress packet rate", fmt.Sprintf("%.0f", rate), "packets/sec", false})
}
if rate, ok := rate(n.EdgeMetadata.IngressPacketCount); ok {
rows = append(rows, Row{"Ingress packet rate", fmt.Sprintf("%.0f", rate), "packets/sec", false})
}
if rate, ok := rate(n.EdgeMetadata.EgressByteCount); ok {
s, unit := shortenByteRate(rate)
rows = append(rows, Row{"Egress byte rate", s, unit, false})
}
if rate, ok := rate(n.EdgeMetadata.IngressByteCount); ok {
s, unit := shortenByteRate(rate)
rows = append(rows, Row{"Ingress byte rate", s, unit, false})
}
if len(connections) > 0 {
sort.Sort(rows(connections))
tables = append(tables, connectionDetailsTable(connections))
sort.Sort(sortableRows(connections))
rows = append(rows, Row{Key: "Client", ValueMajor: "Server", Expandable: true})
rows = append(rows, connections...)
}
// Sort tables by rank
sort.Sort(tables)
return DetailedNode{
ID: n.ID,
LabelMajor: n.LabelMajor,
LabelMinor: n.LabelMinor,
Pseudo: n.Pseudo,
Tables: tables,
if len(rows) > 0 {
*tables = append(*tables, Table{"Connections", true, connectionsRank, rows})
}
}
@@ -195,6 +196,7 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row {
rows = append(rows, Row{
Key: local,
ValueMajor: remote,
Expandable: true,
})
}
// Next, scan the topology for incoming connections to this node.
@@ -216,20 +218,12 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row {
rows = append(rows, Row{
Key: remote,
ValueMajor: local,
Expandable: true,
})
}
return rows
}
func connectionDetailsTable(connectionRows []Row) Table {
return Table{
Title: "Connection Details",
Numeric: false,
Rows: append([]Row{{Key: "Client", ValueMajor: "Server"}}, connectionRows...),
Rank: endpointRank,
}
}
func processOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{

View File

@@ -54,19 +54,6 @@ func TestMakeDetailedHostNode(t *testing.T) {
LabelMinor: "hostname.com",
Pseudo: false,
Tables: []render.Table{
{
Title: "Connections",
Numeric: true,
Rank: 100,
Rows: []render.Row{
{
Key: "TCP connections",
ValueMajor: "3",
ValueMinor: "",
Expandable: false,
},
},
},
{
Title: "Origin Host",
Numeric: false,
@@ -76,38 +63,40 @@ func TestMakeDetailedHostNode(t *testing.T) {
Key: "Host name",
ValueMajor: "client.hostname.com",
ValueMinor: "",
Expandable: false,
},
{
Key: "Load",
ValueMajor: "0.01 0.01 0.01",
ValueMinor: "",
Expandable: false,
},
{
Key: "Operating system",
ValueMajor: "Linux",
ValueMinor: "",
Expandable: false,
},
},
},
{
Title: "Connection Details",
Numeric: false,
Title: "Connections",
Numeric: true,
Rank: 0,
Rows: []render.Row{
{
Key: "TCP connections",
ValueMajor: "3",
ValueMinor: "",
},
{
Key: "Client",
ValueMajor: "Server",
ValueMinor: "",
Expandable: false,
Expandable: true,
},
{
Key: "10.10.10.20",
ValueMajor: "192.168.1.1",
ValueMinor: "",
Expandable: false,
Expandable: true,
},
},
},
@@ -127,15 +116,6 @@ func TestMakeDetailedContainerNode(t *testing.T) {
LabelMinor: test.ServerHostName,
Pseudo: false,
Tables: []render.Table{
{
Title: "Connections",
Numeric: true,
Rank: 100,
Rows: []render.Row{
{"Egress packet rate", "105", "packets/sec", false},
{"Egress byte rate", "1.0", "KBps", false},
},
},
{
Title: "Origin Container",
Numeric: false,
@@ -166,45 +146,48 @@ func TestMakeDetailedContainerNode(t *testing.T) {
},
},
{
Title: "Connection Details",
Numeric: false,
Title: "Connections",
Numeric: true,
Rank: 0,
Rows: []render.Row{
{"Client", "Server", "", false},
{"Egress packet rate", "105", "packets/sec", false},
{"Egress byte rate", "1.0", "KBps", false},
{"Client", "Server", "", true},
{
fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
false,
true,
},
{
fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
false,
true,
},
{
fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
false,
true,
},
{
fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
false,
true,
},
{
fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
false,
true,
},
{
fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345),
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
"",
false,
true,
},
},
},