github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/diag.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "strings" 8 "text/template" 9 "time" 10 11 cmds "github.com/ipfs/go-ipfs/commands" 12 diag "github.com/ipfs/go-ipfs/diagnostics" 13 ) 14 15 type DiagnosticConnection struct { 16 ID string 17 // TODO use milliseconds or microseconds for human readability 18 NanosecondsLatency uint64 19 Count int 20 } 21 22 var ( 23 visD3 = "d3" 24 visDot = "dot" 25 visFmts = []string{visD3, visDot} 26 ) 27 28 type DiagnosticPeer struct { 29 ID string 30 UptimeSeconds uint64 31 BandwidthBytesIn uint64 32 BandwidthBytesOut uint64 33 Connections []DiagnosticConnection 34 } 35 36 type DiagnosticOutput struct { 37 Peers []DiagnosticPeer 38 } 39 40 var DefaultDiagnosticTimeout = time.Second * 20 41 42 var DiagCmd = &cmds.Command{ 43 Helptext: cmds.HelpText{ 44 Tagline: "Generates diagnostic reports", 45 }, 46 47 Subcommands: map[string]*cmds.Command{ 48 "net": diagNetCmd, 49 }, 50 } 51 52 var diagNetCmd = &cmds.Command{ 53 Helptext: cmds.HelpText{ 54 Tagline: "Generates a network diagnostics report", 55 ShortDescription: ` 56 Sends out a message to each node in the network recursively 57 requesting a listing of data about them including number of 58 connected peers and latencies between them. 59 60 The given timeout will be decremented 2s at every network hop, 61 ensuring peers try to return their diagnostics before the initiator's 62 timeout. If the timeout is too small, some peers may not be reached. 63 30s and 60s are reasonable timeout values, though network vary. 64 The default timeout is 20 seconds. 65 66 The 'vis' option may be used to change the output format. 67 four formats are supported: 68 * plain text - easy to read 69 * d3 - json ready to be fed into d3view 70 * dot - graphviz format 71 72 The d3 format will output a json object ready to be consumed by 73 the chord network viewer, available at the following hash: 74 75 /ipfs/QmbesKpGyQGd5jtJFUGEB1ByPjNFpukhnKZDnkfxUiKn38 76 77 To view your diag output, 'ipfs add' the d3 vis output, and 78 open the following link: 79 80 http://gateway.ipfs.io/ipfs/QmbesKpGyQGd5jtJFUGEB1ByPjNFpukhnKZDnkfxUiKn38/chord#<your hash> 81 82 The dot format can be fed into graphviz and other programs 83 that consume the dot format to generate graphs of the network. 84 `, 85 }, 86 87 Options: []cmds.Option{ 88 cmds.StringOption("vis", "output vis. one of: "+strings.Join(visFmts, ", ")), 89 }, 90 91 Run: func(req cmds.Request, res cmds.Response) { 92 n, err := req.InvocContext().GetNode() 93 if err != nil { 94 res.SetError(err, cmds.ErrNormal) 95 return 96 } 97 98 if !n.OnlineMode() { 99 res.SetError(errNotOnline, cmds.ErrClient) 100 return 101 } 102 103 vis, _, err := req.Option("vis").String() 104 if err != nil { 105 res.SetError(err, cmds.ErrNormal) 106 return 107 } 108 109 timeoutS, _, err := req.Option("timeout").String() 110 if err != nil { 111 res.SetError(err, cmds.ErrNormal) 112 return 113 } 114 timeout := DefaultDiagnosticTimeout 115 if timeoutS != "" { 116 t, err := time.ParseDuration(timeoutS) 117 if err != nil { 118 res.SetError(errors.New("error parsing timeout"), cmds.ErrNormal) 119 return 120 } 121 timeout = t 122 } 123 124 info, err := n.Diagnostics.GetDiagnostic(req.Context(), timeout) 125 if err != nil { 126 res.SetError(err, cmds.ErrNormal) 127 return 128 } 129 130 switch vis { 131 case visD3: 132 res.SetOutput(bytes.NewReader(diag.GetGraphJson(info))) 133 case visDot: 134 buf := new(bytes.Buffer) 135 w := diag.DotWriter{W: buf} 136 err := w.WriteGraph(info) 137 if err != nil { 138 res.SetError(err, cmds.ErrNormal) 139 return 140 } 141 res.SetOutput(io.Reader(buf)) 142 default: 143 output, err := stdDiagOutputMarshal(standardDiagOutput(info)) 144 if err != nil { 145 res.SetError(err, cmds.ErrNormal) 146 return 147 } 148 res.SetOutput(output) 149 } 150 }, 151 } 152 153 func stdDiagOutputMarshal(output *DiagnosticOutput) (io.Reader, error) { 154 buf := new(bytes.Buffer) 155 err := printDiagnostics(buf, output) 156 if err != nil { 157 return nil, err 158 } 159 return buf, nil 160 } 161 162 func standardDiagOutput(info []*diag.DiagInfo) *DiagnosticOutput { 163 output := make([]DiagnosticPeer, len(info)) 164 for i, peer := range info { 165 connections := make([]DiagnosticConnection, len(peer.Connections)) 166 for j, conn := range peer.Connections { 167 connections[j] = DiagnosticConnection{ 168 ID: conn.ID, 169 NanosecondsLatency: uint64(conn.Latency.Nanoseconds()), 170 Count: conn.Count, 171 } 172 } 173 174 output[i] = DiagnosticPeer{ 175 ID: peer.ID, 176 UptimeSeconds: uint64(peer.LifeSpan.Seconds()), 177 BandwidthBytesIn: peer.BwIn, 178 BandwidthBytesOut: peer.BwOut, 179 Connections: connections, 180 } 181 } 182 return &DiagnosticOutput{output} 183 } 184 185 func printDiagnostics(out io.Writer, info *DiagnosticOutput) error { 186 diagTmpl := ` 187 {{ range $peer := .Peers }} 188 ID {{ $peer.ID }} up {{ $peer.UptimeSeconds }} seconds connected to {{ len .Connections }}:{{ range $connection := .Connections }} 189 ID {{ $connection.ID }} connections: {{ $connection.Count }} latency: {{ $connection.NanosecondsLatency }} ns{{ end }} 190 {{end}} 191 ` 192 193 templ, err := template.New("DiagnosticOutput").Parse(diagTmpl) 194 if err != nil { 195 return err 196 } 197 198 err = templ.Execute(out, info) 199 if err != nil { 200 return err 201 } 202 203 return nil 204 }