blob: f5ce43c1ec97ec76348e7b76564e03d1db47ebb6 [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 Greysone6532cd2013-04-12 14:53:34 -0700218 flow.flowId = liveFlow.flowId;
219 if (flow.createPending) {
220 startIPerfForFlow(flow);
221 flow.createPending = false;
222 }
Paul Greyson17f84472013-04-12 14:39:21 -0700223 flow.dataPath = liveFlow.dataPath;
224 newSelectedFlows.push(flow);
Paul Greyson7a300822013-04-09 12:57:49 -0700225 } else if (flow.createPending) {
226 newSelectedFlows.push(flow);
227 } else if (hasIPerf(flow)) {
228 clearIPerf(flow);
229 }
230 }
231 });
232 selectedFlows = newSelectedFlows;
233 }
234 selectedFlows.forEach(function (flow) {
235 if (!hasIPerf(flow)) {
236 startIPerfForFlow(flow);
237 }
238 });
239 while (selectedFlows.length < 3) {
240 selectedFlows.push(null);
241 }
242
243 updateSelectedFlowsTable();
Paul Greysonbbd708b2013-04-09 22:37:31 -0700244 // on app init, the table is updated before the svg is constructed
245 if (flowLayer) {
246 updateSelectedFlowsTopology();
247 }
Paul Greyson7a300822013-04-09 12:57:49 -0700248}
249
250function selectFlow(flow) {
251 var flowKey = makeFlowKey(flow);
252 var alreadySelected = false;
253 selectedFlows.forEach(function (f) {
254 if (f && makeFlowKey(f) === flowKey) {
255 alreadySelected = true;
256 }
257 });
258
259 if (!alreadySelected) {
260 selectedFlows.unshift(flow);
261 selectedFlows = selectedFlows.slice(0, 3);
262 updateSelectedFlows();
263 }
264}
265
Paul Greyson7a300822013-04-09 12:57:49 -0700266function deselectFlow(flow, ifCreatePending) {
Paul Greysonaf750bf2013-04-09 23:26:37 -0700267 if (!flow) {
268 return;
269 }
270
Paul Greyson7a300822013-04-09 12:57:49 -0700271 var flowKey = makeFlowKey(flow);
272 var newSelectedFlows = [];
273 selectedFlows.forEach(function (flow) {
274 if (!flow ||
275 flowKey !== makeFlowKey(flow) ||
276 flowKey === makeFlowKey(flow) && ifCreatePending && !flow.createPending ) {
277 newSelectedFlows.push(flow);
278 } else {
279 if (hasIPerf(flow)) {
280 clearIPerf(flow);
281 }
282 }
283 });
284 selectedFlows = newSelectedFlows;
285 while (selectedFlows.length < 3) {
286 selectedFlows.push(null);
287 }
288
289 updateSelectedFlows();
290}
291
292function deselectFlowIfCreatePending(flow) {
293 deselectFlow(flow, true);
294}
295
296function showFlowChooser() {
297 function rowEnter(d) {
298 var row = d3.select(this);
299
300 row.append('div')
301 .classed('black-eye', true).
302 on('click', function () {
303 selectFlow(d);
304 });
305
306 row.append('div')
307 .classed('flowId', true)
308 .text(function (d) {
309 return d.flowId;
310 });
311
312 row.append('div')
313 .classed('srcDPID', true)
314 .text(function (d) {
315 return d.srcDpid;
316 });
317
318
319 row.append('div')
320 .classed('dstDPID', true)
321 .text(function (d) {
322 return d.dstDpid;
323 });
324
325 }
326
Paul Greysonf3070172013-04-09 23:37:00 -0700327 var flowChooser = d3.select(document.getElementById('flowChooser'));
328 flowChooser.html('');
329 flowChooser.style('-webkit-transform', 'translate3d(-100%, 0, 0)')
330 .style('-webkit-transition');
331
332 var flows = flowChooser
Paul Greyson7a300822013-04-09 12:57:49 -0700333 .append('div')
334 .style('pointer-events', 'auto')
335 .selectAll('.selectedFlow')
336 .data(model.flows)
337 .enter()
338 .append('div')
339 .classed('selectedFlow', true)
340 .each(rowEnter);
341
Paul Greysonf3070172013-04-09 23:37:00 -0700342
Paul Greyson7a300822013-04-09 12:57:49 -0700343 setTimeout(function () {
Paul Greysonf3070172013-04-09 23:37:00 -0700344 flowChooser.style('-webkit-transition', '-webkit-transform .25s');
345 setTimeout(function () {
346 flowChooser.style('-webkit-transform', 'translate3d(0,0,0)');
347 }, 0);
348
349
Paul Greyson7a300822013-04-09 12:57:49 -0700350 d3.select(document.body).on('click', function () {
Paul Greysonf3070172013-04-09 23:37:00 -0700351 flowChooser.style('-webkit-transform', 'translate3d(-100%, 0, 0)')
Paul Greyson7a300822013-04-09 12:57:49 -0700352 d3.select(document.body).on('click', null);
353 });
354 }, 0);
355}