diff --git a/heplify-server/hom7-elastic/conf/kibana-import.json b/heplify-server/hom7-elastic/conf/kibana-import.json index cd67390..c76cbe6 100644 --- a/heplify-server/hom7-elastic/conf/kibana-import.json +++ b/heplify-server/hom7-elastic/conf/kibana-import.json @@ -1,53 +1,4 @@ [ - { - "_id": "46962380-8b96-11e8-bedf-8783a42e8fa1", - "_type": "dashboard", - "_source": { - "title": "HOMER Flows", - "hits": 0, - "description": "", - "panelsJSON": "[{\"panelIndex\":\"1\",\"gridData\":{\"x\":0,\"y\":0,\"w\":25,\"h\":23,\"i\":\"1\"},\"embeddableConfig\":{},\"id\":\"e8596200-8b95-11e8-bedf-8783a42e8fa1\",\"title\":\"IP Flows\",\"type\":\"visualization\",\"version\":\"6.3.1\"},{\"panelIndex\":\"2\",\"gridData\":{\"x\":25,\"y\":0,\"w\":23,\"h\":23,\"i\":\"2\"},\"version\":\"6.3.1\",\"type\":\"visualization\",\"id\":\"f1ffd270-8b96-11e8-bedf-8783a42e8fa1\",\"embeddableConfig\":{}}]", - "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", - "version": 1, - "timeRestore": false, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "a2800970-8b98-11e8-bedf-8783a42e8fa1", - "_type": "dashboard", - "_source": { - "title": "HOMER Stats", - "hits": 0, - "description": "HOMER Statistics", - "panelsJSON": "[{\"panelIndex\":\"1\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":8,\"i\":\"1\"},\"version\":\"6.3.1\",\"type\":\"visualization\",\"id\":\"8625bc70-8b98-11e8-bedf-8783a42e8fa1\",\"embeddableConfig\":{}},{\"panelIndex\":\"2\",\"gridData\":{\"x\":0,\"y\":8,\"w\":48,\"h\":9,\"i\":\"2\"},\"version\":\"6.3.1\",\"type\":\"visualization\",\"id\":\"e2b2c790-8b97-11e8-bedf-8783a42e8fa1\",\"embeddableConfig\":{\"vis\":{\"legendOpen\":false}}}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "version": 1, - "timeRestore": false, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "986fe870-8b9c-11e8-afdd-37bc064a6f4f", - "_type": "dashboard", - "_source": { - "title": "SIP Search", - "hits": 0, - "description": "HOMER SIP Search", - "panelsJSON": "[{\"panelIndex\":\"1\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":22,\"i\":\"1\"},\"version\":\"6.3.1\",\"type\":\"search\",\"id\":\"7d36c2e0-8b9c-11e8-afdd-37bc064a6f4f\",\"embeddableConfig\":{}}]", - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "version": 1, - "timeRestore": false,[ { "_id": "986fe870-8b9c-11e8-afdd-37bc064a6f4f", "_type": "dashboard", @@ -254,109 +205,4 @@ "savedObjectVersion": 2 } } -] - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "7d36c2e0-8b9c-11e8-afdd-37bc064a6f4f", - "_type": "search", - "_source": { - "title": "SIP Search", - "description": "", - "hits": 0, - "columns": [ - "NetSrcIP", - "NetDstIP", - "SIP.FromUser", - "SIP.ToUser", - "SIP.CallID", - "SIP.CseqMethod" - ], - "sort": [ - "_score", - "desc" - ], - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"580b1340-8b9c-11e8-afdd-37bc064a6f4f\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"ProtoType:1\"},\"filter\":[]}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "8625bc70-8b98-11e8-bedf-8783a42e8fa1", - "_type": "visualization", - "_source": { - "title": "SIP CPS", - "visState": "{\"type\":\"timelion\",\"title\":\"SIP CPS\",\"params\":{\"expression\":\".es(index=hep-*, timefield=@timestamp,q='measurement_name:\\\"heplify_method_response\\\" AND tag.method:\\\"INVITE\\\" AND tag.response:\\\"200\\\"', metric=avg:heplify_method_response.counter).derivative().mvavg(1m).scale_interval(1s).yaxis(min=0).color(green).lines(fill=1,width=1).label(\\\"CPS\\\").legend(position=nw,showTime=true)\",\"interval\":\"auto\"}}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "f1ffd270-8b96-11e8-bedf-8783a42e8fa1", - "_type": "visualization", - "_source": { - "title": "SIP User Flows", - "visState": "{\"title\":\"SIP User Flows\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\r\\n $schema: https://vega.github.io/schema/vega/v3.0.json\\r\\n data: [\\r\\n {\\r\\n // query ES based on the currently selected time range and filter string\\r\\n name: rawData\\r\\n url: {\\r\\n %context%: true\\r\\n %timefield%: Timestamp\\r\\n index: heplify-*\\r\\n body: {\\r\\n size: 0\\r\\n aggs: {\\r\\n table: {\\r\\n composite: {\\r\\n size: 100\\r\\n sources: [\\r\\n {\\r\\n stk1: {\\r\\n terms: {field: \\\"SIP.FromUser.keyword\\\"}\\r\\n }\\r\\n }\\r\\n {\\r\\n stk2: {\\r\\n terms: {field: \\\"SIP.ToUser.keyword\\\"}\\r\\n }\\r\\n }\\r\\n ]\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n // From the result, take just the data we are interested in\\r\\n format: {property: \\\"aggregations.table.buckets\\\"}\\r\\n // Convert key.stk1 -> stk1 for simpler access below\\r\\n transform: [\\r\\n {type: \\\"formula\\\", expr: \\\"datum.key.stk1\\\", as: \\\"stk1\\\"}\\r\\n {type: \\\"formula\\\", expr: \\\"datum.key.stk2\\\", as: \\\"stk2\\\"}\\r\\n {type: \\\"formula\\\", expr: \\\"datum.doc_count\\\", as: \\\"size\\\"}\\r\\n ]\\r\\n }\\r\\n {\\r\\n name: nodes\\r\\n source: rawData\\r\\n transform: [\\r\\n // when a country is selected, filter out unrelated data\\r\\n {\\r\\n type: filter\\r\\n expr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\r\\n }\\r\\n // Set new key for later lookups - identifies each node\\r\\n {type: \\\"formula\\\", expr: \\\"datum.stk1+datum.stk2\\\", as: \\\"key\\\"}\\r\\n // instead of each table row, create two new rows,\\r\\n // one for the source (stack=stk1) and one for destination node (stack=stk2).\\r\\n // The country code stored in stk1 and stk2 fields is placed into grpId field.\\r\\n {\\r\\n type: fold\\r\\n fields: [\\\"stk1\\\", \\\"stk2\\\"]\\r\\n as: [\\\"stack\\\", \\\"grpId\\\"]\\r\\n }\\r\\n // Create a sortkey, different for stk1 and stk2 stacks.\\r\\n {\\r\\n type: formula\\r\\n expr: datum.stack == 'stk1' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\r\\n as: sortField\\r\\n }\\r\\n // Calculate y0 and y1 positions for stacking nodes one on top of the other,\\r\\n // independently for each stack, and ensuring they are in the proper order,\\r\\n // alphabetical from the top (reversed on the y axis)\\r\\n {\\r\\n type: stack\\r\\n groupby: [\\\"stack\\\"]\\r\\n sort: {field: \\\"sortField\\\", order: \\\"descending\\\"}\\r\\n field: size\\r\\n }\\r\\n // calculate vertical center point for each node, used to draw edges\\r\\n {type: \\\"formula\\\", expr: \\\"(datum.y0+datum.y1)/2\\\", as: \\\"yc\\\"}\\r\\n ]\\r\\n }\\r\\n {\\r\\n name: groups\\r\\n source: nodes\\r\\n transform: [\\r\\n // combine all nodes into country groups, summing up the doc counts\\r\\n {\\r\\n type: aggregate\\r\\n groupby: [\\\"stack\\\", \\\"grpId\\\"]\\r\\n fields: [\\\"size\\\"]\\r\\n ops: [\\\"sum\\\"]\\r\\n as: [\\\"total\\\"]\\r\\n }\\r\\n // re-calculate the stacking y0,y1 values\\r\\n {\\r\\n type: stack\\r\\n groupby: [\\\"stack\\\"]\\r\\n sort: {field: \\\"grpId\\\", order: \\\"descending\\\"}\\r\\n field: total\\r\\n }\\r\\n // project y0 and y1 values to screen coordinates\\r\\n // doing it once here instead of doing it several times in marks\\r\\n {type: \\\"formula\\\", expr: \\\"scale('y', datum.y0)\\\", as: \\\"scaledY0\\\"}\\r\\n {type: \\\"formula\\\", expr: \\\"scale('y', datum.y1)\\\", as: \\\"scaledY1\\\"}\\r\\n // boolean flag if the label should be on the right of the stack\\r\\n {type: \\\"formula\\\", expr: \\\"datum.stack == 'stk1'\\\", as: \\\"rightLabel\\\"}\\r\\n // Calculate traffic percentage for this country using \\\"y\\\" scale\\r\\n // domain upper bound, which represents the total traffic\\r\\n {\\r\\n type: formula\\r\\n expr: datum.total/domain('y')[1]\\r\\n as: percentage\\r\\n }\\r\\n ]\\r\\n }\\r\\n {\\r\\n // This is a temp lookup table with all the 'stk2' stack nodes\\r\\n name: destinationNodes\\r\\n source: nodes\\r\\n transform: [\\r\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk2'\\\"}\\r\\n ]\\r\\n }\\r\\n {\\r\\n name: edges\\r\\n source: nodes\\r\\n transform: [\\r\\n // we only want nodes from the left stack\\r\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk1'\\\"}\\r\\n // find corresponding node from the right stack, keep it as \\\"target\\\"\\r\\n {\\r\\n type: lookup\\r\\n from: destinationNodes\\r\\n key: key\\r\\n fields: [\\\"key\\\"]\\r\\n as: [\\\"target\\\"]\\r\\n }\\r\\n // calculate SVG link path between stk1 and stk2 stacks for the node pair\\r\\n {\\r\\n type: linkpath\\r\\n orient: horizontal\\r\\n shape: diagonal\\r\\n sourceY: {expr: \\\"scale('y', datum.yc)\\\"}\\r\\n sourceX: {expr: \\\"scale('x', 'stk1') + bandwidth('x')\\\"}\\r\\n targetY: {expr: \\\"scale('y', datum.target.yc)\\\"}\\r\\n targetX: {expr: \\\"scale('x', 'stk2')\\\"}\\r\\n }\\r\\n // A little trick to calculate the thickness of the line.\\r\\n // The value needs to be the same as the hight of the node, but scaling\\r\\n // size to screen's height gives inversed value because screen's Y\\r\\n // coordinate goes from the top to the bottom, whereas the graph's Y=0\\r\\n // is at the bottom. So subtracting scaled doc count from screen height\\r\\n // (which is the \\\"lower\\\" bound of the \\\"y\\\" scale) gives us the right value\\r\\n {\\r\\n type: formula\\r\\n expr: range('y')[0]-scale('y', datum.size)\\r\\n as: strokeWidth\\r\\n }\\r\\n // Tooltip needs individual link's percentage of all traffic\\r\\n {\\r\\n type: formula\\r\\n expr: datum.size/domain('y')[1]\\r\\n as: percentage\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n scales: [\\r\\n {\\r\\n // calculates horizontal stack positioning\\r\\n name: x\\r\\n type: band\\r\\n range: width\\r\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\r\\n paddingOuter: 0.05\\r\\n paddingInner: 0.95\\r\\n }\\r\\n {\\r\\n // this scale goes up as high as the highest y1 value of all nodes\\r\\n name: y\\r\\n type: linear\\r\\n range: height\\r\\n domain: {data: \\\"nodes\\\", field: \\\"y1\\\"}\\r\\n }\\r\\n {\\r\\n // use rawData to ensure the colors stay the same when clicking.\\r\\n name: color\\r\\n type: ordinal\\r\\n range: category\\r\\n domain: {data: \\\"rawData\\\", fields: [\\\"stk1\\\", \\\"stk2\\\"]}\\r\\n }\\r\\n {\\r\\n // this scale is used to map internal ids (stk1, stk2) to stack names\\r\\n name: stackNames\\r\\n type: ordinal\\r\\n range: [\\\"Source\\\", \\\"Destination\\\"]\\r\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\r\\n }\\r\\n ]\\r\\n axes: [\\r\\n {\\r\\n // x axis should use custom label formatting to print proper stack names\\r\\n orient: bottom\\r\\n scale: x\\r\\n encode: {\\r\\n labels: {\\r\\n update: {\\r\\n text: {scale: \\\"stackNames\\\", field: \\\"value\\\"}\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n {orient: \\\"left\\\", scale: \\\"y\\\"}\\r\\n ]\\r\\n marks: [\\r\\n {\\r\\n // draw the connecting line between stacks\\r\\n type: path\\r\\n name: edgeMark\\r\\n from: {data: \\\"edges\\\"}\\r\\n // this prevents some autosizing issues with large strokeWidth for paths\\r\\n clip: true\\r\\n encode: {\\r\\n update: {\\r\\n // By default use color of the left node, except when showing traffic\\r\\n // from just one country, in which case use destination color.\\r\\n stroke: [\\r\\n {\\r\\n test: groupSelector && groupSelector.stack=='stk1'\\r\\n scale: color\\r\\n field: stk2\\r\\n }\\r\\n {scale: \\\"color\\\", field: \\\"stk1\\\"}\\r\\n ]\\r\\n strokeWidth: {field: \\\"strokeWidth\\\"}\\r\\n path: {field: \\\"path\\\"}\\r\\n // when showing all traffic, and hovering over a country,\\r\\n // highlight the traffic from that country.\\r\\n strokeOpacity: {\\r\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\r\\n }\\r\\n // Ensure that the hover-selected edges show on top\\r\\n zindex: {\\r\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\r\\n }\\r\\n // format tooltip string\\r\\n tooltip: {\\r\\n signal: datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\r\\n }\\r\\n }\\r\\n // Simple mouseover highlighting of a single line\\r\\n hover: {\\r\\n strokeOpacity: {value: 1}\\r\\n }\\r\\n }\\r\\n }\\r\\n {\\r\\n // draw stack groups (countries)\\r\\n type: rect\\r\\n name: groupMark\\r\\n from: {data: \\\"groups\\\"}\\r\\n encode: {\\r\\n enter: {\\r\\n fill: {scale: \\\"color\\\", field: \\\"grpId\\\"}\\r\\n width: {scale: \\\"x\\\", band: 1}\\r\\n }\\r\\n update: {\\r\\n x: {scale: \\\"x\\\", field: \\\"stack\\\"}\\r\\n y: {field: \\\"scaledY0\\\"}\\r\\n y2: {field: \\\"scaledY1\\\"}\\r\\n fillOpacity: {value: 0.6}\\r\\n tooltip: {\\r\\n signal: datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\r\\n }\\r\\n }\\r\\n hover: {\\r\\n fillOpacity: {value: 1}\\r\\n }\\r\\n }\\r\\n }\\r\\n {\\r\\n // draw country code labels on the inner side of the stack\\r\\n type: text\\r\\n from: {data: \\\"groups\\\"}\\r\\n // don't process events for the labels - otherwise line mouseover is unclean\\r\\n interactive: false\\r\\n encode: {\\r\\n update: {\\r\\n // depending on which stack it is, position x with some padding\\r\\n x: {\\r\\n signal: scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)\\r\\n }\\r\\n // middle of the group\\r\\n yc: {signal: \\\"(datum.scaledY0 + datum.scaledY1)/2\\\"}\\r\\n align: {signal: \\\"datum.rightLabel ? 'left' : 'right'\\\"}\\r\\n baseline: {value: \\\"middle\\\"}\\r\\n fontWeight: {value: \\\"bold\\\"}\\r\\n // only show text label if the group's height is large enough\\r\\n text: {signal: \\\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''\\\"}\\r\\n }\\r\\n }\\r\\n }\\r\\n {\\r\\n // Create a \\\"show all\\\" button. Shown only when a country is selected.\\r\\n type: group\\r\\n data: [\\r\\n // We need to make the button show only when groupSelector signal is true.\\r\\n // Each mark is drawn as many times as there are elements in the backing data.\\r\\n // Which means that if values list is empty, it will not be drawn.\\r\\n // Here I create a data source with one empty object, and filter that list\\r\\n // based on the signal value. This can only be done in a group.\\r\\n {\\r\\n name: dataForShowAll\\r\\n values: [{}]\\r\\n transform: [{type: \\\"filter\\\", expr: \\\"groupSelector\\\"}]\\r\\n }\\r\\n ]\\r\\n // Set button size and positioning\\r\\n encode: {\\r\\n enter: {\\r\\n xc: {signal: \\\"width/2\\\"}\\r\\n y: {value: 30}\\r\\n width: {value: 80}\\r\\n height: {value: 30}\\r\\n }\\r\\n }\\r\\n marks: [\\r\\n {\\r\\n // This group is shown as a button with rounded corners.\\r\\n type: group\\r\\n // mark name allows signal capturing\\r\\n name: groupReset\\r\\n // Only shows button if dataForShowAll has values.\\r\\n from: {data: \\\"dataForShowAll\\\"}\\r\\n encode: {\\r\\n enter: {\\r\\n cornerRadius: {value: 6}\\r\\n fill: {value: \\\"#f5f5f5\\\"}\\r\\n stroke: {value: \\\"#c1c1c1\\\"}\\r\\n strokeWidth: {value: 2}\\r\\n // use parent group's size\\r\\n height: {\\r\\n field: {group: \\\"height\\\"}\\r\\n }\\r\\n width: {\\r\\n field: {group: \\\"width\\\"}\\r\\n }\\r\\n }\\r\\n update: {\\r\\n // groups are transparent by default\\r\\n opacity: {value: 1}\\r\\n }\\r\\n hover: {\\r\\n opacity: {value: 0.7}\\r\\n }\\r\\n }\\r\\n marks: [\\r\\n {\\r\\n type: text\\r\\n // if true, it will prevent clicking on the button when over text.\\r\\n interactive: false\\r\\n encode: {\\r\\n enter: {\\r\\n // center text in the paren group\\r\\n xc: {\\r\\n field: {group: \\\"width\\\"}\\r\\n mult: 0.5\\r\\n }\\r\\n yc: {\\r\\n field: {group: \\\"height\\\"}\\r\\n mult: 0.5\\r\\n offset: 2\\r\\n }\\r\\n align: {value: \\\"center\\\"}\\r\\n baseline: {value: \\\"middle\\\"}\\r\\n fontWeight: {value: \\\"bold\\\"}\\r\\n text: {value: \\\"Show All\\\"}\\r\\n }\\r\\n }\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n signals: [\\r\\n {\\r\\n // used to highlight traffic to/from the same country\\r\\n name: groupHover\\r\\n value: {}\\r\\n on: [\\r\\n {\\r\\n events: @groupMark:mouseover\\r\\n update: \\\"{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\r\\n }\\r\\n {events: \\\"mouseout\\\", update: \\\"{}\\\"}\\r\\n ]\\r\\n }\\r\\n // used to filter only the data related to the selected country\\r\\n {\\r\\n name: groupSelector\\r\\n value: false\\r\\n on: [\\r\\n {\\r\\n // Clicking groupMark sets this signal to the filter values\\r\\n events: @groupMark:click!\\r\\n update: \\\"{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\r\\n }\\r\\n {\\r\\n // Clicking \\\"show all\\\" button, or double-clicking anywhere resets it\\r\\n events: [\\r\\n {type: \\\"click\\\", markname: \\\"groupReset\\\"}\\r\\n {type: \\\"dblclick\\\"}\\r\\n ]\\r\\n update: \\\"false\\\"\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n}\"},\"aggs\":[]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "e2b2c790-8b97-11e8-bedf-8783a42e8fa1", - "_type": "visualization", - "_source": { - "title": "RTCP Jitter", - "visState": "{\"title\":\"RTCP Jitter\",\"type\":\"line\",\"params\":{\"type\":\"line\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Avg Jitter (ms)\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"normal\",\"data\":{\"label\":\"Avg Jitter (ms)\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true},{\"show\":true,\"mode\":\"normal\",\"type\":\"line\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"3\",\"label\":\"Max Jitter (ms)\"},\"valueAxis\":\"ValueAxis-1\"},{\"show\":true,\"mode\":\"normal\",\"type\":\"line\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"4\",\"label\":\"Min Jitter (ms)\"},\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"heplify_rtcp_jitter.gauge\",\"customLabel\":\"Avg Jitter (ms)\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"heplify_rtcp_jitter.gauge\",\"customLabel\":\"Max Jitter (ms)\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"min\",\"schema\":\"metric\",\"params\":{\"field\":\"heplify_rtcp_jitter.gauge\",\"customLabel\":\"Min Jitter (ms)\"}}]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"bc9ff070-8b90-11e8-96c0-37f2e673999f\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - }, - { - "_id": "e8596200-8b95-11e8-bedf-8783a42e8fa1", - "_type": "visualization", - "_source": { - "title": "Vega Sankey", - "visState": "{\"title\":\"Vega Sankey\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\r\\n $schema: https://vega.github.io/schema/vega/v3.0.json\\r\\n data: [\\r\\n {\\r\\n // query ES based on the currently selected time range and filter string\\r\\n name: rawData\\r\\n url: {\\r\\n %context%: true\\r\\n %timefield%: Timestamp\\r\\n index: heplify-*\\r\\n body: {\\r\\n size: 0\\r\\n aggs: {\\r\\n table: {\\r\\n composite: {\\r\\n size: 100\\r\\n sources: [\\r\\n {\\r\\n stk1: {\\r\\n terms: {field: \\\"NetSrcIP.keyword\\\"}\\r\\n }\\r\\n }\\r\\n {\\r\\n stk2: {\\r\\n terms: {field: \\\"NetDstIP.keyword\\\"}\\r\\n }\\r\\n }\\r\\n ]\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n // From the result, take just the data we are interested in\\r\\n format: {property: \\\"aggregations.table.buckets\\\"}\\r\\n // Convert key.stk1 -> stk1 for simpler access below\\r\\n transform: [\\r\\n {type: \\\"formula\\\", expr: \\\"datum.key.stk1\\\", as: \\\"stk1\\\"}\\r\\n {type: \\\"formula\\\", expr: \\\"datum.key.stk2\\\", as: \\\"stk2\\\"}\\r\\n {type: \\\"formula\\\", expr: \\\"datum.doc_count\\\", as: \\\"size\\\"}\\r\\n ]\\r\\n }\\r\\n {\\r\\n name: nodes\\r\\n source: rawData\\r\\n transform: [\\r\\n // when a country is selected, filter out unrelated data\\r\\n {\\r\\n type: filter\\r\\n expr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\r\\n }\\r\\n // Set new key for later lookups - identifies each node\\r\\n {type: \\\"formula\\\", expr: \\\"datum.stk1+datum.stk2\\\", as: \\\"key\\\"}\\r\\n // instead of each table row, create two new rows,\\r\\n // one for the source (stack=stk1) and one for destination node (stack=stk2).\\r\\n // The country code stored in stk1 and stk2 fields is placed into grpId field.\\r\\n {\\r\\n type: fold\\r\\n fields: [\\\"stk1\\\", \\\"stk2\\\"]\\r\\n as: [\\\"stack\\\", \\\"grpId\\\"]\\r\\n }\\r\\n // Create a sortkey, different for stk1 and stk2 stacks.\\r\\n {\\r\\n type: formula\\r\\n expr: datum.stack == 'stk1' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\r\\n as: sortField\\r\\n }\\r\\n // Calculate y0 and y1 positions for stacking nodes one on top of the other,\\r\\n // independently for each stack, and ensuring they are in the proper order,\\r\\n // alphabetical from the top (reversed on the y axis)\\r\\n {\\r\\n type: stack\\r\\n groupby: [\\\"stack\\\"]\\r\\n sort: {field: \\\"sortField\\\", order: \\\"descending\\\"}\\r\\n field: size\\r\\n }\\r\\n // calculate vertical center point for each node, used to draw edges\\r\\n {type: \\\"formula\\\", expr: \\\"(datum.y0+datum.y1)/2\\\", as: \\\"yc\\\"}\\r\\n ]\\r\\n }\\r\\n {\\r\\n name: groups\\r\\n source: nodes\\r\\n transform: [\\r\\n // combine all nodes into country groups, summing up the doc counts\\r\\n {\\r\\n type: aggregate\\r\\n groupby: [\\\"stack\\\", \\\"grpId\\\"]\\r\\n fields: [\\\"size\\\"]\\r\\n ops: [\\\"sum\\\"]\\r\\n as: [\\\"total\\\"]\\r\\n }\\r\\n // re-calculate the stacking y0,y1 values\\r\\n {\\r\\n type: stack\\r\\n groupby: [\\\"stack\\\"]\\r\\n sort: {field: \\\"grpId\\\", order: \\\"descending\\\"}\\r\\n field: total\\r\\n }\\r\\n // project y0 and y1 values to screen coordinates\\r\\n // doing it once here instead of doing it several times in marks\\r\\n {type: \\\"formula\\\", expr: \\\"scale('y', datum.y0)\\\", as: \\\"scaledY0\\\"}\\r\\n {type: \\\"formula\\\", expr: \\\"scale('y', datum.y1)\\\", as: \\\"scaledY1\\\"}\\r\\n // boolean flag if the label should be on the right of the stack\\r\\n {type: \\\"formula\\\", expr: \\\"datum.stack == 'stk1'\\\", as: \\\"rightLabel\\\"}\\r\\n // Calculate traffic percentage for this country using \\\"y\\\" scale\\r\\n // domain upper bound, which represents the total traffic\\r\\n {\\r\\n type: formula\\r\\n expr: datum.total/domain('y')[1]\\r\\n as: percentage\\r\\n }\\r\\n ]\\r\\n }\\r\\n {\\r\\n // This is a temp lookup table with all the 'stk2' stack nodes\\r\\n name: destinationNodes\\r\\n source: nodes\\r\\n transform: [\\r\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk2'\\\"}\\r\\n ]\\r\\n }\\r\\n {\\r\\n name: edges\\r\\n source: nodes\\r\\n transform: [\\r\\n // we only want nodes from the left stack\\r\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk1'\\\"}\\r\\n // find corresponding node from the right stack, keep it as \\\"target\\\"\\r\\n {\\r\\n type: lookup\\r\\n from: destinationNodes\\r\\n key: key\\r\\n fields: [\\\"key\\\"]\\r\\n as: [\\\"target\\\"]\\r\\n }\\r\\n // calculate SVG link path between stk1 and stk2 stacks for the node pair\\r\\n {\\r\\n type: linkpath\\r\\n orient: horizontal\\r\\n shape: diagonal\\r\\n sourceY: {expr: \\\"scale('y', datum.yc)\\\"}\\r\\n sourceX: {expr: \\\"scale('x', 'stk1') + bandwidth('x')\\\"}\\r\\n targetY: {expr: \\\"scale('y', datum.target.yc)\\\"}\\r\\n targetX: {expr: \\\"scale('x', 'stk2')\\\"}\\r\\n }\\r\\n // A little trick to calculate the thickness of the line.\\r\\n // The value needs to be the same as the hight of the node, but scaling\\r\\n // size to screen's height gives inversed value because screen's Y\\r\\n // coordinate goes from the top to the bottom, whereas the graph's Y=0\\r\\n // is at the bottom. So subtracting scaled doc count from screen height\\r\\n // (which is the \\\"lower\\\" bound of the \\\"y\\\" scale) gives us the right value\\r\\n {\\r\\n type: formula\\r\\n expr: range('y')[0]-scale('y', datum.size)\\r\\n as: strokeWidth\\r\\n }\\r\\n // Tooltip needs individual link's percentage of all traffic\\r\\n {\\r\\n type: formula\\r\\n expr: datum.size/domain('y')[1]\\r\\n as: percentage\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n scales: [\\r\\n {\\r\\n // calculates horizontal stack positioning\\r\\n name: x\\r\\n type: band\\r\\n range: width\\r\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\r\\n paddingOuter: 0.05\\r\\n paddingInner: 0.95\\r\\n }\\r\\n {\\r\\n // this scale goes up as high as the highest y1 value of all nodes\\r\\n name: y\\r\\n type: linear\\r\\n range: height\\r\\n domain: {data: \\\"nodes\\\", field: \\\"y1\\\"}\\r\\n }\\r\\n {\\r\\n // use rawData to ensure the colors stay the same when clicking.\\r\\n name: color\\r\\n type: ordinal\\r\\n range: category\\r\\n domain: {data: \\\"rawData\\\", fields: [\\\"stk1\\\", \\\"stk2\\\"]}\\r\\n }\\r\\n {\\r\\n // this scale is used to map internal ids (stk1, stk2) to stack names\\r\\n name: stackNames\\r\\n type: ordinal\\r\\n range: [\\\"Source\\\", \\\"Destination\\\"]\\r\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\r\\n }\\r\\n ]\\r\\n axes: [\\r\\n {\\r\\n // x axis should use custom label formatting to print proper stack names\\r\\n orient: bottom\\r\\n scale: x\\r\\n encode: {\\r\\n labels: {\\r\\n update: {\\r\\n text: {scale: \\\"stackNames\\\", field: \\\"value\\\"}\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n {orient: \\\"left\\\", scale: \\\"y\\\"}\\r\\n ]\\r\\n marks: [\\r\\n {\\r\\n // draw the connecting line between stacks\\r\\n type: path\\r\\n name: edgeMark\\r\\n from: {data: \\\"edges\\\"}\\r\\n // this prevents some autosizing issues with large strokeWidth for paths\\r\\n clip: true\\r\\n encode: {\\r\\n update: {\\r\\n // By default use color of the left node, except when showing traffic\\r\\n // from just one country, in which case use destination color.\\r\\n stroke: [\\r\\n {\\r\\n test: groupSelector && groupSelector.stack=='stk1'\\r\\n scale: color\\r\\n field: stk2\\r\\n }\\r\\n {scale: \\\"color\\\", field: \\\"stk1\\\"}\\r\\n ]\\r\\n strokeWidth: {field: \\\"strokeWidth\\\"}\\r\\n path: {field: \\\"path\\\"}\\r\\n // when showing all traffic, and hovering over a country,\\r\\n // highlight the traffic from that country.\\r\\n strokeOpacity: {\\r\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\r\\n }\\r\\n // Ensure that the hover-selected edges show on top\\r\\n zindex: {\\r\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\r\\n }\\r\\n // format tooltip string\\r\\n tooltip: {\\r\\n signal: datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\r\\n }\\r\\n }\\r\\n // Simple mouseover highlighting of a single line\\r\\n hover: {\\r\\n strokeOpacity: {value: 1}\\r\\n }\\r\\n }\\r\\n }\\r\\n {\\r\\n // draw stack groups (countries)\\r\\n type: rect\\r\\n name: groupMark\\r\\n from: {data: \\\"groups\\\"}\\r\\n encode: {\\r\\n enter: {\\r\\n fill: {scale: \\\"color\\\", field: \\\"grpId\\\"}\\r\\n width: {scale: \\\"x\\\", band: 1}\\r\\n }\\r\\n update: {\\r\\n x: {scale: \\\"x\\\", field: \\\"stack\\\"}\\r\\n y: {field: \\\"scaledY0\\\"}\\r\\n y2: {field: \\\"scaledY1\\\"}\\r\\n fillOpacity: {value: 0.6}\\r\\n tooltip: {\\r\\n signal: datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\r\\n }\\r\\n }\\r\\n hover: {\\r\\n fillOpacity: {value: 1}\\r\\n }\\r\\n }\\r\\n }\\r\\n {\\r\\n // draw country code labels on the inner side of the stack\\r\\n type: text\\r\\n from: {data: \\\"groups\\\"}\\r\\n // don't process events for the labels - otherwise line mouseover is unclean\\r\\n interactive: false\\r\\n encode: {\\r\\n update: {\\r\\n // depending on which stack it is, position x with some padding\\r\\n x: {\\r\\n signal: scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)\\r\\n }\\r\\n // middle of the group\\r\\n yc: {signal: \\\"(datum.scaledY0 + datum.scaledY1)/2\\\"}\\r\\n align: {signal: \\\"datum.rightLabel ? 'left' : 'right'\\\"}\\r\\n baseline: {value: \\\"middle\\\"}\\r\\n fontWeight: {value: \\\"bold\\\"}\\r\\n // only show text label if the group's height is large enough\\r\\n text: {signal: \\\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''\\\"}\\r\\n }\\r\\n }\\r\\n }\\r\\n {\\r\\n // Create a \\\"show all\\\" button. Shown only when a country is selected.\\r\\n type: group\\r\\n data: [\\r\\n // We need to make the button show only when groupSelector signal is true.\\r\\n // Each mark is drawn as many times as there are elements in the backing data.\\r\\n // Which means that if values list is empty, it will not be drawn.\\r\\n // Here I create a data source with one empty object, and filter that list\\r\\n // based on the signal value. This can only be done in a group.\\r\\n {\\r\\n name: dataForShowAll\\r\\n values: [{}]\\r\\n transform: [{type: \\\"filter\\\", expr: \\\"groupSelector\\\"}]\\r\\n }\\r\\n ]\\r\\n // Set button size and positioning\\r\\n encode: {\\r\\n enter: {\\r\\n xc: {signal: \\\"width/2\\\"}\\r\\n y: {value: 30}\\r\\n width: {value: 80}\\r\\n height: {value: 30}\\r\\n }\\r\\n }\\r\\n marks: [\\r\\n {\\r\\n // This group is shown as a button with rounded corners.\\r\\n type: group\\r\\n // mark name allows signal capturing\\r\\n name: groupReset\\r\\n // Only shows button if dataForShowAll has values.\\r\\n from: {data: \\\"dataForShowAll\\\"}\\r\\n encode: {\\r\\n enter: {\\r\\n cornerRadius: {value: 6}\\r\\n fill: {value: \\\"#f5f5f5\\\"}\\r\\n stroke: {value: \\\"#c1c1c1\\\"}\\r\\n strokeWidth: {value: 2}\\r\\n // use parent group's size\\r\\n height: {\\r\\n field: {group: \\\"height\\\"}\\r\\n }\\r\\n width: {\\r\\n field: {group: \\\"width\\\"}\\r\\n }\\r\\n }\\r\\n update: {\\r\\n // groups are transparent by default\\r\\n opacity: {value: 1}\\r\\n }\\r\\n hover: {\\r\\n opacity: {value: 0.7}\\r\\n }\\r\\n }\\r\\n marks: [\\r\\n {\\r\\n type: text\\r\\n // if true, it will prevent clicking on the button when over text.\\r\\n interactive: false\\r\\n encode: {\\r\\n enter: {\\r\\n // center text in the paren group\\r\\n xc: {\\r\\n field: {group: \\\"width\\\"}\\r\\n mult: 0.5\\r\\n }\\r\\n yc: {\\r\\n field: {group: \\\"height\\\"}\\r\\n mult: 0.5\\r\\n offset: 2\\r\\n }\\r\\n align: {value: \\\"center\\\"}\\r\\n baseline: {value: \\\"middle\\\"}\\r\\n fontWeight: {value: \\\"bold\\\"}\\r\\n text: {value: \\\"Show All\\\"}\\r\\n }\\r\\n }\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n signals: [\\r\\n {\\r\\n // used to highlight traffic to/from the same country\\r\\n name: groupHover\\r\\n value: {}\\r\\n on: [\\r\\n {\\r\\n events: @groupMark:mouseover\\r\\n update: \\\"{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\r\\n }\\r\\n {events: \\\"mouseout\\\", update: \\\"{}\\\"}\\r\\n ]\\r\\n }\\r\\n // used to filter only the data related to the selected country\\r\\n {\\r\\n name: groupSelector\\r\\n value: false\\r\\n on: [\\r\\n {\\r\\n // Clicking groupMark sets this signal to the filter values\\r\\n events: @groupMark:click!\\r\\n update: \\\"{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\r\\n }\\r\\n {\\r\\n // Clicking \\\"show all\\\" button, or double-clicking anywhere resets it\\r\\n events: [\\r\\n {type: \\\"click\\\", markname: \\\"groupReset\\\"}\\r\\n {type: \\\"dblclick\\\"}\\r\\n ]\\r\\n update: \\\"false\\\"\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n}\"},\"aggs\":[]}", - "uiStateJSON": "{}", - "description": "", - "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - } - }, - "_meta": { - "savedObjectVersion": 2 - } - } ]