Jonathan Hart

Refactored the HostStore to allow multiple MAC addresses bound to a single port

Change-Id: Icd3b2e483b15486251ac1cca107478a012d1a3e7
......@@ -53,11 +53,13 @@ public class HostToInterfaceAdaptor implements InterfaceService {
public Interface getInterface(ConnectPoint connectPoint) {
checkNotNull(connectPoint);
PortAddresses portAddresses =
Set<PortAddresses> portAddresses =
hostService.getAddressBindingsForPort(connectPoint);
if (!portAddresses.ipAddresses().isEmpty()) {
return new Interface(portAddresses);
for (PortAddresses addresses : portAddresses) {
if (addresses.connectPoint().equals(connectPoint)) {
return new Interface(addresses);
}
}
return null;
......
......@@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
......@@ -63,10 +64,6 @@ public class HostToInterfaceAdaptorTest {
private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint(
DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1));
private static final PortAddresses DEFAULT_PA = new PortAddresses(
NON_EXISTENT_CP, null, null);
@Before
public void setUp() throws Exception {
hostService = createMock(HostService.class);
......@@ -123,7 +120,8 @@ public class HostToInterfaceAdaptorTest {
MacAddress mac) {
PortAddresses pa = new PortAddresses(cp, ipAddresses, mac);
portAddresses.add(pa);
expect(hostService.getAddressBindingsForPort(cp)).andReturn(pa).anyTimes();
expect(hostService.getAddressBindingsForPort(cp)).andReturn(
Collections.singleton(pa)).anyTimes();
Interface intf = new Interface(cp, ipAddresses, mac);
interfaces.put(cp, intf);
......@@ -158,7 +156,7 @@ public class HostToInterfaceAdaptorTest {
// Try and get an interface for a connect point with no addresses
reset(hostService);
expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP))
.andReturn(DEFAULT_PA).anyTimes();
.andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
replay(hostService);
assertNull(adaptor.getInterface(NON_EXISTENT_CP));
......
......@@ -34,11 +34,7 @@ public interface HostAdminService {
* Binds IP and MAC addresses to the given connection point.
* <p>
* The addresses are added to the set of addresses already bound to the
* connection point. If any of the fields in addresses is null, no change
* is made to the corresponding addresses in the store.
* {@link #unbindAddressesFromPort(PortAddresses)} must be use to unbind
* addresses that have previously been bound.
* </p>
* connection point.
*
* @param addresses address object containing addresses to add and the port
* to add them to
......
......@@ -135,7 +135,7 @@ public interface HostService {
* @param connectPoint the connection point to retrieve address bindings for
* @return addresses bound to the port
*/
PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint);
Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint);
/**
* Adds the specified host listener.
......
......@@ -15,6 +15,8 @@
*/
package org.onlab.onos.net.host;
import java.util.Set;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Host;
......@@ -25,8 +27,6 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import java.util.Set;
/**
* Manages inventory of end-station hosts; not intended for direct use.
*/
......@@ -153,5 +153,5 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> {
* for
* @return address information for the connection point
*/
PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint);
Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint);
}
......
......@@ -95,7 +95,7 @@ public class HostServiceAdapter implements HostService {
}
@Override
public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
return null;
}
......
......@@ -207,7 +207,7 @@ public class HostManager
}
@Override
public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
return store.getAddressBindingsForPort(connectPoint);
}
......
......@@ -175,13 +175,15 @@ public class HostMonitor implements TimerTask {
for (Device device : deviceService.getDevices()) {
for (Port port : deviceService.getPorts(device.id())) {
ConnectPoint cp = new ConnectPoint(device.id(), port.number());
PortAddresses portAddresses =
Set<PortAddresses> portAddressSet =
hostManager.getAddressBindingsForPort(cp);
for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
if (ia.subnetAddress().contains(targetIp)) {
sendProbe(device.id(), port, targetIp,
ia.ipAddress(), portAddresses.mac());
for (PortAddresses portAddresses : portAddressSet) {
for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
if (ia.subnetAddress().contains(targetIp)) {
sendProbe(device.id(), port, targetIp,
ia.ipAddress(), portAddresses.mac());
}
}
}
}
......
......@@ -134,14 +134,16 @@ public class ProxyArpManager implements ProxyArpService {
IpAddress target =
IpAddress.valueOf(IpAddress.Version.INET,
arp.getTargetProtocolAddress());
PortAddresses addresses =
Set<PortAddresses> addressSet =
hostService.getAddressBindingsForPort(inPort);
for (InterfaceIpAddress ia : addresses.ipAddresses()) {
if (ia.ipAddress().equals(target)) {
Ethernet arpReply =
buildArpReply(ia.ipAddress(), addresses.mac(), eth);
sendTo(arpReply, inPort);
for (PortAddresses addresses : addressSet) {
for (InterfaceIpAddress ia : addresses.ipAddresses()) {
if (ia.ipAddress().equals(target)) {
Ethernet arpReply =
buildArpReply(ia.ipAddress(), addresses.mac(), eth);
sendTo(arpReply, inPort);
}
}
}
return;
......@@ -244,7 +246,7 @@ public class ProxyArpManager implements ProxyArpService {
// TODO: Is this sufficient to identify outside-facing ports: just
// having IP addresses on a port?
//
return !hostService.getAddressBindingsForPort(port).ipAddresses().isEmpty();
return !hostService.getAddressBindingsForPort(port).isEmpty();
}
@Override
......
......@@ -234,10 +234,10 @@ public class HostManagerTest {
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(add1.ipAddresses().equals(storedAddresses.ipAddresses()));
assertTrue(add1.mac().equals(storedAddresses.mac()));
assertEquals(1, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
// Add some more addresses and check that they're added correctly
PortAddresses add2 =
......@@ -246,18 +246,19 @@ public class HostManagerTest {
mgr.bindAddressesToPort(add2);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().equals(
Sets.newHashSet(IA1, IA2, IA3)));
assertTrue(storedAddresses.mac().equals(MAC1));
assertEquals(2, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
assertTrue(storedAddresses.contains(add2));
PortAddresses add3 = new PortAddresses(CP1, null, MAC2);
mgr.bindAddressesToPort(add3);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().equals(
Sets.newHashSet(IA1, IA2, IA3)));
assertTrue(storedAddresses.mac().equals(MAC2));
assertEquals(3, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
assertTrue(storedAddresses.contains(add2));
assertTrue(storedAddresses.contains(add3));
}
@Test
......@@ -266,10 +267,10 @@ public class HostManagerTest {
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().size() == 2);
assertNotNull(storedAddresses.mac());
assertEquals(1, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
PortAddresses rem1 =
new PortAddresses(CP1, Sets.newHashSet(IA1), null);
......@@ -277,25 +278,15 @@ public class HostManagerTest {
mgr.unbindAddressesFromPort(rem1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().equals(Sets.newHashSet(IA2)));
assertTrue(storedAddresses.mac().equals(MAC1));
// It shouldn't have been removed because it didn't match the originally
// submitted address object
assertEquals(1, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
PortAddresses rem2 = new PortAddresses(CP1, null, MAC1);
mgr.unbindAddressesFromPort(rem2);
mgr.unbindAddressesFromPort(add1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().equals(Sets.newHashSet(IA2)));
assertNull(storedAddresses.mac());
PortAddresses rem3 =
new PortAddresses(CP1, Sets.newHashSet(IA2), MAC1);
mgr.unbindAddressesFromPort(rem3);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().isEmpty());
assertNull(storedAddresses.mac());
assertTrue(storedAddresses.isEmpty());
}
@Test
......@@ -304,16 +295,15 @@ public class HostManagerTest {
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().size() == 2);
assertNotNull(storedAddresses.mac());
assertEquals(1, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
mgr.clearAddresses(CP1);
storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.ipAddresses().isEmpty());
assertNull(storedAddresses.mac());
assertTrue(storedAddresses.isEmpty());
}
@Test
......@@ -322,12 +312,10 @@ public class HostManagerTest {
new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
mgr.bindAddressesToPort(add1);
PortAddresses storedAddresses = mgr.getAddressBindingsForPort(CP1);
Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
assertTrue(storedAddresses.connectPoint().equals(CP1));
assertTrue(storedAddresses.ipAddresses().equals(
Sets.newHashSet(IA1, IA2)));
assertTrue(storedAddresses.mac().equals(MAC1));
assertEquals(1, storedAddresses.size());
assertTrue(storedAddresses.contains(add1));
}
@Test
......
......@@ -20,7 +20,9 @@ import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
......@@ -130,7 +132,7 @@ public class HostMonitorTest {
expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
.andReturn(Collections.<Host>emptySet()).anyTimes();
expect(hostManager.getAddressBindingsForPort(cp))
.andReturn(pa).anyTimes();
.andReturn(Collections.singleton(pa)).anyTimes();
replay(hostManager);
TestPacketService packetService = new TestPacketService();
......
......@@ -19,7 +19,10 @@ import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
......@@ -207,13 +210,18 @@ public class ProxyArpManagerTest {
IpAddress addr2 = IpAddress.valueOf("10.0." + (2 * i) + ".1");
InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
PortAddresses pa =
new PortAddresses(cp, Sets.newHashSet(ia1, ia2),
MacAddress.valueOf(i));
addresses.add(pa);
PortAddresses pa1 =
new PortAddresses(cp, Sets.newHashSet(ia1),
MacAddress.valueOf(2 * i - 1));
PortAddresses pa2 =
new PortAddresses(cp, Sets.newHashSet(ia2),
MacAddress.valueOf(2 * i));
addresses.add(pa1);
addresses.add(pa2);
expect(hostService.getAddressBindingsForPort(cp))
.andReturn(pa).anyTimes();
.andReturn(Sets.newHashSet(pa1, pa2)).anyTimes();
}
expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
......@@ -222,7 +230,7 @@ public class ProxyArpManagerTest {
ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
P1);
expect(hostService.getAddressBindingsForPort(cp))
.andReturn(new PortAddresses(cp, null, null)).anyTimes();
.andReturn(Collections.<PortAddresses>emptySet()).anyTimes();
}
}
......@@ -339,7 +347,8 @@ public class ProxyArpManagerTest {
IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
IpAddress ourFirstIp = IpAddress.valueOf("10.0.1.1");
IpAddress ourSecondIp = IpAddress.valueOf("10.0.2.1");
MacAddress ourMac = MacAddress.valueOf(1L);
MacAddress firstMac = MacAddress.valueOf(1L);
MacAddress secondMac = MacAddress.valueOf(2L);
Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
Collections.singleton(theirIp));
......@@ -352,7 +361,7 @@ public class ProxyArpManagerTest {
proxyArp.reply(arpRequest, LOC1);
assertEquals(1, packetService.packets.size());
Ethernet arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourFirstIp, theirIp);
Ethernet arpReply = buildArp(ARP.OP_REPLY, firstMac, MAC2, ourFirstIp, theirIp);
verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
// Test a request for the second address on that port
......@@ -362,7 +371,7 @@ public class ProxyArpManagerTest {
proxyArp.reply(arpRequest, LOC1);
assertEquals(1, packetService.packets.size());
arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourSecondIp, theirIp);
arpReply = buildArp(ARP.OP_REPLY, secondMac, MAC2, ourSecondIp, theirIp);
verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
}
......
......@@ -15,12 +15,25 @@
*/
package org.onlab.onos.store.host.impl;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.RandomUtils;
import org.apache.felix.scr.annotations.Activate;
......@@ -45,7 +58,6 @@ import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.HostStoreDelegate;
import org.onlab.onos.net.host.InterfaceIpAddress;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
......@@ -63,21 +75,13 @@ import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
import static org.onlab.onos.net.host.HostEvent.Type.*;
import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
//TODO: multi-provider, annotation not supported.
/**
......@@ -100,8 +104,9 @@ public class GossipHostStore
// Hosts tracked by their location
private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
private final Map<ConnectPoint, PortAddresses> portAddresses =
new ConcurrentHashMap<>();
private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
Multimaps.synchronizedSetMultimap(
HashMultimap.<ConnectPoint, PortAddresses>create());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostClockService hostClockService;
......@@ -343,77 +348,37 @@ public class GossipHostStore
@Override
public void updateAddressBindings(PortAddresses addresses) {
synchronized (portAddresses) {
PortAddresses existing = portAddresses.get(addresses.connectPoint());
if (existing == null) {
portAddresses.put(addresses.connectPoint(), addresses);
} else {
Set<InterfaceIpAddress> union =
Sets.union(existing.ipAddresses(),
addresses.ipAddresses()).immutableCopy();
MacAddress newMac = (addresses.mac() == null) ? existing.mac()
: addresses.mac();
PortAddresses newAddresses =
new PortAddresses(addresses.connectPoint(), union, newMac);
portAddresses.put(newAddresses.connectPoint(), newAddresses);
}
}
portAddresses.put(addresses.connectPoint(), addresses);
}
@Override
public void removeAddressBindings(PortAddresses addresses) {
synchronized (portAddresses) {
PortAddresses existing = portAddresses.get(addresses.connectPoint());
if (existing != null) {
Set<InterfaceIpAddress> difference =
Sets.difference(existing.ipAddresses(),
addresses.ipAddresses()).immutableCopy();
// If they removed the existing mac, set the new mac to null.
// Otherwise, keep the existing mac.
MacAddress newMac = existing.mac();
if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
newMac = null;
}
PortAddresses newAddresses =
new PortAddresses(addresses.connectPoint(), difference, newMac);
portAddresses.put(newAddresses.connectPoint(), newAddresses);
}
}
portAddresses.remove(addresses.connectPoint(), addresses);
}
@Override
public void clearAddressBindings(ConnectPoint connectPoint) {
synchronized (portAddresses) {
portAddresses.remove(connectPoint);
}
portAddresses.removeAll(connectPoint);
}
@Override
public Set<PortAddresses> getAddressBindings() {
synchronized (portAddresses) {
return new HashSet<>(portAddresses.values());
return ImmutableSet.copyOf(portAddresses.values());
}
}
@Override
public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
PortAddresses addresses;
public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
synchronized (portAddresses) {
addresses = portAddresses.get(connectPoint);
}
Set<PortAddresses> addresses = portAddresses.get(connectPoint);
if (addresses == null) {
addresses = new PortAddresses(connectPoint, null, null);
if (addresses == null) {
return Collections.emptySet();
} else {
return ImmutableSet.copyOf(addresses);
}
}
return addresses;
}
// Auxiliary extension to allow location to mutate.
......
......@@ -15,10 +15,18 @@
*/
package org.onlab.onos.store.trivial.impl;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -34,7 +42,6 @@ import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostStore;
import org.onlab.onos.net.host.HostStoreDelegate;
import org.onlab.onos.net.host.InterfaceIpAddress;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
......@@ -43,13 +50,11 @@ import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.onlab.onos.net.host.HostEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
// TODO: multi-provider, annotation not supported.
/**
......@@ -70,8 +75,9 @@ public class SimpleHostStore
// Hosts tracked by their location
private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
private final Map<ConnectPoint, PortAddresses> portAddresses =
new ConcurrentHashMap<>();
private final SetMultimap<ConnectPoint, PortAddresses> portAddresses =
Multimaps.synchronizedSetMultimap(
HashMultimap.<ConnectPoint, PortAddresses>create());
@Activate
public void activate() {
......@@ -213,77 +219,37 @@ public class SimpleHostStore
@Override
public void updateAddressBindings(PortAddresses addresses) {
synchronized (portAddresses) {
PortAddresses existing = portAddresses.get(addresses.connectPoint());
if (existing == null) {
portAddresses.put(addresses.connectPoint(), addresses);
} else {
Set<InterfaceIpAddress> union =
Sets.union(existing.ipAddresses(),
addresses.ipAddresses()).immutableCopy();
MacAddress newMac = (addresses.mac() == null) ? existing.mac()
: addresses.mac();
PortAddresses newAddresses =
new PortAddresses(addresses.connectPoint(), union, newMac);
portAddresses.put(newAddresses.connectPoint(), newAddresses);
}
}
portAddresses.put(addresses.connectPoint(), addresses);
}
@Override
public void removeAddressBindings(PortAddresses addresses) {
synchronized (portAddresses) {
PortAddresses existing = portAddresses.get(addresses.connectPoint());
if (existing != null) {
Set<InterfaceIpAddress> difference =
Sets.difference(existing.ipAddresses(),
addresses.ipAddresses()).immutableCopy();
// If they removed the existing mac, set the new mac to null.
// Otherwise, keep the existing mac.
MacAddress newMac = existing.mac();
if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
newMac = null;
}
PortAddresses newAddresses =
new PortAddresses(addresses.connectPoint(), difference, newMac);
portAddresses.put(newAddresses.connectPoint(), newAddresses);
}
}
portAddresses.remove(addresses.connectPoint(), addresses);
}
@Override
public void clearAddressBindings(ConnectPoint connectPoint) {
synchronized (portAddresses) {
portAddresses.remove(connectPoint);
}
portAddresses.removeAll(connectPoint);
}
@Override
public Set<PortAddresses> getAddressBindings() {
synchronized (portAddresses) {
return new HashSet<>(portAddresses.values());
return ImmutableSet.copyOf(portAddresses.values());
}
}
@Override
public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
PortAddresses addresses;
public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
synchronized (portAddresses) {
addresses = portAddresses.get(connectPoint);
}
Set<PortAddresses> addresses = portAddresses.get(connectPoint);
if (addresses == null) {
addresses = new PortAddresses(connectPoint, null, null);
if (addresses == null) {
return Collections.emptySet();
} else {
return ImmutableSet.copyOf(addresses);
}
}
return addresses;
}
// Auxiliary extension to allow location to mutate.
......