/*
* Copyright (c) 2014, 2017 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.sfc.provider.api;
import static org.opendaylight.sfc.provider.SfcProviderDebug.printTraceStart;
import static org.opendaylight.sfc.provider.SfcProviderDebug.printTraceStop;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffDataPlaneLocatorName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfpName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SftTypeName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePaths;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.create.rendered.path.input.ContextHeaderAllocationType1;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.create.rendered.path.input.context.header.allocation.type._1.VxlanClassifier;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHopBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathKey;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHopBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHopKey;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.state.service.function.state.SfServicePath;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChainBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.service.function.chain.SfcServiceFunction;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.service.function.chain.SfcServiceFunctionBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfg.rev150214.service.function.groups.ServiceFunctionGroup;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPathBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.ServiceFunctionType;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Nsh;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Transport;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.VxlanGpe;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
import org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.LoadBalance;
import org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.LoadPathAware;
import org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.Random;
import org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.RoundRobin;
import org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.ServiceFunctionSchedulerTypeIdentity;
import org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.ShortestPath;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class has the APIs to operate on the Service Classifier datastore.
*
* <p>
* It is normally called from onDataChanged() through a executor service. We
* need to use an executor service because we can not operate on a datastore
* while on onDataChanged() context.
*
* @author Reinaldo Penno (rapenno@gmail.com)
* @version 0.1
*
* @since 2014-11-04
*/
public class SfcProviderRenderedPathAPI {
private static final String FUNCTION = "function";
private static final String IP = "ip";
private static final String LISP = "lisp";
private static final String MAC = "mac";
private static final String MPLS = "mpls";
private static final int MAX_STARTING_INDEX = 255;
private static SfcServiceFunctionSchedulerAPI defaultScheduler;
private static final String REVERSED_PATH_SUFFIX = "-Reverse";
private static final Logger LOG = LoggerFactory.getLogger(SfcProviderRenderedPathAPI.class);
protected SfcProviderRenderedPathAPI() {
}
private static SfcServiceFunctionSchedulerAPI getServiceFunctionScheduler(
Class<? extends ServiceFunctionSchedulerTypeIdentity> serviceFunctionSchedulerType) {
SfcServiceFunctionSchedulerAPI scheduler;
if (serviceFunctionSchedulerType == RoundRobin.class) {
scheduler = new SfcServiceFunctionRoundRobinSchedulerAPI();
} else if (serviceFunctionSchedulerType == LoadBalance.class) {
scheduler = new SfcServiceFunctionLoadBalanceSchedulerAPI();
} else if (serviceFunctionSchedulerType == Random.class) {
scheduler = new SfcServiceFunctionRandomSchedulerAPI();
} else if (serviceFunctionSchedulerType == ShortestPath.class) {
scheduler = new SfcServiceFunctionShortestPathSchedulerAPI();
} else if (serviceFunctionSchedulerType == LoadPathAware.class) {
scheduler = new SfcServiceFunctionLoadPathAwareSchedulerAPI();
} else {
scheduler = new SfcServiceFunctionRandomSchedulerAPI();
}
return scheduler;
}
@SuppressWarnings("checkstyle:IllegalCatch")
private static void initDefaultServiceFunctionScheduler() {
java.lang.Class<? extends ServiceFunctionSchedulerTypeIdentity> serviceFunctionSchedulerType;
try {
serviceFunctionSchedulerType = SfcProviderScheduleTypeAPI.readEnabledServiceFunctionScheduleTypeEntry()
.getType();
} catch (Exception e) {
serviceFunctionSchedulerType = Random.class;
}
defaultScheduler = getServiceFunctionScheduler(serviceFunctionSchedulerType);
LOG.info("Selected SF Schdedule Type: {}", serviceFunctionSchedulerType);
}
/**
* Creates a RSP and all the associated operational state based on the given
* service function path and scheduler.
*
* <p>
*
* @param createdServiceFunctionPath
* Service Function Path
* @param createRenderedPathInput
* CreateRenderedPathInput object
* @param scheduler
* SfcServiceFunctionSchedulerAPI object
* @return RenderedServicePath Created RSP or null
*/
public static RenderedServicePath createRenderedServicePathAndState(ServiceFunctionPath createdServiceFunctionPath,
CreateRenderedPathInput createRenderedPathInput, SfcServiceFunctionSchedulerAPI scheduler) {
boolean rspSuccessful = false;
boolean addPathToSffStateSuccessful = false;
boolean addPathToSfStateSuccessful = false;
RenderedServicePath renderedServicePath;
// Fall back to defaultScheduler
if (scheduler == null) {
SfcProviderRenderedPathAPI.initDefaultServiceFunctionScheduler();
scheduler = defaultScheduler;
}
// Create RSP
if ((renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathEntry(createdServiceFunctionPath,
createRenderedPathInput, scheduler)) != null) {
rspSuccessful = true;
} else {
LOG.error("Could not create RSP. System state inconsistent. Deleting and add SFP {} back",
createdServiceFunctionPath.getName());
}
// Add Path name to SFF operational state
if (rspSuccessful && SfcProviderServiceForwarderAPI.addPathToServiceForwarderState(renderedServicePath)) {
addPathToSffStateSuccessful = true;
} else {
if (renderedServicePath != null) {
SfcProviderRenderedPathAPI.deleteRenderedServicePath(renderedServicePath.getName());
}
}
// Add Path to SF operational state
if (addPathToSffStateSuccessful
&& SfcProviderServiceFunctionAPI.addPathToServiceFunctionState(renderedServicePath)) {
addPathToSfStateSuccessful = true;
} else {
SfcProviderServiceForwarderAPI.deletePathFromServiceForwarderState(createdServiceFunctionPath);
if (renderedServicePath != null) {
SfcProviderRenderedPathAPI.deleteRenderedServicePath(renderedServicePath.getName());
}
}
// Add RSP to SFP operational state
if (!(addPathToSfStateSuccessful && SfcProviderServicePathAPI.addRenderedPathToServicePathState(
createdServiceFunctionPath.getName(), renderedServicePath.getName()))) {
SfcProviderServiceFunctionAPI
.deleteServicePathFromServiceFunctionState(createdServiceFunctionPath.getName());
SfcProviderServiceForwarderAPI.deletePathFromServiceForwarderState(createdServiceFunctionPath);
if (renderedServicePath != null) {
SfcProviderRenderedPathAPI.deleteRenderedServicePath(renderedServicePath.getName());
}
}
if (renderedServicePath == null) {
LOG.error("Failed to create RSP for SFP {}", createdServiceFunctionPath.getName());
} else {
LOG.info("Create RSP {} for SFP {} successfully", renderedServicePath.getName(),
createdServiceFunctionPath.getName());
}
return renderedServicePath;
}
/**
* Creates a RSP and all the associated operational state based on the given
* service function path.
*
* <p>
*
* @param createdServiceFunctionPath
* Service Function Path
* @param createRenderedPathInput
* CreateRenderedPathInput object
* @return RenderedServicePath Created RSP or null
*/
public static RenderedServicePath createRenderedServicePathAndState(ServiceFunctionPath createdServiceFunctionPath,
CreateRenderedPathInput createRenderedPathInput) {
return createRenderedServicePathAndState(createdServiceFunctionPath, createRenderedPathInput, defaultScheduler);
}
/**
* Create a Symmetric Path and all the associated operational state based on
* the given rendered service path.
*
* <p>
*
* @param renderedServicePath
* RSP Object
* @return Nothing.
*/
public static RenderedServicePath createSymmetricRenderedServicePathAndState(
RenderedServicePath renderedServicePath) {
RenderedServicePath revRenderedServicePath;
boolean revRspSuccessful = false;
boolean addRevPathToSffStateSuccessful = false;
boolean addRevPathToSfStateSuccessful = false;
// Reverse Path
if ((revRenderedServicePath = SfcProviderRenderedPathAPI
.createReverseRenderedServicePathEntry(renderedServicePath)) != null) {
revRspSuccessful = true;
} else {
LOG.error("Could not create Reverse RSP {}", renderedServicePath.getName());
}
// Add Path name to SFF operational state
if (revRspSuccessful && SfcProviderServiceForwarderAPI.addPathToServiceForwarderState(revRenderedServicePath)) {
addRevPathToSffStateSuccessful = true;
} else {
SfcProviderRenderedPathAPI.deleteRenderedServicePath(revRenderedServicePath.getName());
}
// Add Path to SF operational state
if (addRevPathToSffStateSuccessful
&& SfcProviderServiceFunctionAPI.addPathToServiceFunctionState(revRenderedServicePath)) {
addRevPathToSfStateSuccessful = true;
} else {
SfcProviderServiceForwarderAPI.deletePathFromServiceForwarderState(revRenderedServicePath.getName());
SfcProviderRenderedPathAPI.deleteRenderedServicePath(revRenderedServicePath.getName());
}
// Add RSP to SFP operational state
if (!(addRevPathToSfStateSuccessful && SfcProviderServicePathAPI.addRenderedPathToServicePathState(
renderedServicePath.getParentServiceFunctionPath(), revRenderedServicePath.getName()))) {
// TODO Bug 4495 - RPCs hiding heuristics using Strings - alagalah
/*
* XXX TODO this exemplifies the issue. There is no method called
* SfcProviderServiceFunctionAPI.
* deleteServicePathFromServiceFunctionState() with signature
* (RspName): ie SfcProviderServiceFunctionAPI.
* deleteServicePathFromServiceFunctionState(
* revRenderedServicePath.getName()); There is one with signature
* (RspName, SfName).... and there's one with signature (SfpName)
* ... so what should this method be? Which one should I really be
* using? If you use rspName and sfpName interchangeably because
* they were strings, then you get this sort of confusion going on.
* I suspect that SfpName is correct, so I'll make the change here,
* and test.
*/
SfcProviderServiceFunctionAPI.deleteServicePathFromServiceFunctionState(
new SfpName(revRenderedServicePath.getName().getValue()));
SfcProviderServiceForwarderAPI.deletePathFromServiceForwarderState(revRenderedServicePath.getName());
SfcProviderRenderedPathAPI.deleteRenderedServicePath(revRenderedServicePath.getName());
}
return revRenderedServicePath;
}
/**
* Given a list of Service Functions, create a RenderedServicePath Hop List.
*
* @param serviceFunctionNameList
* List of ServiceFunctions
* @param sfgNameList
* List of ServiceFunctionGroups
* @param serviceIndex
* Starting index
* @return List of {@link RenderedServicePathHop}
*/
protected static List<RenderedServicePathHop> createRenderedServicePathHopList(List<SfName> serviceFunctionNameList,
List<String> sfgNameList, int serviceIndex) {
List<RenderedServicePathHop> renderedServicePathHopArrayList = new ArrayList<>();
RenderedServicePathHopBuilder renderedServicePathHopBuilder = new RenderedServicePathHopBuilder();
short posIndex = 0;
if (serviceFunctionNameList == null && sfgNameList == null) {
LOG.error("Could not create the hop list caused by empty name list");
return null;
}
if (sfgNameList != null) {
for (String sfgName : sfgNameList) {
ServiceFunctionGroup sfg = SfcProviderServiceFunctionGroupAPI.readServiceFunctionGroup(sfgName);
if (sfg == null) {
LOG.error("Could not find suitable SFG in data store by name: {}", sfgName);
renderedServicePathHopArrayList.clear();
break;
}
// TODO Bug 4495 - RPCs hiding heuristics using Strings -
// alagalah
/*
* Note I didn't change SFG's typing since I still am unclear as
* to what problem SFG is trying to solve, hence any
* String-String heuristics would be opaque for me to resolve in
* refactoring.
*/
ServiceFunction serviceFunction = SfcProviderServiceFunctionAPI
.readServiceFunction(new SfName(sfg.getSfcServiceFunction().get(0).getName()));
if (serviceFunction == null) {
LOG.error("Could not find suitable SF in data store by name: {}",
sfg.getSfcServiceFunction().get(0).getName());
renderedServicePathHopArrayList.clear();
break;
}
createSFGHopBuilder(serviceIndex, renderedServicePathHopBuilder, posIndex, sfg.getName(),
serviceFunction);
renderedServicePathHopArrayList.add(posIndex, renderedServicePathHopBuilder.build());
serviceIndex--;
posIndex++;
}
} else {
for (SfName serviceFunctionName : serviceFunctionNameList) {
ServiceFunction serviceFunction = SfcProviderServiceFunctionAPI
.readServiceFunction(serviceFunctionName);
if (serviceFunction == null) {
LOG.error("Could not find suitable SF in data store by name: {}", serviceFunctionName);
return null;
}
createSFHopBuilder(serviceIndex, renderedServicePathHopBuilder, posIndex, serviceFunctionName,
serviceFunction);
renderedServicePathHopArrayList.add(posIndex, renderedServicePathHopBuilder.build());
serviceIndex--;
posIndex++;
}
}
return renderedServicePathHopArrayList;
}
private static void createSFHopBuilder(int serviceIndex,
RenderedServicePathHopBuilder renderedServicePathHopBuilder, short posIndex, SfName serviceFunctionName,
ServiceFunction serviceFunction) {
createHopBuilderInternal(serviceIndex, renderedServicePathHopBuilder, posIndex, serviceFunction);
renderedServicePathHopBuilder.setServiceFunctionName(serviceFunctionName);
}
private static void createSFGHopBuilder(int serviceIndex,
RenderedServicePathHopBuilder renderedServicePathHopBuilder, short posIndex,
String serviceFunctionGroupName, ServiceFunction serviceFunction) {
createHopBuilderInternal(serviceIndex, renderedServicePathHopBuilder, posIndex, serviceFunction);
renderedServicePathHopBuilder.setServiceFunctionGroupName(serviceFunctionGroupName);
}
private static void createHopBuilderInternal(int serviceIndex,
RenderedServicePathHopBuilder renderedServicePathHopBuilder, short posIndex,
ServiceFunction serviceFunction) {
SffName serviceFunctionForwarderName = serviceFunction.getSfDataPlaneLocator().get(0)
.getServiceFunctionForwarder();
ServiceFunctionForwarder serviceFunctionForwarder = SfcProviderServiceForwarderAPI
.readServiceFunctionForwarder(serviceFunctionForwarderName);
if (serviceFunctionForwarder != null && serviceFunctionForwarder.getSffDataPlaneLocator() != null) {
if (serviceFunctionForwarder.getSffDataPlaneLocator().size() == 1) {
renderedServicePathHopBuilder.setServiceFunctionForwarderLocator(
serviceFunctionForwarder.getSffDataPlaneLocator().get(0).getName());
} else {
// If there is more than one SFF DPL, then find
// the one that is not associated with an SF
List<SffDataPlaneLocator> sffNonSfDplList =
SfcProviderServiceForwarderAPI.getNonSfDataPlaneLocators(serviceFunctionForwarder);
if (sffNonSfDplList.size() == 1) {
renderedServicePathHopBuilder.setServiceFunctionForwarderLocator(sffNonSfDplList.get(0).getName());
}
}
}
renderedServicePathHopBuilder.setHopNumber(posIndex).setServiceIndex((short) serviceIndex)
.setServiceFunctionForwarder(serviceFunctionForwarderName);
}
/**
* Create a Rendered Path and all the associated operational state based on
* the given rendered service path and scheduler.
*
* <p>
*
* @param serviceFunctionPath
* RSP Object
* @param createRenderedPathInput
* CreateRenderedPathInput object
* @param scheduler
* SfcServiceFunctionSchedulerAPI object
* @return RenderedServicePath
*/
protected static RenderedServicePath createRenderedServicePathEntry(ServiceFunctionPath serviceFunctionPath,
CreateRenderedPathInput createRenderedPathInput, SfcServiceFunctionSchedulerAPI scheduler) {
printTraceStart(LOG);
long pathId;
RenderedServicePath ret = null;
// Provisional code to test new RPC parameters
ContextHeaderAllocationType1 contextHeaderAllocationType1 = createRenderedPathInput
.getContextHeaderAllocationType1();
if (contextHeaderAllocationType1 != null) {
Class<? extends DataContainer> contextHeaderAllocationType1ImplementedInterface =
contextHeaderAllocationType1.getImplementedInterface();
if (contextHeaderAllocationType1ImplementedInterface.equals(VxlanClassifier.class)) {
LOG.debug("ok");
}
}
// String simplectxName =
// contextHeaderAllocationType1ImplementedInterface.getSimpleName();
// simplectxName is VxlanClassifier
ServiceFunctionChain serviceFunctionChain;
SfcName serviceFunctionChainName = serviceFunctionPath.getServiceChainName();
serviceFunctionChain = serviceFunctionChainName != null
? SfcProviderServiceChainAPI.readServiceFunctionChain(serviceFunctionChainName) : null;
if (serviceFunctionChain == null) {
LOG.error("ServiceFunctionChain name for Path {} not provided", serviceFunctionPath.getName());
return null;
}
// Descending order
int serviceIndex = MAX_STARTING_INDEX;
List<String> sfgNameList = SfcProviderServiceFunctionGroupAPI.getSfgNameList(serviceFunctionChain);
List<SfName> sfNameList = scheduler.scheduleServiceFunctions(serviceFunctionChain, serviceIndex,
serviceFunctionPath);
if (sfNameList == null && sfgNameList == null) {
LOG.warn("createRenderedServicePathEntry scheduler.scheduleServiceFunctions() returned null list");
return null;
}
// Before trying to create the RSP, iterate the SFs checking for one-chain-only
for (SfName sfName : sfNameList) {
List<SfServicePath> sfServicePathList = SfcProviderServiceFunctionAPI.readServiceFunctionState(sfName);
ServiceFunction sf = SfcProviderServiceFunctionAPI.readServiceFunction(sfName);
if (sf.isOneChainOnly() != null && sfServicePathList != null) {
if (sf.isOneChainOnly() == true && !sfServicePathList.isEmpty()) {
LOG.error(
"createRenderedServicePathEntry SF [{}] is-one-chain-only is TRUE and the SF is already in use",
sfName);
return null;
}
}
}
List<RenderedServicePathHop> renderedServicePathHopArrayList = createRenderedServicePathHopList(sfNameList,
sfgNameList, serviceIndex);
if (renderedServicePathHopArrayList == null) {
LOG.warn("createRenderedServicePathEntry createRenderedServicePathHopList returned null list");
return null;
}
// Build the service function path so it can be committed to datastore
/*
* pathId = (serviceFunctionPath.getPathId() != null) ?
* serviceFunctionPath.getPathId() : numCreatedPathIncrementGet();
*/
if (serviceFunctionPath.getPathId() == null) {
pathId = SfcServicePathId.checkAndAllocatePathId();
} else {
pathId = SfcServicePathId.chechAndAllocatePathId(serviceFunctionPath.getPathId());
}
if (pathId == -1) {
LOG.error("{}: Failed to allocate path-id: {}", Thread.currentThread().getStackTrace()[1], pathId);
return null;
}
RenderedServicePathBuilder renderedServicePathBuilder = new RenderedServicePathBuilder();
renderedServicePathBuilder.setRenderedServicePathHop(renderedServicePathHopArrayList);
// TODO Bug 4495 - RPCs hiding heuristics using Strings - alagalah
if (createRenderedPathInput.getName() == null || createRenderedPathInput.getName().isEmpty()) {
if (serviceFunctionPath.getName() != null) {
renderedServicePathBuilder
.setName(new RspName(serviceFunctionPath.getName().getValue() + "-Path-" + pathId));
} else {
LOG.error("{}: Failed to set RSP Name as it was null and SFP Name was null.",
Thread.currentThread().getStackTrace()[1]);
return null;
}
} else {
renderedServicePathBuilder.setName(new RspName(createRenderedPathInput.getName()));
}
renderedServicePathBuilder.setPathId(pathId);
// TODO: Find out the exact rules for service index generation
// renderedServicePathBuilder.setStartingIndex((short)
// renderedServicePathHopArrayList.size());
renderedServicePathBuilder.setStartingIndex((short) MAX_STARTING_INDEX);
renderedServicePathBuilder.setServiceChainName(serviceFunctionChainName);
renderedServicePathBuilder.setParentServiceFunctionPath(serviceFunctionPath.getName());
renderedServicePathBuilder.setContextMetadata(serviceFunctionPath.getContextMetadata());
renderedServicePathBuilder.setVariableMetadata(serviceFunctionPath.getVariableMetadata());
if (serviceFunctionPath.getTransportType() == null) {
// TODO this is a temporary workaround to a YANG problem
// Even though the SFP.transportType is defined with a default, if
// its not
// specified in the configuration, it can still return null
renderedServicePathBuilder.setTransportType(VxlanGpe.class);
} else {
renderedServicePathBuilder.setTransportType(serviceFunctionPath.getTransportType());
}
// If no encapsulation type specified, default is NSH for VxlanGpe and
// Transport
// in any other case
renderedServicePathBuilder.setSfcEncapsulation(serviceFunctionPath.getSfcEncapsulation() != null
? serviceFunctionPath.getSfcEncapsulation()
: renderedServicePathBuilder.getTransportType().equals(VxlanGpe.class) ? Nsh.class : Transport.class);
RenderedServicePathKey renderedServicePathKey = new RenderedServicePathKey(
renderedServicePathBuilder.getName());
InstanceIdentifier<RenderedServicePath> rspIID;
rspIID = InstanceIdentifier.builder(RenderedServicePaths.class)
.child(RenderedServicePath.class, renderedServicePathKey).build();
RenderedServicePath renderedServicePath = renderedServicePathBuilder.build();
if (SfcDataStoreAPI.writeMergeTransactionAPI(rspIID, renderedServicePath, LogicalDatastoreType.OPERATIONAL)) {
ret = renderedServicePath;
} else {
LOG.error("{}: Failed to create Rendered Service Path: {}", Thread.currentThread().getStackTrace()[1],
serviceFunctionPath.getName());
}
printTraceStop(LOG);
return ret;
}
/**
* Creates a RSP that is mirror image of the given one. It reverses the hop
* list and adjusts hop number and service index accordingly.
*
* <p>
*
* @param renderedServicePath
* RSP object
* @return Nothing
*/
public static RenderedServicePath createReverseRenderedServicePathEntry(RenderedServicePath renderedServicePath) {
RenderedServicePath ret = null;
long pathId = SfcServicePathId.checkAndAllocateSymmetricPathId(renderedServicePath.getPathId());
printTraceStart(LOG);
if (pathId == -1) {
LOG.error("{}: Failed to allocate symmetric path Id for Path Id: {}",
Thread.currentThread().getStackTrace()[1], renderedServicePath.getPathId());
}
RenderedServicePathBuilder revRenderedServicePathBuilder = new RenderedServicePathBuilder(renderedServicePath);
revRenderedServicePathBuilder.setPathId(pathId);
RspName revPathName = generateReversedPathName(renderedServicePath.getName());
revRenderedServicePathBuilder.setName(revPathName);
RenderedServicePathKey revRenderedServicePathKey = new RenderedServicePathKey(revPathName);
revRenderedServicePathBuilder.setKey(revRenderedServicePathKey);
List<RenderedServicePathHop> renderedServicePathHopList = renderedServicePath.getRenderedServicePathHop();
// Populate new array with elements from existing service path. They
// will be replaced as we
// go along
List<RenderedServicePathHop> revRenderedServicePathHopArrayList = new ArrayList<>();
revRenderedServicePathHopArrayList.addAll(renderedServicePathHopList);
ListIterator<RenderedServicePathHop> iter = renderedServicePathHopList
.listIterator(renderedServicePathHopList.size());
short revServiceHop = 0;
while (iter.hasPrevious()) {
RenderedServicePathHop renderedServicePathHop = iter.previous();
RenderedServicePathHopKey revRenderedServicePathHopKey = new RenderedServicePathHopKey(revServiceHop);
RenderedServicePathHopBuilder revRenderedServicePathHopBuilder = new RenderedServicePathHopBuilder(
renderedServicePathHop);
revRenderedServicePathHopBuilder.setHopNumber(revServiceHop);
revRenderedServicePathHopBuilder.setServiceIndex((short) (MAX_STARTING_INDEX - revServiceHop));
revRenderedServicePathHopBuilder.setKey(revRenderedServicePathHopKey);
revRenderedServicePathHopArrayList.set(revServiceHop, revRenderedServicePathHopBuilder.build());
revServiceHop++;
}
revRenderedServicePathBuilder.setRenderedServicePathHop(revRenderedServicePathHopArrayList);
revRenderedServicePathBuilder.setSymmetricPathId(renderedServicePath.getPathId());
InstanceIdentifier<RenderedServicePath> rspIID;
rspIID = InstanceIdentifier.builder(RenderedServicePaths.class)
.child(RenderedServicePath.class, revRenderedServicePathKey).build();
RenderedServicePath revRenderedServicePath = revRenderedServicePathBuilder.build();
if (SfcDataStoreAPI.writeMergeTransactionAPI(rspIID, revRenderedServicePath,
LogicalDatastoreType.OPERATIONAL)) {
ret = revRenderedServicePath;
} else {
LOG.error("{}: Failed to create Reverse Rendered Service Path: {}",
Thread.currentThread().getStackTrace()[1], revPathName);
}
printTraceStop(LOG);
return ret;
}
/**
* Given the name of an RSP, return its reverse RSP name.
*
* @param rspName
* the RSP name
* @return the reverse RSP name
*/
public static RspName generateReversedPathName(RspName rspName) {
return rspName.getValue().endsWith(REVERSED_PATH_SUFFIX)
? new RspName(rspName.getValue().replaceFirst(REVERSED_PATH_SUFFIX, ""))
: new RspName(rspName.getValue() + REVERSED_PATH_SUFFIX);
}
/**
* It returns the Symmetric RSP Name from a RspName if both exist.
*
* <p>
*
* @param rspName
* RspName object with the primary Rendered Service Path Name
* @return The Symmetric (Reversed) RSP; null in case the RSP does not exist
* or if it has not a symmetric Path Id
*/
public static RspName getReversedRspName(RspName rspName) {
RspName returnRspName = null;
RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
if (renderedServicePath != null && renderedServicePath.getSymmetricPathId() != null) {
// The RSP has a symmetric ("Reverse") Path
returnRspName = SfcProviderRenderedPathAPI.generateReversedPathName(renderedServicePath.getName());
}
return returnRspName;
}
/**
* This function reads a RSP from the datastore.
*
* <p>
*
* @param rspName
* RSP name
* @return Nothing.
*/
public static RenderedServicePath readRenderedServicePath(RspName rspName) {
printTraceStart(LOG);
RenderedServicePathKey renderedServicePathKey = new RenderedServicePathKey(rspName);
InstanceIdentifier<RenderedServicePath> rspIID = InstanceIdentifier.builder(RenderedServicePaths.class)
.child(RenderedServicePath.class, renderedServicePathKey).build();
RenderedServicePath rsp = SfcDataStoreAPI.readTransactionAPI(rspIID, LogicalDatastoreType.OPERATIONAL);
printTraceStop(LOG);
return rsp;
}
/**
* When a SFF is deleted directly we need to delete all associated SFPs.
*
* <p>
*
* @param servicePaths
* SffServicePath object
* @return Nothing.
*/
public static boolean deleteRenderedServicePaths(List<RspName> servicePaths) {
printTraceStart(LOG);
boolean ret = false;
for (RspName rspName : servicePaths) {
if (SfcProviderRenderedPathAPI.readRenderedServicePath(rspName) != null) {
if (SfcProviderRenderedPathAPI.deleteRenderedServicePath(rspName)) {
ret = true;
} else {
LOG.error("Could not delete RSP: {}", rspName);
ret = false;
}
} else {
LOG.debug("RSP {} already deleted by another thread or client", rspName);
ret = true;
}
}
return ret;
}
/**
* Delete a list of RSPs and associated states.
*
* @param rspNames
* the list of RSP names.
* @return true if everything was deleted ok, false otherwise.
*/
public static boolean deleteRenderedServicePathsAndStates(List<RspName> rspNames) {
boolean sfStateOk = SfcProviderServiceFunctionAPI.deleteServicePathFromServiceFunctionState(rspNames);
boolean sffStateOk = SfcProviderServiceForwarderAPI.deletePathFromServiceForwarderState(rspNames);
boolean rspOk = SfcProviderRenderedPathAPI.deleteRenderedServicePaths(rspNames);
return sfStateOk && sffStateOk && rspOk;
}
/**
* This method deletes a RSP from the datastore and frees the Path ID.
*
* <p>
*
* @param renderedServicePathName
* RSP name
* @return Nothing.
*/
public static boolean deleteRenderedServicePath(RspName renderedServicePathName) {
boolean ret = false;
printTraceStart(LOG);
RenderedServicePathKey renderedServicePathKey = new RenderedServicePathKey(renderedServicePathName);
InstanceIdentifier<RenderedServicePath> rspEntryIID = InstanceIdentifier.builder(RenderedServicePaths.class)
.child(RenderedServicePath.class, renderedServicePathKey).build();
RenderedServicePath renderedServicePath = SfcDataStoreAPI.readTransactionAPI(rspEntryIID,
LogicalDatastoreType.OPERATIONAL);
if (renderedServicePath != null) {
long pathId = renderedServicePath.getPathId();
if (SfcDataStoreAPI.deleteTransactionAPI(rspEntryIID, LogicalDatastoreType.OPERATIONAL)) {
ret = true;
// Free pathId
SfcServicePathId.freePathId(pathId);
} else {
LOG.error("{}: Failed to delete RSP: {}", Thread.currentThread().getStackTrace()[1],
renderedServicePathName);
}
} else {
ret = true;
}
printTraceStop(LOG);
return ret;
}
/**
* This method provides all necessary information for a system to construct
* a NSH header and associated overlay packet to target the first service
* hop of a Rendered Service Path.
*
* <p>
*
* @param rspName
* RSP name
* @return Nothing.
*/
public static RenderedServicePathFirstHop readRenderedServicePathFirstHop(RspName rspName) {
RenderedServicePathFirstHop renderedServicePathFirstHop = null;
RenderedServicePath renderedServicePath = readRenderedServicePath(rspName);
if (renderedServicePath != null) {
RenderedServicePathFirstHopBuilder renderedServicePathFirstHopBuilder =
new RenderedServicePathFirstHopBuilder();
renderedServicePathFirstHopBuilder.setPathId(renderedServicePath.getPathId())
.setStartingIndex(renderedServicePath.getStartingIndex())
.setSymmetricPathId(renderedServicePath.getSymmetricPathId());
List<RenderedServicePathHop> renderedServicePathHopList = renderedServicePath.getRenderedServicePathHop();
RenderedServicePathHop renderedServicePathHop = renderedServicePathHopList.get(0);
SffName sffName = renderedServicePathHop.getServiceFunctionForwarder();
SffDataPlaneLocatorName sffLocatorName = renderedServicePathHop.getServiceFunctionForwarderLocator();
if (sffLocatorName == null) {
return renderedServicePathFirstHopBuilder.build();
}
SffDataPlaneLocator sffDataPlaneLocator = SfcProviderServiceForwarderAPI
.readServiceFunctionForwarderDataPlaneLocator(sffName, sffLocatorName);
if (sffDataPlaneLocator != null && sffDataPlaneLocator.getDataPlaneLocator() != null
&& sffDataPlaneLocator.getDataPlaneLocator().getLocatorType() != null
&& sffDataPlaneLocator.getDataPlaneLocator().getLocatorType().getImplementedInterface() != null
&& sffDataPlaneLocator.getDataPlaneLocator().getLocatorType().getImplementedInterface()
.getSimpleName() != null) {
String type = sffDataPlaneLocator.getDataPlaneLocator().getLocatorType().getImplementedInterface()
.getSimpleName().toLowerCase();
switch (type) {
case FUNCTION:
break;
case IP:
Ip ipLocator = (Ip) sffDataPlaneLocator.getDataPlaneLocator().getLocatorType();
if (ipLocator.getIp() != null) {
renderedServicePathFirstHopBuilder.setIp(ipLocator.getIp());
if (ipLocator.getPort() != null) {
renderedServicePathFirstHopBuilder.setPort(ipLocator.getPort());
}
}
// IP means VXLAN-GPE, later we might have other options...
renderedServicePathFirstHopBuilder.setTransportType(VxlanGpe.class);
break;
case LISP:
break;
case MAC:
break;
case MPLS:
// TODO: Brady
break;
default:
break;
}
} else {
LOG.error("{}: Failed to read data plane locator {} for SFF {}",
Thread.currentThread().getStackTrace()[1], sffLocatorName, sffName);
}
renderedServicePathFirstHop = renderedServicePathFirstHopBuilder.build();
}
return renderedServicePathFirstHop;
}
/**
* This method gets all necessary information for a system to construct a
* NSH header and associated overlay packet to target the first service hop
* of a Rendered Service Path by ServiceFunctionTypeIdentity list.
*
* <p>
*
* @param serviceFunctionSchedulerType
* schedulertype for the Service Function
* @param serviceFunctionTypeList
* ServiceFunctionTypeIdentity list
* @return RenderedServicePathFirstHop first hop of the rendered path
*/
public static RenderedServicePathFirstHop readRspFirstHopBySftList(
Class<? extends ServiceFunctionSchedulerTypeIdentity> serviceFunctionSchedulerType,
List<SftTypeName> serviceFunctionTypeList) {
int index;
String serviceTypeName;
ServiceFunctionType serviceFunctionType = null;
List<SfcServiceFunction> sfcServiceFunctionArrayList = new ArrayList<>();
// TODO Not sure why we need references to GBP in here. The way the
// integration was done, we
// can add our own strings. This maybe an artifact of
// driving everything through RSPs which was a bad idea, and I don't
// believe these are
// necessary moving forward. Suggest someone cleans this up but in
// this patch I am simply introducing typedefs so we can move forward to
// some data model
// work
SfcName sfcName = new SfcName("chain-sfc-gbp");
SfpName pathName = new SfpName("path-sfc-gbp");
boolean ret;
SfcServiceFunctionSchedulerAPI scheduler;
printTraceStart(LOG);
scheduler = getServiceFunctionScheduler(serviceFunctionSchedulerType);
/* Build sfcName, pathName and ServiceFunction list */
for (index = 0; index < serviceFunctionTypeList.size(); index++) {
serviceFunctionType = SfcProviderServiceTypeAPI.readServiceFunctionType(serviceFunctionTypeList.get(index));
serviceTypeName = serviceFunctionType.getType().getValue();
if (serviceTypeName == null) {
LOG.error("Unknown ServiceFunctionType: {}", serviceFunctionType.getType());
return null;
}
sfcName = new SfcName(sfcName.getValue() + "-" + serviceTypeName);
pathName = new SfpName(pathName.getValue() + "-" + serviceTypeName);
SfcServiceFunctionBuilder sfcServiceFunctionBuilder = new SfcServiceFunctionBuilder();
sfcServiceFunctionArrayList.add(sfcServiceFunctionBuilder.setName(serviceTypeName + "-gbp-sfc")
.setType(serviceFunctionType.getType()).build());
}
/* Read service chain sfcName if it exists */
ServiceFunctionChain serviceFunctionChain = SfcProviderServiceChainAPI.readServiceFunctionChain(sfcName);
/* Create service chain sfcName if it doesn't exist */
if (serviceFunctionChain == null) {
// Create ServiceFunctionChain
ServiceFunctionChainBuilder sfcBuilder = new ServiceFunctionChainBuilder();
sfcBuilder.setName(sfcName).setSfcServiceFunction(sfcServiceFunctionArrayList);
serviceFunctionChain = sfcBuilder.build();
ret = SfcProviderServiceChainAPI.putServiceFunctionChain(serviceFunctionChain);
if (!ret) {
LOG.error("Failed to create ServiceFunctionChain: {}", sfcName);
return null;
}
}
/* Read ServiceFunctionPath pathName if it exists */
ServiceFunctionPath serviceFunctionPath = SfcProviderServicePathAPI.readServiceFunctionPath(pathName);
/* Create ServiceFunctionPath pathName if it doesn't exist */
if (serviceFunctionPath == null) {
/* Create ServiceFunctionPath pathName if it doesn't exist */
ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
sfpBuilder.setName(pathName).setServiceChainName(sfcName);
serviceFunctionPath = sfpBuilder.build();
ret = SfcProviderServicePathAPI.putServiceFunctionPath(serviceFunctionPath);
if (!ret) {
LOG.error("Failed to create ServiceFunctionPath: {}", pathName);
return null;
}
}
/* Create RenderedServicePath */
RenderedServicePath renderedServicePath;
RenderedServicePath revRenderedServicePath;
/*
* We need to provide the same information as we would through the RPC
*/
CreateRenderedPathInputBuilder createRenderedPathInputBuilder = new CreateRenderedPathInputBuilder();
renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(serviceFunctionPath,
createRenderedPathInputBuilder.build(), scheduler);
if (renderedServicePath == null) {
LOG.error("Failed to create RenderedServicePath for ServiceFunctionPath: {}", pathName);
return null;
}
if (isChainSymmetric(serviceFunctionPath, renderedServicePath)) {
revRenderedServicePath = SfcProviderRenderedPathAPI
.createSymmetricRenderedServicePathAndState(renderedServicePath);
if (revRenderedServicePath == null) {
LOG.error("Failed to create symmetric RenderedServicePath for ServiceFunctionPath: {}", pathName);
}
// Set the symmetric path ID on the original RSP
SfcProviderRenderedPathAPI.setSymmetricPathId(renderedServicePath, revRenderedServicePath.getPathId());
}
RenderedServicePathFirstHop firstHop = SfcProviderRenderedPathAPI
.readRenderedServicePathFirstHop(renderedServicePath.getName());
printTraceStop(LOG);
return firstHop;
}
/**
* This method gets all necessary information for a system to construct a
* NSH header and associated overlay packet to target the first service hop
* of a Rendered Service Path by ServiceFunctionTypeIdentity list.
*
* <p>
*
* @param renderedServicePath
* RenderedServicePath Object
* @param pathId
* Symmetric Path Id
* @return true if symmetric path-id was set, otherwise false
*/
public static boolean setSymmetricPathId(RenderedServicePath renderedServicePath, long pathId) {
RenderedServicePathKey renderedServicePathKey = new RenderedServicePathKey(renderedServicePath.getName());
InstanceIdentifier<RenderedServicePath> rspIID;
rspIID = InstanceIdentifier.builder(RenderedServicePaths.class)
.child(RenderedServicePath.class, renderedServicePathKey).build();
RenderedServicePathBuilder renderedServicePathBuilder = new RenderedServicePathBuilder(renderedServicePath);
renderedServicePathBuilder.setSymmetricPathId(pathId);
return SfcDataStoreAPI.writeMergeTransactionAPI(rspIID, renderedServicePathBuilder.build(),
LogicalDatastoreType.OPERATIONAL);
}
/**
* Determine if a Rendered Service Path should be Symmetric. The Service
* Function Path symmetric field, if present, has priority over the SF
* SF-type bidirectionality fields. First use the SFP symmetric value, if
* its not present, then use the SF SF-type bidirectionality values by
* iterating the ServiceFunctions in the RSP Hops to check if there is at
* least one SF with an SF-Type that has the bidirectionality field set
* true.
*
* @param sfp
* - used to get the symmetric flag
* @param rsp
* - the RSP to iterate over the SFs in the hops
* @return True is there the RSP is symmetric, false otherwise.
*/
public static boolean isChainSymmetric(ServiceFunctionPath sfp, RenderedServicePath rsp) {
if (sfp.isSymmetric() != null) {
return sfp.isSymmetric();
}
List<RenderedServicePathHop> rspHops = rsp.getRenderedServicePathHop();
for (RenderedServicePathHop hop : rspHops) {
ServiceFunction sf = SfcProviderServiceFunctionAPI.readServiceFunction(hop.getServiceFunctionName());
ServiceFunctionType sfType = SfcProviderServiceTypeAPI.readServiceFunctionType(sf.getType());
if (sfType == null) {
LOG.error("Service Function type [{}] for Service Function [{}] does not exist.", sf.getType(),
sf.getName());
continue;
}
if (sfType.isBidirectional() != null && sfType.isBidirectional()) {
return true;
}
}
return false;
}
}