Brian O'Connor

Adding Intent Impl and shell command to install simple intent

Showing 26 changed files with 1258 additions and 49 deletions
package org.onlab.onos.cli.net;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentService;
/**
* Lists all shortest-paths paths between the specified source and
* destination devices.
*/
@Command(scope = "onos", name = "add-intent",
description = "Installs HostToHostIntent between the specified source and destination devices")
public class IntentInstallCommand extends AbstractShellCommand {
@Argument(index = 0, name = "src", description = "Source device ID",
required = true, multiValued = false)
String src = null;
@Argument(index = 1, name = "dst", description = "Destination device ID",
required = true, multiValued = false)
String dst = null;
private static long id = 1;
@Override
protected void execute() {
IntentService service = get(IntentService.class);
HostService hosts = get(HostService.class);
HostId srcId = HostId.hostId(src);
HostId dstId = HostId.hostId(dst);
TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
builder
.matchEthSrc(hosts.getHost(srcId).mac())
.matchEthDst(hosts.getHost(dstId).mac());
TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
HostToHostIntent intent = new HostToHostIntent(
new IntentId(id++),
srcId,
dstId,
builder.build(),
treat.build());
log.info("Adding intent {}", intent);
service.submit(intent);
}
}
......@@ -57,6 +57,13 @@
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.IntentInstallCommand"/>
<completers>
<ref component-id="hostIdCompleter"/>
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.ClustersListCommand"/>
</command>
<command>
......
package org.onlab.onos.net.intent;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import com.google.common.base.MoreObjects;
/**
* Abstraction of point-to-point connectivity.
*/
public class HostToHostIntent extends ConnectivityIntent {
private final HostId src;
private final HostId dst;
/**
* Creates a new point-to-point intent with the supplied ingress/egress
* ports.
*
* @param id intent identifier
* @param match traffic match
* @param action action
* @param ingressPort ingress port
* @param egressPort egress port
* @throws NullPointerException if {@code ingressPort} or {@code egressPort}
* is null.
*/
public HostToHostIntent(IntentId id, HostId src, HostId dst,
TrafficSelector selector, TrafficTreatment treatment) {
super(id, selector, treatment);
this.src = checkNotNull(src);
this.dst = checkNotNull(dst);
}
/**
* Returns the port on which the ingress traffic should be connected to the
* egress.
*
* @return ingress port
*/
public HostId getSrc() {
return src;
}
/**
* Returns the port on which the traffic should egress.
*
* @return egress port
*/
public HostId getDst() {
return dst;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
HostToHostIntent that = (HostToHostIntent) o;
return Objects.equals(this.src, that.src)
&& Objects.equals(this.dst, that.dst);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), src, dst);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("id", getId())
.add("selector", getTrafficSelector())
.add("treatmetn", getTrafficTreatment())
.add("src", src)
.add("dst", dst)
.toString();
}
}
......@@ -4,14 +4,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import org.onlab.onos.event.AbstractEvent;
import com.google.common.base.MoreObjects;
/**
* A class to represent an intent related event.
*/
public class IntentEvent {
public class IntentEvent extends AbstractEvent<IntentState, Intent> {
// TODO: determine a suitable parent class; if one does not exist, consider introducing one
// TODO: determine a suitable parent class; if one does not exist, consider
// introducing one
private final long time;
private final Intent intent;
......@@ -28,6 +31,7 @@ public class IntentEvent {
* @throws NullPointerException if the intent or state is null
*/
public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
super(state, intent);
this.intent = checkNotNull(intent);
this.state = checkNotNull(state);
this.previous = previous;
......@@ -35,16 +39,6 @@ public class IntentEvent {
}
/**
* Constructor for serializer.
*/
protected IntentEvent() {
this.intent = null;
this.state = null;
this.previous = null;
this.time = 0;
}
/**
* Returns the state of the intent which caused the event.
*
* @return the state of the intent
......
package org.onlab.onos.net.intent;
import org.onlab.onos.event.EventListener;
/**
* Listener for {@link IntentEvent intent events}.
*/
public interface IntentEventListener {
/**
* Processes the specified intent event.
*
* @param event the event to process
*/
void event(IntentEvent event);
public interface IntentListener extends EventListener<IntentEvent> {
}
......
package org.onlab.onos.net.intent;
import java.util.Set;
/**
* Service for application submitting or withdrawing their intents.
......@@ -9,8 +8,8 @@ public interface IntentService {
/**
* Submits an intent into the system.
*
* This is an asynchronous request meaning that any compiling
* or installation activities may be done at later time.
* This is an asynchronous request meaning that any compiling or
* installation activities may be done at later time.
*
* @param intent intent to be submitted
*/
......@@ -19,8 +18,8 @@ public interface IntentService {
/**
* Withdraws an intent from the system.
*
* This is an asynchronous request meaning that the environment
* may be affected at later time.
* This is an asynchronous request meaning that the environment may be
* affected at later time.
*
* @param intent intent to be withdrawn
*/
......@@ -30,19 +29,26 @@ public interface IntentService {
* Submits a batch of submit &amp; withdraw operations. Such a batch is
* assumed to be processed together.
*
* This is an asynchronous request meaning that the environment
* may be affected at later time.
* This is an asynchronous request meaning that the environment may be
* affected at later time.
*
* @param operations batch of intent operations
*/
void execute(IntentOperations operations);
/**
* Returns immutable set of intents currently in the system.
* Returns an iterable of intents currently in the system.
*
* @return set of intents
*/
Set<Intent> getIntents();
Iterable<Intent> getIntents();
/**
* Returns the number of intents currently in the system.
*
* @return number of intents
*/
long getIntentCount();
/**
* Retrieves the intent specified by its identifier.
......@@ -56,7 +62,8 @@ public interface IntentService {
* Retrieves the state of an intent by its identifier.
*
* @param id intent identifier
* @return the intent state or null if one with the given identifier is not found
* @return the intent state or null if one with the given identifier is not
* found
*/
IntentState getIntentState(IntentId id);
......@@ -65,12 +72,12 @@ public interface IntentService {
*
* @param listener listener to be added
*/
void addListener(IntentEventListener listener);
void addListener(IntentListener listener);
/**
* Removes the specified listener for intent events.
*
* @param listener listener to be removed
*/
void removeListener(IntentEventListener listener);
void removeListener(IntentListener listener);
}
......
package org.onlab.onos.net.intent;
import java.util.List;
import org.onlab.onos.store.Store;
/**
* Manages inventory of end-station intents; not intended for direct use.
*/
public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
/**
* Creates a new intent.
*
* @param intent intent
* @return appropriate event or null if no change resulted
*/
IntentEvent createIntent(Intent intent);
/**
* Removes the specified intent from the inventory.
*
* @param intentId intent identification
* @return remove event or null if intent was not found
*/
IntentEvent removeIntent(IntentId intent);
/**
* Returns the number of intents in the store.
*
*/
long getIntentCount();
/**
* Returns a collection of all intents in the store.
*
* @return iterable collection of all intents
*/
Iterable<Intent> getIntents();
/**
* Returns the intent with the specified identifer.
*
* @param intentId intent identification
* @return intent or null if not found
*/
Intent getIntent(IntentId intentId);
IntentState getIntentState(IntentId id);
/**
* Sets the state of the specified intent to the new state.
*
* @param intent intent whose state is to be changed
* @param newState new state
*/
IntentEvent setState(Intent intent, IntentState newState);
IntentEvent addInstallableIntents(IntentId intentId, List<InstallableIntent> result);
List<InstallableIntent> getInstallableIntents(IntentId intentId);
void removeInstalledIntents(IntentId intentId);
}
package org.onlab.onos.net.intent;
import org.onlab.onos.store.StoreDelegate;
/**
* Infrastructure link store delegate abstraction.
*/
public interface IntentStoreDelegate extends StoreDelegate<IntentEvent> {
}
......@@ -12,7 +12,7 @@ import com.google.common.base.MoreObjects;
/**
* Abstraction of explicitly path specified connectivity intent.
*/
public class PathIntent extends PointToPointIntent {
public class PathIntent extends PointToPointIntent implements InstallableIntent {
private final Path path;
......
......@@ -11,19 +11,19 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Fake implementation of the intent service to assist in developing tests
* of the interface contract.
* Fake implementation of the intent service to assist in developing tests of
* the interface contract.
*/
public class FakeIntentManager implements TestableIntentService {
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> intentStates = new HashMap<>();
private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
private final Set<IntentEventListener> listeners = new HashSet<>();
private final Set<IntentListener> listeners = new HashSet<>();
private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
private final Map<Class<? extends InstallableIntent>,
IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>();
private final Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> installers
= new HashMap<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<IntentException> exceptions = new ArrayList<>();
......@@ -76,7 +76,8 @@ public class FakeIntentManager implements TestableIntentService {
private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent
.getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
......@@ -125,7 +126,6 @@ public class FakeIntentManager implements TestableIntentService {
}
}
// Sets the internal state for the given intent and dispatches an event
private void setState(Intent intent, IntentState state) {
IntentState previous = intentStates.get(intent.getId());
......@@ -175,6 +175,11 @@ public class FakeIntentManager implements TestableIntentService {
}
@Override
public long getIntentCount() {
return intents.size();
}
@Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
......@@ -185,23 +190,24 @@ public class FakeIntentManager implements TestableIntentService {
}
@Override
public void addListener(IntentEventListener listener) {
public void addListener(IntentListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(IntentEventListener listener) {
public void removeListener(IntentListener listener) {
listeners.remove(listener);
}
private void dispatch(IntentEvent event) {
for (IntentEventListener listener : listeners) {
for (IntentListener listener : listeners) {
listener.event(event);
}
}
@Override
public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
public <T extends Intent> void registerCompiler(Class<T> cls,
IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
......@@ -216,7 +222,8 @@ public class FakeIntentManager implements TestableIntentService {
}
@Override
public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
public <T extends InstallableIntent> void registerInstaller(Class<T> cls,
IntentInstaller<T> installer) {
installers.put(cls, installer);
}
......@@ -252,7 +259,8 @@ public class FakeIntentManager implements TestableIntentService {
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the InstallableIntent class descendants
// As long as we're within the InstallableIntent class
// descendants
if (InstallableIntent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
......
......@@ -51,7 +51,7 @@ public class IntentServiceTest {
@Test
public void basics() {
// Make sure there are no intents
assertEquals("incorrect intent count", 0, service.getIntents().size());
assertEquals("incorrect intent count", 0, service.getIntentCount());
// Register a compiler and an installer both setup for success.
service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
......@@ -73,8 +73,7 @@ public class IntentServiceTest {
validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
// Make sure there is just one intent (and is ours)
assertEquals("incorrect intent count", 1, service.getIntents().size());
assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
assertEquals("incorrect intent count", 1, service.getIntentCount());
// Reset the listener events
listener.events.clear();
......@@ -250,7 +249,7 @@ public class IntentServiceTest {
// Fixture to track emitted intent events
protected class TestListener implements IntentEventListener {
protected class TestListener implements IntentListener {
final List<IntentEvent> events = new ArrayList<>();
@Override
......
package org.onlab.onos.net.intent.impl;
import org.onlab.onos.net.intent.IdGenerator;
/**
* Base class of {@link IdGenerator} implementations which use {@link IdBlockAllocator} as
* backend.
*
* @param <T> the type of ID
*/
public abstract class AbstractBlockAllocatorBasedIdGenerator<T> implements IdGenerator<T> {
protected final IdBlockAllocator allocator;
protected IdBlock idBlock;
/**
* Constructs an ID generator which use {@link IdBlockAllocator} as backend.
*
* @param allocator
*/
protected AbstractBlockAllocatorBasedIdGenerator(IdBlockAllocator allocator) {
this.allocator = allocator;
this.idBlock = allocator.allocateUniqueIdBlock();
}
@Override
public synchronized T getNewId() {
try {
return convertFrom(idBlock.getNextId());
} catch (UnavailableIdException e) {
idBlock = allocator.allocateUniqueIdBlock();
return convertFrom(idBlock.getNextId());
}
}
/**
* Returns an ID instance of {@code T} type from the long value.
*
* @param value original long value
* @return ID instance
*/
protected abstract T convertFrom(long value);
}
package org.onlab.onos.net.intent.impl;
public class DummyIdBlockAllocator implements IdBlockAllocator {
private long blockTop;
private static final long BLOCK_SIZE = 0x1000000L;
/**
* Returns a block of IDs which are unique and unused.
* Range of IDs is fixed size and is assigned incrementally as this method
* called.
*
* @return an IdBlock containing a set of unique IDs
*/
@Override
public IdBlock allocateUniqueIdBlock() {
synchronized (this) {
long blockHead = blockTop;
long blockTail = blockTop + BLOCK_SIZE;
IdBlock block = new IdBlock(blockHead, BLOCK_SIZE);
blockTop = blockTail;
return block;
}
}
@Override
public IdBlock allocateUniqueIdBlock(long range) {
throw new UnsupportedOperationException("Not supported yet");
}
}
package org.onlab.onos.net.intent.impl;
import java.util.Arrays;
import java.util.List;
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;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.IdGenerator;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentCompiler;
import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.topology.PathService;
/**
* A intent compiler for {@link HostToHostIntent}.
*/
@Component(immediate = true)
public class HostToHostIntentCompiler
implements IntentCompiler<HostToHostIntent> {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentExtensionService intentManager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PathService pathService;
private IdGenerator<IntentId> intentIdGenerator;
@Activate
public void activate() {
IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
intentManager.registerCompiler(HostToHostIntent.class, this);
}
@Deactivate
public void deactivate() {
intentManager.unregisterCompiler(HostToHostIntent.class);
}
@Override
public List<Intent> compile(HostToHostIntent intent) {
Set<Path> paths = pathService.getPaths(intent.getSrc(), intent.getDst());
if (paths.isEmpty()) {
throw new PathNotFoundException();
}
Path path = paths.iterator().next();
return Arrays.asList((Intent) new PathIntent(
intentIdGenerator.getNewId(),
intent.getTrafficSelector(),
intent.getTrafficTreatment(),
new ConnectPoint(intent.getSrc(), PortNumber.ALL),
new ConnectPoint(intent.getDst(), PortNumber.ALL),
path));
}
}
package org.onlab.onos.net.intent.impl;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import com.google.common.base.MoreObjects;
/**
* A class representing an ID space.
*/
public final class IdBlock {
private final long start;
private final long size;
private final AtomicLong currentId;
/**
* Constructs a new ID block with the specified size and initial value.
*
* @param start initial value of the block
* @param size size of the block
* @throws IllegalArgumentException if the size is less than or equal to 0
*/
public IdBlock(long start, long size) {
checkArgument(size > 0, "size should be more than 0, but %s", size);
this.start = start;
this.size = size;
this.currentId = new AtomicLong(start);
}
// TODO: consider if this method is needed or not
/**
* Returns the initial value.
*
* @return initial value
*/
public long getStart() {
return start;
}
// TODO: consider if this method is needed or not
/**
* Returns the last value.
*
* @return last value
*/
public long getEnd() {
return start + size - 1;
}
/**
* Returns the block size.
*
* @return block size
*/
public long getSize() {
return size;
}
/**
* Returns the next ID in the block.
*
* @return next ID
* @throws UnavailableIdException if there is no available ID in the block.
*/
public long getNextId() {
final long id = currentId.getAndIncrement();
if (id > getEnd()) {
throw new UnavailableIdException(String.format(
"used all IDs in allocated space (size: %d, end: %d, current: %d)",
size, getEnd(), id
));
}
return id;
}
// TODO: Do we really need equals and hashCode? Should it contain currentId?
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
IdBlock that = (IdBlock) o;
return Objects.equals(this.start, that.start)
&& Objects.equals(this.size, that.size)
&& Objects.equals(this.currentId.get(), that.currentId.get());
}
@Override
public int hashCode() {
return Objects.hash(start, size, currentId);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("start", start)
.add("size", size)
.add("currentId", currentId)
.toString();
}
}
package org.onlab.onos.net.intent.impl;
/**
* An interface that gives unique ID spaces.
*/
public interface IdBlockAllocator {
/**
* Allocates a unique Id Block.
*
* @return Id Block.
*/
IdBlock allocateUniqueIdBlock();
/**
* Allocates next unique id and retrieve a new range of ids if needed.
*
* @param range range to use for the identifier
* @return Id Block.
*/
IdBlock allocateUniqueIdBlock(long range);
}
package org.onlab.onos.net.intent.impl;
import org.onlab.onos.net.intent.IntentId;
/**
* An implementation of {@link net.onrc.onos.core.util.IdGenerator} of intent ID,
* which uses {@link IdBlockAllocator}.
*/
public class IdBlockAllocatorBasedIntentIdGenerator extends AbstractBlockAllocatorBasedIdGenerator<IntentId> {
/**
* Constructs an intent ID generator, which uses the specified ID block allocator
* to generate a global unique intent ID.
*
* @param allocator the ID block allocator to use for generating intent IDs
*/
public IdBlockAllocatorBasedIntentIdGenerator(IdBlockAllocator allocator) {
super(allocator);
}
@Override
protected IntentId convertFrom(long value) {
return new IntentId(value);
}
}
package org.onlab.onos.net.intent.impl;
import org.onlab.onos.net.intent.IntentException;
/**
* An exception thrown when a intent compilation fails.
*/
public class IntentCompilationException extends IntentException {
private static final long serialVersionUID = 235237603018210810L;
public IntentCompilationException() {
super();
}
public IntentCompilationException(String message) {
super(message);
}
public IntentCompilationException(String message, Throwable cause) {
super(message, cause);
}
}
package org.onlab.onos.net.intent.impl;
import org.onlab.onos.net.intent.IntentException;
/**
* An exception thrown when intent installation fails.
*/
public class IntentInstallationException extends IntentException {
private static final long serialVersionUID = 3720268258616014168L;
public IntentInstallationException() {
super();
}
public IntentInstallationException(String message) {
super(message);
}
public IntentInstallationException(String message, Throwable cause) {
super(message, cause);
}
}
package org.onlab.onos.net.intent.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.intent.IntentState.FAILED;
import static org.onlab.onos.net.intent.IntentState.INSTALLED;
import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.net.intent.InstallableIntent;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentCompiler;
import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentException;
import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentInstaller;
import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.IntentOperations;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.IntentState;
import org.onlab.onos.net.intent.IntentStore;
import org.onlab.onos.net.intent.IntentStoreDelegate;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableMap;
/**
* An implementation of Intent Manager.
*/
@Component(immediate = true)
@Service
public class IntentManager
implements IntentService, IntentExtensionService {
private final Logger log = getLogger(getClass());
public static final String INTENT_NULL = "Intent cannot be null";
public static final String INTENT_ID_NULL = "Intent ID cannot be null";
// Collections for compiler, installer, and listener are ONOS instance local
private final ConcurrentMap<Class<? extends Intent>,
IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
private final ConcurrentMap<Class<? extends InstallableIntent>,
IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
private final CopyOnWriteArrayList<IntentListener> listeners = new CopyOnWriteArrayList<>();
private final AbstractListenerRegistry<IntentEvent, IntentListener>
listenerRegistry = new AbstractListenerRegistry<>();
private final IntentStoreDelegate delegate = new InternalStoreDelegate();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentStore store;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
@Activate
public void activate() {
store.setDelegate(delegate);
eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
// this.intentEvents = new IntentMap<>("intentState", IntentEvent.class, collectionsService);
// this.installableIntents =
// new IntentMap<>("installableIntents", IntentCompilationResult.class, collectionsService);
//
//
// this.intentEvents.addListener(new InternalEntryListener(new InternalIntentEventListener()));
log.info("Started");
}
@Deactivate
public void deactivate() {
store.unsetDelegate(delegate);
eventDispatcher.removeSink(IntentEvent.class);
log.info("Stopped");
}
@Override
public void submit(Intent intent) {
checkNotNull(intent, INTENT_NULL);
registerSubclassCompilerIfNeeded(intent);
IntentEvent event = store.createIntent(intent);
eventDispatcher.post(event);
processStoreEvent(event);
}
@Override
public void withdraw(Intent intent) {
checkNotNull(intent, INTENT_NULL);
IntentEvent event = store.setState(intent, WITHDRAWING);
eventDispatcher.post(event);
processStoreEvent(event);
}
// FIXME: implement this method
@Override
public void execute(IntentOperations operations) {
throw new UnsupportedOperationException("execute() is not implemented yet");
}
@Override
public Iterable<Intent> getIntents() {
return store.getIntents();
}
@Override
public long getIntentCount() {
return store.getIntentCount();
}
@Override
public Intent getIntent(IntentId id) {
checkNotNull(id, INTENT_ID_NULL);
return store.getIntent(id);
}
@Override
public IntentState getIntentState(IntentId id) {
checkNotNull(id, INTENT_ID_NULL);
return store.getIntentState(id);
}
@Override
public void addListener(IntentListener listener) {
listenerRegistry.addListener(listener);
}
@Override
public void removeListener(IntentListener listener) {
listenerRegistry.removeListener(listener);
}
@Override
public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
@Override
public <T extends Intent> void unregisterCompiler(Class<T> cls) {
compilers.remove(cls);
}
@Override
public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
return ImmutableMap.copyOf(compilers);
}
@Override
public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
installers.put(cls, installer);
}
@Override
public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
installers.remove(cls);
}
@Override
public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
return ImmutableMap.copyOf(installers);
}
/**
* Invokes all of registered intent event listener.
*
* @param event event supplied to a listener as an argument
*/
private void invokeListeners(IntentEvent event) {
for (IntentListener listener : listeners) {
listener.event(event);
}
}
/**
* Returns the corresponding intent compiler to the specified intent.
*
* @param intent intent
* @param <T> the type of intent
* @return intent compiler corresponding to the specified intent
*/
private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
@SuppressWarnings("unchecked")
IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
if (compiler == null) {
throw new IntentException("no compiler for class " + intent.getClass());
}
return compiler;
}
/**
* Returns the corresponding intent installer to the specified installable intent.
* @param intent intent
* @param <T> the type of installable intent
* @return intent installer corresponding to the specified installable intent
*/
private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
return installer;
}
/**
* Compiles an intent.
*
* @param intent intent
*/
private void compileIntent(Intent intent) {
// FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
// TODO: implement compilation traversing tree structure
List<InstallableIntent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) {
installable.add((InstallableIntent) compiled);
}
IntentEvent event = store.addInstallableIntents(intent.getId(), installable);
eventDispatcher.post(event);
processStoreEvent(event);
}
/**
* Installs an intent.
*
* @param intent intent
*/
private void installIntent(Intent intent) {
for (InstallableIntent installable : store.getInstallableIntents(intent.getId())) {
registerSubclassInstallerIfNeeded(installable);
getInstaller(installable).install(installable);
}
IntentEvent event = store.setState(intent, INSTALLED);
eventDispatcher.post(event);
processStoreEvent(event);
}
/**
* Uninstalls an intent.
*
* @param intent intent
*/
private void uninstallIntent(Intent intent) {
for (InstallableIntent installable : store.getInstallableIntents(intent.getId())) {
getInstaller(installable).uninstall(installable);
}
store.removeInstalledIntents(intent.getId());
store.setState(intent, WITHDRAWN);
}
/**
* Registers an intent compiler of the specified intent if an intent compiler
* for the intent is not registered. This method traverses the class hierarchy of
* the intent. Once an intent compiler for a parent type is found, this method
* registers the found intent compiler.
*
* @param intent intent
*/
private void registerSubclassCompilerIfNeeded(Intent intent) {
if (!compilers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the Intent class descendants
if (Intent.class.isAssignableFrom(cls)) {
IntentCompiler<?> compiler = compilers.get(cls);
if (compiler != null) {
compilers.put(intent.getClass(), compiler);
return;
}
}
cls = cls.getSuperclass();
}
}
}
/**
* Registers an intent installer of the specified intent if an intent installer
* for the intent is not registered. This method traverses the class hierarchy of
* the intent. Once an intent installer for a parent type is found, this method
* registers the found intent installer.
*
* @param intent intent
*/
private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the InstallableIntent class descendants
if (InstallableIntent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
installers.put(intent.getClass(), installer);
return;
}
}
cls = cls.getSuperclass();
}
}
}
/**
* Handles state transition of submitted intents.
*/
private void processStoreEvent(IntentEvent event) {
invokeListeners(event);
Intent intent = event.getIntent();
try {
switch (event.getState()) {
case SUBMITTED:
compileIntent(intent);
break;
case COMPILED:
installIntent(intent);
break;
case INSTALLED:
break;
case WITHDRAWING:
uninstallIntent(intent);
break;
case WITHDRAWN:
break;
case FAILED:
break;
default:
throw new IllegalStateException(
"the state of IntentEvent is illegal: " + event.getState());
}
} catch (IntentException e) {
store.setState(intent, FAILED);
}
}
// Store delegate to re-post events emitted from the store.
private class InternalStoreDelegate implements IntentStoreDelegate {
@Override
public void notify(IntentEvent event) {
processStoreEvent(event);
eventDispatcher.post(event);
}
}
}
package org.onlab.onos.net.intent.impl;
import org.onlab.onos.net.intent.IntentException;
/**
* An exception thrown when intent removal failed.
*/
public class IntentRemovalException extends IntentException {
private static final long serialVersionUID = -5259226322037891951L;
public IntentRemovalException() {
super();
}
public IntentRemovalException(String message) {
super(message);
}
public IntentRemovalException(String message, Throwable cause) {
super(message, cause);
}
}
package org.onlab.onos.net.intent.impl;
import java.util.Iterator;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.FlowRuleService;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.IntentInstaller;
import org.onlab.onos.net.intent.PathIntent;
/**
* An intent installer for {@link PathIntent}.
*/
@Component(immediate = true)
public class PathIntentInstaller
implements IntentInstaller<PathIntent> {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentExtensionService intentManager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private FlowRuleService flowRuleService;
private final ApplicationId appId = ApplicationId.valueOf(1);
@Activate
public void activate() {
intentManager.registerInstaller(PathIntent.class, this);
}
@Deactivate
public void deactivate() {
intentManager.unregisterInstaller(PathIntent.class);
}
@Override
public void install(PathIntent intent) {
TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
TrafficSelector selector = intent.getTrafficSelector();
for (Criterion c : selector.criteria()) {
builder.add(c);
}
Iterator<Link> links = intent.getPath().links().iterator();
ConnectPoint prev = links.next().dst();
while (links.hasNext()) {
builder.matchInport(prev.port());
Link link = links.next();
TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
treat.setOutput(link.src().port());
FlowRule f = new DefaultFlowRule(link.src().deviceId(),
builder.build(), treat.build(), 0, appId);
flowRuleService.applyFlowRules(f);
prev = link.dst();
}
}
@Override
public void uninstall(PathIntent intent) {
//TODO
}
}
package org.onlab.onos.net.intent.impl;
import org.onlab.onos.net.intent.IntentException;
/**
* An exception thrown when a path is not found.
*/
public class PathNotFoundException extends IntentException {
private static final long serialVersionUID = -2087045731049914733L;
public PathNotFoundException() {
super();
}
public PathNotFoundException(String message) {
super(message);
}
public PathNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
package org.onlab.onos.net.intent.impl;
/**
* Represents that there is no available IDs.
*/
public class UnavailableIdException extends RuntimeException {
private static final long serialVersionUID = -2287403908433720122L;
/**
* Constructs an exception with no message and no underlying cause.
*/
public UnavailableIdException() {
}
/**
* Constructs an exception with the specified message.
*
* @param message the message describing the specific nature of the error
*/
public UnavailableIdException(String message) {
super(message);
}
/**
* Constructs an exception with the specified message and the underlying cause.
*
* @param message the message describing the specific nature of the error
* @param cause the underlying cause of this error
*/
public UnavailableIdException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* Intent Service Implementation. TODO
*/
package org.onlab.onos.net.intent.impl;
\ No newline at end of file
package org.onlab.onos.store.trivial.impl;
import static org.onlab.onos.net.intent.IntentState.COMPILED;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.intent.InstallableIntent;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentState;
import org.onlab.onos.net.intent.IntentStore;
import org.onlab.onos.net.intent.IntentStoreDelegate;
import org.onlab.onos.store.AbstractStore;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableSet;
@Component(immediate = true)
@Service
public class SimpleIntentStore
extends AbstractStore<IntentEvent, IntentStoreDelegate>
implements IntentStore {
private final Logger log = getLogger(getClass());
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> states = new HashMap<>();
private final Map<IntentId, List<InstallableIntent>> installable = new HashMap<>();
@Activate
public void activate() {
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public IntentEvent createIntent(Intent intent) {
intents.put(intent.getId(), intent);
return this.setState(intent, IntentState.SUBMITTED);
}
@Override
public IntentEvent removeIntent(IntentId intentId) {
Intent intent = intents.remove(intentId);
installable.remove(intentId);
IntentEvent event = this.setState(intent, IntentState.WITHDRAWN);
states.remove(intentId);
return event;
}
@Override
public long getIntentCount() {
return intents.size();
}
@Override
public Iterable<Intent> getIntents() {
return ImmutableSet.copyOf(intents.values());
}
@Override
public Intent getIntent(IntentId intentId) {
return intents.get(intentId);
}
@Override
public IntentState getIntentState(IntentId id) {
return states.get(id);
}
// TODO return dispatch event here... replace with state transition methods
@Override
public IntentEvent setState(Intent intent, IntentState newState) {
IntentId id = intent.getId();
IntentState oldState = states.get(id);
states.put(id, newState);
return new IntentEvent(intent, newState, oldState, System.currentTimeMillis());
}
@Override
public IntentEvent addInstallableIntents(IntentId intentId, List<InstallableIntent> result) {
installable.put(intentId, result);
return this.setState(intents.get(intentId), COMPILED);
}
@Override
public List<InstallableIntent> getInstallableIntents(IntentId intentId) {
return installable.get(intentId);
}
@Override
public void removeInstalledIntents(IntentId intentId) {
installable.remove(intentId);
}
}