Simon Hunt
Committed by Gerrit Code Review

ONOS-3347 - HostMoved event now processed correctly.

- added new DefaultHashMap utility class
- updated TopoViewMsgHdlrBase for cleaner event translation.

Change-Id: I1c5e8c981e2d617366c25f497dc9336e09684a2e
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.util;
import java.util.HashMap;
/**
* HashMap that returns a default value for unmapped keys.
*/
public class DefaultHashMap<K, V> extends HashMap<K, V> {
/** Default value to return when no key binding exists. */
protected V defaultValue;
/**
* Constructs an empty map with the given default value.
*
* @param defaultValue the default value
*/
public DefaultHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
\ No newline at end of file
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Unit tests for {@link DefaultHashMap}.
*/
public class DefaultHashMapTest {
private static final String ONE = "one";
private static final String TWO = "two";
private static final String THREE = "three";
private static final String FOUR = "four";
private static final String ALPHA = "Alpha";
private static final String BETA = "Beta";
private static final String OMEGA = "Omega";
private DefaultHashMap<String, Integer> map;
private DefaultHashMap<String, String> chartis;
private void loadMap() {
map.put(ONE, 1);
map.put(TWO, 2);
}
private void fortioCharti() {
chartis.put(ONE, ALPHA);
chartis.put(TWO, BETA);
}
@Test
public void nullDefaultIsAllowed() {
// but makes this class behave no different than HashMap
map = new DefaultHashMap<>(null);
loadMap();
assertEquals("missing 1", 1, (int) map.get(ONE));
assertEquals("missing 2", 2, (int) map.get(TWO));
assertEquals("three?", null, map.get(THREE));
assertEquals("four?", null, map.get(FOUR));
}
@Test
public void defaultToFive() {
map = new DefaultHashMap<>(5);
loadMap();
assertEquals("missing 1", 1, (int) map.get(ONE));
assertEquals("missing 2", 2, (int) map.get(TWO));
assertEquals("three?", 5, (int) map.get(THREE));
assertEquals("four?", 5, (int) map.get(FOUR));
}
@Test
public void defaultToOmega() {
chartis = new DefaultHashMap<>(OMEGA);
fortioCharti();
assertEquals("missing 1", ALPHA, chartis.get(ONE));
assertEquals("missing 2", BETA, chartis.get(TWO));
assertEquals("three?", OMEGA, chartis.get(THREE));
assertEquals("four?", OMEGA, chartis.get(FOUR));
}
}
......@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.IpAddress;
import org.onlab.util.DefaultHashMap;
import org.onosproject.cluster.ClusterEvent;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
......@@ -80,17 +81,9 @@ import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
import static org.onosproject.ui.topo.TopoConstants.Properties;
import static org.onosproject.ui.topo.TopoUtils.compactLinkString;
......@@ -100,6 +93,33 @@ import static org.onosproject.ui.topo.TopoUtils.compactLinkString;
*/
public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// default to an "add" event...
private static final DefaultHashMap<ClusterEvent.Type, String> CLUSTER_EVENT =
new DefaultHashMap<>("addInstance");
// default to an "update" event...
private static final DefaultHashMap<DeviceEvent.Type, String> DEVICE_EVENT =
new DefaultHashMap<>("updateDevice");
private static final DefaultHashMap<LinkEvent.Type, String> LINK_EVENT =
new DefaultHashMap<>("updateLink");
private static final DefaultHashMap<HostEvent.Type, String> HOST_EVENT =
new DefaultHashMap<>("updateHost");
// but call out specific events that we care to differentiate...
static {
CLUSTER_EVENT.put(ClusterEvent.Type.INSTANCE_REMOVED, "removeInstance");
DEVICE_EVENT.put(DeviceEvent.Type.DEVICE_ADDED, "addDevice");
DEVICE_EVENT.put(DeviceEvent.Type.DEVICE_REMOVED, "removeDevice");
LINK_EVENT.put(LinkEvent.Type.LINK_ADDED, "addLink");
LINK_EVENT.put(LinkEvent.Type.LINK_REMOVED, "removeLink");
HOST_EVENT.put(HostEvent.Type.HOST_ADDED, "addHost");
HOST_EVENT.put(HostEvent.Type.HOST_REMOVED, "removeHost");
HOST_EVENT.put(HostEvent.Type.HOST_MOVED, "moveHost");
}
protected static final Logger log =
LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
......@@ -204,7 +224,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
}
// Produces a cluster instance message to the client.
protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
protected ObjectNode instanceMessage(ClusterEvent event, String msgType) {
ControllerNode node = event.subject();
int switchCount = mastershipService.getDevicesOf(node.id()).size();
ObjectNode payload = objectNode()
......@@ -222,10 +242,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
payload.set("labels", labels);
addMetaUi(node.id().toString(), payload);
String type = messageType != null ? messageType :
((event.type() == INSTANCE_ADDED) ? "addInstance" :
((event.type() == INSTANCE_REMOVED ? "removeInstance" :
"addInstance")));
String type = msgType != null ? msgType : CLUSTER_EVENT.get(event.type());
return JsonUtils.envelope(type, 0, payload);
}
......@@ -251,8 +268,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
addGeoLocation(device, payload);
addMetaUi(device.id().toString(), payload);
String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
String type = DEVICE_EVENT.get(event.type());
return JsonUtils.envelope(type, 0, payload);
}
......@@ -268,8 +284,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
.put("srcPort", link.src().port().toString())
.put("dst", link.dst().deviceId().toString())
.put("dstPort", link.dst().port().toString());
String type = (event.type() == LINK_ADDED) ? "addLink" :
((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
String type = LINK_EVENT.get(event.type());
return JsonUtils.envelope(type, 0, payload);
}
......@@ -277,20 +292,24 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
protected ObjectNode hostMessage(HostEvent event) {
Host host = event.subject();
String hostType = host.annotations().value(AnnotationKeys.TYPE);
HostLocation prevLoc = event.prevLocation();
ObjectNode payload = objectNode()
.put("id", host.id().toString())
.put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
.put("ingress", compactLinkString(edgeLink(host, true)))
.put("egress", compactLinkString(edgeLink(host, false)));
payload.set("cp", hostConnect(host.location()));
if (prevLoc != null) {
payload.set("prevCp", hostConnect(event.prevLocation()));
}
payload.set("labels", labels(ip(host.ipAddresses()),
host.mac().toString()));
payload.set("props", props(host.annotations()));
addGeoLocation(host, payload);
addMetaUi(host.id().toString(), payload);
String type = (event.type() == HOST_ADDED) ? "addHost" :
((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
String type = HOST_EVENT.get(event.type());
return JsonUtils.envelope(type, 0, payload);
}
......
......@@ -55,6 +55,7 @@
removeDevice: tfs,
addHost: tfs,
updateHost: tfs,
moveHost: tfs,
removeHost: tfs,
addLink: tfs,
updateLink: tfs,
......
......@@ -187,6 +187,35 @@
}
}
function moveHost(data) {
var id = data.id,
d = lu[id],
lnk;
if (d) {
// first remove the old host link
removeLinkElement(d.linkData);
// merge new data
angular.extend(d, data);
if (tms.positionNode(d, true)) {
sendUpdateMeta(d);
}
// now create a new host link
lnk = tms.createHostLink(data);
if (lnk) {
d.linkData = lnk;
network.links.push(lnk);
lu[d.ingress] = lnk;
lu[d.egress] = lnk;
}
updateNodes();
updateLinks();
fResume();
}
}
function removeHost(data) {
var id = data.id,
d = lu[id];
......@@ -1142,6 +1171,7 @@
removeDevice: removeDevice,
addHost: addHost,
updateHost: updateHost,
moveHost: moveHost,
removeHost: removeHost,
addLink: addLink,
updateLink: updateLink,
......