Optical Path Porvisioning

Change-Id: I9788fd9172edc8ab571aa1d41962c2bd10697c50
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;
+    }
+
+}