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> | ... | ... |
| ... | @@ -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) { | ... | ... |
-
Please register or login to post a comment