/*
* Copyright (c) 2015, 2017 Intel Ltd. 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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.opendaylight.sfc.provider.topology.SfcProviderGraph;
import org.opendaylight.sfc.provider.topology.SfcProviderTopologyNode;
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.SffName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.ServiceFunctions;
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.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.service.function.chain.SfcServiceFunction;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.ServiceFunctionForwarders;
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.sff.rev140701.service.function.forwarders.service.function.forwarder.ConnectedSffDictionary;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.ServiceFunctionDictionary;
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.sft.rev140701.service.function.types.ServiceFunctionType;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.service.function.type.SftServiceFunctionName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements shortest path scheduling mode.
*
* <p>
*
* @author Shuqiang Zhao (shuqiangx.zhao@intel.com)
* @author Yi Yang (yi.y.yang@intel.com)
*
* @since 2015-03-13
*/
public class SfcServiceFunctionShortestPathSchedulerAPI extends SfcServiceFunctionSchedulerAPI {
private static final Logger LOG = LoggerFactory.getLogger(SfcServiceFunctionShortestPathSchedulerAPI.class);
SfcServiceFunctionShortestPathSchedulerAPI() {
super.setSfcServiceFunctionSchedulerType(
org.opendaylight.yang.gen.v1.urn.intel.params.xml.ns.yang.sfc.sfst.rev150312.ShortestPath.class);
}
/**
* This method finds out name of the Service Function closest to Service
* Function preSfName per serviceFunctionType.
*
* <p>
*
* @param serviceFunctionType
* Type of Service Function to find
* @param preSfName
* Name of previous Service Function in Service Function Path
* @param sfcProviderGraph
* Topology graph comprised of all the SFs and SFFs
* @return String Name of the Service Function with type serviceFunctionType
*/
private SfName getServiceFunctionByType(ServiceFunctionType serviceFunctionType, SfName preSfName,
SfcProviderGraph sfcProviderGraph) {
SfName sfcProviderTopologyNodeName = null;
List<SftServiceFunctionName> sftServiceFunctionNameList = serviceFunctionType.getSftServiceFunctionName();
int maxTries = sftServiceFunctionNameList.size();
/* Return null if sftServiceFunctionNameList is empty */
if (sftServiceFunctionNameList.isEmpty()) {
LOG.debug("No Service Function for {}", serviceFunctionType);
return null;
}
/*
* Randomly find one instance of serviceFunctionType and return its name
* if preSfName is null
*/
if (preSfName == null) {
/* Randomly find one instance of serviceFunctionType */
Random rad = new Random();
int start = rad.nextInt(sftServiceFunctionNameList.size());
SfcProviderTopologyNode firstHopNode;
while (maxTries > 0) {
sfcProviderTopologyNodeName = new SfName(sftServiceFunctionNameList.get(start).getName());
/*
* XXX noticed that SfcProviderGraph sometimes refers to SFFs as
* well so leaving that alone for now until a general discussion
* about Schedulers can be had.
*/
firstHopNode = sfcProviderGraph.getNode(sfcProviderTopologyNodeName.getValue());
if (firstHopNode != null) {
break;
} else {
LOG.debug("ServiceFunction {} doesn't exist", sfcProviderTopologyNodeName);
sfcProviderTopologyNodeName = null;
start = (start + 1) % sftServiceFunctionNameList.size();
maxTries--;
}
}
LOG.debug("The first ServiceFunction name: {}", sfcProviderTopologyNodeName);
return sfcProviderTopologyNodeName; // The first hop
}
SfcProviderTopologyNode preSfcProviderTopologyNode = sfcProviderGraph.getNode(preSfName.getValue());
/* return null if preSfName doesn't exist in sfcProviderGraph */
if (preSfcProviderTopologyNode == null) {
LOG.debug("Node {} doesn't exist", preSfName);
return null;
}
/* Find one instance of serviceFunctionType closest to preSfName */
int minLength = Integer.MAX_VALUE;
int length;
sfcProviderTopologyNodeName = null;
for (SftServiceFunctionName sftServiceFunctionName : sftServiceFunctionNameList) {
SfName curSfName = new SfName(sftServiceFunctionName.getName());
SfcProviderTopologyNode curSfcProviderTopologyNode = sfcProviderGraph.getNode(curSfName.getValue());
if (curSfcProviderTopologyNode == null) {
// curSfName doesn't exist in sfcProviderGraph, so skip it
continue;
}
List<SfcProviderTopologyNode> sfcProviderTopologyNodeList = sfcProviderGraph
.getShortestPath(preSfName.getValue(), curSfName.getValue());
length = sfcProviderTopologyNodeList.size();
if (length <= 1) {
LOG.debug("No path from {} to {}", preSfName, curSfName);
continue;
}
if (minLength > length) {
minLength = length;
sfcProviderTopologyNodeName = curSfName;
}
}
/*
* sfcProviderTopologyNodeName will be null if the next hop can't be
* found.
*/
if (sfcProviderTopologyNodeName == null) {
LOG.debug("Next hop of {} doesn't exist", preSfName);
}
return sfcProviderTopologyNodeName;
}
/**
* This method builds a SfcProviderGraph comprised of all the SFs and SFFs.
* sfcProviderGraph will store all the info about vertex/node and edge.
*
* <p>
* @param sfcProviderGraph
* input and output of this method
*/
private void buildTopologyGraph(SfcProviderGraph sfcProviderGraph) {
SfName sfName;
SffName sffName;
SffName toSffName;
/* Add all the ServiceFunction nodes */
ServiceFunctions sfs = SfcProviderServiceFunctionAPI.readAllServiceFunctions();
List<ServiceFunction> serviceFunctionList = sfs.getServiceFunction();
for (ServiceFunction serviceFunction : serviceFunctionList) {
sfName = serviceFunction.getName();
sfcProviderGraph.addNode(sfName.getValue());
LOG.debug("Add ServiceFunction: {}", sfName);
}
ServiceFunctionForwarders sffs = SfcProviderServiceForwarderAPI.readAllServiceFunctionForwarders();
List<ServiceFunctionForwarder> serviceFunctionForwarderList = sffs.getServiceFunctionForwarder();
/* Add edges and node for every ServiceFunctionForwarder */
for (ServiceFunctionForwarder serviceFunctionForwarder : serviceFunctionForwarderList) {
/* Add ServiceFunctionForwarder node */
sffName = serviceFunctionForwarder.getName();
sfcProviderGraph.addNode(sffName.getValue());
LOG.debug("Add ServiceFunctionForwarder: {}", sffName);
List<ServiceFunctionDictionary> serviceFunctionDictionaryList = serviceFunctionForwarder
.getServiceFunctionDictionary();
/*
* Add edge for every ServiceFunction attached to
* serviceFunctionForwarder
*/
for (ServiceFunctionDictionary serviceFunctionDictionary : serviceFunctionDictionaryList) {
sfName = serviceFunctionDictionary.getName();
sfcProviderGraph.addEdge(sfName.getValue(), sffName.getValue());
LOG.debug("Add SF-to-SFF edge: {} => {}", sfName, sffName);
}
List<ConnectedSffDictionary> connectedSffDictionaryList = serviceFunctionForwarder
.getConnectedSffDictionary();
/*
* Add edge for every ServiceFunctionForwarder connected to
* serviceFunctionForwarder
*/
for (ConnectedSffDictionary connectedSffDictionary : connectedSffDictionaryList) {
toSffName = connectedSffDictionary.getName();
sfcProviderGraph.addEdge(sffName.getValue(), toSffName.getValue());
LOG.debug("Add SFF-to-SFF edge: {} => {}", sffName, toSffName);
}
}
}
/**
* This method finds out the shortest Service Function Path for the given
* Service Function Chain chain, any two adjacent Service Functions in this
* Service Function Path have the shortest distance compared to other two
* Service Functions with same Service Function Types.
*
* <p>
* @param chain
* Service Function Chain to render
* @param serviceIndex
* Not used currently
* @return List<String> Service Function name list in the shortest
* path
*/
@Override
public List<SfName> scheduleServiceFunctions(ServiceFunctionChain chain, int serviceIndex,
ServiceFunctionPath sfp) {
SfName preSfName = null;
SfName sfName;
List<SfName> sfNameList = new ArrayList<>();
List<SfcServiceFunction> sfcServiceFunctionList = new ArrayList<>();
sfcServiceFunctionList.addAll(chain.getSfcServiceFunction());
SfcProviderGraph sfcProviderGraph = new SfcProviderGraph();
short index = 0;
Map<Short, SfName> sfpMapping = getSFPHopSfMapping(sfp);
/*
* Build topology graph for all the nodes, including every
* ServiceFunction and ServiceFunctionForwarder
*/
buildTopologyGraph(sfcProviderGraph);
/*
* Select a SF instance closest to previous hop in SFP for each
* ServiceFunction type in sfcServiceFunctionList.
*/
for (SfcServiceFunction sfcServiceFunction : sfcServiceFunctionList) {
LOG.debug("ServiceFunction name: {}", sfcServiceFunction.getName());
SfName hopSf = sfpMapping.get(index++);
if (hopSf != null) {
sfNameList.add(hopSf);
continue;
}
ServiceFunctionType serviceFunctionType = SfcProviderServiceTypeAPI
.readServiceFunctionType(sfcServiceFunction.getType());
if (serviceFunctionType != null) {
List<SftServiceFunctionName> sftServiceFunctionNameList = serviceFunctionType
.getSftServiceFunctionName();
if (!sftServiceFunctionNameList.isEmpty()) {
sfName = getServiceFunctionByType(serviceFunctionType, preSfName, sfcProviderGraph);
if (sfName != null) {
sfNameList.add(sfName);
preSfName = sfName;
LOG.debug("Next Service Function: {}", sfName);
} else {
LOG.error("Couldn't find a reachable SF for ServiceFunctionType: {}",
sfcServiceFunction.getType());
return null;
}
} else {
LOG.debug("No {} Service Function instance", sfcServiceFunction.getName());
return null;
}
} else {
LOG.debug("No {} Service Function type", sfcServiceFunction.getName());
return null;
}
}
return sfNameList;
}
}