Dual-homing probing improvements
(1) Active probing mechanism in the following two scenarios
(1-1) Probe all ports on the pair device within the same vlan (excluding the pair port) when the 1st location of a host is learnt
(1-2) Probe again when a device/port goes down and comes up again
* Introduce HostLocationProvingService
- DISCOVER mode: discover potential new locations
- VERIFY mode: verify old locations
* Can be enabled/disabled via component config
* Improve HostHandlerTest to test the probing behavior
(2) Fix an issue that redirection flow doesn't get installed after device re-connects
(3) Temporarily fix a race condition in HostHandler by adding a little bit delay
Change-Id: I33d3fe94a6ca491a88b8e06f65bef11447ead0bf
diff --git a/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index 6ac25f3..1379e79 100644
--- a/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -35,6 +35,7 @@
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
import org.onosproject.net.host.HostService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,6 +44,9 @@
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
@@ -51,8 +55,9 @@
* Handles host-related events.
*/
public class HostHandler {
-
private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
+ static final int HOST_MOVED_DELAY_MS = 1000;
+
protected final SegmentRoutingManager srManager;
private HostService hostService;
private FlowObjectiveService flowObjectiveService;
@@ -71,7 +76,8 @@
protected void init(DeviceId devId) {
hostService.getHosts().forEach(host ->
host.locations().stream()
- .filter(location -> location.deviceId().equals(devId))
+ .filter(location -> location.deviceId().equals(devId) ||
+ location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
.forEach(location -> processHostAddedAtLocation(host, location))
);
}
@@ -114,6 +120,10 @@
processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
ip, false));
+
+ if (srManager.activeProbing) {
+ probe(host, location, pairDeviceId, pairRemotePort);
+ }
});
}
});
@@ -162,8 +172,23 @@
Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Set<HostLocation> newLocations = event.subject().locations();
Set<IpAddress> newIps = event.subject().ipAddresses();
- log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ if (prevLocations.size() == 1 && newLocations.size() == 2) {
+ log.debug("Delay event handling when host {}/{} moves from 1 to 2 locations", hostMac, hostVlanId);
+ ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.schedule(() ->
+ processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps),
+ HOST_MOVED_DELAY_MS, TimeUnit.MILLISECONDS);
+ } else {
+ processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps);
+ }
+ }
+
+ private void processHostMoved(MacAddress hostMac, VlanId hostVlanId, Set<HostLocation> prevLocations,
+ Set<IpAddress> prevIps, Set<HostLocation> newLocations, Set<IpAddress> newIps) {
+ log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
.collect(Collectors.toSet());
@@ -254,11 +279,12 @@
}
void processHostUpdatedEvent(HostEvent event) {
- MacAddress hostMac = event.subject().mac();
- VlanId hostVlanId = event.subject().vlan();
- Set<HostLocation> locations = event.subject().locations();
+ Host host = event.subject();
+ MacAddress hostMac = host.mac();
+ VlanId hostVlanId = host.vlan();
+ Set<HostLocation> locations = host.locations();
Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
- Set<IpAddress> newIps = event.subject().ipAddresses();
+ Set<IpAddress> newIps = host.ipAddresses();
log.info("Host {}/{} is updated", hostMac, hostVlanId);
locations.stream().filter(srManager::isMasterOf).forEach(location -> {
@@ -273,25 +299,94 @@
// Use the pair link temporarily before the second location of a dual-homed host shows up.
// This do not affect single-homed hosts since the flow will be blocked in
// processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
- locations.forEach(location -> {
+ locations.forEach(location ->
srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
if (srManager.mastershipService.isLocalMaster(pairDeviceId) &&
locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
+ Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
+ Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
+
srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
// NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
// when the host is untagged
VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
- Sets.difference(prevIps, newIps).forEach(ip ->
+ ipsToRemove.forEach(ip ->
processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
);
- Sets.difference(newIps, prevIps).forEach(ip ->
+ ipsToAdd.forEach(ip ->
processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
);
+
+ if (srManager.activeProbing) {
+ probe(host, location, pairDeviceId, pairRemotePort);
+ }
});
}
- });
- });
+ })
+ );
+ }
+
+ /**
+ * When a non-pair port comes up, probe each host on the pair device if
+ * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
+ * (2) the host is untagged and the internal vlan is the same on the host port and current port.
+ *
+ * @param cp connect point
+ */
+ void processPortUp(ConnectPoint cp) {
+ if (cp.port().equals(srManager.getPairLocalPorts(cp.deviceId()).orElse(null))) {
+ return;
+ }
+ if (srManager.activeProbing) {
+ srManager.getPairDeviceId(cp.deviceId())
+ .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
+ .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
+ .forEach(host -> srManager.probingService.probeHostLocation(host, cp, ProbeMode.DISCOVER))
+ );
+ }
+ }
+
+ /**
+ * Checks if given host located on given device id matches VLAN config of current port.
+ *
+ * @param host host to check
+ * @param deviceId device id to check
+ * @param cp current connect point
+ * @return true if the host located at deviceId matches the VLAN config on cp
+ */
+ private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
+ VlanId internalVlan = srManager.getInternalVlanId(cp);
+ Set<VlanId> taggedVlan = srManager.getTaggedVlanId(cp);
+
+ return taggedVlan.contains(host.vlan()) ||
+ (internalVlan != null && host.locations().stream()
+ .filter(l -> l.deviceId().equals(deviceId))
+ .map(srManager::getInternalVlanId)
+ .anyMatch(internalVlan::equals));
+ }
+
+ /**
+ * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
+ *
+ * @param host host to probe
+ * @param location newly discovered host location
+ * @param pairDeviceId pair device id
+ * @param pairRemotePort pair remote port
+ */
+ private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
+ VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
+ srManager.getInternalVlanId(location) : host.vlan();
+ srManager.interfaceService.getInterfaces().stream()
+ .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
+ i.vlanUntagged().equals(vlanToProbe) ||
+ i.vlanNative().equals(vlanToProbe))
+ .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
+ .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
+ .forEach(i -> {
+ log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
+ srManager.probingService.probeHostLocation(host, i.connectPoint(), ProbeMode.DISCOVER);
+ });
}
/**
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index d57a681..7a17d39 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -15,6 +15,7 @@
*/
package org.onosproject.segmentrouting;
+
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
@@ -24,6 +25,8 @@
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
@@ -35,6 +38,7 @@
import org.onlab.packet.IpPrefix;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@@ -64,6 +68,7 @@
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostLocationProbingService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intf.Interface;
@@ -106,10 +111,12 @@
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -158,6 +165,9 @@
HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ HostLocationProbingService probingService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -187,6 +197,10 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
public InterfaceService interfaceService;
+ @Property(name = "activeProbing", boolValue = true,
+ label = "Enable active probing to discover dual-homed hosts.")
+ boolean activeProbing = true;
+
ArpHandler arpHandler = null;
IcmpHandler icmpHandler = null;
IpHandler ipHandler = null;
@@ -226,19 +240,19 @@
* Per device next objective ID store with (device id + destination set) as key.
* Used to keep track on MPLS group information.
*/
- EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
+ private EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
dsNextObjStore = null;
/**
* Per device next objective ID store with (device id + vlanid) as key.
* Used to keep track on L2 flood group information.
*/
- EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
+ private EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
vlanNextObjStore = null;
/**
* Per device next objective ID store with (device id + port + treatment + meta) as key.
* Used to keep track on L2 interface group and L3 unicast group information.
*/
- EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
+ private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
// Local store for all links seen and their present status, used for
@@ -323,7 +337,7 @@
public static final VlanId INTERNAL_VLAN = VlanId.vlanId((short) 4094);
@Activate
- protected void activate() {
+ protected void activate(ComponentContext context) {
appId = coreService.registerApplication(APP_NAME);
log.debug("Creating EC map nsnextobjectivestore");
@@ -392,6 +406,8 @@
"staleLinkAge", "15000");
compCfgService.preSetProperty("org.onosproject.net.host.impl.HostManager",
"allowDuplicateIps", "false");
+ compCfgService.registerProperties(getClass());
+ modified(context);
processor = new InternalPacketProcessor();
linkListener = new InternalLinkListener();
@@ -449,6 +465,7 @@
cfgService.unregisterConfigFactory(xConnectConfigFactory);
cfgService.unregisterConfigFactory(mcastConfigFactory);
cfgService.unregisterConfigFactory(pwaasConfigFactory);
+ compCfgService.unregisterProperties(getClass(), false);
hostService.removeListener(hostListener);
packetService.removeProcessor(processor);
@@ -472,6 +489,22 @@
log.info("Stopped");
}
+ @Modified
+ private void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ if (properties == null) {
+ return;
+ }
+
+ String strActiveProving = Tools.get(properties, "activeProbing");
+ boolean expectActiveProbing = Boolean.parseBoolean(strActiveProving);
+
+ if (expectActiveProbing != activeProbing) {
+ activeProbing = expectActiveProbing;
+ log.info("{} active probing", activeProbing ? "Enabling" : "Disabling");
+ }
+ }
+
@Override
public List<Tunnel> getTunnels() {
return tunnelHandler.getTunnels();
@@ -922,7 +955,7 @@
* true if the seen-link is up;
* false if the seen-link is down
*/
- Boolean isSeenLinkUp(Link link) {
+ private Boolean isSeenLinkUp(Link link) {
return seenLinks.get(link);
}
@@ -970,7 +1003,7 @@
* @return true if another unidirectional link exists in the reverse direction,
* has been seen-before and is up
*/
- public boolean isBidirectional(Link link) {
+ boolean isBidirectional(Link link) {
Link reverseLink = linkService.getLink(link.dst(), link.src());
if (reverseLink == null) {
return false;
@@ -989,7 +1022,7 @@
* @param link the infrastructure link being queried
* @return true if link should be avoided
*/
- public boolean avoidLink(Link link) {
+ boolean avoidLink(Link link) {
// XXX currently only avoids all pair-links. In the future can be
// extended to avoid any generic link
DeviceId src = link.src().deviceId();
@@ -1012,12 +1045,9 @@
return false;
}
- if (srcPort.equals(pairLocalPort) &&
+ return srcPort.equals(pairLocalPort) &&
link.dst().deviceId().equals(pairDev) &&
- link.dst().port().equals(pairRemotePort)) {
- return true;
- }
- return false;
+ link.dst().port().equals(pairRemotePort);
}
private class InternalPacketProcessor implements PacketProcessor {
@@ -1374,9 +1404,7 @@
private void processDeviceRemoved(Device device) {
dsNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
- .forEach(entry -> {
- dsNextObjStore.remove(entry.getKey());
- });
+ .forEach(entry -> dsNextObjStore.remove(entry.getKey()));
vlanNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> vlanNextObjStore.remove(entry.getKey()));
@@ -1456,6 +1484,7 @@
if (portUp) {
log.info("Device:EdgePort {}:{} is enabled in vlan: {}", device.id(),
port.number(), vlanId);
+ hostHandler.processPortUp(new ConnectPoint(device.id(), port.number()));
} else {
log.info("Device:EdgePort {}:{} is disabled in vlan: {}", device.id(),
port.number(), vlanId);
@@ -1509,7 +1538,7 @@
/**
* Reads network config and initializes related data structure accordingly.
*/
- public void configureNetwork() {
+ void configureNetwork() {
createOrUpdateDeviceConfiguration();
arpHandler = new ArpHandler(srManager);
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index b7d32a1..7ea430d 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -501,6 +501,20 @@
}
/**
+ * Returns all ports that has a subnet that contains any of the given IP addresses.
+ *
+ * @param ips a set of IP addresses
+ * @return a set of connect point that has a subnet that contains any of the given IP addresses
+ */
+ public Set<ConnectPoint> getPortByIps(Set<IpAddress> ips) {
+ return srManager.interfaceService.getInterfaces().stream()
+ .filter(intf -> intf.ipAddressesList().stream().anyMatch(intfAddress ->
+ ips.stream().anyMatch(ip -> intfAddress.subnetAddress().contains(ip))))
+ .map(Interface::connectPoint)
+ .collect(Collectors.toSet());
+ }
+
+ /**
* Returns the router ip address of segment router that has the
* specified ip address in its subnets.
*
diff --git a/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index 3b6d00a..cce5a31 100644
--- a/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -28,6 +28,7 @@
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.host.HostLocationProbingService;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultHost;
@@ -176,6 +177,8 @@
private static final Set<Interface> INTERFACES = Sets.newHashSet(INTF11, INTF12, INTF13, INTF21,
INTF22, INTF31, INTF32, INTF39, INTF41, INTF49);
+ private MockLocationProbingService mockLocationProbingService;
+
@Before
public void setUp() throws Exception {
// Initialize pairDevice and pairLocalPort config
@@ -207,6 +210,8 @@
srManager.mastershipService = new MockMastershipService(LOCAL_DEVICES);
srManager.hostService = new MockHostService(HOSTS);
srManager.cfgService = mockNetworkConfigRegistry;
+ mockLocationProbingService = new MockLocationProbingService();
+ srManager.probingService = mockLocationProbingService;
hostHandler = new HostHandler(srManager);
@@ -336,15 +341,22 @@
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // Expect probe to be sent out on pair device
+ assertTrue(mockLocationProbingService.verifyProbe(host1, CP41, HostLocationProbingService.ProbeMode.DISCOVER));
// Add the second location of dual-homed host
// Expect: no longer use the pair link
hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
assertEquals(2, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ Thread.sleep(HostHandler.HOST_MOVED_DELAY_MS + 50);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
}
@@ -612,10 +624,16 @@
assertEquals(4, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ Thread.sleep(HostHandler.HOST_MOVED_DELAY_MS + 50);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
}
@@ -714,22 +732,29 @@
// Discover IP
// Expect: routing redirection should also work
- hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host1));
+ hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host1));
assertEquals(2, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // Expect probe to be sent out on pair device
+ assertTrue(mockLocationProbingService.verifyProbe(host2, CP41, HostLocationProbingService.ProbeMode.DISCOVER));
// Discover location
// Expect: cancel all redirections
- hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
+ hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
assertEquals(2, ROUTING_TABLE.size());
assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
- assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
+ assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(2, BRIDGING_TABLE.size());
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
+ // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
+ // The permanent solution would be introducing CompletableFuture and wait for it
+ Thread.sleep(HostHandler.HOST_MOVED_DELAY_MS + 50);
+ assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
}
diff --git a/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java b/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
index 2367b57..f8d04ca 100644
--- a/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
+++ b/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
@@ -39,4 +39,9 @@
return interfaces.stream().filter(intf -> cp.equals(intf.connectPoint()))
.collect(Collectors.toSet());
}
+
+ @Override
+ public Set<Interface> getInterfaces() {
+ return interfaces;
+ }
}
diff --git a/src/test/java/org/onosproject/segmentrouting/MockLocationProbingService.java b/src/test/java/org/onosproject/segmentrouting/MockLocationProbingService.java
new file mode 100644
index 0000000..b0cdf43
--- /dev/null
+++ b/src/test/java/org/onosproject/segmentrouting/MockLocationProbingService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.onosproject.segmentrouting;
+
+import com.google.common.collect.Lists;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostLocationProbingService;
+
+import java.util.List;
+import java.util.Objects;
+
+public class MockLocationProbingService implements HostLocationProbingService {
+ List<Probe> probes;
+
+ private class Probe {
+ private Host host;
+ private ConnectPoint connectPoint;
+ private ProbeMode probeMode;
+
+ Probe(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ this.host = host;
+ this.connectPoint = connectPoint;
+ this.probeMode = probeMode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Probe)) {
+ return false;
+ }
+ Probe that = (Probe) o;
+ return (Objects.equals(this.host, that.host) &&
+ Objects.equals(this.connectPoint, that.connectPoint) &&
+ Objects.equals(this.probeMode, that.probeMode));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(host, connectPoint, probeMode);
+ }
+ }
+
+ MockLocationProbingService() {
+ probes = Lists.newArrayList();
+ }
+
+ boolean verifyProbe(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ Probe probe = new Probe(host, connectPoint, probeMode);
+ return probes.contains(probe);
+ }
+
+ @Override
+ public void probeHostLocation(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
+ probes.add(new Probe(host, connectPoint, probeMode));
+ }
+}