Thomas Vachuska

Added GUI to intent perf app to monitor performance stats in real-time.

Fixed app ids for metrics app.

Change-Id: Icea99991ad71c80c53a832c236dcc05fefbb9b02
...@@ -65,7 +65,7 @@ public class IntentPerfCollector { ...@@ -65,7 +65,7 @@ public class IntentPerfCollector {
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected ClusterService clusterService; 66 protected ClusterService clusterService;
67 67
68 - @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY) 68 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected IntentPerfUi ui; 69 protected IntentPerfUi ui;
70 70
71 // Auxiliary structures used to accrue data for normalized time interval 71 // Auxiliary structures used to accrue data for normalized time interval
...@@ -99,6 +99,7 @@ public class IntentPerfCollector { ...@@ -99,6 +99,7 @@ public class IntentPerfCollector {
99 nodeToIndex.put(nodes[i].id(), i); 99 nodeToIndex.put(nodes[i].id(), i);
100 } 100 }
101 101
102 + ui.setHeaders(getSampleHeaders());
102 clearSamples(); 103 clearSamples();
103 log.info("Started"); 104 log.info("Started");
104 } 105 }
......
...@@ -99,7 +99,7 @@ public class IntentPerfInstaller { ...@@ -99,7 +99,7 @@ public class IntentPerfInstaller {
99 private static final int DEFAULT_NUM_NEIGHBORS = 0; 99 private static final int DEFAULT_NUM_NEIGHBORS = 0;
100 100
101 private static final int START_DELAY = 5_000; // ms 101 private static final int START_DELAY = 5_000; // ms
102 - private static final int REPORT_PERIOD = 5_000; //ms 102 + private static final int REPORT_PERIOD = 1_000; //ms
103 103
104 private static final String START = "start"; 104 private static final String START = "start";
105 private static final String STOP = "stop"; 105 private static final String STOP = "stop";
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.intentperf; 16 package org.onosproject.intentperf;
17 17
18 +import com.fasterxml.jackson.databind.node.ArrayNode;
18 import com.fasterxml.jackson.databind.node.ObjectNode; 19 import com.fasterxml.jackson.databind.node.ObjectNode;
19 import com.google.common.collect.ImmutableList; 20 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableSet; 21 import com.google.common.collect.ImmutableSet;
...@@ -23,6 +24,7 @@ import org.apache.felix.scr.annotations.Component; ...@@ -23,6 +24,7 @@ import org.apache.felix.scr.annotations.Component;
23 import org.apache.felix.scr.annotations.Deactivate; 24 import org.apache.felix.scr.annotations.Deactivate;
24 import org.apache.felix.scr.annotations.Reference; 25 import org.apache.felix.scr.annotations.Reference;
25 import org.apache.felix.scr.annotations.ReferenceCardinality; 26 import org.apache.felix.scr.annotations.ReferenceCardinality;
27 +import org.apache.felix.scr.annotations.Service;
26 import org.onlab.osgi.ServiceDirectory; 28 import org.onlab.osgi.ServiceDirectory;
27 import org.onosproject.intentperf.IntentPerfCollector.Sample; 29 import org.onosproject.intentperf.IntentPerfCollector.Sample;
28 import org.onosproject.ui.UiConnection; 30 import org.onosproject.ui.UiConnection;
...@@ -34,14 +36,17 @@ import org.onosproject.ui.UiView; ...@@ -34,14 +36,17 @@ import org.onosproject.ui.UiView;
34 import java.util.Collection; 36 import java.util.Collection;
35 import java.util.HashSet; 37 import java.util.HashSet;
36 import java.util.List; 38 import java.util.List;
39 +import java.util.Random;
37 import java.util.Set; 40 import java.util.Set;
41 +import java.util.TimerTask;
38 42
39 import static java.util.Collections.synchronizedSet; 43 import static java.util.Collections.synchronizedSet;
40 44
41 /** 45 /**
42 * Mechanism to stream data to the GUI. 46 * Mechanism to stream data to the GUI.
43 */ 47 */
44 -@Component(immediate = true, enabled = false) 48 +@Component(immediate = true, enabled = true)
49 +@Service(value = IntentPerfUi.class)
45 public class IntentPerfUi { 50 public class IntentPerfUi {
46 51
47 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 52 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
...@@ -53,9 +58,25 @@ public class IntentPerfUi { ...@@ -53,9 +58,25 @@ public class IntentPerfUi {
53 private UiExtension uiExtension = new UiExtension(views, this::newHandlers, 58 private UiExtension uiExtension = new UiExtension(views, this::newHandlers,
54 getClass().getClassLoader()); 59 getClass().getClassLoader());
55 60
61 + private List<String> headers = ImmutableList.of("One", "Two", "Three", "Four", "Five");
62 +
63 + private Random random = new Random();
64 + private TimerTask task;
65 +
56 @Activate 66 @Activate
57 protected void activate() { 67 protected void activate() {
58 uiExtensionService.register(uiExtension); 68 uiExtensionService.register(uiExtension);
69 +// task = new TimerTask() {
70 +// @Override
71 +// public void run() {
72 +// Sample sample = new Sample(System.currentTimeMillis(), headers.size());
73 +// for (int i = 0; i < headers.size(); i++) {
74 +// sample.data[i] = 25_000 + random.nextInt(20_000) - 5_000;
75 +// }
76 +// reportSample(sample);
77 +// }
78 +// };
79 +// SharedExecutors.getTimer().scheduleAtFixedRate(task, 1000, 1000);
59 } 80 }
60 81
61 @Deactivate 82 @Deactivate
...@@ -74,6 +95,15 @@ public class IntentPerfUi { ...@@ -74,6 +95,15 @@ public class IntentPerfUi {
74 } 95 }
75 } 96 }
76 97
98 + /**
99 + * Sets the headers for the subsequently reported samples.
100 + *
101 + * @param headers list of headers for future samples
102 + */
103 + public void setHeaders(List<String> headers) {
104 + this.headers = headers;
105 + }
106 +
77 // Creates and returns session specific message handler. 107 // Creates and returns session specific message handler.
78 private Collection<UiMessageHandler> newHandlers() { 108 private Collection<UiMessageHandler> newHandlers() {
79 return ImmutableList.of(new StreamingControl()); 109 return ImmutableList.of(new StreamingControl());
...@@ -90,7 +120,22 @@ public class IntentPerfUi { ...@@ -90,7 +120,22 @@ public class IntentPerfUi {
90 120
91 @Override 121 @Override
92 public void process(ObjectNode message) { 122 public void process(ObjectNode message) {
93 - streamingEnabled = message.path("event").asText("unknown").equals("initPerfStart"); 123 + streamingEnabled = message.path("event").asText("unknown").equals("intentPerfStart");
124 + if (streamingEnabled) {
125 + sendHeaders();
126 + }
127 + }
128 +
129 + private void sendHeaders() {
130 + ArrayNode an = mapper.createArrayNode();
131 + for (String header : headers) {
132 + an.add(header);
133 + }
134 +
135 + ObjectNode sn = mapper.createObjectNode();
136 + sn.set("headers", an);
137 +
138 + connection().sendMessage("intentPerfHeaders", 0, sn);
94 } 139 }
95 140
96 @Override 141 @Override
...@@ -106,10 +151,18 @@ public class IntentPerfUi { ...@@ -106,10 +151,18 @@ public class IntentPerfUi {
106 } 151 }
107 152
108 private void send(Sample sample) { 153 private void send(Sample sample) {
109 - // FIXME: finish this 154 + if (streamingEnabled) {
110 - ObjectNode sn = mapper.createObjectNode() 155 + ArrayNode an = mapper.createArrayNode();
111 - .put("time", sample.time); 156 + for (double d : sample.data) {
112 - connection().sendMessage("intentPerf", 0, sn); 157 + an.add(d);
158 + }
159 +
160 + ObjectNode sn = mapper.createObjectNode();
161 + sn.put("time", sample.time);
162 + sn.set("data", an);
163 +
164 + connection().sendMessage("intentPerfSample", 0, sn);
165 + }
113 } 166 }
114 } 167 }
115 168
......
...@@ -18,39 +18,35 @@ ...@@ -18,39 +18,35 @@
18 ONOS GUI -- Intent Perf View -- CSS file 18 ONOS GUI -- Intent Perf View -- CSS file
19 */ 19 */
20 20
21 -.light #ov-intentPerf { 21 +svg {
22 - color: navy; 22 + font: 12px sans-serif;
23 } 23 }
24 24
25 -.dark #ov-intentPerf { 25 +.line {
26 - color: #1e5e6f; 26 + fill: none;
27 -} 27 + stroke: #000;
28 - 28 + stroke-width: 2px;
29 -.dark a {
30 - color: #88c;
31 -}
32 -
33 -#ov-intentPerf .msg {
34 - color: darkorange;
35 -}
36 -
37 -.light #ov-intentPerf .msg {
38 - color: darkorange;
39 -}
40 -
41 -.dark #ov-intentPerf .msg {
42 - color: #904e00;
43 } 29 }
44 30
45 -
46 -
47 .axis path, 31 .axis path,
48 .axis line { 32 .axis line {
49 fill: none; 33 fill: none;
50 - stroke: #000; 34 + stroke-width: 2px;
51 shape-rendering: crispEdges; 35 shape-rendering: crispEdges;
52 } 36 }
53 37
54 -.browser text { 38 +.light .axis path,
55 - text-anchor: end; 39 +.light .axis line,
40 +.light .axis text {
41 + stroke: #999;
42 +}
43 +
44 +.dark .axis path,
45 +.dark .axis line,
46 +.dark .axis text {
47 + stroke: #eee;
56 } 48 }
49 +
50 +.axis text {
51 + stroke-width: 0.3;
52 +}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
15 --> 15 -->
16 16
17 <!-- Intent Performance partial HTML --> 17 <!-- Intent Performance partial HTML -->
18 -<div id="ov-sample"> 18 +<div id="ov-intentPerf">
19 <h2> Intent Performance View </h2> 19 <h2> Intent Performance View </h2>
20 20
21 - <span class="msg">{{ ctrl.message }}</span> 21 + <div id="intent-perf-chart"
22 - 22 + resize
23 - <div id="intent-perf-chart"></div> 23 + ng-style="resizeWithOffset(56, 12)"
24 + notifier="ctrl.notifyResize()">
25 + </div>
24 </div> 26 </div>
......
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
18 <head> 18 <head>
19 <title>Dev View</title> 19 <title>Dev View</title>
20 <script src="tp/d3.min.js"></script> 20 <script src="tp/d3.min.js"></script>
21 - <script src="tp/jquery-2.1.1.min.js"></script>
22 -
23 <link rel="stylesheet" href="app/view/intentPerf/intentPerf.css"> 21 <link rel="stylesheet" href="app/view/intentPerf/intentPerf.css">
24 </head> 22 </head>
25 <body> 23 <body>
......
1 +<!DOCTYPE html>
2 +<meta charset="utf-8">
3 +<style>
4 +
5 + svg {
6 + font: 10px sans-serif;
7 + }
8 +
9 + .line {
10 + fill: none;
11 + stroke: darkgreen;
12 + stroke-width: 2px;
13 + }
14 +
15 + .axis path,
16 + .axis line {
17 + fill: none;
18 + stroke: #999;
19 + stroke-width: 2px;
20 + shape-rendering: crispEdges;
21 + }
22 +
23 +</style>
24 +<body>
25 +<script src="http://d3js.org/d3.v3.min.js"></script>
26 +<script>
27 + (function () {
28 + var cs = 0,
29 + samples = [
30 + 89.53,
31 + 37515.81,
32 + 104609.6,
33 + 113105.11,
34 + 103194.74,
35 + 122151.63,
36 + 128623.9,
37 + 137325.33,
38 + 154897.31,
39 + 161235.07,
40 + 162025.4,
41 + 164902.64,
42 + 158196.26,
43 + 161072.44,
44 + 160792.54,
45 + 164692.44,
46 + 161979.74,
47 + 162137.4,
48 + 159325.19,
49 + 170465.44,
50 + 168186.46,
51 + 171152.34,
52 + 168221.02,
53 + 167440.73,
54 + 165003.39,
55 + 166855.18,
56 + 157268.79,
57 + 164087.54,
58 + 162265.21,
59 + 165990.16,
60 + 176364.01,
61 + 172064.07,
62 + 184872.24,
63 + 183249.8,
64 + 182282.47,
65 + 171475.11,
66 + 158880.58,
67 + 166016.69,
68 + 168233.16,
69 + 177759.92,
70 + 179742.87,
71 + 170819.44,
72 + 167577.73,
73 + 169479.9,
74 + 175544.89,
75 + 183792.01,
76 + 184689.52,
77 + 178503.87,
78 + 173219.27,
79 + 179085.49,
80 + 179700.54,
81 + 174281.17,
82 + 181353.08,
83 + 180173.14,
84 + 184093.16,
85 + 186011.5,
86 + 176952.79,
87 + 175319.2,
88 + 169001.05,
89 + 174545.12,
90 + 169156.29,
91 + 171804.3,
92 + 159155.54,
93 + 154709.96,
94 + 157263.97
95 + ],
96 + theSample,
97 + headers = [ "Whole", "Half", "Third" ];
98 +
99 + var n = 243,
100 + duration = 750,
101 + now = new Date(Date.now() - duration),
102 + data = [];
103 +
104 + headers.forEach(function (d, li) {
105 + data[li] = d3.range(n).map(function () { return 0; });
106 + });
107 +
108 + var margin = {top: 20, right: 100, bottom: 20, left: 100},
109 + width = 960 - margin.right,
110 + height = 512 - margin.top - margin.bottom;
111 +
112 + var x = d3.time.scale()
113 + .domain([now - (n - 2) * duration, now - duration])
114 + .range([0, width]);
115 +
116 + var y = d3.scale.linear()
117 + .domain([0, 200000])
118 + .range([height, 0]);
119 +
120 + var svg = d3.select("body").append("p").append("svg")
121 + .attr("width", width + margin.left + margin.right)
122 + .attr("height", height + margin.top + margin.bottom)
123 + .append("g")
124 + .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
125 +
126 + svg.append("defs").append("clipPath")
127 + .attr("id", "clip")
128 + .append("rect")
129 + .attr("width", width)
130 + .attr("height", height);
131 +
132 + var axis = svg.append("g")
133 + .attr("class", "x axis")
134 + .attr("transform", "translate(0," + height + ")")
135 + .call(x.axis = d3.svg.axis().scale(x).orient("bottom"));
136 +
137 + svg.append("g")
138 + .attr("class", "y axis")
139 + .call(d3.svg.axis().scale(y).orient("left"));
140 +
141 + svg.append("g")
142 + .attr("class", "y axis")
143 + .attr("transform", "translate(" + width + " ,0)")
144 + .call(d3.svg.axis().scale(y).orient("right"));
145 +
146 + var lines = [], paths = [];
147 + data.forEach(function (p, li) {
148 + lines[li]= d3.svg.line()
149 + .interpolate("basis")
150 + .x(function (d, i) {
151 + return x(now - (n - 1 - i) * duration);
152 + })
153 + .y(function (d, i) {
154 + return y(d);
155 + });
156 +
157 + paths[li] = svg.append("g")
158 + .attr("clip-path", "url(#clip)")
159 + .append("path")
160 + .datum(function () { return data[li]; })
161 + .attr("id", "line" + li)
162 + .attr("class", "line");
163 + });
164 +
165 + var transition = d3.select({}).transition()
166 + .duration(750)
167 + .ease("linear");
168 +
169 + function tick() {
170 + transition = transition.each(function () {
171 + // update the domains
172 + now = new Date();
173 + x.domain([now - (n - 2) * duration, now - duration]);
174 +
175 + data.forEach(function (d, li) {
176 + // push the new most recent sample onto the back
177 + d.push(theSample[li]);
178 +
179 + // redraw the line and slide it left
180 + paths[li].attr("d", lines[li]).attr("transform", null);
181 + paths[li].transition().attr("transform", "translate(" + x(now - (n - 1) * duration) + ")");
182 +
183 + // pop the old data point off the front
184 + d.shift();
185 + });
186 +
187 + // slide the x-axis left
188 + axis.call(x.axis);
189 +
190 + }).transition().each("start", tick);
191 + }
192 +
193 + function setSample() {
194 + var v = samples[cs++];
195 + theSample = [ v, v/2, v/3 ];
196 + }
197 +
198 + setSample();
199 + setInterval(setSample, 1000);
200 + tick();
201 +
202 + })()
203 +</script>
204 +</body>
205 +</html>
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
21 /* ------ for summary-list tables ------ */ 21 /* ------ for summary-list tables ------ */
22 22
23 table.summary-list { 23 table.summary-list {
24 - margin: 0 50px 50px 50px; 24 + margin: 0 20px 16px 12px;
25 font-size: 10pt; 25 font-size: 10pt;
26 border-spacing: 0; 26 border-spacing: 0;
27 } 27 }
......
...@@ -48,6 +48,10 @@ body.dark { ...@@ -48,6 +48,10 @@ body.dark {
48 height: 100%; 48 height: 100%;
49 } 49 }
50 50
51 +#view h2 {
52 + padding-left: 12px;
53 +}
54 +
51 .light #view h2 { 55 .light #view h2 {
52 color: #800; 56 color: #800;
53 } 57 }
......