diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java b/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java index b9251dd5c8..1fafc32365 100644 --- a/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java +++ b/apps/foo/src/main/java/org/onlab/onos/foo/FooComponent.java @@ -8,6 +8,9 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.onos.cluster.ClusterEvent; import org.onlab.onos.cluster.ClusterEventListener; import org.onlab.onos.cluster.ClusterService; +import org.onlab.onos.net.device.DeviceEvent; +import org.onlab.onos.net.device.DeviceListener; +import org.onlab.onos.net.device.DeviceService; import org.slf4j.Logger; import static org.slf4j.LoggerFactory.getLogger; @@ -23,17 +26,23 @@ public class FooComponent { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterService clusterService; - private ClusterEventListener clusterListener = new InnerClusterListener(); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + private final ClusterEventListener clusterListener = new InnerClusterListener(); + private final DeviceListener deviceListener = new InnerDeviceListener(); @Activate public void activate() { clusterService.addListener(clusterListener); + deviceService.addListener(deviceListener); log.info("Started"); } @Deactivate public void deactivate() { clusterService.removeListener(clusterListener); + deviceService.removeListener(deviceListener); log.info("Stopped"); } @@ -43,6 +52,13 @@ public class FooComponent { log.info("WOOOOT! {}", event); } } + + private class InnerDeviceListener implements DeviceListener { + @Override + public void event(DeviceEvent event) { + log.info("YEEEEHAAAAW! {}", event); + } + } } diff --git a/apps/foo/src/main/java/org/onlab/onos/foo/package-info.java b/apps/foo/src/main/java/org/onlab/onos/foo/package-info.java new file mode 100644 index 0000000000..6372772899 --- /dev/null +++ b/apps/foo/src/main/java/org/onlab/onos/foo/package-info.java @@ -0,0 +1,4 @@ +/** + * Sample application for use in various experiments. + */ +package org.onlab.onos.foo; \ No newline at end of file diff --git a/core/api/src/main/java/org/onlab/onos/ApplicationId.java b/core/api/src/main/java/org/onlab/onos/ApplicationId.java new file mode 100644 index 0000000000..f34560774f --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/ApplicationId.java @@ -0,0 +1,59 @@ +package org.onlab.onos; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Application id generator class. + */ +public final class ApplicationId { + + private static AtomicInteger idDispenser; + private final Integer id; + + // Ban public construction + private ApplicationId(Integer id) { + this.id = id; + } + + public Integer id() { + return id; + } + + public static ApplicationId valueOf(Integer id) { + return new ApplicationId(id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ApplicationId)) { + return false; + } + ApplicationId other = (ApplicationId) obj; + return Objects.equals(this.id, other.id); + } + + /** + * Returns a new application id. + * + * @return app id + */ + public static ApplicationId getAppId() { + if (ApplicationId.idDispenser == null) { + ApplicationId.idDispenser = new AtomicInteger(1); + } + return new ApplicationId(ApplicationId.idDispenser.getAndIncrement()); + } + +} 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 3e2ee03dd5..5c0c207c1b 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 @@ -4,12 +4,13 @@ import java.util.Set; import org.onlab.onos.net.DeviceId; import org.onlab.onos.net.MastershipRole; +import org.onlab.onos.store.Store; /** * Manages inventory of mastership roles for devices, across controller * instances; not intended for direct use. */ -public interface MastershipStore { +public interface MastershipStore extends Store { // three things to map: NodeId, DeviceId, MastershipRole @@ -51,9 +52,7 @@ public interface MastershipStore { * * @param nodeId controller instance identifier * @param deviceId device identifier - * @param role new role * @return a mastership event */ - MastershipEvent setRole(NodeId nodeId, DeviceId deviceId, - MastershipRole role); + MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId); } diff --git a/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java b/core/api/src/main/java/org/onlab/onos/net/device/DeviceStore.java index ef111e9d7b..c84aac895c 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 @@ -5,13 +5,14 @@ import org.onlab.onos.net.DeviceId; import org.onlab.onos.net.Port; import org.onlab.onos.net.PortNumber; import org.onlab.onos.net.provider.ProviderId; +import org.onlab.onos.store.Store; import java.util.List; /** * Manages inventory of infrastructure devices; not intended for direct use. */ -public interface DeviceStore { +public interface DeviceStore extends Store { /** * Returns the number of devices known to the system. diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java index 28793e6985..c6093384e1 100644 --- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java +++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java @@ -2,11 +2,12 @@ package org.onlab.onos.net.flow; import org.onlab.onos.ApplicationId; import org.onlab.onos.net.DeviceId; +import org.onlab.onos.store.Store; /** * Manages inventory of flow rules; not intended for direct use. */ -public interface FlowRuleStore { +public interface FlowRuleStore extends Store { /** * Returns the stored flow. diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStoreDelegate.java new file mode 100644 index 0000000000..119712bdaa --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStoreDelegate.java @@ -0,0 +1,9 @@ +package org.onlab.onos.net.flow; + +import org.onlab.onos.store.StoreDelegate; + +/** + * Flow rule store delegate abstraction. + */ +public interface FlowRuleStoreDelegate extends StoreDelegate { +} diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java index e70bbf201a..3f1cb23c41 100644 --- a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java +++ b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java @@ -1,20 +1,21 @@ package org.onlab.onos.net.host; -import java.util.Set; - import org.onlab.onos.net.ConnectPoint; import org.onlab.onos.net.DeviceId; import org.onlab.onos.net.Host; import org.onlab.onos.net.HostId; import org.onlab.onos.net.provider.ProviderId; +import org.onlab.onos.store.Store; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import java.util.Set; + /** * Manages inventory of end-station hosts; not intended for direct use. */ -public interface HostStore { +public interface HostStore extends Store { /** * Creates a new host or updates the existing one based on the specified @@ -133,7 +134,7 @@ public interface HostStore { * Returns the address bindings for a particular connection point. * * @param connectPoint the connection point to return address information - * for + * for * @return address information for the connection point */ PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint); diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/host/HostStoreDelegate.java new file mode 100644 index 0000000000..999b28fe41 --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/net/host/HostStoreDelegate.java @@ -0,0 +1,9 @@ +package org.onlab.onos.net.host; + +import org.onlab.onos.store.StoreDelegate; + +/** + * Infrastructure link store delegate abstraction. + */ +public interface HostStoreDelegate extends StoreDelegate { +} diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java index dbe487791e..0197417e07 100644 --- a/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java +++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java @@ -4,13 +4,14 @@ import org.onlab.onos.net.ConnectPoint; import org.onlab.onos.net.DeviceId; import org.onlab.onos.net.Link; import org.onlab.onos.net.provider.ProviderId; +import org.onlab.onos.store.Store; import java.util.Set; /** * Manages inventory of infrastructure links; not intended for direct use. */ -public interface LinkStore { +public interface LinkStore extends Store { /** * Returns the number of links in the store. diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkStoreDelegate.java new file mode 100644 index 0000000000..ec747c46bc --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkStoreDelegate.java @@ -0,0 +1,9 @@ +package org.onlab.onos.net.link; + +import org.onlab.onos.store.StoreDelegate; + +/** + * Infrastructure link store delegate abstraction. + */ +public interface LinkStoreDelegate extends StoreDelegate { +} diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java index adc6145823..1945f4c277 100644 --- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java +++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStore.java @@ -6,6 +6,7 @@ import org.onlab.onos.net.DeviceId; import org.onlab.onos.net.Link; import org.onlab.onos.net.Path; import org.onlab.onos.net.provider.ProviderId; +import org.onlab.onos.store.Store; import java.util.List; import java.util.Set; @@ -13,7 +14,7 @@ import java.util.Set; /** * Manages inventory of topology snapshots; not intended for direct use. */ -public interface TopologyStore { +public interface TopologyStore extends Store { /** * Returns the current topology snapshot. diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStoreDelegate.java new file mode 100644 index 0000000000..2a19a0cdac --- /dev/null +++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyStoreDelegate.java @@ -0,0 +1,9 @@ +package org.onlab.onos.net.topology; + +import org.onlab.onos.store.StoreDelegate; + +/** + * Topology store delegate abstraction. + */ +public interface TopologyStoreDelegate extends StoreDelegate { +} diff --git a/core/api/src/main/java/org/onlab/onos/store/AbstractStore.java b/core/api/src/main/java/org/onlab/onos/store/AbstractStore.java index efd0d03ef0..5d76e0fc80 100644 --- a/core/api/src/main/java/org/onlab/onos/store/AbstractStore.java +++ b/core/api/src/main/java/org/onlab/onos/store/AbstractStore.java @@ -2,6 +2,8 @@ package org.onlab.onos.store; import org.onlab.onos.event.Event; +import static com.google.common.base.Preconditions.checkState; + /** * Base implementation of a store. */ @@ -12,12 +14,21 @@ public class AbstractStore> @Override public void setDelegate(D delegate) { + checkState(this.delegate == null || this.delegate == delegate, + "Store delegate already set"); this.delegate = delegate; } @Override - public D getDelegate() { - return delegate; + public void unsetDelegate(D delegate) { + if (this.delegate == delegate) { + this.delegate = null; + } + } + + @Override + public boolean hasDelegate() { + return delegate != null; } /** diff --git a/core/api/src/main/java/org/onlab/onos/store/Store.java b/core/api/src/main/java/org/onlab/onos/store/Store.java index 9eaef665c7..28bc08e068 100644 --- a/core/api/src/main/java/org/onlab/onos/store/Store.java +++ b/core/api/src/main/java/org/onlab/onos/store/Store.java @@ -12,14 +12,25 @@ public interface Store> { * Sets the delegate on the store. * * @param delegate new store delegate + * @throws java.lang.IllegalStateException if a delegate is already + * currently set on the store and is a different one that */ void setDelegate(D delegate); /** - * Get the current store delegate. + * Withdraws the delegate from the store. * - * @return store delegate + * @param delegate store delegate to withdraw + * @throws java.lang.IllegalArgumentException if the delegate is not + * currently set on the store */ - D getDelegate(); + void unsetDelegate(D delegate); + + /** + * Indicates whether the store has a delegate. + * + * @return true if delegate is set + */ + boolean hasDelegate(); } diff --git a/core/api/src/main/java/org/onlab/onos/store/StoreDelegate.java b/core/api/src/main/java/org/onlab/onos/store/StoreDelegate.java index e2c5cd34ac..c7b0465356 100644 --- a/core/api/src/main/java/org/onlab/onos/store/StoreDelegate.java +++ b/core/api/src/main/java/org/onlab/onos/store/StoreDelegate.java @@ -8,6 +8,11 @@ import org.onlab.onos.event.Event; */ public interface StoreDelegate { + /** + * Notifies the delegate via the specified event. + * + * @param event store generated event + */ void notify(E event); } diff --git a/core/net/pom.xml b/core/net/pom.xml index b252636333..e2703b2b31 100644 --- a/core/net/pom.xml +++ b/core/net/pom.xml @@ -44,6 +44,13 @@ ${project.version} test + + org.onlab.onos + onos-core-store + ${project.version} + tests + test + org.apache.felix diff --git a/core/net/src/main/java/org/onlab/onos/cluster/impl/ClusterManager.java b/core/net/src/main/java/org/onlab/onos/cluster/impl/ClusterManager.java index d0cc949d41..9913ad0eea 100644 --- a/core/net/src/main/java/org/onlab/onos/cluster/impl/ClusterManager.java +++ b/core/net/src/main/java/org/onlab/onos/cluster/impl/ClusterManager.java @@ -53,6 +53,7 @@ public class ClusterManager implements ClusterService, ClusterAdminService { @Deactivate public void deactivate() { + store.unsetDelegate(delegate); eventDispatcher.removeSink(ClusterEvent.class); log.info("Stopped"); } 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 4ac6052d01..255830ca02 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 @@ -64,9 +64,12 @@ public class MastershipManager checkNotNull(nodeId, NODE_ID_NULL); checkNotNull(deviceId, DEVICE_ID_NULL); checkNotNull(role, ROLE_NULL); - MastershipEvent event = store.setRole(nodeId, deviceId, role); - if (event != null) { - post(event); + //TODO figure out appropriate action for non-MASTER roles, if we even set those + if (role.equals(MastershipRole.MASTER)) { + MastershipEvent event = store.setMaster(nodeId, deviceId); + if (event != null) { + post(event); + } } } 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 9b6c83aacd..3dfce00b90 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 @@ -26,6 +26,7 @@ import org.onlab.onos.net.device.DeviceProviderRegistry; import org.onlab.onos.net.device.DeviceProviderService; import org.onlab.onos.net.device.DeviceService; 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.AbstractProviderRegistry; import org.onlab.onos.net.provider.AbstractProviderService; @@ -33,8 +34,8 @@ import org.slf4j.Logger; import java.util.List; -import static org.onlab.onos.net.device.DeviceEvent.Type.*; import static com.google.common.base.Preconditions.checkNotNull; +import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED; import static org.slf4j.LoggerFactory.getLogger; /** @@ -57,7 +58,9 @@ public class DeviceManager protected final AbstractListenerRegistry listenerRegistry = new AbstractListenerRegistry<>(); - private final MastershipListener mastershipListener = new InnerMastershipListener(); + private DeviceStoreDelegate delegate = new InternalStoreDelegate(); + + private final MastershipListener mastershipListener = new InternalMastershipListener(); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceStore store; @@ -73,6 +76,7 @@ public class DeviceManager @Activate public void activate() { + store.setDelegate(delegate); eventDispatcher.addSink(DeviceEvent.class, listenerRegistry); mastershipService.addListener(mastershipListener); log.info("Started"); @@ -80,6 +84,7 @@ public class DeviceManager @Deactivate public void deactivate() { + store.unsetDelegate(delegate); mastershipService.removeListener(mastershipListener); eventDispatcher.removeSink(DeviceEvent.class); log.info("Stopped"); @@ -239,7 +244,7 @@ public class DeviceManager } // Intercepts mastership events - private class InnerMastershipListener implements MastershipListener { + private class InternalMastershipListener implements MastershipListener { @Override public void event(MastershipEvent event) { // FIXME: for now we're taking action only on becoming master @@ -248,4 +253,12 @@ public class DeviceManager } } } + + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements DeviceStoreDelegate { + @Override + public void notify(DeviceEvent event) { + post(event); + } + } } 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 238c4d0715..337f437a7f 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 @@ -1,11 +1,6 @@ package org.onlab.onos.net.flow.impl; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.slf4j.LoggerFactory.getLogger; - -import java.util.Iterator; -import java.util.List; - +import com.google.common.collect.Lists; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -26,11 +21,16 @@ import org.onlab.onos.net.flow.FlowRuleProviderRegistry; import org.onlab.onos.net.flow.FlowRuleProviderService; import org.onlab.onos.net.flow.FlowRuleService; import org.onlab.onos.net.flow.FlowRuleStore; +import org.onlab.onos.net.flow.FlowRuleStoreDelegate; import org.onlab.onos.net.provider.AbstractProviderRegistry; import org.onlab.onos.net.provider.AbstractProviderService; import org.slf4j.Logger; -import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; /** * Provides implementation of the flow NB & SB APIs. @@ -47,6 +47,8 @@ implements FlowRuleService, FlowRuleProviderRegistry { private final AbstractListenerRegistry listenerRegistry = new AbstractListenerRegistry<>(); + private FlowRuleStoreDelegate delegate = new InternalStoreDelegate(); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected FlowRuleStore store; @@ -58,12 +60,14 @@ implements FlowRuleService, FlowRuleProviderRegistry { @Activate public void activate() { + store.setDelegate(delegate); eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry); log.info("Started"); } @Deactivate public void deactivate() { + store.unsetDelegate(delegate); eventDispatcher.removeSink(FlowRuleEvent.class); log.info("Stopped"); } @@ -249,4 +253,11 @@ implements FlowRuleService, FlowRuleProviderRegistry { } } + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements FlowRuleStoreDelegate { + @Override + public void notify(FlowRuleEvent event) { + eventDispatcher.post(event); + } + } } diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java index 9b8ecf7ed0..e3f53fe2ef 100644 --- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java +++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostManager.java @@ -1,10 +1,5 @@ package org.onlab.onos.net.host.impl; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.slf4j.LoggerFactory.getLogger; - -import java.util.Set; - import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -26,6 +21,7 @@ import org.onlab.onos.net.host.HostProviderRegistry; import org.onlab.onos.net.host.HostProviderService; import org.onlab.onos.net.host.HostService; import org.onlab.onos.net.host.HostStore; +import org.onlab.onos.net.host.HostStoreDelegate; import org.onlab.onos.net.host.PortAddresses; import org.onlab.onos.net.provider.AbstractProviderRegistry; import org.onlab.onos.net.provider.AbstractProviderService; @@ -35,6 +31,11 @@ import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; import org.slf4j.Logger; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + /** * Provides basic implementation of the host SB & NB APIs. */ @@ -50,6 +51,8 @@ public class HostManager private final AbstractListenerRegistry listenerRegistry = new AbstractListenerRegistry<>(); + private HostStoreDelegate delegate = new InternalStoreDelegate(); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected HostStore store; @@ -59,12 +62,14 @@ public class HostManager @Activate public void activate() { + store.setDelegate(delegate); eventDispatcher.addSink(HostEvent.class, listenerRegistry); log.info("Started"); } @Deactivate public void deactivate() { + store.unsetDelegate(delegate); eventDispatcher.removeSink(HostEvent.class); log.info("Stopped"); } @@ -219,4 +224,11 @@ public class HostManager } } + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements HostStoreDelegate { + @Override + public void notify(HostEvent event) { + post(event); + } + } } diff --git a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java index 9ac5e80d2c..493580d1cf 100644 --- a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java +++ b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java @@ -28,6 +28,7 @@ import org.onlab.onos.net.link.LinkProviderRegistry; import org.onlab.onos.net.link.LinkProviderService; import org.onlab.onos.net.link.LinkService; import org.onlab.onos.net.link.LinkStore; +import org.onlab.onos.net.link.LinkStoreDelegate; import org.onlab.onos.net.provider.AbstractProviderRegistry; import org.onlab.onos.net.provider.AbstractProviderService; import org.slf4j.Logger; @@ -52,7 +53,9 @@ public class LinkManager protected final AbstractListenerRegistry listenerRegistry = new AbstractListenerRegistry<>(); - private final DeviceListener deviceListener = new InnerDeviceListener(); + private LinkStoreDelegate delegate = new InternalStoreDelegate(); + + private final DeviceListener deviceListener = new InternalDeviceListener(); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected LinkStore store; @@ -65,6 +68,7 @@ public class LinkManager @Activate public void activate() { + store.setDelegate(delegate); eventDispatcher.addSink(LinkEvent.class, listenerRegistry); deviceService.addListener(deviceListener); log.info("Started"); @@ -72,6 +76,7 @@ public class LinkManager @Deactivate public void deactivate() { + store.unsetDelegate(delegate); eventDispatcher.removeSink(LinkEvent.class); deviceService.removeListener(deviceListener); log.info("Stopped"); @@ -154,7 +159,7 @@ public class LinkManager // Auxiliary interceptor for device remove events to prune links that // are associated with the removed device or its port. - private class InnerDeviceListener implements DeviceListener { + private class InternalDeviceListener implements DeviceListener { @Override public void event(DeviceEvent event) { if (event.type() == DeviceEvent.Type.DEVICE_REMOVED) { @@ -236,4 +241,11 @@ public class LinkManager } } + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements LinkStoreDelegate { + @Override + public void notify(LinkEvent event) { + post(event); + } + } } diff --git a/core/net/src/main/java/org/onlab/onos/net/topology/impl/TopologyManager.java b/core/net/src/main/java/org/onlab/onos/net/topology/impl/TopologyManager.java index 57e9fb70a7..4846944d01 100644 --- a/core/net/src/main/java/org/onlab/onos/net/topology/impl/TopologyManager.java +++ b/core/net/src/main/java/org/onlab/onos/net/topology/impl/TopologyManager.java @@ -28,6 +28,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.topology.TopologyStore; +import org.onlab.onos.net.topology.TopologyStoreDelegate; import org.slf4j.Logger; import java.util.List; @@ -56,6 +57,8 @@ public class TopologyManager private final AbstractListenerRegistry listenerRegistry = new AbstractListenerRegistry<>(); + private TopologyStoreDelegate delegate = new InternalStoreDelegate(); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected TopologyStore store; @@ -65,12 +68,14 @@ public class TopologyManager @Activate public void activate() { + store.setDelegate(delegate); eventDispatcher.addSink(TopologyEvent.class, listenerRegistry); log.info("Started"); } @Deactivate public void deactivate() { + store.unsetDelegate(delegate); eventDispatcher.removeSink(TopologyEvent.class); log.info("Stopped"); } @@ -188,4 +193,11 @@ public class TopologyManager } } + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements TopologyStoreDelegate { + @Override + public void notify(TopologyEvent event) { + eventDispatcher.post(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 new file mode 100644 index 0000000000..40902f24c7 --- /dev/null +++ b/core/net/src/test/java/org/onlab/onos/cluster/impl/MastershipManagerTest.java @@ -0,0 +1,136 @@ +package org.onlab.onos.cluster.impl; + +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.onos.cluster.ClusterEventListener; +import org.onlab.onos.cluster.ClusterService; +import org.onlab.onos.cluster.ControllerNode; +import org.onlab.onos.cluster.ControllerNode.State; +import org.onlab.onos.cluster.DefaultControllerNode; +import org.onlab.onos.cluster.MastershipService; +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.packet.IpPrefix; + +import static org.junit.Assert.assertEquals; +import static org.onlab.onos.net.MastershipRole.*; + +/** + * Test codifying the mastership service contracts. + */ +public class MastershipManagerTest { + + private static final NodeId NID_LOCAL = new NodeId("local"); + private static final NodeId NID_OTHER = new NodeId("foo"); + private static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1"); + private static final DeviceId DEV_MASTER = DeviceId.deviceId("of:1"); + private static final DeviceId DEV_OTHER = DeviceId.deviceId("of:2"); + + private MastershipManager mgr; + protected MastershipService service; + + @Before + public void setUp() { + mgr = new MastershipManager(); + service = mgr; + mgr.store = new SimpleMastershipStore(); + mgr.eventDispatcher = new TestEventDispatcher(); + mgr.clusterService = new TestClusterService(); + mgr.activate(); + } + + @After + public void tearDown() { + mgr.deactivate(); + mgr.clusterService = null; + mgr.eventDispatcher = null; + mgr.store = null; + } + + @Test + public void setRole() { + mgr.setRole(NID_OTHER, DEV_MASTER, MASTER); + assertEquals("wrong local role:", STANDBY, mgr.getLocalRole(DEV_MASTER)); + + //set to master + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + assertEquals("wrong local role:", MASTER, mgr.getLocalRole(DEV_MASTER)); + } + + @Test + public void relinquishMastership() { + //TODO + } + + @Test + public void requestRoleFor() { + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + mgr.setRole(NID_OTHER, DEV_OTHER, MASTER); + + //local should be master for one but standby for other + assertEquals("wrong role:", MASTER, mgr.requestRoleFor(DEV_MASTER)); + assertEquals("wrong role:", STANDBY, mgr.requestRoleFor(DEV_OTHER)); + } + + @Test + public void getMasterFor() { + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); + mgr.setRole(NID_OTHER, DEV_OTHER, MASTER); + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_MASTER)); + assertEquals("wrong master:", NID_OTHER, mgr.getMasterFor(DEV_OTHER)); + + //have NID_OTHER hand over DEV_OTHER to NID_LOCAL + mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER); + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_OTHER)); + } + + @Test + public void getDevicesOf() { + 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()); + } + + private final class TestClusterService implements ClusterService { + + ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST); + + @Override + public ControllerNode getLocalNode() { + return local; + } + + @Override + public Set getNodes() { + return null; + } + + @Override + public ControllerNode getNode(NodeId nodeId) { + return null; + } + + @Override + public State getState(NodeId nodeId) { + return null; + } + + @Override + public void addListener(ClusterEventListener listener) { + } + + @Override + public void removeListener(ClusterEventListener listener) { + } + + } +} diff --git a/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java index b7362b903d..8923da96b9 100644 --- a/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java +++ b/core/net/src/test/java/org/onlab/onos/net/device/impl/DistributedDeviceManagerTest.java @@ -4,13 +4,15 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.hazelcast.config.Config; import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; + import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.onlab.onos.cluster.DefaultControllerNode; import org.onlab.onos.cluster.MastershipServiceAdapter; import org.onlab.onos.cluster.NodeId; import org.onlab.onos.event.Event; +import org.onlab.onos.event.EventDeliveryService; import org.onlab.onos.event.impl.TestEventDispatcher; import org.onlab.onos.net.Device; import org.onlab.onos.net.DeviceId; @@ -30,23 +32,26 @@ import org.onlab.onos.net.device.DeviceService; 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.store.common.StoreService; import org.onlab.onos.store.device.impl.DistributedDeviceStore; import org.onlab.onos.store.impl.StoreManager; +import org.onlab.onos.store.impl.TestStoreManager; +import org.onlab.packet.IpPrefix; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map.Entry; import java.util.Set; -import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import static org.junit.Assert.*; import static org.onlab.onos.net.Device.Type.SWITCH; import static org.onlab.onos.net.DeviceId.deviceId; import static org.onlab.onos.net.device.DeviceEvent.Type.*; -// FIXME This test is painfully slow starting up Hazelcast on each test cases, -// turning it off in repository for now. +// FIXME This test is slow starting up Hazelcast on each test cases. // FIXME DistributedDeviceStore should have it's own test cases. /** @@ -67,6 +72,11 @@ public class DistributedDeviceManagerTest { private static final PortNumber P2 = PortNumber.portNumber(2); private static final PortNumber P3 = PortNumber.portNumber(3); + private static final DefaultControllerNode SELF + = new DefaultControllerNode(new NodeId("foobar"), + IpPrefix.valueOf("127.0.0.1")); + + private DeviceManager mgr; protected StoreManager storeManager; @@ -77,6 +87,8 @@ public class DistributedDeviceManagerTest { protected TestProvider provider; protected TestListener listener = new TestListener(); private DistributedDeviceStore dstore; + private TestMastershipManager masterManager; + private EventDeliveryService eventService; @Before public void setUp() { @@ -84,26 +96,21 @@ public class DistributedDeviceManagerTest { service = mgr; admin = mgr; registry = mgr; - // FIXME should be reading the hazelcast.xml - Config config = new Config(); - // avoid accidentally joining other cluster - config.getGroupConfig().setName(UUID.randomUUID().toString()); - // quickly form single node cluster - config.getNetworkConfig().getJoin() - .getTcpIpConfig() - .setEnabled(true).setConnectionTimeoutSeconds(0); - config.getNetworkConfig().getJoin() - .getMulticastConfig() - .setEnabled(false); + // TODO should find a way to clean Hazelcast instance without shutdown. + Config config = TestStoreManager.getTestConfig(); + + masterManager = new TestMastershipManager(); storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config)); storeManager.activate(); - dstore = new TestDistributedDeviceStore(storeManager); + dstore = new TestDistributedDeviceStore(); dstore.activate(); + mgr.store = dstore; - mgr.eventDispatcher = new TestEventDispatcher(); - mgr.mastershipService = new TestMastershipService(); + eventService = new TestEventDispatcher(); + mgr.eventDispatcher = eventService; + mgr.mastershipService = masterManager; mgr.activate(); service.addListener(listener); @@ -153,7 +160,7 @@ public class DistributedDeviceManagerTest { public void deviceDisconnected() { connectDevice(DID1, SW1); connectDevice(DID2, SW1); - validateEvents(DEVICE_ADDED, DEVICE_ADDED); + validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED); assertTrue("device should be available", service.isAvailable(DID1)); // Disconnect @@ -172,7 +179,7 @@ public class DistributedDeviceManagerTest { @Test public void deviceUpdated() { connectDevice(DID1, SW1); - validateEvents(DEVICE_ADDED); + validateEvents(DEVICE_ADDED, DEVICE_ADDED); connectDevice(DID1, SW2); validateEvents(DEVICE_UPDATED); @@ -192,7 +199,7 @@ public class DistributedDeviceManagerTest { pds.add(new DefaultPortDescription(P2, true)); pds.add(new DefaultPortDescription(P3, true)); providerService.updatePorts(DID1, pds); - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED); + validateEvents(DEVICE_ADDED, DEVICE_ADDED, PORT_ADDED, PORT_ADDED, PORT_ADDED); pds.clear(); pds.add(new DefaultPortDescription(P1, false)); @@ -208,7 +215,7 @@ public class DistributedDeviceManagerTest { pds.add(new DefaultPortDescription(P1, true)); pds.add(new DefaultPortDescription(P2, true)); providerService.updatePorts(DID1, pds); - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED); + validateEvents(DEVICE_ADDED, DEVICE_ADDED, PORT_ADDED, PORT_ADDED); providerService.portStatusChanged(DID1, new DefaultPortDescription(P1, false)); validateEvents(PORT_UPDATED); @@ -223,7 +230,7 @@ public class DistributedDeviceManagerTest { pds.add(new DefaultPortDescription(P1, true)); pds.add(new DefaultPortDescription(P2, true)); providerService.updatePorts(DID1, pds); - validateEvents(DEVICE_ADDED, PORT_ADDED, PORT_ADDED); + validateEvents(DEVICE_ADDED, DEVICE_ADDED, PORT_ADDED, PORT_ADDED); assertEquals("wrong port count", 2, service.getPorts(DID1).size()); Port port = service.getPort(DID1, P1); @@ -237,10 +244,10 @@ public class DistributedDeviceManagerTest { connectDevice(DID2, SW2); assertEquals("incorrect device count", 2, service.getDeviceCount()); admin.removeDevice(DID1); + validateEvents(DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED, DEVICE_ADDED, DEVICE_REMOVED, DEVICE_REMOVED); assertNull("device should not be found", service.getDevice(DID1)); assertNotNull("device should be found", service.getDevice(DID2)); assertEquals("incorrect device count", 1, service.getDeviceCount()); - } protected void validateEvents(Enum... types) { @@ -283,23 +290,21 @@ public class DistributedDeviceManagerTest { } private class TestDistributedDeviceStore extends DistributedDeviceStore { - public TestDistributedDeviceStore(StoreService storeService) { - this.storeService = storeService; + + public TestDistributedDeviceStore() { + this.storeService = storeManager; } } - private class TestStoreManager extends StoreManager { - TestStoreManager(HazelcastInstance instance) { - this.instance = instance; - } + private static class TestMastershipManager extends MastershipServiceAdapter { - @Override - public void activate() { - setupKryoPool(); - } - } + private ConcurrentMap masters = new ConcurrentHashMap<>(); - private static class TestMastershipService extends MastershipServiceAdapter { + public TestMastershipManager() { + // SELF master of all initially + masters.put(DID1, SELF.id()); + masters.put(DID1, SELF.id()); + } @Override public MastershipRole getLocalRole(DeviceId deviceId) { return MastershipRole.MASTER; @@ -307,13 +312,27 @@ public class DistributedDeviceManagerTest { @Override public Set getDevicesOf(NodeId nodeId) { - return Sets.newHashSet(DID1, DID2); + HashSet set = Sets.newHashSet(); + for (Entry e : masters.entrySet()) { + if (e.getValue().equals(nodeId)) { + set.add(e.getKey()); + } + } + return set; } @Override public MastershipRole requestRoleFor(DeviceId deviceId) { - return MastershipRole.MASTER; + if (SELF.id().equals(masters.get(deviceId))) { + return MastershipRole.MASTER; + } else { + return MastershipRole.STANDBY; + } + } + + @Override + public void relinquishMastership(DeviceId deviceId) { + masters.remove(deviceId, SELF.id()); } } - } diff --git a/core/store/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java b/core/store/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java index 2a7f67ae7e..d3fcf3eb77 100644 --- a/core/store/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java +++ b/core/store/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java @@ -4,6 +4,7 @@ import com.google.common.base.Optional; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; import com.hazelcast.core.IMap; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -42,6 +43,7 @@ public class DistributedMastershipStore @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterService clusterService; + @Override @Activate public void activate() { super.activate(); @@ -52,19 +54,28 @@ public class DistributedMastershipStore masters = new AbsentInvalidatingLoadingCache<>(newBuilder().build(nodeLoader)); rawMasters.addEntryListener(new RemoteEventHandler<>(masters), true); + loadMasters(); + log.info("Started"); } + private void loadMasters() { + for (byte[] keyBytes : rawMasters.keySet()) { + final DeviceId id = deserialize(keyBytes); + masters.refresh(id); + } + } + @Deactivate public void deactivate() { log.info("Stopped"); } @Override - public MastershipEvent setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) { + public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { synchronized (this) { NodeId currentMaster = getMaster(deviceId); - if (role == MastershipRole.MASTER && Objects.equals(currentMaster, nodeId)) { + if (Objects.equals(currentMaster, nodeId)) { return null; } @@ -94,7 +105,7 @@ public class DistributedMastershipStore @Override public MastershipRole requestRole(DeviceId deviceId) { // FIXME: for now we are 'selecting' as master whoever asks - setRole(clusterService.getLocalNode().id(), deviceId, MastershipRole.MASTER); + setMaster(clusterService.getLocalNode().id(), deviceId); return MastershipRole.MASTER; } diff --git a/core/store/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java b/core/store/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java index 52e8ed0f89..64ae3c886d 100644 --- a/core/store/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java +++ b/core/store/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java @@ -15,7 +15,6 @@ 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.MastershipRole; import org.onlab.onos.net.Port; import org.onlab.onos.net.PortNumber; import org.onlab.onos.net.device.DeviceDescription; @@ -61,10 +60,6 @@ public class DistributedDeviceStore private IMap rawDevices; private LoadingCache> devices; - // private IMap roles; - private IMap rawRoles; - private LoadingCache> roles; - // private ISet availableDevices; private ISet availableDevices; @@ -73,6 +68,7 @@ public class DistributedDeviceStore private IMap rawDevicePorts; private LoadingCache>> devicePorts; + @Override @Activate public void activate() { super.activate(); @@ -86,14 +82,7 @@ public class DistributedDeviceStore = new OptionalCacheLoader<>(storeService, rawDevices); devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader)); // refresh/populate cache based on notification from other instance - rawDevices.addEntryListener(new RemoteEventHandler<>(devices), includeValue); - - rawRoles = theInstance.getMap("roles"); - final OptionalCacheLoader rolesLoader - = new OptionalCacheLoader<>(storeService, rawRoles); - roles = new AbsentInvalidatingLoadingCache<>(newBuilder().build(rolesLoader)); - // refresh/populate cache based on notification from other instance - rawRoles.addEntryListener(new RemoteEventHandler<>(roles), includeValue); + rawDevices.addEntryListener(new RemoteDeviceEventHandler(devices), includeValue); // TODO cache availableDevices availableDevices = theInstance.getSet("availableDevices"); @@ -103,7 +92,9 @@ public class DistributedDeviceStore = new OptionalCacheLoader<>(storeService, rawDevicePorts); devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader)); // refresh/populate cache based on notification from other instance - rawDevicePorts.addEntryListener(new RemoteEventHandler<>(devicePorts), includeValue); + rawDevicePorts.addEntryListener(new RemotePortEventHandler(devicePorts), includeValue); + + loadDeviceCache(); log.info("Started"); } @@ -115,22 +106,11 @@ public class DistributedDeviceStore @Override public int getDeviceCount() { - // TODO IMap size or cache size? - return rawDevices.size(); + return devices.asMap().size(); } @Override public Iterable getDevices() { -// TODO Revisit if we ever need to do this. -// log.info("{}:{}", rawMap.size(), cache.size()); -// if (rawMap.size() != cache.size()) { -// for (Entry e : rawMap.entrySet()) { -// final DeviceId key = deserialize(e.getKey()); -// final DefaultDevice val = deserialize(e.getValue()); -// cache.put(key, val); -// } -// } - // TODO builder v.s. copyOf. Guava semms to be using copyOf? Builder builder = ImmutableSet.builder(); for (Optional e : devices.asMap().values()) { @@ -141,6 +121,17 @@ public class DistributedDeviceStore return builder.build(); } + private void loadDeviceCache() { + log.info("{}:{}", rawDevices.size(), devices.size()); + if (rawDevices.size() != devices.size()) { + for (Map.Entry e : rawDevices.entrySet()) { + final DeviceId key = deserialize(e.getKey()); + final DefaultDevice val = deserialize(e.getValue()); + devices.put(key, Optional.of(val)); + } + } + } + @Override public Device getDevice(DeviceId deviceId) { // TODO revisit if ignoring exception is safe. @@ -171,12 +162,8 @@ public class DistributedDeviceStore devices.put(deviceId, Optional.of(device)); availableDevices.add(deviceIdBytes); - - // For now claim the device as a master automatically. - //rawRoles.put(deviceIdBytes, serialize(MastershipRole.MASTER)); - //roles.put(deviceId, Optional.of(MastershipRole.MASTER)); } - return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null); + return new DeviceEvent(DEVICE_ADDED, device, null); } // Updates the device and returns the appropriate event if necessary. @@ -348,8 +335,6 @@ public class DistributedDeviceStore public DeviceEvent removeDevice(DeviceId deviceId) { synchronized (this) { byte[] deviceIdBytes = serialize(deviceId); - rawRoles.remove(deviceIdBytes); - roles.invalidate(deviceId); // TODO conditional remove? Device device = deserialize(rawDevices.remove(deviceIdBytes)); @@ -359,6 +344,48 @@ public class DistributedDeviceStore } } - // TODO cache serialized DeviceID if we suffer from serialization cost + private class RemoteDeviceEventHandler extends RemoteEventHandler { + public RemoteDeviceEventHandler(LoadingCache> cache) { + super(cache); + } + @Override + protected void onAdd(DeviceId deviceId, DefaultDevice device) { + delegate.notify(new DeviceEvent(DEVICE_ADDED, device)); + } + + @Override + protected void onRemove(DeviceId deviceId, DefaultDevice device) { + delegate.notify(new DeviceEvent(DEVICE_REMOVED, device)); + } + + @Override + protected void onUpdate(DeviceId deviceId, DefaultDevice device) { + delegate.notify(new DeviceEvent(DEVICE_UPDATED, device)); + } + } + + private class RemotePortEventHandler extends RemoteEventHandler> { + public RemotePortEventHandler(LoadingCache>> cache) { + super(cache); + } + + @Override + protected void onAdd(DeviceId deviceId, Map ports) { +// delegate.notify(new DeviceEvent(PORT_ADDED, getDevice(deviceId))); + } + + @Override + protected void onRemove(DeviceId deviceId, Map ports) { +// delegate.notify(new DeviceEvent(PORT_REMOVED, getDevice(deviceId))); + } + + @Override + protected void onUpdate(DeviceId deviceId, Map ports) { +// delegate.notify(new DeviceEvent(PORT_UPDATED, getDevice(deviceId))); + } + } + + + // TODO cache serialized DeviceID if we suffer from serialization cost } diff --git a/core/store/src/main/java/org/onlab/onos/store/impl/AbstractDistributedStore.java b/core/store/src/main/java/org/onlab/onos/store/impl/AbstractDistributedStore.java index bca585dd70..e7c2d58bc6 100644 --- a/core/store/src/main/java/org/onlab/onos/store/impl/AbstractDistributedStore.java +++ b/core/store/src/main/java/org/onlab/onos/store/impl/AbstractDistributedStore.java @@ -24,7 +24,7 @@ import static org.slf4j.LoggerFactory.getLogger; */ @Component(componentAbstract = true) public abstract class AbstractDistributedStore> - extends AbstractStore { + extends AbstractStore { protected final Logger log = getLogger(getClass()); @@ -66,7 +66,7 @@ public abstract class AbstractDistributedStore IMap key type after deserialization * @param IMap value type after deserialization */ - public final class RemoteEventHandler extends EntryAdapter { + public class RemoteEventHandler extends EntryAdapter { private LoadingCache> cache; @@ -84,20 +84,59 @@ public abstract class AbstractDistributedStore event) { + K key = deserialize(event.getKey()); + V newVal = deserialize(event.getValue()); + Optional newValue = Optional.of(newVal); + cache.asMap().putIfAbsent(key, newValue); + onAdd(key, newVal); + } + @Override public void entryUpdated(EntryEvent event) { - cache.put(storeService.deserialize(event.getKey()), - Optional.of(storeService.deserialize(event.getValue()))); + K key = deserialize(event.getKey()); + V oldVal = deserialize(event.getOldValue()); + Optional oldValue = Optional.fromNullable(oldVal); + V newVal = deserialize(event.getValue()); + Optional newValue = Optional.of(newVal); + cache.asMap().replace(key, oldValue, newValue); + onUpdate(key, newVal); } @Override public void entryRemoved(EntryEvent event) { - cache.invalidate(storeService.deserialize(event.getKey())); + K key = deserialize(event.getKey()); + V val = deserialize(event.getValue()); + cache.invalidate(key); + onRemove(key, val); } - @Override - public void entryAdded(EntryEvent event) { - entryUpdated(event); + /** + * Cache entry addition hook. + * + * @param key new key + * @param newVal new value + */ + protected void onAdd(K key, V newVal) { + } + + /** + * Cache entry update hook. + * + * @param key new key + * @param newVal new value + */ + protected void onUpdate(K key, V newVal) { + } + + /** + * Cache entry remove hook. + * + * @param key new key + * @param val old value + */ + protected void onRemove(K key, V val) { } } diff --git a/core/store/src/main/java/org/onlab/onos/store/impl/StoreManager.java b/core/store/src/main/java/org/onlab/onos/store/impl/StoreManager.java index 77463fd4df..abd8ade831 100644 --- a/core/store/src/main/java/org/onlab/onos/store/impl/StoreManager.java +++ b/core/store/src/main/java/org/onlab/onos/store/impl/StoreManager.java @@ -45,7 +45,7 @@ import java.util.HashMap; @Service public class StoreManager implements StoreService { - private static final String HAZELCAST_XML_FILE = "etc/hazelcast.xml"; + protected static final String HAZELCAST_XML_FILE = "etc/hazelcast.xml"; private final Logger log = LoggerFactory.getLogger(getClass()); diff --git a/core/store/src/test/java/org/onlab/onos/store/impl/TestStoreManager.java b/core/store/src/test/java/org/onlab/onos/store/impl/TestStoreManager.java new file mode 100644 index 0000000000..c9d8821bf9 --- /dev/null +++ b/core/store/src/test/java/org/onlab/onos/store/impl/TestStoreManager.java @@ -0,0 +1,54 @@ +package org.onlab.onos.store.impl; + +import java.io.FileNotFoundException; +import java.util.UUID; + +import com.hazelcast.config.Config; +import com.hazelcast.config.FileSystemXmlConfig; +import com.hazelcast.core.HazelcastInstance; + +/** + * Dummy StoreManager to use specified Hazelcast instance. + */ +public class TestStoreManager extends StoreManager { + + /** + * Gets the Hazelcast Config for testing. + * + * @return + */ + public static Config getTestConfig() { + Config config; + try { + config = new FileSystemXmlConfig(HAZELCAST_XML_FILE); + } catch (FileNotFoundException e) { + // falling back to default + config = new Config(); + } + // avoid accidentally joining other cluster + config.getGroupConfig().setName(UUID.randomUUID().toString()); + // quickly form single node cluster + config.getNetworkConfig().getJoin() + .getTcpIpConfig() + .setEnabled(true).setConnectionTimeoutSeconds(0); + config.getNetworkConfig().getJoin() + .getMulticastConfig() + .setEnabled(false); + return config; + } + + /** + * Constructor. + * + * @param instance Hazelast instance to return on #getHazelcastInstance() + */ + public TestStoreManager(HazelcastInstance instance) { + this.instance = instance; + } + + // Hazelcast setup removed from original code. + @Override + public void activate() { + setupKryoPool(); + } +} diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java index 78d6a4c3a7..9b78798e7c 100644 --- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java +++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleDeviceStore.java @@ -15,8 +15,10 @@ 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; @@ -40,7 +42,9 @@ import static org.slf4j.LoggerFactory.getLogger; */ @Component(immediate = true) @Service -public class SimpleDeviceStore implements DeviceStore { +public class SimpleDeviceStore + extends AbstractStore + implements DeviceStore { private final Logger log = getLogger(getClass()); diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java index 816ea63e2d..38e94aa596 100644 --- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java +++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleFlowRuleStore.java @@ -19,6 +19,8 @@ import org.onlab.onos.net.flow.FlowRule.FlowRuleState; import org.onlab.onos.net.flow.FlowRuleEvent; import org.onlab.onos.net.flow.FlowRuleEvent.Type; import org.onlab.onos.net.flow.FlowRuleStore; +import org.onlab.onos.net.flow.FlowRuleStoreDelegate; +import org.onlab.onos.store.AbstractStore; import org.slf4j.Logger; import com.google.common.collect.ArrayListMultimap; @@ -30,7 +32,9 @@ import com.google.common.collect.Multimap; */ @Component(immediate = true) @Service -public class SimpleFlowRuleStore implements FlowRuleStore { +public class SimpleFlowRuleStore + extends AbstractStore + implements FlowRuleStore { private final Logger log = getLogger(getClass()); diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java index bcd84dff8e..be609a84e4 100644 --- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java +++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleHostStore.java @@ -24,8 +24,10 @@ import org.onlab.onos.net.HostId; import org.onlab.onos.net.host.HostDescription; import org.onlab.onos.net.host.HostEvent; import org.onlab.onos.net.host.HostStore; +import org.onlab.onos.net.host.HostStoreDelegate; import org.onlab.onos.net.host.PortAddresses; import org.onlab.onos.net.provider.ProviderId; +import org.onlab.onos.store.AbstractStore; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; @@ -41,7 +43,9 @@ import com.google.common.collect.Multimap; */ @Component(immediate = true) @Service -public class SimpleHostStore implements HostStore { +public class SimpleHostStore + extends AbstractStore + implements HostStore { private final Logger log = getLogger(getClass()); diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java index 5c99682ef9..ccb2bfbc08 100644 --- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java +++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleLinkStore.java @@ -14,7 +14,9 @@ import org.onlab.onos.net.Link; import org.onlab.onos.net.link.LinkDescription; import org.onlab.onos.net.link.LinkEvent; import org.onlab.onos.net.link.LinkStore; +import org.onlab.onos.net.link.LinkStoreDelegate; import org.onlab.onos.net.provider.ProviderId; +import org.onlab.onos.store.AbstractStore; import org.slf4j.Logger; import java.util.Collections; @@ -35,7 +37,9 @@ import static org.slf4j.LoggerFactory.getLogger; */ @Component(immediate = true) @Service -public class SimpleLinkStore implements LinkStore { +public class SimpleLinkStore + extends AbstractStore + implements LinkStore { private final Logger log = getLogger(getClass()); diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java index 24480c613c..da691fea45 100644 --- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java +++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleMastershipStore.java @@ -3,6 +3,8 @@ package org.onlab.onos.net.trivial.impl; import static org.slf4j.LoggerFactory.getLogger; import java.util.Collections; +import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -15,9 +17,11 @@ 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.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; @@ -25,24 +29,27 @@ import static org.onlab.onos.cluster.MastershipEvent.Type.*; /** * Manages inventory of controller mastership over devices using - * trivial in-memory structures implementation. + * trivial, non-distributed in-memory structures implementation. */ @Component(immediate = true) @Service -public class SimpleMastershipStore implements MastershipStore { - - public static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1"); +public class SimpleMastershipStore + extends AbstractStore + implements MastershipStore { private final Logger log = getLogger(getClass()); - private ControllerNode instance; + public static final IpPrefix LOCALHOST = IpPrefix.valueOf("127.0.0.1"); - protected final ConcurrentMap roleMap = + private ControllerNode instance = + new DefaultControllerNode(new NodeId("local"), LOCALHOST); + + //devices mapped to their masters, to emulate multiple nodes + protected final ConcurrentMap masterMap = new ConcurrentHashMap<>(); @Activate public void activate() { - instance = new DefaultControllerNode(new NodeId("local"), LOCALHOST); log.info("Started"); } @@ -52,23 +59,36 @@ public class SimpleMastershipStore implements MastershipStore { } @Override - public MastershipEvent setRole(NodeId nodeId, DeviceId deviceId, - MastershipRole role) { - if (roleMap.get(deviceId) == null) { - return null; + public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { + + NodeId node = masterMap.get(deviceId); + if (node == null) { + masterMap.put(deviceId, nodeId); + return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); + } + + if (node.equals(nodeId)) { + return null; + } else { + masterMap.put(deviceId, nodeId); + return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); } - roleMap.put(deviceId, role); - return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); } @Override public NodeId getMaster(DeviceId deviceId) { - return instance.id(); + return masterMap.get(deviceId); } @Override public Set getDevices(NodeId nodeId) { - return Collections.unmodifiableSet(roleMap.keySet()); + Set ids = new HashSet<>(); + for (Map.Entry d : masterMap.entrySet()) { + if (d.getValue().equals(nodeId)) { + ids.add(d.getKey()); + } + } + return Collections.unmodifiableSet(ids); } @Override @@ -78,11 +98,18 @@ public class SimpleMastershipStore implements MastershipStore { @Override public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { - MastershipRole role = roleMap.get(deviceId); - if (role == null) { - //say MASTER. If clustered, we'd figure out if anyone's got dibs here. + 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; - roleMap.put(deviceId, role); + masterMap.put(deviceId, nodeId); } return role; } diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java index 5d9c8decff..32cc1f7fc4 100644 --- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java +++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/impl/SimpleTopologyStore.java @@ -18,6 +18,8 @@ import org.onlab.onos.net.topology.TopologyCluster; import org.onlab.onos.net.topology.TopologyEvent; import org.onlab.onos.net.topology.TopologyGraph; import org.onlab.onos.net.topology.TopologyStore; +import org.onlab.onos.net.topology.TopologyStoreDelegate; +import org.onlab.onos.store.AbstractStore; import org.slf4j.Logger; import java.util.List; @@ -31,7 +33,9 @@ import static org.slf4j.LoggerFactory.getLogger; */ @Component(immediate = true) @Service -public class SimpleTopologyStore implements TopologyStore { +public class SimpleTopologyStore + extends AbstractStore + implements TopologyStore { private final Logger log = getLogger(getClass()); diff --git a/openflow/api/pom.xml b/openflow/api/pom.xml index f849e39a9b..2c58e47385 100644 --- a/openflow/api/pom.xml +++ b/openflow/api/pom.xml @@ -16,6 +16,18 @@ ONOS OpenFlow controller subsystem API + + + + sonatype-oss-snapshot + Sonatype OSS snapshot repository + https://oss.sonatype.org/content/repositories/snapshots + + false + + + + org.projectfloodlight diff --git a/pom.xml b/pom.xml index dcd2a6b6e7..1cc5d3be54 100644 --- a/pom.xml +++ b/pom.xml @@ -391,13 +391,13 @@ Network Model & Services - org.onlab.onos:org.onlab.onos.*: + org.onlab.onos:org.onlab.onos.* Core Subsystems - org.onlab.onos.cluster.impl:org.onlab.onos.store: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.cluster: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.net.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.* @@ -422,7 +422,7 @@ Sample Applications - org.onlab.onos.tvue:org.onlab.onos.fwd + org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.foo diff --git a/tools/build/onos-package b/tools/build/onos-package index 8f49516bec..cf751c76c0 100755 --- a/tools/build/onos-package +++ b/tools/build/onos-package @@ -51,7 +51,7 @@ perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-feature $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,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,webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo|' \ $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg # Patch the Apache Karaf distribution with ONOS branding bundle diff --git a/tools/test/bin/onos-config b/tools/test/bin/onos-config index e9f3f0ae8a..9f1e3b0ec5 100755 --- a/tools/test/bin/onos-config +++ b/tools/test/bin/onos-config @@ -8,4 +8,7 @@ remote=$ONOS_USER@${1:-$OCI} -echo "Deprecated!" \ No newline at end of file +ssh $remote " + sudo perl -pi.bak -e \"s/ .*${ONOS_NIC:-192.168.56.*}