weibit

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 29 changed files with 2356 additions and 228 deletions
...@@ -53,11 +53,13 @@ public class HostToInterfaceAdaptor implements InterfaceService { ...@@ -53,11 +53,13 @@ public class HostToInterfaceAdaptor implements InterfaceService {
53 public Interface getInterface(ConnectPoint connectPoint) { 53 public Interface getInterface(ConnectPoint connectPoint) {
54 checkNotNull(connectPoint); 54 checkNotNull(connectPoint);
55 55
56 - PortAddresses portAddresses = 56 + Set<PortAddresses> portAddresses =
57 hostService.getAddressBindingsForPort(connectPoint); 57 hostService.getAddressBindingsForPort(connectPoint);
58 58
59 - if (!portAddresses.ipAddresses().isEmpty()) { 59 + for (PortAddresses addresses : portAddresses) {
60 - return new Interface(portAddresses); 60 + if (addresses.connectPoint().equals(connectPoint)) {
61 + return new Interface(addresses);
62 + }
61 } 63 }
62 64
63 return null; 65 return null;
......
...@@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals; ...@@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.assertTrue;
25 25
26 +import java.util.Collections;
26 import java.util.Map; 27 import java.util.Map;
27 import java.util.Set; 28 import java.util.Set;
28 29
...@@ -63,10 +64,6 @@ public class HostToInterfaceAdaptorTest { ...@@ -63,10 +64,6 @@ public class HostToInterfaceAdaptorTest {
63 private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint( 64 private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint(
64 DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1)); 65 DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1));
65 66
66 - private static final PortAddresses DEFAULT_PA = new PortAddresses(
67 - NON_EXISTENT_CP, null, null);
68 -
69 -
70 @Before 67 @Before
71 public void setUp() throws Exception { 68 public void setUp() throws Exception {
72 hostService = createMock(HostService.class); 69 hostService = createMock(HostService.class);
...@@ -123,7 +120,8 @@ public class HostToInterfaceAdaptorTest { ...@@ -123,7 +120,8 @@ public class HostToInterfaceAdaptorTest {
123 MacAddress mac) { 120 MacAddress mac) {
124 PortAddresses pa = new PortAddresses(cp, ipAddresses, mac); 121 PortAddresses pa = new PortAddresses(cp, ipAddresses, mac);
125 portAddresses.add(pa); 122 portAddresses.add(pa);
126 - expect(hostService.getAddressBindingsForPort(cp)).andReturn(pa).anyTimes(); 123 + expect(hostService.getAddressBindingsForPort(cp)).andReturn(
124 + Collections.singleton(pa)).anyTimes();
127 125
128 Interface intf = new Interface(cp, ipAddresses, mac); 126 Interface intf = new Interface(cp, ipAddresses, mac);
129 interfaces.put(cp, intf); 127 interfaces.put(cp, intf);
...@@ -158,7 +156,7 @@ public class HostToInterfaceAdaptorTest { ...@@ -158,7 +156,7 @@ public class HostToInterfaceAdaptorTest {
158 // Try and get an interface for a connect point with no addresses 156 // Try and get an interface for a connect point with no addresses
159 reset(hostService); 157 reset(hostService);
160 expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP)) 158 expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP))
161 - .andReturn(DEFAULT_PA).anyTimes(); 159 + .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
162 replay(hostService); 160 replay(hostService);
163 161
164 assertNull(adaptor.getInterface(NON_EXISTENT_CP)); 162 assertNull(adaptor.getInterface(NON_EXISTENT_CP));
......
...@@ -34,11 +34,7 @@ public interface HostAdminService { ...@@ -34,11 +34,7 @@ public interface HostAdminService {
34 * Binds IP and MAC addresses to the given connection point. 34 * Binds IP and MAC addresses to the given connection point.
35 * <p> 35 * <p>
36 * The addresses are added to the set of addresses already bound to the 36 * The addresses are added to the set of addresses already bound to the
37 - * connection point. If any of the fields in addresses is null, no change 37 + * connection point.
38 - * is made to the corresponding addresses in the store.
39 - * {@link #unbindAddressesFromPort(PortAddresses)} must be use to unbind
40 - * addresses that have previously been bound.
41 - * </p>
42 * 38 *
43 * @param addresses address object containing addresses to add and the port 39 * @param addresses address object containing addresses to add and the port
44 * to add them to 40 * to add them to
......
...@@ -135,7 +135,7 @@ public interface HostService { ...@@ -135,7 +135,7 @@ public interface HostService {
135 * @param connectPoint the connection point to retrieve address bindings for 135 * @param connectPoint the connection point to retrieve address bindings for
136 * @return addresses bound to the port 136 * @return addresses bound to the port
137 */ 137 */
138 - PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint); 138 + Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint);
139 139
140 /** 140 /**
141 * Adds the specified host listener. 141 * Adds the specified host listener.
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
15 */ 15 */
16 package org.onlab.onos.net.host; 16 package org.onlab.onos.net.host;
17 17
18 +import java.util.Set;
19 +
18 import org.onlab.onos.net.ConnectPoint; 20 import org.onlab.onos.net.ConnectPoint;
19 import org.onlab.onos.net.DeviceId; 21 import org.onlab.onos.net.DeviceId;
20 import org.onlab.onos.net.Host; 22 import org.onlab.onos.net.Host;
...@@ -25,8 +27,6 @@ import org.onlab.packet.IpAddress; ...@@ -25,8 +27,6 @@ import org.onlab.packet.IpAddress;
25 import org.onlab.packet.MacAddress; 27 import org.onlab.packet.MacAddress;
26 import org.onlab.packet.VlanId; 28 import org.onlab.packet.VlanId;
27 29
28 -import java.util.Set;
29 -
30 /** 30 /**
31 * Manages inventory of end-station hosts; not intended for direct use. 31 * Manages inventory of end-station hosts; not intended for direct use.
32 */ 32 */
...@@ -153,5 +153,5 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { ...@@ -153,5 +153,5 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> {
153 * for 153 * for
154 * @return address information for the connection point 154 * @return address information for the connection point
155 */ 155 */
156 - PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint); 156 + Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint);
157 } 157 }
......
...@@ -95,7 +95,7 @@ public class HostServiceAdapter implements HostService { ...@@ -95,7 +95,7 @@ public class HostServiceAdapter implements HostService {
95 } 95 }
96 96
97 @Override 97 @Override
98 - public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) { 98 + public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
99 return null; 99 return null;
100 } 100 }
101 101
......
...@@ -207,7 +207,7 @@ public class HostManager ...@@ -207,7 +207,7 @@ public class HostManager
207 } 207 }
208 208
209 @Override 209 @Override
210 - public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) { 210 + public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
211 return store.getAddressBindingsForPort(connectPoint); 211 return store.getAddressBindingsForPort(connectPoint);
212 } 212 }
213 213
......
...@@ -175,13 +175,15 @@ public class HostMonitor implements TimerTask { ...@@ -175,13 +175,15 @@ public class HostMonitor implements TimerTask {
175 for (Device device : deviceService.getDevices()) { 175 for (Device device : deviceService.getDevices()) {
176 for (Port port : deviceService.getPorts(device.id())) { 176 for (Port port : deviceService.getPorts(device.id())) {
177 ConnectPoint cp = new ConnectPoint(device.id(), port.number()); 177 ConnectPoint cp = new ConnectPoint(device.id(), port.number());
178 - PortAddresses portAddresses = 178 + Set<PortAddresses> portAddressSet =
179 hostManager.getAddressBindingsForPort(cp); 179 hostManager.getAddressBindingsForPort(cp);
180 180
181 - for (InterfaceIpAddress ia : portAddresses.ipAddresses()) { 181 + for (PortAddresses portAddresses : portAddressSet) {
182 - if (ia.subnetAddress().contains(targetIp)) { 182 + for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
183 - sendProbe(device.id(), port, targetIp, 183 + if (ia.subnetAddress().contains(targetIp)) {
184 - ia.ipAddress(), portAddresses.mac()); 184 + sendProbe(device.id(), port, targetIp,
185 + ia.ipAddress(), portAddresses.mac());
186 + }
185 } 187 }
186 } 188 }
187 } 189 }
......
...@@ -134,14 +134,16 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -134,14 +134,16 @@ public class ProxyArpManager implements ProxyArpService {
134 IpAddress target = 134 IpAddress target =
135 IpAddress.valueOf(IpAddress.Version.INET, 135 IpAddress.valueOf(IpAddress.Version.INET,
136 arp.getTargetProtocolAddress()); 136 arp.getTargetProtocolAddress());
137 - PortAddresses addresses = 137 + Set<PortAddresses> addressSet =
138 hostService.getAddressBindingsForPort(inPort); 138 hostService.getAddressBindingsForPort(inPort);
139 139
140 - for (InterfaceIpAddress ia : addresses.ipAddresses()) { 140 + for (PortAddresses addresses : addressSet) {
141 - if (ia.ipAddress().equals(target)) { 141 + for (InterfaceIpAddress ia : addresses.ipAddresses()) {
142 - Ethernet arpReply = 142 + if (ia.ipAddress().equals(target)) {
143 - buildArpReply(ia.ipAddress(), addresses.mac(), eth); 143 + Ethernet arpReply =
144 - sendTo(arpReply, inPort); 144 + buildArpReply(ia.ipAddress(), addresses.mac(), eth);
145 + sendTo(arpReply, inPort);
146 + }
145 } 147 }
146 } 148 }
147 return; 149 return;
...@@ -244,7 +246,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -244,7 +246,7 @@ public class ProxyArpManager implements ProxyArpService {
244 // TODO: Is this sufficient to identify outside-facing ports: just 246 // TODO: Is this sufficient to identify outside-facing ports: just
245 // having IP addresses on a port? 247 // having IP addresses on a port?
246 // 248 //
247 - return !hostService.getAddressBindingsForPort(port).ipAddresses().isEmpty(); 249 + return !hostService.getAddressBindingsForPort(port).isEmpty();
248 } 250 }
249 251
250 @Override 252 @Override
......
...@@ -234,10 +234,10 @@ public class HostManagerTest { ...@@ -234,10 +234,10 @@ public class HostManagerTest {
234 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1); 234 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
235 235
236 mgr.bindAddressesToPort(add1); 236 mgr.bindAddressesToPort(add1);
237 - PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1); 237 + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
238 238
239 - assertTrue(add1.ipAddresses().equals(storedAddresses.ipAddresses())); 239 + assertEquals(1, storedAddresses.size());
240 - assertTrue(add1.mac().equals(storedAddresses.mac())); 240 + assertTrue(storedAddresses.contains(add1));
241 241
242 // Add some more addresses and check that they're added correctly 242 // Add some more addresses and check that they're added correctly
243 PortAddresses add2 = 243 PortAddresses add2 =
...@@ -246,18 +246,19 @@ public class HostManagerTest { ...@@ -246,18 +246,19 @@ public class HostManagerTest {
246 mgr.bindAddressesToPort(add2); 246 mgr.bindAddressesToPort(add2);
247 storedAddresses = mgr.getAddressBindingsForPort(CP1); 247 storedAddresses = mgr.getAddressBindingsForPort(CP1);
248 248
249 - assertTrue(storedAddresses.ipAddresses().equals( 249 + assertEquals(2, storedAddresses.size());
250 - Sets.newHashSet(IA1, IA2, IA3))); 250 + assertTrue(storedAddresses.contains(add1));
251 - assertTrue(storedAddresses.mac().equals(MAC1)); 251 + assertTrue(storedAddresses.contains(add2));
252 252
253 PortAddresses add3 = new PortAddresses(CP1, null, MAC2); 253 PortAddresses add3 = new PortAddresses(CP1, null, MAC2);
254 254
255 mgr.bindAddressesToPort(add3); 255 mgr.bindAddressesToPort(add3);
256 storedAddresses = mgr.getAddressBindingsForPort(CP1); 256 storedAddresses = mgr.getAddressBindingsForPort(CP1);
257 257
258 - assertTrue(storedAddresses.ipAddresses().equals( 258 + assertEquals(3, storedAddresses.size());
259 - Sets.newHashSet(IA1, IA2, IA3))); 259 + assertTrue(storedAddresses.contains(add1));
260 - assertTrue(storedAddresses.mac().equals(MAC2)); 260 + assertTrue(storedAddresses.contains(add2));
261 + assertTrue(storedAddresses.contains(add3));
261 } 262 }
262 263
263 @Test 264 @Test
...@@ -266,10 +267,10 @@ public class HostManagerTest { ...@@ -266,10 +267,10 @@ public class HostManagerTest {
266 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1); 267 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
267 268
268 mgr.bindAddressesToPort(add1); 269 mgr.bindAddressesToPort(add1);
269 - PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1); 270 + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
270 271
271 - assertTrue(storedAddresses.ipAddresses().size() == 2); 272 + assertEquals(1, storedAddresses.size());
272 - assertNotNull(storedAddresses.mac()); 273 + assertTrue(storedAddresses.contains(add1));
273 274
274 PortAddresses rem1 = 275 PortAddresses rem1 =
275 new PortAddresses(CP1, Sets.newHashSet(IA1), null); 276 new PortAddresses(CP1, Sets.newHashSet(IA1), null);
...@@ -277,25 +278,15 @@ public class HostManagerTest { ...@@ -277,25 +278,15 @@ public class HostManagerTest {
277 mgr.unbindAddressesFromPort(rem1); 278 mgr.unbindAddressesFromPort(rem1);
278 storedAddresses = mgr.getAddressBindingsForPort(CP1); 279 storedAddresses = mgr.getAddressBindingsForPort(CP1);
279 280
280 - assertTrue(storedAddresses.ipAddresses().equals(Sets.newHashSet(IA2))); 281 + // It shouldn't have been removed because it didn't match the originally
281 - assertTrue(storedAddresses.mac().equals(MAC1)); 282 + // submitted address object
283 + assertEquals(1, storedAddresses.size());
284 + assertTrue(storedAddresses.contains(add1));
282 285
283 - PortAddresses rem2 = new PortAddresses(CP1, null, MAC1); 286 + mgr.unbindAddressesFromPort(add1);
284 -
285 - mgr.unbindAddressesFromPort(rem2);
286 storedAddresses = mgr.getAddressBindingsForPort(CP1); 287 storedAddresses = mgr.getAddressBindingsForPort(CP1);
287 288
288 - assertTrue(storedAddresses.ipAddresses().equals(Sets.newHashSet(IA2))); 289 + assertTrue(storedAddresses.isEmpty());
289 - assertNull(storedAddresses.mac());
290 -
291 - PortAddresses rem3 =
292 - new PortAddresses(CP1, Sets.newHashSet(IA2), MAC1);
293 -
294 - mgr.unbindAddressesFromPort(rem3);
295 - storedAddresses = mgr.getAddressBindingsForPort(CP1);
296 -
297 - assertTrue(storedAddresses.ipAddresses().isEmpty());
298 - assertNull(storedAddresses.mac());
299 } 290 }
300 291
301 @Test 292 @Test
...@@ -304,16 +295,15 @@ public class HostManagerTest { ...@@ -304,16 +295,15 @@ public class HostManagerTest {
304 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1); 295 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
305 296
306 mgr.bindAddressesToPort(add1); 297 mgr.bindAddressesToPort(add1);
307 - PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1); 298 + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
308 299
309 - assertTrue(storedAddresses.ipAddresses().size() == 2); 300 + assertEquals(1, storedAddresses.size());
310 - assertNotNull(storedAddresses.mac()); 301 + assertTrue(storedAddresses.contains(add1));
311 302
312 mgr.clearAddresses(CP1); 303 mgr.clearAddresses(CP1);
313 storedAddresses = mgr.getAddressBindingsForPort(CP1); 304 storedAddresses = mgr.getAddressBindingsForPort(CP1);
314 305
315 - assertTrue(storedAddresses.ipAddresses().isEmpty()); 306 + assertTrue(storedAddresses.isEmpty());
316 - assertNull(storedAddresses.mac());
317 } 307 }
318 308
319 @Test 309 @Test
...@@ -322,12 +312,10 @@ public class HostManagerTest { ...@@ -322,12 +312,10 @@ public class HostManagerTest {
322 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1); 312 new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
323 313
324 mgr.bindAddressesToPort(add1); 314 mgr.bindAddressesToPort(add1);
325 - PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1); 315 + Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
326 316
327 - assertTrue(storedAddresses.connectPoint().equals(CP1)); 317 + assertEquals(1, storedAddresses.size());
328 - assertTrue(storedAddresses.ipAddresses().equals( 318 + assertTrue(storedAddresses.contains(add1));
329 - Sets.newHashSet(IA1, IA2)));
330 - assertTrue(storedAddresses.mac().equals(MAC1));
331 } 319 }
332 320
333 @Test 321 @Test
......
...@@ -20,7 +20,9 @@ import static org.easymock.EasyMock.expect; ...@@ -20,7 +20,9 @@ import static org.easymock.EasyMock.expect;
20 import static org.easymock.EasyMock.expectLastCall; 20 import static org.easymock.EasyMock.expectLastCall;
21 import static org.easymock.EasyMock.replay; 21 import static org.easymock.EasyMock.replay;
22 import static org.easymock.EasyMock.verify; 22 import static org.easymock.EasyMock.verify;
23 -import static org.junit.Assert.*; 23 +import static org.junit.Assert.assertArrayEquals;
24 +import static org.junit.Assert.assertEquals;
25 +import static org.junit.Assert.assertTrue;
24 26
25 import java.util.ArrayList; 27 import java.util.ArrayList;
26 import java.util.Collections; 28 import java.util.Collections;
...@@ -130,7 +132,7 @@ public class HostMonitorTest { ...@@ -130,7 +132,7 @@ public class HostMonitorTest {
130 expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) 132 expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
131 .andReturn(Collections.<Host>emptySet()).anyTimes(); 133 .andReturn(Collections.<Host>emptySet()).anyTimes();
132 expect(hostManager.getAddressBindingsForPort(cp)) 134 expect(hostManager.getAddressBindingsForPort(cp))
133 - .andReturn(pa).anyTimes(); 135 + .andReturn(Collections.singleton(pa)).anyTimes();
134 replay(hostManager); 136 replay(hostManager);
135 137
136 TestPacketService packetService = new TestPacketService(); 138 TestPacketService packetService = new TestPacketService();
......
...@@ -19,7 +19,10 @@ import static org.easymock.EasyMock.anyObject; ...@@ -19,7 +19,10 @@ import static org.easymock.EasyMock.anyObject;
19 import static org.easymock.EasyMock.createMock; 19 import static org.easymock.EasyMock.createMock;
20 import static org.easymock.EasyMock.expect; 20 import static org.easymock.EasyMock.expect;
21 import static org.easymock.EasyMock.replay; 21 import static org.easymock.EasyMock.replay;
22 -import static org.junit.Assert.*; 22 +import static org.junit.Assert.assertArrayEquals;
23 +import static org.junit.Assert.assertEquals;
24 +import static org.junit.Assert.assertFalse;
25 +import static org.junit.Assert.assertTrue;
23 26
24 import java.util.ArrayList; 27 import java.util.ArrayList;
25 import java.util.Collections; 28 import java.util.Collections;
...@@ -207,13 +210,18 @@ public class ProxyArpManagerTest { ...@@ -207,13 +210,18 @@ public class ProxyArpManagerTest {
207 IpAddress addr2 = IpAddress.valueOf("10.0." + (2 * i) + ".1"); 210 IpAddress addr2 = IpAddress.valueOf("10.0." + (2 * i) + ".1");
208 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1); 211 InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
209 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2); 212 InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
210 - PortAddresses pa = 213 + PortAddresses pa1 =
211 - new PortAddresses(cp, Sets.newHashSet(ia1, ia2), 214 + new PortAddresses(cp, Sets.newHashSet(ia1),
212 - MacAddress.valueOf(i)); 215 + MacAddress.valueOf(2 * i - 1));
213 - addresses.add(pa); 216 + PortAddresses pa2 =
217 + new PortAddresses(cp, Sets.newHashSet(ia2),
218 + MacAddress.valueOf(2 * i));
219 +
220 + addresses.add(pa1);
221 + addresses.add(pa2);
214 222
215 expect(hostService.getAddressBindingsForPort(cp)) 223 expect(hostService.getAddressBindingsForPort(cp))
216 - .andReturn(pa).anyTimes(); 224 + .andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
217 } 225 }
218 226
219 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes(); 227 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
...@@ -222,7 +230,7 @@ public class ProxyArpManagerTest { ...@@ -222,7 +230,7 @@ public class ProxyArpManagerTest {
222 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS), 230 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
223 P1); 231 P1);
224 expect(hostService.getAddressBindingsForPort(cp)) 232 expect(hostService.getAddressBindingsForPort(cp))
225 - .andReturn(new PortAddresses(cp, null, null)).anyTimes(); 233 + .andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
226 } 234 }
227 } 235 }
228 236
...@@ -339,7 +347,8 @@ public class ProxyArpManagerTest { ...@@ -339,7 +347,8 @@ public class ProxyArpManagerTest {
339 IpAddress theirIp = IpAddress.valueOf("10.0.1.254"); 347 IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
340 IpAddress ourFirstIp = IpAddress.valueOf("10.0.1.1"); 348 IpAddress ourFirstIp = IpAddress.valueOf("10.0.1.1");
341 IpAddress ourSecondIp = IpAddress.valueOf("10.0.2.1"); 349 IpAddress ourSecondIp = IpAddress.valueOf("10.0.2.1");
342 - MacAddress ourMac = MacAddress.valueOf(1L); 350 + MacAddress firstMac = MacAddress.valueOf(1L);
351 + MacAddress secondMac = MacAddress.valueOf(2L);
343 352
344 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, 353 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
345 Collections.singleton(theirIp)); 354 Collections.singleton(theirIp));
...@@ -352,7 +361,7 @@ public class ProxyArpManagerTest { ...@@ -352,7 +361,7 @@ public class ProxyArpManagerTest {
352 proxyArp.reply(arpRequest, LOC1); 361 proxyArp.reply(arpRequest, LOC1);
353 362
354 assertEquals(1, packetService.packets.size()); 363 assertEquals(1, packetService.packets.size());
355 - Ethernet arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourFirstIp, theirIp); 364 + Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
356 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); 365 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
357 366
358 // Test a request for the second address on that port 367 // Test a request for the second address on that port
...@@ -362,7 +371,7 @@ public class ProxyArpManagerTest { ...@@ -362,7 +371,7 @@ public class ProxyArpManagerTest {
362 proxyArp.reply(arpRequest, LOC1); 371 proxyArp.reply(arpRequest, LOC1);
363 372
364 assertEquals(1, packetService.packets.size()); 373 assertEquals(1, packetService.packets.size());
365 - arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourSecondIp, theirIp); 374 + arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
366 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); 375 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
367 } 376 }
368 377
......
...@@ -15,12 +15,25 @@ ...@@ -15,12 +15,25 @@
15 */ 15 */
16 package org.onlab.onos.store.host.impl; 16 package org.onlab.onos.store.host.impl;
17 17
18 -import com.google.common.collect.FluentIterable; 18 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
19 -import com.google.common.collect.HashMultimap; 19 +import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
20 -import com.google.common.collect.ImmutableList; 20 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
21 -import com.google.common.collect.ImmutableSet; 21 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
22 -import com.google.common.collect.Multimap; 22 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
23 -import com.google.common.collect.Sets; 23 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
24 +import static org.onlab.util.Tools.namedThreads;
25 +import static org.slf4j.LoggerFactory.getLogger;
26 +
27 +import java.io.IOException;
28 +import java.util.Collections;
29 +import java.util.HashMap;
30 +import java.util.HashSet;
31 +import java.util.Map;
32 +import java.util.Map.Entry;
33 +import java.util.Set;
34 +import java.util.concurrent.ConcurrentHashMap;
35 +import java.util.concurrent.ScheduledExecutorService;
36 +import java.util.concurrent.TimeUnit;
24 37
25 import org.apache.commons.lang3.RandomUtils; 38 import org.apache.commons.lang3.RandomUtils;
26 import org.apache.felix.scr.annotations.Activate; 39 import org.apache.felix.scr.annotations.Activate;
...@@ -45,7 +58,6 @@ import org.onlab.onos.net.host.HostDescription; ...@@ -45,7 +58,6 @@ import org.onlab.onos.net.host.HostDescription;
45 import org.onlab.onos.net.host.HostEvent; 58 import org.onlab.onos.net.host.HostEvent;
46 import org.onlab.onos.net.host.HostStore; 59 import org.onlab.onos.net.host.HostStore;
47 import org.onlab.onos.net.host.HostStoreDelegate; 60 import org.onlab.onos.net.host.HostStoreDelegate;
48 -import org.onlab.onos.net.host.InterfaceIpAddress;
49 import org.onlab.onos.net.host.PortAddresses; 61 import org.onlab.onos.net.host.PortAddresses;
50 import org.onlab.onos.net.provider.ProviderId; 62 import org.onlab.onos.net.provider.ProviderId;
51 import org.onlab.onos.store.AbstractStore; 63 import org.onlab.onos.store.AbstractStore;
...@@ -63,21 +75,13 @@ import org.onlab.packet.VlanId; ...@@ -63,21 +75,13 @@ import org.onlab.packet.VlanId;
63 import org.onlab.util.KryoNamespace; 75 import org.onlab.util.KryoNamespace;
64 import org.slf4j.Logger; 76 import org.slf4j.Logger;
65 77
66 -import java.io.IOException; 78 +import com.google.common.collect.FluentIterable;
67 -import java.util.HashMap; 79 +import com.google.common.collect.HashMultimap;
68 -import java.util.HashSet; 80 +import com.google.common.collect.ImmutableList;
69 -import java.util.Map; 81 +import com.google.common.collect.ImmutableSet;
70 -import java.util.Set; 82 +import com.google.common.collect.Multimap;
71 -import java.util.Map.Entry; 83 +import com.google.common.collect.Multimaps;
72 -import java.util.concurrent.ConcurrentHashMap; 84 +import com.google.common.collect.SetMultimap;
73 -import java.util.concurrent.ScheduledExecutorService;
74 -import java.util.concurrent.TimeUnit;
75 -
76 -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
77 -import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
78 -import static org.onlab.onos.net.host.HostEvent.Type.*;
79 -import static org.onlab.util.Tools.namedThreads;
80 -import static org.slf4j.LoggerFactory.getLogger;
81 85
82 //TODO: multi-provider, annotation not supported. 86 //TODO: multi-provider, annotation not supported.
83 /** 87 /**
...@@ -100,8 +104,9 @@ public class GossipHostStore ...@@ -100,8 +104,9 @@ public class GossipHostStore
100 // Hosts tracked by their location 104 // Hosts tracked by their location
101 private final Multimap<ConnectPoint, Host> locations = HashMultimap.create(); 105 private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
102 106
103 - private final Map<ConnectPoint, PortAddresses> portAddresses = 107 + private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
104 - new ConcurrentHashMap<>(); 108 + Multimaps.synchronizedSetMultimap(
109 + HashMultimap.<ConnectPoint, PortAddresses>create());
105 110
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected HostClockService hostClockService; 112 protected HostClockService hostClockService;
...@@ -343,77 +348,37 @@ public class GossipHostStore ...@@ -343,77 +348,37 @@ public class GossipHostStore
343 348
344 @Override 349 @Override
345 public void updateAddressBindings(PortAddresses addresses) { 350 public void updateAddressBindings(PortAddresses addresses) {
346 - synchronized (portAddresses) { 351 + portAddresses.put(addresses.connectPoint(), addresses);
347 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
348 - if (existing == null) {
349 - portAddresses.put(addresses.connectPoint(), addresses);
350 - } else {
351 - Set<InterfaceIpAddress> union =
352 - Sets.union(existing.ipAddresses(),
353 - addresses.ipAddresses()).immutableCopy();
354 -
355 - MacAddress newMac = (addresses.mac() == null) ? existing.mac()
356 - : addresses.mac();
357 -
358 - PortAddresses newAddresses =
359 - new PortAddresses(addresses.connectPoint(), union, newMac);
360 -
361 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
362 - }
363 - }
364 } 352 }
365 353
366 @Override 354 @Override
367 public void removeAddressBindings(PortAddresses addresses) { 355 public void removeAddressBindings(PortAddresses addresses) {
368 - synchronized (portAddresses) { 356 + portAddresses.remove(addresses.connectPoint(), addresses);
369 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
370 - if (existing != null) {
371 - Set<InterfaceIpAddress> difference =
372 - Sets.difference(existing.ipAddresses(),
373 - addresses.ipAddresses()).immutableCopy();
374 -
375 - // If they removed the existing mac, set the new mac to null.
376 - // Otherwise, keep the existing mac.
377 - MacAddress newMac = existing.mac();
378 - if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
379 - newMac = null;
380 - }
381 -
382 - PortAddresses newAddresses =
383 - new PortAddresses(addresses.connectPoint(), difference, newMac);
384 -
385 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
386 - }
387 - }
388 } 357 }
389 358
390 @Override 359 @Override
391 public void clearAddressBindings(ConnectPoint connectPoint) { 360 public void clearAddressBindings(ConnectPoint connectPoint) {
392 - synchronized (portAddresses) { 361 + portAddresses.removeAll(connectPoint);
393 - portAddresses.remove(connectPoint);
394 - }
395 } 362 }
396 363
397 @Override 364 @Override
398 public Set<PortAddresses> getAddressBindings() { 365 public Set<PortAddresses> getAddressBindings() {
399 synchronized (portAddresses) { 366 synchronized (portAddresses) {
400 - return new HashSet<>(portAddresses.values()); 367 + return ImmutableSet.copyOf(portAddresses.values());
401 } 368 }
402 } 369 }
403 370
404 @Override 371 @Override
405 - public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) { 372 + public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
406 - PortAddresses addresses;
407 -
408 synchronized (portAddresses) { 373 synchronized (portAddresses) {
409 - addresses = portAddresses.get(connectPoint); 374 + Set<PortAddresses> addresses = portAddresses.get(connectPoint);
410 - }
411 375
412 - if (addresses == null) { 376 + if (addresses == null) {
413 - addresses = new PortAddresses(connectPoint, null, null); 377 + return Collections.emptySet();
378 + } else {
379 + return ImmutableSet.copyOf(addresses);
380 + }
414 } 381 }
415 -
416 - return addresses;
417 } 382 }
418 383
419 // Auxiliary extension to allow location to mutate. 384 // Auxiliary extension to allow location to mutate.
......
...@@ -447,13 +447,16 @@ implements MastershipStore { ...@@ -447,13 +447,16 @@ implements MastershipStore {
447 RoleValue oldValue = event.getOldValue(); 447 RoleValue oldValue = event.getOldValue();
448 RoleValue newValue = event.getValue(); 448 RoleValue newValue = event.getValue();
449 449
450 + // There will be no oldValue at the very first instance of an EntryEvent.
451 + // Technically, the progression is: null event -> null master -> some master;
452 + // We say a null master and a null oldValue are the same condition.
450 NodeId oldMaster = null; 453 NodeId oldMaster = null;
451 if (oldValue != null) { 454 if (oldValue != null) {
452 oldMaster = oldValue.get(MASTER); 455 oldMaster = oldValue.get(MASTER);
453 } 456 }
454 NodeId newMaster = newValue.get(MASTER); 457 NodeId newMaster = newValue.get(MASTER);
455 458
456 - if (Objects.equal(oldMaster, newMaster)) { 459 + if (!Objects.equal(oldMaster, newMaster)) {
457 notifyDelegate(new MastershipEvent( 460 notifyDelegate(new MastershipEvent(
458 MASTER_CHANGED, event.getKey(), event.getValue().roleInfo())); 461 MASTER_CHANGED, event.getKey(), event.getValue().roleInfo()));
459 } else { 462 } else {
......
...@@ -15,10 +15,18 @@ ...@@ -15,10 +15,18 @@
15 */ 15 */
16 package org.onlab.onos.store.trivial.impl; 16 package org.onlab.onos.store.trivial.impl;
17 17
18 -import com.google.common.collect.HashMultimap; 18 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
19 -import com.google.common.collect.ImmutableSet; 19 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
20 -import com.google.common.collect.Multimap; 20 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
21 -import com.google.common.collect.Sets; 21 +import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
22 +import static org.slf4j.LoggerFactory.getLogger;
23 +
24 +import java.util.Collections;
25 +import java.util.HashSet;
26 +import java.util.Map;
27 +import java.util.Set;
28 +import java.util.concurrent.ConcurrentHashMap;
29 +
22 import org.apache.felix.scr.annotations.Activate; 30 import org.apache.felix.scr.annotations.Activate;
23 import org.apache.felix.scr.annotations.Component; 31 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Deactivate; 32 import org.apache.felix.scr.annotations.Deactivate;
...@@ -34,7 +42,6 @@ import org.onlab.onos.net.host.HostDescription; ...@@ -34,7 +42,6 @@ import org.onlab.onos.net.host.HostDescription;
34 import org.onlab.onos.net.host.HostEvent; 42 import org.onlab.onos.net.host.HostEvent;
35 import org.onlab.onos.net.host.HostStore; 43 import org.onlab.onos.net.host.HostStore;
36 import org.onlab.onos.net.host.HostStoreDelegate; 44 import org.onlab.onos.net.host.HostStoreDelegate;
37 -import org.onlab.onos.net.host.InterfaceIpAddress;
38 import org.onlab.onos.net.host.PortAddresses; 45 import org.onlab.onos.net.host.PortAddresses;
39 import org.onlab.onos.net.provider.ProviderId; 46 import org.onlab.onos.net.provider.ProviderId;
40 import org.onlab.onos.store.AbstractStore; 47 import org.onlab.onos.store.AbstractStore;
...@@ -43,13 +50,11 @@ import org.onlab.packet.MacAddress; ...@@ -43,13 +50,11 @@ import org.onlab.packet.MacAddress;
43 import org.onlab.packet.VlanId; 50 import org.onlab.packet.VlanId;
44 import org.slf4j.Logger; 51 import org.slf4j.Logger;
45 52
46 -import java.util.HashSet; 53 +import com.google.common.collect.HashMultimap;
47 -import java.util.Map; 54 +import com.google.common.collect.ImmutableSet;
48 -import java.util.Set; 55 +import com.google.common.collect.Multimap;
49 -import java.util.concurrent.ConcurrentHashMap; 56 +import com.google.common.collect.Multimaps;
50 - 57 +import com.google.common.collect.SetMultimap;
51 -import static org.onlab.onos.net.host.HostEvent.Type.*;
52 -import static org.slf4j.LoggerFactory.getLogger;
53 58
54 // TODO: multi-provider, annotation not supported. 59 // TODO: multi-provider, annotation not supported.
55 /** 60 /**
...@@ -70,8 +75,9 @@ public class SimpleHostStore ...@@ -70,8 +75,9 @@ public class SimpleHostStore
70 // Hosts tracked by their location 75 // Hosts tracked by their location
71 private final Multimap<ConnectPoint, Host> locations = HashMultimap.create(); 76 private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
72 77
73 - private final Map<ConnectPoint, PortAddresses> portAddresses = 78 + private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
74 - new ConcurrentHashMap<>(); 79 + Multimaps.synchronizedSetMultimap(
80 + HashMultimap.<ConnectPoint, PortAddresses>create());
75 81
76 @Activate 82 @Activate
77 public void activate() { 83 public void activate() {
...@@ -213,77 +219,37 @@ public class SimpleHostStore ...@@ -213,77 +219,37 @@ public class SimpleHostStore
213 219
214 @Override 220 @Override
215 public void updateAddressBindings(PortAddresses addresses) { 221 public void updateAddressBindings(PortAddresses addresses) {
216 - synchronized (portAddresses) { 222 + portAddresses.put(addresses.connectPoint(), addresses);
217 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
218 - if (existing == null) {
219 - portAddresses.put(addresses.connectPoint(), addresses);
220 - } else {
221 - Set<InterfaceIpAddress> union =
222 - Sets.union(existing.ipAddresses(),
223 - addresses.ipAddresses()).immutableCopy();
224 -
225 - MacAddress newMac = (addresses.mac() == null) ? existing.mac()
226 - : addresses.mac();
227 -
228 - PortAddresses newAddresses =
229 - new PortAddresses(addresses.connectPoint(), union, newMac);
230 -
231 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
232 - }
233 - }
234 } 223 }
235 224
236 @Override 225 @Override
237 public void removeAddressBindings(PortAddresses addresses) { 226 public void removeAddressBindings(PortAddresses addresses) {
238 - synchronized (portAddresses) { 227 + portAddresses.remove(addresses.connectPoint(), addresses);
239 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
240 - if (existing != null) {
241 - Set<InterfaceIpAddress> difference =
242 - Sets.difference(existing.ipAddresses(),
243 - addresses.ipAddresses()).immutableCopy();
244 -
245 - // If they removed the existing mac, set the new mac to null.
246 - // Otherwise, keep the existing mac.
247 - MacAddress newMac = existing.mac();
248 - if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
249 - newMac = null;
250 - }
251 -
252 - PortAddresses newAddresses =
253 - new PortAddresses(addresses.connectPoint(), difference, newMac);
254 -
255 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
256 - }
257 - }
258 } 228 }
259 229
260 @Override 230 @Override
261 public void clearAddressBindings(ConnectPoint connectPoint) { 231 public void clearAddressBindings(ConnectPoint connectPoint) {
262 - synchronized (portAddresses) { 232 + portAddresses.removeAll(connectPoint);
263 - portAddresses.remove(connectPoint);
264 - }
265 } 233 }
266 234
267 @Override 235 @Override
268 public Set<PortAddresses> getAddressBindings() { 236 public Set<PortAddresses> getAddressBindings() {
269 synchronized (portAddresses) { 237 synchronized (portAddresses) {
270 - return new HashSet<>(portAddresses.values()); 238 + return ImmutableSet.copyOf(portAddresses.values());
271 } 239 }
272 } 240 }
273 241
274 @Override 242 @Override
275 - public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) { 243 + public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
276 - PortAddresses addresses;
277 -
278 synchronized (portAddresses) { 244 synchronized (portAddresses) {
279 - addresses = portAddresses.get(connectPoint); 245 + Set<PortAddresses> addresses = portAddresses.get(connectPoint);
280 - }
281 246
282 - if (addresses == null) { 247 + if (addresses == null) {
283 - addresses = new PortAddresses(connectPoint, null, null); 248 + return Collections.emptySet();
249 + } else {
250 + return ImmutableSet.copyOf(addresses);
251 + }
284 } 252 }
285 -
286 - return addresses;
287 } 253 }
288 254
289 // Auxiliary extension to allow location to mutate. 255 // Auxiliary extension to allow location to mutate.
......
...@@ -5,7 +5,7 @@ export ONOS_ROOT=${ONOS_ROOT:-~/onos-next} ...@@ -5,7 +5,7 @@ export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
5 5
6 # M2 repository and Karaf gold bits 6 # M2 repository and Karaf gold bits
7 export M2_REPO=${M2_REPO:-~/.m2/repository} 7 export M2_REPO=${M2_REPO:-~/.m2/repository}
8 -export KARAF_VERSION=${KARAF_VERSION:-3.0.1} 8 +export KARAF_VERSION=${KARAF_VERSION:-3.0.2}
9 export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-$KARAF_VERSION.zip} 9 export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-$KARAF_VERSION.zip}
10 export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-$KARAF_VERSION.tar.gz} 10 export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-$KARAF_VERSION.tar.gz}
11 export KARAF_DIST=$(basename $KARAF_ZIP .zip) 11 export KARAF_DIST=$(basename $KARAF_ZIP .zip)
......
...@@ -8,7 +8,9 @@ export ONOS_ROOT=${ONOS_ROOT:-~/onos-next} ...@@ -8,7 +8,9 @@ export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
8 # Setup some environmental context for developers 8 # Setup some environmental context for developers
9 if [ -z "${JAVA_HOME}" ]; then 9 if [ -z "${JAVA_HOME}" ]; then
10 if [ -x /usr/libexec/java_home ]; then 10 if [ -x /usr/libexec/java_home ]; then
11 - export JAVA_HOME=$(/usr/libexec/java_home -v 1.7) 11 + export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
12 + elif [ -d /usr/lib/jvm/java-8-oracle ]; then
13 + export JAVA_HOME="/usr/lib/jvm/java-8-oracle"
12 elif [ -d /usr/lib/jvm/java-7-openjdk-amd64 ]; then 14 elif [ -d /usr/lib/jvm/java-7-openjdk-amd64 ]; then
13 export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64" 15 export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64"
14 fi 16 fi
...@@ -16,7 +18,7 @@ fi ...@@ -16,7 +18,7 @@ fi
16 18
17 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2} 19 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2}
18 20
19 -export KARAF_VERSION=${KARAF_VERSION:-3.0.1} 21 +export KARAF_VERSION=${KARAF_VERSION:-3.0.2}
20 export KARAF=${KARAF:-~/Applications/apache-karaf-$KARAF_VERSION} 22 export KARAF=${KARAF:-~/Applications/apache-karaf-$KARAF_VERSION}
21 export KARAF_LOG=$KARAF/data/log/karaf.log 23 export KARAF_LOG=$KARAF/data/log/karaf.log
22 24
......
...@@ -6,4 +6,4 @@ export OC2="192.168.56.102" ...@@ -6,4 +6,4 @@ export OC2="192.168.56.102"
6 export OCN="192.168.56.103" 6 export OCN="192.168.56.103"
7 export OCI="${OC1}" 7 export OCI="${OC1}"
8 8
9 -export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}" 9 +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-gui,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
...@@ -7,4 +7,4 @@ export OC3="192.168.56.104" ...@@ -7,4 +7,4 @@ export OC3="192.168.56.104"
7 export OCN="192.168.56.103" 7 export OCN="192.168.56.103"
8 export OCI="${OC1}" 8 export OCI="${OC1}"
9 9
10 -export ONOS_FEATURES="" 10 +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-gui,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
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 + Version 1.1
21 +
22 + @author Simon Hunt
23 + -->
24 +<html>
25 +<head>
26 + <meta charset="utf-8">
27 + <title>ONOS GUI (v1.1)</title>
28 +
29 + <link rel="shortcut icon" href="img/onos-logo.png">
30 +
31 + <!-- first script to be run -->
32 + <script src="preamble.js"></script>
33 +
34 + <!-- Third party library code included here -->
35 + <!--TODO: use the minified version of d3, once debugging is complete -->
36 + <script src="libs/d3.js"></script>
37 + <script src="libs/jquery-2.1.1.min.js"></script>
38 +
39 + <!-- Base library and framework stylesheets included here -->
40 + <link rel="stylesheet" href="base.css">
41 + <link rel="stylesheet" href="onos2.css">
42 + <link rel="stylesheet" href="mast2.css">
43 +
44 + <!-- This is where contributed stylesheets get INJECTED -->
45 + <!-- TODO: replace with template marker and inject refs server-side -->
46 + <link rel="stylesheet" href="topo2.css">
47 +
48 +
49 + <!-- General library modules included here-->
50 + <script src="geometry2.js"></script>
51 +
52 + <!-- ONOS UI Framework included here-->
53 + <script src="onos2.js"></script>
54 +
55 +</head>
56 +<body>
57 + <div id="frame">
58 + <div id="mast">
59 + <!-- NOTE: masthead injected here by mast.js -->
60 + </div>
61 + <div id="view">
62 + <!-- NOTE: views injected here by onos.js -->
63 + </div>
64 + <div id="overlays">
65 + <!-- NOTE: overlays injected here, as needed -->
66 + </div>
67 + </div>
68 +
69 + <!-- Initialize the UI...-->
70 + <script type="text/javascript">
71 + var ONOS = $.onos({note: "config, if needed"});
72 + </script>
73 +
74 + <!-- Framework module files included here -->
75 + <script src="mast2.js"></script>
76 +
77 + <!-- Contributed (application) views injected here -->
78 + <!-- TODO: replace with template marker and inject refs server-side -->
79 + <script src="temp2.js"></script>
80 +
81 + <!-- finally, build the UI-->
82 + <script type="text/javascript">
83 + $(ONOS.buildUi);
84 + </script>
85 +
86 +</body>
87 +</html>
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 GUI -- Masthead -- CSS file
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +#mast {
24 + height: 36px;
25 + padding: 4px;
26 + background-color: #bbb;
27 + vertical-align: baseline;
28 + box-shadow: 0px 2px 8px #777;
29 +}
30 +
31 +#mast img#logo {
32 + height: 38px;
33 + padding-left: 8px;
34 + padding-right: 8px;
35 +}
36 +
37 +#mast span.title {
38 + color: #369;
39 + font-size: 14pt;
40 + font-style: italic;
41 + vertical-align: 12px;
42 +}
43 +
44 +#mast span.right {
45 + padding-top: 8px;
46 + padding-right: 16px;
47 + float: right;
48 +}
49 +
50 +#mast span.radio {
51 + color: darkslateblue;
52 + font-size: 10pt;
53 +}
54 +
55 +#mast span.radio {
56 + margin: 4px 0;
57 + border: 1px dotted #222;
58 + padding: 1px 6px;
59 + color: #eee;
60 + cursor: pointer;
61 +}
62 +
63 +#mast span.radio.active {
64 + background-color: #bbb;
65 + border: 1px solid #eee;
66 + padding: 1px 6px;
67 + color: #666;
68 + font-weight: bold;
69 +}
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 GUI -- Masthead
19 +
20 + Defines the masthead for the UI. Injects logo and title, as well as providing
21 + the placeholder for a set of radio buttons.
22 +
23 + @author Simon Hunt
24 + */
25 +
26 +(function (onos){
27 + 'use strict';
28 +
29 + // API's
30 + var api = onos.api;
31 +
32 + // Config variables
33 + var guiTitle = 'Open Networking Operating System';
34 +
35 + // DOM elements and the like
36 + var mast = d3.select('#mast');
37 +
38 + mast.append('img')
39 + .attr({
40 + id: 'logo',
41 + src: 'img/onos-logo.png'
42 + });
43 +
44 + mast.append('span')
45 + .attr({
46 + class: 'title'
47 + })
48 + .text(guiTitle);
49 +
50 + mast.append('span')
51 + .attr({
52 + id: 'mastRadio',
53 + class: 'right'
54 + });
55 +
56 +}(ONOS));
...\ 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 GUI -- Base Framework -- CSS file
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +html, body {
24 + height: 100%;
25 +}
26 +
27 +
28 +/*
29 + * === DEBUGGING ======
30 + */
31 +svg {
32 + /*border: 1px dashed red;*/
33 +}
34 +
35 +svg #bg {
36 + opacity: 0.5;
37 +}
38 +
39 +
40 +/*
41 + * Network Graph elements ======================================
42 + */
43 +
44 +svg .link {
45 + fill: none;
46 + stroke: #666;
47 + stroke-width: 2.0px;
48 + opacity: .7;
49 +
50 + transition: opacity 250ms;
51 + -webkit-transition: opacity 250ms;
52 + -moz-transition: opacity 250ms;
53 +}
54 +
55 +svg .link.host {
56 + stroke: #666;
57 + stroke-width: 1px;
58 +}
59 +
60 +svg g.portLayer rect.port {
61 + fill: #ccc;
62 +}
63 +
64 +svg g.portLayer text {
65 + font: 8pt sans-serif;
66 + pointer-events: none;
67 +}
68 +
69 +svg .node.device rect {
70 + stroke-width: 1.5px;
71 +
72 + transition: opacity 250ms;
73 + -webkit-transition: opacity 250ms;
74 + -moz-transition: opacity 250ms;
75 +}
76 +
77 +svg .node.device.fixed rect {
78 + stroke-width: 1.5;
79 + stroke: #ccc;
80 +}
81 +
82 +svg .node.device.roadm rect {
83 + fill: #03c;
84 +}
85 +
86 +svg .node.device.switch rect {
87 + fill: #06f;
88 +}
89 +
90 +svg .node.host circle {
91 + fill: #c96;
92 + stroke: #000;
93 +}
94 +
95 +svg .node text {
96 + fill: white;
97 + font: 10pt sans-serif;
98 + pointer-events: none;
99 +}
100 +
101 +/* for debugging */
102 +svg .node circle.debug {
103 + fill: white;
104 + stroke: red;
105 +}
106 +svg .node rect.debug {
107 + fill: yellow;
108 + stroke: red;
109 + opacity: 0.35;
110 +}
111 +
112 +
113 +svg .node.selected rect,
114 +svg .node.selected circle {
115 + filter: url(#blue-glow);
116 +}
117 +
118 +svg .link.inactive,
119 +svg .port.inactive,
120 +svg .portText.inactive,
121 +svg .node.inactive rect,
122 +svg .node.inactive circle,
123 +svg .node.inactive text,
124 +svg .node.inactive image {
125 + opacity: .1;
126 +}
127 +
128 +svg .node.inactive.selected rect,
129 +svg .node.inactive.selected text,
130 +svg .node.inactive.selected image {
131 + opacity: .6;
132 +}
133 +
134 +/*
135 + * =============================================================
136 + */
137 +
138 +/*
139 + * Specific structural elements
140 + */
141 +
142 +/* This is to ensure that the body does not expand to account for the
143 + flyout details pane, that is positioned "off screen".
144 + */
145 +body {
146 + overflow: hidden;
147 +}
148 +
149 +
150 +#frame {
151 + width: 100%;
152 + height: 100%;
153 + background-color: #fff;
154 +}
155 +
156 +#flyout {
157 + position: absolute;
158 + z-index: 100;
159 + display: block;
160 + top: 10%;
161 + width: 280px;
162 + right: -300px;
163 + opacity: 0;
164 + background-color: rgba(255,255,255,0.8);
165 +
166 + padding: 10px;
167 + color: black;
168 + font-size: 10pt;
169 + box-shadow: 2px 2px 16px #777;
170 +}
171 +
172 +#flyout h2 {
173 + margin: 8px 4px;
174 + color: black;
175 + vertical-align: middle;
176 +}
177 +
178 +#flyout h2 img {
179 + height: 32px;
180 + padding-right: 8px;
181 + vertical-align: middle;
182 +}
183 +
184 +#flyout p, table {
185 + margin: 4px 4px;
186 +}
187 +
188 +#flyout td.label {
189 + font-style: italic;
190 + color: #777;
191 + padding-right: 12px;
192 +}
193 +
194 +#flyout td.value {
195 +
196 +}
197 +
198 +#flyout hr {
199 + height: 1px;
200 + color: #ccc;
201 + background-color: #ccc;
202 + border: 0;
203 +}
204 +
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 GUI -- Base 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 + defaultHash = 'temp1';
28 +
29 +
30 + // attach our main function to the jQuery object
31 + $.onos = function (options) {
32 + var publicApi; // public api
33 +
34 + // internal state
35 + var views = {},
36 + current = {
37 + view: null,
38 + ctx: ''
39 + },
40 + built = false,
41 + errorCount = 0;
42 +
43 + // DOM elements etc.
44 + var $view;
45 +
46 +
47 + // ..........................................................
48 + // Internal functions
49 +
50 + // throw an error
51 + function throwError(msg) {
52 + // separate function, as we might add tracing here too, later
53 + throw new Error(msg);
54 + }
55 +
56 + function doError(msg) {
57 + errorCount++;
58 + console.warn(msg);
59 + }
60 +
61 + // hash navigation
62 + function hash() {
63 + var hash = window.location.hash,
64 + redo = false,
65 + view,
66 + t;
67 +
68 + if (!hash) {
69 + hash = defaultHash;
70 + redo = true;
71 + }
72 +
73 + t = parseHash(hash);
74 + if (!t || !t.vid) {
75 + doError('Unable to parse target hash: ' + hash);
76 + }
77 +
78 + view = views[t.vid];
79 + if (!view) {
80 + doError('No view defined with id: ' + t.vid);
81 + }
82 +
83 + if (redo) {
84 + window.location.hash = makeHash(t);
85 + // the above will result in a hashchange event, invoking
86 + // this function again
87 + } else {
88 + // hash was not modified... navigate to where we need to be
89 + navigate(hash, view, t);
90 + }
91 +
92 + }
93 +
94 + function parseHash(s) {
95 + // extract navigation coordinates from the supplied string
96 + // "vid,ctx" --> { vid:vid, ctx:ctx }
97 +
98 + var m = /^[#]{0,1}(\S+),(\S*)$/.exec(s);
99 + if (m) {
100 + return { vid: m[1], ctx: m[2] };
101 + }
102 +
103 + m = /^[#]{0,1}(\S+)$/.exec(s);
104 + return m ? { vid: m[1] } : null;
105 + }
106 +
107 + function makeHash(t, ctx) {
108 + // make a hash string from the given navigation coordinates.
109 + // if t is not an object, then it is a vid
110 + var h = t,
111 + c = ctx || '';
112 +
113 + if ($.isPlainObject(t)) {
114 + h = t.vid;
115 + c = t.ctx || '';
116 + }
117 +
118 + if (c) {
119 + h += ',' + c;
120 + }
121 + return h;
122 + }
123 +
124 + function navigate(hash, view, t) {
125 + // closePanes() // flyouts etc.
126 + // updateNav() // accordion / selected nav item
127 + createView(view);
128 + setView(view, hash, t);
129 + }
130 +
131 + function reportBuildErrors() {
132 + // TODO: validate registered views / nav-item linkage etc.
133 + console.log('(no build errors)');
134 + }
135 +
136 + // ..........................................................
137 + // View life-cycle functions
138 +
139 + function createView(view) {
140 + var $d;
141 + // lazy initialization of the view
142 + if (view && !view.$div) {
143 + $d = $view.append('div')
144 + .attr({
145 + id: view.vid
146 + });
147 + view.$div = $d; // cache a reference to the selected div
148 + }
149 + }
150 +
151 + function setView(view, hash, t) {
152 + // set the specified view as current, while invoking the
153 + // appropriate life-cycle callbacks
154 +
155 + // if there is a current view, and it is not the same as
156 + // the incoming view, then unload it...
157 + if (current.view && !(current.view.vid !== view.vid)) {
158 + current.view.unload();
159 + }
160 +
161 + // cache new view and context
162 + current.view = view;
163 + current.ctx = t.ctx || '';
164 +
165 + // TODO: clear radio button set (store on view?)
166 +
167 + // preload is called only once, after the view is in the DOM
168 + if (!view.preloaded) {
169 + view.preload(t.ctx);
170 + }
171 +
172 + // clear the view of stale data
173 + view.reset();
174 +
175 + // load the view
176 + view.load(t.ctx);
177 + }
178 +
179 + function resizeView() {
180 + if (current.view) {
181 + current.view.resize();
182 + }
183 + }
184 +
185 + // ..........................................................
186 + // View class
187 + // Captures state information about a view.
188 +
189 + // Constructor
190 + // vid : view id
191 + // nid : id of associated nav-item (optional)
192 + // cb : callbacks (preload, reset, load, resize, unload, error)
193 + // data: custom data object (optional)
194 + function View(vid) {
195 + var av = 'addView(): ',
196 + args = Array.prototype.slice.call(arguments),
197 + nid,
198 + cb,
199 + data;
200 +
201 + args.shift(); // first arg is always vid
202 + if (typeof args[0] === 'string') { // nid specified
203 + nid = args.shift();
204 + }
205 + cb = args.shift();
206 + data = args.shift();
207 +
208 + this.vid = vid;
209 +
210 + if (validateViewArgs(vid)) {
211 + this.nid = nid; // explicit navitem id (can be null)
212 + this.cb = $.isPlainObject(cb) ? cb : {}; // callbacks
213 + this.data = data; // custom data (can be null)
214 + this.$div = null; // view not yet added to DOM
215 + this.ok = true; // valid view
216 + }
217 +
218 + }
219 +
220 + function validateViewArgs(vid) {
221 + var ok = false;
222 + if (typeof vid !== 'string' || !vid) {
223 + doError(av + 'vid required');
224 + } else if (views[vid]) {
225 + doError(av + 'View ID "' + vid + '" already exists');
226 + } else {
227 + ok = true;
228 + }
229 + return ok;
230 + }
231 +
232 + var viewInstanceMethods = {
233 + toString: function () {
234 + return '[View: id="' + this.vid + '"]';
235 + },
236 +
237 + token: function() {
238 + return {
239 + vid: this.vid,
240 + nid: this.nid,
241 + data: this.data
242 + }
243 + }
244 + // TODO: create, preload, reset, load, error, resize, unload
245 + };
246 +
247 + // attach instance methods to the view prototype
248 + $.extend(View.prototype, viewInstanceMethods);
249 +
250 + // ..........................................................
251 + // Exported API
252 +
253 + publicApi = {
254 + printTime: function () {
255 + console.log("the time is " + new Date());
256 + },
257 +
258 + addView: function (vid, nid, cb, data) {
259 + var view = new View(vid, nid, cb, data),
260 + token;
261 + if (view.ok) {
262 + views[vid] = view;
263 + token = view.token();
264 + } else {
265 + token = { vid: view.vid, bad: true };
266 + }
267 + return token;
268 + }
269 + };
270 +
271 + // function to be called from index.html to build the ONOS UI
272 + function buildOnosUi() {
273 + tsB = new Date().getTime();
274 + tsI = tsB - tsI; // initialization duration
275 +
276 + console.log('ONOS UI initialized in ' + tsI + 'ms');
277 +
278 + if (built) {
279 + throwError("ONOS UI already built!");
280 + }
281 + built = true;
282 +
283 + $view = d3.select('#view');
284 +
285 + $(window).on('hashchange', hash);
286 +
287 + // Invoke hashchange callback to navigate to content
288 + // indicated by the window location hash.
289 + hash();
290 +
291 + // If there were any build errors, report them
292 + reportBuildErrors();
293 + }
294 +
295 +
296 + // export the api and build-UI function
297 + return {
298 + api: publicApi,
299 + buildUi: buildOnosUi
300 + };
301 + };
302 +
303 +}(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 GUI -- Preamble -- the first thing we do
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +(function () {
24 + // Check if the URL in the address bar contains a parameter section
25 + // (delineated by '?'). If this is the case, rewrite using '#' instead.
26 +
27 + var m = /([^?]*)\?(.*)/.exec(window.location.href);
28 + if (m) {
29 + window.location.href = m[1] + '#' + m[2];
30 + }
31 +
32 +}());
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 + Temporary module file to test the framework integration.
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +(function (onos) {
24 + 'use strict';
25 +
26 + var api = onos.api;
27 +
28 + var vid,
29 + svg;
30 +
31 + // == define your functions here.....
32 +
33 +
34 + // NOTE: view is a data structure:
35 + // {
36 + // id: 'view-id',
37 + // el: ... // d3 selection of dom view div.
38 + // }
39 +
40 + function load(view) {
41 + vid = view.id;
42 + svg = view.el.append('svg')
43 + .attr({
44 + width: 400,
45 + height: 300
46 + });
47 +
48 + var fill = (vid === 'temp1') ? 'red' : 'blue',
49 + stroke = (vid === 'temp2') ? 'yellow' : 'black';
50 +
51 + svg.append('circle')
52 + .attr({
53 + cx: 200,
54 + cy: 150,
55 + r: 30
56 + })
57 + .style({
58 + fill: fill,
59 + stroke: stroke,
60 + 'stroke-width': 3.5
61 + });
62 + }
63 +
64 + // == register views here, with links to lifecycle callbacks
65 +
66 + api.addView('temp1', {
67 + load: load
68 + });
69 +
70 + api.addView('temp2', {
71 + load: load
72 + });
73 +
74 +
75 +}(ONOS));
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 GUI -- Topology view -- CSS file
19 +
20 + @author Simon Hunt
21 + */
22 +
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: true,
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 +