Madan Jampani

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

Showing 33 changed files with 760 additions and 142 deletions
...@@ -30,6 +30,7 @@ import org.onlab.onos.core.CoreService; ...@@ -30,6 +30,7 @@ import org.onlab.onos.core.CoreService;
30 import org.onlab.onos.net.host.HostService; 30 import org.onlab.onos.net.host.HostService;
31 import org.onlab.onos.net.intent.IntentService; 31 import org.onlab.onos.net.intent.IntentService;
32 import org.onlab.onos.sdnip.bgp.BgpRouteEntry; 32 import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
33 +import org.onlab.onos.sdnip.bgp.BgpSession;
33 import org.onlab.onos.sdnip.bgp.BgpSessionManager; 34 import org.onlab.onos.sdnip.bgp.BgpSessionManager;
34 import org.onlab.onos.sdnip.config.SdnIpConfigReader; 35 import org.onlab.onos.sdnip.config.SdnIpConfigReader;
35 import org.slf4j.Logger; 36 import org.slf4j.Logger;
...@@ -97,6 +98,11 @@ public class SdnIp implements SdnIpService { ...@@ -97,6 +98,11 @@ public class SdnIp implements SdnIpService {
97 } 98 }
98 99
99 @Override 100 @Override
101 + public Collection<BgpSession> getBgpSessions() {
102 + return bgpSessionManager.getBgpSessions();
103 + }
104 +
105 + @Override
100 public Collection<BgpRouteEntry> getBgpRoutes() { 106 public Collection<BgpRouteEntry> getBgpRoutes() {
101 return bgpSessionManager.getBgpRoutes(); 107 return bgpSessionManager.getBgpRoutes();
102 } 108 }
......
...@@ -18,12 +18,20 @@ package org.onlab.onos.sdnip; ...@@ -18,12 +18,20 @@ package org.onlab.onos.sdnip;
18 import java.util.Collection; 18 import java.util.Collection;
19 19
20 import org.onlab.onos.sdnip.bgp.BgpRouteEntry; 20 import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
21 +import org.onlab.onos.sdnip.bgp.BgpSession;
21 22
22 /** 23 /**
23 * Service interface exported by SDN-IP. 24 * Service interface exported by SDN-IP.
24 */ 25 */
25 public interface SdnIpService { 26 public interface SdnIpService {
26 /** 27 /**
28 + * Gets the BGP sessions.
29 + *
30 + * @return the BGP sessions
31 + */
32 + public Collection<BgpSession> getBgpSessions();
33 +
34 + /**
27 * Gets the BGP routes. 35 * Gets the BGP routes.
28 * 36 *
29 * @return the BGP routes 37 * @return the BGP routes
......
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 +package org.onlab.onos.sdnip.cli;
17 +
18 +import java.util.Collection;
19 +
20 +import com.fasterxml.jackson.databind.JsonNode;
21 +import com.fasterxml.jackson.databind.ObjectMapper;
22 +import com.fasterxml.jackson.databind.node.ArrayNode;
23 +import com.fasterxml.jackson.databind.node.ObjectNode;
24 +import org.apache.karaf.shell.commands.Command;
25 +import org.apache.karaf.shell.commands.Option;
26 +import org.onlab.onos.cli.AbstractShellCommand;
27 +import org.onlab.onos.sdnip.SdnIpService;
28 +import org.onlab.onos.sdnip.bgp.BgpSession;
29 +
30 +/**
31 + * Command to show the BGP neighbors.
32 + */
33 +@Command(scope = "onos", name = "bgp-neighbors",
34 + description = "Lists the BGP neighbors")
35 +public class BgpNeighborsListCommand extends AbstractShellCommand {
36 + @Option(name = "-n", aliases = "--neighbor",
37 + description = "BGP neighbor to display information about",
38 + required = false, multiValued = false)
39 + private String bgpNeighbor;
40 +
41 + private static final String FORMAT_NEIGHBOR_LINE1 =
42 + "BGP neighbor is %s, remote AS %d, local AS %d";
43 + private static final String FORMAT_NEIGHBOR_LINE2 =
44 + " Remote router ID %s, IP %s, BGP version %d, Hold time %d";
45 + private static final String FORMAT_NEIGHBOR_LINE3 =
46 + " Local router ID %s, IP %s, BGP version %d, Hold time %d";
47 +
48 + @Override
49 + protected void execute() {
50 + SdnIpService service = get(SdnIpService.class);
51 + Collection<BgpSession> bgpSessions = service.getBgpSessions();
52 +
53 + if (bgpNeighbor != null) {
54 + // Print a single neighbor (if found)
55 + BgpSession foundBgpSession = null;
56 + for (BgpSession bgpSession : bgpSessions) {
57 + if (bgpSession.getRemoteBgpId().toString().equals(bgpNeighbor)) {
58 + foundBgpSession = bgpSession;
59 + break;
60 + }
61 + }
62 + if (foundBgpSession != null) {
63 + printNeighbor(foundBgpSession);
64 + } else {
65 + print("BGP neighbor %s not found", bgpNeighbor);
66 + }
67 + return;
68 + }
69 +
70 + // Print all neighbors
71 + printNeighbors(bgpSessions);
72 + }
73 +
74 + /**
75 + * Prints all BGP neighbors.
76 + *
77 + * @param bgpSessions the BGP sessions for the neighbors to print
78 + */
79 + private void printNeighbors(Collection<BgpSession> bgpSessions) {
80 + if (outputJson()) {
81 + print("%s", json(bgpSessions));
82 + } else {
83 + for (BgpSession bgpSession : bgpSessions) {
84 + printNeighbor(bgpSession);
85 + }
86 + }
87 + }
88 +
89 + /**
90 + * Prints a BGP neighbor.
91 + *
92 + * @param bgpSession the BGP session for the neighbor to print
93 + */
94 + private void printNeighbor(BgpSession bgpSession) {
95 + print(FORMAT_NEIGHBOR_LINE1,
96 + bgpSession.getRemoteBgpId().toString(),
97 + bgpSession.getRemoteAs(),
98 + bgpSession.getLocalAs());
99 + print(FORMAT_NEIGHBOR_LINE2,
100 + bgpSession.getRemoteBgpId().toString(),
101 + bgpSession.getRemoteAddress().toString(),
102 + bgpSession.getRemoteBgpVersion(),
103 + bgpSession.getRemoteHoldtime());
104 + print(FORMAT_NEIGHBOR_LINE3,
105 + bgpSession.getLocalBgpId().toString(),
106 + bgpSession.getLocalAddress().toString(),
107 + bgpSession.getLocalBgpVersion(),
108 + bgpSession.getLocalHoldtime());
109 + }
110 +
111 + /**
112 + * Produces a JSON array of BGP neighbors.
113 + *
114 + * @param bgpSessions the BGP sessions with the data
115 + * @return JSON array with the neighbors
116 + */
117 + private JsonNode json(Collection<BgpSession> bgpSessions) {
118 + ObjectMapper mapper = new ObjectMapper();
119 + ArrayNode result = mapper.createArrayNode();
120 +
121 + for (BgpSession bgpSession : bgpSessions) {
122 + result.add(json(mapper, bgpSession));
123 + }
124 + return result;
125 + }
126 +
127 + /**
128 + * Produces JSON object for a BGP neighbor.
129 + *
130 + * @param mapper the JSON object mapper to use
131 + * @param bgpSession the BGP session with the data
132 + * @return JSON object for the route
133 + */
134 + private ObjectNode json(ObjectMapper mapper, BgpSession bgpSession) {
135 + ObjectNode result = mapper.createObjectNode();
136 +
137 + result.put("remoteAddress", bgpSession.getRemoteAddress().toString());
138 + result.put("remoteBgpVersion", bgpSession.getRemoteBgpVersion());
139 + result.put("remoteAs", bgpSession.getRemoteAs());
140 + result.put("remoteHoldtime", bgpSession.getRemoteHoldtime());
141 + result.put("remoteBgpId", bgpSession.getRemoteBgpId().toString());
142 + //
143 + result.put("localAddress", bgpSession.getLocalAddress().toString());
144 + result.put("localBgpVersion", bgpSession.getLocalBgpVersion());
145 + result.put("localAs", bgpSession.getLocalAs());
146 + result.put("localHoldtime", bgpSession.getLocalHoldtime());
147 + result.put("localBgpId", bgpSession.getLocalBgpId().toString());
148 +
149 + return result;
150 + }
151 +}
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
17 17
18 <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> 18 <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
19 <command> 19 <command>
20 + <action class="org.onlab.onos.sdnip.cli.BgpNeighborsListCommand"/>
21 + </command>
22 + <command>
20 <action class="org.onlab.onos.sdnip.cli.BgpRoutesListCommand"/> 23 <action class="org.onlab.onos.sdnip.cli.BgpRoutesListCommand"/>
21 </command> 24 </command>
22 <command> 25 <command>
......
...@@ -61,8 +61,8 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -61,8 +61,8 @@ public class DefaultTopologyProvider extends AbstractProvider
61 61
62 // TODO: make these configurable 62 // TODO: make these configurable
63 private static final int MAX_EVENTS = 100; 63 private static final int MAX_EVENTS = 100;
64 - private static final int MAX_IDLE_MS = 50; 64 + private static final int MAX_IDLE_MS = 5;
65 - private static final int MAX_BATCH_MS = 200; 65 + private static final int MAX_BATCH_MS = 50;
66 private static final int MAX_THREADS = 8; 66 private static final int MAX_THREADS = 8;
67 67
68 // FIXME: Replace with a system-wide timer instance; 68 // FIXME: Replace with a system-wide timer instance;
......
...@@ -34,10 +34,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -34,10 +34,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
34 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 34 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
35 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 35 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
36 import org.onlab.onos.store.cluster.messaging.MessageSubject; 36 import org.onlab.onos.store.cluster.messaging.MessageSubject;
37 -import org.onlab.onos.store.serializers.ClusterMessageSerializer;
38 import org.onlab.onos.store.serializers.KryoNamespaces; 37 import org.onlab.onos.store.serializers.KryoNamespaces;
39 import org.onlab.onos.store.serializers.KryoSerializer; 38 import org.onlab.onos.store.serializers.KryoSerializer;
40 -import org.onlab.onos.store.serializers.MessageSubjectSerializer; 39 +import org.onlab.onos.store.serializers.impl.ClusterMessageSerializer;
40 +import org.onlab.onos.store.serializers.impl.MessageSubjectSerializer;
41 import org.onlab.util.KryoNamespace; 41 import org.onlab.util.KryoNamespace;
42 import org.onlab.netty.Endpoint; 42 import org.onlab.netty.Endpoint;
43 import org.onlab.netty.Message; 43 import org.onlab.netty.Message;
......
...@@ -59,7 +59,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; ...@@ -59,7 +59,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
59 import org.onlab.onos.store.cluster.messaging.MessageSubject; 59 import org.onlab.onos.store.cluster.messaging.MessageSubject;
60 import org.onlab.onos.store.impl.Timestamped; 60 import org.onlab.onos.store.impl.Timestamped;
61 import org.onlab.onos.store.serializers.KryoSerializer; 61 import org.onlab.onos.store.serializers.KryoSerializer;
62 -import org.onlab.onos.store.serializers.DistributedStoreSerializers; 62 +import org.onlab.onos.store.serializers.impl.DistributedStoreSerializers;
63 import org.onlab.packet.ChassisId; 63 import org.onlab.packet.ChassisId;
64 import org.onlab.util.KryoNamespace; 64 import org.onlab.util.KryoNamespace;
65 import org.onlab.util.NewConcurrentHashMap; 65 import org.onlab.util.NewConcurrentHashMap;
...@@ -230,9 +230,10 @@ public class GossipDeviceStore ...@@ -230,9 +230,10 @@ public class GossipDeviceStore
230 final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp); 230 final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
231 final DeviceEvent event; 231 final DeviceEvent event;
232 final Timestamped<DeviceDescription> mergedDesc; 232 final Timestamped<DeviceDescription> mergedDesc;
233 - synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) { 233 + final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId);
234 + synchronized (device) {
234 event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc); 235 event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
235 - mergedDesc = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId).getDeviceDesc(); 236 + mergedDesc = device.get(providerId).getDeviceDesc();
236 } 237 }
237 if (event != null) { 238 if (event != null) {
238 log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}", 239 log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}",
...@@ -252,10 +253,10 @@ public class GossipDeviceStore ...@@ -252,10 +253,10 @@ public class GossipDeviceStore
252 Timestamped<DeviceDescription> deltaDesc) { 253 Timestamped<DeviceDescription> deltaDesc) {
253 254
254 // Collection of DeviceDescriptions for a Device 255 // Collection of DeviceDescriptions for a Device
255 - Map<ProviderId, DeviceDescriptions> providerDescs 256 + Map<ProviderId, DeviceDescriptions> device
256 = getOrCreateDeviceDescriptionsMap(deviceId); 257 = getOrCreateDeviceDescriptionsMap(deviceId);
257 258
258 - synchronized (providerDescs) { 259 + synchronized (device) {
259 // locking per device 260 // locking per device
260 261
261 if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) { 262 if (isDeviceRemoved(deviceId, deltaDesc.timestamp())) {
...@@ -263,7 +264,7 @@ public class GossipDeviceStore ...@@ -263,7 +264,7 @@ public class GossipDeviceStore
263 return null; 264 return null;
264 } 265 }
265 266
266 - DeviceDescriptions descs = getOrCreateProviderDeviceDescriptions(providerDescs, providerId, deltaDesc); 267 + DeviceDescriptions descs = getOrCreateProviderDeviceDescriptions(device, providerId, deltaDesc);
267 268
268 final Device oldDevice = devices.get(deviceId); 269 final Device oldDevice = devices.get(deviceId);
269 final Device newDevice; 270 final Device newDevice;
...@@ -272,7 +273,7 @@ public class GossipDeviceStore ...@@ -272,7 +273,7 @@ public class GossipDeviceStore
272 deltaDesc.isNewer(descs.getDeviceDesc())) { 273 deltaDesc.isNewer(descs.getDeviceDesc())) {
273 // on new device or valid update 274 // on new device or valid update
274 descs.putDeviceDesc(deltaDesc); 275 descs.putDeviceDesc(deltaDesc);
275 - newDevice = composeDevice(deviceId, providerDescs); 276 + newDevice = composeDevice(deviceId, device);
276 } else { 277 } else {
277 // outdated event, ignored. 278 // outdated event, ignored.
278 return null; 279 return null;
...@@ -444,9 +445,10 @@ public class GossipDeviceStore ...@@ -444,9 +445,10 @@ public class GossipDeviceStore
444 final List<DeviceEvent> events; 445 final List<DeviceEvent> events;
445 final Timestamped<List<PortDescription>> merged; 446 final Timestamped<List<PortDescription>> merged;
446 447
447 - synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) { 448 + final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId);
449 + synchronized (device) {
448 events = updatePortsInternal(providerId, deviceId, timestampedInput); 450 events = updatePortsInternal(providerId, deviceId, timestampedInput);
449 - final DeviceDescriptions descs = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId); 451 + final DeviceDescriptions descs = device.get(providerId);
450 List<PortDescription> mergedList = 452 List<PortDescription> mergedList =
451 FluentIterable.from(portDescriptions) 453 FluentIterable.from(portDescriptions)
452 .transform(new Function<PortDescription, PortDescription>() { 454 .transform(new Function<PortDescription, PortDescription>() {
...@@ -632,9 +634,10 @@ public class GossipDeviceStore ...@@ -632,9 +634,10 @@ public class GossipDeviceStore
632 = new Timestamped<>(portDescription, newTimestamp); 634 = new Timestamped<>(portDescription, newTimestamp);
633 final DeviceEvent event; 635 final DeviceEvent event;
634 final Timestamped<PortDescription> mergedDesc; 636 final Timestamped<PortDescription> mergedDesc;
635 - synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) { 637 + final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId);
638 + synchronized (device) {
636 event = updatePortStatusInternal(providerId, deviceId, deltaDesc); 639 event = updatePortStatusInternal(providerId, deviceId, deltaDesc);
637 - mergedDesc = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId) 640 + mergedDesc = device.get(providerId)
638 .getPortDesc(portDescription.portNumber()); 641 .getPortDesc(portDescription.portNumber());
639 } 642 }
640 if (event != null) { 643 if (event != null) {
......
...@@ -75,9 +75,9 @@ import org.onlab.onos.store.flow.ReplicaInfoService; ...@@ -75,9 +75,9 @@ import org.onlab.onos.store.flow.ReplicaInfoService;
75 import org.onlab.onos.store.hz.AbstractHazelcastStore; 75 import org.onlab.onos.store.hz.AbstractHazelcastStore;
76 import org.onlab.onos.store.hz.SMap; 76 import org.onlab.onos.store.hz.SMap;
77 import org.onlab.onos.store.serializers.DecodeTo; 77 import org.onlab.onos.store.serializers.DecodeTo;
78 -import org.onlab.onos.store.serializers.DistributedStoreSerializers;
79 import org.onlab.onos.store.serializers.KryoSerializer; 78 import org.onlab.onos.store.serializers.KryoSerializer;
80 import org.onlab.onos.store.serializers.StoreSerializer; 79 import org.onlab.onos.store.serializers.StoreSerializer;
80 +import org.onlab.onos.store.serializers.impl.DistributedStoreSerializers;
81 import org.onlab.util.KryoNamespace; 81 import org.onlab.util.KryoNamespace;
82 import org.slf4j.Logger; 82 import org.slf4j.Logger;
83 83
......
...@@ -67,8 +67,8 @@ import org.onlab.onos.store.cluster.messaging.ClusterMessage; ...@@ -67,8 +67,8 @@ import org.onlab.onos.store.cluster.messaging.ClusterMessage;
67 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 67 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
68 import org.onlab.onos.store.cluster.messaging.MessageSubject; 68 import org.onlab.onos.store.cluster.messaging.MessageSubject;
69 import org.onlab.onos.store.impl.Timestamped; 69 import org.onlab.onos.store.impl.Timestamped;
70 -import org.onlab.onos.store.serializers.DistributedStoreSerializers;
71 import org.onlab.onos.store.serializers.KryoSerializer; 70 import org.onlab.onos.store.serializers.KryoSerializer;
71 +import org.onlab.onos.store.serializers.impl.DistributedStoreSerializers;
72 import org.onlab.packet.IpAddress; 72 import org.onlab.packet.IpAddress;
73 import org.onlab.packet.MacAddress; 73 import org.onlab.packet.MacAddress;
74 import org.onlab.packet.VlanId; 74 import org.onlab.packet.VlanId;
......
...@@ -55,8 +55,8 @@ import org.onlab.onos.store.cluster.messaging.ClusterMessage; ...@@ -55,8 +55,8 @@ import org.onlab.onos.store.cluster.messaging.ClusterMessage;
55 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 55 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
56 import org.onlab.onos.store.cluster.messaging.MessageSubject; 56 import org.onlab.onos.store.cluster.messaging.MessageSubject;
57 import org.onlab.onos.store.impl.Timestamped; 57 import org.onlab.onos.store.impl.Timestamped;
58 -import org.onlab.onos.store.serializers.DistributedStoreSerializers;
59 import org.onlab.onos.store.serializers.KryoSerializer; 58 import org.onlab.onos.store.serializers.KryoSerializer;
59 +import org.onlab.onos.store.serializers.impl.DistributedStoreSerializers;
60 import org.onlab.util.KryoNamespace; 60 import org.onlab.util.KryoNamespace;
61 import org.slf4j.Logger; 61 import org.slf4j.Logger;
62 62
......
...@@ -73,7 +73,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -73,7 +73,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
73 * @param link the target link 73 * @param link the target link
74 * @return free resources 74 * @return free resources
75 */ 75 */
76 - private Set<ResourceAllocation> readOriginalFreeResources(Link link) { 76 + private synchronized Set<ResourceAllocation> readOriginalFreeResources(Link link) {
77 // TODO read capacity and lambda resources from topology 77 // TODO read capacity and lambda resources from topology
78 Set<ResourceAllocation> allocations = new HashSet<>(); 78 Set<ResourceAllocation> allocations = new HashSet<>();
79 for (int i = 1; i <= 100; i++) { 79 for (int i = 1; i <= 100; i++) {
...@@ -92,7 +92,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -92,7 +92,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
92 * {@link org.onlab.onos.net.resource.BandwidthResourceAllocation} object with 0 bandwidth 92 * {@link org.onlab.onos.net.resource.BandwidthResourceAllocation} object with 0 bandwidth
93 * 93 *
94 */ 94 */
95 - private BandwidthResourceAllocation getBandwidth(Set<ResourceAllocation> freeRes) { 95 + private synchronized BandwidthResourceAllocation getBandwidth(Set<ResourceAllocation> freeRes) {
96 for (ResourceAllocation res : freeRes) { 96 for (ResourceAllocation res : freeRes) {
97 if (res.type() == ResourceType.BANDWIDTH) { 97 if (res.type() == ResourceType.BANDWIDTH) {
98 return (BandwidthResourceAllocation) res; 98 return (BandwidthResourceAllocation) res;
...@@ -107,7 +107,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -107,7 +107,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
107 * @param link the target link 107 * @param link the target link
108 * @param allocations the resources to be subtracted 108 * @param allocations the resources to be subtracted
109 */ 109 */
110 - private void subtractFreeResources(Link link, LinkResourceAllocations allocations) { 110 + private synchronized void subtractFreeResources(Link link, LinkResourceAllocations allocations) {
111 // TODO Use lock or version for updating freeResources. 111 // TODO Use lock or version for updating freeResources.
112 checkNotNull(link); 112 checkNotNull(link);
113 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link)); 113 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link));
...@@ -141,7 +141,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -141,7 +141,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
141 * @param link the target link 141 * @param link the target link
142 * @param allocations the resources to be added 142 * @param allocations the resources to be added
143 */ 143 */
144 - private void addFreeResources(Link link, LinkResourceAllocations allocations) { 144 + private synchronized void addFreeResources(Link link, LinkResourceAllocations allocations) {
145 // TODO Use lock or version for updating freeResources. 145 // TODO Use lock or version for updating freeResources.
146 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link)); 146 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link));
147 Set<ResourceAllocation> addRes = allocations.getResourceAllocation(link); 147 Set<ResourceAllocation> addRes = allocations.getResourceAllocation(link);
...@@ -167,7 +167,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -167,7 +167,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
167 } 167 }
168 168
169 @Override 169 @Override
170 - public Set<ResourceAllocation> getFreeResources(Link link) { 170 + public synchronized Set<ResourceAllocation> getFreeResources(Link link) {
171 checkNotNull(link); 171 checkNotNull(link);
172 Set<ResourceAllocation> freeRes = freeResources.get(link); 172 Set<ResourceAllocation> freeRes = freeResources.get(link);
173 if (freeRes == null) { 173 if (freeRes == null) {
...@@ -178,7 +178,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -178,7 +178,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
178 } 178 }
179 179
180 @Override 180 @Override
181 - public void allocateResources(LinkResourceAllocations allocations) { 181 + public synchronized void allocateResources(LinkResourceAllocations allocations) {
182 checkNotNull(allocations); 182 checkNotNull(allocations);
183 linkResourceAllocationsMap.put(allocations.intendId(), allocations); 183 linkResourceAllocationsMap.put(allocations.intendId(), allocations);
184 for (Link link : allocations.links()) { 184 for (Link link : allocations.links()) {
...@@ -193,7 +193,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -193,7 +193,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
193 } 193 }
194 194
195 @Override 195 @Override
196 - public void releaseResources(LinkResourceAllocations allocations) { 196 + public synchronized void releaseResources(LinkResourceAllocations allocations) {
197 checkNotNull(allocations); 197 checkNotNull(allocations);
198 linkResourceAllocationsMap.remove(allocations.intendId()); 198 linkResourceAllocationsMap.remove(allocations.intendId());
199 for (Link link : allocations.links()) { 199 for (Link link : allocations.links()) {
...@@ -209,13 +209,13 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -209,13 +209,13 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
209 } 209 }
210 210
211 @Override 211 @Override
212 - public LinkResourceAllocations getAllocations(IntentId intentId) { 212 + public synchronized LinkResourceAllocations getAllocations(IntentId intentId) {
213 checkNotNull(intentId); 213 checkNotNull(intentId);
214 return linkResourceAllocationsMap.get(intentId); 214 return linkResourceAllocationsMap.get(intentId);
215 } 215 }
216 216
217 @Override 217 @Override
218 - public Iterable<LinkResourceAllocations> getAllocations(Link link) { 218 + public synchronized Iterable<LinkResourceAllocations> getAllocations(Link link) {
219 checkNotNull(link); 219 checkNotNull(link);
220 Set<LinkResourceAllocations> result = allocatedResources.get(link); 220 Set<LinkResourceAllocations> result = allocatedResources.get(link);
221 if (result == null) { 221 if (result == null) {
...@@ -225,7 +225,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore { ...@@ -225,7 +225,7 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
225 } 225 }
226 226
227 @Override 227 @Override
228 - public Iterable<LinkResourceAllocations> getAllocations() { 228 + public synchronized Iterable<LinkResourceAllocations> getAllocations() {
229 return Collections.unmodifiableCollection(linkResourceAllocationsMap.values()); 229 return Collections.unmodifiableCollection(linkResourceAllocationsMap.values());
230 } 230 }
231 231
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onlab.onos.store.serializers; 16 +package org.onlab.onos.store.serializers.impl;
17 17
18 import org.onlab.onos.cluster.NodeId; 18 import org.onlab.onos.cluster.NodeId;
19 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 19 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
......
...@@ -13,11 +13,12 @@ ...@@ -13,11 +13,12 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onlab.onos.store.serializers; 16 +package org.onlab.onos.store.serializers.impl;
17 17
18 import org.onlab.onos.store.impl.MastershipBasedTimestamp; 18 import org.onlab.onos.store.impl.MastershipBasedTimestamp;
19 import org.onlab.onos.store.impl.Timestamped; 19 import org.onlab.onos.store.impl.Timestamped;
20 import org.onlab.onos.store.impl.WallClockTimestamp; 20 import org.onlab.onos.store.impl.WallClockTimestamp;
21 +import org.onlab.onos.store.serializers.KryoNamespaces;
21 import org.onlab.util.KryoNamespace; 22 import org.onlab.util.KryoNamespace;
22 23
23 public final class DistributedStoreSerializers { 24 public final class DistributedStoreSerializers {
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onlab.onos.store.serializers; 16 +package org.onlab.onos.store.serializers.impl;
17 17
18 import org.onlab.onos.store.impl.MastershipBasedTimestamp; 18 import org.onlab.onos.store.impl.MastershipBasedTimestamp;
19 19
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onlab.onos.store.serializers; 16 +package org.onlab.onos.store.serializers.impl;
17 17
18 import org.onlab.onos.store.cluster.messaging.MessageSubject; 18 import org.onlab.onos.store.cluster.messaging.MessageSubject;
19 19
......
...@@ -85,7 +85,7 @@ public class ClusterMessagingProtocolClient implements ProtocolClient { ...@@ -85,7 +85,7 @@ public class ClusterMessagingProtocolClient implements ProtocolClient {
85 return CompletableFuture.completedFuture(null); 85 return CompletableFuture.completedFuture(null);
86 } 86 }
87 87
88 - public <I> MessageSubject messageType(I input) { 88 + private <I> MessageSubject messageType(I input) {
89 Class<?> clazz = input.getClass(); 89 Class<?> clazz = input.getClass();
90 if (clazz.equals(PollRequest.class)) { 90 if (clazz.equals(PollRequest.class)) {
91 return ClusterMessagingProtocol.COPYCAT_POLL; 91 return ClusterMessagingProtocol.COPYCAT_POLL;
...@@ -117,7 +117,7 @@ public class ClusterMessagingProtocolClient implements ProtocolClient { ...@@ -117,7 +117,7 @@ public class ClusterMessagingProtocolClient implements ProtocolClient {
117 this.request = request; 117 this.request = request;
118 this.message = 118 this.message =
119 new ClusterMessage( 119 new ClusterMessage(
120 - null, 120 + null, // FIXME fill in proper sender
121 messageType(request), 121 messageType(request),
122 ClusterMessagingProtocol.SERIALIZER.encode(request)); 122 ClusterMessagingProtocol.SERIALIZER.encode(request));
123 this.future = future; 123 this.future = future;
...@@ -132,19 +132,20 @@ public class ClusterMessagingProtocolClient implements ProtocolClient { ...@@ -132,19 +132,20 @@ public class ClusterMessagingProtocolClient implements ProtocolClient {
132 future.complete(ClusterMessagingProtocol.SERIALIZER.decode(response)); 132 future.complete(ClusterMessagingProtocol.SERIALIZER.decode(response));
133 133
134 } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) { 134 } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
135 - if (message.subject().equals(ClusterMessagingProtocol.COPYCAT_SYNC) || 135 +// if (message.subject().equals(ClusterMessagingProtocol.COPYCAT_SYNC) ||
136 - message.subject().equals(ClusterMessagingProtocol.COPYCAT_PING)) { 136 +// message.subject().equals(ClusterMessagingProtocol.COPYCAT_PING)) {
137 - log.warn("{} Request to {} failed. Will retry in {} ms", 137 +// log.warn("{} Request to {} failed. Will retry in {} ms",
138 - message.subject(), remoteNode, RETRY_INTERVAL_MILLIS); 138 +// message.subject(), remoteNode, RETRY_INTERVAL_MILLIS);
139 - THREAD_POOL.schedule( 139 +// THREAD_POOL.schedule(
140 - this, 140 +// this,
141 - RETRY_INTERVAL_MILLIS, 141 +// RETRY_INTERVAL_MILLIS,
142 - TimeUnit.MILLISECONDS); 142 +// TimeUnit.MILLISECONDS);
143 - } else { 143 +// } else {
144 log.warn("RPCTask for {} failed.", request, e); 144 log.warn("RPCTask for {} failed.", request, e);
145 future.completeExceptionally(e); 145 future.completeExceptionally(e);
146 - } 146 +// }
147 } catch (Exception e) { 147 } catch (Exception e) {
148 + log.warn("RPCTask for {} terribly failed.", request, e);
148 future.completeExceptionally(e); 149 future.completeExceptionally(e);
149 } 150 }
150 } 151 }
......
...@@ -67,14 +67,36 @@ public class ClusterMessagingProtocolServer implements ProtocolServer { ...@@ -67,14 +67,36 @@ public class ClusterMessagingProtocolServer implements ProtocolServer {
67 @Override 67 @Override
68 public void handle(ClusterMessage message) { 68 public void handle(ClusterMessage message) {
69 T request = ClusterMessagingProtocol.SERIALIZER.decode(message.payload()); 69 T request = ClusterMessagingProtocol.SERIALIZER.decode(message.payload());
70 + if (handler == null) {
71 + // there is a slight window of time during state transition,
72 + // where handler becomes null
73 + for (int i = 0; i < 10; ++i) {
74 + if (handler != null) {
75 + break;
76 + }
77 + try {
78 + Thread.sleep(1);
79 + } catch (InterruptedException e) {
80 + log.trace("Exception", e);
81 + }
82 + }
83 + if (handler == null) {
84 + log.error("There was no handler for registered!");
85 + return;
86 + }
87 + }
70 if (request.getClass().equals(PingRequest.class)) { 88 if (request.getClass().equals(PingRequest.class)) {
71 - handler.ping((PingRequest) request).whenComplete(new PostExecutionTask<PingResponse>(message)); 89 + handler.ping((PingRequest) request)
90 + .whenComplete(new PostExecutionTask<PingResponse>(message));
72 } else if (request.getClass().equals(PollRequest.class)) { 91 } else if (request.getClass().equals(PollRequest.class)) {
73 - handler.poll((PollRequest) request).whenComplete(new PostExecutionTask<PollResponse>(message)); 92 + handler.poll((PollRequest) request)
93 + .whenComplete(new PostExecutionTask<PollResponse>(message));
74 } else if (request.getClass().equals(SyncRequest.class)) { 94 } else if (request.getClass().equals(SyncRequest.class)) {
75 - handler.sync((SyncRequest) request).whenComplete(new PostExecutionTask<SyncResponse>(message)); 95 + handler.sync((SyncRequest) request)
96 + .whenComplete(new PostExecutionTask<SyncResponse>(message));
76 } else if (request.getClass().equals(SubmitRequest.class)) { 97 } else if (request.getClass().equals(SubmitRequest.class)) {
77 - handler.submit((SubmitRequest) request).whenComplete(new PostExecutionTask<SubmitResponse>(message)); 98 + handler.submit((SubmitRequest) request)
99 + .whenComplete(new PostExecutionTask<SubmitResponse>(message));
78 } else { 100 } else {
79 throw new IllegalStateException("Unknown request type: " + request.getClass().getName()); 101 throw new IllegalStateException("Unknown request type: " + request.getClass().getName());
80 } 102 }
...@@ -94,6 +116,7 @@ public class ClusterMessagingProtocolServer implements ProtocolServer { ...@@ -94,6 +116,7 @@ public class ClusterMessagingProtocolServer implements ProtocolServer {
94 log.error("Processing for " + message.subject() + " failed.", t); 116 log.error("Processing for " + message.subject() + " failed.", t);
95 } else { 117 } else {
96 try { 118 try {
119 + log.trace("responding to {}", message.subject());
97 message.respond(ClusterMessagingProtocol.SERIALIZER.encode(response)); 120 message.respond(ClusterMessagingProtocol.SERIALIZER.encode(response));
98 } catch (Exception e) { 121 } catch (Exception e) {
99 log.error("Failed to respond to " + response.getClass().getName(), e); 122 log.error("Failed to respond to " + response.getClass().getName(), e);
......
...@@ -21,7 +21,7 @@ import java.nio.ByteBuffer; ...@@ -21,7 +21,7 @@ import java.nio.ByteBuffer;
21 21
22 import org.junit.Test; 22 import org.junit.Test;
23 import org.onlab.onos.store.Timestamp; 23 import org.onlab.onos.store.Timestamp;
24 -import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer; 24 +import org.onlab.onos.store.serializers.impl.MastershipBasedTimestampSerializer;
25 import org.onlab.util.KryoNamespace; 25 import org.onlab.util.KryoNamespace;
26 26
27 import com.google.common.testing.EqualsTester; 27 import com.google.common.testing.EqualsTester;
......
...@@ -73,7 +73,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -73,7 +73,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
73 * @param link the target link 73 * @param link the target link
74 * @return free resources 74 * @return free resources
75 */ 75 */
76 - private Set<ResourceAllocation> readOriginalFreeResources(Link link) { 76 + private synchronized Set<ResourceAllocation> readOriginalFreeResources(Link link) {
77 // TODO read capacity and lambda resources from topology 77 // TODO read capacity and lambda resources from topology
78 Set<ResourceAllocation> allocations = new HashSet<>(); 78 Set<ResourceAllocation> allocations = new HashSet<>();
79 for (int i = 1; i <= 100; i++) { 79 for (int i = 1; i <= 100; i++) {
...@@ -92,7 +92,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -92,7 +92,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
92 * {@link BandwidthResourceAllocation} object with 0 bandwidth 92 * {@link BandwidthResourceAllocation} object with 0 bandwidth
93 * 93 *
94 */ 94 */
95 - private BandwidthResourceAllocation getBandwidth(Set<ResourceAllocation> freeRes) { 95 + private synchronized BandwidthResourceAllocation getBandwidth(Set<ResourceAllocation> freeRes) {
96 for (ResourceAllocation res : freeRes) { 96 for (ResourceAllocation res : freeRes) {
97 if (res.type() == ResourceType.BANDWIDTH) { 97 if (res.type() == ResourceType.BANDWIDTH) {
98 return (BandwidthResourceAllocation) res; 98 return (BandwidthResourceAllocation) res;
...@@ -107,7 +107,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -107,7 +107,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
107 * @param link the target link 107 * @param link the target link
108 * @param allocations the resources to be subtracted 108 * @param allocations the resources to be subtracted
109 */ 109 */
110 - private void subtractFreeResources(Link link, LinkResourceAllocations allocations) { 110 + private synchronized void subtractFreeResources(Link link, LinkResourceAllocations allocations) {
111 // TODO Use lock or version for updating freeResources. 111 // TODO Use lock or version for updating freeResources.
112 checkNotNull(link); 112 checkNotNull(link);
113 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link)); 113 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link));
...@@ -141,7 +141,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -141,7 +141,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
141 * @param link the target link 141 * @param link the target link
142 * @param allocations the resources to be added 142 * @param allocations the resources to be added
143 */ 143 */
144 - private void addFreeResources(Link link, LinkResourceAllocations allocations) { 144 + private synchronized void addFreeResources(Link link, LinkResourceAllocations allocations) {
145 // TODO Use lock or version for updating freeResources. 145 // TODO Use lock or version for updating freeResources.
146 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link)); 146 Set<ResourceAllocation> freeRes = new HashSet<>(getFreeResources(link));
147 Set<ResourceAllocation> addRes = allocations.getResourceAllocation(link); 147 Set<ResourceAllocation> addRes = allocations.getResourceAllocation(link);
...@@ -167,7 +167,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -167,7 +167,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
167 } 167 }
168 168
169 @Override 169 @Override
170 - public Set<ResourceAllocation> getFreeResources(Link link) { 170 + public synchronized Set<ResourceAllocation> getFreeResources(Link link) {
171 checkNotNull(link); 171 checkNotNull(link);
172 Set<ResourceAllocation> freeRes = freeResources.get(link); 172 Set<ResourceAllocation> freeRes = freeResources.get(link);
173 if (freeRes == null) { 173 if (freeRes == null) {
...@@ -178,7 +178,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -178,7 +178,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
178 } 178 }
179 179
180 @Override 180 @Override
181 - public void allocateResources(LinkResourceAllocations allocations) { 181 + public synchronized void allocateResources(LinkResourceAllocations allocations) {
182 checkNotNull(allocations); 182 checkNotNull(allocations);
183 linkResourceAllocationsMap.put(allocations.intendId(), allocations); 183 linkResourceAllocationsMap.put(allocations.intendId(), allocations);
184 for (Link link : allocations.links()) { 184 for (Link link : allocations.links()) {
...@@ -193,7 +193,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -193,7 +193,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
193 } 193 }
194 194
195 @Override 195 @Override
196 - public void releaseResources(LinkResourceAllocations allocations) { 196 + public synchronized void releaseResources(LinkResourceAllocations allocations) {
197 checkNotNull(allocations); 197 checkNotNull(allocations);
198 linkResourceAllocationsMap.remove(allocations.intendId()); 198 linkResourceAllocationsMap.remove(allocations.intendId());
199 for (Link link : allocations.links()) { 199 for (Link link : allocations.links()) {
...@@ -209,13 +209,13 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -209,13 +209,13 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
209 } 209 }
210 210
211 @Override 211 @Override
212 - public LinkResourceAllocations getAllocations(IntentId intentId) { 212 + public synchronized LinkResourceAllocations getAllocations(IntentId intentId) {
213 checkNotNull(intentId); 213 checkNotNull(intentId);
214 return linkResourceAllocationsMap.get(intentId); 214 return linkResourceAllocationsMap.get(intentId);
215 } 215 }
216 216
217 @Override 217 @Override
218 - public Iterable<LinkResourceAllocations> getAllocations(Link link) { 218 + public synchronized Iterable<LinkResourceAllocations> getAllocations(Link link) {
219 checkNotNull(link); 219 checkNotNull(link);
220 Set<LinkResourceAllocations> result = allocatedResources.get(link); 220 Set<LinkResourceAllocations> result = allocatedResources.get(link);
221 if (result == null) { 221 if (result == null) {
...@@ -225,7 +225,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore { ...@@ -225,7 +225,7 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
225 } 225 }
226 226
227 @Override 227 @Override
228 - public Iterable<LinkResourceAllocations> getAllocations() { 228 + public synchronized Iterable<LinkResourceAllocations> getAllocations() {
229 return Collections.unmodifiableCollection(linkResourceAllocationsMap.values()); 229 return Collections.unmodifiableCollection(linkResourceAllocationsMap.values());
230 } 230 }
231 231
......
...@@ -316,7 +316,11 @@ public class NettyMessagingService implements MessagingService { ...@@ -316,7 +316,11 @@ public class NettyMessagingService implements MessagingService {
316 return; 316 return;
317 } 317 }
318 MessageHandler handler = NettyMessagingService.this.getMessageHandler(type); 318 MessageHandler handler = NettyMessagingService.this.getMessageHandler(type);
319 + if (handler != null) {
319 handler.handle(message); 320 handler.handle(message);
321 + } else {
322 + log.debug("No handler registered for {}", type);
323 + }
320 } 324 }
321 325
322 @Override 326 @Override
......
...@@ -15,8 +15,20 @@ ...@@ -15,8 +15,20 @@
15 */ 15 */
16 package org.onlab.onos.gui; 16 package org.onlab.onos.gui;
17 17
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.ObjectMapper;
20 +import com.fasterxml.jackson.databind.node.ArrayNode;
21 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.eclipse.jetty.websocket.WebSocket; 22 import org.eclipse.jetty.websocket.WebSocket;
23 +import org.onlab.onos.event.Event;
24 +import org.onlab.onos.net.Annotations;
25 +import org.onlab.onos.net.Device;
26 +import org.onlab.onos.net.DeviceId;
27 +import org.onlab.onos.net.Link;
28 +import org.onlab.onos.net.Path;
29 +import org.onlab.onos.net.device.DeviceEvent;
19 import org.onlab.onos.net.device.DeviceService; 30 import org.onlab.onos.net.device.DeviceService;
31 +import org.onlab.onos.net.link.LinkEvent;
20 import org.onlab.onos.net.topology.Topology; 32 import org.onlab.onos.net.topology.Topology;
21 import org.onlab.onos.net.topology.TopologyEdge; 33 import org.onlab.onos.net.topology.TopologyEdge;
22 import org.onlab.onos.net.topology.TopologyEvent; 34 import org.onlab.onos.net.topology.TopologyEvent;
...@@ -27,6 +39,15 @@ import org.onlab.onos.net.topology.TopologyVertex; ...@@ -27,6 +39,15 @@ import org.onlab.onos.net.topology.TopologyVertex;
27 import org.onlab.osgi.ServiceDirectory; 39 import org.onlab.osgi.ServiceDirectory;
28 40
29 import java.io.IOException; 41 import java.io.IOException;
42 +import java.util.HashMap;
43 +import java.util.Map;
44 +import java.util.Set;
45 +
46 +import static org.onlab.onos.net.DeviceId.deviceId;
47 +import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
48 +import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
49 +import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
50 +import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
30 51
31 /** 52 /**
32 * Web socket capable of interacting with the GUI topology view. 53 * Web socket capable of interacting with the GUI topology view.
...@@ -37,8 +58,16 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -37,8 +58,16 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
37 private final TopologyService topologyService; 58 private final TopologyService topologyService;
38 private final DeviceService deviceService; 59 private final DeviceService deviceService;
39 60
61 + private final ObjectMapper mapper = new ObjectMapper();
62 +
40 private Connection connection; 63 private Connection connection;
41 64
65 + // TODO: extract into an external & durable state; good enough for now and demo
66 + private static Map<String, ObjectNode> metaUi = new HashMap<>();
67 +
68 + private static final String COMPACT = "%s/%s-%s/%s";
69 +
70 +
42 /** 71 /**
43 * Creates a new web-socket for serving data to GUI topology view. 72 * Creates a new web-socket for serving data to GUI topology view.
44 * 73 *
...@@ -58,22 +87,19 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -58,22 +87,19 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
58 if (topologyService != null && deviceService != null) { 87 if (topologyService != null && deviceService != null) {
59 topologyService.addListener(this); 88 topologyService.addListener(this);
60 89
61 - sendMessage("Yo!!!");
62 -
63 Topology topology = topologyService.currentTopology(); 90 Topology topology = topologyService.currentTopology();
64 TopologyGraph graph = topologyService.getGraph(topology); 91 TopologyGraph graph = topologyService.getGraph(topology);
65 for (TopologyVertex vertex : graph.getVertexes()) { 92 for (TopologyVertex vertex : graph.getVertexes()) {
66 - sendMessage(deviceService.getDevice(vertex.deviceId()).toString()); 93 + sendMessage(message(new DeviceEvent(DEVICE_ADDED,
94 + deviceService.getDevice(vertex.deviceId()))));
67 } 95 }
68 96
69 for (TopologyEdge edge : graph.getEdges()) { 97 for (TopologyEdge edge : graph.getEdges()) {
70 - sendMessage(edge.link().toString()); 98 + sendMessage(message(new LinkEvent(LINK_ADDED, edge.link())));
71 } 99 }
72 100
73 - sendMessage("That's what we're starting with...");
74 -
75 } else { 101 } else {
76 - sendMessage("No topology service!!!"); 102 + sendMessage(message("error", "No topology service!!!"));
77 } 103 }
78 } 104 }
79 105
...@@ -87,10 +113,57 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -87,10 +113,57 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
87 113
88 @Override 114 @Override
89 public void onMessage(String data) { 115 public void onMessage(String data) {
116 + try {
117 + ObjectNode event = (ObjectNode) mapper.reader().readTree(data);
118 + String type = event.path("event").asText("unknown");
119 + ObjectNode payload = (ObjectNode) event.path("payload");
120 +
121 + switch (type) {
122 + case "updateMeta":
123 + metaUi.put(payload.path("id").asText(), payload);
124 + break;
125 + case "requestPath":
126 + findPath(deviceId(payload.path("one").asText()),
127 + deviceId(payload.path("two").asText()));
128 + default:
129 + break;
130 + }
131 + } catch (IOException e) {
90 System.out.println("Received: " + data); 132 System.out.println("Received: " + data);
91 } 133 }
134 + }
135 +
136 + private void findPath(DeviceId one, DeviceId two) {
137 + Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
138 + one, two);
139 + if (!paths.isEmpty()) {
140 + ObjectNode payload = mapper.createObjectNode();
141 + ArrayNode links = mapper.createArrayNode();
142 +
143 + Path path = paths.iterator().next();
144 + for (Link link : path.links()) {
145 + links.add(compactLinkString(link));
146 + }
147 +
148 + payload.set("links", links);
149 + sendMessage(envelope("showPath", payload));
150 + }
151 + // TODO: when no path, send a message to the client
152 + }
153 +
154 + /**
155 + * Returns a compact string representing the given link.
156 + *
157 + * @param link infrastructure link
158 + * @return formatted link string
159 + */
160 + public static String compactLinkString(Link link) {
161 + return String.format(COMPACT, link.src().deviceId(), link.src().port(),
162 + link.dst().deviceId(), link.dst().port());
163 + }
92 164
93 - public void sendMessage(String data) { 165 +
166 + private void sendMessage(String data) {
94 try { 167 try {
95 connection.sendMessage(data); 168 connection.sendMessage(data);
96 } catch (IOException e) { 169 } catch (IOException e) {
...@@ -98,9 +171,84 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe ...@@ -98,9 +171,84 @@ public class TopologyWebSocket implements WebSocket.OnTextMessage, TopologyListe
98 } 171 }
99 } 172 }
100 173
174 + // Produces a link event message to the client.
175 + private String message(DeviceEvent event) {
176 + Device device = event.subject();
177 + ObjectNode payload = mapper.createObjectNode()
178 + .put("id", device.id().toString())
179 + .put("type", device.type().toString().toLowerCase())
180 + .put("online", deviceService.isAvailable(device.id()));
181 +
182 + // Generate labels: id, chassis id, no-label, optional-name
183 + ArrayNode labels = mapper.createArrayNode();
184 + labels.add(device.id().toString());
185 + labels.add(device.chassisId().toString());
186 + labels.add(" "); // compact no-label view
187 + labels.add(device.annotations().value("name"));
188 +
189 + // Add labels, props and stuff the payload into envelope.
190 + payload.set("labels", labels);
191 + payload.set("props", props(device.annotations()));
192 +
193 + ObjectNode meta = metaUi.get(device.id().toString());
194 + if (meta != null) {
195 + payload.set("metaUi", meta);
196 + }
197 +
198 + String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
199 + ((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
200 + return envelope(type, payload);
201 + }
202 +
203 + // Produces a link event message to the client.
204 + private String message(LinkEvent event) {
205 + Link link = event.subject();
206 + ObjectNode payload = mapper.createObjectNode()
207 + .put("type", link.type().toString().toLowerCase())
208 + .put("linkWidth", 2)
209 + .put("src", link.src().deviceId().toString())
210 + .put("srcPort", link.src().port().toString())
211 + .put("dst", link.dst().deviceId().toString())
212 + .put("dstPort", link.dst().port().toString());
213 + String type = (event.type() == LINK_ADDED) ? "addLink" :
214 + ((event.type() == LINK_REMOVED) ? "removeLink" : "removeLink");
215 + return envelope(type, payload);
216 + }
217 +
218 + // Produces JSON structure from annotations.
219 + private JsonNode props(Annotations annotations) {
220 + ObjectNode props = mapper.createObjectNode();
221 + for (String key : annotations.keys()) {
222 + props.put(key, annotations.value(key));
223 + }
224 + return props;
225 + }
226 +
227 + // Produces a log message event bound to the client.
228 + private String message(String severity, String message) {
229 + return envelope("message",
230 + mapper.createObjectNode()
231 + .put("severity", severity)
232 + .put("message", message));
233 + }
234 +
235 + // Puts the payload into an envelope and returns it.
236 + private String envelope(String type, ObjectNode payload) {
237 + ObjectNode event = mapper.createObjectNode();
238 + event.put("event", type);
239 + event.set("payload", payload);
240 + return event.toString();
241 + }
242 +
101 @Override 243 @Override
102 public void event(TopologyEvent event) { 244 public void event(TopologyEvent event) {
103 - sendMessage(event.toString()); 245 + for (Event reason : event.reasons()) {
246 + if (reason instanceof DeviceEvent) {
247 + sendMessage(message((DeviceEvent) reason));
248 + } else if (reason instanceof LinkEvent) {
249 + sendMessage(message((LinkEvent) reason));
250 + }
251 + }
104 } 252 }
105 } 253 }
106 254
......
...@@ -72,9 +72,9 @@ ...@@ -72,9 +72,9 @@
72 <!-- Initialize the UI...--> 72 <!-- Initialize the UI...-->
73 <script type="text/javascript"> 73 <script type="text/javascript">
74 var ONOS = $.onos({ 74 var ONOS = $.onos({
75 - comment: "configuration options", 75 + comment: 'configuration options',
76 + theme: 'light',
76 startVid: 'topo', 77 startVid: 'topo',
77 -// startVid: 'sampleKeys',
78 trace: false 78 trace: false
79 }); 79 });
80 </script> 80 </script>
......
1 +{
2 + "event": "addHostIntent",
3 + "sid": 1,
4 + "payload": {
5 + "one": "hostOne",
6 + "two": "hostTwo"
7 + }
8 +}
1 +{
2 + "event": "showPath",
3 + "sid": 1,
4 + "payload": {
5 + "intentId": "0x1234",
6 + "path": {
7 + "links": [ "1-2", "2-3" ],
8 + "traffic": false
9 + }
10 + }
11 +}
1 +{
2 + "event": "monitorIntent",
3 + "sid": 2,
4 + "payload": {
5 + "intentId": "0x1234"
6 + }
7 +}
1 +{
2 + "event": "showPath",
3 + "sid": 2,
4 + "payload": {
5 + "intentId": "0x1234",
6 + "path": {
7 + "links": [ "1-2", "2-3" ],
8 + "traffic": true,
9 + "srcLabel": "567 Mb",
10 + "dstLabel": "6 Mb"
11 + }
12 + }
13 +}
1 +{
2 + "event": "showPath",
3 + "sid": 2,
4 + "payload": {
5 + "intentId": "0x1234",
6 + "path": {
7 + "links": [ "1-2", "2-3" ],
8 + "traffic": true,
9 + "srcLabel": "967 Mb",
10 + "dstLabel": "65 Mb"
11 + }
12 + }
13 +}
1 +{
2 + "event": "showPath",
3 + "sid": 2,
4 + "payload": {
5 + "intentId": "0x1234",
6 + "path": {
7 + "links": [ "1-2", "2-3" ],
8 + "traffic": false
9 + }
10 + }
11 +}
1 +{
2 + "event": "cancelMonitorIntent",
3 + "sid": 3,
4 + "payload": {
5 + "intentId": "0x1234"
6 + }
7 +}
...@@ -23,8 +23,15 @@ ...@@ -23,8 +23,15 @@
23 #mast { 23 #mast {
24 height: 36px; 24 height: 36px;
25 padding: 4px; 25 padding: 4px;
26 - background-color: #bbb;
27 vertical-align: baseline; 26 vertical-align: baseline;
27 +}
28 +
29 +.light #mast {
30 + background-color: #bbb;
31 + box-shadow: 0px 2px 8px #777;
32 +}
33 +.dark #mast {
34 + background-color: #444;
28 box-shadow: 0px 2px 8px #777; 35 box-shadow: 0px 2px 8px #777;
29 } 36 }
30 37
...@@ -35,12 +42,18 @@ ...@@ -35,12 +42,18 @@
35 } 42 }
36 43
37 #mast span.title { 44 #mast span.title {
38 - color: #369;
39 font-size: 14pt; 45 font-size: 14pt;
40 font-style: italic; 46 font-style: italic;
41 vertical-align: 12px; 47 vertical-align: 12px;
42 } 48 }
43 49
50 +.light #mast span.title {
51 + color: #369;
52 +}
53 +.dark #mast span.title {
54 + color: #78a;
55 +}
56 +
44 #mast span.right { 57 #mast span.right {
45 padding-top: 8px; 58 padding-top: 8px;
46 padding-right: 16px; 59 padding-right: 16px;
...@@ -50,16 +63,31 @@ ...@@ -50,16 +63,31 @@
50 #mast span.radio { 63 #mast span.radio {
51 font-size: 10pt; 64 font-size: 10pt;
52 margin: 4px 2px; 65 margin: 4px 2px;
53 - border: 1px dotted #222;
54 padding: 1px 6px; 66 padding: 1px 6px;
55 - color: #eee;
56 cursor: pointer; 67 cursor: pointer;
57 } 68 }
58 69
70 +.light #mast span.radio {
71 + border: 1px dotted #222;
72 + color: #eee;
73 +}
74 +.dark #mast span.radio {
75 + border: 1px dotted #bbb;
76 + color: #888;
77 +}
78 +
59 #mast span.radio.active { 79 #mast span.radio.active {
80 + padding: 1px 6px;
81 + font-weight: bold;
82 +}
83 +
84 +.light #mast span.radio.active {
60 background-color: #bbb; 85 background-color: #bbb;
61 border: 1px solid #eee; 86 border: 1px solid #eee;
62 - padding: 1px 6px;
63 color: #666; 87 color: #666;
64 - font-weight: bold; 88 +}
89 +.dark #mast span.radio.active {
90 + background-color: #222;
91 + border: 1px solid #eee;
92 + color: #aaf;
65 } 93 }
......
...@@ -32,7 +32,7 @@ div.onosView.currentView { ...@@ -32,7 +32,7 @@ div.onosView.currentView {
32 display: block; 32 display: block;
33 } 33 }
34 34
35 -div#alerts { 35 +#alerts {
36 display: none; 36 display: none;
37 position: absolute; 37 position: absolute;
38 z-index: 2000; 38 z-index: 2000;
...@@ -45,21 +45,28 @@ div#alerts { ...@@ -45,21 +45,28 @@ div#alerts {
45 box-shadow: 4px 6px 12px #777; 45 box-shadow: 4px 6px 12px #777;
46 } 46 }
47 47
48 -div#alerts pre { 48 +#alerts pre {
49 margin: 0.2em 6px; 49 margin: 0.2em 6px;
50 } 50 }
51 51
52 -div#alerts span.close { 52 +#alerts span.close {
53 color: #6af; 53 color: #6af;
54 float: right; 54 float: right;
55 right: 2px; 55 right: 2px;
56 cursor: pointer; 56 cursor: pointer;
57 } 57 }
58 58
59 -div#alerts span.close:hover { 59 +#alerts span.close:hover {
60 color: #fff; 60 color: #fff;
61 } 61 }
62 62
63 +#alerts p.footnote {
64 + text-align: right;
65 + font-size: 8pt;
66 + margin: 8px 0 0 0;
67 + color: #66d;
68 +}
69 +
63 /* 70 /*
64 * ============================================================== 71 * ==============================================================
65 * END OF NEW ONOS.JS file 72 * END OF NEW ONOS.JS file
......
...@@ -37,23 +37,34 @@ ...@@ -37,23 +37,34 @@
37 37
38 var defaultOptions = { 38 var defaultOptions = {
39 trace: false, 39 trace: false,
40 + theme: 'light',
40 startVid: defaultVid 41 startVid: defaultVid
41 }; 42 };
42 43
43 // compute runtime settings 44 // compute runtime settings
44 var settings = $.extend({}, defaultOptions, options); 45 var settings = $.extend({}, defaultOptions, options);
45 46
47 + // set the selected theme
48 + d3.select('body').classed(settings.theme, true);
49 +
46 // internal state 50 // internal state
47 var views = {}, 51 var views = {},
48 current = { 52 current = {
49 view: null, 53 view: null,
50 - ctx: '' 54 + ctx: '',
55 + theme: settings.theme
51 }, 56 },
52 built = false, 57 built = false,
53 errorCount = 0, 58 errorCount = 0,
54 keyHandler = { 59 keyHandler = {
55 - fn: null, 60 + globalKeys: {},
56 - map: {} 61 + maskedKeys: {},
62 + viewKeys: {},
63 + viewFn: null
64 + },
65 + alerts = {
66 + open: false,
67 + count: 0
57 }; 68 };
58 69
59 // DOM elements etc. 70 // DOM elements etc.
...@@ -240,8 +251,8 @@ ...@@ -240,8 +251,8 @@
240 251
241 // detach radio buttons, key handlers, etc. 252 // detach radio buttons, key handlers, etc.
242 $('#mastRadio').children().detach(); 253 $('#mastRadio').children().detach();
243 - keyHandler.fn = null; 254 + keyHandler.viewKeys = {};
244 - keyHandler.map = {}; 255 + keyHandler.viewFn = null;
245 } 256 }
246 257
247 // cache new view and context 258 // cache new view and context
...@@ -322,20 +333,74 @@ ...@@ -322,20 +333,74 @@
322 $mastRadio.node().appendChild(btnG.node()); 333 $mastRadio.node().appendChild(btnG.node());
323 } 334 }
324 335
336 + function setupGlobalKeys() {
337 + keyHandler.globalKeys = {
338 + esc: escapeKey,
339 + T: toggleTheme
340 + };
341 + // Masked keys are global key handlers that always return true.
342 + // That is, the view will never see the event for that key.
343 + keyHandler.maskedKeys = {
344 + T: true
345 + };
346 + }
347 +
348 + function escapeKey(view, key, code, ev) {
349 + if (alerts.open) {
350 + closeAlerts();
351 + return true;
352 + }
353 + return false;
354 + }
355 +
356 + function toggleTheme(view, key, code, ev) {
357 + var body = d3.select('body');
358 + current.theme = (current.theme === 'light') ? 'dark' : 'light';
359 + body.classed('light dark', false);
360 + body.classed(current.theme, true);
361 + return true;
362 + }
363 +
325 function setKeyBindings(keyArg) { 364 function setKeyBindings(keyArg) {
365 + var viewKeys,
366 + masked = [];
367 +
326 if ($.isFunction(keyArg)) { 368 if ($.isFunction(keyArg)) {
327 // set general key handler callback 369 // set general key handler callback
328 - keyHandler.fn = keyArg; 370 + keyHandler.viewFn = keyArg;
329 } else { 371 } else {
330 // set specific key filter map 372 // set specific key filter map
331 - keyHandler.map = keyArg; 373 + viewKeys = d3.map(keyArg).keys();
374 + viewKeys.forEach(function (key) {
375 + if (keyHandler.maskedKeys[key]) {
376 + masked.push(' Key "' + key + '" is reserved');
377 + }
378 + });
379 +
380 + if (masked.length) {
381 + doAlert('WARNING...\n\nsetKeys():\n' + masked.join('\n'));
382 + }
383 + keyHandler.viewKeys = keyArg;
332 } 384 }
333 } 385 }
334 386
335 - var alerts = { 387 + function keyIn() {
336 - open: false, 388 + var event = d3.event,
337 - count: 0 389 + keyCode = event.keyCode,
338 - }; 390 + key = whatKey(keyCode),
391 + gcb = isF(keyHandler.globalKeys[key]),
392 + vcb = isF(keyHandler.viewKeys[key]) || isF(keyHandler.viewFn);
393 +
394 + // global callback?
395 + if (gcb && gcb(current.view.token(), key, keyCode, event)) {
396 + // if the event was 'handled', we are done
397 + return;
398 + }
399 + // otherwise, let the view callback have a shot
400 + if (vcb) {
401 + vcb(current.view.token(), key, keyCode, event);
402 + }
403 + }
339 404
340 function createAlerts() { 405 function createAlerts() {
341 var al = d3.select('#alerts') 406 var al = d3.select('#alerts')
...@@ -345,15 +410,16 @@ ...@@ -345,15 +410,16 @@
345 .text('X') 410 .text('X')
346 .on('click', closeAlerts); 411 .on('click', closeAlerts);
347 al.append('pre'); 412 al.append('pre');
413 + al.append('p').attr('class', 'footnote')
414 + .text('Press ESCAPE to close');
348 alerts.open = true; 415 alerts.open = true;
349 alerts.count = 0; 416 alerts.count = 0;
350 } 417 }
351 418
352 function closeAlerts() { 419 function closeAlerts() {
353 d3.select('#alerts') 420 d3.select('#alerts')
354 - .style('display', 'none'); 421 + .style('display', 'none')
355 - d3.select('#alerts span').remove(); 422 + .html('');
356 - d3.select('#alerts pre').remove();
357 alerts.open = false; 423 alerts.open = false;
358 } 424 }
359 425
...@@ -384,17 +450,6 @@ ...@@ -384,17 +450,6 @@
384 addAlert(msg); 450 addAlert(msg);
385 } 451 }
386 452
387 - function keyIn() {
388 - var event = d3.event,
389 - keyCode = event.keyCode,
390 - key = whatKey(keyCode),
391 - cb = isF(keyHandler.map[key]) || isF(keyHandler.fn);
392 -
393 - if (cb) {
394 - cb(current.view.token(), key, keyCode, event);
395 - }
396 - }
397 -
398 function resize(e) { 453 function resize(e) {
399 d3.selectAll('.onosView').call(setViewDimensions); 454 d3.selectAll('.onosView').call(setViewDimensions);
400 // allow current view to react to resize event... 455 // allow current view to react to resize event...
...@@ -683,6 +738,7 @@ ...@@ -683,6 +738,7 @@
683 $(window).on('resize', resize); 738 $(window).on('resize', resize);
684 739
685 d3.select('body').on('keydown', keyIn); 740 d3.select('body').on('keydown', keyIn);
741 + setupGlobalKeys();
686 742
687 // Invoke hashchange callback to navigate to content 743 // Invoke hashchange callback to navigate to content
688 // indicated by the window location hash. 744 // indicated by the window location hash.
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
28 28
29 // configuration data 29 // configuration data
30 var config = { 30 var config = {
31 - useLiveData: false, 31 + useLiveData: true,
32 debugOn: false, 32 debugOn: false,
33 debug: { 33 debug: {
34 showNodeXY: true, 34 showNodeXY: true,
...@@ -113,27 +113,31 @@ ...@@ -113,27 +113,31 @@
113 113
114 // key bindings 114 // key bindings
115 var keyDispatch = { 115 var keyDispatch = {
116 - space: injectTestEvent, // TODO: remove (testing only)
117 - S: injectStartupEvents, // TODO: remove (testing only)
118 - A: testAlert, // TODO: remove (testing only)
119 M: testMe, // TODO: remove (testing only) 116 M: testMe, // TODO: remove (testing only)
117 + S: injectStartupEvents, // TODO: remove (testing only)
118 + space: injectTestEvent, // TODO: remove (testing only)
120 119
121 - B: toggleBg, 120 + B: toggleBg, // TODO: do we really need this?
122 - G: toggleLayout,
123 L: cycleLabels, 121 L: cycleLabels,
124 P: togglePorts, 122 P: togglePorts,
125 - U: unpin 123 + U: unpin,
124 +
125 + X: requestPath
126 }; 126 };
127 127
128 // state variables 128 // state variables
129 var network = { 129 var network = {
130 + view: null, // view token reference
130 nodes: [], 131 nodes: [],
131 links: [], 132 links: [],
132 lookup: {} 133 lookup: {}
133 }, 134 },
134 webSock, 135 webSock,
135 labelIdx = 0, 136 labelIdx = 0,
136 - selected = {}, 137 +
138 + selectOrder = [],
139 + selections = {},
140 +
137 highlighted = null, 141 highlighted = null,
138 hovered = null, 142 hovered = null,
139 viewMode = 'showAll', 143 viewMode = 'showAll',
...@@ -167,19 +171,19 @@ ...@@ -167,19 +171,19 @@
167 // ============================== 171 // ==============================
168 // Key Callbacks 172 // Key Callbacks
169 173
170 - function testAlert(view) {
171 - alertNumber++;
172 - view.alert("Test me! -- " + alertNumber);
173 - }
174 -
175 function testMe(view) { 174 function testMe(view) {
175 + view.alert('test');
176 } 176 }
177 177
178 function injectTestEvent(view) { 178 function injectTestEvent(view) {
179 + if (config.useLiveData) {
180 + view.alert("Sorry, currently using live data..");
181 + return;
182 + }
183 +
179 eventNumber++; 184 eventNumber++;
180 var eventUrl = eventPrefix + eventNumber + '.json'; 185 var eventUrl = eventPrefix + eventNumber + '.json';
181 186
182 - console.log('Fetching JSON: ' + eventUrl);
183 d3.json(eventUrl, function(err, data) { 187 d3.json(eventUrl, function(err, data) {
184 if (err) { 188 if (err) {
185 view.dataLoadError(err, eventUrl); 189 view.dataLoadError(err, eventUrl);
...@@ -190,6 +194,11 @@ ...@@ -190,6 +194,11 @@
190 } 194 }
191 195
192 function injectStartupEvents(view) { 196 function injectStartupEvents(view) {
197 + if (config.useLiveData) {
198 + view.alert("Sorry, currently using live data..");
199 + return;
200 + }
201 +
193 var lastStartupEvent = 32; 202 var lastStartupEvent = 32;
194 while (eventNumber < lastStartupEvent) { 203 while (eventNumber < lastStartupEvent) {
195 injectTestEvent(view); 204 injectTestEvent(view);
...@@ -201,19 +210,20 @@ ...@@ -201,19 +210,20 @@
201 bgImg.style('visibility', (vis === 'hidden') ? 'visible' : 'hidden'); 210 bgImg.style('visibility', (vis === 'hidden') ? 'visible' : 'hidden');
202 } 211 }
203 212
204 - function toggleLayout(view) { 213 + function cycleLabels() {
214 + labelIdx = (labelIdx === network.deviceLabelCount - 1) ? 0 : labelIdx + 1;
205 215
216 + function niceLabel(label) {
217 + return (label && label.trim()) ? label : '.';
206 } 218 }
207 219
208 - function cycleLabels() {
209 - labelIdx = (labelIdx === network.deviceLabelCount - 1) ? 0 : labelIdx + 1;
210 network.nodes.forEach(function (d) { 220 network.nodes.forEach(function (d) {
211 var idx = (labelIdx < d.labels.length) ? labelIdx : 0, 221 var idx = (labelIdx < d.labels.length) ? labelIdx : 0,
212 node = d3.select('#' + safeId(d.id)), 222 node = d3.select('#' + safeId(d.id)),
213 box; 223 box;
214 224
215 node.select('text') 225 node.select('text')
216 - .text(d.labels[idx]) 226 + .text(niceLabel(d.labels[idx]))
217 .style('opacity', 0) 227 .style('opacity', 0)
218 .transition() 228 .transition()
219 .style('opacity', 1); 229 .style('opacity', 1);
...@@ -232,11 +242,19 @@ ...@@ -232,11 +242,19 @@
232 } 242 }
233 243
234 function togglePorts(view) { 244 function togglePorts(view) {
235 - 245 + view.alert('togglePorts() callback')
236 } 246 }
237 247
238 function unpin(view) { 248 function unpin(view) {
249 + view.alert('unpin() callback')
250 + }
239 251
252 + function requestPath(view) {
253 + var payload = {
254 + one: selections[selectOrder[0]].obj.id,
255 + two: selections[selectOrder[1]].obj.id
256 + }
257 + sendMessage('requestPath', payload);
240 } 258 }
241 259
242 // ============================== 260 // ==============================
...@@ -248,19 +266,19 @@ ...@@ -248,19 +266,19 @@
248 // d3.selectAll('svg .port').classed('inactive', false); 266 // d3.selectAll('svg .port').classed('inactive', false);
249 // d3.selectAll('svg .portText').classed('inactive', false); 267 // d3.selectAll('svg .portText').classed('inactive', false);
250 // TODO ... 268 // TODO ...
251 - console.log('showAllLayers()'); 269 + network.view.alert('showAllLayers() callback');
252 } 270 }
253 271
254 function showPacketLayer() { 272 function showPacketLayer() {
255 showAllLayers(); 273 showAllLayers();
256 // TODO ... 274 // TODO ...
257 - console.log('showPacketLayer()'); 275 + network.view.alert('showPacketLayer() callback');
258 } 276 }
259 277
260 function showOpticalLayer() { 278 function showOpticalLayer() {
261 showAllLayers(); 279 showAllLayers();
262 // TODO ... 280 // TODO ...
263 - console.log('showOpticalLayer()'); 281 + network.view.alert('showOpticalLayer() callback');
264 } 282 }
265 283
266 // ============================== 284 // ==============================
...@@ -279,11 +297,6 @@ ...@@ -279,11 +297,6 @@
279 }); 297 });
280 } 298 }
281 299
282 - function establishWebSocket() {
283 - // TODO: establish a real web-socket
284 - // NOTE, for now, we are using the 'Q' key to artificially inject
285 - // "events" from the server.
286 - }
287 300
288 // ============================== 301 // ==============================
289 // Event handlers for server-pushed events 302 // Event handlers for server-pushed events
...@@ -292,7 +305,8 @@ ...@@ -292,7 +305,8 @@
292 addDevice: addDevice, 305 addDevice: addDevice,
293 updateDevice: updateDevice, 306 updateDevice: updateDevice,
294 removeDevice: removeDevice, 307 removeDevice: removeDevice,
295 - addLink: addLink 308 + addLink: addLink,
309 + showPath: showPath
296 }; 310 };
297 311
298 function addDevice(data) { 312 function addDevice(data) {
...@@ -331,11 +345,14 @@ ...@@ -331,11 +345,14 @@
331 } 345 }
332 } 346 }
333 347
348 + function showPath(data) {
349 + network.view.alert(data.event + "\n" + data.payload.links.length);
350 + }
351 +
334 // .... 352 // ....
335 353
336 function unknownEvent(data) { 354 function unknownEvent(data) {
337 - // TODO: use dialog, not alert 355 + network.view.alert('Unknown event type: "' + data.event + '"');
338 - alert('Unknown event type: "' + data.event + '"');
339 } 356 }
340 357
341 function handleServerEvent(data) { 358 function handleServerEvent(data) {
...@@ -360,7 +377,9 @@ ...@@ -360,7 +377,9 @@
360 lnk; 377 lnk;
361 378
362 if (!(srcNode && dstNode)) { 379 if (!(srcNode && dstNode)) {
363 - alert('nodes not on map'); 380 + // TODO: send warning message back to server on websocket
381 + network.view.alert('nodes not on map for link\n\n' +
382 + 'src = ' + src + '\ndst = ' + dst);
364 return null; 383 return null;
365 } 384 }
366 385
...@@ -381,6 +400,7 @@ ...@@ -381,6 +400,7 @@
381 400
382 function linkWidth(w) { 401 function linkWidth(w) {
383 // w is number of links between nodes. Scale appropriately. 402 // w is number of links between nodes. Scale appropriately.
403 + // TODO: use a d3.scale (linear, log, ... ?)
384 return w * 1.2; 404 return w * 1.2;
385 } 405 }
386 406
...@@ -604,6 +624,7 @@ ...@@ -604,6 +624,7 @@
604 webSock.ws.onmessage = function(m) { 624 webSock.ws.onmessage = function(m) {
605 if (m.data) { 625 if (m.data) {
606 console.log(m.data); 626 console.log(m.data);
627 + handleServerEvent(JSON.parse(m.data));
607 } 628 }
608 }; 629 };
609 630
...@@ -613,7 +634,7 @@ ...@@ -613,7 +634,7 @@
613 }, 634 },
614 635
615 send : function(text) { 636 send : function(text) {
616 - if (text != null && text.length > 0) { 637 + if (text != null) {
617 webSock._send(text); 638 webSock._send(text);
618 } 639 }
619 }, 640 },
...@@ -621,11 +642,87 @@ ...@@ -621,11 +642,87 @@
621 _send : function(message) { 642 _send : function(message) {
622 if (webSock.ws) { 643 if (webSock.ws) {
623 webSock.ws.send(message); 644 webSock.ws.send(message);
645 + } else {
646 + network.view.alert('no web socket open');
624 } 647 }
625 } 648 }
626 649
627 }; 650 };
628 651
652 + var sid = 0;
653 +
654 + function sendMessage(evType, payload) {
655 + var toSend = {
656 + event: evType,
657 + sid: ++sid,
658 + payload: payload
659 + };
660 + webSock.send(JSON.stringify(toSend));
661 + }
662 +
663 +
664 + // ==============================
665 + // Selection stuff
666 +
667 + function selectObject(obj, el) {
668 + var n,
669 + meta = d3.event.sourceEvent.metaKey;
670 +
671 + if (el) {
672 + n = d3.select(el);
673 + } else {
674 + node.each(function(d) {
675 + if (d == obj) {
676 + n = d3.select(el = this);
677 + }
678 + });
679 + }
680 + if (!n) return;
681 +
682 + if (meta && n.classed('selected')) {
683 + deselectObject(obj.id);
684 + //flyinPane(null);
685 + return;
686 + }
687 +
688 + if (!meta) {
689 + deselectAll();
690 + }
691 +
692 + selections[obj.id] = { obj: obj, el : el};
693 + selectOrder.push(obj.id);
694 +
695 + n.classed('selected', true);
696 + //flyinPane(obj);
697 + }
698 +
699 + function deselectObject(id) {
700 + var obj = selections[id];
701 + if (obj) {
702 + d3.select(obj.el).classed('selected', false);
703 + selections[id] = null;
704 + // TODO: use splice to remove element
705 + }
706 + //flyinPane(null);
707 + }
708 +
709 + function deselectAll() {
710 + // deselect all nodes in the network...
711 + node.classed('selected', false);
712 + selections = {};
713 + selectOrder = [];
714 + //flyinPane(null);
715 + }
716 +
717 +
718 + $('#view').on('click', function(e) {
719 + if (!$(e.target).closest('.node').length) {
720 + if (!e.metaKey) {
721 + deselectAll();
722 + }
723 + }
724 + });
725 +
629 // ============================== 726 // ==============================
630 // View life-cycle callbacks 727 // View life-cycle callbacks
631 728
...@@ -680,7 +777,7 @@ ...@@ -680,7 +777,7 @@
680 } 777 }
681 778
682 function selectCb(d, self) { 779 function selectCb(d, self) {
683 - // TODO: selectObject(d, self); 780 + selectObject(d, self);
684 } 781 }
685 782
686 function atDragEnd(d, self) { 783 function atDragEnd(d, self) {
...@@ -688,10 +785,21 @@ ...@@ -688,10 +785,21 @@
688 // if it is a device (not a host) 785 // if it is a device (not a host)
689 if (d.class === 'device') { 786 if (d.class === 'device') {
690 d.fixed = true; 787 d.fixed = true;
691 - d3.select(self).classed('fixed', true) 788 + d3.select(self).classed('fixed', true);
692 - // TODO: send new [x,y] back to server, via websocket. 789 + if (config.useLiveData) {
790 + tellServerCoords(d);
693 } 791 }
694 } 792 }
793 + }
794 +
795 + function tellServerCoords(d) {
796 + sendMessage('updateMeta', {
797 + id: d.id,
798 + 'class': d.class,
799 + x: Math.floor(d.x),
800 + y: Math.floor(d.y)
801 + });
802 + }
695 803
696 // set up the force layout 804 // set up the force layout
697 network.force = d3.layout.force() 805 network.force = d3.layout.force()
...@@ -704,7 +812,6 @@ ...@@ -704,7 +812,6 @@
704 .on('tick', tick); 812 .on('tick', tick);
705 813
706 network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd); 814 network.drag = d3u.createDragBehavior(network.force, selectCb, atDragEnd);
707 - webSock.connect();
708 } 815 }
709 816
710 function load(view, ctx) { 817 function load(view, ctx) {
...@@ -715,7 +822,9 @@ ...@@ -715,7 +822,9 @@
715 view.setRadio(btnSet); 822 view.setRadio(btnSet);
716 view.setKeys(keyDispatch); 823 view.setKeys(keyDispatch);
717 824
718 - establishWebSocket(); 825 + if (config.useLiveData) {
826 + webSock.connect();
827 + }
719 } 828 }
720 829
721 function resize(view, ctx) { 830 function resize(view, ctx) {
......