blob: 62df8724f1fd7f4c3e8b7850250f2ce01b0637d0 [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])
5 .scale(8000)
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 = {
167 aggregation: 90,
168 edge: 7
169}
170
171function makeSwitchesModel(switches, className) {
Paul Greyson981e8c22013-04-09 17:43:59 -0700172 var switchesModel = [];
173 switches.forEach(function (s) {
Paul Greyson4e348b92013-04-09 21:02:06 -0700174 var geo = model.configuration.geo[s.dpid];
175
176 var pos, label;
177 if (geo) {
178 pos = projection([geo.lng, geo.lat]);
179 label = geo.label;
180 } else {
181 var upstream = getUpstream(s.dpid, className);
182 if (upstream) {
183 var upstreamGeo = fanouts[upstream];
184 pos = projection([upstreamGeo.lng, upstreamGeo.lat]);
185
186 var fanOutAngle = upstreamGeo.fanOutAngle;
187 fanOutAngle -= (upstreamGeo.count - 1) * fanOutAngles[className]/2;
188
189 var angle = toRadians(fanOutAngle);
190 var xOff = Math.sin(angle) * widths[className] * 20;
191 var yOff = Math.cos(angle) * widths[className] * 20;
192
193 pos = [pos[0] + xOff, pos[1] + yOff];
194
195 var fakeGeo = projection.invert(pos);
196
197 var fanout = fanouts[s.dpid];
198 fanout.fanOutAngle = fanOutAngle;
199 fanout.lng = fakeGeo[0];
200 fanout.lat = fakeGeo[1];
201
202 upstreamGeo.fanOutAngle += fanOutAngles[className];
203
204 } else {
205 pos = projection([-98, 39]);
206 }
207 }
208
Paul Greyson981e8c22013-04-09 17:43:59 -0700209 switchesModel.push({
210 dpid: s.dpid,
211 state: s.state,
Paul Greyson4e348b92013-04-09 21:02:06 -0700212 className: className,
Paul Greyson981e8c22013-04-09 17:43:59 -0700213 controller: s.controller,
Paul Greyson4e348b92013-04-09 21:02:06 -0700214 label: label,
215 x: pos[0],
216 y: pos[1]
Paul Greyson981e8c22013-04-09 17:43:59 -0700217 });
218 });
219
220 return switchesModel;
Paul Greysonc090d142013-04-09 16:59:03 -0700221}
222
Paul Greyson4e348b92013-04-09 21:02:06 -0700223function switchEnter(d) {
224 var g = d3.select(this);
Paul Greyson15e5da22013-04-10 00:16:27 -0700225 var width;
Paul Greysonc090d142013-04-09 16:59:03 -0700226
Paul Greyson15e5da22013-04-10 00:16:27 -0700227 // attempt to draw an svg switch
228 if (false && d.className == 'core') {
229 width = 30;
230 g.select(function () {
231 return this.appendChild(switchXML.cloneNode(true));
232 })
233 .classed(d.className, true)
234 .attr('x', d.x - 30)
235 .attr('y', d.y - 30);
236
237 } else {
238 width = widths[d.className];
239 g.append('svg:circle').attr('r', width)
240 .classed(d.className, true)
241 .attr('cx', d.x)
242 .attr('cy', d.y);
243 }
244
Paul Greyson981e8c22013-04-09 17:43:59 -0700245
Paul Greyson4e348b92013-04-09 21:02:06 -0700246 if (d.label) {
247 g.append('svg:text')
Paul Greyson45aceb22013-04-09 22:17:03 -0700248 .classed('label', true)
Paul Greyson4e348b92013-04-09 21:02:06 -0700249 .text(d.label)
Paul Greyson45aceb22013-04-09 22:17:03 -0700250 .attr("text-anchor", "end")
251 .attr('x', d.x - width)
252 .attr('y', d.y - width);
Paul Greyson981e8c22013-04-09 17:43:59 -0700253 }
Paul Greyson4e348b92013-04-09 21:02:06 -0700254}
Paul Greyson981e8c22013-04-09 17:43:59 -0700255
Paul Greyson45aceb22013-04-09 22:17:03 -0700256function labelsEnter(switches) {
257 return labelsLayer.selectAll('g').data(switches, function (d) {
258 return d.dpid;
259 }).enter().append('svg:g')
260 .classed('nolabel', true)
261 .attr("id", function (data) {
262 return data.dpid + '-label';
263 })
264 .append("svg:text")
265 .text(function (data) {return data.dpid;})
266 .attr('x', function (d) {
267 return d.x;
268 })
269 .attr('y', function (d) {
270 return d.y;
271 })
272 .attr("text-anchor", function (d) {
273 return d.x > 500 ? "end" : "start";
274 })
275
276}
277
Paul Greyson4e348b92013-04-09 21:02:06 -0700278function switchesEnter(switches) {
279 return switchLayer.selectAll('g').data(switches, function (d) {
280 return d.dpid;
281 })
Paul Greyson981e8c22013-04-09 17:43:59 -0700282 .enter()
Paul Greyson4e348b92013-04-09 21:02:06 -0700283 .append('svg:g')
284 .attr("id", function (d) {
285 return d.dpid;
286 })
287 .attr('x', function (d) {
288 return d.x;
289 })
290 .attr('y', function (d) {
291 return d.y;
292 })
293 .each(switchEnter);
294}
295
Paul Greyson981e8c22013-04-09 17:43:59 -0700296
Paul Greyson4e348b92013-04-09 21:02:06 -0700297function switchesUpdate(switches) {
298 switches.each(function (d) {
Paul Greyson981e8c22013-04-09 17:43:59 -0700299 // if there's a pending state changed and then the state changes, clear the pending class
300 var circle = d3.select(this);
Paul Greyson4e348b92013-04-09 21:02:06 -0700301 if (d.state === 'ACTIVE' && circle.classed('inactive') ||
302 d.state === 'INACTIVE' && circle.classed('active')) {
Paul Greyson981e8c22013-04-09 17:43:59 -0700303 circle.classed('pending', false);
304 }
305 })
Paul Greyson4e348b92013-04-09 21:02:06 -0700306 .attr('class', function (d) {
307 if (d.state === 'ACTIVE' && d.controller) {
Paul Greysonb9e879e2013-04-09 21:16:16 -0700308 return 'active ' + controllerColorMap[d.controller];
Paul Greyson981e8c22013-04-09 17:43:59 -0700309 } else {
Paul Greysonb9e879e2013-04-09 21:16:16 -0700310 return 'inactive ' + 'colorInactive';
Paul Greyson981e8c22013-04-09 17:43:59 -0700311 }
312 });
Paul Greyson4e348b92013-04-09 21:02:06 -0700313}
Paul Greyson981e8c22013-04-09 17:43:59 -0700314
Paul Greyson4e348b92013-04-09 21:02:06 -0700315drawTopology = function () {
316
317 makeSwitchMap();
318 makeAssociations();
319 makeFanouts();
320
321 var coreSwitches = makeSwitchesModel(model.coreSwitches, 'core');
322 var aggregationSwitches = makeSwitchesModel(model.aggregationSwitches, 'aggregation');
323 var edgeSwitches = makeSwitchesModel(model.edgeSwitches, 'edge');
324
325 var switches = coreSwitches.concat(aggregationSwitches).concat(edgeSwitches);
326
327 switchesUpdate(switchesEnter(switches));
328
Paul Greyson45aceb22013-04-09 22:17:03 -0700329 drawLinkLines();
Paul Greyson4e348b92013-04-09 21:02:06 -0700330
Paul Greyson45aceb22013-04-09 22:17:03 -0700331 labelsEnter(switches);
Paul Greysonc090d142013-04-09 16:59:03 -0700332}
333
334})();