2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
12 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
14 * This program and the accompanying materials are made available under the
15 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
16 * and is available at http://www.eclipse.org/legal/epl-v10.html
19 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.ChainActionFlows.createChainTunnelFlows;
20 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxSetNsiAction;
21 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxSetNspAction;
23 import java.util.List;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
29 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.FlowMap;
30 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory;
31 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.NetworkElements;
32 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.PolicyPair;
33 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcIidFactory;
34 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader;
35 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader.SfcNshHeaderBuilder;
36 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
37 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
38 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
39 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
40 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
41 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
42 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
44 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
45 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
46 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
47 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
48 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.IsRequired;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.Type;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.ParameterBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
69 import com.google.common.base.Optional;
70 import com.google.common.collect.ImmutableList;
71 import com.google.common.collect.Iterables;
74 * Chain action for the OpenFlow Overlay renderer
75 * TODO: separate the generic definition from the concrete
76 * implementation for the OpenFlow Overlay renderer
78 public class ChainAction extends Action {
80 private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class);
82 public static final ActionDefinitionId ID = new ActionDefinitionId("3d886be7-059f-4c4f-bbef-0356bea40933");
84 public static final Integer CHAIN_CONDITION_GROUP = 0xfffffe;
86 protected static final String TYPE = "type";
89 public static final String SFC_CHAIN_ACTION = "chain";
90 // the parameter used for storing the chain name
91 public static final String SFC_CHAIN_NAME = "sfc-chain-name";
93 protected static final ActionDefinition DEF = new ActionDefinitionBuilder().setId(ID)
94 .setName(new ActionName(SFC_CHAIN_ACTION))
95 .setDescription(new Description("Send the traffic through a Service Function Chain"))
97 (ImmutableList.of(new ParameterBuilder().setName(new ParameterName(SFC_CHAIN_NAME))
98 .setDescription(new Description("The named chain to match against"))
99 .setIsRequired(IsRequired.Required)
100 .setType(Type.String)
105 public ActionDefinitionId getId() {
110 public ActionDefinition getActionDef() {
115 public List<ActionBuilder> updateAction(List<ActionBuilder> actions, Map<String, Object> params, Integer order,
116 NetworkElements netElements, PolicyPair policyPair, FlowMap flowMap, OfContext ctx, Direction direction) {
118 * Get the named chain
120 String chainName = null;
121 if (params != null) {
122 LOG.debug("updateAction: Searching for named chain");
123 for (String name : params.keySet()) {
124 if (name instanceof String) {
125 if (name.equals(SFC_CHAIN_NAME)) {
126 chainName = (String) params.get(name);
127 if (chainName == null) {
128 LOG.error("updateAction: Chain name was null");
135 LOG.error("updateAction: Parameters null for chain action");
139 if (chainName == null) {
140 LOG.error("updateAction: Chain name was null");
145 * If path is symmetrical then there are two RSPs.
146 * if srcEp is in consumer EPG use "rspName"
147 * else srcEp is in provider EPG, "rspName-Reverse".
149 ServiceFunctionPath sfcPath = getSfcPath(chainName);
150 if (sfcPath == null || sfcPath.getName() == null) {
151 LOG.error("updateAction: SFC Path was invalid. Either null or name was null.", sfcPath);
154 // Find existing RSP based on following naming convention, else create it.
155 String rspName = sfcPath.getName() + "-gbp-rsp";
156 ReadOnlyTransaction rTx = ctx.getDataBroker().newReadOnlyTransaction();
157 RenderedServicePath renderedServicePath;
158 RenderedServicePath rsp = getRspByName(rspName, rTx);
160 renderedServicePath = createRsp(sfcPath, rspName);
161 if (renderedServicePath != null) {
162 LOG.info("updateAction: Could not find RSP {} for Chain {}, created.", rspName, chainName);
164 LOG.error("updateAction: Could not create RSP {} for Chain {}", rspName, chainName);
168 renderedServicePath = rsp;
172 if (sfcPath.isSymmetric() && direction.equals(Direction.Out)){
173 rspName = rspName + "-Reverse";
174 rsp = getRspByName(rspName, rTx);
176 LOG.info("updateAction: Could not find Reverse RSP {} for Chain {}", rspName, chainName);
177 renderedServicePath = createSymmetricRsp(renderedServicePath);
178 if (renderedServicePath == null) {
179 LOG.error("updateAction: Could not create RSP {} for Chain {}", rspName, chainName);
183 renderedServicePath = rsp;
186 } catch (Exception e) {
187 LOG.error("updateAction: Attemping to determine if srcEp {} was consumer.", netElements.getSrcEp().getKey(), e);
191 RenderedServicePathFirstHop rspFirstHop = SfcProviderRenderedPathAPI.readRenderedServicePathFirstHop(rspName);
192 if (!isValidRspFirstHop(rspFirstHop)) {
193 // Errors logged in method.
197 NodeId tunnelDestNodeId=netElements.getDstNodeId();
199 Long returnVnid = (long) netElements.getSrcEpOrds().getTunnelId();
201 IpAddress tunnelDest = ctx.getSwitchManager().getTunnelIP(tunnelDestNodeId, TunnelTypeVxlanGpe.class);
202 if (tunnelDest == null || tunnelDest.getIpv4Address() == null) {
203 LOG.error("updateAction: Invalid tunnelDest for NodeId: {}", tunnelDestNodeId);
207 RenderedServicePathHop firstRspHop = renderedServicePath.getRenderedServicePathHop().get(0);
208 RenderedServicePathHop lastRspHop = Iterables.getLast(renderedServicePath.getRenderedServicePathHop());
209 SfcNshHeader sfcNshHeader = new SfcNshHeaderBuilder().setNshTunIpDst(rspFirstHop.getIp().getIpv4Address())
210 .setNshTunUdpPort(rspFirstHop.getPort())
211 .setNshNsiToChain(firstRspHop.getServiceIndex())
212 .setNshNspToChain(renderedServicePath.getPathId())
213 .setNshNsiFromChain((short) (lastRspHop.getServiceIndex().intValue() - 1))
214 .setNshNspFromChain(renderedServicePath.getPathId())
215 .setNshMetaC1(SfcNshHeader.convertIpAddressToLong(tunnelDest.getIpv4Address()))
216 .setNshMetaC2(returnVnid)
219 // Cannot set all actions here. Some actions are destination specific, and we don't know
220 // a destination is to be
221 // chained until we reach this point. Need to write match/action in External Table for
223 actions = addActionBuilder(actions, nxSetNsiAction(sfcNshHeader.getNshNsiToChain()), order);
224 actions = addActionBuilder(actions, nxSetNspAction(sfcNshHeader.getNshNspToChain()), order);
225 createChainTunnelFlows(sfcNshHeader, netElements, flowMap, ctx);
229 private RenderedServicePath createRsp(ServiceFunctionPath sfcPath, String rspName) {
230 CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder().setParentServiceFunctionPath(
233 .setSymmetric(sfcPath.isSymmetric())
235 return SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfcPath, rspInput);
238 private RenderedServicePath createSymmetricRsp(RenderedServicePath rsp) {
242 return SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
245 private boolean isValidRspFirstHop(RenderedServicePathFirstHop rspFirstHop) {
246 boolean valid = true;
247 if (rspFirstHop == null) {
248 LOG.error("isValidRspFirstHop: rspFirstHop is null.");
251 if (rspFirstHop.getIp() == null || rspFirstHop.getIp().getIpv4Address() == null
252 || rspFirstHop.getIp().getIpv6Address() != null) {
253 LOG.error("isValidRspFirstHop: rspFirstHop has invalid IP address.");
256 if (rspFirstHop.getPort() == null) {
257 LOG.error("isValidRspFirstHop: rspFirstHop has no IP port .");
260 if (rspFirstHop.getPathId() == null) {
261 LOG.error("isValidRspFirstHop: rspFirstHop has no Path Id (NSP).");
264 if (rspFirstHop.getStartingIndex() == null) {
265 LOG.error("isValidRspFirstHop: rspFirstHop has no Starting Index (NSI)");
271 private RenderedServicePath getRspByName(String rspName, ReadOnlyTransaction rTx) {
272 Optional<RenderedServicePath> optRsp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
273 SfcIidFactory.rspIid(rspName), rTx);
274 if (optRsp.isPresent()) {
281 public boolean isValid(ActionInstance actionInstance) {
282 return isValidGbpChain(actionInstance.getParameterValue());
285 private boolean isValidGbpChain(List<ParameterValue> paramValue) {
286 ParameterValue pv = getChainNameParameter(paramValue);
290 LOG.trace("isValidGbpChain: Invoking RPC for chain {}", pv.getStringValue());
291 ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(pv.getStringValue());
292 return (chain != null);
295 public ServiceFunctionPath getSfcPath(String chainName) {
296 ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
297 for (ServiceFunctionPath path : paths.getServiceFunctionPath()) {
298 if (path.getServiceChainName().equals(chainName)) {
305 private ParameterValue getChainNameParameter(List<ParameterValue> paramValueList) {
306 if (paramValueList == null)
308 for (ParameterValue pv : paramValueList) {
309 if (pv.getName().getValue().equals(SFC_CHAIN_NAME)) {
316 private List<ActionBuilder> addActionBuilder(List<ActionBuilder> actions,
317 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action action, Integer order) {
318 ActionBuilder ab = new ActionBuilder();
319 ab.setAction(action);