Implement IntentCompilers
For the following Intent types
- PointToPointIntent
- PathIntent
- MultiPointToSinglePointIntent
This resolves ONOS-1657, ONOS-1659, and ONOS-1660.
Change-Id: I7c68cec4b025a59f9c97bae4488801bea2716baf
diff --git a/src/main/java/net/onrc/onos/core/newintent/AbstractFlowGeneratingIntentCompiler.java b/src/main/java/net/onrc/onos/core/newintent/AbstractFlowGeneratingIntentCompiler.java
new file mode 100644
index 0000000..07041e9
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/AbstractFlowGeneratingIntentCompiler.java
@@ -0,0 +1,41 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.flowmanager.FlowId;
+import net.onrc.onos.api.flowmanager.FlowIdGenerator;
+import net.onrc.onos.api.newintent.Intent;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A base class of {@link net.onrc.onos.api.newintent.IntentCompiler},
+ * which generates Flow objects.
+ * @param <T>
+ */
+public abstract class AbstractFlowGeneratingIntentCompiler<T extends Intent>
+ extends AbstractIntentCompiler<T> {
+
+ private final FlowIdGenerator flowIdGenerator;
+
+ /**
+ * Constructs an object with the specified {@link IntentIdGenerator}
+ * and {@link FlowIdGenerator}.
+ *
+ * @param intentIdGenerator
+ * @param flowIdGenerator
+ */
+ protected AbstractFlowGeneratingIntentCompiler(IntentIdGenerator intentIdGenerator,
+ FlowIdGenerator flowIdGenerator) {
+ super(intentIdGenerator);
+ this.flowIdGenerator = checkNotNull(flowIdGenerator);
+ }
+
+ /**
+ * Returns the next {@link FlowId}.
+ *
+ * @return the next {@link FlowId}
+ */
+ protected FlowId getNextFlowId() {
+ return flowIdGenerator.getNewId();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/newintent/AbstractIntentCompiler.java b/src/main/java/net/onrc/onos/core/newintent/AbstractIntentCompiler.java
new file mode 100644
index 0000000..0b16f10
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/AbstractIntentCompiler.java
@@ -0,0 +1,54 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.newintent.ConnectivityIntent;
+import net.onrc.onos.api.newintent.Intent;
+import net.onrc.onos.api.newintent.IntentCompiler;
+import net.onrc.onos.api.newintent.IntentId;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.action.Actions;
+import net.onrc.onos.core.matchaction.action.OutputAction;
+import net.onrc.onos.core.util.SwitchPort;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A base IntentCompiler implementation.
+ * @param <T> the type of intent
+ */
+public abstract class AbstractIntentCompiler<T extends Intent> implements IntentCompiler<T> {
+ private final IntentIdGenerator idGenerator;
+
+ /**
+ * Constructs an instance with the specified Intent ID generator.
+ * <p>
+ * Intent compiler generates intents from an input intent.
+ * To make sure to use unique IDs for generated intents, intent
+ * ID generator is given as the argument of a constructor in normal
+ * cases.
+ * </p>
+ * @param idGenerator intent ID generator
+ */
+ protected AbstractIntentCompiler(IntentIdGenerator idGenerator) {
+ this.idGenerator = checkNotNull(idGenerator);
+ }
+
+ protected IntentId getNextId() {
+ return idGenerator.getNewId();
+ }
+
+ protected List<Action> packActions(ConnectivityIntent intent, SwitchPort egress) {
+ List<Action> actions = new ArrayList<>();
+ Action intentAction = intent.getAction();
+ if (!intentAction.equals(Actions.nullAction())) {
+ actions.add(intentAction);
+ }
+
+ OutputAction output = new OutputAction(egress.getPortNumber());
+ actions.add(output);
+ return actions;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/newintent/IntentCompilationException.java b/src/main/java/net/onrc/onos/core/newintent/IntentCompilationException.java
new file mode 100644
index 0000000..23e7c72
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/IntentCompilationException.java
@@ -0,0 +1,22 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.newintent.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/src/main/java/net/onrc/onos/core/newintent/MultiPointToSinglePointIntentCompiler.java b/src/main/java/net/onrc/onos/core/newintent/MultiPointToSinglePointIntentCompiler.java
new file mode 100644
index 0000000..eb31457
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/MultiPointToSinglePointIntentCompiler.java
@@ -0,0 +1,111 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.flowmanager.FlowIdGenerator;
+import net.onrc.onos.api.flowmanager.SingleDstTreeFlow;
+import net.onrc.onos.api.flowmanager.Tree;
+import net.onrc.onos.api.newintent.Intent;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+import net.onrc.onos.api.newintent.MultiPointToSinglePointIntent;
+import net.onrc.onos.core.intent.ConstrainedBFSTree;
+import net.onrc.onos.core.intent.Path;
+import net.onrc.onos.core.matchaction.match.Match;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.topology.BaseTopology;
+import net.onrc.onos.core.topology.ITopologyService;
+import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.util.SwitchPort;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static net.onrc.onos.core.newintent.PointToPointIntentCompiler.convertPath;
+
+/**
+ * An intent compiler for {@link MultiPointToSinglePointIntent}.
+ */
+public class MultiPointToSinglePointIntentCompiler
+ extends AbstractFlowGeneratingIntentCompiler<MultiPointToSinglePointIntent> {
+
+ private final ITopologyService topologyService;
+
+ /**
+ * Constructs an intent compiler for {@link MultiPointToSinglePointIntent}.
+ *
+ * @param intentIdGenerator intent ID generator
+ * @param flowIdGenerator flow ID generator
+ * @param topologyService topology service
+ */
+ public MultiPointToSinglePointIntentCompiler(IntentIdGenerator intentIdGenerator,
+ FlowIdGenerator flowIdGenerator,
+ ITopologyService topologyService) {
+ super(intentIdGenerator, flowIdGenerator);
+ this.topologyService = checkNotNull(topologyService);
+ }
+
+ @Override
+ public List<Intent> compile(MultiPointToSinglePointIntent intent) {
+ Match match = intent.getMatch();
+ if (!(match instanceof PacketMatch)) {
+ throw new IntentCompilationException(
+ "intent has unsupported type of match object: " + match
+ );
+ }
+
+ SingleDstTreeFlow treeFlow = new SingleDstTreeFlow(
+ getNextFlowId(),
+ (PacketMatch) intent.getMatch(), // down-cast, but it is safe due to the guard above
+ intent.getIngressPorts(),
+ calculateTree(intent.getIngressPorts(), intent.getEgressPort()),
+ packActions(intent, intent.getEgressPort())
+ );
+ Intent compiled = new SingleDstTreeFlowIntent(getNextId(), treeFlow);
+ return Arrays.asList(compiled);
+ }
+
+ /**
+ * Calculates a tree with the specified ingress ports and egress port.
+ *
+ * {@link PathNotFoundException} is thrown when no tree found or
+ * the specified egress port is not found in the topology.
+ *
+ * @param ingressPorts ingress ports
+ * @param egressPort egress port
+ * @return tree
+ * @throws PathNotFoundException if the specified egress switch is not
+ * found or no tree is found.
+ */
+ private Tree calculateTree(Set<SwitchPort> ingressPorts, SwitchPort egressPort) {
+ BaseTopology topology = topologyService.getTopology();
+ Switch egressSwitch = topology.getSwitch(egressPort.getDpid());
+ if (egressSwitch == null) {
+ throw new PathNotFoundException("destination switch not found: " + egressPort.getDpid());
+ }
+
+ ConstrainedBFSTree bfs = new ConstrainedBFSTree(egressSwitch);
+ Tree tree = new Tree();
+
+ for (SwitchPort ingressPort: ingressPorts) {
+ Switch ingressSwitch = topology.getSwitch(ingressPort.getDpid());
+ if (ingressSwitch == null) {
+ continue;
+ }
+
+ Path path = bfs.getPath(ingressSwitch);
+ if (path.isEmpty()) {
+ continue;
+ }
+
+ tree.addAll(convertPath(path));
+ }
+
+ if (tree.isEmpty()) {
+ throw new PathNotFoundException(
+ String.format("No tree found (ingress: %s, egress: %s", ingressPorts, egressPort)
+ );
+ }
+
+ return tree;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/newintent/PathIntentCompiler.java b/src/main/java/net/onrc/onos/core/newintent/PathIntentCompiler.java
new file mode 100644
index 0000000..e7e2f87
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/PathIntentCompiler.java
@@ -0,0 +1,82 @@
+package net.onrc.onos.core.newintent;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import net.onrc.onos.api.flowmanager.FlowIdGenerator;
+import net.onrc.onos.api.flowmanager.FlowLink;
+import net.onrc.onos.api.flowmanager.PacketPathFlow;
+import net.onrc.onos.api.flowmanager.Path;
+import net.onrc.onos.api.newintent.Intent;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+import net.onrc.onos.api.newintent.PathIntent;
+import net.onrc.onos.core.matchaction.match.Match;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.util.LinkTuple;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * An intent compiler for {@link PathIntent}.
+ */
+public class PathIntentCompiler
+ extends AbstractFlowGeneratingIntentCompiler<PathIntent> {
+
+ /**
+ * Construct an {@link net.onrc.onos.api.newintent.IntentCompiler}
+ * for {@link PathIntent}.
+ *
+ * @param intentIdGenerator intent ID generator
+ * @param flowIdGenerator flow ID generator
+ */
+ public PathIntentCompiler(IntentIdGenerator intentIdGenerator,
+ FlowIdGenerator flowIdGenerator) {
+ super(intentIdGenerator, flowIdGenerator);
+ }
+
+ @Override
+ public List<Intent> compile(PathIntent intent) {
+ Match match = intent.getMatch();
+ if (!(match instanceof PacketMatch)) {
+ throw new IntentCompilationException(
+ "intent has unsupported type of match object: " + match
+ );
+ }
+
+ Path path = convertPath(intent.getPath());
+ PacketPathFlow flow = new PacketPathFlow(
+ getNextFlowId(),
+ (PacketMatch) match,
+ intent.getIngressPort().getPortNumber(),
+ path,
+ packActions(intent, intent.getEgressPort()),
+ 0, 0
+ );
+ Intent compiled = new PathFlowIntent(getNextId(), flow);
+ return Arrays.asList(compiled);
+ }
+
+ /**
+ * Converts list of {@link LinkTuple LinkTuples} to a {@link Path}.
+ *
+ * @param tuples original list of {@link LinkTuple LinkTuples}
+ * @return converted {@link Path}
+ */
+ private Path convertPath(List<LinkTuple> tuples) {
+ // would like to use filter and transform, but Findbugs detects
+ // inconsistency of use of @Nullable annotation. Then, use of the
+ // transform is avoided.
+ // Ref: https://code.google.com/p/guava-libraries/issues/detail?id=1812
+ // TODO: replace with transform when the above issue is resolved
+ ImmutableList<LinkTuple> links = FluentIterable.from(tuples)
+ .filter(Predicates.notNull())
+ .toList();
+
+ Path path = new Path();
+ for (LinkTuple link: links) {
+ path.add(new FlowLink(link.getSrc(), link.getDst()));
+ }
+ return path;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/newintent/PathNotFoundException.java b/src/main/java/net/onrc/onos/core/newintent/PathNotFoundException.java
new file mode 100644
index 0000000..f08636e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/PathNotFoundException.java
@@ -0,0 +1,22 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.newintent.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/src/main/java/net/onrc/onos/core/newintent/PointToPointIntentCompiler.java b/src/main/java/net/onrc/onos/core/newintent/PointToPointIntentCompiler.java
new file mode 100644
index 0000000..fcf718d
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/PointToPointIntentCompiler.java
@@ -0,0 +1,117 @@
+package net.onrc.onos.core.newintent;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import net.onrc.onos.api.flowmanager.FlowId;
+import net.onrc.onos.api.flowmanager.FlowIdGenerator;
+import net.onrc.onos.api.flowmanager.FlowLink;
+import net.onrc.onos.api.flowmanager.PacketPathFlow;
+import net.onrc.onos.api.flowmanager.Path;
+import net.onrc.onos.api.newintent.Intent;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+import net.onrc.onos.api.newintent.PointToPointIntent;
+import net.onrc.onos.core.intent.ConstrainedBFSTree;
+import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.match.Match;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.topology.BaseTopology;
+import net.onrc.onos.core.topology.ITopologyService;
+import net.onrc.onos.core.topology.LinkEvent;
+import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.util.SwitchPort;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A intent compiler for {@link PointToPointIntent}.
+ */
+public class PointToPointIntentCompiler
+ extends AbstractFlowGeneratingIntentCompiler<PointToPointIntent> {
+
+ private final ITopologyService topologyService;
+
+ /**
+ * Constructs an intent compiler for {@link PointToPointIntent} with the specified
+ * ID generator and topology service.
+ *
+ * @param intentIdGenerator intent ID generator
+ * @param topologyService topology service
+ */
+ public PointToPointIntentCompiler(IntentIdGenerator intentIdGenerator,
+ FlowIdGenerator flowIdGenerator,
+ ITopologyService topologyService) {
+ super(intentIdGenerator, flowIdGenerator);
+ this.topologyService = checkNotNull(topologyService);
+ }
+
+ @Override
+ public List<Intent> compile(PointToPointIntent intent) {
+ Match match = intent.getMatch();
+ if (!(match instanceof PacketMatch)) {
+ throw new IntentCompilationException(
+ "intent has unsupported type of match object: " + match
+ );
+ }
+
+ SwitchPort ingress = intent.getIngressPort();
+ SwitchPort egress = intent.getEgressPort();
+ FlowId flowId = getNextFlowId();
+ Path path = calculatePath(ingress, egress);
+
+ List<Action> actions = packActions(intent, intent.getEgressPort());
+
+ PacketPathFlow flow = new PacketPathFlow(flowId, (PacketMatch) match,
+ ingress.getPortNumber(), path, actions, 0, 0);
+ return Arrays.asList((Intent) new PathFlowIntent(getNextId(), flow));
+ }
+
+ /**
+ * Calculates a path between the specified ingress port and the specified egress port.
+ * @param ingress ingress port
+ * @param egress egress port
+ * @return path
+ */
+ private Path calculatePath(SwitchPort ingress, SwitchPort egress) {
+ BaseTopology topology = topologyService.getTopology();
+ Switch source = topology.getSwitch(ingress.getDpid());
+ Switch destination = topology.getSwitch(egress.getDpid());
+
+ if (source == null) {
+ throw new PathNotFoundException("source switch not found: " + ingress.getDpid());
+ }
+ if (destination == null) {
+ throw new PathNotFoundException("destination switch not found: " + egress.getDpid());
+ }
+
+ ConstrainedBFSTree tree = new ConstrainedBFSTree(source);
+ net.onrc.onos.core.intent.Path path = tree.getPath(destination);
+ return convertPath(path);
+ }
+
+ /**
+ * Converts a {@link net.onrc.onos.core.intent.Path} to {@link Path}.
+ *
+ * @param path original {@link net.onrc.onos.core.intent.Path}
+ * @return converted {@link Path}
+ */
+ static Path convertPath(net.onrc.onos.core.intent.Path path) {
+ // would like to use filter and transform, but Findbugs detects
+ // inconsistency of use of @Nullable annotation. Then, use of the
+ // transform is avoided.
+ // Ref: https://code.google.com/p/guava-libraries/issues/detail?id=1812
+ // TODO: replace with transform when the above issue is resolved
+ ImmutableList<LinkEvent> events = FluentIterable.from(path)
+ .filter(Predicates.notNull())
+ .toList();
+
+ Path converted = new Path();
+ for (LinkEvent event: events) {
+ converted.add(new FlowLink(event.getSrc(), event.getDst()));
+ }
+ return converted;
+ }
+}