blob: f4506e25aff190883d79d2409d2c81e5259a20b0 [file] [log] [blame]
Paul Greyson2794ca62013-04-10 00:33:30 -07001function startFlowAnimation(flow) {
2 if (flow.select('animate').empty()) {
3 flow.append('svg:animate')
4 .attr('attributeName', 'stroke-dashoffset')
5 .attr('attributeType', 'xml')
6 .attr('from', '500')
7 .attr('to', '-500')
8 .attr('dur', '20s')
9 .attr('repeatCount', 'indefinite');
10 }
11}
12
13function stopFlowAnimation(flow) {
14 flow.select('animate').remove();
15}
16
17
Paul Greyson7a300822013-04-09 12:57:49 -070018function updateSelectedFlowsTopology() {
19 // DRAW THE FLOWS
20 var topologyFlows = [];
21 selectedFlows.forEach(function (flow) {
22 if (flow) {
23 topologyFlows.push(flow);
24 }
25 });
26
Paul Greysonbbd708b2013-04-09 22:37:31 -070027 var flows = flowLayer.selectAll('.flow').data(topologyFlows);
Paul Greyson7a300822013-04-09 12:57:49 -070028
29 flows.enter().append("svg:path").attr('class', 'flow')
30 .attr('stroke-dasharray', '4, 10')
Paul Greyson7a300822013-04-09 12:57:49 -070031
32 flows.exit().remove();
33
34 flows.attr('d', function (d) {
35 if (!d) {
36 return;
37 }
38 var pts = [];
Paul Greyson45aceb22013-04-09 22:17:03 -070039 if (d.createPending) {
Paul Greyson7a300822013-04-09 12:57:49 -070040 // create a temporary vector to indicate the pending flow
41 var s1 = d3.select(document.getElementById(d.srcDpid));
42 var s2 = d3.select(document.getElementById(d.dstDpid));
43
44 var pt1 = document.querySelector('svg').createSVGPoint();
45 pt1.x = s1.attr('x');
46 pt1.y = s1.attr('y');
Paul Greysonbbd708b2013-04-09 22:37:31 -070047 if (drawingRings) {
48 pt1 = pt1.matrixTransform(s1[0][0].getCTM());
49 }
Paul Greyson7a300822013-04-09 12:57:49 -070050 pts.push(pt1);
51
52 var pt2 = document.querySelector('svg').createSVGPoint();
53 pt2.x = s2.attr('x');
54 pt2.y = s2.attr('y');
Paul Greysonbbd708b2013-04-09 22:37:31 -070055 if (drawingRings) {
56 pt2 = pt2.matrixTransform(s2[0][0].getCTM());
57 }
Paul Greyson7a300822013-04-09 12:57:49 -070058 pts.push(pt2);
59
Paul Greyson45aceb22013-04-09 22:17:03 -070060 } else if (d.dataPath && d.dataPath.flowEntries) {
Paul Greyson7a300822013-04-09 12:57:49 -070061 d.dataPath.flowEntries.forEach(function (flowEntry) {
62 var s = d3.select(document.getElementById(flowEntry.dpid.value));
63 // s[0] is null if the flow entry refers to a non-existent switch
64 if (s[0][0]) {
65 var pt = document.querySelector('svg').createSVGPoint();
66 pt.x = s.attr('x');
67 pt.y = s.attr('y');
Paul Greysonbbd708b2013-04-09 22:37:31 -070068 if (drawingRings) {
69 pt = pt.matrixTransform(s[0][0].getCTM());
70 }
Paul Greyson7a300822013-04-09 12:57:49 -070071 pts.push(pt);
72 } else {
73 console.log('flow refers to non-existent switch: ' + flowEntry.dpid.value);
74 }
75 });
76 }
77 if (pts.length) {
78 return line(pts);
79 } else {
80 return "M0,0";
81 }
82 })
83 .attr('id', function (d) {
84 if (d) {
85 return makeFlowKey(d);
86 }
87 })
88 .classed('pending', function (d) {
89 return d && (d.createPending || d.deletePending);
90 });
Paul Greyson7a300822013-04-09 12:57:49 -070091}
92
93function updateSelectedFlowsTable() {
94 function rowEnter(d) {
95 var row = d3.select(this);
96 row.append('div').classed('deleteFlow', true);
97 row.append('div').classed('flowId', true);
98 row.append('div').classed('srcDPID', true);
99 row.append('div').classed('dstDPID', true);
100 row.append('div').classed('iperf', true);
101
102 row.select('.iperf')
103 .append('div')
104 .attr('class', 'iperf-container')
105 .append('svg:svg')
106 .attr('viewBox', '0 0 1000 32')
107 .attr('preserveAspectRatio', 'none')
108 .append('svg:g')
109 .append('svg:path')
110 .attr('class', 'iperfdata');
111
112 row.on('mouseover', function (d) {
113 if (d) {
114 var path = document.getElementById(makeFlowKey(d));
115 d3.select(path).classed('highlight', true);
116 }
117 });
118 row.on('mouseout', function (d) {
119 if (d) {
120 var path = document.getElementById(makeFlowKey(d));
121 d3.select(path).classed('highlight', false);
122 }
123 });
124 }
125
126 function rowUpdate(d) {
127 var row = d3.select(this);
128 row.attr('id', function (d) {
129 if (d) {
130 return makeSelectedFlowKey(d);
131 }
132 });
133
134 if (!d || !hasIPerf(d)) {
135 row.select('.iperfdata')
136 .attr('d', 'M0,0');
137 }
138
139 row.select('.deleteFlow').on('click', function () {
140 deselectFlow(d);
141 });
142 row.on('dblclick', function () {
143 if (d) {
144 var prompt = 'Delete flow ' + d.flowId + '?';
Paul Greyson28cf92b2013-04-10 13:15:06 -0700145 doConfirm(prompt, function (result) {
146 if (result) {
147 deleteFlow(d);
148 d.deletePending = true;
Paul Greyson7a300822013-04-09 12:57:49 -0700149 updateSelectedFlows();
Paul Greyson28cf92b2013-04-10 13:15:06 -0700150
151 setTimeout(function () {
152 d.deletePending = false;
153 updateSelectedFlows();
154 }, pendingTimeout)
155 };
156 });
Paul Greyson7a300822013-04-09 12:57:49 -0700157 }
158 });
159
160 row.select('.flowId')
161 .text(function (d) {
162 if (d) {
163 if (d.flowId) {
164 return d.flowId;
165 } else {
166 return '0x--';
167 }
168 }
169 })
170 .classed('pending', function (d) {
171 return d && (d.createPending || d.deletePending);
172 });
173
174 row.select('.srcDPID')
175 .text(function (d) {
176 if (d) {
177 return d.srcDpid;
178 }
179 });
180
181 row.select('.dstDPID')
182 .text(function (d) {
183 if (d) {
184 return d.dstDpid;
185 }
186 });
187 }
188
189 var flows = d3.select('#selectedFlows')
190 .selectAll('.selectedFlow')
191 .data(selectedFlows);
192
193 flows.enter()
194 .append('div')
195 .classed('selectedFlow', true)
196 .each(rowEnter);
197
198 flows.each(rowUpdate);
199
200 flows.exit().remove();
201}
202
Paul Greyson7a300822013-04-09 12:57:49 -0700203function updateSelectedFlows() {
204 // make sure that all of the selected flows are either
205 // 1) valid (meaning they are in the latest list of flows)
206 // 2) pending
207 if (model) {
208 var flowMap = {};
209 model.flows.forEach(function (flow) {
210 flowMap[makeFlowKey(flow)] = flow;
211 });
212
213 var newSelectedFlows = [];
214 selectedFlows.forEach(function (flow) {
215 if (flow) {
216 var liveFlow = flowMap[makeFlowKey(flow)];
217 if (liveFlow) {
Paul Greyson17f84472013-04-12 14:39:21 -0700218 flow.dataPath = liveFlow.dataPath;
219 newSelectedFlows.push(flow);
Paul Greyson7a300822013-04-09 12:57:49 -0700220 } else if (flow.createPending) {
221 newSelectedFlows.push(flow);
222 } else if (hasIPerf(flow)) {
223 clearIPerf(flow);
224 }
225 }
226 });
227 selectedFlows = newSelectedFlows;
228 }
229 selectedFlows.forEach(function (flow) {
230 if (!hasIPerf(flow)) {
231 startIPerfForFlow(flow);
232 }
233 });
234 while (selectedFlows.length < 3) {
235 selectedFlows.push(null);
236 }
237
238 updateSelectedFlowsTable();
Paul Greysonbbd708b2013-04-09 22:37:31 -0700239 // on app init, the table is updated before the svg is constructed
240 if (flowLayer) {
241 updateSelectedFlowsTopology();
242 }
Paul Greyson7a300822013-04-09 12:57:49 -0700243}
244
245function selectFlow(flow) {
246 var flowKey = makeFlowKey(flow);
247 var alreadySelected = false;
248 selectedFlows.forEach(function (f) {
249 if (f && makeFlowKey(f) === flowKey) {
250 alreadySelected = true;
251 }
252 });
253
254 if (!alreadySelected) {
255 selectedFlows.unshift(flow);
256 selectedFlows = selectedFlows.slice(0, 3);
257 updateSelectedFlows();
258 }
259}
260
Paul Greyson7a300822013-04-09 12:57:49 -0700261function deselectFlow(flow, ifCreatePending) {
Paul Greysonaf750bf2013-04-09 23:26:37 -0700262 if (!flow) {
263 return;
264 }
265
Paul Greyson7a300822013-04-09 12:57:49 -0700266 var flowKey = makeFlowKey(flow);
267 var newSelectedFlows = [];
268 selectedFlows.forEach(function (flow) {
269 if (!flow ||
270 flowKey !== makeFlowKey(flow) ||
271 flowKey === makeFlowKey(flow) && ifCreatePending && !flow.createPending ) {
272 newSelectedFlows.push(flow);
273 } else {
274 if (hasIPerf(flow)) {
275 clearIPerf(flow);
276 }
277 }
278 });
279 selectedFlows = newSelectedFlows;
280 while (selectedFlows.length < 3) {
281 selectedFlows.push(null);
282 }
283
284 updateSelectedFlows();
285}
286
287function deselectFlowIfCreatePending(flow) {
288 deselectFlow(flow, true);
289}
290
291function showFlowChooser() {
292 function rowEnter(d) {
293 var row = d3.select(this);
294
295 row.append('div')
296 .classed('black-eye', true).
297 on('click', function () {
298 selectFlow(d);
299 });
300
301 row.append('div')
302 .classed('flowId', true)
303 .text(function (d) {
304 return d.flowId;
305 });
306
307 row.append('div')
308 .classed('srcDPID', true)
309 .text(function (d) {
310 return d.srcDpid;
311 });
312
313
314 row.append('div')
315 .classed('dstDPID', true)
316 .text(function (d) {
317 return d.dstDpid;
318 });
319
320 }
321
Paul Greysonf3070172013-04-09 23:37:00 -0700322 var flowChooser = d3.select(document.getElementById('flowChooser'));
323 flowChooser.html('');
324 flowChooser.style('-webkit-transform', 'translate3d(-100%, 0, 0)')
325 .style('-webkit-transition');
326
327 var flows = flowChooser
Paul Greyson7a300822013-04-09 12:57:49 -0700328 .append('div')
329 .style('pointer-events', 'auto')
330 .selectAll('.selectedFlow')
331 .data(model.flows)
332 .enter()
333 .append('div')
334 .classed('selectedFlow', true)
335 .each(rowEnter);
336
Paul Greysonf3070172013-04-09 23:37:00 -0700337
Paul Greyson7a300822013-04-09 12:57:49 -0700338 setTimeout(function () {
Paul Greysonf3070172013-04-09 23:37:00 -0700339 flowChooser.style('-webkit-transition', '-webkit-transform .25s');
340 setTimeout(function () {
341 flowChooser.style('-webkit-transform', 'translate3d(0,0,0)');
342 }, 0);
343
344
Paul Greyson7a300822013-04-09 12:57:49 -0700345 d3.select(document.body).on('click', function () {
Paul Greysonf3070172013-04-09 23:37:00 -0700346 flowChooser.style('-webkit-transform', 'translate3d(-100%, 0, 0)')
Paul Greyson7a300822013-04-09 12:57:49 -0700347 d3.select(document.body).on('click', null);
348 });
349 }, 0);
350}