blob: 29907f678dd05dd4316f1429c5b4993053e3cc88 [file] [log] [blame]
Paul Greyson30636252013-04-09 15:22:04 -07001(function () {
2
Paul Greysonc090d142013-04-09 16:59:03 -07003createTopologyView = function (cb) {
Paul Greysone15c4392013-04-09 15:05:31 -07004
5 window.addEventListener('resize', function () {
6 // this is too slow. instead detect first resize event and hide the paths that have explicit matrix applied
7 // either that or is it possible to position the paths so they get the automatic transform as well?
8// updateTopology();
9 });
10
Paul Greyson981e8c22013-04-09 17:43:59 -070011 var svg = createRootSVG();
Paul Greysone15c4392013-04-09 15:05:31 -070012
Paul Greysonc090d142013-04-09 16:59:03 -070013 topology = svg.append('svg:svg').attr('id', 'viewBox').attr('viewBox', '0 0 1000 1000').attr('preserveAspectRatio', 'none').
14 attr('id', 'viewbox').append('svg:g').attr('id', 'topology').attr('transform', 'translate(500 500)');
15
16 cb();
Paul Greysone15c4392013-04-09 15:05:31 -070017}
18
Paul Greyson4e348b92013-04-09 21:02:06 -070019function updateLinkLines() {
20
21 // key on link dpids since these will come/go during demo
22 var linkLines = d3.select('svg').selectAll('.link').data(links, function (d) {
23 return d['src-switch']+'->'+d['dst-switch'];
24 });
25
26 // add new links
27 linkLines.enter().append("svg:path").attr("class", "link");
28
29 linkLines.attr('id', function (d) {
30 return makeLinkKey(d);
31 }).attr("d", function (d) {
32 var src = d3.select(document.getElementById(d['src-switch']));
33 var dst = d3.select(document.getElementById(d['dst-switch']));
34
35 if (src.empty() || dst.empty()) {
36 return "M0,0";
37 }
38
39 var srcPt = document.querySelector('svg').createSVGPoint();
40 srcPt.x = src.attr('x');
41 srcPt.y = src.attr('y');
42 srcPt = srcPt.matrixTransform(src[0][0].getCTM());
43
44 var dstPt = document.querySelector('svg').createSVGPoint();
45 dstPt.x = dst.attr('x');
46 dstPt.y = dst.attr('y');
47 dstPt = dstPt.matrixTransform(dst[0][0].getCTM());
48
49 var midPt = document.querySelector('svg').createSVGPoint();
50 midPt.x = (srcPt.x + dstPt.x)/2;
51 midPt.y = (srcPt.y + dstPt.y)/2;
52
53 return line([srcPt, midPt, dstPt]);
54 })
55 .attr("marker-mid", function(d) { return "url(#arrow)"; })
56 .classed('pending', function (d) {
57 return d.pending;
58 });
59
60 // remove old links
61 linkLines.exit().remove();
62}
63
64
Paul Greyson30636252013-04-09 15:22:04 -070065function createRingTopologyModel(model) {
Paul Greysone15c4392013-04-09 15:05:31 -070066 var rings = [{
67 radius: 3,
68 width: widths.edge,
69 switches: model.edgeSwitches,
70 className: 'edge',
71 angles: []
72 }, {
73 radius: 2.25,
74 width: widths.aggregation,
75 switches: model.aggregationSwitches,
76 className: 'aggregation',
77 angles: []
78 }, {
79 radius: 0.75,
80 width: widths.core,
81 switches: model.coreSwitches,
82 className: 'core',
83 angles: []
84 }];
85
86
87 var aggRanges = {};
88
89 // arrange edge switches at equal increments
90 var k = 360 / rings[0].switches.length;
91 rings[0].switches.forEach(function (s, i) {
92 var angle = k * i;
93
94 rings[0].angles[i] = angle;
95
96 // record the angle for the agg switch layout
97 var dpid = s.dpid.split(':');
98 dpid[7] = '01'; // the last component of the agg switch is always '01'
99 var aggdpid = dpid.join(':');
100 var aggRange = aggRanges[aggdpid];
101 if (!aggRange) {
102 aggRange = aggRanges[aggdpid] = {};
103 aggRange.min = aggRange.max = angle;
104 } else {
105 aggRange.max = angle;
106 }
107 });
108
109 // arrange aggregation switches to "fan out" to edge switches
110 k = 360 / rings[1].switches.length;
111 rings[1].switches.forEach(function (s, i) {
112// rings[1].angles[i] = k * i;
113 var range = aggRanges[s.dpid];
114
115 rings[1].angles[i] = (range.min + range.max)/2;
116 });
117
118 // find the association between core switches and aggregation switches
119 var aggregationSwitchMap = {};
120 model.aggregationSwitches.forEach(function (s, i) {
121 aggregationSwitchMap[s.dpid] = i;
122 });
123
124 // put core switches next to linked aggregation switches
125 k = 360 / rings[2].switches.length;
126 rings[2].switches.forEach(function (s, i) {
127// rings[2].angles[i] = k * i;
128 var associatedAggregationSwitches = model.configuration.association[s.dpid];
129 // TODO: go between if there are multiple
130 var index = aggregationSwitchMap[associatedAggregationSwitches[0]];
131
132 rings[2].angles[i] = rings[1].angles[index];
133 });
134
135 // TODO: construct this form initially rather than converting. it works better because
136 // it allows binding by dpid
137 var testRings = [];
138 rings.forEach(function (ring) {
139 var testRing = [];
140 ring.switches.forEach(function (s, i) {
141 var testSwitch = {
142 dpid: s.dpid,
143 state: s.state,
144 radius: ring.radius,
145 width: ring.width,
146 className: ring.className,
147 angle: ring.angles[i],
148 controller: s.controller
149 };
150 testRing.push(testSwitch);
151 });
152
153
154 testRings.push(testRing);
155 });
156
157
158// return rings;
159 return testRings;
Paul Greyson30636252013-04-09 15:22:04 -0700160}
161
162drawTopology = function () {
163 // DRAW THE SWITCHES
Paul Greysonc090d142013-04-09 16:59:03 -0700164 var rings = topology.selectAll('.ring').data(createRingTopologyModel(model));
Paul Greyson30636252013-04-09 15:22:04 -0700165
166 function ringEnter(data, i) {
167 if (!data.length) {
168 return;
169 }
170
171 // create the nodes
172 var nodes = d3.select(this).selectAll("g")
173 .data(data, function (data) {
174 return data.dpid;
175 })
176 .enter().append("svg:g")
177 .attr("id", function (data, i) {
178 return data.dpid;
179 })
180 .attr("transform", function(data, i) {
181 return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
182 });
183
184 // add the cirles representing the switches
185 nodes.append("svg:circle")
186 .attr("transform", function(data, i) {
187 var m = document.querySelector('#viewbox').getTransformToElement().inverse();
188 if (data.scale) {
189 m = m.scale(data.scale);
190 }
191 return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
192 })
193 .attr("x", function (data) {
194 return -data.width / 2;
195 })
196 .attr("y", function (data) {
197 return -data.width / 2;
198 })
199 .attr("r", function (data) {
200 return data.width;
201 });
202 }
203
204 // append switches
205 rings.enter().append("svg:g")
206 .attr("class", "ring")
207 .each(ringEnter);
208
209
210 function ringUpdate(data, i) {
211 var nodes = d3.select(this).selectAll("g")
212 .data(data, function (data) {
213 return data.dpid;
214 });
215 nodes.select('circle')
216 .each(function (data) {
217 // if there's a pending state changed and then the state changes, clear the pending class
218 var circle = d3.select(this);
219 if (data.state === 'ACTIVE' && circle.classed('inactive') ||
220 data.state === 'INACTIVE' && circle.classed('active')) {
221 circle.classed('pending', false);
222 }
223 })
224 .attr('class', function (data) {
225 if (data.state === 'ACTIVE' && data.controller) {
226 return data.className + ' active ' + controllerColorMap[data.controller];
227 } else {
228 return data.className + ' inactive ' + 'colorInactive';
229 }
230 });
231 }
232
233 // update switches
234 rings.each(ringUpdate);
235
236
237 // Now setup the labels
238 // This is done separately because SVG draws in node order and we want the labels
239 // always on top
Paul Greysonc090d142013-04-09 16:59:03 -0700240 var labelRings = topology.selectAll('.labelRing').data(createRingTopologyModel(model));
Paul Greyson30636252013-04-09 15:22:04 -0700241
242 function labelRingEnter(data) {
243 if (!data.length) {
244 return;
245 }
246
247 // create the nodes
248 var nodes = d3.select(this).selectAll("g")
249 .data(data, function (data) {
250 return data.dpid;
251 })
252 .enter().append("svg:g")
253 .classed('nolabel', true)
254 .attr("id", function (data) {
255 return data.dpid + '-label';
256 })
257 .attr("transform", function(data, i) {
258 return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
259 })
260
261 // add the text nodes which show on mouse over
262 nodes.append("svg:text")
263 .text(function (data) {return data.dpid;})
264 .attr("x", function (data) {
265 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
266 if (data.className == 'edge') {
267 return - data.width*3 - 4;
268 } else {
269 return - data.width - 4;
270 }
271 } else {
272 if (data.className == 'edge') {
273 return data.width*3 + 4;
274 } else {
275 return data.width + 4;
276 }
277 }
278 })
279 .attr("y", function (data) {
280 var y;
281 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
282 if (data.className == 'edge') {
283 y = data.width*3/2 + 4;
284 } else {
285 y = data.width/2 + 4;
286 }
287 } else {
288 if (data.className == 'edge') {
289 y = data.width*3/2 + 4;
290 } else {
291 y = data.width/2 + 4;
292 }
293 }
294 return y - 6;
295 })
296 .attr("text-anchor", function (data) {
297 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
298 return "end";
299 } else {
300 return "start";
301 }
302 })
303 .attr("transform", function(data) {
304 var m = document.querySelector('#viewbox').getTransformToElement().inverse();
305 if (data.scale) {
306 m = m.scale(data.scale);
307 }
308 return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
309 })
310 }
311
312 labelRings.enter().append("svg:g")
313 .attr("class", "textRing")
314 .each(labelRingEnter);
315
316 // switches should not change during operation of the ui so no
317 // rings.exit()
Paul Greyson4e348b92013-04-09 21:02:06 -0700318
319 updateLinkLines();
Paul Greyson30636252013-04-09 15:22:04 -0700320}
321
322})();