Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
index 738fa2a..f7fbdbb 100644
--- a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
+++ b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
@@ -100,6 +100,7 @@
context.block();
return;
}
+
HostId id = HostId.hostId(ethPkt.getDestinationMAC());
// Do we know who this is for? If not, flood and bail.
@@ -112,7 +113,9 @@
// Are we on an edge switch that our destination is on? If so,
// simply forward out to the destination and bail.
if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
- installRule(context, dst.location().port());
+ if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
+ installRule(context, dst.location().port());
+ }
return;
}
@@ -175,21 +178,24 @@
// We don't yet support bufferids in the flowservice so packet out first.
packetOut(context, portNumber);
- // Install the flow rule to handle this type of message from now on.
- Ethernet inPkt = context.inPacket().parsed();
- TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
- builder.matchEthType(inPkt.getEtherType())
- .matchEthSrc(inPkt.getSourceMAC())
- .matchEthDst(inPkt.getDestinationMAC())
- .matchInport(context.inPacket().receivedFrom().port());
+ if (context.inPacket().parsed().getEtherType() == Ethernet.TYPE_IPV4) {
- TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
- treat.setOutput(portNumber);
+ // Install the flow rule to handle this type of message from now on.
+ Ethernet inPkt = context.inPacket().parsed();
+ TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
+ builder.matchEthType(inPkt.getEtherType())
+ .matchEthSrc(inPkt.getSourceMAC())
+ .matchEthDst(inPkt.getDestinationMAC())
+ .matchInport(context.inPacket().receivedFrom().port());
- FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
- builder.build(), treat.build(), 0, appId);
+ TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
+ treat.setOutput(portNumber);
- flowRuleService.applyFlowRules(f);
+ FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
+ builder.build(), treat.build(), 0, appId);
+
+ flowRuleService.applyFlowRules(f);
+ }
}
}
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/AbstractAnnotated.java b/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
new file mode 100644
index 0000000..f3c4b86
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
@@ -0,0 +1,45 @@
+package org.onlab.onos.net;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Base abstraction of an annotated entity.
+ */
+public class AbstractAnnotated implements Annotated {
+
+ private static final Map<String, String> EMPTY = new HashMap<>();
+
+ private final Map<String, String> annotations;
+
+ // For serialization
+ protected AbstractAnnotated() {
+ this.annotations = EMPTY;
+ }
+
+ /**
+ * Creates a new entity, annotated with the specified annotations.
+ *
+ * @param annotations optional key/value annotations map
+ */
+ protected AbstractAnnotated(Map<String, String>[] annotations) {
+ checkArgument(annotations.length <= 1, "Only one set of annotations is expected");
+ this.annotations = annotations.length == 1 ? annotations[0] : EMPTY;
+ }
+
+ @Override
+ public Set<String> annotationKeys() {
+ return ImmutableSet.copyOf(annotations.keySet());
+ }
+
+ @Override
+ public String annotation(String key) {
+ return annotations.get(key);
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java b/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
index 8c25cda..6bdda72 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
@@ -2,10 +2,12 @@
import org.onlab.onos.net.provider.ProviderId;
+import java.util.Map;
+
/**
* Base implementation of a network model entity.
*/
-public class AbstractModel implements Provided {
+public class AbstractModel extends AbstractAnnotated implements Provided {
private final ProviderId providerId;
@@ -15,11 +17,16 @@
}
/**
- * Creates a model entity attributed to the specified provider.
+ * Creates a model entity attributed to the specified provider and
+ * optionally annotated.
*
- * @param providerId identity of the provider
+ * @param providerId identity of the provider
+ * @param annotations optional key/value annotations
*/
- protected AbstractModel(ProviderId providerId) {
+ @SafeVarargs
+ protected AbstractModel(ProviderId providerId,
+ Map<String, String>... annotations) {
+ super(annotations);
this.providerId = providerId;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/Annotated.java b/core/api/src/main/java/org/onlab/onos/net/Annotated.java
new file mode 100644
index 0000000..f68cd46
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/Annotated.java
@@ -0,0 +1,25 @@
+package org.onlab.onos.net;
+
+import java.util.Set;
+
+/**
+ * Represents an entity that carries arbitrary annotations.
+ */
+public interface Annotated {
+
+ /**
+ * Returns the set of annotation keys currently available.
+ *
+ * @return set of annotation keys
+ */
+ Set<String> annotationKeys();
+
+ /**
+ * Returns the annotation value for the specified key.
+ *
+ * @param key annotation key
+ * @return annotation value; null if there is no annotation
+ */
+ String annotation(String key);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/Description.java b/core/api/src/main/java/org/onlab/onos/net/Description.java
index 38338c1..8b5464b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Description.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Description.java
@@ -3,5 +3,5 @@
/**
* Base abstraction of a piece of information about network elements.
*/
-public interface Description {
+public interface Description extends Annotated {
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
index 833625d..f5bc0d6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/device/DefaultDeviceDescription.java
@@ -1,6 +1,9 @@
package org.onlab.onos.net.device;
+import org.onlab.onos.net.AbstractAnnotated;
+
import java.net.URI;
+import java.util.Map;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -9,7 +12,8 @@
/**
* Default implementation of immutable device description entity.
*/
-public class DefaultDeviceDescription implements DeviceDescription {
+public class DefaultDeviceDescription extends AbstractAnnotated
+ implements DeviceDescription {
private final URI uri;
private final Type type;
private final String manufacturer;
@@ -26,10 +30,14 @@
* @param hwVersion device HW version
* @param swVersion device SW version
* @param serialNumber device serial number
+ * @param annotations optional key/value annotations map
*/
+ @SafeVarargs
public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
String hwVersion, String swVersion,
- String serialNumber) {
+ String serialNumber,
+ Map<String, String>... annotations) {
+ super(annotations);
this.uri = checkNotNull(uri, "Device URI cannot be null");
this.type = checkNotNull(type, "Device type cannot be null");
this.manufacturer = manufacturer;
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/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index 65c4a16..f705a94 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -27,9 +27,11 @@
private final ApplicationId appId;
+ private boolean expired;
+
public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
TrafficTreatment treatment, int priority, FlowRuleState state,
- long life, long packets, long bytes, long flowId) {
+ long life, long packets, long bytes, long flowId, boolean expired) {
this.deviceId = deviceId;
this.priority = priority;
this.selector = selector;
@@ -37,7 +39,7 @@
this.state = state;
this.appId = ApplicationId.valueOf((int) (flowId >> 32));
this.id = FlowId.valueOf(flowId);
-
+ this.expired = expired;
this.life = life;
this.packets = packets;
this.bytes = bytes;
@@ -186,4 +188,9 @@
.toString();
}
+ @Override
+ public boolean expired() {
+ return expired;
+ }
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
index 487659b..2728e21 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
@@ -111,4 +111,11 @@
*/
long bytes();
+ /**
+ * Indicates that this flow has expired at the device.
+ *
+ * @return true if it has expired, false otherwise
+ */
+ boolean expired();
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java b/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
index 0a419fd..6190183 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/DefaultHostDescription.java
@@ -1,38 +1,62 @@
package org.onlab.onos.net.host;
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-import java.util.HashSet;
-import java.util.Set;
-
+import com.google.common.collect.ImmutableSet;
+import org.onlab.onos.net.AbstractAnnotated;
import org.onlab.onos.net.HostLocation;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
-import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
-public class DefaultHostDescription implements HostDescription {
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default implementation of an immutable host description.
+ */
+public class DefaultHostDescription extends AbstractAnnotated
+ implements HostDescription {
private final MacAddress mac;
private final VlanId vlan;
private final HostLocation location;
private final Set<IpPrefix> ips;
+ /**
+ * Creates a host description using the supplied information.
+ *
+ * @param mac host MAC address
+ * @param vlan host VLAN identifier
+ * @param location host location
+ * @param annotations optional key/value annotations map
+ */
+ @SafeVarargs
public DefaultHostDescription(MacAddress mac, VlanId vlan,
- HostLocation loc) {
- this.mac = mac;
- this.vlan = vlan;
- this.location = loc;
- this.ips = new HashSet<IpPrefix>();
+ HostLocation location,
+ Map<String, String>... annotations) {
+ this(mac, vlan, location, new HashSet<IpPrefix>(), annotations);
}
+ /**
+ * Creates a host description using the supplied information.
+ *
+ * @param mac host MAC address
+ * @param vlan host VLAN identifier
+ * @param location host location
+ * @param ips of host IP addresses
+ * @param annotations optional key/value annotations map
+ */
+ @SafeVarargs
public DefaultHostDescription(MacAddress mac, VlanId vlan,
- HostLocation loc, Set<IpPrefix> ips) {
+ HostLocation location, Set<IpPrefix> ips,
+ Map<String, String>... annotations) {
+ super(annotations);
this.mac = mac;
this.vlan = vlan;
- this.location = loc;
- this.ips = new HashSet<IpPrefix>(ips);
+ this.location = location;
+ this.ips = new HashSet<>(ips);
}
@Override
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..afaecbe 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
@@ -5,12 +5,35 @@
import static com.google.common.base.MoreObjects.toStringHelper;
/**
- * Notion of provider identity.
+ * External identity of a {@link org.onlab.onos.net.provider.Provider} family.
+ * It also carriers two designations of external characteristics, the URI
+ * scheme and primary/ancillary indicator.
+ * <p/>
+ * The device URI scheme is used to determine applicability of a provider to
+ * operations on a specific device. The ancillary indicator serves to designate
+ * a provider as a primary or ancillary.
+ *
+ * A {@link org.onlab.onos.net.provider.ProviderRegistry} uses this designation
+ * to permit only one primary provider per device URI scheme. Multiple
+ * ancillary providers can register with the same device URI scheme however.
*/
public class ProviderId {
private final String scheme;
private final String id;
+ private final boolean ancillary;
+
+ /**
+ * Creates a new primary 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
+ */
+ public ProviderId(String scheme, String id) {
+ this(scheme, id, false);
+ }
/**
* Creates a new provider identifier from the specified string.
@@ -19,10 +42,12 @@
*
* @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) {
+ public ProviderId(String scheme, String id, boolean ancillary) {
this.scheme = scheme;
this.id = id;
+ this.ancillary = ancillary;
}
/**
@@ -43,6 +68,15 @@
return id;
}
+ /**
+ * Indicates whether this identifier designates an ancillary providers.
+ *
+ * @return true if the provider is ancillary; false if primary
+ */
+ public boolean isAncillary() {
+ return ancillary;
+ }
+
@Override
public int hashCode() {
return Objects.hash(scheme, id);
@@ -56,14 +90,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/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java b/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java
index 94a6abf..1369fda 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/DefaultGraphDescription.java
@@ -2,6 +2,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
+import org.onlab.onos.net.AbstractAnnotated;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
@@ -12,7 +13,8 @@
/**
* Default implementation of an immutable topology graph data carrier.
*/
-public class DefaultGraphDescription implements GraphDescription {
+public class DefaultGraphDescription extends AbstractAnnotated
+ implements GraphDescription {
private final long nanos;
private final ImmutableSet<TopologyVertex> vertexes;
@@ -25,11 +27,16 @@
* Creates a minimal topology graph description to allow core to construct
* and process the topology graph.
*
- * @param nanos time in nanos of when the topology description was created
- * @param devices collection of infrastructure devices
- * @param links collection of infrastructure links
+ * @param nanos time in nanos of when the topology description was created
+ * @param devices collection of infrastructure devices
+ * @param links collection of infrastructure links
+ * @param annotations optional key/value annotations map
*/
- public DefaultGraphDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) {
+ @SafeVarargs
+ public DefaultGraphDescription(long nanos, Iterable<Device> devices,
+ Iterable<Link> links,
+ Map<String, String>... annotations) {
+ super(annotations);
this.nanos = nanos;
this.vertexes = buildVertexes(devices);
this.edges = buildEdges(links);
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/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 89c3399..a6f5ebb 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -161,7 +161,11 @@
switch (stored.state()) {
case ADDED:
case PENDING_ADD:
- frp.applyFlowRule(stored);
+ if (flowRule.expired()) {
+ event = store.removeFlowRule(flowRule);
+ } else {
+ frp.applyFlowRule(stored);
+ }
break;
case PENDING_REMOVE:
case REMOVED:
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index e7e08fc..6e07c3e 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -231,6 +231,7 @@
arp.setOpCode(ARP.OP_REPLY);
arp.setProtocolType(ARP.PROTO_TYPE_IP);
arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
+
arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
arp.setSenderHardwareAddress(h.mac().getAddress());
@@ -238,7 +239,7 @@
arp.setTargetProtocolAddress(((ARP) request.getPayload())
.getSenderProtocolAddress());
- arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toInt());
+ arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toRealInt());
eth.setPayload(arp);
return eth;
}
@@ -291,7 +292,6 @@
case DEVICE_MASTERSHIP_CHANGED:
case DEVICE_SUSPENDED:
case DEVICE_UPDATED:
- case PORT_UPDATED:
// nothing to do in these cases; handled when links get reported
break;
case DEVICE_REMOVED:
@@ -301,9 +301,12 @@
}
break;
case PORT_ADDED:
+ case PORT_UPDATED:
synchronized (externalPorts) {
- externalPorts.put(device, event.port().number());
- internalPorts.remove(device, event.port().number());
+ if (event.port().isEnabled()) {
+ externalPorts.put(device, event.port().number());
+ internalPorts.remove(device, event.port().number());
+ }
}
break;
case PORT_REMOVED:
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/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
index 6ae334b..6accb06 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
@@ -2,6 +2,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.onlab.onos.cluster.DefaultControllerNode;
import org.onlab.onos.cluster.NodeId;
@@ -58,6 +59,7 @@
ccm2.deactivate();
}
+ @Ignore("FIXME: failing randomly?")
@Test
public void connect() throws Exception {
cnd1.latch = new CountDownLatch(1);
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/pom.xml b/core/store/trivial/pom.xml
index 40016d4..b35f6a50 100644
--- a/core/store/trivial/pom.xml
+++ b/core/store/trivial/pom.xml
@@ -25,6 +25,10 @@
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
</dependencies>
<build>
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/net/trivial/impl/SimpleDeviceStore.java
deleted file mode 100644
index 7c7d38f..0000000
--- a/core/store/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package org.onlab.onos.net.trivial.impl;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-
-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.net.DefaultDevice;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.device.DeviceDescription;
-import org.onlab.onos.net.device.DeviceEvent;
-import org.onlab.onos.net.device.DeviceStore;
-import org.onlab.onos.net.device.DeviceStoreDelegate;
-import org.onlab.onos.net.device.PortDescription;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.AbstractStore;
-import org.slf4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Predicates.notNull;
-import static org.onlab.onos.net.device.DeviceEvent.Type.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Manages inventory of infrastructure devices using trivial in-memory
- * structures implementation.
- */
-@Component(immediate = true)
-@Service
-public class SimpleDeviceStore
- extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
- implements DeviceStore {
-
- private final Logger log = getLogger(getClass());
-
- public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
-
- private final Map<DeviceId, DefaultDevice> devices = new ConcurrentHashMap<>();
- private final Set<DeviceId> availableDevices = new HashSet<>();
- private final Map<DeviceId, Map<PortNumber, Port>> devicePorts = new HashMap<>();
-
- @Activate
- public void activate() {
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- @Override
- public int getDeviceCount() {
- return devices.size();
- }
-
- @Override
- public Iterable<Device> getDevices() {
- return Collections.unmodifiableSet(new HashSet<Device>(devices.values()));
- }
-
- @Override
- public Device getDevice(DeviceId deviceId) {
- return devices.get(deviceId);
- }
-
- @Override
- public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
- DeviceDescription deviceDescription) {
- DefaultDevice device = devices.get(deviceId);
- if (device == null) {
- return createDevice(providerId, deviceId, deviceDescription);
- }
- return updateDevice(providerId, device, deviceDescription);
- }
-
- // Creates the device and returns the appropriate event if necessary.
- private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
- DeviceDescription desc) {
- DefaultDevice device = new DefaultDevice(providerId, deviceId, desc.type(),
- desc.manufacturer(),
- desc.hwVersion(), desc.swVersion(),
- desc.serialNumber());
- synchronized (this) {
- devices.put(deviceId, device);
- availableDevices.add(deviceId);
- }
- return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null);
- }
-
- // Updates the device and returns the appropriate event if necessary.
- private DeviceEvent updateDevice(ProviderId providerId, DefaultDevice device,
- DeviceDescription desc) {
- // We allow only certain attributes to trigger update
- if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
- !Objects.equals(device.swVersion(), desc.swVersion())) {
- DefaultDevice updated = new DefaultDevice(providerId, device.id(),
- desc.type(),
- desc.manufacturer(),
- desc.hwVersion(),
- desc.swVersion(),
- desc.serialNumber());
- synchronized (this) {
- devices.put(device.id(), updated);
- availableDevices.add(device.id());
- }
- return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, updated, null);
- }
-
- // Otherwise merely attempt to change availability
- synchronized (this) {
- boolean added = availableDevices.add(device.id());
- return !added ? null :
- new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
- }
- }
-
- @Override
- public DeviceEvent markOffline(DeviceId deviceId) {
- synchronized (this) {
- Device device = devices.get(deviceId);
- boolean removed = device != null && availableDevices.remove(deviceId);
- return !removed ? null :
- new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
- }
- }
-
- @Override
- public List<DeviceEvent> updatePorts(DeviceId deviceId,
- List<PortDescription> portDescriptions) {
- List<DeviceEvent> events = new ArrayList<>();
- synchronized (this) {
- Device device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- Map<PortNumber, Port> ports = getPortMap(deviceId);
-
- // Add new ports
- Set<PortNumber> processed = new HashSet<>();
- for (PortDescription portDescription : portDescriptions) {
- Port port = ports.get(portDescription.portNumber());
- events.add(port == null ?
- createPort(device, portDescription, ports) :
- updatePort(device, port, portDescription, ports));
- processed.add(portDescription.portNumber());
- }
-
- events.addAll(pruneOldPorts(device, ports, processed));
- }
- return FluentIterable.from(events).filter(notNull()).toList();
- }
-
- // Creates a new port based on the port description adds it to the map and
- // Returns corresponding event.
- private DeviceEvent createPort(Device device, PortDescription portDescription,
- Map<PortNumber, Port> ports) {
- DefaultPort port = new DefaultPort(device, portDescription.portNumber(),
- portDescription.isEnabled());
- ports.put(port.number(), port);
- return new DeviceEvent(PORT_ADDED, device, port);
- }
-
- // CHecks if the specified port requires update and if so, it replaces the
- // existing entry in the map and returns corresponding event.
- private DeviceEvent updatePort(Device device, Port port,
- PortDescription portDescription,
- Map<PortNumber, Port> ports) {
- if (port.isEnabled() != portDescription.isEnabled()) {
- DefaultPort updatedPort =
- new DefaultPort(device, portDescription.portNumber(),
- portDescription.isEnabled());
- ports.put(port.number(), updatedPort);
- return new DeviceEvent(PORT_UPDATED, device, updatedPort);
- }
- return null;
- }
-
- // Prunes the specified list of ports based on which ports are in the
- // processed list and returns list of corresponding events.
- private List<DeviceEvent> pruneOldPorts(Device device,
- Map<PortNumber, Port> ports,
- Set<PortNumber> processed) {
- List<DeviceEvent> events = new ArrayList<>();
- Iterator<PortNumber> iterator = ports.keySet().iterator();
- while (iterator.hasNext()) {
- PortNumber portNumber = iterator.next();
- if (!processed.contains(portNumber)) {
- events.add(new DeviceEvent(PORT_REMOVED, device,
- ports.get(portNumber)));
- iterator.remove();
- }
- }
- return events;
- }
-
- // Gets the map of ports for the specified device; if one does not already
- // exist, it creates and registers a new one.
- private Map<PortNumber, Port> getPortMap(DeviceId deviceId) {
- Map<PortNumber, Port> ports = devicePorts.get(deviceId);
- if (ports == null) {
- ports = new HashMap<>();
- devicePorts.put(deviceId, ports);
- }
- return ports;
- }
-
- @Override
- public DeviceEvent updatePortStatus(DeviceId deviceId,
- PortDescription portDescription) {
- synchronized (this) {
- Device device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- Map<PortNumber, Port> ports = getPortMap(deviceId);
- Port port = ports.get(portDescription.portNumber());
- return updatePort(device, port, portDescription, ports);
- }
- }
-
- @Override
- public List<Port> getPorts(DeviceId deviceId) {
- Map<PortNumber, Port> ports = devicePorts.get(deviceId);
- return ports == null ? new ArrayList<Port>() : ImmutableList.copyOf(ports.values());
- }
-
- @Override
- public Port getPort(DeviceId deviceId, PortNumber portNumber) {
- Map<PortNumber, Port> ports = devicePorts.get(deviceId);
- return ports == null ? null : ports.get(portNumber);
- }
-
- @Override
- public boolean isAvailable(DeviceId deviceId) {
- return availableDevices.contains(deviceId);
- }
-
- @Override
- public DeviceEvent removeDevice(DeviceId deviceId) {
- synchronized (this) {
- Device device = devices.remove(deviceId);
- return device == null ? null :
- new DeviceEvent(DEVICE_REMOVED, device, null);
- }
- }
-}
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/store/trivial/impl/SimpleDeviceStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
new file mode 100644
index 0000000..bc0a055
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleDeviceStore.java
@@ -0,0 +1,455 @@
+package org.onlab.onos.store.trivial.impl;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+import org.apache.commons.lang3.concurrent.ConcurrentException;
+import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+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.net.DefaultDevice;
+import org.onlab.onos.net.DefaultPort;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.Device.Type;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceStore;
+import org.onlab.onos.net.device.DeviceStoreDelegate;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.AbstractStore;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.notNull;
+import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
+
+// TODO: synchronization should be done in more fine-grained manner.
+/**
+ * Manages inventory of infrastructure devices using trivial in-memory
+ * structures implementation.
+ */
+@Component(immediate = true)
+@Service
+public class SimpleDeviceStore
+ extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
+ implements DeviceStore {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
+
+ // collection of Description given from various providers
+ private final ConcurrentMap<DeviceId,
+ ConcurrentMap<ProviderId, DeviceDescriptions>>
+ deviceDescs = new ConcurrentHashMap<>();
+
+ // cache of Device and Ports generated by compositing descriptions from providers
+ private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
+
+ // available(=UP) devices
+ private final Set<DeviceId> availableDevices = new HashSet<>();
+
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return devices.size();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return Collections.unmodifiableCollection(devices.values());
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ return devices.get(deviceId);
+ }
+
+ @Override
+ public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
+ DeviceDescription deviceDescription) {
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
+ = createIfAbsentUnchecked(deviceDescs, deviceId,
+ new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
+
+ Device oldDevice = devices.get(deviceId);
+
+ DeviceDescriptions descs
+ = createIfAbsentUnchecked(providerDescs, providerId,
+ new InitDeviceDescs(deviceDescription));
+
+ descs.putDeviceDesc(deviceDescription);
+
+ Device newDevice = composeDevice(deviceId, providerDescs);
+
+ if (oldDevice == null) {
+ // ADD
+ return createDevice(providerId, newDevice);
+ } else {
+ // UPDATE or ignore (no change or stale)
+ return updateDevice(providerId, oldDevice, newDevice);
+ }
+ }
+
+ // Creates the device and returns the appropriate event if necessary.
+ private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
+
+ // update composed device cache
+ synchronized (this) {
+ devices.putIfAbsent(newDevice.id(), newDevice);
+ if (!providerId.isAncillary()) {
+ availableDevices.add(newDevice.id());
+ }
+ }
+
+ return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
+ }
+
+ // Updates the device and returns the appropriate event if necessary.
+ private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
+
+ // We allow only certain attributes to trigger update
+ if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
+ !Objects.equals(oldDevice.swVersion(), newDevice.swVersion())) {
+
+ synchronized (this) {
+ devices.replace(newDevice.id(), oldDevice, newDevice);
+ if (!providerId.isAncillary()) {
+ availableDevices.add(newDevice.id());
+ }
+ }
+ return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
+ }
+
+ // Otherwise merely attempt to change availability if primary provider
+ if (!providerId.isAncillary()) {
+ synchronized (this) {
+ boolean added = availableDevices.add(newDevice.id());
+ return !added ? null :
+ new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public DeviceEvent markOffline(DeviceId deviceId) {
+ synchronized (this) {
+ Device device = devices.get(deviceId);
+ boolean removed = (device != null) && availableDevices.remove(deviceId);
+ return !removed ? null :
+ new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
+ }
+ }
+
+ @Override
+ public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+
+ // TODO: implement multi-provider
+ Device device = devices.get(deviceId);
+ checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
+
+ ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
+
+ DeviceDescriptions descs = descsMap.get(providerId);
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+
+ List<DeviceEvent> events = new ArrayList<>();
+ synchronized (this) {
+ ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
+
+ // Add new ports
+ Set<PortNumber> processed = new HashSet<>();
+ for (PortDescription portDescription : portDescriptions) {
+ PortNumber number = portDescription.portNumber();
+ Port oldPort = ports.get(number);
+ // update description
+ descs.putPortDesc(number, portDescription);
+ Port newPort = composePort(device, number, descsMap);
+
+ events.add(oldPort == null ?
+ createPort(device, newPort, ports) :
+ updatePort(device, oldPort, newPort, ports));
+ processed.add(portDescription.portNumber());
+ }
+
+ events.addAll(pruneOldPorts(device, ports, processed));
+ }
+ return FluentIterable.from(events).filter(notNull()).toList();
+ }
+
+ // Creates a new port based on the port description adds it to the map and
+ // Returns corresponding event.
+ private DeviceEvent createPort(Device device, Port newPort,
+ ConcurrentMap<PortNumber, Port> ports) {
+ ports.put(newPort.number(), newPort);
+ return new DeviceEvent(PORT_ADDED, device, newPort);
+ }
+
+ // CHecks if the specified port requires update and if so, it replaces the
+ // existing entry in the map and returns corresponding event.
+ private DeviceEvent updatePort(Device device, Port oldPort,
+ Port newPort,
+ ConcurrentMap<PortNumber, Port> ports) {
+ if (oldPort.isEnabled() != newPort.isEnabled()) {
+ ports.put(oldPort.number(), newPort);
+ return new DeviceEvent(PORT_UPDATED, device, newPort);
+ }
+ return null;
+ }
+
+ // Prunes the specified list of ports based on which ports are in the
+ // processed list and returns list of corresponding events.
+ private List<DeviceEvent> pruneOldPorts(Device device,
+ Map<PortNumber, Port> ports,
+ Set<PortNumber> processed) {
+ List<DeviceEvent> events = new ArrayList<>();
+ Iterator<PortNumber> iterator = ports.keySet().iterator();
+ while (iterator.hasNext()) {
+ PortNumber portNumber = iterator.next();
+ if (!processed.contains(portNumber)) {
+ events.add(new DeviceEvent(PORT_REMOVED, device,
+ ports.get(portNumber)));
+ iterator.remove();
+ }
+ }
+ return events;
+ }
+
+ // Gets the map of ports for the specified device; if one does not already
+ // exist, it creates and registers a new one.
+ private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
+ return createIfAbsentUnchecked(devicePorts, deviceId,
+ new InitConcurrentHashMap<PortNumber, Port>());
+ }
+
+ @Override
+ public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
+ PortDescription portDescription) {
+ Device device = devices.get(deviceId);
+ checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
+
+ ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
+
+ DeviceDescriptions descs = descsMap.get(providerId);
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+ // TODO: implement multi-provider
+ synchronized (this) {
+ ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
+ final PortNumber number = portDescription.portNumber();
+ Port oldPort = ports.get(number);
+ // update description
+ descs.putPortDesc(number, portDescription);
+ Port newPort = composePort(device, number, descsMap);
+ if (oldPort == null) {
+ return createPort(device, newPort, ports);
+ } else {
+ return updatePort(device, oldPort, newPort, ports);
+ }
+ }
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ if (ports == null) {
+ return Collections.emptyList();
+ }
+ return ImmutableList.copyOf(ports.values());
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ return ports == null ? null : ports.get(portNumber);
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return availableDevices.contains(deviceId);
+ }
+
+ @Override
+ public DeviceEvent removeDevice(DeviceId deviceId) {
+ synchronized (this) {
+ Device device = devices.remove(deviceId);
+ return device == null ? null :
+ new DeviceEvent(DEVICE_REMOVED, device, null);
+ }
+ }
+
+ /**
+ * Returns a Device, merging description given from multiple Providers.
+ *
+ * @param deviceId device identifier
+ * @param providerDescs Collection of Descriptions from multiple providers
+ * @return Device instance
+ */
+ private Device composeDevice(DeviceId deviceId,
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+
+ checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
+
+ ProviderId primary = pickPrimaryPID(providerDescs);
+
+ DeviceDescriptions desc = providerDescs.get(primary);
+ Type type = desc.getDeviceDesc().type();
+ String manufacturer = desc.getDeviceDesc().manufacturer();
+ String hwVersion = desc.getDeviceDesc().hwVersion();
+ String swVersion = desc.getDeviceDesc().swVersion();
+ String serialNumber = desc.getDeviceDesc().serialNumber();
+
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (e.getKey().equals(primary)) {
+ continue;
+ }
+ // FIXME: implement attribute merging once we have K-V attributes
+ }
+
+ return new DefaultDevice(primary, deviceId , type, manufacturer, hwVersion, swVersion, serialNumber);
+ }
+
+ // probably want composePorts
+ private Port composePort(Device device, PortNumber number,
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+
+ ProviderId primary = pickPrimaryPID(providerDescs);
+ DeviceDescriptions primDescs = providerDescs.get(primary);
+ final PortDescription portDesc = primDescs.getPortDesc(number);
+ boolean isEnabled;
+ if (portDesc != null) {
+ isEnabled = portDesc.isEnabled();
+ } else {
+ // if no primary, assume not enabled
+ // TODO: revisit this port enabled/disabled behavior
+ isEnabled = false;
+ }
+
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (e.getKey().equals(primary)) {
+ continue;
+ }
+ // FIXME: implement attribute merging once we have K-V attributes
+ }
+
+ return new DefaultPort(device, number, isEnabled);
+ }
+
+ /**
+ * @return primary ProviderID, or randomly chosen one if none exists
+ */
+ private ProviderId pickPrimaryPID(
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+ ProviderId fallBackPrimary = null;
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (!e.getKey().isAncillary()) {
+ return e.getKey();
+ } else if (fallBackPrimary == null) {
+ // pick randomly as a fallback in case there is no primary
+ fallBackPrimary = e.getKey();
+ }
+ }
+ return fallBackPrimary;
+ }
+
+ // TODO: can be made generic
+ private static final class InitConcurrentHashMap<K, V> implements
+ ConcurrentInitializer<ConcurrentMap<K, V>> {
+ @Override
+ public ConcurrentMap<K, V> get() throws ConcurrentException {
+ return new ConcurrentHashMap<>();
+ }
+ }
+
+ public static final class InitDeviceDescs
+ implements ConcurrentInitializer<DeviceDescriptions> {
+ private final DeviceDescription deviceDesc;
+ public InitDeviceDescs(DeviceDescription deviceDesc) {
+ this.deviceDesc = checkNotNull(deviceDesc);
+ }
+ @Override
+ public DeviceDescriptions get() throws ConcurrentException {
+ return new DeviceDescriptions(deviceDesc);
+ }
+ }
+
+
+ /**
+ * Collection of Description of a Device and it's Ports given from a Provider.
+ */
+ private static class DeviceDescriptions {
+ // private final DeviceId id;
+ // private final ProviderId pid;
+
+ private final AtomicReference<DeviceDescription> deviceDesc;
+ private final ConcurrentMap<PortNumber, PortDescription> portDescs;
+
+ public DeviceDescriptions(DeviceDescription desc) {
+ this.deviceDesc = new AtomicReference<>(desc);
+ this.portDescs = new ConcurrentHashMap<>();
+ }
+
+ public DeviceDescription getDeviceDesc() {
+ return deviceDesc.get();
+ }
+
+ public PortDescription getPortDesc(PortNumber number) {
+ return portDescs.get(number);
+ }
+
+ public Collection<PortDescription> getPortDescs() {
+ return Collections.unmodifiableCollection(portDescs.values());
+ }
+
+ public DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
+ return deviceDesc.getAndSet(newDesc);
+ }
+
+ public PortDescription putPortDesc(PortNumber number, PortDescription newDesc) {
+ return portDescs.put(number, newDesc);
+ }
+ }
+}
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 79%
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..431fba3 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;
@@ -44,6 +44,7 @@
public class SimpleDeviceStoreTest {
private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "bar", true);
private static final DeviceId DID1 = deviceId("of:foo");
private static final DeviceId DID2 = deviceId("of:bar");
private static final String MFR = "whitebox";
@@ -89,6 +90,13 @@
deviceStore.createOrUpdateDevice(PID, deviceId, description);
}
+ private void putDeviceAncillary(DeviceId deviceId, String swVersion) {
+ DeviceDescription description =
+ new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
+ HW, swVersion, SN);
+ deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
+ }
+
private static void assertDevice(DeviceId id, String swVersion, Device device) {
assertNotNull(device);
assertEquals(id, device.id());
@@ -160,6 +168,33 @@
}
@Test
+ public final void testCreateOrUpdateDeviceAncillary() {
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN);
+ DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(PIDA, event.subject().providerId());
+ assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN);
+ DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertEquals(DEVICE_UPDATED, event2.type());
+ assertDevice(DID1, SW2, event2.subject());
+ assertEquals(PID, event2.subject().providerId());
+ assertTrue(deviceStore.isAvailable(DID1));
+
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
+
+ // For now, Ancillary is ignored once primary appears
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
+ }
+
+
+ @Test
public final void testMarkOffline() {
putDevice(DID1, SW1);
@@ -182,7 +217,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 +236,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 +259,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,14 +284,42 @@
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());
assertEquals(P1, event.port().number());
assertFalse("Port is disabled", event.port().isEnabled());
+
+ }
+ @Test
+ public final void testUpdatePortStatusAncillary() {
+ putDeviceAncillary(DID1, SW1);
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
+ new DefaultPortDescription(P1, false));
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(P1, event.port().number());
+ assertFalse("Port is disabled", event.port().isEnabled());
+
+ DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P1, true));
+ assertNull("Ancillary is ignored if primary exists", event2);
+
+ DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P2, true));
+ assertEquals(PORT_ADDED, event3.type());
+ assertDevice(DID1, SW1, event3.subject());
+ assertEquals(P2, event3.port().number());
+ assertFalse("Port is disabled if not given from provider", event3.port().isEnabled());
}
@Test
@@ -267,7 +330,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 +353,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/features/features.xml b/features/features.xml
index d2d9567..f008c14 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -11,6 +11,7 @@
<bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
<bundle>mvn:com.hazelcast/hazelcast/3.3</bundle>
+ <bundle>mvn:com.codahale.metrics/metrics-core/3.0.2</bundle>
<bundle>mvn:com.eclipsesource.minimal-json/minimal-json/0.9.1</bundle>
<bundle>mvn:com.esotericsoftware.kryo/kryo/2.24.0</bundle>
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/DefaultOpenFlowPacketContext.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/DefaultOpenFlowPacketContext.java
index 4cd29c4..0f69bac 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/DefaultOpenFlowPacketContext.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/DefaultOpenFlowPacketContext.java
@@ -48,7 +48,7 @@
OFPacketOut.Builder builder = sw.factory().buildPacketOut();
OFAction act = buildOutput(outPort.getPortNumber());
pktout = builder.setXid(pktin.getXid())
- .setInPort(pktin.getInPort())
+ .setInPort(inport())
.setBufferId(pktin.getBufferId())
.setActions(Collections.singletonList(act))
.build();
@@ -63,7 +63,7 @@
OFAction act = buildOutput(outPort.getPortNumber());
pktout = builder.setXid(pktin.getXid())
.setBufferId(OFBufferId.NO_BUFFER)
- .setInPort(pktin.getInPort())
+ .setInPort(inport())
.setActions(Collections.singletonList(act))
.setData(ethFrame.serialize())
.build();
@@ -88,10 +88,16 @@
@Override
public Integer inPort() {
+ return inport().getPortNumber();
+ }
+
+
+ private OFPort inport() {
+ //FIXME: this has to change in fucking loxi
try {
- return pktin.getInPort().getPortNumber();
+ return pktin.getInPort();
} catch (UnsupportedOperationException e) {
- return pktin.getMatch().get(MatchField.IN_PORT).getPortNumber();
+ return pktin.getMatch().get(MatchField.IN_PORT);
}
}
diff --git a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/AbstractOpenFlowSwitch.java b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/AbstractOpenFlowSwitch.java
index 69ddc71..4334395 100644
--- a/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/AbstractOpenFlowSwitch.java
+++ b/openflow/api/src/main/java/org/onlab/onos/openflow/controller/driver/AbstractOpenFlowSwitch.java
@@ -243,6 +243,8 @@
if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
this.role = role;
}
+ } else {
+ this.role = role;
}
} catch (IOException e) {
log.error("Unable to write to switch {}.", this.dpid);
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
index 75c139d..04aef8d 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/controller/impl/OFChannelHandler.java
@@ -651,7 +651,7 @@
* @param error The error message
*/
protected void logError(OFChannelHandler h, OFErrorMsg error) {
- log.error("{} from switch {} in state {}",
+ log.info("{} from switch {} in state {}",
new Object[] {
error,
h.getSwitchInfoString(),
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java
index 6cf4fa4..868eb86 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/DriverManager.java
@@ -6,6 +6,7 @@
import java.util.List;
import org.onlab.onos.openflow.controller.Dpid;
+import org.onlab.onos.openflow.controller.RoleState;
import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch;
import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver;
import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriverFactory;
@@ -61,6 +62,11 @@
return new AbstractOpenFlowSwitch(dpid, desc) {
@Override
+ public void setRole(RoleState state) {
+ this.role = RoleState.MASTER;
+ }
+
+ @Override
public void write(List<OFMessage> msgs) {
channel.write(msgs);
}
diff --git a/pom.xml b/pom.xml
index 16ca891..3124467 100644
--- a/pom.xml
+++ b/pom.xml
@@ -425,7 +425,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/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
index efe436f..86ab701 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
@@ -73,10 +73,11 @@
List<OFAction> actions = buildActions();
//TODO: what to do without bufferid? do we assume that there will be a pktout as well?
- OFFlowMod fm = factory.buildFlowModify()
+ OFFlowMod fm = factory.buildFlowAdd()
.setCookie(U64.of(cookie.value()))
.setBufferId(OFBufferId.NO_BUFFER)
.setActions(actions)
+ .setIdleTimeout(10)
.setMatch(match)
.setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
.setPriority(priority)
@@ -93,7 +94,7 @@
OFFlowMod fm = factory.buildFlowDelete()
.setCookie(U64.of(cookie.value()))
.setBufferId(OFBufferId.NO_BUFFER)
- //.setActions(actions)
+ .setActions(actions)
.setMatch(match)
.setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
.setPriority(priority)
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
index 6d773de..ac00f05 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
@@ -18,6 +18,7 @@
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
@@ -70,14 +71,15 @@
buildSelector(), buildTreatment(), stat.getPriority(),
FlowRuleState.ADDED, stat.getDurationNsec() / 1000000,
stat.getPacketCount().getValue(), stat.getByteCount().getValue(),
- stat.getCookie().getValue());
+ stat.getCookie().getValue(), false);
} else {
// TODO: revisit potentially.
return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
buildSelector(), null, removed.getPriority(),
FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000,
removed.getPacketCount().getValue(), removed.getByteCount().getValue(),
- removed.getCookie().getValue());
+ removed.getCookie().getValue(),
+ removed.getReason() == OFFlowRemovedReason.IDLE_TIMEOUT.ordinal());
}
}
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
index c8d9c25..bf29ae4 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -158,7 +158,7 @@
case BARRIER_REPLY:
case ERROR:
default:
- log.warn("Unhandled message type: {}", msg.getType());
+ log.debug("Unhandled message type: {}", msg.getType());
}
}
diff --git a/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java b/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java
index 132b185..0c4502b 100644
--- a/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java
+++ b/providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/LinkDiscovery.java
@@ -131,7 +131,7 @@
}
timeout = Timer.getTimer().newTimeout(this, 0,
TimeUnit.MILLISECONDS);
- this.log.debug("Started discovery manager for switch {}",
+ this.log.info("Started discovery manager for switch {}",
sw.getId());
}
diff --git a/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java b/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
index 41cb586..94f7a33 100644
--- a/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
+++ b/providers/openflow/packet/src/main/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProvider.java
@@ -1,6 +1,5 @@
package org.onlab.onos.provider.of.packet.impl;
-import static org.onlab.onos.openflow.controller.RoleState.SLAVE;
import static org.slf4j.LoggerFactory.getLogger;
import java.nio.ByteBuffer;
@@ -95,9 +94,6 @@
if (sw == null) {
log.warn("Device {} isn't available?", devId);
return;
- } else if (sw.getRole().equals(SLAVE)) {
- log.warn("Can't write to Device {} as slave", devId);
- return;
}
Ethernet eth = new Ethernet();
diff --git a/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java b/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
index 66c0aea..030563c 100644
--- a/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
+++ b/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
@@ -140,12 +140,12 @@
sw.sent.clear();
//wrong Role
- sw.setRole(RoleState.SLAVE);
- provider.emit(passPkt);
- assertEquals("invalid switch", sw, controller.current);
- assertEquals("message sent incorrectly", 0, sw.sent.size());
+ //sw.setRole(RoleState.SLAVE);
+ //provider.emit(passPkt);
+ //assertEquals("invalid switch", sw, controller.current);
+ //assertEquals("message sent incorrectly", 0, sw.sent.size());
- sw.setRole(RoleState.MASTER);
+ //sw.setRole(RoleState.MASTER);
//missing switch
OutboundPacket swFailPkt = outPacket(DID_MISSING, TR, eth);
diff --git a/tools/build/onos-package b/tools/build/onos-package
index cf751c7..2d6b954 100755
--- a/tools/build/onos-package
+++ b/tools/build/onos-package
@@ -34,6 +34,8 @@
mkdir -p $KARAF_DIST/system/org/onlab
cp -r $M2_REPO/org/onlab $KARAF_DIST/system/org/
+export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo}"
+
# Cellar Patching --------------------------------------------------------------
# Patch the Apache Karaf distribution file to add Cellar features repository
@@ -51,7 +53,7 @@
$ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
# Patch the Apache Karaf distribution file to load ONOS features
-perl -pi.old -e 's|^(featuresBoot=.*)|\1,webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo|' \
+perl -pi.old -e "s|^(featuresBoot=.*)|\1,$ONOS_FEATURES|" \
$ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
# Patch the Apache Karaf distribution with ONOS branding bundle
diff --git a/tools/build/onos-test b/tools/build/onos-test
index 1eb8edb..1526e1b 100755
--- a/tools/build/onos-test
+++ b/tools/build/onos-test
@@ -9,5 +9,10 @@
nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
onos-package
-for node in $nodes; do (printf "%s: %s\n" "$node" "`onos-install -f $node`")& done
+for node in $nodes; do onos-install -f $node 1>/dev/null & done
+
+# Wait for shutdown before waiting for restart
+sleep 3
+
for node in $nodes; do onos-wait-for-start $node; done
+for node in $nodes; do onos-check-logs $node; done
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 821ee43..8332a47 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
@@ -57,6 +57,7 @@
if [ -n "$1" ]; then
[ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
echo "No such cell: $1" >&2 && return 1
+ unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
. $ONOS_ROOT/tools/test/cells/$1
export OCI=$OC1
export ONOS_CELL=$1
@@ -66,6 +67,7 @@
env | egrep "OCI"
env | egrep "OC[0-9]+" | sort
env | egrep "OCN"
+ env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL'
fi
}
@@ -73,7 +75,11 @@
# Lists available cells
function cells {
- ls -1 $ONOS_ROOT/tools/test/cells
+ for cell in $(ls -1 $ONOS_ROOT/tools/test/cells); do
+ printf "%-12s %s\n" \
+ "$([ $cell = $ONOS_CELL ] && echo $cell '*' || echo $cell)" \
+ "$(grep '^#' $ONOS_ROOT/tools/test/cells/$cell | head -n 1)"
+ done
}
# Miscellaneous
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
deleted file mode 100644
index c025225..0000000
--- a/tools/test/cells/.reset
+++ /dev/null
@@ -1 +0,0 @@
-unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN ONOS_NIC
diff --git a/tools/test/cells/local b/tools/test/cells/local
index 6b9fea5..b535934 100644
--- a/tools/test/cells/local
+++ b/tools/test/cells/local
@@ -1,8 +1,6 @@
-# Default virtual box ONOS instances 1,2 & ONOS mininet box
-. $ONOS_ROOT/tools/test/cells/.reset
+# Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box
export ONOS_NIC=192.168.56.*
-
export OC1="192.168.56.101"
export OC2="192.168.56.102"
diff --git a/tools/test/cells/office b/tools/test/cells/office
new file mode 100644
index 0000000..acedac2
--- /dev/null
+++ b/tools/test/cells/office
@@ -0,0 +1,7 @@
+# ProxMox-based cell of ONOS instance; no mininet-box
+
+export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue"
+
+export ONOS_NIC="10.128.4.*"
+export OC1="10.128.4.60"
+
diff --git a/tools/test/cells/prox b/tools/test/cells/prox
index 3fa1279..1731eb8 100644
--- a/tools/test/cells/prox
+++ b/tools/test/cells/prox
@@ -1,8 +1,6 @@
# ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box
-. $ONOS_ROOT/tools/test/cells/.reset
export ONOS_NIC="10.1.9.*"
-
export OC1="10.1.9.94"
export OC2="10.1.9.82"
diff --git a/tools/test/cells/single b/tools/test/cells/single
new file mode 100644
index 0000000..bc969f3
--- /dev/null
+++ b/tools/test/cells/single
@@ -0,0 +1,6 @@
+# Local VirtualBox-based single ONOS instance & ONOS mininet box
+
+export ONOS_NIC=192.168.56.*
+export OC1="192.168.56.101"
+export OCN="192.168.56.103"
+
diff --git a/tools/test/cells/tom b/tools/test/cells/tom
deleted file mode 100644
index 2eb0523..0000000
--- a/tools/test/cells/tom
+++ /dev/null
@@ -1,10 +0,0 @@
-# Default virtual box ONOS instances 1,2 & ONOS mininet box
-
-export ONOS_NIC=192.168.56.*
-
-export OC1="192.168.56.11"
-export OC2="192.168.56.12"
-
-export OCN="192.168.56.7"
-
-
diff --git a/tools/test/cells/triple b/tools/test/cells/triple
new file mode 100644
index 0000000..baae31a
--- /dev/null
+++ b/tools/test/cells/triple
@@ -0,0 +1,9 @@
+# Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box
+
+export ONOS_NIC=192.168.56.*
+export OC1="192.168.56.101"
+export OC2="192.168.56.102"
+export OC3="192.168.56.104"
+
+export OCN="192.168.56.103"
+
diff --git a/utils/misc/pom.xml b/utils/misc/pom.xml
index b451b50..bb25635 100644
--- a/utils/misc/pom.xml
+++ b/utils/misc/pom.xml
@@ -55,6 +55,11 @@
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.codahale.metrics</groupId>
+ <artifactId>metrics-core</artifactId>
+ <version>3.0.2</version>
+ </dependency>
</dependencies>
</project>
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java
new file mode 100644
index 0000000..996fa6f1
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsComponent.java
@@ -0,0 +1,42 @@
+package org.onlab.metrics;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Components to register for metrics.
+ */
+public class MetricsComponent implements MetricsComponentRegistry {
+ private final String name;
+
+ /**
+ * Registry to hold the Features defined in this Component.
+ */
+ private final ConcurrentMap<String, MetricsFeature> featuresRegistry =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Constructs a component from a name.
+ *
+ * @param newName name of the component
+ */
+ MetricsComponent(final String newName) {
+ name = newName;
+ }
+
+ @Override public String getName() {
+ return name;
+ }
+
+ @Override public MetricsFeature registerFeature(final String featureName) {
+ MetricsFeature feature = featuresRegistry.get(featureName);
+ if (feature == null) {
+ final MetricsFeature createdFeature = new MetricsFeature(featureName);
+ feature = featuresRegistry.putIfAbsent(featureName, createdFeature);
+ if (feature == null) {
+ feature = createdFeature;
+ }
+ }
+ return feature;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java
new file mode 100644
index 0000000..1602de6
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsComponentRegistry.java
@@ -0,0 +1,10 @@
+package org.onlab.metrics;
+
+/**
+ * Registry Entry for Metrics Components.
+ */
+public interface MetricsComponentRegistry {
+ String getName();
+
+ MetricsFeature registerFeature(String featureName);
+}
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
new file mode 100644
index 0000000..7a97b08
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
@@ -0,0 +1,21 @@
+package org.onlab.metrics;
+
+/**
+ * Features to tag metrics.
+ */
+public class MetricsFeature {
+ private final String name;
+
+ /**
+ * Constructs a Feature from a name.
+ *
+ * @param newName name of the Feature
+ */
+ MetricsFeature(final String newName) {
+ name = newName;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
new file mode 100644
index 0000000..2b13efb
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
@@ -0,0 +1,260 @@
+package org.onlab.metrics;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+
+/**
+ * This class holds the Metrics registry for ONOS.
+ * All metrics (Counter, Histogram, Timer, Meter, Gauge) use a hierarchical
+ * string-based naming scheme: COMPONENT.FEATURE.NAME.
+ * Example: "Topology.Counters.TopologyUpdates".
+ * The COMPONENT and FEATURE names have to be registered in advance before
+ * a metric can be created. Example:
+ * <pre>
+ * <code>
+ * private final MetricsManager.MetricsComponent COMPONENT =
+ * MetricsManager.registerComponent("Topology");
+ * private final MetricsManager.MetricsFeature FEATURE =
+ * COMPONENT.registerFeature("Counters");
+ * private final Counter counterTopologyUpdates =
+ * MetricsManager.createCounter(COMPONENT, FEATURE, "TopologyUpdates");
+ * </code>
+ * </pre>
+ * Gauges are slightly different because they are not created directly in
+ * this class, but are allocated by the caller and passed in for registration:
+ * <pre>
+ * <code>
+ * private final Gauge<Long> gauge =
+ * new {@literal Gauge<Long>}() {
+ * {@literal @}Override
+ * public Long getValue() {
+ * return gaugeValue;
+ * }
+ * };
+ * MetricsManager.registerMetric(COMPONENT, FEATURE, GAUGE_NAME, gauge);
+ * </code>
+ * </pre>
+ */
+public final class MetricsManager implements MetricsService {
+
+ /**
+ * Registry to hold the Components defined in the system.
+ */
+ private ConcurrentMap<String, MetricsComponent> componentsRegistry =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Registry for the Metrics objects created in the system.
+ */
+ private final MetricRegistry metricsRegistry = new MetricRegistry();
+
+ /**
+ * Hide constructor. The only way to get the registry is through the
+ * singleton getter.
+ */
+ private MetricsManager() {}
+
+ /**
+ * Registers a component.
+ *
+ * @param name name of the Component to register
+ * @return MetricsComponent object that can be used to create Metrics.
+ */
+ @Override
+ public MetricsComponent registerComponent(final String name) {
+ MetricsComponent component = componentsRegistry.get(name);
+ if (component == null) {
+ final MetricsComponent createdComponent = new MetricsComponent(name);
+ component = componentsRegistry.putIfAbsent(name, createdComponent);
+ if (component == null) {
+ component = createdComponent;
+ }
+ }
+ return component;
+ }
+
+ /**
+ * Generates a name for a Metric from its component and feature.
+ *
+ * @param component component the metric is defined in
+ * @param feature feature the metric is defined in
+ * @param metricName local name of the metric
+ *
+ * @return full name of the metric
+ */
+ private String generateName(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ return MetricRegistry.name(component.getName(),
+ feature.getName(),
+ metricName);
+ }
+
+ /**
+ * Creates a Counter metric.
+ *
+ * @param component component the Counter is defined in
+ * @param feature feature the Counter is defined in
+ * @param metricName local name of the metric
+ * @return the created Counter Meteric
+ */
+ @Override
+ public Counter createCounter(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.counter(name);
+ }
+
+ /**
+ * Creates a Histogram metric.
+ *
+ * @param component component the Histogram is defined in
+ * @param feature feature the Histogram is defined in
+ * @param metricName local name of the metric
+ * @return the created Histogram Metric
+ */
+ @Override
+ public Histogram createHistogram(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.histogram(name);
+ }
+
+ /**
+ * Creates a Timer metric.
+ *
+ * @param component component the Timer is defined in
+ * @param feature feature the Timeer is defined in
+ * @param metricName local name of the metric
+ * @return the created Timer Metric
+ */
+ @Override
+ public Timer createTimer(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.timer(name);
+ }
+
+ /**
+ * Creates a Meter metric.
+ *
+ * @param component component the Meter is defined in
+ * @param feature feature the Meter is defined in
+ * @param metricName local name of the metric
+ * @return the created Meter Metric
+ */
+ @Override
+ public Meter createMeter(final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName) {
+ final String name = generateName(component, feature, metricName);
+ return metricsRegistry.meter(name);
+ }
+
+ /**
+ * Registers an already created Metric. This is used for situation where a
+ * caller needs to allocate its own Metric, but still register it with the
+ * system.
+ *
+ * @param <T> Metric type
+ * @param component component the Metric is defined in
+ * @param feature feature the Metric is defined in
+ * @param metricName local name of the metric
+ * @param metric Metric to register
+ * @return the registered Metric
+ */
+ @Override
+ public <T extends Metric> T registerMetric(
+ final MetricsComponent component,
+ final MetricsFeature feature,
+ final String metricName,
+ final T metric) {
+ final String name = generateName(component, feature, metricName);
+ metricsRegistry.register(name, metric);
+ return metric;
+ }
+
+ /**
+ * Fetches the existing Timers.
+ *
+ * @param filter filter to use to select Timers
+ * @return a map of the Timers that match the filter, with the key as the
+ * name String to the Timer.
+ */
+ @Override
+ public Map<String, Timer> getTimers(final MetricFilter filter) {
+ return metricsRegistry.getTimers(filter);
+ }
+
+ /**
+ * Fetches the existing Gauges.
+ *
+ * @param filter filter to use to select Gauges
+ * @return a map of the Gauges that match the filter, with the key as the
+ * name String to the Gauge.
+ */
+ @Override
+ public Map<String, Gauge> getGauges(final MetricFilter filter) {
+ return metricsRegistry.getGauges(filter);
+ }
+
+ /**
+ * Fetches the existing Counters.
+ *
+ * @param filter filter to use to select Counters
+ * @return a map of the Counters that match the filter, with the key as the
+ * name String to the Counter.
+ */
+ @Override
+ public Map<String, Counter> getCounters(final MetricFilter filter) {
+ return metricsRegistry.getCounters(filter);
+ }
+
+ /**
+ * Fetches the existing Meters.
+ *
+ * @param filter filter to use to select Meters
+ * @return a map of the Meters that match the filter, with the key as the
+ * name String to the Meter.
+ */
+ @Override
+ public Map<String, Meter> getMeters(final MetricFilter filter) {
+ return metricsRegistry.getMeters(filter);
+ }
+
+ /**
+ * Fetches the existing Histograms.
+ *
+ * @param filter filter to use to select Histograms
+ * @return a map of the Histograms that match the filter, with the key as the
+ * name String to the Histogram.
+ */
+ @Override
+ public Map<String, Histogram> getHistograms(final MetricFilter filter) {
+ return metricsRegistry.getHistograms(filter);
+ }
+
+ /**
+ * Removes all Metrics that match a given filter.
+ *
+ * @param filter filter to use to select the Metrics to remove.
+ */
+ @Override
+ public void removeMatching(final MetricFilter filter) {
+ metricsRegistry.removeMatching(filter);
+ }
+}
+
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
new file mode 100644
index 0000000..1c4baea
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
@@ -0,0 +1,143 @@
+package org.onlab.metrics;
+
+import java.util.Map;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.Timer;
+
+/**
+ * Metrics Service to collect metrics.
+ */
+interface MetricsService {
+
+ /**
+ * Registers a component.
+ *
+ * @param name name of the Component to register
+ * @return MetricsComponent object that can be used to create Metrics.
+ */
+ MetricsComponent registerComponent(String name);
+
+ /**
+ * Creates a Counter metric.
+ *
+ * @param component component the Counter is defined in
+ * @param feature feature the Counter is defined in
+ * @param metricName local name of the metric
+ * @return the created Counter Meteric
+ */
+ Counter createCounter(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Creates a Histogram metric.
+ *
+ * @param component component the Histogram is defined in
+ * @param feature feature the Histogram is defined in
+ * @param metricName local name of the metric
+ * @return the created Histogram Metric
+ */
+ Histogram createHistogram(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Creates a Timer metric.
+ *
+ * @param component component the Timer is defined in
+ * @param feature feature the Timer is defined in
+ * @param metricName local name of the metric
+ * @return the created Timer Metric
+ */
+ Timer createTimer(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Creates a Meter metric.
+ *
+ * @param component component the Meter is defined in
+ * @param feature feature the Meter is defined in
+ * @param metricName local name of the metric
+ * @return the created Meter Metric
+ */
+ Meter createMeter(MetricsComponent component,
+ MetricsFeature feature,
+ String metricName);
+
+ /**
+ * Registers an already created Metric. This is used for situation where a
+ * caller needs to allocate its own Metric, but still register it with the
+ * system.
+ *
+ * @param <T> Metric type
+ * @param component component the Metric is defined in
+ * @param feature feature the Metric is defined in
+ * @param metricName local name of the metric
+ * @param metric Metric to register
+ * @return the registered Metric
+ */
+ <T extends Metric> T registerMetric(
+ MetricsComponent component,
+ MetricsFeature feature,
+ String metricName,
+ T metric);
+
+ /**
+ * Fetches the existing Timers.
+ *
+ * @param filter filter to use to select Timers
+ * @return a map of the Timers that match the filter, with the key as the
+ * name String to the Timer.
+ */
+ Map<String, Timer> getTimers(MetricFilter filter);
+
+ /**
+ * Fetches the existing Gauges.
+ *
+ * @param filter filter to use to select Gauges
+ * @return a map of the Gauges that match the filter, with the key as the
+ * name String to the Gauge.
+ */
+ Map<String, Gauge> getGauges(MetricFilter filter);
+
+ /**
+ * Fetches the existing Counters.
+ *
+ * @param filter filter to use to select Counters
+ * @return a map of the Counters that match the filter, with the key as the
+ * name String to the Counter.
+ */
+ Map<String, Counter> getCounters(MetricFilter filter);
+
+ /**
+ * Fetches the existing Meters.
+ *
+ * @param filter filter to use to select Meters
+ * @return a map of the Meters that match the filter, with the key as the
+ * name String to the Meter.
+ */
+ Map<String, Meter> getMeters(MetricFilter filter);
+
+ /**
+ * Fetches the existing Histograms.
+ *
+ * @param filter filter to use to select Histograms
+ * @return a map of the Histograms that match the filter, with the key as the
+ * name String to the Histogram.
+ */
+ Map<String, Histogram> getHistograms(MetricFilter filter);
+ /**
+ * Removes all Metrics that match a given filter.
+ *
+ * @param filter filter to use to select the Metrics to remove.
+ */
+ void removeMatching(MetricFilter filter);
+
+}
diff --git a/utils/misc/src/main/java/org/onlab/metrics/package-info.java b/utils/misc/src/main/java/org/onlab/metrics/package-info.java
new file mode 100644
index 0000000..48151d4
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Misc utils for various performance metrics.
+ */
+package org.onlab.metrics;
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
index f466122..b205f90 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
@@ -180,6 +180,15 @@
return address;
}
+ public int toRealInt() {
+ int val = 0;
+ for (int i = 0; i < octets.length; i++) {
+ val <<= 8;
+ val |= octets[i] & 0xff;
+ }
+ return val;
+ }
+
/**
* Helper for computing the mask value from CIDR.
*
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) {
+ }
+ }
}