5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-22 14:10:28 +00:00

reorganize and add more to the admin

This commit is contained in:
Arceliar 2018-01-29 18:48:14 -06:00
parent fe8a78f966
commit 2dd8152a0c

View File

@ -5,6 +5,7 @@ import "os"
import "bytes" import "bytes"
import "fmt" import "fmt"
import "sort" import "sort"
import "strings"
// TODO? Make all of this JSON // TODO? Make all of this JSON
// TODO: Add authentication // TODO: Add authentication
@ -12,11 +13,45 @@ import "sort"
type admin struct { type admin struct {
core *Core core *Core
listenaddr string listenaddr string
handlers []admin_handlerInfo
}
type admin_handlerInfo struct {
name string // Checked against the first word of the api call
args []string // List of human-readable argument names
handler func(*[]byte, ...string) // First arg is pointer to the out slice, rest is args
}
func (a *admin) addHandler(name string, args []string, handler func(*[]byte, ...string)) {
a.handlers = append(a.handlers, admin_handlerInfo{name, args, handler})
} }
func (a *admin) init(c *Core, listenaddr string) { func (a *admin) init(c *Core, listenaddr string) {
a.core = c a.core = c
a.listenaddr = listenaddr a.listenaddr = listenaddr
a.addHandler("help", nil, func(out *[]byte, _ ...string) {
for _, handler := range a.handlers {
tmp := append([]string{handler.name}, handler.args...)
*out = append(*out, []byte(strings.Join(tmp, " "))...)
*out = append(*out, "\n"...)
}
})
// TODO? have other parts of the program call to add their own handlers
a.addHandler("dot", nil, func(out *[]byte, _ ...string) {
*out = a.getResponse_dot()
})
a.addHandler("getSelf", nil, func(out *[]byte, _ ...string) {
*out = []byte(a.printInfos(a.getData_getPeers()))
})
a.addHandler("getPeers", nil, func(out *[]byte, _ ...string) {
*out = []byte(a.printInfos(a.getData_getPeers()))
})
a.addHandler("getDHT", nil, func(out *[]byte, _ ...string) {
*out = []byte(a.printInfos(a.getData_getDHT()))
})
a.addHandler("getSessions", nil, func(out *[]byte, _ ...string) {
*out = []byte(a.printInfos(a.getData_getSessions()))
})
go a.listen() go a.listen()
} }
@ -44,80 +79,164 @@ func (a *admin) handleRequest(conn net.Conn) {
conn.Close() conn.Close()
return return
} }
var out []byte
buf = bytes.Trim(buf, "\x00\r\n\t") buf = bytes.Trim(buf, "\x00\r\n\t")
switch string(buf) { call := strings.Split(string(buf), " ")
case "dot": var cmd string
const mDepth = 1024 var args []string
m := make(map[[mDepth]switchPort]string) if len(call) > 0 {
table := a.core.switchTable.table.Load().(lookupTable) cmd = call[0]
peers := a.core.peers.ports.Load().(map[switchPort]*peer) args = call[1:]
// Add my own entry
peerID := address_addrForNodeID(getNodeID(&peers[0].box))
myAddr := net.IP(peerID[:]).String()
var index [mDepth]switchPort
copy(index[:mDepth], table.self.coords[:])
m[index] = myAddr
// Connect switch table entries to peer entries
for _, tableentry := range table.elems {
for _, peerentry := range peers {
if peerentry.port == tableentry.port {
peerID := address_addrForNodeID(getNodeID(&peerentry.box))
addr := net.IP(peerID[:]).String()
var index [mDepth]switchPort
copy(index[:], tableentry.locator.coords)
m[index] = addr
} }
} done := false
} for _, handler := range a.handlers {
if cmd == handler.name {
getPorts := func(coords []byte) []switchPort { handler.handler(&out, args...)
var ports []switchPort done = true
for offset := 0; ; {
coord, length := wire_decode_uint64(coords[offset:])
if length == 0 {
break break
} }
ports = append(ports, switchPort(coord))
offset += length
} }
return ports if !done {
out = []byte("I didn't understand that!\n")
}
_, err = conn.Write(out)
if err != nil {
a.core.log.Printf("Admin socket error: %v", err)
}
conn.Close()
} }
// Look up everything we know from DHT // Maps things like "IP", "port", "bucket", or "coords" onto strings
type admin_pair struct {
key string
val string
}
type admin_nodeInfo []admin_pair
func (n *admin_nodeInfo) asMap() map[string]string {
m := make(map[string]string, len(*n))
for _, p := range *n {
m[p.key] = p.val
}
return m
}
func (n *admin_nodeInfo) toString() string {
// TODO return something nicer looking than this
var out []string
for _, p := range *n {
out = append(out, fmt.Sprintf("%v: %v", p.key, p.val))
}
return strings.Join(out, ", ")
return fmt.Sprint(*n)
}
func (a *admin) printInfos(infos []admin_nodeInfo) string {
var out []string
for _, info := range infos {
out = append(out, info.toString())
}
out = append(out, "") // To add a trailing "\n" in the join
return strings.Join(out, "\n")
}
func (a *admin) getData_getSelf() *admin_nodeInfo {
table := a.core.switchTable.table.Load().(lookupTable)
addr := a.core.router.addr
coords := table.self.getCoords()
self := admin_nodeInfo{
{"IP", net.IP(addr[:]).String()},
{"coords", fmt.Sprint(coords)},
}
return &self
}
func (a *admin) getData_getPeers() []admin_nodeInfo {
var peerInfos []admin_nodeInfo
table := a.core.switchTable.table.Load().(lookupTable)
peers := a.core.peers.ports.Load().(map[switchPort]*peer)
for _, elem := range table.elems {
peer, isIn := peers[elem.port]
if !isIn {
continue
}
addr := *address_addrForNodeID(getNodeID(&peer.box))
coords := elem.locator.getCoords()
info := admin_nodeInfo{
{"IP", net.IP(addr[:]).String()},
{"coords", fmt.Sprint(coords)},
{"port", fmt.Sprint(elem.port)},
}
peerInfos = append(peerInfos, info)
}
return peerInfos
}
func (a *admin) getData_getDHT() []admin_nodeInfo {
var infos []admin_nodeInfo
getDHT := func() { getDHT := func() {
for i := 0; i < a.core.dht.nBuckets(); i++ { for i := 0; i < a.core.dht.nBuckets(); i++ {
b := a.core.dht.getBucket(i) b := a.core.dht.getBucket(i)
for _, v := range b.infos { for _, v := range b.infos {
destPorts := getPorts(v.coords) addr := *address_addrForNodeID(v.getNodeID())
addr := net.IP(address_addrForNodeID(v.nodeID_hidden)[:]).String() info := admin_nodeInfo{
var index [mDepth]switchPort {"IP", net.IP(addr[:]).String()},
copy(index[:], destPorts) {"coords", fmt.Sprint(v.coords)},
m[index] = addr {"bucket", fmt.Sprint(i)},
}
infos = append(infos, info)
} }
} }
} }
a.core.router.doAdmin(getDHT) a.core.router.doAdmin(getDHT)
return infos
}
// Look up everything we know from active sessions func (a *admin) getData_getSessions() []admin_nodeInfo {
var infos []admin_nodeInfo
getSessions := func() { getSessions := func() {
for _, sinfo := range a.core.sessions.sinfos { for _, sinfo := range a.core.sessions.sinfos {
destPorts := getPorts(sinfo.coords) // TODO? skipped known but timed out sessions?
var index [mDepth]switchPort info := admin_nodeInfo{
copy(index[:], destPorts) {"IP", net.IP(sinfo.theirAddr[:]).String()},
m[index] = net.IP(sinfo.theirAddr[:]).String() {"coords", fmt.Sprint(sinfo.coords)},
}
infos = append(infos, info)
} }
} }
a.core.router.doAdmin(getSessions) a.core.router.doAdmin(getSessions)
return infos
}
func (a *admin) getResponse_dot() []byte {
self := a.getData_getSelf().asMap()
myAddr := self["IP"]
peers := a.getData_getPeers()
dht := a.getData_getDHT()
sessions := a.getData_getSessions()
// Map of coords onto IP
m := make(map[string]string)
m[self["coords"]] = self["IP"]
for _, peer := range peers {
p := peer.asMap()
m[p["coords"]] = p["IP"]
}
for _, node := range dht {
n := node.asMap()
m[n["coords"]] = n["IP"]
}
for _, node := range sessions {
n := node.asMap()
m[n["coords"]] = n["IP"]
}
// Start building a tree from all known nodes // Start building a tree from all known nodes
type nodeInfo struct { type nodeInfo struct {
name string name string
key [mDepth]switchPort key string
parent [mDepth]switchPort parent string
} }
infos := make(map[[mDepth]switchPort]nodeInfo) infos := make(map[string]nodeInfo)
// First fill the tree with all known nodes, no parents // First fill the tree with all known nodes, no parents
for k, n := range m { for k, n := range m {
infos[k] = nodeInfo{ infos[k] = nodeInfo{
@ -125,14 +244,18 @@ func (a *admin) handleRequest(conn net.Conn) {
key: k, key: k,
} }
} }
// Get coords as a slice of strings, FIXME? this looks very fragile
coordSlice := func(coords string) []string {
tmp := strings.Replace(coords, "[", "", -1)
tmp = strings.Replace(tmp, "]", "", -1)
return strings.Split(tmp, " ")
}
// Now go through and create placeholders for any missing nodes // Now go through and create placeholders for any missing nodes
for _, info := range infos { for _, info := range infos {
for idx, port := range info.key { // This is ugly string manipulation
if port == 0 { coordsSplit := coordSlice(info.key)
break for idx := range coordsSplit {
} key := fmt.Sprintf("[%v]", strings.Join(coordsSplit[:idx], " "))
var key [mDepth]switchPort
copy(key[:idx], info.key[:])
newInfo, isIn := infos[key] newInfo, isIn := infos[key]
if isIn { if isIn {
continue continue
@ -144,41 +267,36 @@ func (a *admin) handleRequest(conn net.Conn) {
} }
// Now go through and attach parents // Now go through and attach parents
for _, info := range infos { for _, info := range infos {
info.parent = info.key pSplit := coordSlice(info.key)
for idx := len(info.parent) - 1; idx >= 0; idx-- { if len(pSplit) > 0 {
if info.parent[idx] != 0 { pSplit = pSplit[:len(pSplit)-1]
info.parent[idx] = 0
break
}
} }
info.parent = fmt.Sprintf("[%v]", strings.Join(pSplit, " "))
infos[info.key] = info infos[info.key] = info
} }
// Finally, get a sorted list of keys, which we use to organize the output // Finally, get a sorted list of keys, which we use to organize the output
var keys [][mDepth]switchPort var keys []string
for _, info := range infos { for _, info := range infos {
keys = append(keys, info.key) keys = append(keys, info.key)
} }
// TODO sort
less := func(i, j int) bool { less := func(i, j int) bool {
for idx := range keys[i] { return keys[i] < keys[j]
if keys[i][idx] < keys[j][idx] {
return true
}
if keys[i][idx] > keys[j][idx] {
return false
}
}
return false
} }
sort.Slice(keys, less) sort.Slice(keys, less)
// Now print it all out // Now print it all out
conn.Write([]byte(fmt.Sprintf("digraph {\n"))) var out []byte
put := func(s string) {
out = append(out, []byte(s)...)
}
put("digraph {\n")
// First set the labels // First set the labels
for _, key := range keys { for _, key := range keys {
info := infos[key] info := infos[key]
if info.name == myAddr { if info.name == myAddr {
conn.Write([]byte(fmt.Sprintf("\"%v\" [ style = \"filled\", label = \"%v\" ];\n", info.key, info.name))) put(fmt.Sprintf("\"%v\" [ style = \"filled\", label = \"%v\" ];\n", info.key, info.name))
} else { } else {
conn.Write([]byte(fmt.Sprintf("\"%v\" [ label = \"%v\" ];\n", info.key, info.name))) put(fmt.Sprintf("\"%v\" [ label = \"%v\" ];\n", info.key, info.name))
} }
} }
// Then print the tree structure // Then print the tree structure
@ -187,23 +305,13 @@ func (a *admin) handleRequest(conn net.Conn) {
if info.key == info.parent { if info.key == info.parent {
continue continue
} // happens for the root, skip it } // happens for the root, skip it
for idx := len(info.key) - 1; idx >= 0; idx-- { coordsSplit := coordSlice(key)
port := info.key[idx] if len(coordsSplit) == 0 {
if port == 0 {
continue continue
} }
conn.Write([]byte(fmt.Sprintf(" \"%+v\" -> \"%+v\" [ label = \"%v\" ];\n", info.parent, info.key, port))) port := coordsSplit[len(coordsSplit)-1]
break put(fmt.Sprintf(" \"%+v\" -> \"%+v\" [ label = \"%v\" ];\n", info.parent, info.key, port))
} }
} put("}\n")
conn.Write([]byte(fmt.Sprintf("}\n"))) return out
break
default:
conn.Write([]byte("I didn't understand that!\n"))
}
if err != nil {
a.core.log.Printf("Admin socket error: %v", err)
}
conn.Close()
} }