Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
index be91609..d417516 100644
--- a/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipService.java
@@ -56,7 +56,8 @@
Set<DeviceId> getDevicesOf(NodeId nodeId);
/**
- * Returns the mastership term service for getting term information.
+ * Returns the mastership term service for getting read-only
+ * term information.
*
* @return the MastershipTermService for this mastership manager
*/
diff --git a/core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java b/core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java
index be5d873..bedc5e9 100644
--- a/core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java
+++ b/core/api/src/main/java/org/onlab/onos/cluster/MastershipStore.java
@@ -64,4 +64,14 @@
* @return the current master's ID and the term value for device, or null
*/
MastershipTerm getTermFor(DeviceId deviceId);
+
+ /**
+ * Revokes a controller instance's mastership over a device and hands
+ * over mastership to another controller instance.
+ *
+ * @param nodeId the controller instance identifier
+ * @param deviceId device to revoke mastership for
+ * @return a mastership event
+ */
+ MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
index c84aac8..fdb9f6d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java
@@ -48,6 +48,7 @@
DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
DeviceDescription deviceDescription);
+ // TODO: We may need to enforce that ancillary cannot interfere this state
/**
* Removes the specified infrastructure device.
*
@@ -60,22 +61,24 @@
* Updates the ports of the specified infrastructure device using the given
* list of port descriptions. The list is assumed to be comprehensive.
*
+ * @param providerId provider identifier
* @param deviceId device identifier
* @param portDescriptions list of port descriptions
* @return ready to send events describing what occurred; empty list if no change
*/
- List<DeviceEvent> updatePorts(DeviceId deviceId,
+ List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
List<PortDescription> portDescriptions);
/**
* Updates the port status of the specified infrastructure device using the
* given port description.
*
+ * @param providerId provider identifier
* @param deviceId device identifier
* @param portDescription port description
* @return ready to send event describing what occurred; null if no change
*/
- DeviceEvent updatePortStatus(DeviceId deviceId,
+ DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
PortDescription portDescription);
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java b/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
index a4095ea..d59bfd2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
@@ -35,10 +35,22 @@
public synchronized S register(P provider) {
checkNotNull(provider, "Provider cannot be null");
checkState(!services.containsKey(provider.id()), "Provider %s already registered", provider.id());
+
+ // If the provider is a primary one, check for a conflict.
+ ProviderId pid = provider.id();
+ checkState(pid.isAncillary() || !providersByScheme.containsKey(pid.scheme()),
+ "A primary provider with id %s is already registered",
+ providersByScheme.get(pid.scheme()));
+
S service = createProviderService(provider);
services.put(provider.id(), service);
providers.put(provider.id(), provider);
- // FIXME populate scheme look-up
+
+ // Register the provider by URI scheme only if it is not ancillary.
+ if (!pid.isAncillary()) {
+ providersByScheme.put(pid.scheme(), provider);
+ }
+
return service;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
index 725748a..7d314e8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
@@ -11,6 +11,7 @@
private final String scheme;
private final String id;
+ private final boolean ancillary;
/**
* Creates a new provider identifier from the specified string.
@@ -21,8 +22,22 @@
* @param id string identifier
*/
public ProviderId(String scheme, String id) {
+ this(scheme, id, false);
+ }
+
+ /**
+ * Creates a new provider identifier from the specified string.
+ * The providers are expected to follow the reverse DNS convention, e.g.
+ * {@code org.onlab.onos.provider.of.device}
+ *
+ * @param scheme device URI scheme to which this provider is bound, e.g. "of", "snmp"
+ * @param id string identifier
+ * @param ancillary ancillary provider indicator
+ */
+ public ProviderId(String scheme, String id, boolean ancillary) {
this.scheme = scheme;
this.id = id;
+ this.ancillary = ancillary;
}
/**
@@ -35,6 +50,15 @@
}
/**
+ * Indicates whether the provider id belongs to an ancillary provider.
+ *
+ * @return true for ancillary; false for primary provider
+ */
+ public boolean isAncillary() {
+ return ancillary;
+ }
+
+ /**
* Returns the device URI scheme specific id portion.
*
* @return id
@@ -56,14 +80,16 @@
if (obj instanceof ProviderId) {
final ProviderId other = (ProviderId) obj;
return Objects.equals(this.scheme, other.scheme) &&
- Objects.equals(this.id, other.id);
+ Objects.equals(this.id, other.id) &&
+ this.ancillary == other.ancillary;
}
return false;
}
@Override
public String toString() {
- return toStringHelper(this).add("scheme", scheme).add("id", id).toString();
+ return toStringHelper(this).add("scheme", scheme).add("id", id)
+ .add("ancillary", ancillary).toString();
}
}
diff --git a/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java b/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java
new file mode 100644
index 0000000..139c695
--- /dev/null
+++ b/core/api/src/test/java/org/onlab/onos/cluster/MastershipTermTest.java
@@ -0,0 +1,32 @@
+package org.onlab.onos.cluster;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+public class MastershipTermTest {
+
+ private static final NodeId N1 = new NodeId("foo");
+ private static final NodeId N2 = new NodeId("bar");
+
+ private static final MastershipTerm TERM1 = MastershipTerm.of(N1, 0);
+ private static final MastershipTerm TERM2 = MastershipTerm.of(N2, 1);
+ private static final MastershipTerm TERM3 = MastershipTerm.of(N2, 1);
+ private static final MastershipTerm TERM4 = MastershipTerm.of(N1, 1);
+
+ @Test
+ public void basics() {
+ assertEquals("incorrect term number", 0, TERM1.termNumber());
+ assertEquals("incorrect master", new NodeId("foo"), TERM1.master());
+ }
+
+ @Test
+ public void testEquality() {
+ new EqualsTester().addEqualityGroup(MastershipTerm.of(N1, 0), TERM1)
+ .addEqualityGroup(TERM2, TERM3)
+ .addEqualityGroup(TERM4);
+ }
+
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
index 37bee71..3ecd90d 100644
--- a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
@@ -35,7 +35,7 @@
assertThat("provider not found", registry.getProviders().contains(fooId));
assertEquals("incorrect provider", psFoo.provider(), pFoo);
- ProviderId barId = new ProviderId("of", "bar");
+ ProviderId barId = new ProviderId("snmp", "bar");
TestProvider pBar = new TestProvider(barId);
TestProviderService psBar = registry.register(pBar);
assertEquals("incorrect provider count", 2, registry.getProviders().size());
@@ -49,6 +49,16 @@
assertThat("provider not found", registry.getProviders().contains(barId));
}
+ @Test
+ public void ancillaryProviders() {
+ TestProviderRegistry registry = new TestProviderRegistry();
+ TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
+ TestProvider pBar = new TestProvider(new ProviderId("of", "bar", true));
+ registry.register(pFoo);
+ registry.register(pBar);
+ assertEquals("incorrect provider count", 2, registry.getProviders().size());
+ }
+
@Test(expected = IllegalStateException.class)
public void duplicateRegistration() {
TestProviderRegistry registry = new TestProviderRegistry();
@@ -57,6 +67,15 @@
registry.register(pFoo);
}
+ @Test(expected = IllegalStateException.class)
+ public void duplicateSchemeRegistration() {
+ TestProviderRegistry registry = new TestProviderRegistry();
+ TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
+ TestProvider pBar = new TestProvider(new ProviderId("of", "bar"));
+ registry.register(pFoo);
+ registry.register(pBar);
+ }
+
@Test
public void voidUnregistration() {
TestProviderRegistry registry = new TestProviderRegistry();
diff --git a/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java b/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java
index 1a0c408..a0da87c 100644
--- a/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java
+++ b/core/net/src/main/java/org/onlab/onos/cluster/impl/MastershipManager.java
@@ -11,6 +11,8 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.ClusterEvent;
+import org.onlab.onos.cluster.ClusterEventListener;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.MastershipAdminService;
import org.onlab.onos.cluster.MastershipEvent;
@@ -52,9 +54,12 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
+ private ClusterEventListener clusterListener = new InternalClusterEventListener();
+
@Activate
public void activate() {
eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
+ clusterService.addListener(clusterListener);
store.setDelegate(delegate);
log.info("Started");
}
@@ -62,6 +67,7 @@
@Deactivate
public void deactivate() {
eventDispatcher.removeSink(MastershipEvent.class);
+ clusterService.removeListener(clusterListener);
store.unsetDelegate(delegate);
log.info("Stopped");
}
@@ -71,12 +77,16 @@
checkNotNull(nodeId, NODE_ID_NULL);
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(role, ROLE_NULL);
- //TODO figure out appropriate action for non-MASTER roles, if we even set those
+
+ MastershipEvent event = null;
if (role.equals(MastershipRole.MASTER)) {
- MastershipEvent event = store.setMaster(nodeId, deviceId);
- if (event != null) {
- post(event);
- }
+ event = store.setMaster(nodeId, deviceId);
+ } else {
+ event = store.unsetMaster(nodeId, deviceId);
+ }
+
+ if (event != null) {
+ post(event);
}
}
@@ -88,8 +98,16 @@
@Override
public void relinquishMastership(DeviceId deviceId) {
- checkNotNull(deviceId, DEVICE_ID_NULL);
- // FIXME: add method to store to give up mastership and trigger new master selection process
+ MastershipRole role = getLocalRole(deviceId);
+ if (!role.equals(MastershipRole.MASTER)) {
+ return;
+ }
+
+ MastershipEvent event = store.unsetMaster(
+ clusterService.getLocalNode().id(), deviceId);
+ if (event != null) {
+ post(event);
+ }
}
@Override
@@ -146,6 +164,26 @@
}
+ //callback for reacting to cluster events
+ private class InternalClusterEventListener implements ClusterEventListener {
+
+ @Override
+ public void event(ClusterEvent event) {
+ switch (event.type()) {
+ //FIXME: worry about addition when the time comes
+ case INSTANCE_ADDED:
+ case INSTANCE_ACTIVATED:
+ break;
+ case INSTANCE_REMOVED:
+ case INSTANCE_DEACTIVATED:
+ break;
+ default:
+ log.warn("unknown cluster event {}", event);
+ }
+ }
+
+ }
+
public class InternalDelegate implements MastershipStoreDelegate {
@Override
diff --git a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
index e7f2697..b61afd2 100644
--- a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
@@ -16,6 +16,7 @@
import org.onlab.onos.cluster.MastershipEvent;
import org.onlab.onos.cluster.MastershipListener;
import org.onlab.onos.cluster.MastershipService;
+import org.onlab.onos.cluster.MastershipTermService;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
@@ -76,6 +77,8 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
+ protected MastershipTermService termService;
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClockService clockService;
@@ -84,6 +87,7 @@
store.setDelegate(delegate);
eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
mastershipService.addListener(mastershipListener);
+ termService = mastershipService.requestTermService();
log.info("Started");
}
@@ -198,7 +202,7 @@
log.info("Device {} connected", deviceId);
mastershipService.requestRoleFor(deviceId);
provider().roleChanged(event.subject(),
- mastershipService.getLocalRole(deviceId));
+ mastershipService.requestRoleFor(deviceId));
post(event);
}
}
@@ -208,8 +212,11 @@
checkNotNull(deviceId, DEVICE_ID_NULL);
checkValidity();
DeviceEvent event = store.markOffline(deviceId);
+
+ //we're no longer capable of mastership.
if (event != null) {
log.info("Device {} disconnected", deviceId);
+ mastershipService.relinquishMastership(deviceId);
post(event);
}
}
@@ -221,8 +228,9 @@
checkNotNull(portDescriptions,
"Port descriptions list cannot be null");
checkValidity();
- List<DeviceEvent> events = store.updatePorts(deviceId,
- portDescriptions);
+ this.provider().id();
+ List<DeviceEvent> events = store.updatePorts(this.provider().id(),
+ deviceId, portDescriptions);
for (DeviceEvent event : events) {
post(event);
}
@@ -234,8 +242,8 @@
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
checkValidity();
- DeviceEvent event = store.updatePortStatus(deviceId,
- portDescription);
+ DeviceEvent event = store.updatePortStatus(this.provider().id(),
+ deviceId, portDescription);
if (event != null) {
log.info("Device {} port {} status changed", deviceId, event
.port().number());
diff --git a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
index 770f368..7ee6ddd 100644
--- a/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
+++ b/core/net/src/main/java/org/onlab/onos/net/topology/impl/DefaultTopologyProvider.java
@@ -65,8 +65,8 @@
private volatile boolean isStarted = false;
private TopologyProviderService providerService;
- private DeviceListener deviceListener = new InnerDeviceListener();
- private LinkListener linkListener = new InnerLinkListener();
+ private DeviceListener deviceListener = new InternalDeviceListener();
+ private LinkListener linkListener = new InternalLinkListener();
private EventAccumulator accumulator;
private ExecutorService executor;
@@ -132,7 +132,7 @@
}
// Callback for device events
- private class InnerDeviceListener implements DeviceListener {
+ private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
DeviceEvent.Type type = event.type();
@@ -144,7 +144,7 @@
}
// Callback for link events
- private class InnerLinkListener implements LinkListener {
+ private class InternalLinkListener implements LinkListener {
@Override
public void event(LinkEvent event) {
accumulator.add(event);
diff --git a/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java b/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java
index d4a13ab..29b4ddf 100644
--- a/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java
@@ -15,10 +15,11 @@
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.event.impl.TestEventDispatcher;
import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.trivial.impl.SimpleMastershipStore;
+import org.onlab.onos.store.trivial.impl.SimpleMastershipStore;
import org.onlab.packet.IpPrefix;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.onlab.onos.net.MastershipRole.*;
/**
@@ -65,7 +66,24 @@
@Test
public void relinquishMastership() {
- //TODO
+ //no backups - should turn to standby and no master for device
+ mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
+ assertEquals("wrong role:", MASTER, mgr.getLocalRole(DEV_MASTER));
+ mgr.relinquishMastership(DEV_MASTER);
+ assertNull("wrong master:", mgr.getMasterFor(DEV_OTHER));
+ assertEquals("wrong role:", STANDBY, mgr.getLocalRole(DEV_MASTER));
+
+ //not master, nothing should happen
+ mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY);
+ mgr.relinquishMastership(DEV_OTHER);
+ assertNull("wrong role:", mgr.getMasterFor(DEV_OTHER));
+
+ //provide NID_OTHER as backup and relinquish
+ mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
+ assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_MASTER));
+ mgr.setRole(NID_OTHER, DEV_MASTER, STANDBY);
+ mgr.relinquishMastership(DEV_MASTER);
+ assertEquals("wrong master:", NID_OTHER, mgr.getMasterFor(DEV_MASTER));
}
@Test
@@ -95,7 +113,6 @@
mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY);
assertEquals("should be one device:", 1, mgr.getDevicesOf(NID_LOCAL).size());
-
//hand both devices to NID_LOCAL
mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER);
assertEquals("should be two devices:", 2, mgr.getDevicesOf(NID_LOCAL).size());
diff --git a/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
index 9ff34cc..7d09cca 100644
--- a/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/device/impl/DeviceManagerTest.java
@@ -27,7 +27,7 @@
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.net.trivial.impl.SimpleDeviceStore;
+import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
import java.util.ArrayList;
import java.util.Iterator;
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
index 54abb84..5ff72a2 100644
--- a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
@@ -40,7 +40,7 @@
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.net.trivial.impl.SimpleFlowRuleStore;
+import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
diff --git a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
index 49ac5b8..0b07380 100644
--- a/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/host/impl/HostManagerTest.java
@@ -34,7 +34,7 @@
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.net.trivial.impl.SimpleHostStore;
+import org.onlab.onos.store.trivial.impl.SimpleHostStore;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
diff --git a/core/net/src/test/java/org/onlab/onos/net/link/impl/LinkManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/link/impl/LinkManagerTest.java
index b78d954..1d52ba3 100644
--- a/core/net/src/test/java/org/onlab/onos/net/link/impl/LinkManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/link/impl/LinkManagerTest.java
@@ -23,7 +23,7 @@
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.event.impl.TestEventDispatcher;
import org.onlab.onos.net.device.impl.DeviceManager;
-import org.onlab.onos.net.trivial.impl.SimpleLinkStore;
+import org.onlab.onos.store.trivial.impl.SimpleLinkStore;
import java.util.ArrayList;
import java.util.Iterator;
diff --git a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
index 1a19f96..15b7eca 100644
--- a/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/topology/impl/TopologyManagerTest.java
@@ -24,7 +24,7 @@
import org.onlab.onos.net.topology.TopologyProviderRegistry;
import org.onlab.onos.net.topology.TopologyProviderService;
import org.onlab.onos.net.topology.TopologyService;
-import org.onlab.onos.net.trivial.impl.SimpleTopologyStore;
+import org.onlab.onos.store.trivial.impl.SimpleTopologyStore;
import java.util.ArrayList;
import java.util.List;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/package-info.java
index f4b9710..8d273ac 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/package-info.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/package-info.java
@@ -1,4 +1,4 @@
/**
* Distributed cluster store and messaging subsystem implementation.
*/
-package org.onlab.onos.store.cluster.impl;
\ No newline at end of file
+package org.onlab.onos.store.cluster.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyAdvertisement.java
new file mode 100644
index 0000000..f9e0d63
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyAdvertisement.java
@@ -0,0 +1,49 @@
+package org.onlab.onos.store.cluster.messaging;
+
+import static org.onlab.onos.store.cluster.messaging.MessageSubject.AE_ADVERTISEMENT;
+import java.util.Map;
+
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.store.Timestamp;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Anti-Entropy advertisement message.
+ * <p>
+ * Message to advertise the information this node holds.
+ *
+ * @param <ID> ID type
+ */
+public class AntiEntropyAdvertisement<ID> extends ClusterMessage {
+
+ private final NodeId sender;
+ private final ImmutableMap<ID, Timestamp> advertisement;
+
+ /**
+ * Creates anti-entropy advertisement message.
+ *
+ * @param sender sender of this message
+ * @param advertisement timestamp information of the data sender holds
+ */
+ public AntiEntropyAdvertisement(NodeId sender, Map<ID, Timestamp> advertisement) {
+ super(AE_ADVERTISEMENT);
+ this.sender = sender;
+ this.advertisement = ImmutableMap.copyOf(advertisement);
+ }
+
+ public NodeId sender() {
+ return sender;
+ }
+
+ public ImmutableMap<ID, Timestamp> advertisement() {
+ return advertisement;
+ }
+
+ // Default constructor for serializer
+ protected AntiEntropyAdvertisement() {
+ super(AE_ADVERTISEMENT);
+ this.sender = null;
+ this.advertisement = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyReply.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyReply.java
new file mode 100644
index 0000000..9bc095e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/AntiEntropyReply.java
@@ -0,0 +1,82 @@
+package org.onlab.onos.store.cluster.messaging;
+
+import static org.onlab.onos.store.cluster.messaging.MessageSubject.AE_REPLY;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.store.device.impl.VersionedValue;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Anti-Entropy reply message.
+ * <p>
+ * Message to send in reply to advertisement or another reply.
+ * Suggest to the sender about the more up-to-date data this node has,
+ * and request for more recent data that the receiver has.
+ */
+public class AntiEntropyReply<ID, V extends VersionedValue<?>> extends ClusterMessage {
+
+ private final NodeId sender;
+ private final ImmutableMap<ID, V> suggestion;
+ private final ImmutableSet<ID> request;
+
+ /**
+ * Creates a reply to anti-entropy message.
+ *
+ * @param sender sender of this message
+ * @param suggestion collection of more recent values, sender had
+ * @param request Collection of identifiers
+ */
+ public AntiEntropyReply(NodeId sender,
+ Map<ID, V> suggestion,
+ Set<ID> request) {
+ super(AE_REPLY);
+ this.sender = sender;
+ this.suggestion = ImmutableMap.copyOf(suggestion);
+ this.request = ImmutableSet.copyOf(request);
+ }
+
+ public NodeId sender() {
+ return sender;
+ }
+
+ /**
+ * Returns collection of values, which the recipient of this reply is likely
+ * to be missing or has outdated version.
+ *
+ * @return
+ */
+ public ImmutableMap<ID, V> suggestion() {
+ return suggestion;
+ }
+
+ /**
+ * Returns collection of identifier to request.
+ *
+ * @return collection of identifier to request
+ */
+ public ImmutableSet<ID> request() {
+ return request;
+ }
+
+ /**
+ * Checks if reply contains any suggestion or request.
+ *
+ * @return true if nothing is suggested and requested
+ */
+ public boolean isEmpty() {
+ return suggestion.isEmpty() && request.isEmpty();
+ }
+
+ // Default constructor for serializer
+ protected AntiEntropyReply() {
+ super(AE_REPLY);
+ this.sender = null;
+ this.suggestion = null;
+ this.request = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java
index c7badf2..97cbf1d 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/MessageSubject.java
@@ -15,6 +15,12 @@
LEAVING_MEMBER,
/** Signifies a heart-beat message. */
- ECHO
+ ECHO,
+
+ /** Anti-Entropy advertisement message. */
+ AE_ADVERTISEMENT,
+
+ /** Anti-Entropy reply message. */
+ AE_REPLY,
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/package-info.java
index 5276b0b..326dbe8 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/package-info.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/package-info.java
@@ -1,4 +1,4 @@
/**
* Cluster messaging APIs for the use by the various distributed stores.
*/
-package org.onlab.onos.store.cluster.messaging;
\ No newline at end of file
+package org.onlab.onos.store.cluster.messaging;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
new file mode 100644
index 0000000..301884c
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyAdvertisement.java
@@ -0,0 +1,39 @@
+package org.onlab.onos.store.device.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.cluster.messaging.AntiEntropyAdvertisement;
+
+// TODO DeviceID needs to be changed to something like (ProviderID, DeviceID)
+// TODO: Handle Port as part of these messages, or separate messages for Ports?
+
+public class DeviceAntiEntropyAdvertisement
+ extends AntiEntropyAdvertisement<DeviceId> {
+
+
+ public DeviceAntiEntropyAdvertisement(NodeId sender,
+ Map<DeviceId, Timestamp> advertisement) {
+ super(sender, advertisement);
+ }
+
+ // May need to add ProviderID, etc.
+ public static DeviceAntiEntropyAdvertisement create(
+ NodeId self,
+ Collection<VersionedValue<Device>> localValues) {
+
+ Map<DeviceId, Timestamp> ads = new HashMap<>(localValues.size());
+ for (VersionedValue<Device> e : localValues) {
+ ads.put(e.entity().id(), e.timestamp());
+ }
+ return new DeviceAntiEntropyAdvertisement(self, ads);
+ }
+
+ // For serializer
+ protected DeviceAntiEntropyAdvertisement() {}
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
new file mode 100644
index 0000000..011713e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceAntiEntropyReply.java
@@ -0,0 +1,102 @@
+package org.onlab.onos.store.device.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.cluster.messaging.AntiEntropyReply;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class DeviceAntiEntropyReply
+ extends AntiEntropyReply<DeviceId, VersionedValue<Device>> {
+
+
+ public DeviceAntiEntropyReply(NodeId sender,
+ Map<DeviceId, VersionedValue<Device>> suggestion,
+ Set<DeviceId> request) {
+ super(sender, suggestion, request);
+ }
+
+ /**
+ * Creates a reply to Anti-Entropy advertisement.
+ *
+ * @param advertisement to respond to
+ * @param self node identifier representing local node
+ * @param localValues local values held on this node
+ * @return reply message
+ */
+ public static DeviceAntiEntropyReply reply(
+ DeviceAntiEntropyAdvertisement advertisement,
+ NodeId self,
+ Collection<VersionedValue<Device>> localValues
+ ) {
+
+ ImmutableMap<DeviceId, Timestamp> ads = advertisement.advertisement();
+
+ ImmutableMap.Builder<DeviceId, VersionedValue<Device>>
+ sug = ImmutableMap.builder();
+
+ Set<DeviceId> req = new HashSet<>(ads.keySet());
+
+ for (VersionedValue<Device> e : localValues) {
+ final DeviceId id = e.entity().id();
+ final Timestamp local = e.timestamp();
+ final Timestamp theirs = ads.get(id);
+ if (theirs == null) {
+ // they don't have it, suggest
+ sug.put(id, e);
+ // don't need theirs
+ req.remove(id);
+ } else if (local.compareTo(theirs) < 0) {
+ // they got older one, suggest
+ sug.put(id, e);
+ // don't need theirs
+ req.remove(id);
+ } else if (local.equals(theirs)) {
+ // same, don't need theirs
+ req.remove(id);
+ }
+ }
+
+ return new DeviceAntiEntropyReply(self, sug.build(), req);
+ }
+
+ /**
+ * Creates a reply to request for values held locally.
+ *
+ * @param requests message containing the request
+ * @param self node identifier representing local node
+ * @param localValues local valeds held on this node
+ * @return reply message
+ */
+ public static DeviceAntiEntropyReply reply(
+ DeviceAntiEntropyReply requests,
+ NodeId self,
+ Map<DeviceId, VersionedValue<Device>> localValues
+ ) {
+
+ Set<DeviceId> reqs = requests.request();
+
+ Map<DeviceId, VersionedValue<Device>> requested = new HashMap<>(reqs.size());
+ for (DeviceId id : reqs) {
+ final VersionedValue<Device> value = localValues.get(id);
+ if (value != null) {
+ requested.put(id, value);
+ }
+ }
+
+ Set<DeviceId> empty = ImmutableSet.of();
+ return new DeviceAntiEntropyReply(self, requested, empty);
+ }
+
+ // For serializer
+ protected DeviceAntiEntropyReply() {}
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
index bd5f2fd..d912983 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
@@ -40,6 +40,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.onos.net.device.DeviceEvent.Type.*;
@@ -59,8 +60,8 @@
public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
- private ConcurrentHashMap<DeviceId, VersionedValue<Device>> devices;
- private ConcurrentHashMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts;
+ private ConcurrentMap<DeviceId, VersionedValue<Device>> devices;
+ private ConcurrentMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClockService clockService;
@@ -191,7 +192,7 @@
}
@Override
- public List<DeviceEvent> updatePorts(DeviceId deviceId,
+ public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
List<PortDescription> portDescriptions) {
List<DeviceEvent> events = new ArrayList<>();
synchronized (this) {
@@ -295,7 +296,7 @@
}
@Override
- public DeviceEvent updatePortStatus(DeviceId deviceId,
+ public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
PortDescription portDescription) {
VersionedValue<Device> device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/VersionedValue.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/VersionedValue.java
index 1a85c53..a0f485a 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/VersionedValue.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/VersionedValue.java
@@ -1,5 +1,7 @@
package org.onlab.onos.store.device.impl;
+import java.util.Objects;
+
import org.onlab.onos.store.Timestamp;
/**
@@ -42,4 +44,35 @@
public Timestamp timestamp() {
return timestamp;
}
+
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(entity, timestamp, isUp);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ VersionedValue<T> that = (VersionedValue<T>) obj;
+ return Objects.equals(this.entity, that.entity) &&
+ Objects.equals(this.timestamp, that.timestamp) &&
+ Objects.equals(this.isUp, that.isUp);
+ }
+
+ // Default constructor for serializer
+ protected VersionedValue() {
+ this.entity = null;
+ this.isUp = false;
+ this.timestamp = null;
+ }
}
diff --git a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
index 50c5f08..18e6e96 100644
--- a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
+++ b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
@@ -123,6 +123,12 @@
return null;
}
+ @Override
+ public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
private class RemoteMasterShipEventHandler extends RemoteCacheEventHandler<DeviceId, NodeId> {
public RemoteMasterShipEventHandler(LoadingCache<DeviceId, Optional<NodeId>> cache) {
super(cache);
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
index a3d340b..5feb1ba 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
@@ -221,7 +221,7 @@
}
@Override
- public List<DeviceEvent> updatePorts(DeviceId deviceId,
+ public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
List<PortDescription> portDescriptions) {
List<DeviceEvent> events = new ArrayList<>();
synchronized (this) {
@@ -319,7 +319,7 @@
}
@Override
- public DeviceEvent updatePortStatus(DeviceId deviceId,
+ public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
PortDescription portDescription) {
synchronized (this) {
Device device = devices.getUnchecked(deviceId).orNull();
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index 5a5592a..6ec7c51 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -28,7 +28,7 @@
import com.google.common.collect.Multimap;
/**
- * Manages inventory of flow rules using trivial in-memory implementation.
+ * TEMPORARY: Manages inventory of flow rules using distributed store implementation.
*/
//FIXME: I LIE I AM NOT DISTRIBUTED
@Component(immediate = true)
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/package-info.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/package-info.java
new file mode 100644
index 0000000..f09ac11
--- /dev/null
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Implementation of flow store using Hazelcast distributed structures.
+ */
+package org.onlab.onos.store.flow.impl;
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
index 09820f4..5c706e6 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
@@ -39,8 +39,8 @@
import com.google.common.collect.Sets;
/**
- * Manages inventory of end-station hosts using trivial in-memory
- * implementation.
+ * TEMPORARY: Manages inventory of end-station hosts using distributed
+ * structures implementation.
*/
//FIXME: I LIE I AM NOT DISTRIBUTED
@Component(immediate = true)
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/package-info.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/package-info.java
new file mode 100644
index 0000000..2a9998a
--- /dev/null
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/host/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Implementation of host store using Hazelcast distributed structures.
+ */
+package org.onlab.onos.store.host.impl;
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
index 567861e..4728850 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/DistributedTopologyStore.java
@@ -28,7 +28,7 @@
import org.slf4j.Logger;
/**
- * Manages inventory of topology snapshots using trivial in-memory
+ * TEMPORARY: Manages inventory of topology snapshots using distributed
* structures implementation.
*/
//FIXME: I LIE I AM NOT DISTRIBUTED
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/package-info.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/package-info.java
new file mode 100644
index 0000000..28b7704
--- /dev/null
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/topology/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Implementation of topology store using Hazelcast distributed structures.
+ */
+package org.onlab.onos.store.topology.impl;
diff --git a/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java b/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
index 97b9ebe..80c9464 100644
--- a/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
+++ b/core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
@@ -201,7 +201,7 @@
new DefaultPortDescription(P2, true)
);
- List<DeviceEvent> events = deviceStore.updatePorts(DID1, pds);
+ List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
for (DeviceEvent event : events) {
@@ -220,7 +220,7 @@
new DefaultPortDescription(P3, true)
);
- events = deviceStore.updatePorts(DID1, pds2);
+ events = deviceStore.updatePorts(PID, DID1, pds2);
assertFalse("event should be triggered", events.isEmpty());
for (DeviceEvent event : events) {
PortNumber num = event.port().number();
@@ -243,7 +243,7 @@
new DefaultPortDescription(P1, false),
new DefaultPortDescription(P2, true)
);
- events = deviceStore.updatePorts(DID1, pds3);
+ events = deviceStore.updatePorts(PID, DID1, pds3);
assertFalse("event should be triggered", events.isEmpty());
for (DeviceEvent event : events) {
PortNumber num = event.port().number();
@@ -268,9 +268,9 @@
List<PortDescription> pds = Arrays.<PortDescription>asList(
new DefaultPortDescription(P1, true)
);
- deviceStore.updatePorts(DID1, pds);
+ deviceStore.updatePorts(PID, DID1, pds);
- DeviceEvent event = deviceStore.updatePortStatus(DID1,
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
new DefaultPortDescription(P1, false));
assertEquals(PORT_UPDATED, event.type());
assertDevice(DID1, SW1, event.subject());
@@ -286,7 +286,7 @@
new DefaultPortDescription(P1, true),
new DefaultPortDescription(P2, true)
);
- deviceStore.updatePorts(DID1, pds);
+ deviceStore.updatePorts(PID, DID1, pds);
Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
List<Port> ports = deviceStore.getPorts(DID1);
@@ -309,7 +309,7 @@
new DefaultPortDescription(P1, true),
new DefaultPortDescription(P2, false)
);
- deviceStore.updatePorts(DID1, pds);
+ deviceStore.updatePorts(PID, DID1, pds);
Port port1 = deviceStore.getPort(DID1, P1);
assertEquals(P1, port1.number());
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
new file mode 100644
index 0000000..244cc57
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
@@ -0,0 +1,49 @@
+package org.onlab.onos.store.serializers;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.onlab.util.KryoPool.FamilySerializer;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.serializers.MapSerializer;
+import com.google.common.collect.ImmutableMap;
+
+/**
+* Kryo Serializer for {@link ImmutableMap}.
+*/
+public class ImmutableMapSerializer extends FamilySerializer<ImmutableMap<?, ?>> {
+
+ private final MapSerializer mapSerializer = new MapSerializer();
+
+ public ImmutableMapSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, ImmutableMap<?, ?> object) {
+ // wrapping with unmodifiableMap proxy
+ // to avoid Kryo from writing only the reference marker of this instance,
+ // which will be embedded right before this method call.
+ kryo.writeObject(output, Collections.unmodifiableMap(object), mapSerializer);
+ }
+
+ @Override
+ public ImmutableMap<?, ?> read(Kryo kryo, Input input,
+ Class<ImmutableMap<?, ?>> type) {
+ Map<?, ?> map = kryo.readObject(input, HashMap.class, mapSerializer);
+ return ImmutableMap.copyOf(map);
+ }
+
+ @Override
+ public void registerFamilies(Kryo kryo) {
+ kryo.register(ImmutableMap.of().getClass(), this);
+ kryo.register(ImmutableMap.of(1, 2).getClass(), this);
+ kryo.register(ImmutableMap.of(1, 2, 3, 4).getClass(), this);
+ // TODO register required ImmutableMap variants
+ }
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
new file mode 100644
index 0000000..c08bf9a
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
@@ -0,0 +1,45 @@
+package org.onlab.onos.store.serializers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.util.KryoPool.FamilySerializer;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.serializers.CollectionSerializer;
+import com.google.common.collect.ImmutableSet;
+
+/**
+* Kryo Serializer for {@link ImmutableSet}.
+*/
+public class ImmutableSetSerializer extends FamilySerializer<ImmutableSet<?>> {
+
+ private final CollectionSerializer serializer = new CollectionSerializer();
+
+ public ImmutableSetSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, ImmutableSet<?> object) {
+ kryo.writeObject(output, object.asList(), serializer);
+ }
+
+ @Override
+ public ImmutableSet<?> read(Kryo kryo, Input input,
+ Class<ImmutableSet<?>> type) {
+ List<?> elms = kryo.readObject(input, ArrayList.class, serializer);
+ return ImmutableSet.copyOf(elms);
+ }
+
+ @Override
+ public void registerFamilies(Kryo kryo) {
+ kryo.register(ImmutableSet.of().getClass(), this);
+ kryo.register(ImmutableSet.of(1).getClass(), this);
+ kryo.register(ImmutableSet.of(1, 2).getClass(), this);
+ // TODO register required ImmutableSet variants
+ }
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
index 84e1b73..4b8410d 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
@@ -1,6 +1,7 @@
package org.onlab.onos.store.serializers;
import java.net.URI;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
@@ -100,4 +101,14 @@
return serializerPool.deserialize(bytes);
}
+ @Override
+ public void serialize(Object obj, ByteBuffer buffer) {
+ serializerPool.serialize(obj, buffer);
+ }
+
+ @Override
+ public <T> T deserialize(ByteBuffer buffer) {
+ return serializerPool.deserialize(buffer);
+ }
+
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java
index e92cc4b..385128c 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationService.java
@@ -1,5 +1,7 @@
package org.onlab.onos.store.serializers;
+import java.nio.ByteBuffer;
+
// TODO: To be replaced with SerializationService from IOLoop activity
/**
* Service to serialize Objects into byte array.
@@ -16,6 +18,15 @@
public byte[] serialize(final Object obj);
/**
+ * Serializes the specified object into bytes using one of the
+ * pre-registered serializers.
+ *
+ * @param obj object to be serialized
+ * @param buffer to write serialized bytes
+ */
+ public void serialize(final Object obj, ByteBuffer buffer);
+
+ /**
* Deserializes the specified bytes into an object using one of the
* pre-registered serializers.
*
@@ -24,4 +35,12 @@
*/
public <T> T deserialize(final byte[] bytes);
+ /**
+ * Deserializes the specified bytes into an object using one of the
+ * pre-registered serializers.
+ *
+ * @param buffer bytes to be deserialized
+ * @return deserialized object
+ */
+ public <T> T deserialize(final ByteBuffer buffer);
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
new file mode 100644
index 0000000..3903491
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
@@ -0,0 +1,26 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.net.MastershipRole;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link org.onlab.onos.net.MastershipRole}.
+ */
+public class MastershipRoleSerializer extends Serializer<MastershipRole> {
+
+ @Override
+ public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) {
+ final String role = kryo.readObject(input, String.class);
+ return MastershipRole.valueOf(role);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, MastershipRole object) {
+ kryo.writeObject(output, object.toString());
+ }
+
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
new file mode 100644
index 0000000..a5d6198
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
@@ -0,0 +1,29 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link org.onlab.onos.cluster.MastershipTerm}.
+ */
+public class MastershipTermSerializer extends Serializer<MastershipTerm> {
+
+ @Override
+ public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) {
+ final NodeId node = new NodeId(kryo.readObject(input, String.class));
+ final int term = input.readInt();
+ return MastershipTerm.of(node, term);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, MastershipTerm object) {
+ output.writeString(object.master().toString());
+ output.writeInt(object.termNumber());
+ }
+
+}
diff --git a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
new file mode 100644
index 0000000..c972d1a
--- /dev/null
+++ b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
@@ -0,0 +1,133 @@
+package org.onlab.onos.store.serializers;
+
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.PortNumber.portNumber;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultDevice;
+import org.onlab.onos.net.DefaultLink;
+import org.onlab.onos.net.DefaultPort;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.KryoPool;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
+
+import de.javakaffee.kryoserializers.URISerializer;
+
+public class KryoSerializerTests {
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final DeviceId DID1 = deviceId("of:foo");
+ private static final DeviceId DID2 = deviceId("of:bar");
+ private static final PortNumber P1 = portNumber(1);
+ private static final PortNumber P2 = portNumber(2);
+ private static final ConnectPoint CP1 = new ConnectPoint(DID1, P1);
+ private static final ConnectPoint CP2 = new ConnectPoint(DID2, P2);
+ private static final String MFR = "whitebox";
+ private static final String HW = "1.1.x";
+ private static final String SW1 = "3.8.1";
+ private static final String SW2 = "3.9.5";
+ private static final String SN = "43311-12345";
+ private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN);
+
+ private static KryoPool kryos;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ kryos = KryoPool.newBuilder()
+ .register(
+ ArrayList.class,
+ HashMap.class
+ )
+ .register(
+ Device.Type.class,
+ Link.Type.class
+
+// ControllerNode.State.class,
+// DefaultControllerNode.class,
+// MastershipRole.class,
+// Port.class,
+// Element.class,
+ )
+ .register(ConnectPoint.class, new ConnectPointSerializer())
+ .register(DefaultLink.class, new DefaultLinkSerializer())
+ .register(DefaultPort.class, new DefaultPortSerializer())
+ .register(DeviceId.class, new DeviceIdSerializer())
+ .register(ImmutableMap.class, new ImmutableMapSerializer())
+ .register(ImmutableSet.class, new ImmutableSetSerializer())
+ .register(IpPrefix.class, new IpPrefixSerializer())
+ .register(LinkKey.class, new LinkKeySerializer())
+ .register(NodeId.class, new NodeIdSerializer())
+ .register(PortNumber.class, new PortNumberSerializer())
+ .register(ProviderId.class, new ProviderIdSerializer())
+
+ .register(DefaultDevice.class)
+
+ .register(URI.class, new URISerializer())
+
+ .register(MastershipRole.class, new MastershipRoleSerializer())
+ .register(MastershipTerm.class, new MastershipTermSerializer())
+ .build();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // removing Kryo instance to use fresh Kryo on each tests
+ kryos.getKryo();
+ }
+
+ private static <T> void testSerialized(T original) {
+ ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ kryos.serialize(original, buffer);
+ buffer.flip();
+ T copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(original, copy)
+ .testEquals();
+ }
+
+
+ @Test
+ public final void test() {
+ testSerialized(new ConnectPoint(DID1, P1));
+ testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT));
+ testSerialized(new DefaultPort(DEV1, P1, true));
+ testSerialized(DID1);
+ testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1));
+ testSerialized(ImmutableMap.of(DID1, DEV1));
+ testSerialized(ImmutableMap.of());
+ testSerialized(ImmutableSet.of(DID1, DID2));
+ testSerialized(ImmutableSet.of(DID1));
+ testSerialized(ImmutableSet.of());
+ testSerialized(IpPrefix.valueOf("192.168.0.1/24"));
+ testSerialized(new LinkKey(CP1, CP2));
+ testSerialized(new NodeId("SomeNodeIdentifier"));
+ testSerialized(P1);
+ testSerialized(PID);
+ }
+
+}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java b/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java
deleted file mode 100644
index 61dbe61..0000000
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package org.onlab.onos.net.trivial.impl;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-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.Service;
-import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.MastershipEvent;
-import org.onlab.onos.cluster.MastershipStore;
-import org.onlab.onos.cluster.MastershipStoreDelegate;
-import org.onlab.onos.cluster.MastershipTerm;
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.store.AbstractStore;
-import org.onlab.packet.IpPrefix;
-import org.slf4j.Logger;
-
-import static org.onlab.onos.cluster.MastershipEvent.Type.*;
-
-/**
- * Manages inventory of controller mastership over devices using
- * trivial, non-distributed in-memory structures implementation.
- */
-@Component(immediate = true)
-@Service
-public class SimpleMastershipStore
- extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
- implements MastershipStore {
-
- private final Logger log = getLogger(getClass());
-
- public static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1");
-
- private ControllerNode instance =
- new DefaultControllerNode(new NodeId("local"), LOCALHOST);
-
- //devices mapped to their masters, to emulate multiple nodes
- protected final ConcurrentMap<DeviceId, NodeId> masterMap =
- new ConcurrentHashMap<>();
- protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
-
- @Activate
- public void activate() {
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- @Override
- public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
-
- NodeId node = masterMap.get(deviceId);
- if (node == null) {
- synchronized (this) {
- masterMap.put(deviceId, nodeId);
- termMap.put(deviceId, new AtomicInteger());
- }
- return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
- }
-
- if (node.equals(nodeId)) {
- return null;
- } else {
- synchronized (this) {
- masterMap.put(deviceId, nodeId);
- termMap.get(deviceId).incrementAndGet();
- return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
- }
- }
- }
-
- @Override
- public NodeId getMaster(DeviceId deviceId) {
- return masterMap.get(deviceId);
- }
-
- @Override
- public Set<DeviceId> getDevices(NodeId nodeId) {
- Set<DeviceId> ids = new HashSet<>();
- for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
- if (d.getValue().equals(nodeId)) {
- ids.add(d.getKey());
- }
- }
- return Collections.unmodifiableSet(ids);
- }
-
- @Override
- public MastershipRole requestRole(DeviceId deviceId) {
- return getRole(instance.id(), deviceId);
- }
-
- @Override
- public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
- NodeId node = masterMap.get(deviceId);
- MastershipRole role;
- if (node != null) {
- if (node.equals(nodeId)) {
- role = MastershipRole.MASTER;
- } else {
- role = MastershipRole.STANDBY;
- }
- } else {
- //masterMap doesn't contain it.
- role = MastershipRole.MASTER;
- masterMap.put(deviceId, nodeId);
- }
- return role;
- }
-
- @Override
- public MastershipTerm getTermFor(DeviceId deviceId) {
- if (masterMap.get(deviceId) == null) {
- return null;
- }
- return MastershipTerm.of(
- masterMap.get(deviceId), termMap.get(deviceId).get());
- }
-
-}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/DefaultTopology.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java
similarity index 99%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/DefaultTopology.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java
index e65ad08..e85e091 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/DefaultTopology.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/DefaultTopologyGraph.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopologyGraph.java
similarity index 94%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/DefaultTopologyGraph.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopologyGraph.java
index 401dfd2..b7b721d 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/DefaultTopologyGraph.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopologyGraph.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import org.onlab.graph.AdjacencyListsGraph;
import org.onlab.onos.net.topology.TopologyEdge;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/NoOpClockService.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
similarity index 94%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/NoOpClockService.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
index 88fcddf..b3f8320 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/NoOpClockService.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/PathKey.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/PathKey.java
similarity index 95%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/PathKey.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/PathKey.java
index da3b055..7169290 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/PathKey.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/PathKey.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import org.onlab.onos.net.DeviceId;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleClusterStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleClusterStore.java
similarity index 97%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleClusterStore.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleClusterStore.java
index 2208c86..1363fd0 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleClusterStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleClusterStore.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import com.google.common.collect.ImmutableSet;
import org.apache.felix.scr.annotations.Activate;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
similarity index 97%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
index 7c7d38f..df20b2d 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
@@ -143,7 +143,7 @@
}
@Override
- public List<DeviceEvent> updatePorts(DeviceId deviceId,
+ public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
List<PortDescription> portDescriptions) {
List<DeviceEvent> events = new ArrayList<>();
synchronized (this) {
@@ -221,7 +221,7 @@
}
@Override
- public DeviceEvent updatePortStatus(DeviceId deviceId,
+ public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
PortDescription portDescription) {
synchronized (this) {
Device device = devices.get(deviceId);
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
similarity index 98%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
index 6b6c157..2f43211 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
similarity index 99%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
index 94a3f05..92d6a22 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
similarity index 98%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index 319df89..a0e569d 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java
new file mode 100644
index 0000000..0439d79
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStore.java
@@ -0,0 +1,217 @@
+package org.onlab.onos.store.trivial.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+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.Service;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipEvent;
+import org.onlab.onos.cluster.MastershipStore;
+import org.onlab.onos.cluster.MastershipStoreDelegate;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.store.AbstractStore;
+import org.onlab.packet.IpPrefix;
+import org.slf4j.Logger;
+
+import static org.onlab.onos.cluster.MastershipEvent.Type.*;
+
+/**
+ * Manages inventory of controller mastership over devices using
+ * trivial, non-distributed in-memory structures implementation.
+ */
+@Component(immediate = true)
+@Service
+public class SimpleMastershipStore
+ extends AbstractStore<MastershipEvent, MastershipStoreDelegate>
+ implements MastershipStore {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1");
+
+ private ControllerNode instance =
+ new DefaultControllerNode(new NodeId("local"), LOCALHOST);
+
+ //devices mapped to their masters, to emulate multiple nodes
+ protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
+ //emulate backups with pile of nodes
+ protected final Set<NodeId> backups = new HashSet<>();
+ //terms
+ protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
+ MastershipRole role = getRole(nodeId, deviceId);
+
+ synchronized (this) {
+ switch (role) {
+ case MASTER:
+ return null;
+ case STANDBY:
+ masterMap.put(deviceId, nodeId);
+ termMap.get(deviceId).incrementAndGet();
+ backups.add(nodeId);
+ break;
+ case NONE:
+ masterMap.put(deviceId, nodeId);
+ termMap.put(deviceId, new AtomicInteger());
+ backups.add(nodeId);
+ break;
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ return null;
+ }
+ }
+
+ return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
+ }
+
+ @Override
+ public NodeId getMaster(DeviceId deviceId) {
+ return masterMap.get(deviceId);
+ }
+
+ @Override
+ public Set<DeviceId> getDevices(NodeId nodeId) {
+ Set<DeviceId> ids = new HashSet<>();
+ for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
+ if (d.getValue().equals(nodeId)) {
+ ids.add(d.getKey());
+ }
+ }
+ return Collections.unmodifiableSet(ids);
+ }
+
+ @Override
+ public MastershipRole requestRole(DeviceId deviceId) {
+ //query+possible reelection
+ NodeId node = instance.id();
+ MastershipRole role = getRole(node, deviceId);
+
+ switch (role) {
+ case MASTER:
+ break;
+ case STANDBY:
+ synchronized (this) {
+ //try to "re-elect", since we're really not distributed
+ NodeId rel = reelect(node);
+ if (rel == null) {
+ masterMap.put(deviceId, node);
+ termMap.put(deviceId, new AtomicInteger());
+ role = MastershipRole.MASTER;
+ }
+ backups.add(node);
+ }
+ break;
+ case NONE:
+ //first to get to it, say we are master
+ synchronized (this) {
+ masterMap.put(deviceId, node);
+ termMap.put(deviceId, new AtomicInteger());
+ backups.add(node);
+ role = MastershipRole.MASTER;
+ }
+ break;
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ }
+ return role;
+ }
+
+ @Override
+ public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
+ //just query
+ NodeId current = masterMap.get(deviceId);
+ MastershipRole role;
+
+ if (current == null) {
+ if (backups.contains(nodeId)) {
+ role = MastershipRole.STANDBY;
+ } else {
+ role = MastershipRole.NONE;
+ }
+ } else {
+ if (current.equals(nodeId)) {
+ role = MastershipRole.MASTER;
+ } else {
+ role = MastershipRole.STANDBY;
+ }
+ }
+ return role;
+ }
+
+ @Override
+ public MastershipTerm getTermFor(DeviceId deviceId) {
+ if ((masterMap.get(deviceId) == null) ||
+ (termMap.get(deviceId) == null)) {
+ return null;
+ }
+ return MastershipTerm.of(
+ masterMap.get(deviceId), termMap.get(deviceId).get());
+ }
+
+ @Override
+ public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
+ MastershipRole role = getRole(nodeId, deviceId);
+ synchronized (this) {
+ switch (role) {
+ case MASTER:
+ NodeId backup = reelect(nodeId);
+ if (backup == null) {
+ masterMap.remove(deviceId);
+ } else {
+ masterMap.put(deviceId, backup);
+ termMap.get(deviceId).incrementAndGet();
+ return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
+ }
+ case STANDBY:
+ case NONE:
+ if (!termMap.containsKey(deviceId)) {
+ termMap.put(deviceId, new AtomicInteger());
+ }
+ backups.add(nodeId);
+ break;
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ }
+ }
+ return null;
+ }
+
+ //dumbly selects next-available node that's not the current one
+ //emulate leader election
+ private NodeId reelect(NodeId nodeId) {
+ NodeId backup = null;
+ for (NodeId n : backups) {
+ if (!n.equals(nodeId)) {
+ backup = n;
+ break;
+ }
+ }
+ return backup;
+ }
+
+}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java
similarity index 98%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java
index 32cc1f7..4e7d5ed 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleTopologyStore.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/package-info.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/package-info.java
similarity index 73%
rename from core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/package-info.java
rename to core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/package-info.java
index 9e3f28f..f697a87 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/package-info.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/package-info.java
@@ -2,4 +2,4 @@
* Implementations of in-memory stores suitable for unit testing and
* experimentation; not for production use.
*/
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/DefaultTopologyTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/DefaultTopologyTest.java
similarity index 98%
rename from core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/DefaultTopologyTest.java
rename to core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/DefaultTopologyTest.java
index 57f4d78..ef383c8 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/DefaultTopologyTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/DefaultTopologyTest.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
similarity index 96%
rename from core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStoreTest.java
rename to core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
index f973d9b..42dc7c3 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStoreTest.java
@@ -1,7 +1,7 @@
/**
*
*/
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import static org.junit.Assert.*;
import static org.onlab.onos.net.Device.Type.SWITCH;
@@ -182,7 +182,7 @@
new DefaultPortDescription(P2, true)
);
- List<DeviceEvent> events = deviceStore.updatePorts(DID1, pds);
+ List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
for (DeviceEvent event : events) {
@@ -201,7 +201,7 @@
new DefaultPortDescription(P3, true)
);
- events = deviceStore.updatePorts(DID1, pds2);
+ events = deviceStore.updatePorts(PID, DID1, pds2);
assertFalse("event should be triggered", events.isEmpty());
for (DeviceEvent event : events) {
PortNumber num = event.port().number();
@@ -224,7 +224,7 @@
new DefaultPortDescription(P1, false),
new DefaultPortDescription(P2, true)
);
- events = deviceStore.updatePorts(DID1, pds3);
+ events = deviceStore.updatePorts(PID, DID1, pds3);
assertFalse("event should be triggered", events.isEmpty());
for (DeviceEvent event : events) {
PortNumber num = event.port().number();
@@ -249,9 +249,9 @@
List<PortDescription> pds = Arrays.<PortDescription>asList(
new DefaultPortDescription(P1, true)
);
- deviceStore.updatePorts(DID1, pds);
+ deviceStore.updatePorts(PID, DID1, pds);
- DeviceEvent event = deviceStore.updatePortStatus(DID1,
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
new DefaultPortDescription(P1, false));
assertEquals(PORT_UPDATED, event.type());
assertDevice(DID1, SW1, event.subject());
@@ -267,7 +267,7 @@
new DefaultPortDescription(P1, true),
new DefaultPortDescription(P2, true)
);
- deviceStore.updatePorts(DID1, pds);
+ deviceStore.updatePorts(PID, DID1, pds);
Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
List<Port> ports = deviceStore.getPorts(DID1);
@@ -290,7 +290,7 @@
new DefaultPortDescription(P1, true),
new DefaultPortDescription(P2, false)
);
- deviceStore.updatePorts(DID1, pds);
+ deviceStore.updatePorts(PID, DID1, pds);
Port port1 = deviceStore.getPort(DID1, P1);
assertEquals(P1, port1.number());
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
similarity index 99%
rename from core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkStoreTest.java
rename to core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
index 50d0e47..eb4a312 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/net/trivial/impl/SimpleLinkStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.net.trivial.impl;
+package org.onlab.onos.store.trivial.impl;
import static org.junit.Assert.*;
import static org.onlab.onos.net.DeviceId.deviceId;
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java
new file mode 100644
index 0000000..32d3d68
--- /dev/null
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleMastershipStoreTest.java
@@ -0,0 +1,160 @@
+package org.onlab.onos.store.trivial.impl;
+
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.DeviceId;
+
+import com.google.common.collect.Sets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.onlab.onos.net.MastershipRole.*;
+import static org.onlab.onos.cluster.MastershipEvent.Type.*;
+
+/**
+ * Test for the simple MastershipStore implementation.
+ */
+public class SimpleMastershipStoreTest {
+
+ private static final DeviceId DID1 = DeviceId.deviceId("of:01");
+ private static final DeviceId DID2 = DeviceId.deviceId("of:02");
+ private static final DeviceId DID3 = DeviceId.deviceId("of:03");
+ private static final DeviceId DID4 = DeviceId.deviceId("of:04");
+
+ private static final NodeId N1 = new NodeId("local");
+ private static final NodeId N2 = new NodeId("other");
+
+ private SimpleMastershipStore sms;
+
+ @Before
+ public void setUp() throws Exception {
+ sms = new SimpleMastershipStore();
+ sms.activate();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ sms.deactivate();
+ }
+
+ @Test
+ public void getRole() {
+ //special case, no backup or master
+ put(DID1, N1, false, false);
+ assertEquals("wrong role", NONE, sms.getRole(N1, DID1));
+
+ //backup exists but we aren't mapped
+ put(DID2, N1, false, true);
+ assertEquals("wrong role", STANDBY, sms.getRole(N1, DID2));
+
+ //N2 is master
+ put(DID3, N2, true, true);
+ assertEquals("wrong role", MASTER, sms.getRole(N2, DID3));
+
+ //N2 is master but N1 is only in backups set
+ put(DID4, N2, true, false);
+ assertEquals("wrong role", STANDBY, sms.getRole(N1, DID4));
+ }
+
+ @Test
+ public void getMaster() {
+ put(DID3, N2, true, true);
+ assertEquals("wrong role", MASTER, sms.getRole(N2, DID3));
+ assertEquals("wrong device", N2, sms.getMaster(DID3));
+ }
+
+ @Test
+ public void setMaster() {
+ put(DID1, N1, false, false);
+ assertEquals("wrong event", MASTER_CHANGED, sms.setMaster(N1, DID1).type());
+ assertEquals("wrong role", MASTER, sms.getRole(N1, DID1));
+ //set node that's already master - should be ignored
+ assertNull("wrong event", sms.setMaster(N1, DID1));
+
+ //set STANDBY to MASTER
+ put(DID2, N1, false, true);
+ assertEquals("wrong role", STANDBY, sms.getRole(N1, DID2));
+ assertEquals("wrong event", MASTER_CHANGED, sms.setMaster(N1, DID2).type());
+ assertEquals("wrong role", MASTER, sms.getRole(N1, DID2));
+ }
+
+ @Test
+ public void getDevices() {
+ Set<DeviceId> d = Sets.newHashSet(DID1, DID2);
+
+ put(DID1, N2, true, true);
+ put(DID2, N2, true, true);
+ put(DID3, N1, true, true);
+ assertTrue("wrong devices", d.equals(sms.getDevices(N2)));
+ }
+
+ @Test
+ public void getTermFor() {
+ put(DID1, N1, true, true);
+ assertEquals("wrong term", MastershipTerm.of(N1, 0), sms.getTermFor(DID1));
+
+ //switch to N2 and back - 2 term switches
+ sms.setMaster(N2, DID1);
+ sms.setMaster(N1, DID1);
+ assertEquals("wrong term", MastershipTerm.of(N1, 2), sms.getTermFor(DID1));
+ }
+
+ @Test
+ public void requestRole() {
+ //NONE - become MASTER
+ put(DID1, N1, false, false);
+ assertEquals("wrong role", MASTER, sms.requestRole(DID1));
+
+ //STANDBY without backup - become MASTER
+ put(DID2, N1, false, true);
+ assertEquals("wrong role", MASTER, sms.requestRole(DID2));
+
+ //STANDBY with backup - stay STANDBY
+ put(DID3, N2, false, true);
+ assertEquals("wrong role", STANDBY, sms.requestRole(DID3));
+
+ //local (N1) is MASTER - stay MASTER
+ put(DID4, N1, true, true);
+ assertEquals("wrong role", MASTER, sms.requestRole(DID4));
+ }
+
+ @Test
+ public void unsetMaster() {
+ //NONE - record backup but take no other action
+ put(DID1, N1, false, false);
+ sms.unsetMaster(N1, DID1);
+ assertTrue("not backed up", sms.backups.contains(N1));
+ sms.termMap.clear();
+ sms.unsetMaster(N1, DID1);
+ assertTrue("term not set", sms.termMap.containsKey(DID1));
+
+ //no backup, MASTER
+ put(DID1, N1, true, true);
+ assertNull("wrong event", sms.unsetMaster(N1, DID1));
+ assertNull("wrong node", sms.masterMap.get(DID1));
+
+ //backup, switch
+ sms.masterMap.clear();
+ put(DID1, N1, true, true);
+ put(DID2, N2, true, true);
+ assertEquals("wrong event", MASTER_CHANGED, sms.unsetMaster(N1, DID1).type());
+ }
+
+ //helper to populate master/backup structures
+ private void put(DeviceId dev, NodeId node, boolean store, boolean backup) {
+ if (store) {
+ sms.masterMap.put(dev, node);
+ }
+ if (backup) {
+ sms.backups.add(node);
+ }
+ sms.termMap.put(dev, new AtomicInteger());
+ }
+}
diff --git a/pom.xml b/pom.xml
index 5cf67de..3c2140f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -419,7 +419,7 @@
<group>
<title>Core Subsystems</title>
<packages>
- org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.net.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*
+ org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*
</packages>
</group>
<group>
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 8389ff1..1d3097f 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -6,22 +6,21 @@
export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
# Setup some environmental context for developers
-export JAVA_HOME=$(/usr/libexec/java_home)
+export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2}
export KARAF=${KARAF:-~/Applications/apache-karaf-3.0.1}
export KARAF_LOG=$KARAF/data/log/karaf.log
# Setup a path
-export PS=":"
-export PATH="$PATH:$ONOS_ROOT/tools/dev:$ONOS_ROOT/tools/build"
-export PATH="$PATH:$ONOS_ROOT/tools/test/bin"
+export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin"
+export PATH="$PATH:$ONOS_ROOT/tools/build"
export PATH="$PATH:$MAVEN/bin:$KARAF/bin"
export PATH="$PATH:."
# Convenience utility to warp to various ONOS source projects
# e.g. 'o api', 'o dev', 'o'
function o {
- cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target|src' | \
+ cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target' | \
egrep "${1:-$ONOS_ROOT}" | head -n 1)
}
@@ -30,11 +29,12 @@
# Short-hand for ONOS build, package and test.
alias ob='onos-build'
+alias obs='onos-build-selective'
alias op='onos-package'
alias ot='onos-test'
# Short-hand for tailing the ONOS (karaf) log
-alias tl='$ONOS_ROOT/tools/dev/watchLog'
+alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log'
alias tlo='tl | grep --colour=always org.onlab'
# Pretty-print JSON output
diff --git a/tools/dev/bin/onos-build-selective b/tools/dev/bin/onos-build-selective
new file mode 100755
index 0000000..2fe0188
--- /dev/null
+++ b/tools/dev/bin/onos-build-selective
@@ -0,0 +1,12 @@
+#!/bin/bash
+#------------------------------------------------------------------------------
+# Selectively builds only those projects that contained modified Java files.
+#------------------------------------------------------------------------------
+
+projects=$(find $ONOS_ROOT -name '*.java' \
+ -not -path '*/openflowj/*' -and -not -path '.git/*' \
+ -exec $ONOS_ROOT/tools/dev/bin/onos-build-selective-hook {} \; | \
+ sort -u | sed "s:$ONOS_ROOT::g" | tr '\n' ',' | \
+ sed 's:/,:,:g;s:,/:,:g;s:^/::g;s:,$::g')
+
+[ -n "$projects" ] && cd $ONOS_ROOT && mvn --projects $projects ${@:-clean install}
\ No newline at end of file
diff --git a/tools/dev/bin/onos-build-selective-hook b/tools/dev/bin/onos-build-selective-hook
new file mode 100755
index 0000000..233ab1a
--- /dev/null
+++ b/tools/dev/bin/onos-build-selective-hook
@@ -0,0 +1,18 @@
+#------------------------------------------------------------------------------
+# Echoes project-level directory if a Java file within is newer than its
+# class file counterpart
+#------------------------------------------------------------------------------
+
+javaFile=${1#*\/src\/*\/java/}
+basename=${1/*\//}
+
+[ $basename = "package-info.java" ] && exit 0
+
+src=${1/$javaFile/}
+project=${src/src*/}
+classFile=${javaFile/.java/.class}
+
+[ ${project}target/classes/$classFile -nt ${src}$javaFile -o \
+ ${project}target/test-classes/$classFile -nt ${src}$javaFile ] \
+ || echo ${src/src*/}
+
diff --git a/tools/dev/watchLog b/tools/dev/bin/onos-local-log
similarity index 100%
rename from tools/dev/watchLog
rename to tools/dev/bin/onos-local-log
diff --git a/tools/test/cells/.reset b/tools/test/cells/.reset
index c025225..5b1bc2c 100644
--- a/tools/test/cells/.reset
+++ b/tools/test/cells/.reset
@@ -1 +1 @@
-unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN ONOS_NIC
+unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN ONOS_NIC ONOS_FEATURES
diff --git a/utils/misc/src/main/java/org/onlab/util/KryoPool.java b/utils/misc/src/main/java/org/onlab/util/KryoPool.java
index be662a6..3fae0c5 100644
--- a/utils/misc/src/main/java/org/onlab/util/KryoPool.java
+++ b/utils/misc/src/main/java/org/onlab/util/KryoPool.java
@@ -239,12 +239,41 @@
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(registrationRequired);
for (Pair<Class<?>, Serializer<?>> registry : registeredTypes) {
- if (registry.getRight() == null) {
+ final Serializer<?> serializer = registry.getRight();
+ if (serializer == null) {
kryo.register(registry.getLeft());
} else {
- kryo.register(registry.getLeft(), registry.getRight());
+ kryo.register(registry.getLeft(), serializer);
+ if (serializer instanceof FamilySerializer) {
+ FamilySerializer<?> fser = (FamilySerializer<?>) serializer;
+ fser.registerFamilies(kryo);
+ }
}
}
return kryo;
}
+
+ /**
+ * Serializer implementation, which required registration of family of Classes.
+ * @param <T> base type of this serializer.
+ */
+ public abstract static class FamilySerializer<T> extends Serializer<T> {
+
+
+ public FamilySerializer(boolean acceptsNull) {
+ super(acceptsNull);
+ }
+
+ public FamilySerializer(boolean acceptsNull, boolean immutable) {
+ super(acceptsNull, immutable);
+ }
+
+ /**
+ * Registers other classes this Serializer supports.
+ *
+ * @param kryo instance to register classes to
+ */
+ public void registerFamilies(Kryo kryo) {
+ }
+ }
}