Simon Hunt
Committed by Gerrit Code Review

GUI -- deleted old files.

Change-Id: I3a504fe7e0597ae1d1bb1f659cd70b0611cadda4
Showing 32 changed files with 0 additions and 3786 deletions
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -/*
18 - Geometry library - based on work by Mike Bostock.
19 - */
20 -
21 -(function() {
22 -
23 - if (typeof geo == 'undefined') {
24 - geo = {};
25 - }
26 -
27 - var tolerance = 1e-10;
28 -
29 - function eq(a, b) {
30 - return (Math.abs(a - b) < tolerance);
31 - }
32 -
33 - function gt(a, b) {
34 - return (a - b > -tolerance);
35 - }
36 -
37 - function lt(a, b) {
38 - return gt(b, a);
39 - }
40 -
41 - geo.eq = eq;
42 - geo.gt = gt;
43 - geo.lt = lt;
44 -
45 - geo.LineSegment = function(x1, y1, x2, y2) {
46 - this.x1 = x1;
47 - this.y1 = y1;
48 - this.x2 = x2;
49 - this.y2 = y2;
50 -
51 - // Ax + By = C
52 - this.a = y2 - y1;
53 - this.b = x1 - x2;
54 - this.c = x1 * this.a + y1 * this.b;
55 -
56 - if (eq(this.a, 0) && eq(this.b, 0)) {
57 - throw new Error(
58 - 'Cannot construct a LineSegment with two equal endpoints.');
59 - }
60 - };
61 -
62 - geo.LineSegment.prototype.intersect = function(that) {
63 - var d = (this.x1 - this.x2) * (that.y1 - that.y2) -
64 - (this.y1 - this.y2) * (that.x1 - that.x2);
65 -
66 - if (eq(d, 0)) {
67 - // The two lines are parallel or very close.
68 - return {
69 - x : NaN,
70 - y : NaN
71 - };
72 - }
73 -
74 - var t1 = this.x1 * this.y2 - this.y1 * this.x2,
75 - t2 = that.x1 * that.y2 - that.y1 * that.x2,
76 - x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d,
77 - y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d,
78 - in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) &&
79 - gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))),
80 - in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) &&
81 - gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2)));
82 -
83 - return {
84 - x : x,
85 - y : y,
86 - in1 : in1,
87 - in2 : in2
88 - };
89 - };
90 -
91 - geo.LineSegment.prototype.x = function(y) {
92 - // x = (C - By) / a;
93 - if (this.a) {
94 - return (this.c - this.b * y) / this.a;
95 - } else {
96 - // a == 0 -> horizontal line
97 - return NaN;
98 - }
99 - };
100 -
101 - geo.LineSegment.prototype.y = function(x) {
102 - // y = (C - Ax) / b;
103 - if (this.b) {
104 - return (this.c - this.a * x) / this.b;
105 - } else {
106 - // b == 0 -> vertical line
107 - return NaN;
108 - }
109 - };
110 -
111 - geo.LineSegment.prototype.length = function() {
112 - return Math.sqrt(
113 - (this.y2 - this.y1) * (this.y2 - this.y1) +
114 - (this.x2 - this.x1) * (this.x2 - this.x1));
115 - };
116 -
117 - geo.LineSegment.prototype.offset = function(x, y) {
118 - return new geo.LineSegment(
119 - this.x1 + x, this.y1 + y,
120 - this.x2 + x, this.y2 + y);
121 - };
122 -
123 -})();
1 -<!DOCTYPE html>
2 -<!--
3 - ~ Copyright 2014 Open Networking Laboratory
4 - ~
5 - ~ Licensed under the Apache License, Version 2.0 (the "License");
6 - ~ you may not use this file except in compliance with the License.
7 - ~ You may obtain a copy of the License at
8 - ~
9 - ~ http://www.apache.org/licenses/LICENSE-2.0
10 - ~
11 - ~ Unless required by applicable law or agreed to in writing, software
12 - ~ distributed under the License is distributed on an "AS IS" BASIS,
13 - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 - ~ See the License for the specific language governing permissions and
15 - ~ limitations under the License.
16 - -->
17 -
18 -<!--
19 - ONOS UI - single page web app
20 -
21 - @author Simon Hunt
22 - -->
23 -<html>
24 -<head>
25 - <meta charset="utf-8">
26 - <title>ONOS GUI</title>
27 -
28 - <!--TODO: use the minified version of d3, once debugging is complete -->
29 - <script src="../tp/d3.js"></script>
30 - <script src="../tp/jquery-2.1.1.min.js"></script>
31 -
32 - <link rel="stylesheet" href="../base.css">
33 - <link rel="stylesheet" href="onos.css">
34 -
35 - <script src="../geometry.js"></script>
36 - <script src="onos.js"></script>
37 -
38 -</head>
39 -<body>
40 - <div id="frame">
41 - <div id="mast">
42 - <img id="logo" src="../img/onos-logo.png">
43 - <span class="title">Open Network Operating System</span>
44 - <span id="displayModes" class="right">
45 - <span id="showAll" class="radio active">All Layers</span>
46 - <span id="showPkt" class="radio">Packet Only</span>
47 - <span id="showOpt" class="radio">Optical Only</span>
48 - </span>
49 - </div>
50 - <div id="view">
51 - <!-- NOTE: svg layer injected here -->
52 - </div>
53 - <div id="flyout"></div>
54 - </div>
55 -
56 - <!-- Initialize the UI...-->
57 - <script type="text/javascript">
58 - var ONOS = $.onos({note: "config, if needed"});
59 - </script>
60 -
61 - <!-- include module files-->
62 - <!-- + mast.js-->
63 - <!-- + nav.js-->
64 - <!-- + .... application views-->
65 -
66 - <!-- for now, we are just bootstrapping the network visualization-->
67 - <script src="network.js" type="text/javascript"></script>
68 -
69 - <!-- finally, build the UI-->
70 - <script type="text/javascript">
71 - $(ONOS.buildUi);
72 - </script>
73 -
74 -</body>
75 -</html>
1 -{
2 - "meta": {
3 - "comments": [
4 - "This is sample data for developing the ONOS UI (network view)",
5 - " in a standalone mode (no server required).",
6 - " Eventually, we will wire this up to live data",
7 - " from the server, via a websocket.",
8 - "",
9 - "Note that this is just a first-draft of the data --",
10 - " additional fields will be added when they are needed."
11 - ],
12 - "otherMetaData": "can go here..."
13 - },
14 - "devices": [
15 - {
16 - "id": "of:0000000000000001",
17 - "labels": ["00:00:00:00:00:00:00:01", "SFO-W10", "opt-1"],
18 - "type": "roadm"
19 - },
20 - {
21 - "id": "of:0000000000000002",
22 - "labels": ["00:00:00:00:00:00:00:02", "SJC-W10", "opt-2"],
23 - "type": "roadm"
24 - },
25 - {
26 - "id": "of:0000000000000003",
27 - "labels": ["00:00:00:00:00:00:00:03", "LAX-W10", "opt-3"],
28 - "type": "roadm"
29 - },
30 - {
31 - "id": "of:0000000000000004",
32 - "labels": ["00:00:00:00:00:00:00:04", "SDG-W10", "opt-4"],
33 - "type": "roadm"
34 - },
35 - {
36 - "id": "of:0000000000000011",
37 - "labels": ["00:00:00:00:00:00:00:11", "SFO-pkt", "pkt-11"],
38 - "type": "switch"
39 - },
40 - {
41 - "id": "of:0000000000000012",
42 - "labels": ["00:00:00:00:00:00:00:12", "SJC-pkt", "pkt-12"],
43 - "type": "switch"
44 - },
45 - {
46 - "id": "of:0000000000000013",
47 - "labels": ["00:00:00:00:00:00:00:13", "LAX-pkt", "pkt-13"],
48 - "type": "switch"
49 - }
50 - ],
51 - "linkNotes": [
52 - "even though we have 'directionality' (src/dst), each link is",
53 - " considered to be bi-directional. Note that links between hosts",
54 - " and edge switches are inferred from host information, and not",
55 - " explicitly represented here."
56 - ],
57 - "links": [
58 - {
59 - "src": "of:0000000000000001",
60 - "dst": "of:0000000000000002",
61 - "type": "optical",
62 - "srcPort": 1,
63 - "dstPort": 2,
64 - "linkWidth": 1.5
65 - },
66 - {
67 - "src": "of:0000000000000001",
68 - "dst": "of:0000000000000003",
69 - "type": "optical",
70 - "srcPort": 2,
71 - "dstPort": 5,
72 - "linkWidth": 1.5
73 - },
74 - {
75 - "src": "of:0000000000000001",
76 - "dst": "of:0000000000000004",
77 - "type": "optical",
78 - "srcPort": 3,
79 - "dstPort": 2,
80 - "linkWidth": 1.5
81 - },
82 - {
83 - "src": "of:0000000000000002",
84 - "dst": "of:0000000000000003",
85 - "type": "optical",
86 - "srcPort": 3,
87 - "dstPort": 4,
88 - "linkWidth": 1.5
89 - },
90 - {
91 - "src": "of:0000000000000002",
92 - "dst": "of:0000000000000004",
93 - "type": "optical",
94 - "srcPort": 4,
95 - "dstPort": 1,
96 - "linkWidth": 1.5
97 - },
98 - {
99 - "src": "of:0000000000000003",
100 - "dst": "of:0000000000000004",
101 - "type": "optical",
102 - "srcPort": 3,
103 - "dstPort": 3,
104 - "linkWidth": 1.5
105 - },
106 - {
107 - "src": "of:0000000000000013",
108 - "dst": "of:0000000000000003",
109 - "type": "direct",
110 - "srcPort": 1,
111 - "dstPort": 7,
112 - "linkWidth": 1.0
113 - },
114 - {
115 - "src": "of:0000000000000012",
116 - "dst": "of:0000000000000002",
117 - "type": "direct",
118 - "srcPort": 1,
119 - "dstPort": 9,
120 - "linkWidth": 1.0
121 - },
122 - {
123 - "src": "of:0000000000000011",
124 - "dst": "of:0000000000000001",
125 - "type": "direct",
126 - "srcPort": 1,
127 - "dstPort": 6,
128 - "linkWidth": 1.0
129 - }
130 - ],
131 - "hosts": [
132 - {
133 - "id": "00:60:d3:00:11:01/7",
134 - "labels": ["00:60:d3:00:11:01/7", "Host-11-A"],
135 - "cp" : {
136 - "device": "of:0000000000000011",
137 - "port": 6
138 - }
139 - },
140 - {
141 - "id": "00:60:d3:00:11:02/7",
142 - "labels": ["00:60:d3:00:11:02/7", "Host-11-B"],
143 - "cp" : {
144 - "device": "of:0000000000000011",
145 - "port": 8
146 - }
147 - },
148 - {
149 - "id": "00:60:d3:00:12:01/4",
150 - "labels": ["00:60:d3:00:12:01/4", "Host-12-C"],
151 - "cp" : {
152 - "device": "of:0000000000000012",
153 - "port": 12
154 - }
155 - },
156 - {
157 - "id": "00:60:d3:00:12:02/4",
158 - "labels": ["00:60:d3:00:12:02/4", "Host-12-D"],
159 - "cp" : {
160 - "device": "of:0000000000000012",
161 - "port": 13
162 - }
163 - },
164 - {
165 - "id": "00:60:d3:00:13:01/19",
166 - "labels": ["00:60:d3:00:13:01/19", "Host-13-E"],
167 - "cp" : {
168 - "device": "of:0000000000000013",
169 - "port": 7
170 - }
171 - },
172 - {
173 - "id": "00:60:d3:00:13:02/19",
174 - "labels": ["00:60:d3:00:13:02/19", "Host-13-F"],
175 - "cp" : {
176 - "device": "of:0000000000000013",
177 - "port": 8
178 - }
179 - }
180 - ]
181 -}
1 -{
2 - "devices": [
3 - {
4 - "id": "of:0000ffffffffff08",
5 - "type": "roadm",
6 - "online": false,
7 - "labels": [
8 - "0000ffffffffff08",
9 - "FF:FF:FF:FF:FF:08",
10 - "?"
11 - ]
12 - },
13 - {
14 - "id": "of:0000ffffffffff03",
15 - "type": "roadm",
16 - "online": false,
17 - "labels": [
18 - "0000ffffffffff03",
19 - "FF:FF:FF:FF:FF:03",
20 - "?"
21 - ]
22 - },
23 - {
24 - "id": "of:0000ffffffffff02",
25 - "type": "roadm",
26 - "online": false,
27 - "labels": [
28 - "0000ffffffffff02",
29 - "FF:FF:FF:FF:FF:02",
30 - "?"
31 - ]
32 - },
33 - {
34 - "id": "of:0000ffffffff0003",
35 - "type": "switch",
36 - "online": false,
37 - "labels": [
38 - "0000ffffffff0003",
39 - "FF:FF:FF:FF:00:03",
40 - "?"
41 - ]
42 - },
43 - {
44 - "id": "of:0000ffffffffff07",
45 - "type": "roadm",
46 - "online": false,
47 - "labels": [
48 - "0000ffffffffff07",
49 - "FF:FF:FF:FF:FF:07",
50 - "?"
51 - ]
52 - },
53 - {
54 - "id": "of:0000ffffffffff06",
55 - "type": "roadm",
56 - "online": false,
57 - "labels": [
58 - "0000ffffffffff06",
59 - "FF:FF:FF:FF:FF:06",
60 - "?"
61 - ]
62 - },
63 - {
64 - "id": "of:0000ffffffff0007",
65 - "type": "switch",
66 - "online": false,
67 - "labels": [
68 - "0000ffffffff0007",
69 - "FF:FF:FF:FF:00:07",
70 - "?"
71 - ]
72 - },
73 - {
74 - "id": "of:0000ffffffffff05",
75 - "type": "roadm",
76 - "online": false,
77 - "labels": [
78 - "0000ffffffffff05",
79 - "FF:FF:FF:FF:FF:05",
80 - "?"
81 - ]
82 - },
83 - {
84 - "id": "of:0000ffffffff0009",
85 - "type": "switch",
86 - "online": false,
87 - "labels": [
88 - "0000ffffffff0009",
89 - "FF:FF:FF:FF:00:09",
90 - "?"
91 - ]
92 - },
93 - {
94 - "id": "of:0000ffffffffff04",
95 - "type": "roadm",
96 - "online": false,
97 - "labels": [
98 - "0000ffffffffff04",
99 - "FF:FF:FF:FF:FF:04",
100 - "?"
101 - ]
102 - },
103 - {
104 - "id": "of:0000ffffffff000A",
105 - "type": "switch",
106 - "online": false,
107 - "labels": [
108 - "0000ffffffff000A",
109 - "FF:FF:FF:FF:00:0A",
110 - "?"
111 - ]
112 - },
113 - {
114 - "id": "of:0000ffffffff0001",
115 - "type": "switch",
116 - "online": false,
117 - "labels": [
118 - "0000ffffffff0001",
119 - "FF:FF:FF:FF:00:01",
120 - "?"
121 - ]
122 - },
123 - {
124 - "id": "of:0000ffffffffff01",
125 - "type": "roadm",
126 - "online": false,
127 - "labels": [
128 - "0000ffffffffff01",
129 - "FF:FF:FF:FF:FF:01",
130 - "?"
131 - ]
132 - },
133 - {
134 - "id": "of:0000ffffffff0004",
135 - "type": "switch",
136 - "online": false,
137 - "labels": [
138 - "0000ffffffff0004",
139 - "FF:FF:FF:FF:00:04",
140 - "?"
141 - ]
142 - },
143 - {
144 - "id": "of:0000ffffffffff0A",
145 - "type": "roadm",
146 - "online": false,
147 - "labels": [
148 - "0000ffffffffff0A",
149 - "FF:FF:FF:FF:FF:0A",
150 - "?"
151 - ]
152 - },
153 - {
154 - "id": "of:0000ffffffffff09",
155 - "type": "roadm",
156 - "online": false,
157 - "labels": [
158 - "0000ffffffffff09",
159 - "FF:FF:FF:FF:FF:09",
160 - "?"
161 - ]
162 - }
163 - ],
164 - "links": [
165 - {
166 - "src": "of:0000ffffffffff02",
167 - "srcPort": "20",
168 - "dst": "of:0000ffffffffff05",
169 - "dstPort": "10",
170 - "type": "optical",
171 - "linkWidth": 2
172 - },
173 - {
174 - "src": "of:0000ffffffff000A",
175 - "srcPort": "2",
176 - "dst": "of:0000ffffffffff0A",
177 - "dstPort": "1",
178 - "type": "optical",
179 - "linkWidth": 2
180 - },
181 - {
182 - "src": "of:0000ffffffffff03",
183 - "srcPort": "10",
184 - "dst": "of:0000ffffffffff02",
185 - "dstPort": "10",
186 - "type": "optical",
187 - "linkWidth": 2
188 - },
189 - {
190 - "src": "of:0000ffffffffff07",
191 - "srcPort": "21",
192 - "dst": "of:0000ffffffffff05",
193 - "dstPort": "20",
194 - "type": "optical",
195 - "linkWidth": 2
196 - },
197 - {
198 - "src": "of:0000ffffffff0001",
199 - "srcPort": "2",
200 - "dst": "of:0000ffffffffff01",
201 - "dstPort": "1",
202 - "type": "optical",
203 - "linkWidth": 2
204 - },
205 - {
206 - "src": "of:0000ffffffffff09",
207 - "srcPort": "20",
208 - "dst": "of:0000ffffffffff0A",
209 - "dstPort": "20",
210 - "type": "optical",
211 - "linkWidth": 2
212 - },
213 - {
214 - "src": "of:0000ffffffffff06",
215 - "srcPort": "20",
216 - "dst": "of:0000ffffffffff05",
217 - "dstPort": "30",
218 - "type": "optical",
219 - "linkWidth": 2
220 - },
221 - {
222 - "src": "of:0000ffffffffff07",
223 - "srcPort": "30",
224 - "dst": "of:0000ffffffffff08",
225 - "dstPort": "20",
226 - "type": "optical",
227 - "linkWidth": 2
228 - },
229 - {
230 - "src": "of:0000ffffffffff03",
231 - "srcPort": "20",
232 - "dst": "of:0000ffffffffff06",
233 - "dstPort": "10",
234 - "type": "optical",
235 - "linkWidth": 2
236 - },
237 - {
238 - "src": "of:0000ffffffffff02",
239 - "srcPort": "10",
240 - "dst": "of:0000ffffffffff01",
241 - "dstPort": "10",
242 - "type": "optical",
243 - "linkWidth": 2
244 - },
245 - {
246 - "src": "of:0000ffffffffff09",
247 - "srcPort": "1",
248 - "dst": "of:0000ffffffff0009",
249 - "dstPort": "2",
250 - "type": "optical",
251 - "linkWidth": 2
252 - },
253 - {
254 - "src": "of:0000ffffffffff03",
255 - "srcPort": "30",
256 - "dst": "of:0000ffffffffff04",
257 - "dstPort": "10",
258 - "type": "optical",
259 - "linkWidth": 2
260 - },
261 - {
262 - "src": "of:0000ffffffffff07",
263 - "srcPort": "20",
264 - "dst": "of:0000ffffffffff09",
265 - "dstPort": "10",
266 - "type": "optical",
267 - "linkWidth": 2
268 - },
269 - {
270 - "src": "of:0000ffffffffff0A",
271 - "srcPort": "10",
272 - "dst": "of:0000ffffffffff08",
273 - "dstPort": "30",
274 - "type": "optical",
275 - "linkWidth": 2
276 - },
277 - {
278 - "src": "of:0000ffffffff0004",
279 - "srcPort": "2",
280 - "dst": "of:0000ffffffffff04",
281 - "dstPort": "1",
282 - "type": "optical",
283 - "linkWidth": 2
284 - },
285 - {
286 - "src": "of:0000ffffffffff07",
287 - "srcPort": "1",
288 - "dst": "of:0000ffffffff0007",
289 - "dstPort": "2",
290 - "type": "optical",
291 - "linkWidth": 2
292 - },
293 - {
294 - "src": "of:0000ffffffff0003",
295 - "srcPort": "2",
296 - "dst": "of:0000ffffffffff03",
297 - "dstPort": "1",
298 - "type": "optical",
299 - "linkWidth": 2
300 - },
301 - {
302 - "src": "of:0000ffffffffff06",
303 - "srcPort": "30",
304 - "dst": "of:0000ffffffffff08",
305 - "dstPort": "10",
306 - "type": "optical",
307 - "linkWidth": 2
308 - }
309 - ],
310 - "hosts": [
311 - {
312 - "id": "00:00:00:00:00:03/-1",
313 - "cp": {
314 - "device": "of:0000ffffffff0003",
315 - "port": 1
316 - },
317 - "labels": [
318 - "10.0.0.3",
319 - "00:00:00:00:00:03"
320 - ]
321 - },
322 - {
323 - "id": "00:00:00:00:00:04/-1",
324 - "cp": {
325 - "device": "of:0000ffffffff0004",
326 - "port": 1
327 - },
328 - "labels": [
329 - "10.0.0.4",
330 - "00:00:00:00:00:04"
331 - ]
332 - },
333 - {
334 - "id": "00:00:00:00:00:0A/-1",
335 - "cp": {
336 - "device": "of:0000ffffffff000A",
337 - "port": 1
338 - },
339 - "labels": [
340 - "10.0.0.10",
341 - "00:00:00:00:00:0A"
342 - ]
343 - },
344 - {
345 - "id": "00:00:00:00:00:09/-1",
346 - "cp": {
347 - "device": "of:0000ffffffff0009",
348 - "port": 1
349 - },
350 - "labels": [
351 - "10.0.0.9",
352 - "00:00:00:00:00:09"
353 - ]
354 - },
355 - {
356 - "id": "00:00:00:00:00:07/-1",
357 - "cp": {
358 - "device": "of:0000ffffffff0007",
359 - "port": 1
360 - },
361 - "labels": [
362 - "10.0.0.7",
363 - "00:00:00:00:00:07"
364 - ]
365 - },
366 - {
367 - "id": "00:00:00:00:00:01/-1",
368 - "cp": {
369 - "device": "of:0000ffffffff0001",
370 - "port": 1
371 - },
372 - "labels": [
373 - "10.0.0.1",
374 - "00:00:00:00:00:01"
375 - ]
376 - }
377 - ]
378 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000001",
4 - "type": "roadm",
5 - "propOrder": [ "name", "type", "-", "dpid", "latitude", "longitude", "allowed" ],
6 - "location": {
7 - "type": "latlng",
8 - "lat": 37.6,
9 - "lng": 122.3
10 - },
11 - "props": {
12 - "allowed": true,
13 - "latitude": 37.6,
14 - "longitude": 122.3,
15 - "name": "SFO-W10",
16 - "dpid": "00:00:00:00:00:00:00:01"
17 - }
18 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000002",
4 - "type": "switch",
5 - "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
6 - "location": {
7 - "type": "latlng",
8 - "lat": 37.6,
9 - "lng": 122.3
10 - },
11 - "props": {
12 - "allowed": true,
13 - "latitude": 37.3,
14 - "longitude": 121.9,
15 - "name": "SJC-W10",
16 - "dpid": "00:00:00:00:00:00:00:02",
17 - "type": "Roadm"
18 - }
19 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000003",
4 - "type": "switch",
5 - "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
6 - "location": {
7 - "type": "latlng",
8 - "lat": 33.9,
9 - "lng": 118.4
10 - },
11 - "props": {
12 - "allowed": true,
13 - "latitude": 33.9,
14 - "longitude": 118.4,
15 - "name": "LAX-W10",
16 - "dpid": "00:00:00:00:00:00:00:03",
17 - "type": "Roadm"
18 - }
19 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000004",
4 - "type": "switch",
5 - "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
6 - "location": {
7 - "type": "latlng",
8 - "lat": 32.8,
9 - "lng": 117.1
10 - },
11 - "props": {
12 - "allowed": true,
13 - "latitude": 32.8,
14 - "longitude": 117.1,
15 - "name": "SDG-W10",
16 - "dpid": "00:00:00:00:00:00:00:04",
17 - "type": "Roadm"
18 - }
19 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000011",
4 - "type": "switch",
5 - "propOrder": [ "name", "type", "dpid", "optLink" ],
6 - "props": {
7 - "name": "SFO-pkt",
8 - "dpid": "00:00:00:00:00:00:00:11",
9 - "type": "SwitchX",
10 - "optLink": "SFO-W10"
11 - }
12 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000012",
4 - "type": "switch",
5 - "propOrder": [ "name", "type", "dpid", "optLink" ],
6 - "props": {
7 - "name": "SJC-pkt",
8 - "dpid": "00:00:00:00:00:00:00:12",
9 - "type": "SwitchX",
10 - "optLink": "SJC-W10"
11 - }
12 -}
1 -{
2 - "comment": "sample device properties",
3 - "id": "of:0000000000000013",
4 - "type": "switch",
5 - "propOrder": [ "name", "type", "dpid", "optLink" ],
6 - "props": {
7 - "name": "LAX-pkt",
8 - "dpid": "00:00:00:00:00:00:00:13",
9 - "type": "SwitchX",
10 - "optLink": "LAX-W10"
11 - }
12 -}
1 -{
2 - "id": "of:0000ffffffff0007",
3 - "type": "switch",
4 - "propOrder": [
5 - "Name",
6 - "Vendor",
7 - "H/W Version",
8 - "S/W Version",
9 - "S/W Version",
10 - "-",
11 - "Latitude",
12 - "Longitude",
13 - "Ports"
14 - ],
15 - "props": {
16 - "Name": null,
17 - "Vendor": "Linc",
18 - "H/W Version": "PK",
19 - "S/W Version": "?",
20 - "-": "",
21 - "Latitude": "41.8",
22 - "Longitude": "120.1",
23 - "Ports": "2"
24 - }
25 -}
1 -{
2 - "id": "of:0000ffffffff0009",
3 - "type": "switch",
4 - "propOrder": [
5 - "Name",
6 - "Vendor",
7 - "H/W Version",
8 - "S/W Version",
9 - "S/W Version",
10 - "-",
11 - "Latitude",
12 - "Longitude",
13 - "Ports"
14 - ],
15 - "props": {
16 - "Name": null,
17 - "Vendor": "Linc",
18 - "H/W Version": "PK",
19 - "S/W Version": "?",
20 - "-": "",
21 - "Latitude": "40.8",
22 - "Longitude": "73.1",
23 - "Ports": "2"
24 - }
25 -}
1 -{
2 - "id": "of:0000ffffffffff07",
3 - "type": "roadm",
4 - "propOrder": [
5 - "Name",
6 - "Vendor",
7 - "H/W Version",
8 - "S/W Version",
9 - "S/W Version",
10 - "-",
11 - "Latitude",
12 - "Longitude",
13 - "Ports"
14 - ],
15 - "props": {
16 - "Name": null,
17 - "Vendor": "Linc",
18 - "H/W Version": "OE",
19 - "S/W Version": "?",
20 - "-": "",
21 - "Latitude": "41.8",
22 - "Longitude": "120.1",
23 - "Ports": "2"
24 - }
25 -}
1 -{
2 - "id": "of:0000ffffffffff09",
3 - "type": "roadm",
4 - "propOrder": [
5 - "Name",
6 - "Vendor",
7 - "H/W Version",
8 - "S/W Version",
9 - "S/W Version",
10 - "-",
11 - "Latitude",
12 - "Longitude",
13 - "Ports"
14 - ],
15 - "props": {
16 - "Name": null,
17 - "Vendor": "Linc",
18 - "H/W Version": "OE",
19 - "S/W Version": "?",
20 - "-": "",
21 - "Latitude": "40.8",
22 - "Longitude": "73.1",
23 - "Ports": "2"
24 - }
25 -}
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -/*
18 - ONOS network topology viewer - PoC version 1.0
19 -
20 - @author Simon Hunt
21 - */
22 -
23 -(function (onos) {
24 - 'use strict';
25 -
26 - // reference to the framework api
27 - var api = onos.api;
28 -
29 - // configuration data
30 - var config = {
31 - useLiveData: false,
32 - debugOn: false,
33 - debug: {
34 - showNodeXY: false,
35 - showKeyHandler: true
36 - },
37 - options: {
38 - layering: true,
39 - collisionPrevention: true,
40 - loadBackground: true
41 - },
42 - backgroundUrl: 'img/us-map.png',
43 - data: {
44 - live: {
45 - jsonUrl: 'rs/topology/graph',
46 - detailPrefix: 'rs/topology/graph/',
47 - detailSuffix: ''
48 - },
49 - fake: {
50 - jsonUrl: 'json/network2.json',
51 - detailPrefix: 'json/',
52 - detailSuffix: '.json'
53 - }
54 - },
55 - iconUrl: {
56 - device: 'img/device.png',
57 - host: 'img/host.png',
58 - pkt: 'img/pkt.png',
59 - opt: 'img/opt.png'
60 - },
61 - mastHeight: 36,
62 - force: {
63 - note: 'node.class or link.class is used to differentiate',
64 - linkDistance: {
65 - infra: 200,
66 - host: 40
67 - },
68 - linkStrength: {
69 - infra: 1.0,
70 - host: 1.0
71 - },
72 - charge: {
73 - device: -800,
74 - host: -1000
75 - },
76 - ticksWithoutCollisions: 50,
77 - marginLR: 20,
78 - marginTB: 20,
79 - translate: function() {
80 - return 'translate(' +
81 - config.force.marginLR + ',' +
82 - config.force.marginTB + ')';
83 - }
84 - },
85 - labels: {
86 - imgPad: 16,
87 - padLR: 8,
88 - padTB: 6,
89 - marginLR: 3,
90 - marginTB: 2,
91 - port: {
92 - gap: 3,
93 - width: 18,
94 - height: 14
95 - }
96 - },
97 - icons: {
98 - w: 32,
99 - h: 32,
100 - xoff: -12,
101 - yoff: -8
102 - },
103 - constraints: {
104 - ypos: {
105 - host: 0.05,
106 - switch: 0.3,
107 - roadm: 0.7
108 - }
109 - },
110 - hostLinkWidth: 1.0,
111 - hostRadius: 7,
112 - mouseOutTimerDelayMs: 120
113 - };
114 -
115 - // state variables
116 - var view = {},
117 - network = {},
118 - selected = {},
119 - highlighted = null,
120 - hovered = null,
121 - viewMode = 'showAll',
122 - portLabelsOn = false;
123 -
124 -
125 - function debug(what) {
126 - return config.debugOn && config.debug[what];
127 - }
128 -
129 - function urlData() {
130 - return config.data[config.useLiveData ? 'live' : 'fake'];
131 - }
132 -
133 - function networkJsonUrl() {
134 - return urlData().jsonUrl;
135 - }
136 -
137 - function safeId(id) {
138 - return id.replace(/[^a-z0-9]/gi, '_');
139 - }
140 -
141 - function detailJsonUrl(id) {
142 - var u = urlData(),
143 - encId = config.useLiveData ? encodeURIComponent(id) : safeId(id);
144 - return u.detailPrefix + encId + u.detailSuffix;
145 - }
146 -
147 -
148 - // load the topology view of the network
149 - function loadNetworkView() {
150 - // Hey, here I am, calling something on the ONOS api:
151 - api.printTime();
152 -
153 - resize();
154 -
155 - // go get our network data from the server...
156 - var url = networkJsonUrl();
157 - d3.json(url , function (err, data) {
158 - if (err) {
159 - alert('Oops! Error reading JSON...\n\n' +
160 - 'URL: ' + url + '\n\n' +
161 - 'Error: ' + err.message);
162 - return;
163 - }
164 -// console.log("here is the JSON data...");
165 -// console.log(data);
166 -
167 - network.data = data;
168 - drawNetwork();
169 - });
170 -
171 - // while we wait for the data, set up the handlers...
172 - setUpClickHandler();
173 - setUpRadioButtonHandler();
174 - setUpKeyHandler();
175 - $(window).on('resize', resize);
176 - }
177 -
178 - function setUpClickHandler() {
179 - // click handler for "selectable" objects
180 - $(document).on('click', '.select-object', function () {
181 - // when any object of class "select-object" is clicked...
182 - var obj = network.lookup[$(this).data('id')];
183 - if (obj) {
184 - selectObject(obj);
185 - }
186 - // stop propagation of event (I think) ...
187 - return false;
188 - });
189 - }
190 -
191 - function setUpRadioButtonHandler() {
192 - d3.selectAll('#displayModes .radio').on('click', function () {
193 - var id = d3.select(this).attr('id');
194 - if (id !== viewMode) {
195 - radioButton('displayModes', id);
196 - viewMode = id;
197 - doRadioAction(id);
198 - }
199 - });
200 - }
201 -
202 - function doRadioAction(id) {
203 - showAllLayers();
204 - if (id === 'showPkt') {
205 - showPacketLayer();
206 - } else if (id === 'showOpt') {
207 - showOpticalLayer();
208 - }
209 - }
210 -
211 - function showAllLayers() {
212 - network.node.classed('inactive', false);
213 - network.link.classed('inactive', false);
214 - d3.selectAll('svg .port').classed('inactive', false)
215 - d3.selectAll('svg .portText').classed('inactive', false)
216 - }
217 -
218 - function showPacketLayer() {
219 - network.node.each(function(d) {
220 - // deactivate nodes that are not hosts or switches
221 - if (d.class === 'device' && d.type !== 'switch') {
222 - d3.select(this).classed('inactive', true);
223 - }
224 - });
225 -
226 - network.link.each(function(lnk) {
227 - // deactivate infrastructure links that have opt's as endpoints
228 - if (lnk.source.type === 'roadm' || lnk.target.type === 'roadm') {
229 - d3.select(this).classed('inactive', true);
230 - }
231 - });
232 -
233 - // deactivate non-packet ports
234 - d3.selectAll('svg .optPort').classed('inactive', true)
235 - }
236 -
237 - function showOpticalLayer() {
238 - network.node.each(function(d) {
239 - // deactivate nodes that are not optical devices
240 - if (d.type !== 'roadm') {
241 - d3.select(this).classed('inactive', true);
242 - }
243 - });
244 -
245 - network.link.each(function(lnk) {
246 - // deactivate infrastructure links that have opt's as endpoints
247 - if (lnk.source.type !== 'roadm' || lnk.target.type !== 'roadm') {
248 - d3.select(this).classed('inactive', true);
249 - }
250 - });
251 -
252 - // deactivate non-packet ports
253 - d3.selectAll('svg .pktPort').classed('inactive', true)
254 - }
255 -
256 - function setUpKeyHandler() {
257 - d3.select('body')
258 - .on('keydown', function () {
259 - processKeyEvent();
260 - if (debug('showKeyHandler')) {
261 - network.svg.append('text')
262 - .attr('x', 5)
263 - .attr('y', 15)
264 - .style('font-size', '20pt')
265 - .text('keyCode: ' + d3.event.keyCode +
266 - ' applied to : ' + contextLabel())
267 - .transition().duration(2000)
268 - .style('font-size', '2pt')
269 - .style('fill-opacity', 0.01)
270 - .remove();
271 - }
272 - });
273 - }
274 -
275 - function contextLabel() {
276 - return hovered === null ? "(nothing)" : hovered.id;
277 - }
278 -
279 - function radioButton(group, id) {
280 - d3.selectAll("#" + group + " .radio").classed("active", false);
281 - d3.select("#" + group + " #" + id).classed("active", true);
282 - }
283 -
284 - function processKeyEvent() {
285 - var code = d3.event.keyCode;
286 - switch (code) {
287 - case 66: // B
288 - toggleBackground();
289 - break;
290 - case 71: // G
291 - cycleLayout();
292 - break;
293 - case 76: // L
294 - cycleLabels();
295 - break;
296 - case 80: // P
297 - togglePorts();
298 - break;
299 - case 85: // U
300 - unpin();
301 - break;
302 - }
303 -
304 - }
305 -
306 - function toggleBackground() {
307 - var bg = d3.select('#bg'),
308 - vis = bg.style('visibility'),
309 - newvis = (vis === 'hidden') ? 'visible' : 'hidden';
310 - bg.style('visibility', newvis);
311 - }
312 -
313 - function cycleLayout() {
314 - config.options.layering = !config.options.layering;
315 - network.force.resume();
316 - }
317 -
318 - function cycleLabels() {
319 - console.log('Cycle Labels - context = ' + contextLabel());
320 - }
321 -
322 - function togglePorts() {
323 - portLabelsOn = !portLabelsOn;
324 - var portVis = portLabelsOn ? 'visible' : 'hidden';
325 - d3.selectAll('.port').style('visibility', portVis);
326 - d3.selectAll('.portText').style('visibility', portVis);
327 - }
328 -
329 - function unpin() {
330 - if (hovered) {
331 - hovered.fixed = false;
332 - findNodeFromData(hovered).classed('fixed', false);
333 - network.force.resume();
334 - }
335 - console.log('Unpin - context = ' + contextLabel());
336 - }
337 -
338 -
339 - // ========================================================
340 -
341 - function drawNetwork() {
342 - $('#view').empty();
343 -
344 - prepareNodesAndLinks();
345 - createLayout();
346 - console.log("\n\nHere is the augmented network object...");
347 - console.log(network);
348 - }
349 -
350 - function prepareNodesAndLinks() {
351 - network.lookup = {};
352 - network.nodes = [];
353 - network.links = [];
354 -
355 - var nw = network.forceWidth,
356 - nh = network.forceHeight;
357 -
358 - function yPosConstraintForNode(n) {
359 - return config.constraints.ypos[n.type || 'host'];
360 - }
361 -
362 - // Note that both 'devices' and 'hosts' get mapped into the nodes array
363 -
364 - // first, the devices...
365 - network.data.devices.forEach(function(n) {
366 - var ypc = yPosConstraintForNode(n),
367 - ix = Math.random() * 0.6 * nw + 0.2 * nw,
368 - iy = ypc * nh,
369 - node = {
370 - id: n.id,
371 - labels: n.labels,
372 - class: 'device',
373 - icon: 'device',
374 - type: n.type,
375 - x: ix,
376 - y: iy,
377 - constraint: {
378 - weight: 0.7,
379 - y: iy
380 - }
381 - };
382 - network.lookup[n.id] = node;
383 - network.nodes.push(node);
384 - });
385 -
386 - // then, the hosts...
387 - network.data.hosts.forEach(function(n) {
388 - var ypc = yPosConstraintForNode(n),
389 - ix = Math.random() * 0.6 * nw + 0.2 * nw,
390 - iy = ypc * nh,
391 - node = {
392 - id: n.id,
393 - labels: n.labels,
394 - class: 'host',
395 - icon: 'host',
396 - type: n.type,
397 - x: ix,
398 - y: iy,
399 - constraint: {
400 - weight: 0.7,
401 - y: iy
402 - }
403 - };
404 - network.lookup[n.id] = node;
405 - network.nodes.push(node);
406 - });
407 -
408 -
409 - // now, process the explicit links...
410 - network.data.links.forEach(function(lnk) {
411 - var src = network.lookup[lnk.src],
412 - dst = network.lookup[lnk.dst],
413 - id = src.id + "-" + dst.id;
414 -
415 - var link = {
416 - class: 'infra',
417 - id: id,
418 - type: lnk.type,
419 - width: lnk.linkWidth,
420 - source: src,
421 - srcPort: lnk.srcPort,
422 - target: dst,
423 - tgtPort: lnk.dstPort,
424 - strength: config.force.linkStrength.infra
425 - };
426 - network.links.push(link);
427 - });
428 -
429 - // finally, infer host links...
430 - network.data.hosts.forEach(function(n) {
431 - var src = network.lookup[n.id],
432 - dst = network.lookup[n.cp.device],
433 - id = src.id + "-" + dst.id;
434 -
435 - var link = {
436 - class: 'host',
437 - id: id,
438 - type: 'hostLink',
439 - width: config.hostLinkWidth,
440 - source: src,
441 - target: dst,
442 - strength: config.force.linkStrength.host
443 - };
444 - network.links.push(link);
445 - });
446 - }
447 -
448 - function createLayout() {
449 -
450 - var cfg = config.force;
451 -
452 - network.force = d3.layout.force()
453 - .size([network.forceWidth, network.forceHeight])
454 - .nodes(network.nodes)
455 - .links(network.links)
456 - .linkStrength(function(d) { return cfg.linkStrength[d.class]; })
457 - .linkDistance(function(d) { return cfg.linkDistance[d.class]; })
458 - .charge(function(d) { return cfg.charge[d.class]; })
459 - .on('tick', tick);
460 -
461 - network.svg = d3.select('#view').append('svg')
462 - .attr('width', view.width)
463 - .attr('height', view.height)
464 - .append('g')
465 - .attr('transform', config.force.translate());
466 -// .attr('id', 'zoomable')
467 -// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
468 -
469 - network.svg.append('svg:image')
470 - .attr({
471 - id: 'bg',
472 - width: view.width,
473 - height: view.height,
474 - 'xlink:href': config.backgroundUrl
475 - })
476 - .style('visibility',
477 - config.options.loadBackground ? 'visible' : 'hidden');
478 -
479 -// function zoomRedraw() {
480 -// d3.select("#zoomable").attr("transform",
481 -// "translate(" + d3.event.translate + ")"
482 -// + " scale(" + d3.event.scale + ")");
483 -// }
484 -
485 - // TODO: move glow/blur stuff to util script
486 - var glow = network.svg.append('filter')
487 - .attr('x', '-50%')
488 - .attr('y', '-50%')
489 - .attr('width', '200%')
490 - .attr('height', '200%')
491 - .attr('id', 'blue-glow');
492 -
493 - glow.append('feColorMatrix')
494 - .attr('type', 'matrix')
495 - .attr('values', '0 0 0 0 0 ' +
496 - '0 0 0 0 0 ' +
497 - '0 0 0 0 .7 ' +
498 - '0 0 0 1 0 ');
499 -
500 - glow.append('feGaussianBlur')
501 - .attr('stdDeviation', 3)
502 - .attr('result', 'coloredBlur');
503 -
504 - glow.append('feMerge').selectAll('feMergeNode')
505 - .data(['coloredBlur', 'SourceGraphic'])
506 - .enter().append('feMergeNode')
507 - .attr('in', String);
508 -
509 - // TODO: legend (and auto adjust on scroll)
510 -// $('#view').on('scroll', function() {
511 -//
512 -// });
513 -
514 -
515 - // TODO: move drag behavior into separate method.
516 - // == define node drag behavior...
517 - network.draggedThreshold = d3.scale.linear()
518 - .domain([0, 0.1])
519 - .range([5, 20])
520 - .clamp(true);
521 -
522 - function dragged(d) {
523 - var threshold = network.draggedThreshold(network.force.alpha()),
524 - dx = d.oldX - d.px,
525 - dy = d.oldY - d.py;
526 - if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) {
527 - d.dragged = true;
528 - }
529 - return d.dragged;
530 - }
531 -
532 - network.drag = d3.behavior.drag()
533 - .origin(function(d) { return d; })
534 - .on('dragstart', function(d) {
535 - d.oldX = d.x;
536 - d.oldY = d.y;
537 - d.dragged = false;
538 - d.fixed |= 2;
539 - })
540 - .on('drag', function(d) {
541 - d.px = d3.event.x;
542 - d.py = d3.event.y;
543 - if (dragged(d)) {
544 - if (!network.force.alpha()) {
545 - network.force.alpha(.025);
546 - }
547 - }
548 - })
549 - .on('dragend', function(d) {
550 - if (!dragged(d)) {
551 - selectObject(d, this);
552 - }
553 - d.fixed &= ~6;
554 -
555 - // once we've finished moving, pin the node in position,
556 - // if it is a device (not a host)
557 - if (d.class === 'device') {
558 - d.fixed = true;
559 - d3.select(this).classed('fixed', true)
560 - }
561 - });
562 -
563 - $('#view').on('click', function(e) {
564 - if (!$(e.target).closest('.node').length) {
565 - deselectObject();
566 - }
567 - });
568 -
569 - // ...............................................................
570 -
571 - // add links to the display
572 - network.link = network.svg.append('g').attr('id', 'links')
573 - .selectAll('.link')
574 - .data(network.force.links(), function(d) {return d.id})
575 - .enter().append('line')
576 - .attr('class', function(d) {return 'link ' + d.class});
577 -
578 - network.linkSrcPort = network.svg.append('g')
579 - .attr({
580 - id: 'srcPorts',
581 - class: 'portLayer'
582 - });
583 - network.linkTgtPort = network.svg.append('g')
584 - .attr({
585 - id: 'tgtPorts',
586 - class: 'portLayer'
587 - });
588 -
589 - var portVis = portLabelsOn ? 'visible' : 'hidden',
590 - pw = config.labels.port.width,
591 - ph = config.labels.port.height;
592 -
593 - network.link.filter('.infra').each(function(d) {
594 - var srcType = d.source.type === 'roadm' ? 'optPort' : 'pktPort',
595 - tgtType = d.target.type === 'roadm' ? 'optPort' : 'pktPort';
596 -
597 - if (d.source.type)
598 -
599 - network.linkSrcPort.append('rect').attr({
600 - id: 'srcPort-' + safeId(d.id),
601 - class: 'port ' + srcType,
602 - width: pw,
603 - height: ph,
604 - rx: 4,
605 - ry: 4
606 - }).style('visibility', portVis);
607 -
608 - network.linkTgtPort.append('rect').attr({
609 - id: 'tgtPort-' + safeId(d.id),
610 - class: 'port ' + tgtType,
611 - width: pw,
612 - height: ph,
613 - rx: 4,
614 - ry: 4
615 - }).style('visibility', portVis);
616 -
617 - network.linkSrcPort.append('text').attr({
618 - id: 'srcText-' + safeId(d.id),
619 - class: 'portText ' + srcType
620 - }).text(d.srcPort)
621 - .style('visibility', portVis);
622 -
623 - network.linkTgtPort.append('text').attr({
624 - id: 'tgtText-' + safeId(d.id),
625 - class: 'portText ' + tgtType
626 - }).text(d.tgtPort)
627 - .style('visibility', portVis);
628 - });
629 -
630 - // ...............................................................
631 -
632 - // add nodes to the display
633 - network.node = network.svg.selectAll('.node')
634 - .data(network.force.nodes(), function(d) {return d.id})
635 - .enter().append('g')
636 - .attr('class', function(d) {
637 - var cls = 'node ' + d.class;
638 - if (d.type) {
639 - cls += ' ' + d.type;
640 - }
641 - return cls;
642 - })
643 - .attr('transform', function(d) {
644 - return translate(d.x, d.y);
645 - })
646 - .call(network.drag)
647 - .on('mouseover', function(d) {
648 - // TODO: show tooltip
649 - if (network.mouseoutTimeout) {
650 - clearTimeout(network.mouseoutTimeout);
651 - network.mouseoutTimeout = null;
652 - }
653 - hoverObject(d);
654 - })
655 - .on('mouseout', function(d) {
656 - // TODO: hide tooltip
657 - if (network.mouseoutTimeout) {
658 - clearTimeout(network.mouseoutTimeout);
659 - network.mouseoutTimeout = null;
660 - }
661 - network.mouseoutTimeout = setTimeout(function() {
662 - hoverObject(null);
663 - }, config.mouseOutTimerDelayMs);
664 - });
665 -
666 -
667 - // deal with device nodes first
668 - network.nodeRect = network.node.filter('.device')
669 - .append('rect')
670 - .attr({
671 - rx: 5,
672 - ry: 5,
673 - width: 100,
674 - height: 12
675 - });
676 - // note that width/height are adjusted to fit the label text
677 - // then padded, and space made for the icon.
678 -
679 - network.node.filter('.device').each(function(d) {
680 - var node = d3.select(this),
681 - icon = iconUrl(d);
682 -
683 - node.append('text')
684 - // TODO: add label cycle behavior
685 - .text(d.id)
686 - .attr('dy', '1.1em');
687 -
688 - if (icon) {
689 - var cfg = config.icons;
690 - node.append('svg:image')
691 - .attr({
692 - width: cfg.w,
693 - height: cfg.h,
694 - 'xlink:href': icon
695 - });
696 - // note, icon relative positioning (x,y) is done after we have
697 - // adjusted the bounds of the rectangle...
698 - }
699 -
700 - // debug function to show the modelled x,y coordinates of nodes...
701 - if (debug('showNodeXY')) {
702 - node.select('rect').attr('fill-opacity', 0.5);
703 - node.append('circle')
704 - .attr({
705 - class: 'debug',
706 - cx: 0,
707 - cy: 0,
708 - r: '3px'
709 - });
710 - }
711 - });
712 -
713 - // now process host nodes
714 - network.nodeCircle = network.node.filter('.host')
715 - .append('circle')
716 - .attr({
717 - r: config.hostRadius
718 - });
719 -
720 - network.node.filter('.host').each(function(d) {
721 - var node = d3.select(this),
722 - icon = iconUrl(d);
723 -
724 - // debug function to show the modelled x,y coordinates of nodes...
725 - if (debug('showNodeXY')) {
726 - node.select('circle').attr('fill-opacity', 0.5);
727 - node.append('circle')
728 - .attr({
729 - class: 'debug',
730 - cx: 0,
731 - cy: 0,
732 - r: '3px'
733 - });
734 - }
735 - });
736 -
737 - // this function is scheduled to happen soon after the given thread ends
738 - setTimeout(function() {
739 - var lab = config.labels,
740 - portGap = lab.port.gap,
741 - midW = portGap + lab.port.width/ 2,
742 - midH = portGap + lab.port.height / 2;
743 -
744 - // post process the device nodes, to pad their size to fit the
745 - // label text and attach the icon to the right location.
746 - network.node.filter('.device').each(function(d) {
747 - // for every node, recompute size, padding, etc. so text fits
748 - var node = d3.select(this),
749 - text = node.select('text'),
750 - box = adjustRectToFitText(node);
751 -
752 - // now make the computed adjustment
753 - node.select('rect')
754 - .attr(box);
755 -
756 - node.select('image')
757 - .attr('x', box.x + config.icons.xoff)
758 - .attr('y', box.y + config.icons.yoff);
759 -
760 - var bounds = boundsFromBox(box),
761 - portBounds = {
762 - x1: bounds.x1 - midW,
763 - x2: bounds.x2 + midW,
764 - y1: bounds.y1 - midH,
765 - y2: bounds.y2 + midH
766 - };
767 -
768 - // todo: clean up extent and edge work..
769 - d.extent = {
770 - left: bounds.x1 - lab.marginLR,
771 - right: bounds.x2 + lab.marginLR,
772 - top: bounds.y1 - lab.marginTB,
773 - bottom: bounds.y2 + lab.marginTB
774 - };
775 -
776 - d.edge = {
777 - left : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x1, bounds.y2),
778 - right : new geo.LineSegment(bounds.x2, bounds.y1, bounds.x2, bounds.y2),
779 - top : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x2, bounds.y1),
780 - bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
781 - };
782 -
783 - d.portEdge = {
784 - left : new geo.LineSegment(
785 - portBounds.x1, portBounds.y1, portBounds.x1, portBounds.y2
786 - ),
787 - right : new geo.LineSegment(
788 - portBounds.x2, portBounds.y1, portBounds.x2, portBounds.y2
789 - ),
790 - top : new geo.LineSegment(
791 - portBounds.x1, portBounds.y1, portBounds.x2, portBounds.y1
792 - ),
793 - bottom : new geo.LineSegment(
794 - portBounds.x1, portBounds.y2, portBounds.x2, portBounds.y2
795 - )
796 - };
797 -
798 - });
799 -
800 - network.numTicks = 0;
801 - network.preventCollisions = false;
802 - network.force.start();
803 - for (var i = 0; i < config.force.ticksWithoutCollisions; i++) {
804 - network.force.tick();
805 - }
806 - network.preventCollisions = true;
807 - $('#view').css('visibility', 'visible');
808 - });
809 -
810 -
811 - // returns the newly computed bounding box of the rectangle
812 - function adjustRectToFitText(n) {
813 - var text = n.select('text'),
814 - box = text.node().getBBox(),
815 - lab = config.labels;
816 -
817 - // not sure why n.data() returns an array of 1 element...
818 - var data = n.data()[0];
819 -
820 - text.attr('text-anchor', 'middle')
821 - .attr('y', '-0.8em')
822 - .attr('x', lab.imgPad/2)
823 - ;
824 -
825 - // translate the bbox so that it is centered on [x,y]
826 - box.x = -box.width / 2;
827 - box.y = -box.height / 2;
828 -
829 - // add padding
830 - box.x -= (lab.padLR + lab.imgPad/2);
831 - box.width += lab.padLR * 2 + lab.imgPad;
832 - box.y -= lab.padTB;
833 - box.height += lab.padTB * 2;
834 -
835 - return box;
836 - }
837 -
838 - function boundsFromBox(box) {
839 - return {
840 - x1: box.x,
841 - y1: box.y,
842 - x2: box.x + box.width,
843 - y2: box.y + box.height
844 - };
845 - }
846 -
847 - }
848 -
849 - function iconUrl(d) {
850 - return 'img/' + d.type + '.png';
851 -// return config.iconUrl[d.icon];
852 - }
853 -
854 - function translate(x, y) {
855 - return 'translate(' + x + ',' + y + ')';
856 - }
857 -
858 - // prevents collisions amongst device nodes
859 - function preventCollisions() {
860 - var quadtree = d3.geom.quadtree(network.nodes),
861 - hrad = config.hostRadius;
862 -
863 - network.nodes.forEach(function(n) {
864 - var nx1, nx2, ny1, ny2;
865 -
866 - if (n.class === 'device') {
867 - nx1 = n.x + n.extent.left;
868 - nx2 = n.x + n.extent.right;
869 - ny1 = n.y + n.extent.top;
870 - ny2 = n.y + n.extent.bottom;
871 -
872 - } else {
873 - nx1 = n.x - hrad;
874 - nx2 = n.x + hrad;
875 - ny1 = n.y - hrad;
876 - ny2 = n.y + hrad;
877 - }
878 -
879 - quadtree.visit(function(quad, x1, y1, x2, y2) {
880 - if (quad.point && quad.point !== n) {
881 - // check if the rectangles/circles intersect
882 - var p = quad.point,
883 - px1, px2, py1, py2, ix;
884 -
885 - if (p.class === 'device') {
886 - px1 = p.x + p.extent.left;
887 - px2 = p.x + p.extent.right;
888 - py1 = p.y + p.extent.top;
889 - py2 = p.y + p.extent.bottom;
890 -
891 - } else {
892 - px1 = p.x - hrad;
893 - px2 = p.x + hrad;
894 - py1 = p.y - hrad;
895 - py2 = p.y + hrad;
896 - }
897 -
898 - ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2);
899 -
900 - if (ix) {
901 - var xa1 = nx2 - px1, // shift n left , p right
902 - xa2 = px2 - nx1, // shift n right, p left
903 - ya1 = ny2 - py1, // shift n up , p down
904 - ya2 = py2 - ny1, // shift n down , p up
905 - adj = Math.min(xa1, xa2, ya1, ya2);
906 -
907 - if (adj == xa1) {
908 - n.x -= adj / 2;
909 - p.x += adj / 2;
910 - } else if (adj == xa2) {
911 - n.x += adj / 2;
912 - p.x -= adj / 2;
913 - } else if (adj == ya1) {
914 - n.y -= adj / 2;
915 - p.y += adj / 2;
916 - } else if (adj == ya2) {
917 - n.y += adj / 2;
918 - p.y -= adj / 2;
919 - }
920 - }
921 - return ix;
922 - }
923 - });
924 -
925 - });
926 - }
927 -
928 - function tick(e) {
929 - network.numTicks++;
930 -
931 - if (config.options.layering) {
932 - // adjust the y-coord of each node, based on y-pos constraints
933 - network.nodes.forEach(function (n) {
934 - var z = e.alpha * n.constraint.weight;
935 - if (!isNaN(n.constraint.y)) {
936 - n.y = (n.constraint.y * z + n.y * (1 - z));
937 - }
938 - });
939 - }
940 -
941 - if (config.options.collisionPrevention && network.preventCollisions) {
942 - preventCollisions();
943 - }
944 -
945 - var portHalfW = config.labels.port.width / 2,
946 - portHalfH = config.labels.port.height / 2;
947 -
948 - // clip visualization of links at bounds of nodes...
949 - network.link.each(function(d) {
950 - var xs = d.source.x,
951 - ys = d.source.y,
952 - xt = d.target.x,
953 - yt = d.target.y,
954 - line = new geo.LineSegment(xs, ys, xt, yt),
955 - e, ix,
956 - exs, eys, ext, eyt,
957 - pxs, pys, pxt, pyt;
958 -
959 - if (d.class === 'host') {
960 - // no adjustment for source end of link, since hosts are dots
961 - exs = xs;
962 - eys = ys;
963 -
964 - } else {
965 - for (e in d.source.edge) {
966 - ix = line.intersect(d.source.edge[e].offset(xs, ys));
967 - if (ix.in1 && ix.in2) {
968 - exs = ix.x;
969 - eys = ix.y;
970 -
971 - // also pick off the port label intersection
972 - ix = line.intersect(d.source.portEdge[e].offset(xs, ys));
973 - pxs = ix.x;
974 - pys = ix.y;
975 - break;
976 - }
977 - }
978 - }
979 -
980 - for (e in d.target.edge) {
981 - ix = line.intersect(d.target.edge[e].offset(xt, yt));
982 - if (ix.in1 && ix.in2) {
983 - ext = ix.x;
984 - eyt = ix.y;
985 -
986 - // also pick off the port label intersection
987 - ix = line.intersect(d.target.portEdge[e].offset(xt, yt));
988 - pxt = ix.x;
989 - pyt = ix.y;
990 - break;
991 - }
992 - }
993 -
994 - // adjust the endpoints of the link's line to match rectangles
995 - var sid = safeId(d.id);
996 - d3.select(this)
997 - .attr('x1', exs)
998 - .attr('y1', eys)
999 - .attr('x2', ext)
1000 - .attr('y2', eyt);
1001 -
1002 - d3.select('#srcPort-' + sid)
1003 - .attr('x', pxs - portHalfW)
1004 - .attr('y', pys - portHalfH);
1005 -
1006 - d3.select('#tgtPort-' + sid)
1007 - .attr('x', pxt - portHalfW)
1008 - .attr('y', pyt - portHalfH);
1009 -
1010 - // TODO: fit label rect to size of port number.
1011 - d3.select('#srcText-' + sid)
1012 - .attr('x', pxs - 5)
1013 - .attr('y', pys + 3);
1014 -
1015 - d3.select('#tgtText-' + sid)
1016 - .attr('x', pxt - 5)
1017 - .attr('y', pyt + 3);
1018 -
1019 - });
1020 -
1021 - // position each node by translating the node (group) by x,y
1022 - network.node
1023 - .attr('transform', function(d) {
1024 - return translate(d.x, d.y);
1025 - });
1026 -
1027 - }
1028 -
1029 - // $('#docs-close').on('click', function() {
1030 - // deselectObject();
1031 - // return false;
1032 - // });
1033 -
1034 - // $(document).on('click', '.select-object', function() {
1035 - // var obj = graph.data[$(this).data('name')];
1036 - // if (obj) {
1037 - // selectObject(obj);
1038 - // }
1039 - // return false;
1040 - // });
1041 -
1042 - function findNodeFromData(d) {
1043 - var el = null;
1044 - network.node.filter('.' + d.class).each(function(n) {
1045 - if (n.id === d.id) {
1046 - el = d3.select(this);
1047 - }
1048 - });
1049 - return el;
1050 - }
1051 -
1052 - function selectObject(obj, el) {
1053 - var node;
1054 - if (el) {
1055 - node = d3.select(el);
1056 - } else {
1057 - network.node.each(function(d) {
1058 - if (d == obj) {
1059 - node = d3.select(el = this);
1060 - }
1061 - });
1062 - }
1063 - if (!node) return;
1064 -
1065 - if (node.classed('selected')) {
1066 - deselectObject();
1067 - flyinPane(null);
1068 - return;
1069 - }
1070 - deselectObject(false);
1071 -
1072 - selected = {
1073 - obj : obj,
1074 - el : el
1075 - };
1076 -
1077 - node.classed('selected', true);
1078 - flyinPane(obj);
1079 - }
1080 -
1081 - function deselectObject(doResize) {
1082 - // Review: logic of 'resize(...)' function.
1083 - if (doResize || typeof doResize == 'undefined') {
1084 - resize(false);
1085 - }
1086 -
1087 - // deselect all nodes in the network...
1088 - network.node.classed('selected', false);
1089 - selected = {};
1090 - flyinPane(null);
1091 - }
1092 -
1093 - function flyinPane(obj) {
1094 - var pane = d3.select('#flyout'),
1095 - url;
1096 -
1097 - if (obj) {
1098 - // go get details of the selected object from the server...
1099 - url = detailJsonUrl(obj.id);
1100 - d3.json(url, function (err, data) {
1101 - if (err) {
1102 - alert('Oops! Error reading JSON...\n\n' +
1103 - 'URL: ' + url + '\n\n' +
1104 - 'Error: ' + err.message);
1105 - return;
1106 - }
1107 -// console.log("JSON data... " + url);
1108 -// console.log(data);
1109 -
1110 - displayDetails(data, pane);
1111 - });
1112 -
1113 - } else {
1114 - // hide pane
1115 - pane.transition().duration(750)
1116 - .style('right', '-320px')
1117 - .style('opacity', 0.0);
1118 - }
1119 - }
1120 -
1121 - function displayDetails(data, pane) {
1122 - $('#flyout').empty();
1123 -
1124 - var title = pane.append("h2"),
1125 - table = pane.append("table"),
1126 - tbody = table.append("tbody");
1127 -
1128 - $('<img src="img/' + data.type + '.png">').appendTo(title);
1129 - $('<span>').attr('class', 'icon').text(data.id).appendTo(title);
1130 -
1131 -
1132 - // TODO: consider using d3 data bind to TR/TD
1133 -
1134 - data.propOrder.forEach(function(p) {
1135 - if (p === '-') {
1136 - addSep(tbody);
1137 - } else {
1138 - addProp(tbody, p, data.props[p]);
1139 - }
1140 - });
1141 -
1142 - function addSep(tbody) {
1143 - var tr = tbody.append('tr');
1144 - $('<hr>').appendTo(tr.append('td').attr('colspan', 2));
1145 - }
1146 -
1147 - function addProp(tbody, label, value) {
1148 - var tr = tbody.append('tr');
1149 -
1150 - tr.append('td')
1151 - .attr('class', 'label')
1152 - .text(label + ' :');
1153 -
1154 - tr.append('td')
1155 - .attr('class', 'value')
1156 - .text(value);
1157 - }
1158 -
1159 - // show pane
1160 - pane.transition().duration(750)
1161 - .style('right', '20px')
1162 - .style('opacity', 1.0);
1163 - }
1164 -
1165 - function highlightObject(obj) {
1166 - if (obj) {
1167 - if (obj != highlighted) {
1168 - // TODO set or clear "inactive" class on nodes, based on criteria
1169 - network.node.classed('inactive', function(d) {
1170 - // return (obj !== d &&
1171 - // d.relation(obj.id));
1172 - return (obj !== d);
1173 - });
1174 - // TODO: same with links
1175 - network.link.classed('inactive', function(d) {
1176 - return (obj !== d.source && obj !== d.target);
1177 - });
1178 - }
1179 - highlighted = obj;
1180 - } else {
1181 - if (highlighted) {
1182 - // clear the inactive flag (no longer suppressed visually)
1183 - network.node.classed('inactive', false);
1184 - network.link.classed('inactive', false);
1185 - }
1186 - highlighted = null;
1187 -
1188 - }
1189 - }
1190 -
1191 - function hoverObject(obj) {
1192 - if (obj) {
1193 - hovered = obj;
1194 - } else {
1195 - if (hovered) {
1196 - hovered = null;
1197 - }
1198 - }
1199 - }
1200 -
1201 -
1202 - function resize() {
1203 - view.height = window.innerHeight - config.mastHeight;
1204 - view.width = window.innerWidth;
1205 - $('#view')
1206 - .css('height', view.height + 'px')
1207 - .css('width', view.width + 'px');
1208 -
1209 - network.forceWidth = view.width - config.force.marginLR;
1210 - network.forceHeight = view.height - config.force.marginTB;
1211 - }
1212 -
1213 - // ======================================================================
1214 - // register with the UI framework
1215 -
1216 - api.addView('network', {
1217 - load: loadNetworkView
1218 - });
1219 -
1220 -
1221 -}(ONOS));
1222 -
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -/*
18 - ONOS CSS file
19 -
20 - @author Simon Hunt
21 - */
22 -
23 -body, html {
24 - height: 100%;
25 -}
26 -
27 -/*
28 - * Classes
29 - */
30 -
31 -img#logo {
32 - height: 38px;
33 - padding-left: 8px;
34 - padding-right: 8px;
35 -}
36 -
37 -span.title {
38 - color: #369;
39 - font-size: 14pt;
40 - font-style: italic;
41 - vertical-align: 12px;
42 -}
43 -
44 -span.radio {
45 - color: darkslateblue;
46 - font-size: 10pt;
47 -}
48 -
49 -span.right {
50 - padding-top: 8px;
51 - padding-right: 16px;
52 - float: right;
53 -}
54 -
55 -/*
56 - * Radio Buttons
57 - */
58 -
59 -span.radio {
60 - margin: 4px 0;
61 - border: 1px dotted #222;
62 - padding: 1px 6px;
63 - color: #eee;
64 - cursor: pointer;
65 -}
66 -
67 -span.radio.active {
68 - background-color: #bbb;
69 - border: 1px solid #eee;
70 - padding: 1px 6px;
71 - color: #666;
72 - font-weight: bold;
73 -}
74 -
75 -/*
76 - * === DEBUGGING ======
77 - */
78 -svg {
79 - /*border: 1px dashed red;*/
80 -}
81 -
82 -svg #bg {
83 - opacity: 0.5;
84 -}
85 -
86 -
87 -/*
88 - * Network Graph elements ======================================
89 - */
90 -
91 -svg .link {
92 - fill: none;
93 - stroke: #666;
94 - stroke-width: 2.0px;
95 - opacity: .7;
96 -
97 - transition: opacity 250ms;
98 - -webkit-transition: opacity 250ms;
99 - -moz-transition: opacity 250ms;
100 -}
101 -
102 -svg .link.host {
103 - stroke: #666;
104 - stroke-width: 1px;
105 -}
106 -
107 -svg g.portLayer rect.port {
108 - fill: #ccc;
109 -}
110 -
111 -svg g.portLayer text {
112 - font: 8pt sans-serif;
113 - pointer-events: none;
114 -}
115 -
116 -svg .node.device rect {
117 - stroke-width: 1.5px;
118 -
119 - transition: opacity 250ms;
120 - -webkit-transition: opacity 250ms;
121 - -moz-transition: opacity 250ms;
122 -}
123 -
124 -svg .node.device.fixed rect {
125 - stroke-width: 1.5;
126 - stroke: #ccc;
127 -}
128 -
129 -svg .node.device.roadm rect {
130 - fill: #03c;
131 -}
132 -
133 -svg .node.device.switch rect {
134 - fill: #06f;
135 -}
136 -
137 -svg .node.host circle {
138 - fill: #c96;
139 - stroke: #000;
140 -}
141 -
142 -svg .node text {
143 - fill: white;
144 - font: 10pt sans-serif;
145 - pointer-events: none;
146 -}
147 -
148 -/* for debugging */
149 -svg .node circle.debug {
150 - fill: white;
151 - stroke: red;
152 -}
153 -svg .node rect.debug {
154 - fill: yellow;
155 - stroke: red;
156 - opacity: 0.35;
157 -}
158 -
159 -
160 -svg .node.selected rect,
161 -svg .node.selected circle {
162 - filter: url(#blue-glow);
163 -}
164 -
165 -svg .link.inactive,
166 -svg .port.inactive,
167 -svg .portText.inactive,
168 -svg .node.inactive rect,
169 -svg .node.inactive circle,
170 -svg .node.inactive text,
171 -svg .node.inactive image {
172 - opacity: .1;
173 -}
174 -
175 -svg .node.inactive.selected rect,
176 -svg .node.inactive.selected text,
177 -svg .node.inactive.selected image {
178 - opacity: .6;
179 -}
180 -
181 -/*
182 - * === currently unused ===============================================
183 - */
184 -
185 -svg marker#end {
186 - fill: #666;
187 - stroke: #666;
188 - stroke-width: 1.5px;
189 -}
190 -
191 -svg .legend {
192 - position: fixed;
193 -}
194 -
195 -svg .legend .category rect {
196 - stroke-width: 1px;
197 -}
198 -
199 -svg .legend .category text {
200 - fill: #000;
201 - font: 10px sans-serif;
202 - pointer-events: none;
203 -}
204 -
205 -/*
206 - * =============================================================
207 - */
208 -
209 -/*
210 - * Specific structural elements
211 - */
212 -
213 -/* This is to ensure that the body does not expand to account for the
214 - flyout details pane, that is positioned "off screen".
215 - */
216 -body {
217 - overflow: hidden;
218 -}
219 -
220 -#mast {
221 - height: 36px;
222 - padding: 4px;
223 - background-color: #bbb;
224 - vertical-align: baseline;
225 - box-shadow: 0px 2px 8px #777;
226 -}
227 -
228 -#frame {
229 - width: 100%;
230 - height: 100%;
231 - background-color: #fff;
232 -}
233 -
234 -#flyout {
235 - position: absolute;
236 - z-index: 100;
237 - display: block;
238 - top: 10%;
239 - width: 280px;
240 - right: -300px;
241 - opacity: 0;
242 - background-color: rgba(255,255,255,0.8);
243 -
244 - padding: 10px;
245 - color: black;
246 - font-size: 10pt;
247 - box-shadow: 2px 2px 16px #777;
248 -}
249 -
250 -#flyout h2 {
251 - margin: 8px 4px;
252 - color: black;
253 - vertical-align: middle;
254 -}
255 -
256 -#flyout h2 img {
257 - height: 32px;
258 - padding-right: 8px;
259 - vertical-align: middle;
260 -}
261 -
262 -#flyout p, table {
263 - margin: 4px 4px;
264 -}
265 -
266 -#flyout td.label {
267 - font-style: italic;
268 - color: #777;
269 - padding-right: 12px;
270 -}
271 -
272 -#flyout td.value {
273 -
274 -}
275 -
276 -#flyout hr {
277 - height: 1px;
278 - color: #ccc;
279 - background-color: #ccc;
280 - border: 0;
281 -}
282 -
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -/*
18 - ONOS UI Framework.
19 -
20 - @author Simon Hunt
21 - */
22 -
23 -(function ($) {
24 - 'use strict';
25 - var tsI = new Date().getTime(), // initialize time stamp
26 - tsB; // build time stamp
27 -
28 - // attach our main function to the jQuery object
29 - $.onos = function (options) {
30 - // private namespaces
31 - var publicApi; // public api
32 -
33 - // internal state
34 - var views = {},
35 - currentView = null,
36 - built = false;
37 -
38 - // DOM elements etc.
39 - var $mast;
40 -
41 -
42 - // various functions..................
43 -
44 - // throw an error
45 - function throwError(msg) {
46 - // todo: maybe add tracing later
47 - throw new Error(msg);
48 - }
49 -
50 - // define all the public api functions...
51 - publicApi = {
52 - printTime: function () {
53 - console.log("the time is " + new Date());
54 - },
55 -
56 - addView: function (vid, cb) {
57 - views[vid] = {
58 - vid: vid,
59 - cb: cb
60 - };
61 - // TODO: proper registration of views
62 - // for now, make the one (and only) view current..
63 - currentView = views[vid];
64 - }
65 - };
66 -
67 - // function to be called from index.html to build the ONOS UI
68 - function buildOnosUi() {
69 - tsB = new Date().getTime();
70 - tsI = tsB - tsI; // initialization duration
71 -
72 - console.log('ONOS UI initialized in ' + tsI + 'ms');
73 -
74 - if (built) {
75 - throwError("ONOS UI already built!");
76 - }
77 - built = true;
78 -
79 - // TODO: invoke hash navigation
80 - // --- report build errors ---
81 -
82 - // for now, invoke the one and only load function:
83 -
84 - currentView.cb.load();
85 - }
86 -
87 -
88 - // export the api and build-UI function
89 - return {
90 - api: publicApi,
91 - buildUi: buildOnosUi
92 - };
93 - };
94 -
95 -}(jQuery));
...\ No newline at end of file ...\ No newline at end of file
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -/*
18 - ONOS network topology viewer - PoC version 1.0
19 -
20 - @author Simon Hunt
21 - */
22 -
23 -(function (onos) {
24 - 'use strict';
25 -
26 - // configuration data
27 - var config = {
28 - useLiveData: true,
29 - debugOn: false,
30 - debug: {
31 - showNodeXY: false,
32 - showKeyHandler: true
33 - },
34 - options: {
35 - layering: true,
36 - collisionPrevention: true,
37 - loadBackground: true
38 - },
39 - backgroundUrl: 'img/us-map.png',
40 - data: {
41 - live: {
42 - jsonUrl: 'rs/topology/graph',
43 - detailPrefix: 'rs/topology/graph/',
44 - detailSuffix: ''
45 - },
46 - fake: {
47 - jsonUrl: 'json/network2.json',
48 - detailPrefix: 'json/',
49 - detailSuffix: '.json'
50 - }
51 - },
52 - iconUrl: {
53 - device: 'img/device.png',
54 - host: 'img/host.png',
55 - pkt: 'img/pkt.png',
56 - opt: 'img/opt.png'
57 - },
58 - mastHeight: 36,
59 - force: {
60 - note: 'node.class or link.class is used to differentiate',
61 - linkDistance: {
62 - infra: 200,
63 - host: 40
64 - },
65 - linkStrength: {
66 - infra: 1.0,
67 - host: 1.0
68 - },
69 - charge: {
70 - device: -800,
71 - host: -1000
72 - },
73 - ticksWithoutCollisions: 50,
74 - marginLR: 20,
75 - marginTB: 20,
76 - translate: function() {
77 - return 'translate(' +
78 - config.force.marginLR + ',' +
79 - config.force.marginTB + ')';
80 - }
81 - },
82 - labels: {
83 - imgPad: 16,
84 - padLR: 8,
85 - padTB: 6,
86 - marginLR: 3,
87 - marginTB: 2,
88 - port: {
89 - gap: 3,
90 - width: 18,
91 - height: 14
92 - }
93 - },
94 - icons: {
95 - w: 32,
96 - h: 32,
97 - xoff: -12,
98 - yoff: -8
99 - },
100 - constraints: {
101 - ypos: {
102 - host: 0.05,
103 - switch: 0.3,
104 - roadm: 0.7
105 - }
106 - },
107 - hostLinkWidth: 1.0,
108 - hostRadius: 7,
109 - mouseOutTimerDelayMs: 120
110 - };
111 -
112 - // state variables
113 - var netView = {},
114 - network = {},
115 - selected = {},
116 - highlighted = null,
117 - hovered = null,
118 - viewMode = 'showAll',
119 - portLabelsOn = false;
120 -
121 -
122 - function debug(what) {
123 - return config.debugOn && config.debug[what];
124 - }
125 -
126 - function urlData() {
127 - return config.data[config.useLiveData ? 'live' : 'fake'];
128 - }
129 -
130 - function networkJsonUrl() {
131 - return urlData().jsonUrl;
132 - }
133 -
134 - function safeId(id) {
135 - return id.replace(/[^a-z0-9]/gi, '_');
136 - }
137 -
138 - function detailJsonUrl(id) {
139 - var u = urlData(),
140 - encId = config.useLiveData ? encodeURIComponent(id) : safeId(id);
141 - return u.detailPrefix + encId + u.detailSuffix;
142 - }
143 -
144 -
145 - // load the topology view of the network
146 - function loadNetworkView() {
147 - // Hey, here I am, calling something on the ONOS api:
148 - api.printTime();
149 -
150 - resize();
151 -
152 - // go get our network data from the server...
153 - var url = networkJsonUrl();
154 - d3.json(url , function (err, data) {
155 - if (err) {
156 - alert('Oops! Error reading JSON...\n\n' +
157 - 'URL: ' + url + '\n\n' +
158 - 'Error: ' + err.message);
159 - return;
160 - }
161 -// console.log("here is the JSON data...");
162 -// console.log(data);
163 -
164 - network.data = data;
165 - drawNetwork();
166 - });
167 -
168 - // while we wait for the data, set up the handlers...
169 - setUpClickHandler();
170 - setUpRadioButtonHandler();
171 - setUpKeyHandler();
172 - $(window).on('resize', resize);
173 - }
174 -
175 - function setUpClickHandler() {
176 - // click handler for "selectable" objects
177 - $(document).on('click', '.select-object', function () {
178 - // when any object of class "select-object" is clicked...
179 - var obj = network.lookup[$(this).data('id')];
180 - if (obj) {
181 - selectObject(obj);
182 - }
183 - // stop propagation of event (I think) ...
184 - return false;
185 - });
186 - }
187 -
188 - function setUpRadioButtonHandler() {
189 - d3.selectAll('#displayModes .radio').on('click', function () {
190 - var id = d3.select(this).attr('id');
191 - if (id !== viewMode) {
192 - radioButton('displayModes', id);
193 - viewMode = id;
194 - doRadioAction(id);
195 - }
196 - });
197 - }
198 -
199 - function doRadioAction(id) {
200 - showAllLayers();
201 - if (id === 'showPkt') {
202 - showPacketLayer();
203 - } else if (id === 'showOpt') {
204 - showOpticalLayer();
205 - }
206 - }
207 -
208 - function showAllLayers() {
209 - network.node.classed('inactive', false);
210 - network.link.classed('inactive', false);
211 - d3.selectAll('svg .port').classed('inactive', false);
212 - d3.selectAll('svg .portText').classed('inactive', false);
213 - }
214 -
215 - function showPacketLayer() {
216 - network.node.each(function(d) {
217 - // deactivate nodes that are not hosts or switches
218 - if (d.class === 'device' && d.type !== 'switch') {
219 - d3.select(this).classed('inactive', true);
220 - }
221 - });
222 -
223 - network.link.each(function(lnk) {
224 - // deactivate infrastructure links that have opt's as endpoints
225 - if (lnk.source.type === 'roadm' || lnk.target.type === 'roadm') {
226 - d3.select(this).classed('inactive', true);
227 - }
228 - });
229 -
230 - // deactivate non-packet ports
231 - d3.selectAll('svg .optPort').classed('inactive', true)
232 - }
233 -
234 - function showOpticalLayer() {
235 - network.node.each(function(d) {
236 - // deactivate nodes that are not optical devices
237 - if (d.type !== 'roadm') {
238 - d3.select(this).classed('inactive', true);
239 - }
240 - });
241 -
242 - network.link.each(function(lnk) {
243 - // deactivate infrastructure links that have opt's as endpoints
244 - if (lnk.source.type !== 'roadm' || lnk.target.type !== 'roadm') {
245 - d3.select(this).classed('inactive', true);
246 - }
247 - });
248 -
249 - // deactivate non-packet ports
250 - d3.selectAll('svg .pktPort').classed('inactive', true)
251 - }
252 -
253 - function setUpKeyHandler() {
254 - d3.select('body')
255 - .on('keydown', function () {
256 - processKeyEvent();
257 - if (debug('showKeyHandler')) {
258 - network.svg.append('text')
259 - .attr('x', 5)
260 - .attr('y', 15)
261 - .style('font-size', '20pt')
262 - .text('keyCode: ' + d3.event.keyCode +
263 - ' applied to : ' + contextLabel())
264 - .transition().duration(2000)
265 - .style('font-size', '2pt')
266 - .style('fill-opacity', 0.01)
267 - .remove();
268 - }
269 - });
270 - }
271 -
272 - function contextLabel() {
273 - return hovered === null ? "(nothing)" : hovered.id;
274 - }
275 -
276 - function radioButton(group, id) {
277 - d3.selectAll("#" + group + " .radio").classed("active", false);
278 - d3.select("#" + group + " #" + id).classed("active", true);
279 - }
280 -
281 - function processKeyEvent() {
282 - var code = d3.event.keyCode;
283 - switch (code) {
284 - case 66: // B
285 - toggleBackground();
286 - break;
287 - case 71: // G
288 - cycleLayout();
289 - break;
290 - case 76: // L
291 - cycleLabels();
292 - break;
293 - case 80: // P
294 - togglePorts();
295 - break;
296 - case 85: // U
297 - unpin();
298 - break;
299 - }
300 -
301 - }
302 -
303 - function toggleBackground() {
304 - var bg = d3.select('#bg'),
305 - vis = bg.style('visibility'),
306 - newvis = (vis === 'hidden') ? 'visible' : 'hidden';
307 - bg.style('visibility', newvis);
308 - }
309 -
310 - function cycleLayout() {
311 - config.options.layering = !config.options.layering;
312 - network.force.resume();
313 - }
314 -
315 - function cycleLabels() {
316 - console.log('Cycle Labels - context = ' + contextLabel());
317 - }
318 -
319 - function togglePorts() {
320 - portLabelsOn = !portLabelsOn;
321 - var portVis = portLabelsOn ? 'visible' : 'hidden';
322 - d3.selectAll('.port').style('visibility', portVis);
323 - d3.selectAll('.portText').style('visibility', portVis);
324 - }
325 -
326 - function unpin() {
327 - if (hovered) {
328 - hovered.fixed = false;
329 - findNodeFromData(hovered).classed('fixed', false);
330 - network.force.resume();
331 - }
332 - console.log('Unpin - context = ' + contextLabel());
333 - }
334 -
335 -
336 - // ========================================================
337 -
338 - function drawNetwork() {
339 - $('#view').empty();
340 -
341 - prepareNodesAndLinks();
342 - createLayout();
343 - console.log("\n\nHere is the augmented network object...");
344 - console.log(network);
345 - }
346 -
347 - function prepareNodesAndLinks() {
348 - network.lookup = {};
349 - network.nodes = [];
350 - network.links = [];
351 -
352 - var nw = network.forceWidth,
353 - nh = network.forceHeight;
354 -
355 - function yPosConstraintForNode(n) {
356 - return config.constraints.ypos[n.type || 'host'];
357 - }
358 -
359 - // Note that both 'devices' and 'hosts' get mapped into the nodes array
360 -
361 - // first, the devices...
362 - network.data.devices.forEach(function(n) {
363 - var ypc = yPosConstraintForNode(n),
364 - ix = Math.random() * 0.6 * nw + 0.2 * nw,
365 - iy = ypc * nh,
366 - node = {
367 - id: n.id,
368 - labels: n.labels,
369 - class: 'device',
370 - icon: 'device',
371 - type: n.type,
372 - x: ix,
373 - y: iy,
374 - constraint: {
375 - weight: 0.7,
376 - y: iy
377 - }
378 - };
379 - network.lookup[n.id] = node;
380 - network.nodes.push(node);
381 - });
382 -
383 - // then, the hosts...
384 - network.data.hosts.forEach(function(n) {
385 - var ypc = yPosConstraintForNode(n),
386 - ix = Math.random() * 0.6 * nw + 0.2 * nw,
387 - iy = ypc * nh,
388 - node = {
389 - id: n.id,
390 - labels: n.labels,
391 - class: 'host',
392 - icon: 'host',
393 - type: n.type,
394 - x: ix,
395 - y: iy,
396 - constraint: {
397 - weight: 0.7,
398 - y: iy
399 - }
400 - };
401 - network.lookup[n.id] = node;
402 - network.nodes.push(node);
403 - });
404 -
405 -
406 - // now, process the explicit links...
407 - network.data.links.forEach(function(lnk) {
408 - var src = network.lookup[lnk.src],
409 - dst = network.lookup[lnk.dst],
410 - id = src.id + "-" + dst.id;
411 -
412 - var link = {
413 - class: 'infra',
414 - id: id,
415 - type: lnk.type,
416 - width: lnk.linkWidth,
417 - source: src,
418 - srcPort: lnk.srcPort,
419 - target: dst,
420 - tgtPort: lnk.dstPort,
421 - strength: config.force.linkStrength.infra
422 - };
423 - network.links.push(link);
424 - });
425 -
426 - // finally, infer host links...
427 - network.data.hosts.forEach(function(n) {
428 - var src = network.lookup[n.id],
429 - dst = network.lookup[n.cp.device],
430 - id = src.id + "-" + dst.id;
431 -
432 - var link = {
433 - class: 'host',
434 - id: id,
435 - type: 'hostLink',
436 - width: config.hostLinkWidth,
437 - source: src,
438 - target: dst,
439 - strength: config.force.linkStrength.host
440 - };
441 - network.links.push(link);
442 - });
443 - }
444 -
445 - function createLayout() {
446 -
447 - var cfg = config.force;
448 -
449 - network.force = d3.layout.force()
450 - .size([network.forceWidth, network.forceHeight])
451 - .nodes(network.nodes)
452 - .links(network.links)
453 - .linkStrength(function(d) { return cfg.linkStrength[d.class]; })
454 - .linkDistance(function(d) { return cfg.linkDistance[d.class]; })
455 - .charge(function(d) { return cfg.charge[d.class]; })
456 - .on('tick', tick);
457 -
458 - network.svg = d3.select('#view').append('svg')
459 - .attr('width', netView.width)
460 - .attr('height', netView.height)
461 - .append('g')
462 - .attr('transform', config.force.translate());
463 -// .attr('id', 'zoomable')
464 -// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
465 -
466 - network.svg.append('svg:image')
467 - .attr({
468 - id: 'bg',
469 - width: netView.width,
470 - height: netView.height,
471 - 'xlink:href': config.backgroundUrl
472 - })
473 - .style('visibility',
474 - config.options.loadBackground ? 'visible' : 'hidden');
475 -
476 -// function zoomRedraw() {
477 -// d3.select("#zoomable").attr("transform",
478 -// "translate(" + d3.event.translate + ")"
479 -// + " scale(" + d3.event.scale + ")");
480 -// }
481 -
482 - // TODO: move glow/blur stuff to util script
483 - var glow = network.svg.append('filter')
484 - .attr('x', '-50%')
485 - .attr('y', '-50%')
486 - .attr('width', '200%')
487 - .attr('height', '200%')
488 - .attr('id', 'blue-glow');
489 -
490 - glow.append('feColorMatrix')
491 - .attr('type', 'matrix')
492 - .attr('values', '0 0 0 0 0 ' +
493 - '0 0 0 0 0 ' +
494 - '0 0 0 0 .7 ' +
495 - '0 0 0 1 0 ');
496 -
497 - glow.append('feGaussianBlur')
498 - .attr('stdDeviation', 3)
499 - .attr('result', 'coloredBlur');
500 -
501 - glow.append('feMerge').selectAll('feMergeNode')
502 - .data(['coloredBlur', 'SourceGraphic'])
503 - .enter().append('feMergeNode')
504 - .attr('in', String);
505 -
506 - // TODO: legend (and auto adjust on scroll)
507 -// $('#view').on('scroll', function() {
508 -//
509 -// });
510 -
511 -
512 - // TODO: move drag behavior into separate method.
513 - // == define node drag behavior...
514 - network.draggedThreshold = d3.scale.linear()
515 - .domain([0, 0.1])
516 - .range([5, 20])
517 - .clamp(true);
518 -
519 - function dragged(d) {
520 - var threshold = network.draggedThreshold(network.force.alpha()),
521 - dx = d.oldX - d.px,
522 - dy = d.oldY - d.py;
523 - if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) {
524 - d.dragged = true;
525 - }
526 - return d.dragged;
527 - }
528 -
529 - network.drag = d3.behavior.drag()
530 - .origin(function(d) { return d; })
531 - .on('dragstart', function(d) {
532 - d.oldX = d.x;
533 - d.oldY = d.y;
534 - d.dragged = false;
535 - d.fixed |= 2;
536 - })
537 - .on('drag', function(d) {
538 - d.px = d3.event.x;
539 - d.py = d3.event.y;
540 - if (dragged(d)) {
541 - if (!network.force.alpha()) {
542 - network.force.alpha(.025);
543 - }
544 - }
545 - })
546 - .on('dragend', function(d) {
547 - if (!dragged(d)) {
548 - selectObject(d, this);
549 - }
550 - d.fixed &= ~6;
551 -
552 - // once we've finished moving, pin the node in position,
553 - // if it is a device (not a host)
554 - if (d.class === 'device') {
555 - d.fixed = true;
556 - d3.select(this).classed('fixed', true)
557 - }
558 - });
559 -
560 - $('#view').on('click', function(e) {
561 - if (!$(e.target).closest('.node').length) {
562 - deselectObject();
563 - }
564 - });
565 -
566 - // ...............................................................
567 -
568 - // add links to the display
569 - network.link = network.svg.append('g').attr('id', 'links')
570 - .selectAll('.link')
571 - .data(network.force.links(), function(d) {return d.id})
572 - .enter().append('line')
573 - .attr('class', function(d) {return 'link ' + d.class});
574 -
575 - network.linkSrcPort = network.svg.append('g')
576 - .attr({
577 - id: 'srcPorts',
578 - class: 'portLayer'
579 - });
580 - network.linkTgtPort = network.svg.append('g')
581 - .attr({
582 - id: 'tgtPorts',
583 - class: 'portLayer'
584 - });
585 -
586 - var portVis = portLabelsOn ? 'visible' : 'hidden',
587 - pw = config.labels.port.width,
588 - ph = config.labels.port.height;
589 -
590 - network.link.filter('.infra').each(function(d) {
591 - var srcType = d.source.type === 'roadm' ? 'optPort' : 'pktPort',
592 - tgtType = d.target.type === 'roadm' ? 'optPort' : 'pktPort';
593 -
594 - if (d.source.type)
595 -
596 - network.linkSrcPort.append('rect').attr({
597 - id: 'srcPort-' + safeId(d.id),
598 - class: 'port ' + srcType,
599 - width: pw,
600 - height: ph,
601 - rx: 4,
602 - ry: 4
603 - }).style('visibility', portVis);
604 -
605 - network.linkTgtPort.append('rect').attr({
606 - id: 'tgtPort-' + safeId(d.id),
607 - class: 'port ' + tgtType,
608 - width: pw,
609 - height: ph,
610 - rx: 4,
611 - ry: 4
612 - }).style('visibility', portVis);
613 -
614 - network.linkSrcPort.append('text').attr({
615 - id: 'srcText-' + safeId(d.id),
616 - class: 'portText ' + srcType
617 - }).text(d.srcPort)
618 - .style('visibility', portVis);
619 -
620 - network.linkTgtPort.append('text').attr({
621 - id: 'tgtText-' + safeId(d.id),
622 - class: 'portText ' + tgtType
623 - }).text(d.tgtPort)
624 - .style('visibility', portVis);
625 - });
626 -
627 - // ...............................................................
628 -
629 - // add nodes to the display
630 - network.node = network.svg.selectAll('.node')
631 - .data(network.force.nodes(), function(d) {return d.id})
632 - .enter().append('g')
633 - .attr('class', function(d) {
634 - var cls = 'node ' + d.class;
635 - if (d.type) {
636 - cls += ' ' + d.type;
637 - }
638 - return cls;
639 - })
640 - .attr('transform', function(d) {
641 - return translate(d.x, d.y);
642 - })
643 - .call(network.drag)
644 - .on('mouseover', function(d) {
645 - // TODO: show tooltip
646 - if (network.mouseoutTimeout) {
647 - clearTimeout(network.mouseoutTimeout);
648 - network.mouseoutTimeout = null;
649 - }
650 - hoverObject(d);
651 - })
652 - .on('mouseout', function(d) {
653 - // TODO: hide tooltip
654 - if (network.mouseoutTimeout) {
655 - clearTimeout(network.mouseoutTimeout);
656 - network.mouseoutTimeout = null;
657 - }
658 - network.mouseoutTimeout = setTimeout(function() {
659 - hoverObject(null);
660 - }, config.mouseOutTimerDelayMs);
661 - });
662 -
663 -
664 - // deal with device nodes first
665 - network.nodeRect = network.node.filter('.device')
666 - .append('rect')
667 - .attr({
668 - rx: 5,
669 - ry: 5,
670 - width: 100,
671 - height: 12
672 - });
673 - // note that width/height are adjusted to fit the label text
674 - // then padded, and space made for the icon.
675 -
676 - network.node.filter('.device').each(function(d) {
677 - var node = d3.select(this),
678 - icon = iconUrl(d);
679 -
680 - node.append('text')
681 - // TODO: add label cycle behavior
682 - .text(d.id)
683 - .attr('dy', '1.1em');
684 -
685 - if (icon) {
686 - var cfg = config.icons;
687 - node.append('svg:image')
688 - .attr({
689 - width: cfg.w,
690 - height: cfg.h,
691 - 'xlink:href': icon
692 - });
693 - // note, icon relative positioning (x,y) is done after we have
694 - // adjusted the bounds of the rectangle...
695 - }
696 -
697 - // debug function to show the modelled x,y coordinates of nodes...
698 - if (debug('showNodeXY')) {
699 - node.select('rect').attr('fill-opacity', 0.5);
700 - node.append('circle')
701 - .attr({
702 - class: 'debug',
703 - cx: 0,
704 - cy: 0,
705 - r: '3px'
706 - });
707 - }
708 - });
709 -
710 - // now process host nodes
711 - network.nodeCircle = network.node.filter('.host')
712 - .append('circle')
713 - .attr({
714 - r: config.hostRadius
715 - });
716 -
717 - network.node.filter('.host').each(function(d) {
718 - var node = d3.select(this),
719 - icon = iconUrl(d);
720 -
721 - // debug function to show the modelled x,y coordinates of nodes...
722 - if (debug('showNodeXY')) {
723 - node.select('circle').attr('fill-opacity', 0.5);
724 - node.append('circle')
725 - .attr({
726 - class: 'debug',
727 - cx: 0,
728 - cy: 0,
729 - r: '3px'
730 - });
731 - }
732 - });
733 -
734 - // this function is scheduled to happen soon after the given thread ends
735 - setTimeout(function() {
736 - var lab = config.labels,
737 - portGap = lab.port.gap,
738 - midW = portGap + lab.port.width/ 2,
739 - midH = portGap + lab.port.height / 2;
740 -
741 - // post process the device nodes, to pad their size to fit the
742 - // label text and attach the icon to the right location.
743 - network.node.filter('.device').each(function(d) {
744 - // for every node, recompute size, padding, etc. so text fits
745 - var node = d3.select(this),
746 - text = node.select('text'),
747 - box = adjustRectToFitText(node);
748 -
749 - // now make the computed adjustment
750 - node.select('rect')
751 - .attr(box);
752 -
753 - node.select('image')
754 - .attr('x', box.x + config.icons.xoff)
755 - .attr('y', box.y + config.icons.yoff);
756 -
757 - var bounds = boundsFromBox(box),
758 - portBounds = {
759 - x1: bounds.x1 - midW,
760 - x2: bounds.x2 + midW,
761 - y1: bounds.y1 - midH,
762 - y2: bounds.y2 + midH
763 - };
764 -
765 - // todo: clean up extent and edge work..
766 - d.extent = {
767 - left: bounds.x1 - lab.marginLR,
768 - right: bounds.x2 + lab.marginLR,
769 - top: bounds.y1 - lab.marginTB,
770 - bottom: bounds.y2 + lab.marginTB
771 - };
772 -
773 - d.edge = {
774 - left : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x1, bounds.y2),
775 - right : new geo.LineSegment(bounds.x2, bounds.y1, bounds.x2, bounds.y2),
776 - top : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x2, bounds.y1),
777 - bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
778 - };
779 -
780 - d.portEdge = {
781 - left : new geo.LineSegment(
782 - portBounds.x1, portBounds.y1, portBounds.x1, portBounds.y2
783 - ),
784 - right : new geo.LineSegment(
785 - portBounds.x2, portBounds.y1, portBounds.x2, portBounds.y2
786 - ),
787 - top : new geo.LineSegment(
788 - portBounds.x1, portBounds.y1, portBounds.x2, portBounds.y1
789 - ),
790 - bottom : new geo.LineSegment(
791 - portBounds.x1, portBounds.y2, portBounds.x2, portBounds.y2
792 - )
793 - };
794 -
795 - });
796 -
797 - network.numTicks = 0;
798 - network.preventCollisions = false;
799 - network.force.start();
800 - for (var i = 0; i < config.force.ticksWithoutCollisions; i++) {
801 - network.force.tick();
802 - }
803 - network.preventCollisions = true;
804 - $('#view').css('visibility', 'visible');
805 - });
806 -
807 -
808 - // returns the newly computed bounding box of the rectangle
809 - function adjustRectToFitText(n) {
810 - var text = n.select('text'),
811 - box = text.node().getBBox(),
812 - lab = config.labels;
813 -
814 - // not sure why n.data() returns an array of 1 element...
815 - var data = n.data()[0];
816 -
817 - text.attr('text-anchor', 'middle')
818 - .attr('y', '-0.8em')
819 - .attr('x', lab.imgPad/2)
820 - ;
821 -
822 - // translate the bbox so that it is centered on [x,y]
823 - box.x = -box.width / 2;
824 - box.y = -box.height / 2;
825 -
826 - // add padding
827 - box.x -= (lab.padLR + lab.imgPad/2);
828 - box.width += lab.padLR * 2 + lab.imgPad;
829 - box.y -= lab.padTB;
830 - box.height += lab.padTB * 2;
831 -
832 - return box;
833 - }
834 -
835 - function boundsFromBox(box) {
836 - return {
837 - x1: box.x,
838 - y1: box.y,
839 - x2: box.x + box.width,
840 - y2: box.y + box.height
841 - };
842 - }
843 -
844 - }
845 -
846 - function iconUrl(d) {
847 - return 'img/' + d.type + '.png';
848 -// return config.iconUrl[d.icon];
849 - }
850 -
851 - function translate(x, y) {
852 - return 'translate(' + x + ',' + y + ')';
853 - }
854 -
855 - // prevents collisions amongst device nodes
856 - function preventCollisions() {
857 - var quadtree = d3.geom.quadtree(network.nodes),
858 - hrad = config.hostRadius;
859 -
860 - network.nodes.forEach(function(n) {
861 - var nx1, nx2, ny1, ny2;
862 -
863 - if (n.class === 'device') {
864 - nx1 = n.x + n.extent.left;
865 - nx2 = n.x + n.extent.right;
866 - ny1 = n.y + n.extent.top;
867 - ny2 = n.y + n.extent.bottom;
868 -
869 - } else {
870 - nx1 = n.x - hrad;
871 - nx2 = n.x + hrad;
872 - ny1 = n.y - hrad;
873 - ny2 = n.y + hrad;
874 - }
875 -
876 - quadtree.visit(function(quad, x1, y1, x2, y2) {
877 - if (quad.point && quad.point !== n) {
878 - // check if the rectangles/circles intersect
879 - var p = quad.point,
880 - px1, px2, py1, py2, ix;
881 -
882 - if (p.class === 'device') {
883 - px1 = p.x + p.extent.left;
884 - px2 = p.x + p.extent.right;
885 - py1 = p.y + p.extent.top;
886 - py2 = p.y + p.extent.bottom;
887 -
888 - } else {
889 - px1 = p.x - hrad;
890 - px2 = p.x + hrad;
891 - py1 = p.y - hrad;
892 - py2 = p.y + hrad;
893 - }
894 -
895 - ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2);
896 -
897 - if (ix) {
898 - var xa1 = nx2 - px1, // shift n left , p right
899 - xa2 = px2 - nx1, // shift n right, p left
900 - ya1 = ny2 - py1, // shift n up , p down
901 - ya2 = py2 - ny1, // shift n down , p up
902 - adj = Math.min(xa1, xa2, ya1, ya2);
903 -
904 - if (adj == xa1) {
905 - n.x -= adj / 2;
906 - p.x += adj / 2;
907 - } else if (adj == xa2) {
908 - n.x += adj / 2;
909 - p.x -= adj / 2;
910 - } else if (adj == ya1) {
911 - n.y -= adj / 2;
912 - p.y += adj / 2;
913 - } else if (adj == ya2) {
914 - n.y += adj / 2;
915 - p.y -= adj / 2;
916 - }
917 - }
918 - return ix;
919 - }
920 - });
921 -
922 - });
923 - }
924 -
925 - function tick(e) {
926 - network.numTicks++;
927 -
928 - if (config.options.layering) {
929 - // adjust the y-coord of each node, based on y-pos constraints
930 - network.nodes.forEach(function (n) {
931 - var z = e.alpha * n.constraint.weight;
932 - if (!isNaN(n.constraint.y)) {
933 - n.y = (n.constraint.y * z + n.y * (1 - z));
934 - }
935 - });
936 - }
937 -
938 - if (config.options.collisionPrevention && network.preventCollisions) {
939 - preventCollisions();
940 - }
941 -
942 - var portHalfW = config.labels.port.width / 2,
943 - portHalfH = config.labels.port.height / 2;
944 -
945 - // clip visualization of links at bounds of nodes...
946 - network.link.each(function(d) {
947 - var xs = d.source.x,
948 - ys = d.source.y,
949 - xt = d.target.x,
950 - yt = d.target.y,
951 - line = new geo.LineSegment(xs, ys, xt, yt),
952 - e, ix,
953 - exs, eys, ext, eyt,
954 - pxs, pys, pxt, pyt;
955 -
956 - if (d.class === 'host') {
957 - // no adjustment for source end of link, since hosts are dots
958 - exs = xs;
959 - eys = ys;
960 -
961 - } else {
962 - for (e in d.source.edge) {
963 - ix = line.intersect(d.source.edge[e].offset(xs, ys));
964 - if (ix.in1 && ix.in2) {
965 - exs = ix.x;
966 - eys = ix.y;
967 -
968 - // also pick off the port label intersection
969 - ix = line.intersect(d.source.portEdge[e].offset(xs, ys));
970 - pxs = ix.x;
971 - pys = ix.y;
972 - break;
973 - }
974 - }
975 - }
976 -
977 - for (e in d.target.edge) {
978 - ix = line.intersect(d.target.edge[e].offset(xt, yt));
979 - if (ix.in1 && ix.in2) {
980 - ext = ix.x;
981 - eyt = ix.y;
982 -
983 - // also pick off the port label intersection
984 - ix = line.intersect(d.target.portEdge[e].offset(xt, yt));
985 - pxt = ix.x;
986 - pyt = ix.y;
987 - break;
988 - }
989 - }
990 -
991 - // adjust the endpoints of the link's line to match rectangles
992 - var sid = safeId(d.id);
993 - d3.select(this)
994 - .attr('x1', exs)
995 - .attr('y1', eys)
996 - .attr('x2', ext)
997 - .attr('y2', eyt);
998 -
999 - d3.select('#srcPort-' + sid)
1000 - .attr('x', pxs - portHalfW)
1001 - .attr('y', pys - portHalfH);
1002 -
1003 - d3.select('#tgtPort-' + sid)
1004 - .attr('x', pxt - portHalfW)
1005 - .attr('y', pyt - portHalfH);
1006 -
1007 - // TODO: fit label rect to size of port number.
1008 - d3.select('#srcText-' + sid)
1009 - .attr('x', pxs - 5)
1010 - .attr('y', pys + 3);
1011 -
1012 - d3.select('#tgtText-' + sid)
1013 - .attr('x', pxt - 5)
1014 - .attr('y', pyt + 3);
1015 -
1016 - });
1017 -
1018 - // position each node by translating the node (group) by x,y
1019 - network.node
1020 - .attr('transform', function(d) {
1021 - return translate(d.x, d.y);
1022 - });
1023 -
1024 - }
1025 -
1026 - // $('#docs-close').on('click', function() {
1027 - // deselectObject();
1028 - // return false;
1029 - // });
1030 -
1031 - // $(document).on('click', '.select-object', function() {
1032 - // var obj = graph.data[$(this).data('name')];
1033 - // if (obj) {
1034 - // selectObject(obj);
1035 - // }
1036 - // return false;
1037 - // });
1038 -
1039 - function findNodeFromData(d) {
1040 - var el = null;
1041 - network.node.filter('.' + d.class).each(function(n) {
1042 - if (n.id === d.id) {
1043 - el = d3.select(this);
1044 - }
1045 - });
1046 - return el;
1047 - }
1048 -
1049 - function selectObject(obj, el) {
1050 - var node;
1051 - if (el) {
1052 - node = d3.select(el);
1053 - } else {
1054 - network.node.each(function(d) {
1055 - if (d == obj) {
1056 - node = d3.select(el = this);
1057 - }
1058 - });
1059 - }
1060 - if (!node) return;
1061 -
1062 - if (node.classed('selected')) {
1063 - deselectObject();
1064 - flyinPane(null);
1065 - return;
1066 - }
1067 - deselectObject(false);
1068 -
1069 - selected = {
1070 - obj : obj,
1071 - el : el
1072 - };
1073 -
1074 - node.classed('selected', true);
1075 - flyinPane(obj);
1076 - }
1077 -
1078 - function deselectObject(doResize) {
1079 - // Review: logic of 'resize(...)' function.
1080 - if (doResize || typeof doResize == 'undefined') {
1081 - resize(false);
1082 - }
1083 -
1084 - // deselect all nodes in the network...
1085 - network.node.classed('selected', false);
1086 - selected = {};
1087 - flyinPane(null);
1088 - }
1089 -
1090 - function flyinPane(obj) {
1091 - var pane = d3.select('#flyout'),
1092 - url;
1093 -
1094 - if (obj) {
1095 - // go get details of the selected object from the server...
1096 - url = detailJsonUrl(obj.id);
1097 - d3.json(url, function (err, data) {
1098 - if (err) {
1099 - alert('Oops! Error reading JSON...\n\n' +
1100 - 'URL: ' + url + '\n\n' +
1101 - 'Error: ' + err.message);
1102 - return;
1103 - }
1104 -// console.log("JSON data... " + url);
1105 -// console.log(data);
1106 -
1107 - displayDetails(data, pane);
1108 - });
1109 -
1110 - } else {
1111 - // hide pane
1112 - pane.transition().duration(750)
1113 - .style('right', '-320px')
1114 - .style('opacity', 0.0);
1115 - }
1116 - }
1117 -
1118 - function displayDetails(data, pane) {
1119 - $('#flyout').empty();
1120 -
1121 - var title = pane.append("h2"),
1122 - table = pane.append("table"),
1123 - tbody = table.append("tbody");
1124 -
1125 - $('<img src="img/' + data.type + '.png">').appendTo(title);
1126 - $('<span>').attr('class', 'icon').text(data.id).appendTo(title);
1127 -
1128 -
1129 - // TODO: consider using d3 data bind to TR/TD
1130 -
1131 - data.propOrder.forEach(function(p) {
1132 - if (p === '-') {
1133 - addSep(tbody);
1134 - } else {
1135 - addProp(tbody, p, data.props[p]);
1136 - }
1137 - });
1138 -
1139 - function addSep(tbody) {
1140 - var tr = tbody.append('tr');
1141 - $('<hr>').appendTo(tr.append('td').attr('colspan', 2));
1142 - }
1143 -
1144 - function addProp(tbody, label, value) {
1145 - var tr = tbody.append('tr');
1146 -
1147 - tr.append('td')
1148 - .attr('class', 'label')
1149 - .text(label + ' :');
1150 -
1151 - tr.append('td')
1152 - .attr('class', 'value')
1153 - .text(value);
1154 - }
1155 -
1156 - // show pane
1157 - pane.transition().duration(750)
1158 - .style('right', '20px')
1159 - .style('opacity', 1.0);
1160 - }
1161 -
1162 - function highlightObject(obj) {
1163 - if (obj) {
1164 - if (obj != highlighted) {
1165 - // TODO set or clear "inactive" class on nodes, based on criteria
1166 - network.node.classed('inactive', function(d) {
1167 - // return (obj !== d &&
1168 - // d.relation(obj.id));
1169 - return (obj !== d);
1170 - });
1171 - // TODO: same with links
1172 - network.link.classed('inactive', function(d) {
1173 - return (obj !== d.source && obj !== d.target);
1174 - });
1175 - }
1176 - highlighted = obj;
1177 - } else {
1178 - if (highlighted) {
1179 - // clear the inactive flag (no longer suppressed visually)
1180 - network.node.classed('inactive', false);
1181 - network.link.classed('inactive', false);
1182 - }
1183 - highlighted = null;
1184 -
1185 - }
1186 - }
1187 -
1188 - function hoverObject(obj) {
1189 - if (obj) {
1190 - hovered = obj;
1191 - } else {
1192 - if (hovered) {
1193 - hovered = null;
1194 - }
1195 - }
1196 - }
1197 -
1198 -
1199 - function resize() {
1200 - netView.height = window.innerHeight - config.mastHeight;
1201 - netView.width = window.innerWidth;
1202 - $('#view')
1203 - .css('height', netView.height + 'px')
1204 - .css('width', netView.width + 'px');
1205 -
1206 - network.forceWidth = netView.width - config.force.marginLR;
1207 - network.forceHeight = netView.height - config.force.marginTB;
1208 - }
1209 -
1210 - // ======================================================================
1211 - // register with the UI framework
1212 -
1213 - onos.ui.addView('topo', {
1214 - load: loadNetworkView
1215 - });
1216 -
1217 -
1218 -}(ONOS));
1219 -