diff --git a/webapp/lib/echocont.go b/webapp/lib/echocont.go index 8af873ab..bd82db86 100644 --- a/webapp/lib/echocont.go +++ b/webapp/lib/echocont.go @@ -41,8 +41,6 @@ func ExtractEchoRespData(resp string, d *model.EchoItem, start time.Time) { // store current epoch in ms d.Inserted = time.Now().UnixNano() / 1e6 - log.Info("resp response", "content", resp) - var data = make(map[string]float32) // -1 if no match for response time, indicating response timeout or packets out of order data["response_time"] = -1 diff --git a/webapp/models/traceroute.go b/webapp/models/traceroute.go index 5dd47d18..eafd686d 100644 --- a/webapp/models/traceroute.go +++ b/webapp/models/traceroute.go @@ -138,10 +138,10 @@ func createTracerouteTable() error { CAddr TEXT, SIa TEXT, SAddr TEXT, - Timeout REAL, - CmdOutput TEXT, - Error TEXT, - Path TEXT + Timeout REAL, + CmdOutput TEXT, + Error TEXT, + Path TEXT ); ` _, err := db.Exec(sqlCreateTable) @@ -153,13 +153,13 @@ func createTrHopTable() error { CREATE TABLE IF NOT EXISTS trhops( Inserted BIGINT NOT NULL PRIMARY KEY, RunTimeKey BIGINT, - Ord INT, - HopIa TEXT, - HopAddr TEXT, + Ord INT, + HopIa TEXT, + HopAddr TEXT, IntfID INT, - RespTime1 REAL, - RespTime2 REAL, - RespTime3 REAL + RespTime1 REAL, + RespTime2 REAL, + RespTime3 REAL ); ` _, err := db.Exec(sqlCreateTable) @@ -170,16 +170,16 @@ func createTrHopTable() error { func StoreTracerouteItem(tr *TracerouteItem) error { sqlInsert := ` INSERT INTO traceroute( - Inserted, + Inserted, ActualDuration, CIa, CAddr, SIa, SAddr, - Timeout, - CmdOutput, - Error, - Path + Timeout, + CmdOutput, + Error, + Path ) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` stmt, err := db.Prepare(sqlInsert) @@ -206,15 +206,15 @@ func StoreTracerouteItem(tr *TracerouteItem) error { func StoreTrHopItem(hop *TrHopItem) error { sqlInsert := ` INSERT INTO trhops( - Inserted, + Inserted, RunTimeKey, Ord, - HopIa, - HopAddr, + HopIa, + HopAddr, IntfID, - RespTime1, - RespTime2, - RespTime3 + RespTime1, + RespTime2, + RespTime3 ) values(?, ?, ?, ?, ?, ?, ?, ?, ?) ` stmt, err := db.Prepare(sqlInsert) @@ -239,18 +239,18 @@ func StoreTrHopItem(hop *TrHopItem) error { // ReadTracerouteItemsAll operates on the DB to return all traceroute rows. func ReadTracerouteItemsAll() ([]TracerouteItem, error) { sqlReadAll := ` - SELECT - Inserted, - ActualDuration, - CIa, - CAddr, - SIa, - SAddr, - Timeout, - CmdOutput, - Error, - Path - FROM traceroute + SELECT + Inserted, + ActualDuration, + CIa, + CAddr, + SIa, + SAddr, + Timeout, + CmdOutput, + Error, + Path + FROM traceroute ORDER BY datetime(Inserted) DESC ` rows, err := db.Query(sqlReadAll) @@ -285,36 +285,36 @@ func ReadTracerouteItemsAll() ([]TracerouteItem, error) { // which are more recent than the 'since' epoch in ms. func ReadTracerouteItemsSince(since string) ([]TracerouteGraph, error) { sqlReadSince := ` - SELECT - a.Inserted, - a.ActualDuration, - h.Ord, - h.HopIa, - h.HopAddr, - h.IntfID, - h.RespTime1, - h.RespTime2, - h.RespTime3, - a.CmdOutput, - a.Error, - a.Path - FROM ( - SELECT - Inserted, - ActualDuration, - CIa, - CAddr, - SIa, - SAddr, - Timeout, - CmdOutput, - Error, - Path - FROM traceroute - WHERE Inserted > ? - ) AS a - INNER JOIN trhops AS h ON a.Inserted = h.RunTimeKey - ORDER BY datetime(a.Inserted) DESC + SELECT + a.Inserted, + a.ActualDuration, + h.Ord, + h.HopIa, + h.HopAddr, + h.IntfID, + h.RespTime1, + h.RespTime2, + h.RespTime3, + a.CmdOutput, + a.Error, + a.Path + FROM ( + SELECT + Inserted, + ActualDuration, + CIa, + CAddr, + SIa, + SAddr, + Timeout, + CmdOutput, + Error, + Path + FROM traceroute + WHERE Inserted > ? + ) AS a + INNER JOIN trhops AS h ON a.Inserted = h.RunTimeKey + ORDER BY datetime(a.Inserted) DESC ` rows, err := db.Query(sqlReadSince, since) if err != nil { diff --git a/webapp/tests/asviz/bwtester-d.json b/webapp/tests/asviz/bwtester-d.json new file mode 100644 index 00000000..cbc37ce1 --- /dev/null +++ b/webapp/tests/asviz/bwtester-d.json @@ -0,0 +1,16 @@ +{ + "graph": [ + { + "Inserted": 1562683731800, + "ActualDuration": 5041, + "CSBandwidth": 80000, + "CSThroughput": 80000, + "SCBandwidth": 80000, + "SCThroughput": 80000, + "Error": "", + "Path": "Hops: [1-ff00:0:111 103>4094 1-ff00:0:112] Mtu: 1450", + "Log": "t=2019-07-09T10:48:46-0400 lvl=dbug msg=\"Path selection algorithm choice\" path=\"Hops: [1-ff00:0:111 103>4094 1-ff00:0:112] Mtu: 1450\" score=0.993\nt=2019-07-09T10:48:46-0400 lvl=dbug msg=\"Registered with dispatcher\" addr=\"1-ff00:0:111,[127.0.0.1]:30001 (UDP)\"\nClient DC \tNext Hop [127.0.0.74]:31070\tServer Host [127.0.0.2]:30101\nt=2019-07-09T10:48:46-0400 lvl=dbug msg=\"Registered with dispatcher\" addr=\"1-ff00:0:111,[127.0.0.1]:30002 (UDP)\"\n\nTest parameters:\nclientDCAddr -> serverDCAddr 1-ff00:0:111,[127.0.0.1]:30002 (UDP) -> 1-ff00:0:112,[127.0.0.2]:30101 (UDP)\nclient->server: 3 seconds, 1000 bytes, 30 packets\nserver->client: 3 seconds, 1000 bytes, 30 packets\n\nS->C results\nAttempted bandwidth: 80000 bps / 0.08 Mbps\nAchieved bandwidth: 80000 bps / 0.08 Mbps\nLoss rate: 0 %\nInterarrival time variance: 23ms, average interarrival time: 103ms\nInterarrival time min: 82ms, interarrival time max: 127ms\nWe need to sleep for 2 seconds before we can get the results\n\nC->S results\nAttempted bandwidth: 80000 bps / 0.08 Mbps\nAchieved bandwidth: 80000 bps / 0.08 Mbps\nLoss rate: 0 %\nInterarrival time variance: 23ms, average interarrival time: 103ms\nInterarrival time min: 82ms, interarrival time max: 126ms\n" + } + ], + "active": false +} diff --git a/webapp/tests/asviz/echo-d.json b/webapp/tests/asviz/echo-d.json new file mode 100644 index 00000000..2204c9e7 --- /dev/null +++ b/webapp/tests/asviz/echo-d.json @@ -0,0 +1,15 @@ +{ + "graph": [ + { + "Inserted": 1562683883823, + "ActualDuration": 263, + "ResponseTime": 13.352, + "RunTime": 226.525, + "PktLoss": 0, + "CmdOutput": "Using path:\n Hops: [1-ff00:0:111 103>4094 1-ff00:0:112] Mtu: 1450\n104 bytes from 1-ff00:0:112,[127.0.0.2] scmp_seq=0 time=13.352ms\n\n--- 1-ff00:0:112,[[127.0.0.2]] statistics ---\n1 packets transmitted, 1 received, 0% packet loss, time 226.525ms\n", + "Path": "Hops: [1-ff00:0:111 103>4094 1-ff00:0:112] Mtu: 1450", + "Error": "" + } + ], + "active": false +} diff --git a/webapp/tests/asviz/traceroute-d.json b/webapp/tests/asviz/traceroute-d.json new file mode 100644 index 00000000..d9fbd634 --- /dev/null +++ b/webapp/tests/asviz/traceroute-d.json @@ -0,0 +1,54 @@ +{ + "graph": [ + { + "Inserted": 1570192000394, + "ActualDuration": 35, + "TrHops": [ + { + "HopIa": "1-ff00:0:111", + "HopAddr": "127.0.0.17", + "IntfID": 41, + "RespTime1": 0.558, + "RespTime2": 0.439, + "RespTime3": 0.41 + }, + { + "HopIa": "1-ff00:0:110", + "HopAddr": "127.0.0.9", + "IntfID": 1, + "RespTime1": 0.577, + "RespTime2": 0.597, + "RespTime3": 0.601 + }, + { + "HopIa": "1-ff00:0:110", + "HopAddr": "127.0.0.10", + "IntfID": 2, + "RespTime1": 0.797, + "RespTime2": 0.666, + "RespTime3": 0.771 + }, + { + "HopIa": "1-ff00:0:112", + "HopAddr": "127.0.0.25", + "IntfID": 1, + "RespTime1": 1.18, + "RespTime2": 1.922, + "RespTime3": 1.322 + }, + { + "HopIa": "1-ff00:0:112", + "HopAddr": "127.0.0.2", + "IntfID": -1, + "RespTime1": 1.107, + "RespTime2": 0.899, + "RespTime3": 0.835 + } + ], + "CmdOutput": "Available paths to 1-ff00:0:112\n[ 0] Hops: [1-ff00:0:111 41>1 1-ff00:0:110 2>1 1-ff00:0:112] Mtu: 1280\nChoose path: Using path:\n Hops: [1-ff00:0:111 41>1 1-ff00:0:110 2>1 1-ff00:0:112] Mtu: 1280\n0 1-ff00:0:111,[127.0.0.17] IfID=41 558µs 439µs 410µs\n1 1-ff00:0:110,[127.0.0.9] IfID=1 577µs 597µs 601µs\n2 1-ff00:0:110,[127.0.0.10] IfID=2 797µs 666µs 771µs\n3 1-ff00:0:112,[127.0.0.25] IfID=1 1.18ms 1.922ms 1.322ms\n4 1-ff00:0:112,[127.0.0.2] 1.107ms 899µs 835µs\n", + "Error": "", + "Path": "Hops: [1-ff00:0:111 41>1 1-ff00:0:110 2>1 1-ff00:0:112] Mtu: 1280" + } + ], + "active": false +} diff --git a/webapp/web/static/js/asviz.js b/webapp/web/static/js/asviz.js index e8ccb783..21875911 100644 --- a/webapp/web/static/js/asviz.js +++ b/webapp/web/static/js/asviz.js @@ -68,7 +68,9 @@ function setPaths(type, idx, open) { } else if (type == 'UP') { addSegments(resUp, idx, num, colorSegUp, type); } else if (type == 'PATH') { - addPaths(resPath, idx, num, colorPaths, type); + var latencies = getPathLatencyMin(formatPathString(resPath, idx, + type)); + addPaths(resPath, idx, num, colorPaths, type, latencies); } self.segType = type; self.segNum = idx; @@ -108,12 +110,26 @@ function formatPathString(res, idx, type) { return path; } +function formatPathStringAll(res, type) { + var paths = ""; + for (var i = 0; i < res.if_lists.length; i++) { + var path = formatPathString(res, i, type); + if (path != "") { + if (i > 0) { + paths += ","; + } + paths += formatPathString(res, i, type); + } + } + return paths; +} + /* * Adds D3 forwarding path links with arrows and a title to paths graph. */ -function addPaths(res, idx, num, color, type) { +function addPaths(res, idx, num, color, type, latencies) { if (graphPath) { - drawPath(res, idx, color); + drawPath(res, idx, color, latencies); if (res.if_lists[idx].expTime) { drawTitle(type + ' ' + num, color, res.if_lists[idx].expTime); } else { diff --git a/webapp/web/static/js/tab-paths.js b/webapp/web/static/js/tab-paths.js index cde34432..e7a74eb5 100644 --- a/webapp/web/static/js/tab-paths.js +++ b/webapp/web/static/js/tab-paths.js @@ -17,7 +17,14 @@ var iaLabels; var iaLocations = []; var iaGeoLoc; var g = {}; -var jPathColors = []; +var jPathsAvailable = {}; + +var STAT = { + LAST : 0, + AVG : 1, + MIN : 2, + MAX : 3, +} function setupDebug(src, dst) { var src = $('#ia_cli').val(); @@ -40,12 +47,136 @@ function path_colors(n) { } function getPathColor(hops) { - var idx = jPathColors.indexOf(hops + ''); - if (idx < 0) { + if (!jPathsAvailable[hops]) { return cMissingPath; } else { - return path_colors(idx); + return jPathsAvailable[hops].color; + } +} + +/** + * Updates statistics. + */ +function updateStats(fStat, oldStat) { + var newStat = {} + newStat.Last = fStat; + newStat.Num = oldStat ? (oldStat.Num + 1) : 1; + newStat.Avg = oldStat ? (((oldStat.Avg * oldStat.Num) + fStat) / newStat.Num) + : fStat; + newStat.Min = oldStat ? Math.min(fStat, oldStat.Min) : fStat; + newStat.Max = oldStat ? Math.max(fStat, oldStat.Max) : fStat; + return newStat; +} + +function getPathLatencyLast(hops) { + return getPathLatency(hops, STAT.LAST); +} + +function getPathLatencyAvg(hops) { + return getPathLatency(hops, STAT.AVG); +} + +function getPathLatencyMin(hops) { + return getPathLatency(hops, STAT.MIN); +} + +function getPathLatencyMax(hops) { + return getPathLatency(hops, STAT.MAX); +} + +function getLatencyStat(latency, type) { + switch (type) { + case STAT.LAST: + return latency.Last; + case STAT.AVG: + return latency.Avg; + case STAT.MIN: + return latency.Min; + case STAT.MAX: + return latency.Max; + } +} + +function formatLatency(lat) { + // ignore 1ms negative margin of error + if (lat > -1 && lat < 0) { + return 0; + } else { + return parseFloat(lat).toFixed(0); + } +} + +/** + * Returns array of interface and full path latency stats. + */ +function getPathLatency(hops, type) { + var path = {}; + if (jPathsAvailable[hops]) { + path = jPathsAvailable[hops]; + } + var latencies = []; + for (var i = 0; i < path.interfaces.length; i++) { + if (path.interfaces[i].latency) { + latencies.push(getLatencyStat(path.interfaces[i].latency, type)); + } else { + latencies.push(undefined); + } + } + if (path.latency) { + latencies.push(getLatencyStat(path.latency, type)); + } else { + latencies.push(undefined); + } + return latencies; +} + +function setEchoLatency(hops, latency) { + var path = {}; + if (jPathsAvailable[hops]) { + path = jPathsAvailable[hops]; } + if (latency > 0) { + path.latency = updateStats(latency, path.latency); + jPathsAvailable[hops] = path; + } + return path; +} + +function setTracerouteLatency(hops, interfaces) { + var path = {}; + if (jPathsAvailable[hops]) { + path = jPathsAvailable[hops]; + } + for (var i = 0; i < interfaces.length; i++) { + var if_ = interfaces[i]; + if (i < interfaces.length - 1) { + path.interfaces[i].addr = if_.HopAddr; + if (if_.RespTime1 > 0) { + path.interfaces[i].latency = updateStats(if_.RespTime1, + path.interfaces[i].latency); + } + if (if_.RespTime2 > 0) { + path.interfaces[i].latency = updateStats(if_.RespTime2, + path.interfaces[i].latency); + } + if (if_.RespTime3 > 0) { + path.interfaces[i].latency = updateStats(if_.RespTime3, + path.interfaces[i].latency); + } + } else { + if (if_.RespTime1 > 0) { + path.latency = updateStats(if_.RespTime1, path.latency); + } + if (if_.RespTime2 > 0) { + path.latency = updateStats(if_.RespTime2, path.latency); + } + if (if_.RespTime3 > 0) { + path.latency = updateStats(if_.RespTime3, path.latency); + } + } + } + jPathsAvailable[hops] = path; + return path; } function isConfigComplete(data, textStatus, jqXHR) { @@ -388,17 +519,27 @@ function get_path_html(paths, csegs, usegs, dsegs, show_segs) { var exp = new Date(entry.Expiry); if_ = entry.Hops; var hops = if_.length / 2; - var hcolor = getPathColor(formatPathJson(paths, parseInt(p))); + + var pathStr = formatPathJson(paths, parseInt(p)); + var hcolor = getPathColor(pathStr); var style = `style='background-color: ${hcolor}; '`; - html += `
  • - PATH ${parseInt(p)+1} - - ${hops} -