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/api/src/test/java/org/onosproject/net/resource/MockResourceService.java b/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java
index 6189b6b..436a763 100644
--- a/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java
+++ b/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java
@@ -36,6 +36,8 @@
public class MockResourceService implements ResourceService {
private final Map<Resource, ResourceConsumer> assignment = new HashMap<>();
+ public Set<Short> availableVlanLabels = new HashSet<>();
+ public Set<Integer> availableMplsLabels = new HashSet<>();
@Override
public List<ResourceAllocation> allocate(ResourceConsumer consumer, List<? extends Resource> resources) {
@@ -98,15 +100,45 @@
/**
- * It adds a number of VLAN ids in order to test the random behavior.
+ * Binds VLAN Ids to a parent resource, given a parent resource.
*
* @param parent the parent resource
- * @return a set of VLAN ids
+ * @return the VLAN Ids allocated
*/
private Collection<Resource> addVlanIds(DiscreteResourceId parent) {
Collection<Resource> resources = new HashSet<>();
- for (int i = VlanId.NO_VID + 1; i < VlanId.MAX_VLAN; i++) {
- resources.add(Resources.discrete(parent).resource().child(VlanId.vlanId((short) i)));
+ if (!this.availableVlanLabels.isEmpty()) {
+ this.availableVlanLabels.forEach(label -> {
+ if (label > VlanId.NO_VID && label < VlanId.MAX_VLAN) {
+ resources.add(Resources.discrete(parent).resource().child(VlanId.vlanId(label)));
+ }
+ });
+ } else {
+ for (int i = VlanId.NO_VID + 1; i < 1000; i++) {
+ resources.add(Resources.discrete(parent).resource().child(VlanId.vlanId((short) i)));
+ }
+ }
+ return resources;
+ }
+
+ /**
+ * Binds MPLS labels to a parent resource, given a parent resource.
+ *
+ * @param parent the parent resource
+ * @return the MPLS labels allocated
+ */
+ private Collection<Resource> addMplsLabels(DiscreteResourceId parent) {
+ Collection<Resource> resources = new HashSet<>();
+ if (!this.availableMplsLabels.isEmpty()) {
+ this.availableMplsLabels.forEach(label -> {
+ if (label < MplsLabel.MAX_MPLS) {
+ resources.add(Resources.discrete(parent).resource().child(MplsLabel.mplsLabel(label)));
+ }
+ });
+ } else {
+ for (int i = 1; i < 1000; i++) {
+ resources.add(Resources.discrete(parent).resource().child(MplsLabel.mplsLabel(i)));
+ }
}
return resources;
}
@@ -115,7 +147,7 @@
public Set<Resource> getAvailableResources(DiscreteResourceId parent) {
Collection<Resource> resources = new HashSet<>();
resources.addAll(addVlanIds(parent));
- resources.add(Resources.discrete(parent).resource().child(MplsLabel.mplsLabel(10)));
+ resources.addAll(addMplsLabels(parent));
resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(1)));
resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(2)));
resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(3)));
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;
+ }
+ }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
index a636fd4..e9f5170 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PathIntentCompilerTest.java
@@ -163,7 +163,8 @@
private PathIntent constraintMplsIntent;
/**
- * Configures objects used in all the test cases.
+ * Configures mock objects used in all the test cases.
+ * Creates the intents to test as well.
*/
@Before
public void setUp() {
@@ -914,14 +915,12 @@
}
/**
- * Tests the random selection of VlanIds in the PathCompiler.
+ * Tests the random selection of VLAN Ids in the PathCompiler.
* It can fail randomly (it is unlikely)
*/
@Test
public void testRandomVlanSelection() {
- if (PathCompiler.RANDOM_SELECTION) {
-
sut.activate();
List<Intent> compiled = sut.compile(constraintVlanIntent, Collections.emptyList());
@@ -969,7 +968,6 @@
sut.deactivate();
- }
}
@@ -1043,8 +1041,10 @@
.findFirst()
.get();
verifyIdAndPriority(rule1, d1p0.deviceId());
- assertThat(rule1.selector(), is(DefaultTrafficSelector.builder(selector)
- .matchInPort(d1p0.port()).build()));
+ assertThat(rule1.selector(), is(DefaultTrafficSelector
+ .builder(selector)
+ .matchInPort(d1p0.port())
+ .build()));
MplsLabel mplsLabelToEncap = verifyMplsEncapTreatment(rule1.treatment(), d1p1, true, false);
FlowRule rule2 = rules.stream()
@@ -1053,7 +1053,7 @@
.get();
verifyIdAndPriority(rule2, d2p0.deviceId());
verifyMplsEncapSelector(rule2.selector(), d2p0, mplsLabelToEncap);
- verifyMplsEncapTreatment(rule2.treatment(), d2p1, false, false);
+ mplsLabelToEncap = verifyMplsEncapTreatment(rule2.treatment(), d2p1, false, false);
FlowRule rule3 = rules.stream()
.filter(x -> x.deviceId().equals(d3p0.deviceId()))
@@ -1077,19 +1077,29 @@
assertThat((ruleOutput.iterator().next()).port(), is(egress.port()));
MplsLabel mplsToEncap = MplsLabel.mplsLabel(0);
if (isIngress && !isEgress) {
- Set<L2ModificationInstruction.ModMplsLabelInstruction> mplsRules =
- trafficTreatment.allInstructions().stream()
- .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
- .map(x -> (L2ModificationInstruction.ModMplsLabelInstruction) x)
- .collect(Collectors.toSet());
+ Set<L2ModificationInstruction.ModMplsLabelInstruction> mplsRules = trafficTreatment
+ .allInstructions()
+ .stream()
+ .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
+ .map(x -> (L2ModificationInstruction.ModMplsLabelInstruction) x)
+ .collect(Collectors.toSet());
assertThat(mplsRules, hasSize(1));
L2ModificationInstruction.ModMplsLabelInstruction mplsRule = mplsRules.iterator().next();
- assertThat(mplsRule.mplsLabel().toInt(), greaterThan(0));
- mplsToEncap = mplsRule.mplsLabel();
+ assertThat(mplsRule.label().toInt(), greaterThan(0));
+ assertThat(mplsRule.label().toInt(), lessThan(MplsLabel.MAX_MPLS));
+ mplsToEncap = mplsRule.label();
} else if (!isIngress && !isEgress) {
- assertThat(trafficTreatment.allInstructions().stream()
- .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
- .collect(Collectors.toSet()), hasSize(0));
+ Set<L2ModificationInstruction.ModMplsLabelInstruction> mplsRules = trafficTreatment
+ .allInstructions()
+ .stream()
+ .filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
+ .map(x -> (L2ModificationInstruction.ModMplsLabelInstruction) x)
+ .collect(Collectors.toSet());
+ assertThat(mplsRules, hasSize(1));
+ L2ModificationInstruction.ModMplsLabelInstruction mplsRule = mplsRules.iterator().next();
+ assertThat(mplsRule.label().toInt(), greaterThan(0));
+ assertThat(mplsRule.label().toInt(), lessThan(MplsLabel.MAX_MPLS));
+ mplsToEncap = mplsRule.label();
} else {
assertThat(trafficTreatment.allInstructions().stream()
.filter(treat -> treat instanceof L2ModificationInstruction.ModMplsLabelInstruction)
@@ -1099,7 +1109,6 @@
.collect(Collectors.toSet()), hasSize(1));
}
-
return mplsToEncap;
}
diff --git a/core/net/src/test/java/org/onosproject/net/resource/impl/LabelAllocatorTest.java b/core/net/src/test/java/org/onosproject/net/resource/impl/LabelAllocatorTest.java
new file mode 100644
index 0000000..4c47735
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/resource/impl/LabelAllocatorTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Identifier;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+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.intent.MockIdGenerator;
+import org.onosproject.net.resource.MockResourceService;
+import org.onosproject.net.resource.impl.LabelAllocator.FirstFitSelection;
+import org.onosproject.net.resource.impl.LabelAllocator.LabelSelection;
+import org.onosproject.net.resource.impl.LabelAllocator.RandomSelection;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
+import static org.onosproject.net.Link.Type.DIRECT;
+import static org.onosproject.net.NetTestTools.PID;
+import static org.onosproject.net.NetTestTools.connectPoint;
+
+/**
+ * Unit tests for LabelAllocator.
+ */
+public class LabelAllocatorTest {
+
+ private LabelAllocator allocator;
+ private MockResourceService resourceService;
+ private IdGenerator idGenerator = new MockIdGenerator();
+
+ private final ConnectPoint d1p0 = connectPoint("s1", 0);
+ private final ConnectPoint d1p1 = connectPoint("s1", 1);
+ private final ConnectPoint d2p0 = connectPoint("s2", 0);
+ private final ConnectPoint d2p1 = connectPoint("s2", 1);
+
+ private final List<Link> links = Arrays.asList(
+ createEdgeLink(d1p0, true),
+ DefaultLink.builder().providerId(PID).src(d1p1).dst(d2p1).type(DIRECT).build(),
+ createEdgeLink(d2p0, false)
+ );
+
+ private final String firstFit = "FIRST_FIT";
+ private final String random = "RANDOM";
+ private final String wrong = "BLAHBLAHBLAH";
+
+ @Before
+ public void setUp() {
+ this.resourceService = new MockResourceService();
+ this.allocator = new LabelAllocator(this.resourceService);
+ }
+
+ @After
+ public void tearDown() {
+
+ }
+
+ /**
+ * To test changes to the selection behavior.
+ */
+ @Test
+ public void testChangeBehavior() {
+ // It has to be an instance of LabelSelection
+ assertThat(this.allocator.getLabelSelection(), instanceOf(LabelSelection.class));
+ // By default we have Random Selection
+ assertThat(this.allocator.getLabelSelection(), instanceOf(RandomSelection.class));
+ // We change to FirstFit and we test the change
+ this.allocator.setLabelSelection(firstFit);
+ assertThat(this.allocator.getLabelSelection(), instanceOf(FirstFitSelection.class));
+ // We change to Random and we test the change
+ this.allocator.setLabelSelection(random);
+ assertThat(this.allocator.getLabelSelection(), instanceOf(RandomSelection.class));
+ // We put a wrong type and we should have a Random selection
+ this.allocator.setLabelSelection(wrong);
+ assertThat(this.allocator.getLabelSelection(), instanceOf(RandomSelection.class));
+ }
+
+ /**
+ * To test the first fit behavior with VLAN Id. In the First step
+ * we use the default set, for the first selection the selected label
+ * has to be 1. In the Second step we change the default set and for
+ * the first fit selection the selected has to be 2.
+ */
+ @Test
+ public void testFirstFitBehaviorVlan() {
+ // We change to FirstFit and we test the change
+ this.allocator.setLabelSelection(firstFit);
+ assertThat(this.allocator.getLabelSelection(), instanceOf(FirstFitSelection.class));
+ // We test the behavior for VLAN
+ Map<LinkKey, Identifier<?>> allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.VLAN);
+ Identifier<?> id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a VlanId
+ assertThat(id, instanceOf(VlanId.class));
+ // value should not be a forbidden value
+ VlanId vlanId = (VlanId) id;
+ assertTrue(VlanId.NO_VID < vlanId.toShort() && vlanId.toShort() < VlanId.MAX_VLAN);
+ // value will be always 1
+ assertEquals(1, vlanId.toShort());
+
+ // We change the available Ids
+ this.resourceService.availableVlanLabels = ImmutableSet.of(
+ (short) 100,
+ (short) 11,
+ (short) 20,
+ (short) 2,
+ (short) 3
+ );
+ // We test again the behavior for VLAN
+ allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.VLAN);
+ id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a VlanId
+ assertThat(id, instanceOf(VlanId.class));
+ // value should not be a forbidden value
+ vlanId = (VlanId) id;
+ assertTrue(VlanId.NO_VID < vlanId.toShort() && vlanId.toShort() < VlanId.MAX_VLAN);
+ // value will be always 2
+ assertEquals(2, vlanId.toShort());
+ }
+
+ /**
+ * To test the first fit behavior with MPLS label. In the First step
+ * we use the default set, for the first selection the selected label
+ * has to be 1. In the Second step we change the default set and for
+ * the first fit selection the selected has to be 100.
+ */
+ @Test
+ public void testFirstFitBehaviorMpls() {
+ // We change to FirstFit and we test the change
+ this.allocator.setLabelSelection(firstFit);
+ assertThat(this.allocator.getLabelSelection(), instanceOf(FirstFitSelection.class));
+ // We test the behavior for MPLS
+ Map<LinkKey, Identifier<?>> allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.MPLS);
+ Identifier<?> id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a Mplslabel
+ assertThat(id, instanceOf(MplsLabel.class));
+ // value should not be a forbidden value
+ MplsLabel mplsLabel = (MplsLabel) id;
+ assertTrue(0 < mplsLabel.toInt() && mplsLabel.toInt() < MplsLabel.MAX_MPLS);
+ // value will be always 1
+ assertEquals(1, mplsLabel.toInt());
+
+ // We change the available Ids
+ this.resourceService.availableMplsLabels = ImmutableSet.of(
+ 100,
+ 200,
+ 1000
+ );
+ // We test again the behavior for MPLS
+ allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.MPLS);
+ id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a Mplslabel
+ assertThat(id, instanceOf(MplsLabel.class));
+ // value should not be a forbidden value
+ mplsLabel = (MplsLabel) id;
+ assertTrue(0 < mplsLabel.toInt() && mplsLabel.toInt() < MplsLabel.MAX_MPLS);
+ // value will be always 100
+ assertEquals(100, mplsLabel.toInt());
+ }
+
+ /**
+ * To test the random behavior with VLAN Id. We make two selection,
+ * we test that these two selection are different.
+ */
+ @Test
+ public void testRandomBehaviorVlan() {
+ // Verify the random behavior
+ assertThat(this.allocator.getLabelSelection(), instanceOf(RandomSelection.class));
+ // We test the behavior for VLAN
+ Map<LinkKey, Identifier<?>> allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.VLAN);
+ Identifier<?> id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a VlanId
+ assertThat(id, instanceOf(VlanId.class));
+ // value should not be a forbidden value
+ Short value = Short.parseShort(id.toString());
+ VlanId prevVlanId = VlanId.vlanId(value);
+ assertTrue(VlanId.NO_VID < prevVlanId.toShort() && prevVlanId.toShort() < VlanId.MAX_VLAN);
+
+ allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.VLAN);
+ id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a VlanId
+ assertThat(id, instanceOf(VlanId.class));
+ // value should not be a forbidden value
+ VlanId vlanId = (VlanId) id;
+ assertTrue(VlanId.NO_VID < vlanId.toShort() && vlanId.toShort() < VlanId.MAX_VLAN);
+ assertNotEquals(vlanId, prevVlanId);
+
+ }
+
+ /**
+ * To test random behavior with MPLS label. We make two selection,
+ * we test that these two selection are different.
+ */
+ @Test
+ public void testRandomBehaviorMpls() {
+ // Verify the random behavior
+ assertThat(this.allocator.getLabelSelection(), instanceOf(RandomSelection.class));
+ // We test the behavior for MPLS
+ Map<LinkKey, Identifier<?>> allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.MPLS);
+ Identifier<?> id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a Mplslabel
+ assertThat(id, instanceOf(MplsLabel.class));
+ // value should not be a forbidden value
+ MplsLabel prevMplsId = (MplsLabel) id;
+ assertTrue(0 < prevMplsId.toInt() && prevMplsId.toInt() < MplsLabel.MAX_MPLS);
+
+ allocation = this.allocator.assignLabelToLinks(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.MPLS);
+ id = allocation.get(LinkKey.linkKey(d1p1, d2p1));
+ // value has to be a Mplslabel
+ assertThat(id, instanceOf(MplsLabel.class));
+ // value should not be a forbidden value
+ MplsLabel mplsId = (MplsLabel) id;
+ assertTrue(0 < mplsId.toInt() && mplsId.toInt() < MplsLabel.MAX_MPLS);
+ assertNotEquals(prevMplsId, mplsId);
+
+ }
+
+ /**
+ * To test the port key based API.
+ */
+ @Test
+ public void testPortKey() {
+ // Verify the first behavior
+ this.allocator.setLabelSelection(firstFit);
+ assertThat(this.allocator.getLabelSelection(), instanceOf(FirstFitSelection.class));
+ // We test the behavior for VLAN
+ Map<ConnectPoint, Identifier<?>> allocation = this.allocator.assignLabelToPorts(
+ ImmutableSet.copyOf(links.subList(1, 2)),
+ IntentId.valueOf(idGenerator.getNewId()),
+ EncapsulationType.VLAN);
+ Identifier<?> id = allocation.get(new ConnectPoint(d1p1.elementId(), d1p1.port()));
+ // value has to be a VlanId
+ assertThat(id, instanceOf(VlanId.class));
+ // value should not be a forbidden value
+ VlanId prevVlanId = (VlanId) id;
+ assertTrue(VlanId.NO_VID < prevVlanId.toShort() && prevVlanId.toShort() < VlanId.MAX_VLAN);
+ // value has to be 1
+ assertEquals(1, prevVlanId.toShort());
+ // verify same applies for d2p1
+ id = allocation.get(new ConnectPoint(d2p1.elementId(), d2p1.port()));
+ assertThat(id, instanceOf(VlanId.class));
+ // value should not be a forbidden value
+ VlanId vlanId = (VlanId) id;
+ assertTrue(VlanId.NO_VID < vlanId.toShort() && vlanId.toShort() < VlanId.MAX_VLAN);
+ // value has to be 1
+ assertEquals(prevVlanId, vlanId);
+ }
+
+
+
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java b/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
index 82f951f..6a1418d 100644
--- a/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
+++ b/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
@@ -15,18 +15,18 @@
*/
package org.onlab.packet;
+import org.onlab.util.Identifier;
+
/**
* Representation of a MPLS label.
*/
-public class MplsLabel {
-
- private final int mplsLabel;
+public final class MplsLabel extends Identifier<Integer> {
// An MPLS Label maximum 20 bits.
public static final int MAX_MPLS = 0xFFFFF;
protected MplsLabel(int value) {
- this.mplsLabel = value;
+ super(value);
}
public static MplsLabel mplsLabel(int value) {
@@ -39,35 +39,27 @@
return new MplsLabel(value);
}
+ /**
+ * Creates a MplsLabel object using the supplied decimal string.
+ *
+ * @param value the MPLS identifier expressed as string
+ * @return Mplslabel object created from the string
+ */
+ public static MplsLabel mplsLabel(String value) {
+ try {
+ return MplsLabel.mplsLabel(Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
public int toInt() {
- return this.mplsLabel;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof MplsLabel) {
-
- MplsLabel other = (MplsLabel) obj;
-
- if (this.mplsLabel == other.mplsLabel) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public int hashCode() {
- return this.mplsLabel;
+ return this.id();
}
@Override
public String toString() {
- return String.valueOf(this.mplsLabel);
+ return String.valueOf(this.identifier);
}
+
}