Optical Path Porvisioning
Change-Id: I9788fd9172edc8ab571aa1d41962c2bd10697c50
diff --git a/apps/optical/src/main/java/org/onlab/onos/optical/provisioner/OpticalPathProvisioner.java b/apps/optical/src/main/java/org/onlab/onos/optical/provisioner/OpticalPathProvisioner.java
new file mode 100644
index 0000000..95b5041
--- /dev/null
+++ b/apps/optical/src/main/java/org/onlab/onos/optical/provisioner/OpticalPathProvisioner.java
@@ -0,0 +1,156 @@
+package org.onlab.onos.optical.provisioner;
+
+import java.util.Iterator;
+
+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.ApplicationId;
+import org.onlab.onos.CoreService;
+import org.onlab.onos.net.ConnectPoint;
+
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.device.DeviceService;
+
+import org.onlab.onos.net.intent.IdGenerator;
+import org.onlab.onos.net.intent.Intent;
+
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentListener;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.OpticalConnectivityIntent;
+import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.onos.net.link.LinkService;
+import org.onlab.onos.net.topology.TopologyService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * OpticalPathProvisioner listens event notifications from the Intent F/W.
+ * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
+ * for adding/releasing capacity at the packet layer.
+ *
+ */
+
+@Component(immediate = true)
+public class OpticalPathProvisioner {
+
+ protected static final Logger log = LoggerFactory
+ .getLogger(OpticalPathProvisioner.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private IntentExtensionService intentExtensionService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ // protected LinkResourceService resourceService;
+
+ private ApplicationId appId;
+ private static long intentId = 0x9000000;
+
+ protected IdGenerator<IntentId> intentIdGenerator;
+
+ private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
+
+ @Activate
+ protected void activate() {
+ intentService.addListener(pathProvisioner);
+ appId = coreService.registerApplication("org.onlab.onos.optical");
+ log.info("Starting optical path provisoning...");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ intentService.removeListener(pathProvisioner);
+ }
+
+ public class InternalOpticalPathProvisioner implements IntentListener {
+ @Override
+ public void event(IntentEvent event) {
+ switch (event.type()) {
+ case SUBMITTED:
+ break;
+ case INSTALLED:
+ break;
+ case FAILED:
+ log.info("intent {} failed, calling optical path provisioning APP.", event.subject());
+ setuplightpath(event.subject());
+ break;
+ case WITHDRAWN:
+ log.info("intent {} withdrawn.", event.subject());
+ teardownLightpath(event.subject());
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void setuplightpath(Intent intent) {
+ // TODO: considering user policies and optical reach, may generate more OpticalConnectivityIntents
+ if (!intent.equals(PointToPointIntent.class)) {
+ return;
+ }
+
+ PointToPointIntent pktIntent = (PointToPointIntent) intent;
+ if (pktIntent.ingressPoint() == null || pktIntent.egressPoint() == null) {
+ return;
+ }
+
+ // Set<Port> port1 = deviceService.getPorts(pktIntent.ingressPoint().deviceId());
+
+ Set<Link> srcLink = linkService.getLinks(pktIntent.ingressPoint());
+ Set<Link> dstLink = linkService.getLinks(pktIntent.egressPoint());
+
+ if (srcLink.isEmpty() || dstLink.isEmpty()) {
+ return;
+ }
+
+ Iterator<Link> itrSrc = srcLink.iterator();
+ Iterator<Link> itrDst = dstLink.iterator();
+
+ if (!itrSrc.next().annotations().value("linkType").equals("PktOptLink")) {
+ return;
+ }
+ if (!itrDst.next().annotations().value("linkType").equals("PktOptLink")) {
+ return;
+ }
+
+ ConnectPoint srcWdmPoint = itrSrc.next().dst();
+ ConnectPoint dstWdmPoint = itrDst.next().dst();
+
+ OpticalConnectivityIntent opticalIntent =
+ new OpticalConnectivityIntent(new IntentId(intentId++),
+ srcWdmPoint,
+ dstWdmPoint);
+
+ log.info(opticalIntent.toString());
+ intentService.submit(opticalIntent);
+ }
+
+ private void teardownLightpath(Intent intent) {
+ // TODO: tear down the idle lightpath if the utilization is close to zero.
+ }
+
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
new file mode 100644
index 0000000..4e4ebe5
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
@@ -0,0 +1,53 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.net.ConnectPoint;
+
+/**
+ * An optical layer Intent for a connectivity from a Transponder port to another
+ * Transponder port. No trafficSelector as well as trafficTreament are needed.
+ *
+ */
+public class OpticalConnectivityIntent extends AbstractIntent {
+ protected ConnectPoint src;
+ protected ConnectPoint dst;
+
+ /**
+ * Constructor.
+ *
+ * @param id ID for this new Intent object.
+ * @param src The source transponder port.
+ * @param dst The destination transponder port.
+ */
+ public OpticalConnectivityIntent(IntentId id, ConnectPoint src, ConnectPoint dst) {
+ super(id);
+ this.src = src;
+ this.dst = dst;
+ }
+
+ /**
+ * Constructor for serializer.
+ */
+ protected OpticalConnectivityIntent() {
+ super();
+ this.src = null;
+ this.dst = null;
+ }
+
+ /**
+ * Gets source transponder port.
+ *
+ * @return The source transponder port.
+ */
+ public ConnectPoint getSrcConnectPoint() {
+ return src;
+ }
+
+ /**
+ * Gets destination transponder port.
+ *
+ * @return The source transponder port.
+ */
+ public ConnectPoint getDst() {
+ return dst;
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/OpticalPathIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalPathIntent.java
new file mode 100644
index 0000000..d0d73d9
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalPathIntent.java
@@ -0,0 +1,88 @@
+package org.onlab.onos.net.intent;
+
+import java.util.Collection;
+import java.util.Objects;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+
+import com.google.common.base.MoreObjects;
+
+public class OpticalPathIntent extends OpticalConnectivityIntent implements InstallableIntent {
+
+ private final Path path;
+ private final TrafficSelector opticalMatch;
+ private final TrafficTreatment opticalAction;
+
+ public OpticalPathIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
+ ConnectPoint ingressPort, ConnectPoint egressPort,
+ Path path) {
+ this.opticalMatch = match;
+ this.opticalAction = action;
+ this.path = path;
+ }
+
+ public OpticalPathIntent() {
+ this.opticalMatch = null;
+ this.opticalAction = null;
+ this.path = null;
+ }
+
+ public Path path() {
+ return path;
+ }
+
+ public TrafficSelector selector() {
+ return opticalMatch;
+ }
+
+ public TrafficTreatment treatment() {
+ return opticalAction;
+ }
+
+ @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;
+ }
+
+ OpticalPathIntent that = (OpticalPathIntent) o;
+
+ if (!path.equals(that.path)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), path);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("id", id())
+ .add("match", opticalMatch)
+ .add("action", opticalAction)
+ .add("ingressPort", this.getSrcConnectPoint())
+ .add("egressPort", this.getDst())
+ .add("path", path)
+ .toString();
+ }
+
+ @Override
+ public Collection<Link> requiredLinks() {
+ return path.links();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalConnectivityIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalConnectivityIntentCompiler.java
new file mode 100644
index 0000000..d888ed3
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalConnectivityIntentCompiler.java
@@ -0,0 +1,114 @@
+package org.onlab.onos.net.intent.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+
+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.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+
+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.OpticalConnectivityIntent;
+import org.onlab.onos.net.intent.OpticalPathIntent;
+
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.topology.PathService;
+import org.slf4j.Logger;
+
+/**
+ * Optical compiler for OpticalConnectivityIntent.
+ * It firstly computes K-shortest paths in the optical-layer, then choose the optimal one to assign a wavelength.
+ * Finally, it generates one or more opticalPathintent(s) with opticalMatchs and opticalActions.
+ */
+@Component(immediate = true)
+public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
+
+ private final Logger log = getLogger(getClass());
+ 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;
+
+ // protected LinkResourceService resourceService;
+
+ protected IdGenerator<IntentId> intentIdGenerator;
+
+ @Activate
+ public void activate() {
+ IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
+ intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
+ intentManager.registerCompiler(OpticalConnectivityIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(OpticalConnectivityIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(OpticalConnectivityIntent intent) {
+ // TO DO: compute multiple paths using the K-shortest path algorithm
+ Path path = calculatePath(intent.getSrcConnectPoint(), intent.getDst());
+ log.info("calculate the lightpath: {}.", path.toString());
+
+ List<Link> links = new ArrayList<>();
+ links.add(DefaultEdgeLink.createEdgeLink(intent.getSrcConnectPoint(), true));
+ links.addAll(path.links());
+ links.add(DefaultEdgeLink.createEdgeLink(intent.getDst(), false));
+
+ // TO DO: choose a wavelength using the first-fit algorithm
+ TrafficSelector opticalSelector = null;
+ TrafficTreatment opticalTreatment = null;
+
+ List<Intent> retList = new ArrayList<>();
+ int wavelength = assignWavelength(path);
+ log.info("assign the wavelength: {}.", wavelength);
+
+ // create a new opticalPathIntent
+ Intent newIntent = new OpticalPathIntent(intentIdGenerator.getNewId(),
+ opticalSelector,
+ opticalTreatment,
+ path.src(),
+ path.dst(),
+ path);
+
+ retList.add(newIntent);
+
+ return retList;
+ }
+
+ private Path calculatePath(ConnectPoint one, ConnectPoint two) {
+ // TODO: K-shortest path computation algorithm
+ Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException("No optical path from " + one + " to " + two);
+ }
+ return paths.iterator().next();
+ }
+
+ private int assignWavelength(Path path) {
+ // TODO: wavelength assignment
+ return 1;
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java
new file mode 100644
index 0000000..2e4f2fd
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/OpticalPathIntentInstaller.java
@@ -0,0 +1,128 @@
+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.Future;
+
+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.CoreService;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.flow.CompletedBatchOperation;
+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.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.flow.FlowRuleBatchEntry.FlowRuleOperation;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentInstaller;
+import org.onlab.onos.net.intent.OpticalPathIntent;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+
+/**
+ * OpticaliIntentInstaller for optical path intents.
+ * It essentially generates optical FlowRules and
+ * call the flowRule service to execute them.
+ */
+
+@Component(immediate = true)
+public class OpticalPathIntentInstaller implements IntentInstaller<OpticalPathIntent> {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onlab.onos.net.intent");
+ intentManager.registerInstaller(OpticalPathIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterInstaller(OpticalPathIntent.class);
+ }
+
+ @Override
+ public Future<CompletedBatchOperation> install(OpticalPathIntent 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,
+ 100, // priority is set to 100
+ appId,
+ 100); // ROADM may need longer cross-connect time, here it is assumed 100ms.
+
+ rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
+ prev = link.dst();
+ }
+
+ FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
+ Future<CompletedBatchOperation> future = flowRuleService.applyBatch(batch);
+ return future;
+ }
+
+ @Override
+ public Future<CompletedBatchOperation> uninstall(OpticalPathIntent 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,
+ 100,
+ appId,
+ 100);
+
+ rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
+ prev = link.dst();
+ }
+
+ FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
+ Future<CompletedBatchOperation> future = flowRuleService.applyBatch(batch);
+ return future;
+ }
+
+}