blob: 40867680acfc4eddbf9e6722571f36c73712b73d [file] [log] [blame]
/*
* 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;
}
}
}