/*
* Copyright (c) 2015, 2017 Ericsson 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.ofrenderer.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.sfc.ovs.provider.SfcOvsUtil;
import org.opendaylight.sfc.provider.api.SfcDataStoreAPI;
import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
import org.opendaylight.sfc.provider.api.SfcProviderServiceFunctionAPI;
import org.opendaylight.sfc.provider.api.SfcProviderServiceFunctionGroupAPI;
import org.opendaylight.sfc.provider.api.SfcProviderServiceTypeAPI;
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.service.functions.ServiceFunction;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsBridgeAugmentation;
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.sft.rev140701.service.function.types.ServiceFunctionType;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
public class SfcOfProviderUtils extends SfcOfBaseProviderUtils {
// Since this class can be called by multiple threads,
// store these objects per RSP id to avoid collisions
private class RspContext {
// store the SFs and SFFs internally so we dont have to
// query the DataStore repeatedly for the same thing
private final Map<SfName, ServiceFunction> serviceFunctions;
private final Map<String, ServiceFunctionGroup> serviceFunctionGroups;
private final Map<SffName, ServiceFunctionForwarder> serviceFunctionFowarders;
RspContext() {
serviceFunctions = Collections.synchronizedMap(new HashMap<SfName, ServiceFunction>());
serviceFunctionGroups = Collections.synchronizedMap(new HashMap<String, ServiceFunctionGroup>());
serviceFunctionFowarders = Collections.synchronizedMap(new HashMap<SffName, ServiceFunctionForwarder>());
}
}
private final Map<Long, RspContext> rspIdToContext;
protected static ExecutorService executor = Executors.newFixedThreadPool(10);
public SfcOfProviderUtils() {
rspIdToContext = new HashMap<>();
}
@Override
public void addRsp(long rspId) {
rspIdToContext.put(rspId, new RspContext());
}
@Override
public void removeRsp(long rspId) {
rspIdToContext.remove(rspId);
}
/**
* Return the named ServiceFunction Acts as a local cache to not have to go
* to DataStore so often First look in internal storage, if its not there
* get it from the DataStore and store it internally.
*
* @param sfName
* - The SF Name to search for
* @return - The ServiceFunction object, or null if not found
*/
@Override
public ServiceFunction getServiceFunction(final SfName sfName, long rspId) {
RspContext rspContext = rspIdToContext.get(rspId);
ServiceFunction sf = rspContext.serviceFunctions.get(sfName);
if (sf == null) {
sf = SfcProviderServiceFunctionAPI.readServiceFunction(sfName);
if (sf != null) {
rspContext.serviceFunctions.put(sfName, sf);
}
}
return sf;
}
/**
* Return the service funtion type from a specific sf.
*
* @param sfName
* - The SF Name to search for
* @return - The ServiceFunction object, or null if not found
*/
@Override
public ServiceFunctionType getServiceFunctionType(final SfName sfName, long rspId) {
ServiceFunction sf = getServiceFunction(sfName, rspId);
ServiceFunctionType sfType = SfcProviderServiceTypeAPI.readServiceFunctionType(sf.getType());
return sfType;
}
/**
* Return the named ServiceFunctionForwarder Acts as a local cache to not
* have to go to DataStore so often First look in internal storage, if its
* not there get it from the DataStore and store it internally.
*
* @param sffName
* - The SFF Name to search for
* @return The ServiceFunctionForwarder object, or null if not found
*/
@Override
public ServiceFunctionForwarder getServiceFunctionForwarder(final SffName sffName, long rspId) {
RspContext rspContext = rspIdToContext.get(rspId);
ServiceFunctionForwarder sff = rspContext.serviceFunctionFowarders.get(sffName);
if (sff == null) {
sff = SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName);
if (sff != null) {
sff = SfcOvsUtil.augmentSffWithOpenFlowNodeId(sff);
rspContext.serviceFunctionFowarders.put(sffName, sff);
}
}
return sff;
}
@Override
public ServiceFunctionGroup getServiceFunctionGroup(final String sfgName, long rspId) {
RspContext rspContext = rspIdToContext.get(rspId);
ServiceFunctionGroup sfg = rspContext.serviceFunctionGroups.get(sfgName);
if (sfg == null) {
sfg = SfcProviderServiceFunctionGroupAPI.readServiceFunctionGroup(sfgName);
if (sfg != null) {
rspContext.serviceFunctionGroups.put(sfgName, sfg);
}
}
return sfg;
}
/**
* Currently the switch port the SF is connected to is stored by Tacker.
* Here we take this name and convert it to the port number.
*/
@Override
public Long getPortNumberFromName(final String sffName, final String portName, long rspId) {
ServiceFunctionForwarder sff = getServiceFunctionForwarder(new SffName(sffName), rspId);
SffOvsBridgeAugmentation sffOvsBridge = sff.getAugmentation(SffOvsBridgeAugmentation.class);
if (sffOvsBridge == null || sffOvsBridge.getOvsBridge() == null
|| sffOvsBridge.getOvsBridge().getBridgeName() == null) {
throw new RuntimeException("getPortNumberFromName: SFF [" + sffName
+ "] does not have the expected SffOvsBridgeAugmentation.");
}
// we shouldn't use the getter getOpendaylightSfcObj, but nobody uses
// getPortNumberFromName
Node node = SfcOvsUtil.lookupTopologyNode(sff, executor);
if (node == null || node.getAugmentation(OvsdbNodeAugmentation.class) == null) {
throw new IllegalStateException("OVSDB node does not exist for SFF " + sffName);
}
Long ofPort = 0L;
TerminationPoint tp = readTerminationPoint(node, sffOvsBridge.getOvsBridge().getBridgeName(), portName);
if (tp != null) {
OvsdbTerminationPointAugmentation port = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
if (port != null) {
ofPort = getOFPort(port);
}
}
return ofPort;
}
// internal support method for getPortNumberFromName()
private Long getOFPort(OvsdbTerminationPointAugmentation port) {
Long ofPort = 0L;
if (port.getOfport() != null) {
ofPort = port.getOfport();
}
return ofPort;
}
// internal support method for getPortNumberFromName() and related methods
private OvsdbTerminationPointAugmentation extractTerminationPointAugmentation(Node bridgeNode, String portName) {
if (bridgeNode.getAugmentation(OvsdbBridgeAugmentation.class) != null) {
List<OvsdbTerminationPointAugmentation> tpAugmentations = extractTerminationPointAugmentations(bridgeNode);
for (OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation : tpAugmentations) {
if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
return ovsdbTerminationPointAugmentation;
}
}
}
return null;
}
// internal support method for getPortNumberFromName() and related methods
public List<OvsdbTerminationPointAugmentation> extractTerminationPointAugmentations(Node node) {
List<OvsdbTerminationPointAugmentation> tpAugmentations = new ArrayList<>();
List<TerminationPoint> terminationPoints = node.getTerminationPoint();
if (terminationPoints != null && !terminationPoints.isEmpty()) {
for (TerminationPoint tp : terminationPoints) {
OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation = tp
.getAugmentation(OvsdbTerminationPointAugmentation.class);
if (ovsdbTerminationPointAugmentation != null) {
tpAugmentations.add(ovsdbTerminationPointAugmentation);
}
}
}
return tpAugmentations;
}
// internal support method for getPortNumberFromName() and related methods
private TerminationPoint readTerminationPoint(Node ovsdbNode, String bridgeName, String portName) {
InstanceIdentifier<TerminationPoint> tpIid = InstanceIdentifier.create(NetworkTopology.class)
.child(Topology.class, new TopologyKey(new TopologyId(new Uri("ovsdb:1"))))
.child(Node.class,
new NodeKey(new NodeId(ovsdbNode.getKey().getNodeId().getValue() + "/bridge/" + bridgeName)))
.child(TerminationPoint.class, new TerminationPointKey(new TpId(portName)));
return SfcDataStoreAPI.readTransactionAPI(tpIid, LogicalDatastoreType.OPERATIONAL);
}
// Get the SFF DPLs that are not used by SFs. Useful when there are multiple
// DPL types: one for the SFs and another for the SFF trunk.
public List<SffDataPlaneLocator> getSffNonSfDataPlaneLocators(ServiceFunctionForwarder sff) {
return SfcProviderServiceForwarderAPI.getNonSfDataPlaneLocators(sff);
}
}