blob: 3ed670d3ddbe4866de0158bbcb6c7f5bb3551c06 [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.pce.pceservice;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.LinkedList;
import org.onosproject.incubator.net.resource.label.DefaultLabelResource;
import org.onosproject.incubator.net.resource.label.LabelResource;
import org.onosproject.incubator.net.resource.label.LabelResourceId;
import org.onosproject.incubator.net.resource.label.LabelResourceService;
import org.onosproject.incubator.net.tunnel.Tunnel;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.pce.pcestore.api.PceStore;
import org.onosproject.pce.pcestore.api.LspLocalLabelInfo;
import org.onosproject.pce.pcestore.PceccTunnelInfo;
import org.onosproject.pce.pcestore.DefaultLspLocalLabelInfo;
import org.onosproject.net.Link;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
/**
* Basic PCECC handler.
* In Basic PCECC, after path computation will configure IN and OUT label to nodes.
* [X]OUT---link----IN[Y]OUT---link-----IN[Z] where X, Y and Z are nodes.
* For generating labels, will go thorough links in the path from Egress to Ingress.
* In each link, will take label from destination node local pool as IN label,
* and assign this label as OUT label to source node.
*/
public final class BasicPceccHandler {
private static final Logger log = LoggerFactory.getLogger(BasicPceccHandler.class);
private static final String LABEL_RESOURCE_SERVICE_NULL = "Label Resource Service cannot be null";
private static final String PCE_STORE_NULL = "PCE Store cannot be null";
private static BasicPceccHandler crHandlerInstance = null;
private LabelResourceService labelRsrcService;
private PceStore pceStore;
/**
* Initializes default values.
*/
private BasicPceccHandler() {
}
/**
* Returns single instance of this class.
*
* @return this class single instance
*/
public static BasicPceccHandler getInstance() {
if (crHandlerInstance == null) {
crHandlerInstance = new BasicPceccHandler();
}
return crHandlerInstance;
}
/**
* Initialization of label manager and pce store.
*
* @param labelRsrcService label resource service
* @param pceStore pce label store
*/
public void initialize(LabelResourceService labelRsrcService, PceStore pceStore) {
this.labelRsrcService = labelRsrcService;
this.pceStore = pceStore;
}
/**
* Allocates labels from local resource pool and configure these (IN and OUT) labels into devices.
*
* @param tunnel tunnel between ingress to egress
* @return success or failure
*/
public boolean allocateLabel(Tunnel tunnel) {
long applyNum = 1;
boolean isLastLabelToPush = false;
Collection<LabelResource> labelRscList;
checkNotNull(labelRsrcService, LABEL_RESOURCE_SERVICE_NULL);
checkNotNull(pceStore, PCE_STORE_NULL);
List<Link> linkList = tunnel.path().links();
if ((linkList != null) && (linkList.size() > 0)) {
// Sequence through reverse order to push local labels into devices
// Generation of labels from egress to ingress
for (ListIterator iterator = linkList.listIterator(linkList.size()); iterator.hasPrevious();) {
Link link = (Link) iterator.previous();
DeviceId dstDeviceId = link.dst().deviceId();
DeviceId srcDeviceId = link.src().deviceId();
labelRscList = labelRsrcService.applyFromDevicePool(dstDeviceId, applyNum);
if ((labelRscList != null) && (labelRscList.size() > 0)) {
// Link label value is taken from destination device local pool.
// [X]OUT---link----IN[Y]OUT---link-----IN[Z] where X, Y and Z are nodes.
// Link label value is used as OUT and IN for both ends
// (source and destination devices) of the link.
// Currently only one label is allocated to a device (destination device).
// So, no need to iterate through list
Iterator<LabelResource> labelIterator = labelRscList.iterator();
DefaultLabelResource defaultLabelResource = (DefaultLabelResource) labelIterator.next();
LabelResourceId labelId = defaultLabelResource.labelResourceId();
log.debug("Allocated local label: " + labelId.toString()
+ "to device: " + defaultLabelResource.deviceId().toString());
PortNumber dstPort = link.dst().port();
// Check whether this is last link label to push
if (!iterator.hasPrevious()) {
isLastLabelToPush = true;
}
// Push into destination device
//TODO: uncomment below lines once installLocalLabelRule() method is ready
// Destination device IN port is link.dst().port()
//installLocalLabelRule(dstDeviceId, labelId, dstPort, tunnel.tunnelId(), isLastLabelToPush,
// LabelType.IN, Objective.Operation.ADD);
// Push into source device
//TODO: uncomment below lines once installLocalLabelRule() method is ready
// Source device OUT port will be link.dst().port(). Means its remote port used to send packet.
//installLocalLabelRule(srcDeviceId, labelId, dstPort, tunnel.tunnelId(), isLastLabelToPush,
// LabelType.OUT, Objective.Operation.ADD);
// Add or update pcecc tunnel info in pce store.
updatePceccTunnelInfoInStore(srcDeviceId, dstDeviceId, labelId, dstPort,
tunnel, isLastLabelToPush);
} else {
log.error("Unable to allocate label to device id {}.", dstDeviceId.toString());
releaseLabel(tunnel);
return false;
}
}
} else {
log.error("Tunnel {} is having empty links.", tunnel.toString());
return false;
}
return true;
}
/**
* Updates list of local labels of PCECC tunnel info in pce store.
*
* @param srcDeviceId source device in a link
* @param dstDeviceId destination device in a link
* @param labelId label id of a link
* @param dstPort destination device port number of a link
* @param tunnel tunnel
* @param isLastLabelToPush indicates this is the last label to push in Basic PCECC case
*/
public void updatePceccTunnelInfoInStore(DeviceId srcDeviceId, DeviceId dstDeviceId, LabelResourceId labelId,
PortNumber dstPort, Tunnel tunnel, boolean isLastLabelToPush) {
// First try to retrieve device from store and update its label id if it is exists,
// otherwise add it
boolean dstDeviceUpdated = false;
boolean srcDeviceUpdated = false;
PceccTunnelInfo pceccTunnelInfo = pceStore.getTunnelInfo(tunnel.tunnelId());
List<LspLocalLabelInfo> lspLabelInfoList;
if (pceccTunnelInfo != null) {
lspLabelInfoList = pceccTunnelInfo.lspLocalLabelInfoList();
if ((lspLabelInfoList != null) && (lspLabelInfoList.size() > 0)) {
for (int i = 0; i < lspLabelInfoList.size(); ++i) {
LspLocalLabelInfo lspLocalLabelInfo =
(DefaultLspLocalLabelInfo) lspLabelInfoList.get(i);
LspLocalLabelInfo.Builder lspLocalLabelInfoBuilder = null;
if (dstDeviceId.equals(lspLocalLabelInfo.deviceId())) {
lspLocalLabelInfoBuilder = DefaultLspLocalLabelInfo.builder(lspLocalLabelInfo);
lspLocalLabelInfoBuilder.inLabelId(labelId);
// Destination device IN port will be link destination port
lspLocalLabelInfoBuilder.inPort(dstPort);
dstDeviceUpdated = true;
} else if (srcDeviceId.equals(lspLocalLabelInfo.deviceId())) {
lspLocalLabelInfoBuilder = DefaultLspLocalLabelInfo.builder(lspLocalLabelInfo);
lspLocalLabelInfoBuilder.outLabelId(labelId);
// Source device OUT port will be link destination (remote) port
lspLocalLabelInfoBuilder.outPort(dstPort);
srcDeviceUpdated = true;
}
// Update
if ((lspLocalLabelInfoBuilder != null) && (dstDeviceUpdated || srcDeviceUpdated)) {
lspLabelInfoList.set(i, lspLocalLabelInfoBuilder.build());
}
}
}
}
// If it is not found in store then add it to store
if (!dstDeviceUpdated || !srcDeviceUpdated) {
// If tunnel info itself not available then create new one, otherwise add node to list.
if (pceccTunnelInfo == null) {
pceccTunnelInfo = new PceccTunnelInfo();
lspLabelInfoList = new LinkedList<>();
} else {
lspLabelInfoList = pceccTunnelInfo.lspLocalLabelInfoList();
if (lspLabelInfoList == null) {
lspLabelInfoList = new LinkedList<>();
}
}
if (!dstDeviceUpdated) {
LspLocalLabelInfo lspLocalLabelInfo = DefaultLspLocalLabelInfo.builder()
.deviceId(dstDeviceId)
.inLabelId(labelId)
.outLabelId(null)
.inPort(dstPort) // Destination device IN port will be link destination port
.outPort(null)
.build();
lspLabelInfoList.add(lspLocalLabelInfo);
}
if (!srcDeviceUpdated) {
LspLocalLabelInfo lspLocalLabelInfo = DefaultLspLocalLabelInfo.builder()
.deviceId(srcDeviceId)
.inLabelId(null)
.outLabelId(labelId)
.inPort(null)
.outPort(dstPort) // Source device OUT port will be link destination (remote) port
.build();
lspLabelInfoList.add(lspLocalLabelInfo);
}
pceccTunnelInfo.lspLocalLabelInfoList(lspLabelInfoList);
pceStore.addTunnelInfo(tunnel.tunnelId(), pceccTunnelInfo);
}
}
/**
* Deallocates unused labels to device pools.
*
* @param tunnel tunnel between ingress to egress
*/
public void releaseLabel(Tunnel tunnel) {
boolean isLastLabelToPush = false;
checkNotNull(labelRsrcService, LABEL_RESOURCE_SERVICE_NULL);
checkNotNull(pceStore, PCE_STORE_NULL);
Multimap<DeviceId, LabelResource> release = ArrayListMultimap.create();
PceccTunnelInfo pceccTunnelInfo = pceStore.getTunnelInfo(tunnel.tunnelId());
if (pceccTunnelInfo != null) {
List<LspLocalLabelInfo> lspLocalLabelInfoList = pceccTunnelInfo.lspLocalLabelInfoList();
if ((lspLocalLabelInfoList != null) && (lspLocalLabelInfoList.size() > 0)) {
for (Iterator<LspLocalLabelInfo> iterator = lspLocalLabelInfoList.iterator(); iterator.hasNext();) {
LspLocalLabelInfo lspLocalLabelInfo = (DefaultLspLocalLabelInfo) iterator.next();
DeviceId deviceId = lspLocalLabelInfo.deviceId();
LabelResourceId inLabelId = lspLocalLabelInfo.inLabelId();
LabelResourceId outLabelId = lspLocalLabelInfo.outLabelId();
PortNumber inPort = lspLocalLabelInfo.inPort();
PortNumber outPort = lspLocalLabelInfo.outPort();
// Check whether this is last link label to push
if (!iterator.hasNext()) {
isLastLabelToPush = true;
}
// Push into device
if ((inLabelId != null) && (inPort != null)) {
//TODO: uncomment below lines once installLocalLabelRule() method is ready
//installLocalLabelRule(deviceId, inLabelId, inPort, tunnelId, isLastLabelToPush,
// LabelType.IN, Objective.Operation.REMOVE);
}
if ((outLabelId != null) && (outPort != null)) {
//TODO: uncomment below lines once installLocalLabelRule() method is ready
//installLocalLabelRule(deviceId, outLabelId, outPort, tunnelId, isLastLabelToPush,
// LabelType.OUT, Objective.Operation.REMOVE);
}
// List is stored from egress to ingress. So, using IN label id to release.
// Only one local label is assigned to device (destination node)
// and that is used as OUT label for source node.
// No need to release label for last node in the list from pool because label was not allocated to
// ingress node (source node).
if ((iterator.hasNext()) && (inLabelId != null)) {
LabelResource labelRsc = new DefaultLabelResource(deviceId, inLabelId);
release.put(deviceId, labelRsc);
}
}
}
// Release from label pool
if (!release.isEmpty()) {
labelRsrcService.releaseToDevicePool(release);
}
// Remove tunnel info only if tunnel consumer id is not saved.
// If tunnel consumer id is saved, this tunnel info will be removed during releasing bandwidth.
if (pceccTunnelInfo.tunnelConsumerId() == null) {
pceStore.removeTunnelInfo(tunnel.tunnelId());
}
} else {
log.error("Unable to find PCECC tunnel info in store for a tunnel {}.", tunnel.toString());
}
}
}