blob: 7662aafa43c10be3472a7a49ae43c30fee4ceb16 [file] [log] [blame]
Paul Greysonc090d142013-04-09 16:59:03 -07001(function () {
2
Paul Greyson981e8c22013-04-09 17:43:59 -07003var projection = d3.geo.mercator()
Paul Greyson4e348b92013-04-09 21:02:06 -07004 .center([82, 46])
Paul Greyson5f20cae2013-04-10 09:54:30 -07005 .scale(10000)
Paul Greyson981e8c22013-04-09 17:43:59 -07006 .rotate([-180,0]);
Paul Greysonc090d142013-04-09 16:59:03 -07007
Paul Greyson15e5da22013-04-10 00:16:27 -07008var switchXML;
9
Paul Greyson981e8c22013-04-09 17:43:59 -070010function createMap(svg, cb) {
Paul Greysonc090d142013-04-09 16:59:03 -070011 topology = svg.append('svg:svg').attr('id', 'viewBox').attr('viewBox', '0 0 1000 1000').
12 attr('id', 'viewbox');
13
14 var map = topology.append("g").attr('id', 'map');
15
Paul Greysonc090d142013-04-09 16:59:03 -070016 var path = d3.geo.path().projection(projection);
17
18 d3.json('data/world.json', function(error, topology) {
19 map.selectAll('path')
20 .data(topojson.object(topology, topology.objects.world).geometries)
21 .enter()
22 .append('path')
23 .attr('d', path)
24
Paul Greyson15e5da22013-04-10 00:16:27 -070025 d3.xml('assets/switch.svg', function (xml) {
26 switchXML = document.importNode(xml.documentElement, true);;
27 cb();
28 });
Paul Greysonc090d142013-04-09 16:59:03 -070029 });
Paul Greyson981e8c22013-04-09 17:43:59 -070030}
Paul Greysonc090d142013-04-09 16:59:03 -070031
Paul Greyson4e348b92013-04-09 21:02:06 -070032/***************************************************************************************************
Paul Greysonc090d142013-04-09 16:59:03 -070033
Paul Greyson4e348b92013-04-09 21:02:06 -070034***************************************************************************************************/
35var switchMap;
36function makeSwitchMap() {
37 switchMap = {};
38 model.coreSwitches.forEach(function (s) {
39 switchMap[s.dpid] = s;
40 });
41 model.aggregationSwitches.forEach(function (s) {
42 switchMap[s.dpid] = s;
43 });
44 model.edgeSwitches.forEach(function (s) {
45 switchMap[s.dpid] = s;
46 });
47}
48
49/***************************************************************************************************
50create a map from edge->aggregation and aggreation->core switches
51***************************************************************************************************/
52var switchAssociations;
53function makeAssociations() {
54 switchAssociations = {};
55
56 var key;
57 for (key in model.configuration.association) {
58 var aggregationSwitches = model.configuration.association[key];
59 aggregationSwitches.forEach(function (s) {
60 switchAssociations[s] = key;
61 });
62 }
63}
64
65/***************************************************************************************************
66get the upstream switch. this only makes sense for aggregation and edge switches
67***************************************************************************************************/
68function getUpstream(dpid, className) {
69 if (className === 'aggregation') {
70 return switchAssociations[dpid];
71 } else if (className === 'edge') {
72 var aggregationDpid = dpid.split(':');
73 aggregationDpid[7] = '01'; // the last component of the agg switch is always '01'
74 return aggregationDpid.join(':');
75 }
76}
77
78
79
80/*****************a**********************************************************************************
81create a map to hold the fanout information for the switches
82***************************************************************************************************/
83var fanouts;
84function makeFanouts() {
85 fanouts = {};
86 model.coreSwitches.forEach(function (s) {
87 fanouts[s.dpid] = model.configuration.geo[s.dpid];
88 fanouts[s.dpid].count = 0;
89 });
90
91 model.aggregationSwitches.forEach(function (s) {
92 fanouts[s.dpid] = {count: 0};
93 var upstreamFanout = fanouts[getUpstream(s.dpid, 'aggregation')];
94 upstreamFanout.count += 1;
95 });
96
97 model.edgeSwitches.forEach(function (s) {
98 fanouts[s.dpid] = {};
99 var upstreamFanout = fanouts[getUpstream(s.dpid, 'edge')];
100 upstreamFanout.count += 1;
101 });
102}
103
104
105var projection;
106var switchLayer;
107var labelsLayer;
108var linksLayer;
Paul Greyson981e8c22013-04-09 17:43:59 -0700109createTopologyView = function (cb) {
110 var svg = createRootSVG();
Paul Greysonc090d142013-04-09 16:59:03 -0700111
Paul Greyson4e348b92013-04-09 21:02:06 -0700112 createMap(svg, function () {
113 switchLayer = topology.append('g');
114 labelsLayer = topology.append('g');
115 linksLayer = topology.append('g');
Paul Greysonbbd708b2013-04-09 22:37:31 -0700116 flowLayer = topology.append('g');
117
Paul Greyson4e348b92013-04-09 21:02:06 -0700118
119 cb();
120 });
Paul Greyson981e8c22013-04-09 17:43:59 -0700121}
Paul Greysonc090d142013-04-09 16:59:03 -0700122
Paul Greyson45aceb22013-04-09 22:17:03 -0700123function drawLinkLines() {
Paul Greyson4e348b92013-04-09 21:02:06 -0700124
125 // key on link dpids since these will come/go during demo
126 var linkLines = linksLayer.selectAll('.link').data(links, function (d) {
127 return d['src-switch']+'->'+d['dst-switch'];
128 });
129
130 // add new links
131 linkLines.enter().append("svg:path").attr("class", "link");
132
133 linkLines.attr('id', function (d) {
134 return makeLinkKey(d);
135 }).attr("d", function (d) {
136 var src = d3.select(document.getElementById(d['src-switch']));
137 var dst = d3.select(document.getElementById(d['dst-switch']));
138
139 if (src.empty() || dst.empty()) {
140 return "M0,0";
141 }
142
143 var srcPt = document.querySelector('svg').createSVGPoint();
144 srcPt.x = src.attr('x');
145 srcPt.y = src.attr('y');
146
147 var dstPt = document.querySelector('svg').createSVGPoint();
148 dstPt.x = dst.attr('x');
149 dstPt.y = dst.attr('y');
150
151 var midPt = document.querySelector('svg').createSVGPoint();
152 midPt.x = (srcPt.x + dstPt.x)/2;
153 midPt.y = (srcPt.y + dstPt.y)/2;
154
155 return line([srcPt, midPt, dstPt]);
156 })
157 .attr("marker-mid", function(d) { return "url(#arrow)"; })
158 .classed('pending', function (d) {
159 return d.pending;
160 });
161
162 // remove old links
163 linkLines.exit().remove();
164}
165
166var fanOutAngles = {
Paul Greyson5f20cae2013-04-10 09:54:30 -0700167 aggregation: 100,
168 edge: 5
169}
170
171var fanOutDistances = {
172 aggregation: 60,
173 edge: 140
Paul Greyson4e348b92013-04-09 21:02:06 -0700174}
175
176function makeSwitchesModel(switches, className) {
Paul Greyson981e8c22013-04-09 17:43:59 -0700177 var switchesModel = [];
178 switches.forEach(function (s) {
Paul Greyson4e348b92013-04-09 21:02:06 -0700179 var geo = model.configuration.geo[s.dpid];
180
181 var pos, label;
182 if (geo) {
183 pos = projection([geo.lng, geo.lat]);
184 label = geo.label;
185 } else {
186 var upstream = getUpstream(s.dpid, className);
187 if (upstream) {
188 var upstreamGeo = fanouts[upstream];
189 pos = projection([upstreamGeo.lng, upstreamGeo.lat]);
190
191 var fanOutAngle = upstreamGeo.fanOutAngle;
192 fanOutAngle -= (upstreamGeo.count - 1) * fanOutAngles[className]/2;
193
194 var angle = toRadians(fanOutAngle);
Paul Greyson5f20cae2013-04-10 09:54:30 -0700195 var xOff = Math.sin(angle) * fanOutDistances[className];
196 var yOff = Math.cos(angle) * fanOutDistances[className];
Paul Greyson4e348b92013-04-09 21:02:06 -0700197
198 pos = [pos[0] + xOff, pos[1] + yOff];
199
200 var fakeGeo = projection.invert(pos);
201
202 var fanout = fanouts[s.dpid];
203 fanout.fanOutAngle = fanOutAngle;
204 fanout.lng = fakeGeo[0];
205 fanout.lat = fakeGeo[1];
206
207 upstreamGeo.fanOutAngle += fanOutAngles[className];
208
209 } else {
210 pos = projection([-98, 39]);
211 }
212 }
213
Paul Greyson981e8c22013-04-09 17:43:59 -0700214 switchesModel.push({
215 dpid: s.dpid,
216 state: s.state,
Paul Greyson4e348b92013-04-09 21:02:06 -0700217 className: className,
Paul Greyson981e8c22013-04-09 17:43:59 -0700218 controller: s.controller,
Paul Greyson4e348b92013-04-09 21:02:06 -0700219 label: label,
220 x: pos[0],
221 y: pos[1]
Paul Greyson981e8c22013-04-09 17:43:59 -0700222 });
223 });
224
225 return switchesModel;
Paul Greysonc090d142013-04-09 16:59:03 -0700226}
227
Paul Greyson4e348b92013-04-09 21:02:06 -0700228function switchEnter(d) {
229 var g = d3.select(this);
Paul Greyson15e5da22013-04-10 00:16:27 -0700230 var width;
Paul Greysonc090d142013-04-09 16:59:03 -0700231
Paul Greyson15e5da22013-04-10 00:16:27 -0700232 // attempt to draw an svg switch
233 if (false && d.className == 'core') {
234 width = 30;
235 g.select(function () {
236 return this.appendChild(switchXML.cloneNode(true));
237 })
238 .classed(d.className, true)
239 .attr('x', d.x - 30)
240 .attr('y', d.y - 30);
241
242 } else {
243 width = widths[d.className];
244 g.append('svg:circle').attr('r', width)
245 .classed(d.className, true)
246 .attr('cx', d.x)
247 .attr('cy', d.y);
248 }
249
Paul Greyson981e8c22013-04-09 17:43:59 -0700250
Paul Greyson4e348b92013-04-09 21:02:06 -0700251 if (d.label) {
252 g.append('svg:text')
Paul Greyson45aceb22013-04-09 22:17:03 -0700253 .classed('label', true)
Paul Greyson4e348b92013-04-09 21:02:06 -0700254 .text(d.label)
Paul Greyson45aceb22013-04-09 22:17:03 -0700255 .attr("text-anchor", "end")
256 .attr('x', d.x - width)
257 .attr('y', d.y - width);
Paul Greyson981e8c22013-04-09 17:43:59 -0700258 }
Paul Greyson4e348b92013-04-09 21:02:06 -0700259}
Paul Greyson981e8c22013-04-09 17:43:59 -0700260
Paul Greyson45aceb22013-04-09 22:17:03 -0700261function labelsEnter(switches) {
262 return labelsLayer.selectAll('g').data(switches, function (d) {
263 return d.dpid;
264 }).enter().append('svg:g')
265 .classed('nolabel', true)
266 .attr("id", function (data) {
267 return data.dpid + '-label';
268 })
269 .append("svg:text")
270 .text(function (data) {return data.dpid;})
271 .attr('x', function (d) {
272 return d.x;
273 })
274 .attr('y', function (d) {
275 return d.y;
276 })
277 .attr("text-anchor", function (d) {
278 return d.x > 500 ? "end" : "start";
279 })
280
281}
282
Paul Greyson4e348b92013-04-09 21:02:06 -0700283function switchesEnter(switches) {
284 return switchLayer.selectAll('g').data(switches, function (d) {
285 return d.dpid;
286 })
Paul Greyson981e8c22013-04-09 17:43:59 -0700287 .enter()
Paul Greyson4e348b92013-04-09 21:02:06 -0700288 .append('svg:g')
289 .attr("id", function (d) {
290 return d.dpid;
291 })
292 .attr('x', function (d) {
293 return d.x;
294 })
295 .attr('y', function (d) {
296 return d.y;
297 })
298 .each(switchEnter);
299}
300
Paul Greyson981e8c22013-04-09 17:43:59 -0700301
Paul Greyson4e348b92013-04-09 21:02:06 -0700302function switchesUpdate(switches) {
303 switches.each(function (d) {
Paul Greyson981e8c22013-04-09 17:43:59 -0700304 // if there's a pending state changed and then the state changes, clear the pending class
305 var circle = d3.select(this);
Paul Greyson4e348b92013-04-09 21:02:06 -0700306 if (d.state === 'ACTIVE' && circle.classed('inactive') ||
307 d.state === 'INACTIVE' && circle.classed('active')) {
Paul Greyson981e8c22013-04-09 17:43:59 -0700308 circle.classed('pending', false);
309 }
310 })
Paul Greyson4e348b92013-04-09 21:02:06 -0700311 .attr('class', function (d) {
312 if (d.state === 'ACTIVE' && d.controller) {
Paul Greysonb9e879e2013-04-09 21:16:16 -0700313 return 'active ' + controllerColorMap[d.controller];
Paul Greyson981e8c22013-04-09 17:43:59 -0700314 } else {
Paul Greysonb9e879e2013-04-09 21:16:16 -0700315 return 'inactive ' + 'colorInactive';
Paul Greyson981e8c22013-04-09 17:43:59 -0700316 }
317 });
Paul Greyson4e348b92013-04-09 21:02:06 -0700318}
Paul Greyson981e8c22013-04-09 17:43:59 -0700319
Paul Greyson4e348b92013-04-09 21:02:06 -0700320drawTopology = function () {
321
322 makeSwitchMap();
323 makeAssociations();
324 makeFanouts();
325
326 var coreSwitches = makeSwitchesModel(model.coreSwitches, 'core');
327 var aggregationSwitches = makeSwitchesModel(model.aggregationSwitches, 'aggregation');
328 var edgeSwitches = makeSwitchesModel(model.edgeSwitches, 'edge');
329
330 var switches = coreSwitches.concat(aggregationSwitches).concat(edgeSwitches);
331
332 switchesUpdate(switchesEnter(switches));
333
Paul Greyson45aceb22013-04-09 22:17:03 -0700334 drawLinkLines();
Paul Greyson4e348b92013-04-09 21:02:06 -0700335
Paul Greyson45aceb22013-04-09 22:17:03 -0700336 labelsEnter(switches);
Paul Greysonc090d142013-04-09 16:59:03 -0700337}
338
339})();