github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/diagnostics/vis.go (about)

     1  package diagnostics
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  
     8  	peer "github.com/ipfs/go-ipfs/p2p/peer"
     9  	rtable "github.com/ipfs/go-ipfs/routing/kbucket"
    10  )
    11  
    12  type node struct {
    13  	Name  string `json:"name"`
    14  	Value uint64 `json:"value"`
    15  	RtKey string `json:"rtkey"`
    16  }
    17  
    18  type link struct {
    19  	Source int `json:"source"`
    20  	Target int `json:"target"`
    21  	Value  int `json:"value"`
    22  }
    23  
    24  func GetGraphJson(dinfo []*DiagInfo) []byte {
    25  	out := make(map[string]interface{})
    26  	names := make(map[string]int)
    27  	var nodes []*node
    28  	for _, di := range dinfo {
    29  		names[di.ID] = len(nodes)
    30  		val := di.BwIn + di.BwOut + 10
    31  		// include the routing table key, for proper routing table display
    32  		rtk := peer.ID(rtable.ConvertPeerID(peer.ID(di.ID))).Pretty()
    33  		nodes = append(nodes, &node{Name: di.ID, Value: val, RtKey: rtk})
    34  	}
    35  
    36  	var links []*link
    37  	linkexists := make([][]bool, len(nodes))
    38  	for i := range linkexists {
    39  		linkexists[i] = make([]bool, len(nodes))
    40  	}
    41  
    42  	for _, di := range dinfo {
    43  		myid := names[di.ID]
    44  		for _, con := range di.Connections {
    45  			thisid := names[con.ID]
    46  			if !linkexists[thisid][myid] {
    47  				links = append(links, &link{
    48  					Source: myid,
    49  					Target: thisid,
    50  					Value:  3,
    51  				})
    52  				linkexists[myid][thisid] = true
    53  			}
    54  		}
    55  	}
    56  
    57  	out["nodes"] = nodes
    58  	out["links"] = links
    59  
    60  	b, err := json.Marshal(out)
    61  	if err != nil {
    62  		panic(err)
    63  	}
    64  
    65  	return b
    66  }
    67  
    68  type DotWriter struct {
    69  	W   io.Writer
    70  	err error
    71  }
    72  
    73  // Write writes a buffer to the internal writer.
    74  // It handles errors as in: http://blog.golang.org/errors-are-values
    75  func (w *DotWriter) Write(buf []byte) (n int, err error) {
    76  	if w.err == nil {
    77  		n, w.err = w.W.Write(buf)
    78  	}
    79  	return n, w.err
    80  }
    81  
    82  // WriteS writes a string
    83  func (w *DotWriter) WriteS(s string) (n int, err error) {
    84  	return w.Write([]byte(s))
    85  }
    86  
    87  func (w *DotWriter) WriteNetHeader(dinfo []*DiagInfo) error {
    88  	label := fmt.Sprintf("Nodes: %d\\l", len(dinfo))
    89  
    90  	w.WriteS("subgraph cluster_L { ")
    91  	w.WriteS("L [shape=box fontsize=32 label=\"" + label + "\"] ")
    92  	w.WriteS("}\n")
    93  	return w.err
    94  }
    95  
    96  func (w *DotWriter) WriteNode(i int, di *DiagInfo) error {
    97  	box := "[label=\"%s\n%d conns\" fontsize=8 shape=box tooltip=\"%s (%d conns)\"]"
    98  	box = fmt.Sprintf(box, di.ID, len(di.Connections), di.ID, len(di.Connections))
    99  
   100  	w.WriteS(fmt.Sprintf("N%d %s\n", i, box))
   101  	return w.err
   102  }
   103  
   104  func (w *DotWriter) WriteEdge(i, j int, di *DiagInfo, conn connDiagInfo) error {
   105  
   106  	n := fmt.Sprintf("%s ... %s (%d)", di.ID, conn.ID, conn.Latency)
   107  	s := "[label=\" %d\" weight=%d tooltip=\"%s\" labeltooltip=\"%s\" style=\"dotted\"]"
   108  	s = fmt.Sprintf(s, conn.Latency, conn.Count, n, n)
   109  
   110  	w.WriteS(fmt.Sprintf("N%d -> N%d %s\n", i, j, s))
   111  	return w.err
   112  }
   113  
   114  func (w *DotWriter) WriteGraph(dinfo []*DiagInfo) error {
   115  	w.WriteS("digraph \"diag-net\" {\n")
   116  	w.WriteNetHeader(dinfo)
   117  
   118  	idx := make(map[string]int)
   119  	for i, di := range dinfo {
   120  		if _, found := idx[di.ID]; found {
   121  			log.Debugf("DotWriter skipped duplicate %s", di.ID)
   122  			continue
   123  		}
   124  
   125  		idx[di.ID] = i
   126  		w.WriteNode(i, di)
   127  	}
   128  
   129  	for i, di := range dinfo {
   130  		for _, conn := range di.Connections {
   131  			j, found := idx[conn.ID]
   132  			if !found { // if we didnt get it earlier...
   133  				j = len(idx)
   134  				idx[conn.ID] = j
   135  			}
   136  
   137  			w.WriteEdge(i, j, di, conn)
   138  		}
   139  	}
   140  
   141  	w.WriteS("}")
   142  	return w.err
   143  }