blob: 2ddc819f91b3b2af9e569743c94a6185768cb8b4 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Foundation
*
* 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.pcep.server.impl;
import java.util.Map;
import java.util.TreeMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Collection;
import java.util.Collections;
import java.util.ListIterator;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onosproject.incubator.net.resource.label.LabelResourceAdminService;
import org.onosproject.incubator.net.resource.label.LabelResourceId;
import org.onosproject.incubator.net.resource.label.LabelResourceService;
import org.onosproject.incubator.net.tunnel.DefaultLabelStack;
import org.onosproject.incubator.net.tunnel.DefaultTunnel;
import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
import org.onosproject.incubator.net.tunnel.LabelStack;
import org.onosproject.incubator.net.tunnel.Tunnel;
import org.onosproject.incubator.net.tunnel.TunnelService;
import org.onosproject.incubator.net.tunnel.Tunnel.State;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultAnnotations.Builder;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.Path;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.net.link.LinkService;
import org.onosproject.pcelabelstore.PcepLabelOp;
import org.onosproject.pcelabelstore.api.PceLabelStore;
import org.onosproject.pcep.api.DeviceCapability;
import org.onosproject.pcep.server.LspKey;
import org.onosproject.pcep.server.LspType;
import org.onosproject.pcep.server.PccId;
import org.onosproject.pcep.server.PcepClient;
import org.onosproject.pcep.server.PcepClientController;
import org.onosproject.pcep.server.PcepClientListener;
import org.onosproject.pcep.server.PcepEventListener;
import org.onosproject.pcep.server.PcepLspStatus;
import org.onosproject.pcep.server.PcepNodeListener;
import org.onosproject.pcep.server.SrpIdGenerators;
import org.onosproject.pcep.server.driver.PcepAgent;
import org.onosproject.pcepio.exceptions.PcepParseException;
import org.onosproject.pcepio.protocol.PcInitiatedLspRequest;
import org.onosproject.pcepio.protocol.PcepError;
import org.onosproject.pcepio.protocol.PcepErrorInfo;
import org.onosproject.pcepio.protocol.PcepErrorMsg;
import org.onosproject.pcepio.protocol.PcepErrorObject;
import org.onosproject.pcepio.protocol.PcepFactory;
import org.onosproject.pcepio.protocol.PcepInitiateMsg;
import org.onosproject.pcepio.protocol.PcepLspObject;
import org.onosproject.pcepio.protocol.PcepMessage;
import org.onosproject.pcepio.protocol.PcepNai;
import org.onosproject.pcepio.protocol.PcepReportMsg;
import org.onosproject.pcepio.protocol.PcepSrpObject;
import org.onosproject.pcepio.protocol.PcepStateReport;
import org.onosproject.pcepio.types.PathSetupTypeTlv;
import org.onosproject.pcepio.types.PcepNaiIpv4Adjacency;
import org.onosproject.pcepio.types.PcepNaiIpv4NodeId;
import org.onosproject.pcepio.types.PcepValueType;
import org.onosproject.pcepio.types.SrEroSubObject;
import org.onosproject.pcepio.types.StatefulIPv4LspIdentifiersTlv;
import org.onosproject.pcepio.types.SymbolicPathNameTlv;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.pcep.server.PcepSyncStatus.IN_SYNC;
import static org.onosproject.pcep.server.LspType.WITHOUT_SIGNALLING_AND_WITHOUT_SR;
import static org.onosproject.pcep.server.LspType.WITH_SIGNALLING;
import static org.onosproject.pcep.server.PcepLspSyncAction.REMOVE;
import static org.onosproject.pcep.server.PcepLspSyncAction.SEND_UPDATE;
import static org.onosproject.pcep.server.PcepLspSyncAction.UNSTABLE;
import static org.onosproject.pcepio.types.PcepErrorDetailInfo.ERROR_TYPE_19;
import static org.onosproject.pcepio.types.PcepErrorDetailInfo.ERROR_VALUE_5;
import static org.onosproject.pcep.server.PcepAnnotationKeys.BANDWIDTH;
import static org.onosproject.pcep.server.PcepAnnotationKeys.LOCAL_LSP_ID;
import static org.onosproject.pcep.server.PcepAnnotationKeys.LSP_SIG_TYPE;
import static org.onosproject.pcep.server.PcepAnnotationKeys.PCC_TUNNEL_ID;
import static org.onosproject.pcep.server.PcepAnnotationKeys.PCE_INIT;
import static org.onosproject.pcep.server.PcepAnnotationKeys.PLSP_ID;
import static org.onosproject.pcep.server.PcepAnnotationKeys.DELEGATE;
import static org.onosproject.pcep.server.PcepAnnotationKeys.COST_TYPE;
import static org.onosproject.pcep.server.PcepSyncStatus.SYNCED;
import static org.onosproject.pcep.server.PcepSyncStatus.NOT_SYNCED;
/**
* Implementation of PCEP client controller.
*/
@Component(immediate = true)
@Service
public class PcepClientControllerImpl implements PcepClientController {
private static final Logger log = LoggerFactory.getLogger(PcepClientControllerImpl.class);
private static final long IDENTIFIER_SET = 0x100000000L;
private static final long SET = 0xFFFFFFFFL;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TunnelService tunnelService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService netCfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LabelResourceAdminService labelRsrcAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LabelResourceService labelRsrcService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PceLabelStore pceStore;
protected ConcurrentHashMap<PccId, PcepClient> connectedClients =
new ConcurrentHashMap<>();
protected PcepClientAgent agent = new PcepClientAgent();
protected Set<PcepClientListener> pcepClientListener = new HashSet<>();
protected Set<PcepEventListener> pcepEventListener = Sets.newHashSet();
protected Set<PcepNodeListener> pcepNodeListener = Sets.newHashSet();
// LSR-id and device-id mapping for checking capability if L3 device is not
// having its capability
private Map<String, DeviceId> lsrIdDeviceIdMap = new HashMap<>();
private final Controller ctrl = new Controller();
public static final long GLOBAL_LABEL_SPACE_MIN = 4097;
public static final long GLOBAL_LABEL_SPACE_MAX = 5121;
private static final String LSRID = "lsrId";
private static final String DEVICE_NULL = "Device-cannot be null";
private static final String LINK_NULL = "Link-cannot be null";
private BasicPceccHandler crHandler;
private PceccSrTeBeHandler srTeHandler;
private DeviceListener deviceListener = new InternalDeviceListener();
private LinkListener linkListener = new InternalLinkListener();
private InternalConfigListener cfgListener = new InternalConfigListener();
private Map<Integer, Integer> pcepErrorMsg = new TreeMap<>();
@Activate
public void activate() {
ctrl.start(agent);
crHandler = BasicPceccHandler.getInstance();
crHandler.initialize(labelRsrcService, deviceService, pceStore, this);
srTeHandler = PceccSrTeBeHandler.getInstance();
srTeHandler.initialize(labelRsrcAdminService, labelRsrcService, this, pceStore,
deviceService);
deviceService.addListener(deviceListener);
linkService.addListener(linkListener);
netCfgService.addListener(cfgListener);
// Reserve global node pool
if (!srTeHandler.reserveGlobalPool(GLOBAL_LABEL_SPACE_MIN, GLOBAL_LABEL_SPACE_MAX)) {
log.debug("Global node pool was already reserved.");
}
log.info("Started");
}
@Deactivate
public void deactivate() {
// Close all connected clients
closeConnectedClients();
deviceService.removeListener(deviceListener);
linkService.removeListener(linkListener);
netCfgService.removeListener(cfgListener);
ctrl.stop();
log.info("Stopped");
}
@Override
public void peerErrorMsg(String peerId, Integer errorType, Integer errValue) {
if (peerId == null) {
pcepErrorMsg.put(errorType, errValue);
} else {
if (pcepErrorMsg.size() > 10) {
pcepErrorMsg.clear();
}
pcepErrorMsg.put(errorType, errValue);
}
}
@Override
public Map<String, List<String>> getPcepExceptions() {
return this.ctrl.exceptionsMap();
}
@Override
public Map<Integer, Integer> getPcepErrorMsg() {
return pcepErrorMsg;
}
@Override
public Map<String, String> getPcepSessionMap() {
return this.ctrl.mapPeer();
}
@Override
public Map<String, Byte> getPcepSessionIdMap() {
return this.ctrl.mapSession();
}
@Override
public Collection<PcepClient> getClients() {
return connectedClients.values();
}
@Override
public PcepClient getClient(PccId pccId) {
return connectedClients.get(pccId);
}
@Override
public void addListener(PcepClientListener listener) {
if (!pcepClientListener.contains(listener)) {
this.pcepClientListener.add(listener);
}
}
@Override
public void removeListener(PcepClientListener listener) {
this.pcepClientListener.remove(listener);
}
@Override
public void addEventListener(PcepEventListener listener) {
pcepEventListener.add(listener);
}
@Override
public void removeEventListener(PcepEventListener listener) {
pcepEventListener.remove(listener);
}
@Override
public void writeMessage(PccId pccId, PcepMessage msg) {
this.getClient(pccId).sendMessage(msg);
}
@Override
public void addNodeListener(PcepNodeListener listener) {
pcepNodeListener.add(listener);
}
@Override
public void removeNodeListener(PcepNodeListener listener) {
pcepNodeListener.remove(listener);
}
@Override
public void processClientMessage(PccId pccId, PcepMessage msg) {
PcepClient pc = getClient(pccId);
switch (msg.getType()) {
case NONE:
break;
case OPEN:
break;
case KEEP_ALIVE:
break;
case PATH_COMPUTATION_REQUEST:
break;
case PATH_COMPUTATION_REPLY:
break;
case NOTIFICATION:
break;
case ERROR:
break;
case INITIATE:
if (!pc.capability().pcInstantiationCapability()) {
pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(), ERROR_TYPE_19,
ERROR_VALUE_5)));
}
break;
case UPDATE:
if (!pc.capability().statefulPceCapability()) {
pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(), ERROR_TYPE_19,
ERROR_VALUE_5)));
}
break;
case LABEL_UPDATE:
if (!pc.capability().pceccCapability()) {
pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(), ERROR_TYPE_19,
ERROR_VALUE_5)));
}
break;
case CLOSE:
log.info("Sending Close Message to {" + pccId.toString() + "}");
pc.sendMessage(Collections.singletonList(pc.factory().buildCloseMsg().build()));
//now disconnect client
pc.disconnectClient();
break;
case REPORT:
//Only update the listener if respective capability is supported else send PCEP-ERR msg
if (pc.capability().statefulPceCapability()) {
ListIterator<PcepStateReport> listIterator = ((PcepReportMsg) msg).getStateReportList().listIterator();
while (listIterator.hasNext()) {
PcepStateReport stateRpt = listIterator.next();
PcepLspObject lspObj = stateRpt.getLspObject();
if (lspObj.getSFlag()) {
if (pc.lspDbSyncStatus() != IN_SYNC) {
log.debug("LSP DB sync started for PCC {}", pc.getPccId().id().toString());
// Initialize LSP DB sync and temporary cache.
pc.setLspDbSyncStatus(IN_SYNC);
pc.initializeSyncMsgList(pccId);
}
// Store stateRpt in temporary cache.
pc.addSyncMsgToList(pccId, stateRpt);
// Don't send to provider as of now.
continue;
} else if (lspObj.getPlspId() == 0) {
if (pc.lspDbSyncStatus() == IN_SYNC
|| pc.lspDbSyncStatus() == NOT_SYNCED) {
// Set end of LSPDB sync.
log.debug("LSP DB sync completed for PCC {}", pc.getPccId().id().toString());
pc.setLspDbSyncStatus(SYNCED);
// Call packet provider to initiate label DB sync (only if PCECC capable).
if (pc.capability().pceccCapability()) {
log.debug("Trigger label DB sync for PCC {}", pc.getPccId().id().toString());
pc.setLabelDbSyncStatus(IN_SYNC);
// Get lsrId of the PCEP client from the PCC ID. Session info is based on lsrID.
String lsrId = String.valueOf(pccId.ipAddress());
DeviceId pccDeviceId = DeviceId.deviceId(lsrId);
try {
syncLabelDb(pccDeviceId);
pc.setLabelDbSyncStatus(SYNCED);
} catch (PcepParseException e) {
log.error("Exception caught in sending label masg to PCC while in sync.");
}
} else {
// If label db sync is not to be done, handle end of LSPDB sync actions.
agent.analyzeSyncMsgList(pccId);
}
continue;
}
}
PcepLspStatus pcepLspStatus = PcepLspStatus.values()[lspObj.getOFlag()];
LspType lspType = getLspType(stateRpt.getSrpObject());
// Download (or remove) labels for basic PCECC LSPs.
if (lspType.equals(WITHOUT_SIGNALLING_AND_WITHOUT_SR)) {
boolean isRemove = lspObj.getRFlag();
Tunnel tunnel = null;
if (isRemove || pcepLspStatus.equals(PcepLspStatus.GOING_UP)) {
tunnel = getTunnel(lspObj);
}
if (tunnel != null) {
if (isRemove) {
crHandler.releaseLabel(tunnel);
} else {
crHandler.allocateLabel(tunnel);
}
}
}
// It's a usual report message while sync is not undergoing. So process it immediately.
LinkedList<PcepStateReport> llPcRptList = new LinkedList<>();
llPcRptList.add(stateRpt);
PcepMessage pcReportMsg = pc.factory().buildReportMsg().setStateReportList((llPcRptList))
.build();
for (PcepEventListener l : pcepEventListener) {
l.handleMessage(pccId, pcReportMsg);
}
}
} else {
// Send PCEP-ERROR message.
pc.sendMessage(Collections.singletonList(getErrMsg(pc.factory(),
ERROR_TYPE_19, ERROR_VALUE_5)));
}
break;
case LABEL_RANGE_RESERV:
break;
case LS_REPORT: //TODO: need to handle LS report to add or remove node
break;
case MAX:
break;
case END:
break;
default:
break;
}
}
private LspType getLspType(PcepSrpObject srpObj) {
LspType lspType = WITH_SIGNALLING;
if (null != srpObj) {
LinkedList<PcepValueType> llOptionalTlv = srpObj.getOptionalTlv();
ListIterator<PcepValueType> listIterator = llOptionalTlv.listIterator();
while (listIterator.hasNext()) {
PcepValueType tlv = listIterator.next();
switch (tlv.getType()) {
case PathSetupTypeTlv.TYPE:
lspType = LspType.values()[Integer.valueOf(((PathSetupTypeTlv) tlv).getPst())];
break;
default:
break;
}
}
}
return lspType;
}
private Tunnel getTunnel(PcepLspObject lspObj) {
ListIterator<PcepValueType> listTlvIterator = lspObj.getOptionalTlv().listIterator();
StatefulIPv4LspIdentifiersTlv ipv4LspIdenTlv = null;
SymbolicPathNameTlv pathNameTlv = null;
Tunnel tunnel = null;
while (listTlvIterator.hasNext()) {
PcepValueType tlv = listTlvIterator.next();
switch (tlv.getType()) {
case StatefulIPv4LspIdentifiersTlv.TYPE:
ipv4LspIdenTlv = (StatefulIPv4LspIdentifiersTlv) tlv;
break;
case SymbolicPathNameTlv.TYPE:
pathNameTlv = (SymbolicPathNameTlv) tlv;
break;
default:
break;
}
}
/*
* Draft says: The LSP-IDENTIFIERS TLV MUST be included in the LSP object in PCRpt messages for
* RSVP-signaled LSPs. For ONOS PCECC implementation, it is mandatory.
*/
if (ipv4LspIdenTlv == null) {
log.error("Stateful IPv4 identifier TLV is null in PCRpt msg.");
return null;
}
IpTunnelEndPoint tunnelEndPointSrc = IpTunnelEndPoint
.ipTunnelPoint(IpAddress.valueOf(ipv4LspIdenTlv.getIpv4IngressAddress()));
IpTunnelEndPoint tunnelEndPointDst = IpTunnelEndPoint
.ipTunnelPoint(IpAddress.valueOf(ipv4LspIdenTlv.getIpv4EgressAddress()));
Collection<Tunnel> tunnelQueryResult = tunnelService.queryTunnel(tunnelEndPointSrc, tunnelEndPointDst);
for (Tunnel tunnelObj : tunnelQueryResult) {
if (tunnelObj.annotations().value(PLSP_ID) == null) {
/*
* PLSP_ID is null while Tunnel is created at PCE and PCInit msg carries it as 0. It is allocated by
* PCC and in that case it becomes the first PCRpt msg from PCC for this LSP, and hence symbolic
* path name must be carried in the PCRpt msg. Draft says: The SYMBOLIC-PATH-NAME TLV "MUST" be
* included in the LSP object in the LSP State Report (PCRpt) message when during a given PCEP
* session an LSP is "first" reported to a PCE.
*/
if ((pathNameTlv != null)
&& Arrays.equals(tunnelObj.tunnelName().value().getBytes(), pathNameTlv.getValue())) {
tunnel = tunnelObj;
break;
}
continue;
}
if ((Integer.valueOf(tunnelObj.annotations().value(PLSP_ID)) == lspObj.getPlspId())) {
if ((Integer
.valueOf(tunnelObj.annotations().value(LOCAL_LSP_ID)) == ipv4LspIdenTlv.getLspId())) {
tunnel = tunnelObj;
break;
}
}
}
if (tunnel == null || tunnel.annotations().value(PLSP_ID) != null) {
return tunnel;
}
// The returned tunnel is used just for filling values in Label message. So manipulate locally
// and return so that to allocate label, we don't need to wait for the tunnel in the "core"
// to be updated, as that depends on listener mechanism and there may be timing/multi-threading issues.
Builder annotationBuilder = DefaultAnnotations.builder();
annotationBuilder.set(BANDWIDTH, tunnel.annotations().value(BANDWIDTH));
annotationBuilder.set(COST_TYPE, tunnel.annotations().value(COST_TYPE));
annotationBuilder.set(LSP_SIG_TYPE, tunnel.annotations().value(LSP_SIG_TYPE));
annotationBuilder.set(PCE_INIT, tunnel.annotations().value(PCE_INIT));
annotationBuilder.set(DELEGATE, tunnel.annotations().value(DELEGATE));
annotationBuilder.set(PLSP_ID, String.valueOf(lspObj.getPlspId()));
annotationBuilder.set(PCC_TUNNEL_ID, String.valueOf(ipv4LspIdenTlv.getTunnelId()));
annotationBuilder.set(LOCAL_LSP_ID, tunnel.annotations().value(LOCAL_LSP_ID));
Tunnel updatedTunnel = new DefaultTunnel(tunnel.providerId(), tunnel.src(),
tunnel.dst(), tunnel.type(),
tunnel.state(), tunnel.groupId(),
tunnel.tunnelId(),
tunnel.tunnelName(),
tunnel.path(),
tunnel.resource(),
annotationBuilder.build());
return updatedTunnel;
}
@Override
public void closeConnectedClients() {
PcepClient pc;
for (PccId id : connectedClients.keySet()) {
pc = getClient(id);
pc.disconnectClient();
}
}
/**
* Returns pcep error message with specific error type and value.
*
* @param factory represents pcep factory
* @param errorType pcep error type
* @param errorValue pcep error value
* @return pcep error message
*/
public PcepErrorMsg getErrMsg(PcepFactory factory, byte errorType, byte errorValue) {
LinkedList<PcepError> llPcepErr = new LinkedList<>();
LinkedList<PcepErrorObject> llerrObj = new LinkedList<>();
PcepErrorMsg errMsg;
PcepErrorObject errObj = factory.buildPcepErrorObject().setErrorValue(errorValue).setErrorType(errorType)
.build();
llerrObj.add(errObj);
PcepError pcepErr = factory.buildPcepError().setErrorObjList(llerrObj).build();
llPcepErr.add(pcepErr);
PcepErrorInfo errInfo = factory.buildPcepErrorInfo().setPcepErrorList(llPcepErr).build();
errMsg = factory.buildPcepErrorMsg().setPcepErrorInfo(errInfo).build();
return errMsg;
}
private boolean syncLabelDb(DeviceId deviceId) throws PcepParseException {
checkNotNull(deviceId);
DeviceId actualDevcieId = pceStore.getLsrIdDevice(deviceId.toString());
if (actualDevcieId == null) {
log.error("Device not available {}.", deviceId.toString());
pceStore.addPccLsr(deviceId);
return false;
}
PcepClient pc = connectedClients.get(PccId.pccId(IpAddress.valueOf(deviceId.toString())));
Device specificDevice = deviceService.getDevice(actualDevcieId);
if (specificDevice == null) {
log.error("Unable to find device for specific device id {}.", actualDevcieId.toString());
return false;
}
if (pceStore.getGlobalNodeLabel(actualDevcieId) != null) {
Map<DeviceId, LabelResourceId> globalNodeLabelMap = pceStore.getGlobalNodeLabels();
for (Entry<DeviceId, LabelResourceId> entry : globalNodeLabelMap.entrySet()) {
// Convert from DeviceId to TunnelEndPoint
Device srcDevice = deviceService.getDevice(entry.getKey());
/*
* If there is a slight difference in timing such that if device subsystem has removed the device but
* PCE store still has it, just ignore such devices.
*/
if (srcDevice == null) {
continue;
}
String srcLsrId = srcDevice.annotations().value(LSRID);
if (srcLsrId == null) {
continue;
}
srTeHandler.pushGlobalNodeLabel(pc, entry.getValue(),
IpAddress.valueOf(srcLsrId).getIp4Address().toInt(),
PcepLabelOp.ADD, false);
}
Map<Link, LabelResourceId> adjLabelMap = pceStore.getAdjLabels();
for (Entry<Link, LabelResourceId> entry : adjLabelMap.entrySet()) {
if (entry.getKey().src().deviceId().equals(actualDevcieId)) {
srTeHandler.pushAdjacencyLabel(pc,
entry.getValue(),
(int) entry.getKey().src().port().toLong(),
(int) entry.getKey().dst().port().toLong(),
PcepLabelOp.ADD
);
}
}
}
srTeHandler.pushGlobalNodeLabel(pc, LabelResourceId.labelResourceId(0),
0, PcepLabelOp.ADD, true);
log.debug("End of label DB sync for device {}", actualDevcieId);
if (mastershipService.getLocalRole(specificDevice.id()) == MastershipRole.MASTER) {
// Allocate node-label to this specific device.
allocateNodeLabel(specificDevice);
// Allocate adjacency label
Set<Link> links = linkService.getDeviceEgressLinks(specificDevice.id());
if (links != null) {
for (Link link : links) {
allocateAdjacencyLabel(link);
}
}
}
return true;
}
/**
* Allocates node label to specific device.
*
* @param specificDevice device to which node label needs to be allocated
*/
public void allocateNodeLabel(Device specificDevice) {
checkNotNull(specificDevice, DEVICE_NULL);
DeviceId deviceId = specificDevice.id();
// Retrieve lsrId of a specific device
if (specificDevice.annotations() == null) {
log.debug("Device {} does not have annotations.", specificDevice.toString());
return;
}
String lsrId = specificDevice.annotations().value(LSRID);
if (lsrId == null) {
log.debug("Unable to retrieve lsr-id of a device {}.", specificDevice.toString());
return;
}
// Get capability config from netconfig
DeviceCapability cfg = netCfgService.getConfig(DeviceId.deviceId(lsrId), DeviceCapability.class);
if (cfg == null) {
log.error("Unable to find corresponding capability for a lsrd {} from NetConfig.", lsrId);
// Save info. When PCEP session is comes up then allocate node-label
lsrIdDeviceIdMap.put(lsrId, specificDevice.id());
return;
}
// Check whether device has SR-TE Capability
if (cfg.labelStackCap()) {
srTeHandler.allocateNodeLabel(deviceId, lsrId);
}
}
/**
* Releases node label of a specific device.
*
* @param specificDevice this device label and lsr-id information will be
* released in other existing devices
*/
public void releaseNodeLabel(Device specificDevice) {
checkNotNull(specificDevice, DEVICE_NULL);
DeviceId deviceId = specificDevice.id();
// Retrieve lsrId of a specific device
if (specificDevice.annotations() == null) {
log.debug("Device {} does not have annotations.", specificDevice.toString());
return;
}
String lsrId = specificDevice.annotations().value(LSRID);
if (lsrId == null) {
log.debug("Unable to retrieve lsr-id of a device {}.", specificDevice.toString());
return;
}
// Get capability config from netconfig
DeviceCapability cfg = netCfgService.getConfig(DeviceId.deviceId(lsrId), DeviceCapability.class);
if (cfg == null) {
log.error("Unable to find corresponding capabilty for a lsrd {} from NetConfig.", lsrId);
return;
}
// Check whether device has SR-TE Capability
if (cfg.labelStackCap()) {
if (!srTeHandler.releaseNodeLabel(deviceId, lsrId)) {
log.error("Unable to release node label for a device id {}.", deviceId.toString());
}
}
}
/**
* Allocates adjacency label for a link.
*
* @param link link
*/
public void allocateAdjacencyLabel(Link link) {
checkNotNull(link, LINK_NULL);
Device specificDevice = deviceService.getDevice(link.src().deviceId());
// Retrieve lsrId of a specific device
if (specificDevice.annotations() == null) {
log.debug("Device {} does not have annotations.", specificDevice.toString());
return;
}
String lsrId = specificDevice.annotations().value(LSRID);
if (lsrId == null) {
log.debug("Unable to retrieve lsr-id of a device {}.", specificDevice.toString());
return;
}
// Get capability config from netconfig
DeviceCapability cfg = netCfgService.getConfig(DeviceId.deviceId(lsrId), DeviceCapability.class);
if (cfg == null) {
log.error("Unable to find corresponding capabilty for a lsrd {} from NetConfig.", lsrId);
// Save info. When PCEP session comes up then allocate adjacency
// label
if (lsrIdDeviceIdMap.get(lsrId) != null) {
lsrIdDeviceIdMap.put(lsrId, specificDevice.id());
}
return;
}
// Check whether device has SR-TE Capability
if (cfg.labelStackCap()) {
srTeHandler.allocateAdjacencyLabel(link);
}
}
/**
* Releases allocated adjacency label of a link.
*
* @param link link
*/
public void releaseAdjacencyLabel(Link link) {
checkNotNull(link, LINK_NULL);
Device specificDevice = deviceService.getDevice(link.src().deviceId());
// Retrieve lsrId of a specific device
if (specificDevice.annotations() == null) {
log.debug("Device {} does not have annotations.", specificDevice.toString());
return;
}
String lsrId = specificDevice.annotations().value(LSRID);
if (lsrId == null) {
log.debug("Unable to retrieve lsr-id of a device {}.", specificDevice.toString());
return;
}
// Get capability config from netconfig
DeviceCapability cfg = netCfgService.getConfig(DeviceId.deviceId(lsrId), DeviceCapability.class);
if (cfg == null) {
log.error("Unable to find corresponding capabilty for a lsrd {} from NetConfig.", lsrId);
return;
}
// Check whether device has SR-TE Capability
if (cfg.labelStackCap()) {
if (!srTeHandler.releaseAdjacencyLabel(link)) {
log.error("Unable to release adjacency labels for a link {}.", link.toString());
}
}
}
@Override
public LabelStack computeLabelStack(Path path) {
return srTeHandler.computeLabelStack(path);
}
@Override
public boolean allocateLocalLabel(Tunnel tunnel) {
return crHandler.allocateLabel(tunnel);
}
/**
* Creates label stack for ERO object from network resource.
*
* @param labelStack label stack
* @param path (hop list)
* @return list of ERO subobjects
*/
@Override
public LinkedList<PcepValueType> createPcepLabelStack(DefaultLabelStack labelStack, Path path) {
checkNotNull(labelStack);
LinkedList<PcepValueType> llSubObjects = new LinkedList<PcepValueType>();
Iterator<Link> links = path.links().iterator();
LabelResourceId label = null;
Link link = null;
PcepValueType subObj = null;
PcepNai nai = null;
Device dstNode = null;
long srcPortNo, dstPortNo;
ListIterator<LabelResourceId> labelListIterator = labelStack.labelResources().listIterator();
while (labelListIterator.hasNext()) {
label = labelListIterator.next();
link = links.next();
srcPortNo = link.src().port().toLong();
srcPortNo = ((srcPortNo & IDENTIFIER_SET) == IDENTIFIER_SET) ? srcPortNo & SET : srcPortNo;
dstPortNo = link.dst().port().toLong();
dstPortNo = ((dstPortNo & IDENTIFIER_SET) == IDENTIFIER_SET) ? dstPortNo & SET : dstPortNo;
nai = new PcepNaiIpv4Adjacency((int) srcPortNo, (int) dstPortNo);
subObj = new SrEroSubObject(PcepNaiIpv4Adjacency.ST_TYPE, false, false, false, true, (int) label.labelId(),
nai);
llSubObjects.add(subObj);
dstNode = deviceService.getDevice(link.dst().deviceId());
nai = new PcepNaiIpv4NodeId(Ip4Address.valueOf(dstNode.annotations().value(LSRID)).toInt());
if (!labelListIterator.hasNext()) {
log.error("Malformed label stack.");
}
label = labelListIterator.next();
subObj = new SrEroSubObject(PcepNaiIpv4NodeId.ST_TYPE, false, false, false, true, (int) label.labelId(),
nai);
llSubObjects.add(subObj);
}
return llSubObjects;
}
/**
* Implementation of an Pcep Agent which is responsible for
* keeping track of connected clients and the state in which
* they are.
*/
public class PcepClientAgent implements PcepAgent {
private final Logger log = LoggerFactory.getLogger(PcepClientAgent.class);
@Override
public boolean addConnectedClient(PccId pccId, PcepClient pc) {
if (connectedClients.get(pccId) != null) {
log.error("Trying to add connectedClient but found a previous "
+ "value for pcc ip: {}", pccId.toString());
return false;
} else {
log.debug("Added Client {}", pccId.toString());
connectedClients.put(pccId, pc);
for (PcepClientListener l : pcepClientListener) {
l.clientConnected(pccId);
}
return true;
}
}
@Override
public boolean validActivation(PccId pccId) {
if (connectedClients.get(pccId) == null) {
log.error("Trying to activate client but is not in "
+ "connected client: pccIp {}. Aborting ..", pccId.toString());
return false;
}
return true;
}
@Override
public void removeConnectedClient(PccId pccId) {
connectedClients.remove(pccId);
for (PcepClientListener l : pcepClientListener) {
log.warn("Removal for {}", pccId.toString());
l.clientDisconnected(pccId);
}
}
@Override
public void processPcepMessage(PccId pccId, PcepMessage m) {
processClientMessage(pccId, m);
}
@Override
public void addNode(PcepClient pc) {
for (PcepNodeListener l : pcepNodeListener) {
l.addDevicePcepConfig(pc);
}
}
@Override
public void deleteNode(PccId pccId) {
for (PcepNodeListener l : pcepNodeListener) {
l.deleteDevicePcepConfig(pccId);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public boolean analyzeSyncMsgList(PccId pccId) {
PcepClient pc = getClient(pccId);
/*
* PLSP_ID is null while tunnel is created at PCE and PCInit msg carries it as 0. It is allocated by PCC and
* in that case it becomes the first PCRpt msg from PCC for this LSP, and hence symbolic path name must be
* carried in the PCRpt msg. Draft says: The SYMBOLIC-PATH-NAME TLV "MUST" be included in the LSP object in
* the LSP State Report (PCRpt) message when during a given PCEP session an LSP is "first" reported to a
* PCE. So two separate lists with separate keys are maintained.
*/
Map<LspKey, Tunnel> preSyncLspDbByKey = new HashMap<>();
Map<String, Tunnel> preSyncLspDbByName = new HashMap<>();
// Query tunnel service and fetch all the tunnels with this PCC as ingress.
// Organize into two maps, with LSP key if known otherwise with symbolic path name, for quick search.
Collection<Tunnel> queriedTunnels = tunnelService.queryTunnel(Tunnel.Type.MPLS);
for (Tunnel tunnel : queriedTunnels) {
if (((IpTunnelEndPoint) tunnel.src()).ip().equals(pccId.ipAddress())) {
String pLspId = tunnel.annotations().value(PLSP_ID);
if (pLspId != null) {
String localLspId = tunnel.annotations().value(LOCAL_LSP_ID);
checkNotNull(localLspId);
LspKey lspKey = new LspKey(Integer.valueOf(pLspId), Short.valueOf(localLspId));
preSyncLspDbByKey.put(lspKey, tunnel);
} else {
preSyncLspDbByName.put(tunnel.tunnelName().value(), tunnel);
}
}
}
List<PcepStateReport> syncStateRptList = pc.getSyncMsgList(pccId);
if (syncStateRptList == null) {
// When there are no LSPs to sync, directly end-of-sync PCRpt will come and the
// list will be null.
syncStateRptList = Collections.EMPTY_LIST;
log.debug("No LSPs reported from PCC during sync.");
}
Iterator<PcepStateReport> stateRptListIterator = syncStateRptList.iterator();
// For every report, fetch PLSP id, local LSP id and symbolic path name from the message.
while (stateRptListIterator.hasNext()) {
PcepStateReport stateRpt = stateRptListIterator.next();
Tunnel tunnel = null;
PcepLspObject lspObj = stateRpt.getLspObject();
ListIterator<PcepValueType> listTlvIterator = lspObj.getOptionalTlv().listIterator();
StatefulIPv4LspIdentifiersTlv ipv4LspIdenTlv = null;
SymbolicPathNameTlv pathNameTlv = null;
while (listTlvIterator.hasNext()) {
PcepValueType tlv = listTlvIterator.next();
switch (tlv.getType()) {
case StatefulIPv4LspIdentifiersTlv.TYPE:
ipv4LspIdenTlv = (StatefulIPv4LspIdentifiersTlv) tlv;
break;
case SymbolicPathNameTlv.TYPE:
pathNameTlv = (SymbolicPathNameTlv) tlv;
break;
default:
break;
}
}
LspKey lspKeyOfRpt = new LspKey(lspObj.getPlspId(), ipv4LspIdenTlv.getLspId());
tunnel = preSyncLspDbByKey.get(lspKeyOfRpt);
// PCE tunnel is matched with PCRpt LSP. Now delete it from the preSyncLspDb list as the residual
// non-matching list will be processed at the end.
if (tunnel != null) {
preSyncLspDbByKey.remove(lspKeyOfRpt);
} else if (pathNameTlv != null) {
tunnel = preSyncLspDbByName.get(Arrays.toString(pathNameTlv.getValue()));
if (tunnel != null) {
preSyncLspDbByName.remove(tunnel.tunnelName().value());
}
}
if (tunnel == null) {
// If remove flag is set, and tunnel is not known to PCE, ignore it.
if (lspObj.getCFlag() && !lspObj.getRFlag()) {
// For initiated LSP, need to send PCInit delete msg.
try {
PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(SrpIdGenerators.create())
.setRFlag(true).build();
PcInitiatedLspRequest releaseLspRequest = pc.factory().buildPcInitiatedLspRequest()
.setLspObject(lspObj).setSrpObject(srpobj).build();
LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList
= new LinkedList<PcInitiatedLspRequest>();
llPcInitiatedLspRequestList.add(releaseLspRequest);
PcepInitiateMsg pcInitiateMsg = pc.factory().buildPcepInitiateMsg()
.setPcInitiatedLspRequestList(llPcInitiatedLspRequestList).build();
pc.sendMessage(Collections.singletonList(pcInitiateMsg));
} catch (PcepParseException e) {
log.error("Exception occured while sending initiate delete message {}", e.getMessage());
}
continue;
}
}
if (!lspObj.getCFlag()) {
// For learned LSP process both add/update PCRpt.
LinkedList<PcepStateReport> llPcRptList = new LinkedList<>();
llPcRptList.add(stateRpt);
PcepMessage pcReportMsg = pc.factory().buildReportMsg().setStateReportList((llPcRptList))
.build();
for (PcepEventListener l : pcepEventListener) {
l.handleMessage(pccId, pcReportMsg);
}
continue;
}
// Implied that tunnel != null and lspObj.getCFlag() is set
// State different for PCC sent LSP and PCE known LSP, send PCUpd msg.
State tunnelState = PcepLspStatus
.getTunnelStatusFromLspStatus(PcepLspStatus.values()[lspObj.getOFlag()]);
if (tunnelState != tunnel.state()) {
for (PcepEventListener l : pcepEventListener) {
l.handleEndOfSyncAction(tunnel, SEND_UPDATE);
}
}
}
// Check which tunnels are extra at PCE that were not reported by PCC.
Map<Object, Tunnel> preSyncLspDb = (Map) preSyncLspDbByKey;
handleResidualTunnels(preSyncLspDb);
preSyncLspDbByKey = null;
preSyncLspDb = (Map) preSyncLspDbByName;
handleResidualTunnels(preSyncLspDb);
preSyncLspDbByName = null;
preSyncLspDb = null;
pc.removeSyncMsgList(pccId);
return true;
}
/*
* Go through the tunnels which are known by PCE but were not reported by PCC during LSP DB sync and take
* appropriate actions.
*/
private void handleResidualTunnels(Map<Object, Tunnel> preSyncLspDb) {
for (Tunnel pceExtraTunnel : preSyncLspDb.values()) {
if (pceExtraTunnel.annotations().value(PCE_INIT) == null
|| "false".equalsIgnoreCase(pceExtraTunnel.annotations().value(PCE_INIT))) {
// PCC initiated tunnels should be removed from tunnel store.
for (PcepEventListener l : pcepEventListener) {
l.handleEndOfSyncAction(pceExtraTunnel, REMOVE);
}
} else {
// PCE initiated tunnels should be initiated again.
for (PcepEventListener l : pcepEventListener) {
l.handleEndOfSyncAction(pceExtraTunnel, UNSTABLE);
}
}
}
}
}
/*
* Handle device events.
*/
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
Device specificDevice = event.subject();
if (specificDevice == null) {
log.error("Unable to find device from device event.");
return;
}
switch (event.type()) {
case DEVICE_ADDED:
// Node-label allocation is being done during Label DB Sync.
// So, when device is detected, no need to do node-label
// allocation.
String lsrId = specificDevice.annotations().value(LSRID);
if (lsrId != null) {
pceStore.addLsrIdDevice(lsrId, specificDevice.id());
// Search in failed DB sync store. If found, trigger label DB sync.
DeviceId pccDeviceId = DeviceId.deviceId(lsrId);
if (pceStore.hasPccLsr(pccDeviceId)) {
log.debug("Continue to perform label DB sync for device {}.", pccDeviceId.toString());
try {
syncLabelDb(pccDeviceId);
} catch (PcepParseException e) {
log.error("Exception caught in sending label masg to PCC while in sync.");
}
pceStore.removePccLsr(pccDeviceId);
}
}
break;
case DEVICE_REMOVED:
// Release node-label
if (mastershipService.getLocalRole(specificDevice.id()) == MastershipRole.MASTER) {
releaseNodeLabel(specificDevice);
}
if (specificDevice.annotations().value(LSRID) != null) {
pceStore.removeLsrIdDevice(specificDevice.annotations().value(LSRID));
}
break;
default:
break;
}
}
}
/*
* Handle link events.
*/
private class InternalLinkListener implements LinkListener {
@Override
public void event(LinkEvent event) {
Link link = event.subject();
switch (event.type()) {
case LINK_ADDED:
// Allocate adjacency label
if (mastershipService.getLocalRole(link.src().deviceId()) == MastershipRole.MASTER) {
allocateAdjacencyLabel(link);
}
break;
case LINK_REMOVED:
// Release adjacency label
if (mastershipService.getLocalRole(link.src().deviceId()) == MastershipRole.MASTER) {
releaseAdjacencyLabel(link);
}
break;
default:
break;
}
}
}
private class InternalConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED)
&& event.configClass().equals(DeviceCapability.class)) {
DeviceId deviceIdLsrId = (DeviceId) event.subject();
String lsrId = deviceIdLsrId.toString();
DeviceId deviceId = lsrIdDeviceIdMap.get(lsrId);
if (deviceId == null) {
log.debug("Unable to find device id for a lsr-id {} from lsr-id and device-id map.", lsrId);
return;
}
DeviceCapability cfg = netCfgService.getConfig(DeviceId.deviceId(lsrId), DeviceCapability.class);
if (cfg == null) {
log.error("Unable to find corresponding capabilty for a lsrd {}.", lsrId);
return;
}
if (cfg.labelStackCap()) {
if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
// Allocate node-label
srTeHandler.allocateNodeLabel(deviceId, lsrId);
// Allocate adjacency label to links which are
// originated from this specific device id
Set<Link> links = linkService.getDeviceEgressLinks(deviceId);
for (Link link : links) {
if (!srTeHandler.allocateAdjacencyLabel(link)) {
return;
}
}
}
}
// Remove lsrId info from map
lsrIdDeviceIdMap.remove(lsrId);
}
}
}
}