Fixes [ONOS-5412] and implements [ONOS-5300]
Changes:
- Adds a new Interface for the selection algorithms;
- Re-implements FirstFit and Random selection;
- Adds a new option to select the algorithm;
- LabelAllocator provides a single interface;
- Fix MPLS encapsulation;
Change-Id: Ib07942355c45b7b9e7093fa85964c2ac20800b60
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
index b831fef..b236d59 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathCompiler.java
@@ -15,16 +15,15 @@
*/
package org.onosproject.net.intent.impl.compiler;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import org.apache.commons.lang.math.RandomUtils;
import org.onlab.packet.EthType;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.VlanId;
+import org.onlab.util.Identifier;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.EncapsulationType;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -40,21 +39,15 @@
import org.onosproject.net.intent.IntentCompilationException;
import org.onosproject.net.intent.PathIntent;
import org.onosproject.net.intent.constraint.EncapsulationConstraint;
-import org.onosproject.net.resource.Resource;
-import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceService;
-import org.onosproject.net.resource.Resources;
+import org.onosproject.net.resource.impl.LabelAllocator;
import org.slf4j.Logger;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static org.onosproject.net.LinkKey.linkKey;
@@ -64,7 +57,10 @@
public class PathCompiler<T> {
- public static final boolean RANDOM_SELECTION = true;
+ private static final String ERROR_VLAN = "No VLAN Ids available for ";
+ private static final String ERROR_MPLS = "No available MPLS labels for ";
+
+ static LabelAllocator labelAllocator;
/**
* Defines methods used to create objects representing flows.
@@ -88,108 +84,46 @@
return i == links.size() - 2;
}
- private Map<LinkKey, VlanId> assignVlanId(PathCompilerCreateFlow creator, PathIntent intent) {
- Set<LinkKey> linkRequest =
- Sets.newHashSetWithExpectedSize(intent.path()
- .links().size() - 2);
- for (int i = 1; i <= intent.path().links().size() - 2; i++) {
- LinkKey link = linkKey(intent.path().links().get(i));
- linkRequest.add(link);
- // add the inverse link. I want that the VLANID is reserved both for
- // the direct and inverse link
- linkRequest.add(linkKey(link.dst(), link.src()));
- }
-
- Map<LinkKey, VlanId> vlanIds = findVlanIds(creator, linkRequest);
- if (vlanIds.isEmpty()) {
- creator.log().warn("No VLAN IDs available");
- return Collections.emptyMap();
- }
-
- //same VLANID is used for both directions
- Set<Resource> resources = vlanIds.entrySet().stream()
- .flatMap(x -> Stream.of(
- Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
- .resource(),
- Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
- .resource()
- ))
- .collect(Collectors.toSet());
- List<ResourceAllocation> allocations =
- creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
- if (allocations.isEmpty()) {
- return Collections.emptyMap();
- }
-
- return vlanIds;
- }
-
/**
- * Implements the first fit selection behavior.
+ * Returns the ethertype match needed. If the selector provides
+ * an ethertype, it will be used. IPv4 will be used otherwise.
*
- * @param available the set of available VLAN ids.
- * @return the chosen VLAN id.
+ * @param selector the traffic selector.
+ * @return the ethertype we should match against
*/
- private VlanId firsFitSelection(Set<VlanId> available) {
- if (!available.isEmpty()) {
- return available.iterator().next();
+ private EthType getEthType(TrafficSelector selector) {
+ Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
+ if (c != null && c instanceof EthTypeCriterion) {
+ EthTypeCriterion ethertype = (EthTypeCriterion) c;
+ return ethertype.ethType();
+ } else {
+ return EthType.EtherType.IPV4.ethType();
}
- return VlanId.vlanId(VlanId.NO_VID);
}
/**
- * Implements the random selection behavior.
+ * Creates the flow rules for the path intent using VLAN
+ * encapsulation.
*
- * @param available the set of available VLAN ids.
- * @return the chosen VLAN id.
+ * @param creator the flowrules creator
+ * @param flows the list of flows to fill
+ * @param devices the devices on the path
+ * @param intent the PathIntent to compile
*/
- private VlanId randomSelection(Set<VlanId> available) {
- if (!available.isEmpty()) {
- int size = available.size();
- int index = RandomUtils.nextInt(size);
- return Iterables.get(available, index);
- }
- return VlanId.vlanId(VlanId.NO_VID);
- }
-
- /**
- * Select a VLAN id from the set of available VLAN ids.
- *
- * @param available the set of available VLAN ids.
- * @return the chosen VLAN id.
- */
- private VlanId selectVlanId(Set<VlanId> available) {
- return RANDOM_SELECTION ? randomSelection(available) : firsFitSelection(available);
- }
-
- private Map<LinkKey, VlanId> findVlanIds(PathCompilerCreateFlow creator, Set<LinkKey> links) {
- Map<LinkKey, VlanId> vlanIds = new HashMap<>();
- for (LinkKey link : links) {
- Set<VlanId> forward = findVlanId(creator, link.src());
- Set<VlanId> backward = findVlanId(creator, link.dst());
- Set<VlanId> common = Sets.intersection(forward, backward);
- if (common.isEmpty()) {
- continue;
- }
- VlanId selected = selectVlanId(common);
- if (selected.toShort() == VlanId.NO_VID) {
- continue;
- }
- vlanIds.put(link, selected);
- }
- return vlanIds;
- }
-
- private Set<VlanId> findVlanId(PathCompilerCreateFlow creator, ConnectPoint cp) {
- return creator.resourceService().getAvailableResourceValues(
- Resources.discrete(cp.deviceId(), cp.port()).id(),
- VlanId.class);
- }
-
private void manageVlanEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
List<DeviceId> devices,
PathIntent intent) {
- Map<LinkKey, VlanId> vlanIds = assignVlanId(creator, intent);
+
+ Set<Link> linksSet = Sets.newConcurrentHashSet();
+ for (int i = 1; i <= intent.path().links().size() - 2; i++) {
+ linksSet.add(intent.path().links().get(i));
+ }
+
+ Map<LinkKey, Identifier<?>> vlanIds = labelAllocator.assignLabelToLinks(
+ linksSet,
+ intent.id(),
+ EncapsulationType.VLAN
+ );
Iterator<Link> links = intent.path().links().iterator();
Link srcLink = links.next();
@@ -197,9 +131,9 @@
Link link = links.next();
// Ingress traffic
- VlanId vlanId = vlanIds.get(linkKey(link));
+ VlanId vlanId = (VlanId) vlanIds.get(linkKey(link));
if (vlanId == null) {
- throw new IntentCompilationException("No available VLAN ID for " + link);
+ throw new IntentCompilationException(ERROR_VLAN + link);
}
VlanId prevVlanId = vlanId;
@@ -227,9 +161,9 @@
if (links.hasNext()) {
// Transit traffic
- VlanId egressVlanId = vlanIds.get(linkKey(link));
+ VlanId egressVlanId = (VlanId) vlanIds.get(linkKey(link));
if (egressVlanId == null) {
- throw new IntentCompilationException("No available VLAN ID for " + link);
+ throw new IntentCompilationException(ERROR_VLAN + link);
}
TrafficSelector transitSelector = DefaultTrafficSelector.builder()
@@ -284,68 +218,29 @@
}
}
- private Map<LinkKey, MplsLabel> assignMplsLabel(PathCompilerCreateFlow creator, PathIntent intent) {
- Set<LinkKey> linkRequest =
- Sets.newHashSetWithExpectedSize(intent.path()
- .links().size() - 2);
- for (int i = 1; i <= intent.path().links().size() - 2; i++) {
- LinkKey link = linkKey(intent.path().links().get(i));
- linkRequest.add(link);
- // add the inverse link. I want that the VLANID is reserved both for
- // the direct and inverse link
- linkRequest.add(linkKey(link.dst(), link.src()));
- }
-
- Map<LinkKey, MplsLabel> labels = findMplsLabels(creator, linkRequest);
- if (labels.isEmpty()) {
- throw new IntentCompilationException("No available MPLS Label");
- }
-
- // for short term solution: same label is used for both directions
- // TODO: introduce the concept of Tx and Rx resources of a port
- Set<Resource> resources = labels.entrySet().stream()
- .flatMap(x -> Stream.of(
- Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
- .resource(),
- Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
- .resource()
- ))
- .collect(Collectors.toSet());
- List<ResourceAllocation> allocations =
- creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
- if (allocations.isEmpty()) {
- return Collections.emptyMap();
- }
-
- return labels;
- }
-
- private Map<LinkKey, MplsLabel> findMplsLabels(PathCompilerCreateFlow creator, Set<LinkKey> links) {
- Map<LinkKey, MplsLabel> labels = new HashMap<>();
- for (LinkKey link : links) {
- Set<MplsLabel> forward = findMplsLabel(creator, link.src());
- Set<MplsLabel> backward = findMplsLabel(creator, link.dst());
- Set<MplsLabel> common = Sets.intersection(forward, backward);
- if (common.isEmpty()) {
- continue;
- }
- labels.put(link, common.iterator().next());
- }
-
- return labels;
- }
-
- private Set<MplsLabel> findMplsLabel(PathCompilerCreateFlow creator, ConnectPoint cp) {
- return creator.resourceService().getAvailableResourceValues(
- Resources.discrete(cp.deviceId(), cp.port()).id(),
- MplsLabel.class);
- }
-
+ /**
+ * Creates the flow rules for the path intent using MPLS
+ * encapsulation.
+ *
+ * @param creator the flowrules creator
+ * @param flows the list of flows to fill
+ * @param devices the devices on the path
+ * @param intent the PathIntent to compile
+ */
private void manageMplsEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
List<DeviceId> devices,
PathIntent intent) {
- Map<LinkKey, MplsLabel> mplsLabels = assignMplsLabel(creator, intent);
+ Set<Link> linksSet = Sets.newConcurrentHashSet();
+ for (int i = 1; i <= intent.path().links().size() - 2; i++) {
+ linksSet.add(intent.path().links().get(i));
+ }
+
+ Map<LinkKey, Identifier<?>> mplsLabels = labelAllocator.assignLabelToLinks(
+ linksSet,
+ intent.id(),
+ EncapsulationType.MPLS
+ );
Iterator<Link> links = intent.path().links().iterator();
Link srcLink = links.next();
@@ -353,9 +248,9 @@
// List of flow rules to be installed
// Ingress traffic
- MplsLabel mplsLabel = mplsLabels.get(linkKey(link));
+ MplsLabel mplsLabel = (MplsLabel) mplsLabels.get(linkKey(link));
if (mplsLabel == null) {
- throw new IntentCompilationException("No available MPLS Label for " + link);
+ throw new IntentCompilationException(ERROR_MPLS + link);
}
MplsLabel prevMplsLabel = mplsLabel;
@@ -382,12 +277,10 @@
if (links.hasNext()) {
// Transit traffic
- MplsLabel transitMplsLabel = mplsLabels.get(linkKey(link));
+ MplsLabel transitMplsLabel = (MplsLabel) mplsLabels.get(linkKey(link));
if (transitMplsLabel == null) {
- throw new IntentCompilationException("No available MPLS label for " + link);
+ throw new IntentCompilationException(ERROR_MPLS + link);
}
- prevMplsLabel = transitMplsLabel;
-
TrafficSelector transitSelector = DefaultTrafficSelector.builder()
.matchInPort(prev.port())
.matchEthType(Ethernet.MPLS_UNICAST)
@@ -395,12 +288,13 @@
TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
- // Set the new MPLS Label only if the previous one is different
+ // Set the new MPLS label only if the previous one is different
if (!prevMplsLabel.equals(transitMplsLabel)) {
transitTreat.setMpls(transitMplsLabel);
}
creator.createFlow(transitSelector,
transitTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
+ prevMplsLabel = transitMplsLabel;
prev = link.dst();
} else {
TrafficSelector.Builder egressSelector = DefaultTrafficSelector.builder()
@@ -428,14 +322,7 @@
if (mplsCriterion.isPresent()) {
egressTreat.setMpls(mplsCriterion.get().label());
} else {
- egressTreat.popMpls(outputEthType(intent.selector()));
- }
-
-
- if (mplsCriterion.isPresent()) {
- egressTreat.setMpls(mplsCriterion.get().label());
- } else {
- egressTreat.popVlan();
+ egressTreat.popMpls(getEthType(intent.selector()));
}
creator.createFlow(egressSelector.build(),
@@ -446,23 +333,6 @@
}
- private MplsLabel getMplsLabel(Map<LinkKey, MplsLabel> labels, LinkKey link) {
- return labels.get(link);
- }
-
- // if the ingress ethertype is defined, the egress traffic
- // will be use that value, otherwise the IPv4 ethertype is used.
- private EthType outputEthType(TrafficSelector selector) {
- Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
- if (c != null && c instanceof EthTypeCriterion) {
- EthTypeCriterion ethertype = (EthTypeCriterion) c;
- return ethertype.ethType();
- } else {
- return EthType.EtherType.IPV4.ethType();
- }
- }
-
-
/**
* Compiles an intent down to flows.
*