aggregate connection details table

List Local and Remote endpoints for each TCP connection on the node.
This commit is contained in:
Paul Bellamy
2015-06-29 14:25:02 +01:00
parent a9b53f6d76
commit cd71f82d4d
5 changed files with 119 additions and 33 deletions

View File

@@ -79,6 +79,7 @@ func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
// 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{}
outer:
for _, id := range n.Origins {
if table, ok := OriginTable(r, id); ok {
@@ -89,8 +90,13 @@ outer:
}
}
tables = append(tables, table)
} else if nmd, ok := r.Endpoint.NodeMetadatas[id]; ok {
connections = append(connections, connectionDetailsRows(r.Endpoint, id, nmd)...)
}
}
if len(connections) > 0 {
tables = append(tables, connectionDetailsTable(connections))
}
// Sort tables by rank
sort.Sort(tables)
@@ -107,9 +113,6 @@ outer:
// OriginTable produces a table (to be consumed directly by the UI) based on
// an origin ID, which is (optimistically) a node ID in one of our topologies.
func OriginTable(r report.Report, originID string) (Table, bool) {
if nmd, ok := r.Endpoint.NodeMetadatas[originID]; ok {
return endpointOriginTable(nmd)
}
if nmd, ok := r.Address.NodeMetadatas[originID]; ok {
return addressOriginTable(nmd)
}
@@ -128,22 +131,29 @@ func OriginTable(r report.Report, originID string) (Table, bool) {
return Table{}, false
}
func endpointOriginTable(nmd report.NodeMetadata) (Table, bool) {
func connectionDetailsRows(endpointTopology report.Topology, originID string, nmd report.NodeMetadata) []Row {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{"addr", "Endpoint"},
{"port", "Port"},
} {
if val, ok := nmd[tuple.key]; ok {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
local := fmt.Sprintf("%s:%s", nmd["addr"], nmd["port"])
adjacencies := endpointTopology.Adjacency[report.MakeAdjacencyID(originID)]
sort.Strings(adjacencies)
for _, adj := range adjacencies {
if _, address, port, ok := report.ParseEndpointNodeID(adj); ok {
rows = append(rows, Row{
Key: local,
ValueMajor: fmt.Sprintf("%s:%s", address, port),
})
}
}
return rows
}
func connectionDetailsTable(connectionRows []Row) Table {
return Table{
Title: "Origin Endpoint",
Title: "Connection Details",
Numeric: false,
Rows: rows,
Rows: append([]Row{{Key: "Local", ValueMajor: "Remote"}}, connectionRows...),
Rank: endpointRank,
}, len(rows) > 0
}
}
func addressOriginTable(nmd report.NodeMetadata) (Table, bool) {

View File

@@ -1,6 +1,7 @@
package render_test
import (
"fmt"
"reflect"
"testing"
@@ -13,14 +14,6 @@ func TestOriginTable(t *testing.T) {
t.Errorf("unknown origin ID gave unexpected success")
}
for originID, want := range map[string]render.Table{
test.Client54001NodeID: {
Title: "Origin Endpoint",
Numeric: false,
Rows: []render.Row{
{"Endpoint", test.ClientIP, ""},
{"Port", test.ClientPort54001, ""},
},
},
test.ClientAddressNodeID: {
Title: "Origin Address",
Numeric: false,
@@ -107,11 +100,40 @@ func TestMakeDetailedNode(t *testing.T) {
},
},
{
Title: "Origin Endpoint",
Title: "Connection Details",
Numeric: false,
Rows: []render.Row{
{"Endpoint", test.ServerIP, ""},
{"Port", test.ServerPort, ""},
{"Local", "Remote", ""},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54010),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.UnknownClient1IP, test.ClientPort54020),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.UnknownClient3IP, test.ClientPort54020),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54001),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.ClientIP, test.ClientPort54002),
"",
},
{
fmt.Sprintf("%s:%s", test.ServerIP, test.ServerPort),
fmt.Sprintf("%s:%s", test.RandomClientIP, test.ClientPort12345),
"",
},
},
},
},

View File

@@ -103,6 +103,17 @@ func ParseNodeID(nodeID string) (hostID string, remainder string, ok bool) {
return fields[0], fields[1], true
}
// ParseEndpointNodeID produces the host ID, address, and port and remainder
// (typically an address) from an endpoint node ID. Note that hostID may be
// blank.
func ParseEndpointNodeID(endpointNodeID string) (hostID, address, port string, ok bool) {
fields := strings.SplitN(endpointNodeID, ScopeDelim, 3)
if len(fields) != 3 {
return "", "", "", false
}
return fields[0], fields[1], fields[2], true
}
// ExtractHostID extracts the host id from NodeMetadata
func ExtractHostID(m NodeMetadata) string {
hostid, _, _ := ParseNodeID(m[HostNodeID])

View File

@@ -75,6 +75,42 @@ func TestAdjacencyID(t *testing.T) {
}
}
func TestEndpointNodeID(t *testing.T) {
for _, bad := range []string{
clientAddressNodeID,
serverAddressNodeID,
unknownAddressNodeID,
clientHostNodeID,
serverHostNodeID,
"host.com;1.2.3.4",
"a;b",
"a;",
";b",
";",
"",
} {
if haveName, haveAddress, havePort, ok := report.ParseEndpointNodeID(bad); ok {
t.Errorf("%q: expected failure, but got {%q, %q, %q}", bad, haveName, haveAddress, havePort)
}
}
for input, want := range map[string]struct{ name, address, port string }{
report.MakeEndpointNodeID("host.com", "1.2.3.4", "c"): {"", "1.2.3.4", "c"},
"a;b;c": {"a", "b", "c"},
} {
haveName, haveAddress, havePort, ok := report.ParseEndpointNodeID(input)
if !ok {
t.Errorf("%q: not OK", input)
continue
}
if want.name != haveName ||
want.address != haveAddress ||
want.port != havePort {
t.Errorf("%q: want %q, have {%q, %q, %q}", input, want, haveName, haveAddress, havePort)
}
}
}
func TestEdgeID(t *testing.T) {
for _, bad := range []string{
client54001EndpointNodeID,

View File

@@ -12,11 +12,18 @@ var (
ServerHostID = "server.hostname.com"
UnknownHostID = ""
ClientIP = "10.10.10.20"
ServerIP = "192.168.1.1"
ClientPort54001 = "54001"
ClientPort54002 = "54002"
ServerPort = "80"
ClientIP = "10.10.10.20"
ServerIP = "192.168.1.1"
ClientPort54001 = "54001"
ClientPort54010 = "54010"
ClientPort54002 = "54002"
ClientPort54020 = "54020"
ClientPort12345 = "12345"
ServerPort = "80"
UnknownClient1IP = "10.10.10.10"
UnknownClient2IP = "10.10.10.10"
UnknownClient3IP = "10.10.10.11"
RandomClientIP = "51.52.53.54"
ClientHostName = ClientHostID
ServerHostName = ServerHostID
@@ -32,10 +39,10 @@ var (
Client54001NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54001) // curl (1)
Client54002NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54002) // curl (2)
Server80NodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, ServerPort) // apache
UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, "10.10.10.10", "54010") // we want to ensure two unknown clients, connnected
UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, "10.10.10.10", "54020") // to the same server, are deduped.
UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, "10.10.10.11", "54020") // Check this one isn't deduped
RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, "51.52.53.54", "12345") // this should become an internet node
UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient1IP, "54010") // we want to ensure two unknown clients, connnected
UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient2IP, "54020") // to the same server, are deduped.
UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient3IP, "54020") // Check this one isn't deduped
RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, RandomClientIP, "12345") // this should become an internet node
ClientProcess1NodeID = report.MakeProcessNodeID(ClientHostID, Client1PID)
ClientProcess2NodeID = report.MakeProcessNodeID(ClientHostID, Client2PID)