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/IntentConfigurableRegistrator.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java
index fea1e7b..50578de 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/IntentConfigurableRegistrator.java
@@ -30,6 +30,7 @@
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.resource.impl.LabelAllocator;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
@@ -57,9 +58,15 @@
private static final boolean DEFAULT_FLOW_OBJECTIVES = false;
@Property(name = "useFlowObjectives",
boolValue = DEFAULT_FLOW_OBJECTIVES,
- label = "Indicates whether to use flow objective-based compilers")
+ label = "Indicates whether or not to use flow objective-based compilers")
private boolean useFlowObjectives = DEFAULT_FLOW_OBJECTIVES;
+ private static final String DEFAULT_LABEL_SELECTION = "RANDOM";
+ @Property(name = "labelSelection",
+ value = DEFAULT_LABEL_SELECTION,
+ label = "Defines the label selection algorithm - RANDOM or FIRST_FIT")
+ private String labelSelection = DEFAULT_LABEL_SELECTION;
+
private final Map<Class<Intent>, IntentCompiler<Intent>> flowRuleBased = Maps.newConcurrentMap();
private final Map<Class<Intent>, IntentCompiler<Intent>> flowObjectiveBased = Maps.newConcurrentMap();
@@ -79,6 +86,7 @@
public void modified(ComponentContext context) {
if (context == null) {
log.info("Settings: useFlowObjectives={}", useFlowObjectives);
+ log.info("Settings: labelSelection={}", labelSelection);
return;
}
@@ -95,13 +103,27 @@
changeCompilers();
log.info("Settings: useFlowObjectives={}", useFlowObjectives);
}
+
+ String newLabelSelection;
+ try {
+ String s = Tools.get(context.getProperties(), "labelSelection");
+ newLabelSelection = isNullOrEmpty(s) ? labelSelection : s.trim();
+ } catch (ClassCastException e) {
+ newLabelSelection = labelSelection;
+ }
+
+ if (!labelSelection.equals(newLabelSelection) && LabelAllocator.isInEnum(newLabelSelection)) {
+ labelSelection = newLabelSelection;
+ changeLabelSelections();
+ log.info("Settings: labelSelection={}", labelSelection);
+ }
}
/**
* Registers the specified compiler for the given intent class.
*
- * @param cls intent class
- * @param compiler intent compiler
+ * @param cls the intent class
+ * @param compiler the intent compiler
* @param flowBased true if the compiler is flow based
* @param <T> the type of intent
*/
@@ -121,7 +143,7 @@
/**
* Unregisters the compiler for the specified intent class.
*
- * @param cls intent class
+ * @param cls the intent class
* @param flowBased true if the compiler is flow based
* @param <T> the type of intent
*/
@@ -147,4 +169,8 @@
}
}
+ private void changeLabelSelections() {
+ PathCompiler.labelAllocator.setLabelSelection(labelSelection);
+ }
+
}
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.
*
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
index fdcdd00..ddc7f21 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
@@ -36,6 +36,7 @@
import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.PathIntent;
import org.onosproject.net.resource.ResourceService;
+import org.onosproject.net.resource.impl.LabelAllocator;
import org.slf4j.Logger;
import java.util.LinkedList;
@@ -67,6 +68,7 @@
public void activate() {
appId = coreService.registerApplication("org.onosproject.net.intent");
registrator.registerCompiler(PathIntent.class, this, false);
+ labelAllocator = new LabelAllocator(resourceService);
}
@Deactivate
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java
index ac1c051..3f0fc19 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentFlowObjectiveCompiler.java
@@ -40,6 +40,7 @@
import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.PathIntent;
import org.onosproject.net.resource.ResourceService;
+import org.onosproject.net.resource.impl.LabelAllocator;
import org.slf4j.Logger;
import java.util.LinkedList;
@@ -73,6 +74,7 @@
public void activate() {
appId = coreService.registerApplication("org.onosproject.net.intent");
registrator.registerCompiler(PathIntent.class, this, true);
+ labelAllocator = new LabelAllocator(resourceService);
}
@Deactivate
diff --git a/core/net/src/main/java/org/onosproject/net/resource/impl/LabelAllocator.java b/core/net/src/main/java/org/onosproject/net/resource/impl/LabelAllocator.java
new file mode 100644
index 0000000..4086768
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/resource/impl/LabelAllocator.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.resource.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.commons.lang.math.RandomUtils;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Identifier;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.intent.IntentId;
+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 java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Helper class which interacts with the ResourceService and provides
+ * a unified API to allocate MPLS labels and VLAN Ids.
+ */
+public final class LabelAllocator {
+
+ private enum Behavior {
+ /**
+ * Random selection.
+ */
+ RANDOM,
+ /**
+ * First fit selection.
+ */
+ FIRST_FIT
+ }
+
+ private static final Behavior[] BEHAVIORS = Behavior.values();
+
+ private ResourceService resourceService;
+ private LabelSelection labelSelection;
+
+ /**
+ * Creates a new label allocator. Random is the
+ * default behavior.
+ *
+ * @param rs the resource service
+ */
+ public LabelAllocator(ResourceService rs) {
+ this.resourceService = rs;
+ this.labelSelection = this.getLabelSelection(Behavior.RANDOM);
+ }
+
+ /**
+ * Checks if a given string is a valid Behavior.
+ *
+ * @param value the string to check
+ * @return true if value is a valid Behavior, false otherwise
+ */
+ public static boolean isInEnum(String value) {
+ for (Behavior b : BEHAVIORS) {
+ if (b.name().equals(value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Changes the selection behavior.
+ *
+ * @param type the behavior type
+ */
+ public void setLabelSelection(String type) {
+ if (isInEnum(type)) {
+ this.labelSelection = this.getLabelSelection(type);
+ }
+ }
+
+ /**
+ * Retrieves the label selection behavior.
+ *
+ * @return the label selection behavior in use
+ */
+ public LabelSelection getLabelSelection() {
+ return this.labelSelection;
+ }
+
+ /**
+ * Returns the label selection behavior, given a behavior type.
+ *
+ * @param type the behavior type
+ * @return the label selection behavior in use
+ */
+ private LabelSelection getLabelSelection(String type) {
+ Behavior behavior = Behavior.valueOf(type);
+ return this.getLabelSelection(behavior);
+ }
+
+ /**
+ * Creates a new LabelSelection. Random is
+ * the default label selection behavior.
+ *
+ * @param type the behavior type
+ * @return the object implementing the behavior
+ */
+ private LabelSelection getLabelSelection(Behavior type) {
+ LabelSelection selection = null;
+ switch (type) {
+ case FIRST_FIT:
+ selection = new FirstFitSelection();
+ break;
+ case RANDOM:
+ default:
+ selection = new RandomSelection();
+ break;
+ }
+ return selection;
+ }
+
+ /**
+ * Looks for available Ids.
+ *
+ * @param links the links where to look for Ids
+ * @param type the encapsulation type
+ * @return the mappings between key and id
+ */
+ private Map<LinkKey, Identifier<?>> findAvailableIDs(Set<LinkKey> links, EncapsulationType type) {
+
+ Map<LinkKey, Identifier<?>> ids = Maps.newHashMap();
+ for (LinkKey link : links) {
+ Set<Identifier<?>> availableIDsatSrc = getAvailableIDs(link.src(), type);
+ Set<Identifier<?>> availableIDsatDst = getAvailableIDs(link.dst(), type);
+ Set<Identifier<?>> common = Sets.intersection(availableIDsatSrc, availableIDsatDst);
+ if (common.isEmpty()) {
+ continue;
+ }
+ Identifier<?> selected = labelSelection.select(common);
+ if (selected == null) {
+ continue;
+ }
+ ids.put(link, selected);
+ }
+ return ids;
+ }
+
+ /**
+ * Looks for available Ids associated to the given connection point.
+ *
+ * @param cp the connection point
+ * @param type the type of Id
+ * @return the set of available Ids
+ */
+ private Set<Identifier<?>> getAvailableIDs(ConnectPoint cp, EncapsulationType type) {
+ return resourceService.getAvailableResourceValues(
+ Resources.discrete(cp.deviceId(), cp.port()).id(), getEncapsulationClass(type)
+ );
+ }
+
+ /**
+ * Method to map the encapsulation type to identifier class.
+ * VLAN is the default encapsulation.
+ *
+ * @param type the type of encapsulation
+ * @return the id class
+ */
+ private Class getEncapsulationClass(EncapsulationType type) {
+ Class idType;
+ switch (type) {
+ case MPLS:
+ idType = MplsLabel.class;
+ break;
+ case VLAN:
+ default:
+ idType = VlanId.class;
+ }
+ return idType;
+ }
+
+ /**
+ * Allocates labels and associates them to links.
+ *
+ * @param links the links where labels will be allocated
+ * @param id the intent Id
+ * @param type the encapsulation type
+ * @return the list of links and associated labels
+ */
+ public Map<LinkKey, Identifier<?>> assignLabelToLinks(Set<Link> links, IntentId id, EncapsulationType type) {
+
+ Set<LinkKey> linkRequest = Sets.newHashSet();
+
+ links.forEach(link -> {
+ linkRequest.add(LinkKey.linkKey(link));
+ });
+
+ Map<LinkKey, Identifier<?>> availableIds = findAvailableIDs(linkRequest, type);
+ if (availableIds.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ Set<Resource> resources = availableIds.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 = resourceService.allocate(id, ImmutableList.copyOf(resources));
+
+ if (allocations.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ return ImmutableMap.copyOf(availableIds);
+ }
+
+ /**
+ * Allocates labels and associates them to source
+ * and destination ports of a link.
+ *
+ * @param links the links on which labels will be reserved
+ * @param id the intent Id
+ * @param type the encapsulation type
+ * @return the list of ports and associated labels
+ */
+ public Map<ConnectPoint, Identifier<?>> assignLabelToPorts(Set<Link> links, IntentId id, EncapsulationType type) {
+ Map<LinkKey, Identifier<?>> allocation = this.assignLabelToLinks(links, id, type);
+ if (allocation.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<ConnectPoint, Identifier<?>> finalAllocation = Maps.newHashMap();
+ allocation.forEach((key, value) -> {
+ finalAllocation.putIfAbsent(key.src(), value);
+ finalAllocation.putIfAbsent(key.dst(), value);
+ });
+ return ImmutableMap.copyOf(finalAllocation);
+ }
+
+ /**
+ * Interface for selection algorithms of the labels.
+ */
+ public interface LabelSelection {
+
+ /**
+ * Picks an element from values using a particular algorithm.
+ *
+ * @param values the values to select from
+ * @return the selected identifier if values are present, null otherwise
+ */
+ Identifier<?> select(Set<Identifier<?>> values);
+
+ }
+
+ /**
+ * Random label selection.
+ */
+ public static class RandomSelection implements LabelSelection {
+
+ /**
+ * Selects an identifier from a given set of values using
+ * the random selection algorithm.
+ *
+ * @param values the values to select from
+ * @return the selected identifier if values are present, null otherwise
+ */
+ @Override
+ public Identifier<?> select(Set<Identifier<?>> values) {
+ if (!values.isEmpty()) {
+ int size = values.size();
+ int index = RandomUtils.nextInt(size);
+ return Iterables.get(values, index);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * First fit label selection.
+ */
+ public static class FirstFitSelection implements LabelSelection {
+
+ /**
+ * Selects an identifier from a given set of values using
+ * the first fir selection algorithm.
+ *
+ * @param values the values to select from
+ * @return the selected identifier if values are present, null otherwise.
+ */
+ @Override
+ public Identifier<?> select(Set<Identifier<?>> values) {
+ if (!values.isEmpty()) {
+ return values.iterator().next();
+ }
+ return null;
+ }
+ }
+
+}