Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Conflicts:
core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
core/store/hz/cluster/src/main/java/org/onlab/onos/store/cluster/impl/DistributedMastershipStore.java
Change-Id: Ia1274657b27e01366a4a87196a13068d7104ee80
diff --git a/core/net/src/main/java/org/onlab/onos/cluster/impl/CoreManager.java b/core/net/src/main/java/org/onlab/onos/cluster/impl/CoreManager.java
new file mode 100644
index 0000000..4b1191f
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/cluster/impl/CoreManager.java
@@ -0,0 +1,38 @@
+package org.onlab.onos.cluster.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.CoreService;
+import org.onlab.onos.Version;
+import org.onlab.util.Tools;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Core service implementation.
+ */
+@Component
+@Service
+public class CoreManager implements CoreService {
+
+ private static final File VERSION_FILE = new File("../VERSION");
+ private static Version version = Version.version("1.0.0-SNAPSHOT");
+
+ // TODO: work in progress
+
+ @Activate
+ public void activate() {
+ List<String> versionLines = Tools.slurp(VERSION_FILE);
+ if (versionLines != null && !versionLines.isEmpty()) {
+ version = Version.version(versionLines.get(0));
+ }
+ }
+
+ @Override
+ public Version version() {
+ return version;
+ }
+
+}
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 5400fb0..a8d63c1 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
@@ -18,6 +18,7 @@
import org.onlab.onos.cluster.MastershipService;
import org.onlab.onos.cluster.MastershipTermService;
import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.net.Device;
@@ -38,7 +39,7 @@
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
-import org.onlab.onos.store.ClockService;
+import org.onlab.onos.store.ClockProviderService;
import org.slf4j.Logger;
/**
@@ -80,7 +81,7 @@
protected MastershipTermService termService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClockService clockService;
+ protected ClockProviderService clockProviderService;
@Activate
public void activate() {
@@ -144,6 +145,10 @@
private void applyRole(DeviceId deviceId, MastershipRole newRole) {
if (newRole.equals(MastershipRole.NONE)) {
Device device = store.getDevice(deviceId);
+ // FIXME: Device might not be there yet. (eventual consistent)
+ if (device == null) {
+ return;
+ }
DeviceProvider provider = getProvider(device.providerId());
if (provider != null) {
provider.roleChanged(device, newRole);
@@ -193,13 +198,50 @@
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
checkValidity();
+
+ log.info("Device {} connected", deviceId);
+ // check my Role
+ MastershipRole role = mastershipService.requestRoleFor(deviceId);
+
+ if (role != MastershipRole.MASTER) {
+ // TODO: Do we need to explicitly tell the Provider that
+ // this instance is no longer the MASTER? probably not
+ return;
+ }
+
+ MastershipTerm term = mastershipService.requestTermService()
+ .getMastershipTerm(deviceId);
+ if (!term.master().equals(clusterService.getLocalNode().id())) {
+ // lost mastership after requestRole told this instance was MASTER.
+ return;
+ }
+ // tell clock provider if this instance is the master
+ clockProviderService.setMastershipTerm(deviceId, term);
+
DeviceEvent event = store.createOrUpdateDevice(provider().id(),
deviceId, deviceDescription);
+ // If there was a change of any kind, tell the provider
+ // that this instance is the master.
+ // Note: event can be null, if mastership was lost between
+ // roleRequest and store update calls.
if (event != null) {
- log.info("Device {} connected", deviceId);
- provider().roleChanged(event.subject(),
- mastershipService.requestRoleFor(deviceId));
+ // TODO: Check switch reconnected case. Is it assured that
+ // event will never be null?
+ // Could there be a situation MastershipService told this
+ // instance is the new Master, but
+ // event returned from the store is null?
+
+ // TODO: Confirm: Mastership could be lost after requestRole
+ // and createOrUpdateDevice call.
+ // In that case STANDBY node can
+ // claim itself to be master against the Device.
+ // Will the Node, chosen by the MastershipService, retry
+ // to get the MASTER role when that happen?
+
+ // FIXME: 1st argument should be deviceId, to allow setting
+ // certain roles even if the store returned null.
+ provider().roleChanged(event.subject(), role);
post(event);
}
}
@@ -208,6 +250,15 @@
public void deviceDisconnected(DeviceId deviceId) {
checkNotNull(deviceId, DEVICE_ID_NULL);
checkValidity();
+
+ // FIXME: only the MASTER should be marking off-line in normal cases,
+ // but if I was the last STANDBY connection, etc. and no one else
+ // was there to mark the device offline, this instance may need to
+ // temporarily request for Master Role and mark offline.
+ if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
+ log.debug("Device {} disconnected, but I am not the master", deviceId);
+ return;
+ }
DeviceEvent event = store.markOffline(deviceId);
//we're no longer capable of being master or a candidate.
mastershipService.relinquishMastership(deviceId);
@@ -253,6 +304,9 @@
// FIXME: implement response to this notification
log.warn("Failed to assert role [{}] onto Device {}", role,
deviceId);
+ if (role == MastershipRole.MASTER) {
+ mastershipService.relinquishMastership(deviceId);
+ }
}
}
@@ -268,11 +322,17 @@
@Override
public void event(MastershipEvent event) {
- DeviceId did = event.subject();
+ final DeviceId did = event.subject();
if (isAvailable(did)) {
- if (event.master().equals(clusterService.getLocalNode().id())) {
+ final NodeId myNodeId = clusterService.getLocalNode().id();
+
+ if (myNodeId.equals(event.master())) {
MastershipTerm term = termService.getMastershipTerm(did);
- clockService.setMastershipTerm(did, term);
+
+ if (term.master().equals(myNodeId)) {
+ // only set the new term if I am the master
+ clockProviderService.setMastershipTerm(did, term);
+ }
applyRole(did, MastershipRole.MASTER);
} else {
applyRole(did, MastershipRole.STANDBY);
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 00619b3..a9eddd8 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
@@ -5,9 +5,10 @@
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -21,7 +22,11 @@
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.CompletedBatchOperation;
+import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleListener;
import org.onlab.onos.net.flow.FlowRuleProvider;
@@ -34,7 +39,9 @@
import org.onlab.onos.net.provider.AbstractProviderService;
import org.slf4j.Logger;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
/**
* Provides implementation of the flow NB & SB APIs.
@@ -42,14 +49,14 @@
@Component(immediate = true)
@Service
public class FlowRuleManager
-extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
-implements FlowRuleService, FlowRuleProviderRegistry {
+ extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
+ implements FlowRuleService, FlowRuleProviderRegistry {
public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
private final Logger log = getLogger(getClass());
private final AbstractListenerRegistry<FlowRuleEvent, FlowRuleListener>
- listenerRegistry = new AbstractListenerRegistry<>();
+ listenerRegistry = new AbstractListenerRegistry<>();
private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
@@ -62,8 +69,6 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
- private final Map<FlowRule, AtomicInteger> deadRounds = new ConcurrentHashMap<>();
-
@Activate
public void activate() {
store.setDelegate(delegate);
@@ -79,7 +84,12 @@
}
@Override
- public Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
+ public int getFlowRuleCount() {
+ return store.getFlowRuleCount();
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
return store.getFlowEntries(deviceId);
}
@@ -89,7 +99,6 @@
FlowRule f = flowRules[i];
final Device device = deviceService.getDevice(f.deviceId());
final FlowRuleProvider frp = getProvider(device.providerId());
- deadRounds.put(f, new AtomicInteger(0));
store.storeFlowRule(f);
frp.applyFlowRule(f);
}
@@ -103,16 +112,17 @@
for (int i = 0; i < flowRules.length; i++) {
f = flowRules[i];
device = deviceService.getDevice(f.deviceId());
- frp = getProvider(device.providerId());
- deadRounds.remove(f);
store.deleteFlowRule(f);
- frp.removeFlowRule(f);
+ if (device != null) {
+ frp = getProvider(device.providerId());
+ frp.removeFlowRule(f);
+ }
}
}
@Override
public void removeFlowRulesById(ApplicationId id) {
- Iterable<FlowRule> rules = getFlowRulesById(id);
+ Iterable<FlowRule> rules = getFlowRulesById(id);
FlowRuleProvider frp;
Device device;
@@ -126,7 +136,39 @@
@Override
public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
- return store.getFlowEntriesByAppId(id);
+ return store.getFlowRulesByAppId(id);
+ }
+
+ @Override
+ public Future<CompletedBatchOperation> applyBatch(
+ FlowRuleBatchOperation batch) {
+ Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches =
+ ArrayListMultimap.create();
+ List<Future<Void>> futures = Lists.newArrayList();
+ for (FlowRuleBatchEntry fbe : batch.getOperations()) {
+ final FlowRule f = fbe.getTarget();
+ final Device device = deviceService.getDevice(f.deviceId());
+ final FlowRuleProvider frp = getProvider(device.providerId());
+ batches.put(frp, fbe);
+ switch (fbe.getOperator()) {
+ case ADD:
+ store.storeFlowRule(f);
+ break;
+ case REMOVE:
+ store.deleteFlowRule(f);
+ break;
+ case MODIFY:
+ default:
+ log.error("Batch operation type {} unsupported.", fbe.getOperator());
+ }
+ }
+ for (FlowRuleProvider provider : batches.keySet()) {
+ FlowRuleBatchOperation b =
+ new FlowRuleBatchOperation(batches.get(provider));
+ Future<Void> future = provider.executeBatch(b);
+ futures.add(future);
+ }
+ return new FlowRuleBatchFuture(futures);
}
@Override
@@ -146,63 +188,63 @@
}
private class InternalFlowRuleProviderService
- extends AbstractProviderService<FlowRuleProvider>
- implements FlowRuleProviderService {
+ extends AbstractProviderService<FlowRuleProvider>
+ implements FlowRuleProviderService {
protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
super(provider);
}
@Override
- public void flowRemoved(FlowRule flowRule) {
- checkNotNull(flowRule, FLOW_RULE_NULL);
+ public void flowRemoved(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
checkValidity();
- FlowRule stored = store.getFlowRule(flowRule);
+ FlowEntry stored = store.getFlowEntry(flowEntry);
if (stored == null) {
- log.debug("Rule already evicted from store: {}", flowRule);
+ log.info("Rule already evicted from store: {}", flowEntry);
return;
}
- Device device = deviceService.getDevice(flowRule.deviceId());
+ Device device = deviceService.getDevice(flowEntry.deviceId());
FlowRuleProvider frp = getProvider(device.providerId());
FlowRuleEvent event = null;
switch (stored.state()) {
- case ADDED:
- case PENDING_ADD:
+ case ADDED:
+ case PENDING_ADD:
frp.applyFlowRule(stored);
- break;
- case PENDING_REMOVE:
- case REMOVED:
- event = store.removeFlowRule(flowRule);
- break;
- default:
- break;
+ break;
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(stored);
+ break;
+ default:
+ break;
}
if (event != null) {
- log.debug("Flow {} removed", flowRule);
+ log.debug("Flow {} removed", flowEntry);
post(event);
}
}
- private void flowMissing(FlowRule flowRule) {
+ private void flowMissing(FlowEntry flowRule) {
checkNotNull(flowRule, FLOW_RULE_NULL);
checkValidity();
Device device = deviceService.getDevice(flowRule.deviceId());
FlowRuleProvider frp = getProvider(device.providerId());
FlowRuleEvent event = null;
switch (flowRule.state()) {
- case PENDING_REMOVE:
- case REMOVED:
- event = store.removeFlowRule(flowRule);
- frp.removeFlowRule(flowRule);
- break;
- case ADDED:
- case PENDING_ADD:
- frp.applyFlowRule(flowRule);
- break;
- default:
- log.debug("Flow {} has not been installed.", flowRule);
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(flowRule);
+ frp.removeFlowRule(flowRule);
+ break;
+ case ADDED:
+ case PENDING_ADD:
+ frp.applyFlowRule(flowRule);
+ break;
+ default:
+ log.debug("Flow {} has not been installed.", flowRule);
}
if (event != null) {
@@ -221,36 +263,40 @@
}
- private void flowAdded(FlowRule flowRule) {
- checkNotNull(flowRule, FLOW_RULE_NULL);
+ private void flowAdded(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
checkValidity();
- if (deadRounds.containsKey(flowRule) &&
- checkRuleLiveness(flowRule, store.getFlowRule(flowRule))) {
+ if (checkRuleLiveness(flowEntry, store.getFlowEntry(flowEntry))) {
- FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule);
+ FlowRuleEvent event = store.addOrUpdateFlowRule(flowEntry);
if (event == null) {
log.debug("No flow store event generated.");
} else {
- log.debug("Flow {} {}", flowRule, event.type());
+ log.debug("Flow {} {}", flowEntry, event.type());
post(event);
}
} else {
- removeFlowRules(flowRule);
+ removeFlowRules(flowEntry);
}
}
- private boolean checkRuleLiveness(FlowRule swRule, FlowRule storedRule) {
- int timeout = storedRule.timeout();
+ private boolean checkRuleLiveness(FlowEntry swRule, FlowEntry storedRule) {
+ if (storedRule == null) {
+ return false;
+ }
+ long timeout = storedRule.timeout() * 1000;
+ Long currentTime = System.currentTimeMillis();
if (storedRule.packets() != swRule.packets()) {
- deadRounds.get(swRule).set(0);
+ storedRule.setLastSeen();
return true;
}
- return (deadRounds.get(swRule).getAndIncrement() *
- FlowRuleProvider.POLL_INTERVAL) <= timeout;
-
+ if ((currentTime - storedRule.lastSeen()) <= timeout) {
+ return true;
+ }
+ return false;
}
// Posts the specified event to the local event dispatcher.
@@ -261,13 +307,13 @@
}
@Override
- public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowEntries) {
- List<FlowRule> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
+ public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+ List<FlowEntry> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
- Iterator<FlowRule> switchRulesIterator = flowEntries.iterator();
+ Iterator<FlowEntry> switchRulesIterator = flowEntries.iterator();
while (switchRulesIterator.hasNext()) {
- FlowRule rule = switchRulesIterator.next();
+ FlowEntry rule = switchRulesIterator.next();
if (storedRules.remove(rule)) {
// we both have the rule, let's update some info then.
flowAdded(rule);
@@ -276,7 +322,7 @@
extraneousFlow(rule);
}
}
- for (FlowRule rule : storedRules) {
+ for (FlowEntry rule : storedRules) {
// there are rules in the store that aren't on the switch
flowMissing(rule);
@@ -291,4 +337,63 @@
eventDispatcher.post(event);
}
}
+
+ private class FlowRuleBatchFuture
+ implements Future<CompletedBatchOperation> {
+
+ private final List<Future<Void>> futures;
+
+ public FlowRuleBatchFuture(List<Future<Void>> futures) {
+ this.futures = futures;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ boolean isDone = true;
+ for (Future<Void> future : futures) {
+ isDone &= future.isDone();
+ }
+ return isDone;
+ }
+
+ @Override
+ public CompletedBatchOperation get() throws InterruptedException,
+ ExecutionException {
+ // TODO Auto-generated method stub
+ for (Future<Void> future : futures) {
+ future.get();
+ }
+ return new CompletedBatchOperation();
+ }
+
+ @Override
+ public CompletedBatchOperation get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException,
+ TimeoutException {
+ // TODO we should decrement the timeout
+ long start = System.nanoTime();
+ long end = start + unit.toNanos(timeout);
+ for (Future<Void> future : futures) {
+ long now = System.nanoTime();
+ long thisTimeout = end - now;
+ future.get(thisTimeout, TimeUnit.NANOSECONDS);
+ }
+ return new CompletedBatchOperation();
+ }
+
+ }
+
+
}
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 88b6923..29a0f18 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
@@ -76,7 +76,7 @@
eventDispatcher.addSink(HostEvent.class, listenerRegistry);
monitor = new HostMonitor(deviceService, packetService, this);
-
+ monitor.start();
}
@Deactivate
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
index 9f8dd48..e6e348f 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
@@ -2,11 +2,11 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.util.Timeout;
@@ -33,8 +33,6 @@
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.util.Timer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Monitors hosts on the dataplane to detect changes in host data.
@@ -44,70 +42,91 @@
* probe for hosts that have not yet been detected (specified by IP address).
*/
public class HostMonitor implements TimerTask {
- private static final Logger log = LoggerFactory.getLogger(HostMonitor.class);
-
- private static final byte[] ZERO_MAC_ADDRESS =
- MacAddress.valueOf("00:00:00:00:00:00").getAddress();
-
- // TODO put on Ethernet
- private static final byte[] BROADCAST_MAC =
- MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
-
private DeviceService deviceService;
private PacketService packetService;
private HostManager hostManager;
private final Set<IpAddress> monitoredAddresses;
- private final Map<ProviderId, HostProvider> hostProviders;
+ private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
- private final long probeRate;
+ private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
+ private long probeRate = DEFAULT_PROBE_RATE;
- private final Timeout timeout;
+ private Timeout timeout;
- public HostMonitor(
- DeviceService deviceService,
- PacketService packetService,
- HostManager hostService) {
+ /**
+ * Creates a new host monitor.
+ *
+ * @param deviceService device service used to find edge ports
+ * @param packetService packet service used to send packets on the data plane
+ * @param hostManager host manager used to look up host information and
+ * probe existing hosts
+ */
+ public HostMonitor(DeviceService deviceService, PacketService packetService,
+ HostManager hostManager) {
this.deviceService = deviceService;
this.packetService = packetService;
- this.hostManager = hostService;
+ this.hostManager = hostManager;
- monitoredAddresses = new HashSet<>();
+ monitoredAddresses = Collections.newSetFromMap(
+ new ConcurrentHashMap<IpAddress, Boolean>());
hostProviders = new ConcurrentHashMap<>();
- probeRate = 30000; // milliseconds
-
timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
-
- addDefaultAddresses();
}
- private void addDefaultAddresses() {
- //monitoredAddresses.add(IpAddress.valueOf("10.0.0.1"));
- }
-
+ /**
+ * Adds an IP address to be monitored by the host monitor. The monitor will
+ * periodically probe the host to detect changes.
+ *
+ * @param ip IP address of the host to monitor
+ */
void addMonitoringFor(IpAddress ip) {
monitoredAddresses.add(ip);
}
+ /**
+ * Stops monitoring the given IP address.
+ *
+ * @param ip IP address to stop monitoring on
+ */
void stopMonitoring(IpAddress ip) {
monitoredAddresses.remove(ip);
}
- void shutdown() {
- timeout.cancel();
+ /**
+ * Starts the host monitor. Does nothing if the monitor is already running.
+ */
+ void start() {
+ synchronized (this) {
+ if (timeout == null) {
+ timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
+ }
+ }
}
+ /**
+ * Stops the host monitor.
+ */
+ void shutdown() {
+ synchronized (this) {
+ timeout.cancel();
+ timeout = null;
+ }
+ }
+
+ /**
+ * Registers a host provider with the host monitor. The monitor can use the
+ * provider to probe hosts.
+ *
+ * @param provider the host provider to register
+ */
void registerHostProvider(HostProvider provider) {
hostProviders.put(provider.id(), provider);
}
- void unregisterHostProvider(HostProvider provider) {
- // TODO find out how to call this
- }
-
@Override
public void run(Timeout timeout) throws Exception {
for (IpAddress ip : monitoredAddresses) {
@@ -121,14 +140,16 @@
} else {
for (Host host : hosts) {
HostProvider provider = hostProviders.get(host.providerId());
- if (provider != null) {
+ if (provider == null) {
+ hostProviders.remove(host.providerId(), null);
+ } else {
provider.triggerProbe(host);
}
}
}
}
- timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
+ this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
}
/**
@@ -161,7 +182,7 @@
List<Instruction> instructions = new ArrayList<>();
instructions.add(Instructions.createOutput(port.number()));
- TrafficTreatment treatment = new DefaultTrafficTreatment.Builder()
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(port.number())
.build();
@@ -184,12 +205,12 @@
arp.setSenderHardwareAddress(sourceMac.getAddress())
.setSenderProtocolAddress(sourceIp.toOctets())
- .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
+ .setTargetHardwareAddress(MacAddress.ZERO_MAC_ADDRESS)
.setTargetProtocolAddress(targetIp.toOctets());
Ethernet ethernet = new Ethernet();
ethernet.setEtherType(Ethernet.TYPE_ARP)
- .setDestinationMACAddress(BROADCAST_MAC)
+ .setDestinationMACAddress(MacAddress.BROADCAST_MAC)
.setSourceMACAddress(sourceMac.getAddress())
.setPayload(arp);
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java
new file mode 100644
index 0000000..00b64da
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java
@@ -0,0 +1,42 @@
+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);
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java
new file mode 100644
index 0000000..f331aa2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java
@@ -0,0 +1,31 @@
+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");
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
new file mode 100644
index 0000000..de61e8e
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -0,0 +1,90 @@
+package org.onlab.onos.net.intent.impl;
+
+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.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.host.HostService;
+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;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder;
+
+/**
+ * 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;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ 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) {
+ Path pathOne = getPath(intent.one(), intent.two());
+ Path pathTwo = getPath(intent.two(), intent.one());
+
+ Host one = hostService.getHost(intent.one());
+ Host two = hostService.getHost(intent.two());
+
+ return Arrays.asList(createPathIntent(pathOne, one, two, intent),
+ createPathIntent(pathTwo, two, one, intent));
+ }
+
+ // Creates a path intent from the specified path and original connectivity intent.
+ private Intent createPathIntent(Path path, Host src, Host dst,
+ HostToHostIntent intent) {
+
+ TrafficSelector selector = builder(intent.selector())
+ .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
+
+ return new PathIntent(intentIdGenerator.getNewId(),
+ selector, intent.treatment(),
+ path.src(), path.dst(), path);
+ }
+
+ private Path getPath(HostId one, HostId two) {
+ Set<Path> paths = pathService.getPaths(one, two);
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException("No path from host " + one + " to " + two);
+ }
+ // TODO: let's be more intelligent about this eventually
+ return paths.iterator().next();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java
new file mode 100644
index 0000000..ce418ea
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java
@@ -0,0 +1,111 @@
+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();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java
new file mode 100644
index 0000000..1adac02
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java
@@ -0,0 +1,21 @@
+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);
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java
new file mode 100644
index 0000000..9620e59
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java
@@ -0,0 +1,25 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentId;
+
+/**
+ * An implementation of {@link org.onlab.onos.net.intent.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);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java
new file mode 100644
index 0000000..bf739df
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java
@@ -0,0 +1,22 @@
+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);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java
new file mode 100644
index 0000000..3b17cf1
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java
@@ -0,0 +1,22 @@
+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);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
new file mode 100644
index 0000000..16b75f2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -0,0 +1,463 @@
+package org.onlab.onos.net.intent.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.onos.net.intent.IntentState.COMPILING;
+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.INSTALLING;
+import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+
+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 AbstractListenerRegistry<IntentEvent, IntentListener>
+ listenerRegistry = new AbstractListenerRegistry<>();
+
+ private final ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
+
+ private final IntentStoreDelegate delegate = new InternalStoreDelegate();
+ private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ObjectiveTrackerService trackerService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EventDeliveryService eventDispatcher;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ trackerService.setDelegate(topoDelegate);
+ eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ trackerService.unsetDelegate(topoDelegate);
+ eventDispatcher.removeSink(IntentEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void submit(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ registerSubclassCompilerIfNeeded(intent);
+ IntentEvent event = store.createIntent(intent);
+ if (event != null) {
+ eventDispatcher.post(event);
+ executor.execute(new IntentTask(COMPILING, intent));
+ }
+ }
+
+ @Override
+ public void withdraw(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ executor.execute(new IntentTask(WITHDRAWING, intent));
+ }
+
+ // 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);
+ }
+
+ /**
+ * 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 the specified intent.
+ *
+ * @param intent intent to be compiled
+ */
+ private void executeCompilingPhase(Intent intent) {
+ // Indicate that the intent is entering the compiling phase.
+ store.setState(intent, COMPILING);
+
+ try {
+ // Compile the intent into installable derivatives.
+ List<InstallableIntent> installable = compileIntent(intent);
+
+ // If all went well, associate the resulting list of installable
+ // intents with the top-level intent and proceed to install.
+ store.addInstallableIntents(intent.id(), installable);
+ executeInstallingPhase(intent);
+
+ } catch (Exception e) {
+ log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
+
+ // If compilation failed, mark the intent as failed.
+ store.setState(intent, FAILED);
+ }
+ }
+
+ // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
+ // TODO: implement compilation traversing tree structure
+ private List<InstallableIntent> compileIntent(Intent intent) {
+ List<InstallableIntent> installable = new ArrayList<>();
+ for (Intent compiled : getCompiler(intent).compile(intent)) {
+ InstallableIntent installableIntent = (InstallableIntent) compiled;
+ installable.add(installableIntent);
+ }
+ return installable;
+ }
+
+ /**
+ * Installs all installable intents associated with the specified top-level
+ * intent.
+ *
+ * @param intent intent to be installed
+ */
+ private void executeInstallingPhase(Intent intent) {
+ // Indicate that the intent is entering the installing phase.
+ store.setState(intent, INSTALLING);
+
+ try {
+ List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
+ if (installables != null) {
+ for (InstallableIntent installable : installables) {
+ registerSubclassInstallerIfNeeded(installable);
+ trackerService.addTrackedResources(intent.id(),
+ installable.requiredLinks());
+ getInstaller(installable).install(installable);
+ }
+ }
+ eventDispatcher.post(store.setState(intent, INSTALLED));
+
+ } catch (Exception e) {
+ log.warn("Unable to install intent {} due to: {}", intent.id(), e);
+ uninstallIntent(intent);
+
+ // If compilation failed, kick off the recompiling phase.
+ executeRecompilingPhase(intent);
+ }
+ }
+
+ /**
+ * Recompiles the specified intent.
+ *
+ * @param intent intent to be recompiled
+ */
+ private void executeRecompilingPhase(Intent intent) {
+ // Indicate that the intent is entering the recompiling phase.
+ store.setState(intent, RECOMPILING);
+
+ try {
+ // Compile the intent into installable derivatives.
+ List<InstallableIntent> installable = compileIntent(intent);
+
+ // If all went well, compare the existing list of installable
+ // intents with the newly compiled list. If they are the same,
+ // bail, out since the previous approach was determined not to
+ // be viable.
+ List<InstallableIntent> originalInstallable =
+ store.getInstallableIntents(intent.id());
+
+ if (Objects.equals(originalInstallable, installable)) {
+ eventDispatcher.post(store.setState(intent, FAILED));
+ } else {
+ // Otherwise, re-associate the newly compiled installable intents
+ // with the top-level intent and kick off installing phase.
+ store.addInstallableIntents(intent.id(), installable);
+ executeInstallingPhase(intent);
+ }
+ } catch (Exception e) {
+ log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
+
+ // If compilation failed, mark the intent as failed.
+ eventDispatcher.post(store.setState(intent, FAILED));
+ }
+ }
+
+ /**
+ * Uninstalls the specified intent by uninstalling all of its associated
+ * installable derivatives.
+ *
+ * @param intent intent to be installed
+ */
+ private void executeWithdrawingPhase(Intent intent) {
+ // Indicate that the intent is being withdrawn.
+ store.setState(intent, WITHDRAWING);
+ uninstallIntent(intent);
+
+ // If all went well, disassociate the top-level intent with its
+ // installable derivatives and mark it as withdrawn.
+ store.removeInstalledIntents(intent.id());
+ eventDispatcher.post(store.setState(intent, WITHDRAWN));
+ }
+
+ /**
+ * Uninstalls all installable intents associated with the given intent.
+ *
+ * @param intent intent to be uninstalled
+ */
+ private void uninstallIntent(Intent intent) {
+ try {
+ List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
+ if (installables != null) {
+ for (InstallableIntent installable : installables) {
+ getInstaller(installable).uninstall(installable);
+ }
+ }
+ } catch (IntentException e) {
+ log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
+ }
+ }
+
+ /**
+ * 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();
+ }
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements IntentStoreDelegate {
+ @Override
+ public void notify(IntentEvent event) {
+ eventDispatcher.post(event);
+ if (event.type() == IntentEvent.Type.SUBMITTED) {
+ executor.execute(new IntentTask(COMPILING, event.subject()));
+ }
+ }
+ }
+
+ // Topology change delegate
+ private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
+ @Override
+ public void triggerCompile(Iterable<IntentId> intentIds,
+ boolean compileAllFailed) {
+ // Attempt recompilation of the specified intents first.
+ for (IntentId intentId : intentIds) {
+ Intent intent = getIntent(intentId);
+ uninstallIntent(intent);
+
+ executeRecompilingPhase(intent);
+ }
+
+ if (compileAllFailed) {
+ // If required, compile all currently failed intents.
+ for (Intent intent : getIntents()) {
+ if (getIntentState(intent.id()) == FAILED) {
+ executeCompilingPhase(intent);
+ }
+ }
+ }
+ }
+ }
+
+ // Auxiliary runnable to perform asynchronous tasks.
+ private class IntentTask implements Runnable {
+ private final IntentState state;
+ private final Intent intent;
+
+ public IntentTask(IntentState state, Intent intent) {
+ this.state = state;
+ this.intent = intent;
+ }
+
+ @Override
+ public void run() {
+ if (state == COMPILING) {
+ executeCompilingPhase(intent);
+ } else if (state == RECOMPILING) {
+ executeRecompilingPhase(intent);
+ } else if (state == WITHDRAWING) {
+ executeWithdrawingPhase(intent);
+ }
+ }
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java
new file mode 100644
index 0000000..5ee4ee4
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java
@@ -0,0 +1,22 @@
+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);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
new file mode 100644
index 0000000..d84c367
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
@@ -0,0 +1,140 @@
+package org.onlab.onos.net.intent.impl;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+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.Event;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.link.LinkEvent;
+import org.onlab.onos.net.topology.TopologyEvent;
+import org.onlab.onos.net.topology.TopologyListener;
+import org.onlab.onos.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
+import static org.onlab.util.Tools.namedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Entity responsible for tracking installed flows and for monitoring topology
+ * events to determine what flows are affected by topology changes.
+ */
+@Component
+@Service
+public class ObjectiveTracker implements ObjectiveTrackerService {
+
+ private final Logger log = getLogger(getClass());
+
+ private final SetMultimap<LinkKey, IntentId> intentsByLink =
+ synchronizedSetMultimap(HashMultimap.<LinkKey, IntentId>create());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ private ExecutorService executorService =
+ newSingleThreadExecutor(namedThreads("onos-flowtracker"));
+
+ private TopologyListener listener = new InternalTopologyListener();
+ private TopologyChangeDelegate delegate;
+
+ @Activate
+ public void activate() {
+ topologyService.addListener(listener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ topologyService.removeListener(listener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void setDelegate(TopologyChangeDelegate delegate) {
+ checkNotNull(delegate, "Delegate cannot be null");
+ checkArgument(this.delegate == null || this.delegate == delegate,
+ "Another delegate already set");
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void unsetDelegate(TopologyChangeDelegate delegate) {
+ checkArgument(this.delegate == delegate, "Not the current delegate");
+ this.delegate = null;
+ }
+
+ @Override
+ public void addTrackedResources(IntentId intentId, Collection<Link> resources) {
+ for (Link link : resources) {
+ intentsByLink.put(new LinkKey(link), intentId);
+ }
+ }
+
+ @Override
+ public void removeTrackedResources(IntentId intentId, Collection<Link> resources) {
+ for (Link link : resources) {
+ intentsByLink.remove(new LinkKey(link), intentId);
+ }
+ }
+
+ // Internal re-actor to topology change events.
+ private class InternalTopologyListener implements TopologyListener {
+ @Override
+ public void event(TopologyEvent event) {
+ executorService.execute(new TopologyChangeHandler(event));
+ }
+ }
+
+ // Re-dispatcher of topology change events.
+ private class TopologyChangeHandler implements Runnable {
+
+ private final TopologyEvent event;
+
+ TopologyChangeHandler(TopologyEvent event) {
+ this.event = event;
+ }
+
+ @Override
+ public void run() {
+ if (event.reasons() == null) {
+ delegate.triggerCompile(new HashSet<IntentId>(), true);
+
+ } else {
+ Set<IntentId> toBeRecompiled = new HashSet<>();
+ boolean recompileOnly = true;
+
+ // Scan through the list of reasons and keep accruing all
+ // intents that need to be recompiled.
+ for (Event reason : event.reasons()) {
+ if (reason instanceof LinkEvent) {
+ LinkEvent linkEvent = (LinkEvent) reason;
+ if (linkEvent.type() == LINK_REMOVED) {
+ Set<IntentId> intentIds = intentsByLink.get(new LinkKey(linkEvent.subject()));
+ toBeRecompiled.addAll(intentIds);
+ }
+ recompileOnly = recompileOnly && linkEvent.type() == LINK_REMOVED;
+ }
+ }
+
+ delegate.triggerCompile(toBeRecompiled, !recompileOnly);
+ }
+ }
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
new file mode 100644
index 0000000..15496ff
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
@@ -0,0 +1,44 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.intent.IntentId;
+
+import java.util.Collection;
+
+/**
+ * Auxiliary service for tracking intent path flows and for notifying the
+ * intent service of environment changes via topology change delegate.
+ */
+public interface ObjectiveTrackerService {
+
+ /**
+ * Sets a topology change delegate.
+ *
+ * @param delegate topology change delegate
+ */
+ void setDelegate(TopologyChangeDelegate delegate);
+
+ /**
+ * Unsets topology change delegate.
+ *
+ * @param delegate topology change delegate
+ */
+ void unsetDelegate(TopologyChangeDelegate delegate);
+
+ /**
+ * Adds a path flow to be tracked.
+ *
+ * @param intentId intent identity on whose behalf the path is being tracked
+ * @param resources resources to track
+ */
+ public void addTrackedResources(IntentId intentId, Collection<Link> resources);
+
+ /**
+ * Removes a path flow to be tracked.
+ *
+ * @param intentId intent identity on whose behalf the path is being tracked
+ * @param resources resources to stop tracking
+ */
+ public void removeTrackedResources(IntentId intentId, Collection<Link> resources);
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
new file mode 100644
index 0000000..0ca75c2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -0,0 +1,117 @@
+package org.onlab.onos.net.intent.impl;
+
+import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+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.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry;
+import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
+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.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentInstaller;
+import org.onlab.onos.net.intent.PathIntent;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Installer for {@link PathIntent path connectivity intents}.
+ */
+@Component(immediate = true)
+public class PathIntentInstaller implements IntentInstaller<PathIntent> {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ private final ApplicationId appId = ApplicationId.getAppId();
+
+ @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 =
+ DefaultTrafficSelector.builder(intent.selector());
+ Iterator<Link> links = intent.path().links().iterator();
+ ConnectPoint prev = links.next().dst();
+ List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
+ while (links.hasNext()) {
+ builder.matchInport(prev.port());
+ Link link = links.next();
+ TrafficTreatment treatment = builder()
+ .setOutput(link.src().port()).build();
+
+ FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
+ builder.build(), treatment,
+ 123, appId, 600);
+ rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
+ //flowRuleService.applyFlowRules(rule);
+ prev = link.dst();
+ }
+ FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
+ try {
+ flowRuleService.applyBatch(batch).get();
+ } catch (InterruptedException | ExecutionException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void uninstall(PathIntent intent) {
+ TrafficSelector.Builder builder =
+ DefaultTrafficSelector.builder(intent.selector());
+ Iterator<Link> links = intent.path().links().iterator();
+ ConnectPoint prev = links.next().dst();
+ List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
+
+ while (links.hasNext()) {
+ builder.matchInport(prev.port());
+ Link link = links.next();
+ TrafficTreatment treatment = builder()
+ .setOutput(link.src().port()).build();
+ FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
+ builder.build(), treatment,
+ 123, appId, 600);
+ rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
+ //flowRuleService.removeFlowRules(rule);
+ prev = link.dst();
+ }
+ FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
+ try {
+ flowRuleService.applyBatch(batch).get();
+ } catch (InterruptedException | ExecutionException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java
new file mode 100644
index 0000000..a1fd63a
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java
@@ -0,0 +1,22 @@
+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);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
new file mode 100644
index 0000000..0bd1703
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
@@ -0,0 +1,105 @@
+package org.onlab.onos.net.intent.impl;
+
+import java.util.ArrayList;
+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.DefaultEdgeLink;
+import org.onlab.onos.net.DefaultPath;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.host.HostService;
+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.intent.PointToPointIntent;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.topology.PathService;
+
+/**
+ * A intent compiler for {@link org.onlab.onos.net.intent.HostToHostIntent}.
+ */
+@Component(immediate = true)
+public class PointToPointIntentCompiler
+ implements IntentCompiler<PointToPointIntent> {
+
+ private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private IdGenerator<IntentId> intentIdGenerator;
+
+ @Activate
+ public void activate() {
+ IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
+ intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
+ intentManager.registerCompiler(PointToPointIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(PointToPointIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(PointToPointIntent intent) {
+ Path path = getPath(intent.ingressPoint(), intent.egressPoint());
+
+ List<Link> links = new ArrayList<>();
+ links.add(DefaultEdgeLink.createEdgeLink(intent.ingressPoint(), true));
+ links.addAll(path.links());
+ links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
+
+ return Arrays.asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
+ path.annotations()),
+ intent));
+ }
+
+ /**
+ * Creates a path intent from the specified path and original
+ * connectivity intent.
+ *
+ * @param path path to create an intent for
+ * @param intent original intent
+ */
+ private Intent createPathIntent(Path path,
+ PointToPointIntent intent) {
+
+ return new PathIntent(intentIdGenerator.getNewId(),
+ intent.selector(), intent.treatment(),
+ path.src(), path.dst(), path);
+ }
+
+ /**
+ * Computes a path between two ConnectPoints.
+ *
+ * @param one start of the path
+ * @param two end of the path
+ * @return Path between the two
+ * @throws PathNotFoundException if a path cannot be found
+ */
+ private Path getPath(ConnectPoint one, ConnectPoint two) {
+ Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException("No path from " + one + " to " + two);
+ }
+ // TODO: let's be more intelligent about this eventually
+ return paths.iterator().next();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
new file mode 100644
index 0000000..30e6899
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentId;
+
+/**
+ * Auxiliary delegate for integration of intent manager and flow trackerService.
+ */
+public interface TopologyChangeDelegate {
+
+ /**
+ * Notifies that topology has changed in such a way that the specified
+ * intents should be recompiled. If the {@code compileAllFailed} parameter
+ * is true, then all intents in {@link org.onlab.onos.net.intent.IntentState#FAILED}
+ * state should be compiled as well.
+ *
+ * @param intentIds intents that should be recompiled
+ * @param compileAllFailed true implies full compile of all failed intents
+ * is required; false for selective recompile only
+ */
+ void triggerCompile(Iterable<IntentId> intentIds, boolean compileAllFailed);
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java
new file mode 100644
index 0000000..fd4f122
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java
@@ -0,0 +1,34 @@
+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);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java
new file mode 100644
index 0000000..3f00271
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Core subsystem for tracking high-level intents for treatment of selected
+ * network traffic.
+ */
+package org.onlab.onos.net.intent.impl;
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index 6e07c3e..4933322 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -31,6 +31,8 @@
import org.onlab.onos.net.link.LinkListener;
import org.onlab.onos.net.link.LinkService;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
+import org.onlab.onos.net.packet.InboundPacket;
+import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.proxyarp.ProxyArpService;
import org.onlab.packet.ARP;
@@ -43,7 +45,6 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
-
@Component(immediate = true)
@Service
public class ProxyArpManager implements ProxyArpService {
@@ -93,14 +94,14 @@
@Override
public boolean known(IpPrefix addr) {
- checkNotNull(MAC_ADDR_NULL, addr);
+ checkNotNull(addr, MAC_ADDR_NULL);
Set<Host> hosts = hostService.getHostsByIp(addr);
return !hosts.isEmpty();
}
@Override
public void reply(Ethernet eth) {
- checkNotNull(REQUEST_NULL, eth);
+ checkNotNull(eth, REQUEST_NULL);
checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
REQUEST_NOT_ARP);
ARP arp = (ARP) eth.getPayload();
@@ -128,7 +129,7 @@
Ethernet arpReply = buildArpReply(dst, eth);
// TODO: check send status with host service.
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(src.location().port());
packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
builder.build(), ByteBuffer.wrap(arpReply.serialize())));
@@ -136,7 +137,7 @@
@Override
public void forward(Ethernet eth) {
- checkNotNull(REQUEST_NULL, eth);
+ checkNotNull(eth, REQUEST_NULL);
checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
REQUEST_NOT_ARP);
ARP arp = (ARP) eth.getPayload();
@@ -148,7 +149,7 @@
if (h == null) {
flood(eth);
} else {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(h.location().port());
packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
builder.build(), ByteBuffer.wrap(eth.serialize())));
@@ -156,6 +157,23 @@
}
+ @Override
+ public boolean handleArp(PacketContext context) {
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethPkt = pkt.parsed();
+ if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
+ ARP arp = (ARP) ethPkt.getPayload();
+ if (arp.getOpCode() == ARP.OP_REPLY) {
+ forward(ethPkt);
+ } else if (arp.getOpCode() == ARP.OP_REQUEST) {
+ reply(ethPkt);
+ }
+ context.block();
+ return true;
+ }
+ return false;
+ }
+
/**
* Flood the arp request at all edges in the network.
* @param request the arp request.
@@ -166,7 +184,7 @@
synchronized (externalPorts) {
for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
- builder = new DefaultTrafficTreatment.Builder();
+ builder = DefaultTrafficTreatment.builder();
builder.setOutput(entry.getValue());
packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
builder.build(), buf));
@@ -188,12 +206,12 @@
for (Link l : links) {
// for each link, mark the concerned ports as internal
// and the remaining ports are therefore external.
- if (l.src().deviceId().equals(d)
+ if (l.src().deviceId().equals(d.id())
&& ports.contains(l.src().port())) {
ports.remove(l.src().port());
internalPorts.put(d, l.src().port());
}
- if (l.dst().deviceId().equals(d)
+ if (l.dst().deviceId().equals(d.id())
&& ports.contains(l.dst().port())) {
ports.remove(l.dst().port());
internalPorts.put(d, l.dst().port());
@@ -322,7 +340,6 @@
}
-}
-
+ }
}