Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
33 changed files
with
763 additions
and
145 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 | - handler.handle(message); | 319 | + if (handler != null) { |
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) { |
90 | - System.out.println("Received: " + 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) { | ||
132 | + System.out.println("Received: " + data); | ||
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()); | ||
91 | } | 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> | ... | ... |
... | @@ -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) { | ||
205 | - | ||
206 | - } | ||
207 | - | ||
208 | function cycleLabels() { | 213 | function cycleLabels() { |
209 | labelIdx = (labelIdx === network.deviceLabelCount - 1) ? 0 : labelIdx + 1; | 214 | labelIdx = (labelIdx === network.deviceLabelCount - 1) ? 0 : labelIdx + 1; |
215 | + | ||
216 | + function niceLabel(label) { | ||
217 | + return (label && label.trim()) ? label : '.'; | ||
218 | + } | ||
219 | + | ||
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,11 +785,22 @@ | ... | @@ -688,11 +785,22 @@ |
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); | ||
791 | + } | ||
693 | } | 792 | } |
694 | } | 793 | } |
695 | 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 | + } | ||
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() |
698 | .size(forceDim) | 806 | .size(forceDim) |
... | @@ -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) { | ... | ... |
-
Please register or login to post a comment