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;
11 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.ChainActionFlows.createChainTunnelFlows;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxSetNsiAction;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxSetNspAction;
15 import java.util.List;
18 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.groupbasedpolicy.api.ValidationResult;
21 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
22 import org.opendaylight.groupbasedpolicy.dto.ValidationResultBuilder;
23 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
24 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
25 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer;
26 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.NetworkElements;
27 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.PolicyPair;
28 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcIidFactory;
29 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader;
30 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader.SfcNshHeaderBuilder;
31 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
32 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
33 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
34 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
38 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
40 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
41 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
42 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
43 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
44 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.action.definition.SupportedParameterValues;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.action.definition.SupportedParameterValuesBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 import com.google.common.base.Optional;
61 import com.google.common.collect.ImmutableList;
62 import com.google.common.collect.Iterables;
65 * Chain action for the OpenFlow Overlay renderer
66 * TODO: separate the generic definition from the concrete
67 * implementation for the OpenFlow Overlay renderer
69 public class ChainAction extends Action {
71 private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class);
74 public ActionDefinitionId getId() {
75 return ChainActionDefinition.ID;
79 public ActionDefinition getActionDef() {
80 return ChainActionDefinition.DEFINITION;
84 public List<ActionBuilder> updateAction(List<ActionBuilder> actions, Map<String, Object> params, Integer order,
85 NetworkElements netElements, PolicyPair policyPair, OfWriter ofWriter,
86 OfContext ctx, Direction direction) {
90 String chainName = null;
92 LOG.debug("updateAction: Searching for named chain");
93 for (String name : params.keySet()) {
94 if (name.equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
95 chainName = (String) params.get(name);
96 if (chainName == null) {
97 LOG.error("updateAction: Chain name was null");
103 LOG.error("updateAction: Parameters null for chain action");
107 if (chainName == null) {
108 LOG.error("updateAction: Chain name was null");
115 * If path is symmetrical then there are two RSPs.
116 * if srcEp is in consumer EPG use "rspName"
117 * else srcEp is in provider EPG, "rspName-Reverse".
119 ServiceFunctionPath sfcPath = getSfcPath(new SfcName(chainName));
120 if (sfcPath == null || sfcPath.getName() == null) {
121 LOG.error("updateAction: SFC Path was invalid. Either null or name was null.", sfcPath);
124 // TODO Need helper function to get getTenantName() that returns Name or UUID if Name is
127 String tenantName = netElements.getSrcEp().getTenant().getValue();
128 // Find existing RSP based on following naming convention, else create it.
129 RspName rspName = new RspName(sfcPath.getName().getValue() + tenantName + "-gbp-rsp");
130 ReadOnlyTransaction rTx = ctx.getDataBroker().newReadOnlyTransaction();
131 RenderedServicePath renderedServicePath;
132 RenderedServicePath rsp = getRspByName(rspName, rTx);
133 returnVnId = (long) resolveTunnelId(netElements, false);
135 renderedServicePath = createRsp(sfcPath, rspName);
136 if (renderedServicePath != null) {
137 LOG.info("updateAction: Could not find RSP {} for Chain {}, created.", rspName, chainName);
139 LOG.error("updateAction: Could not create RSP {} for Chain {}", rspName, chainName);
143 renderedServicePath = rsp;
147 if (sfcPath.isSymmetric() && direction.equals(Direction.Out)) {
148 rspName = new RspName(rspName.getValue() + "-Reverse");
149 rsp = getRspByName(rspName, rTx);
150 returnVnId = (long) resolveTunnelId(netElements, true);
152 LOG.info("updateAction: Could not find Reverse RSP {} for Chain {}", rspName, chainName);
153 renderedServicePath = createSymmetricRsp(renderedServicePath);
154 if (renderedServicePath == null) {
155 LOG.error("updateAction: Could not create RSP {} for Chain {}", rspName, chainName);
159 renderedServicePath = rsp;
162 } catch (Exception e) {
163 LOG.error("updateAction: Attempting to determine if srcEp {} was consumer.", netElements.getSrcEp().getKey(),
168 RenderedServicePathFirstHop rspFirstHop = SfcProviderRenderedPathAPI.readRenderedServicePathFirstHop(rspName);
169 if (!isValidRspFirstHop(rspFirstHop)) {
170 // Errors logged in method.
174 NodeId tunnelDestNodeId = netElements.getDstNodeId();
176 IpAddress tunnelDest = ctx.getSwitchManager().getTunnelIP(tunnelDestNodeId, TunnelTypeVxlanGpe.class);
177 if (tunnelDest == null || tunnelDest.getIpv4Address() == null) {
178 LOG.error("updateAction: Invalid tunnelDest for NodeId: {}", tunnelDestNodeId);
182 RenderedServicePathHop firstRspHop = renderedServicePath.getRenderedServicePathHop().get(0);
183 RenderedServicePathHop lastRspHop = Iterables.getLast(renderedServicePath.getRenderedServicePathHop());
184 SfcNshHeader sfcNshHeader = new SfcNshHeaderBuilder().setNshTunIpDst(rspFirstHop.getIp().getIpv4Address())
185 .setNshTunUdpPort(rspFirstHop.getPort())
186 .setNshNsiToChain(firstRspHop.getServiceIndex())
187 .setNshNspToChain(renderedServicePath.getPathId())
188 .setNshNsiFromChain((short) (lastRspHop.getServiceIndex().intValue() - 1))
189 .setNshNspFromChain(renderedServicePath.getPathId())
190 .setNshMetaC1(SfcNshHeader.convertIpAddressToLong(tunnelDest.getIpv4Address()))
191 .setNshMetaC2(returnVnId)
194 // Cannot set all actions here. Some actions are destination specific, and we don't know
195 // a destination is to be
196 // chained until we reach this point. Need to write match/action in External Table for
198 actions = addActionBuilder(actions, nxSetNsiAction(sfcNshHeader.getNshNsiToChain()), order);
199 actions = addActionBuilder(actions, nxSetNspAction(sfcNshHeader.getNshNspToChain()), order);
200 createChainTunnelFlows(sfcNshHeader, netElements, ofWriter, ctx);
204 // Return tunnelId according to policy direction
205 private int resolveTunnelId(NetworkElements netElements, boolean isReversedPath) {
206 if ((isReversedPath && PolicyEnforcer.checkPolicyOrientation())
207 || (!isReversedPath && !PolicyEnforcer.checkPolicyOrientation())) {
208 return netElements.getDstEpOrdinals().getTunnelId();
210 return netElements.getSrcEpOrdinals().getTunnelId();
214 private RenderedServicePath createRsp(ServiceFunctionPath sfcPath, RspName rspName) {
215 CreateRenderedPathInput rspInput =
216 new CreateRenderedPathInputBuilder().setParentServiceFunctionPath(sfcPath.getName().getValue())
217 .setName(rspName.getValue())
218 .setSymmetric(sfcPath.isSymmetric())
220 return SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfcPath, rspInput);
223 private RenderedServicePath createSymmetricRsp(RenderedServicePath rsp) {
227 return SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
230 private boolean isValidRspFirstHop(RenderedServicePathFirstHop rspFirstHop) {
231 boolean valid = true;
232 if (rspFirstHop == null) {
233 LOG.error("isValidRspFirstHop: rspFirstHop is null.");
236 if (rspFirstHop.getIp() == null || rspFirstHop.getIp().getIpv4Address() == null
237 || rspFirstHop.getIp().getIpv6Address() != null) {
238 LOG.error("isValidRspFirstHop: rspFirstHop has invalid IP address.");
241 if (rspFirstHop.getPort() == null) {
242 LOG.error("isValidRspFirstHop: rspFirstHop has no IP port .");
245 if (rspFirstHop.getPathId() == null) {
246 LOG.error("isValidRspFirstHop: rspFirstHop has no Path Id (NSP).");
249 if (rspFirstHop.getStartingIndex() == null) {
250 LOG.error("isValidRspFirstHop: rspFirstHop has no Starting Index (NSI)");
256 private RenderedServicePath getRspByName(RspName rspName, ReadOnlyTransaction rTx) {
257 Optional<RenderedServicePath> optRsp =
258 DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, SfcIidFactory.rspIid(rspName), rTx);
259 if (optRsp.isPresent()) {
266 public ValidationResult validate(ActionInstance actionInstance) {
267 return isValidGbpChain(actionInstance.getParameterValue());
270 private ValidationResult isValidGbpChain(List<ParameterValue> paramValue) {
271 ParameterValue pv = getChainNameParameter(paramValue);
273 return new ValidationResultBuilder().failed().setMessage(
274 "Chain parameter {" + paramValue + "} not found!").build();
276 SfcName sfcName = new SfcName(pv.getStringValue());
277 LOG.trace("isValidGbpChain: Invoking RPC for chain {}", pv.getStringValue());
278 ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(sfcName);
280 return new ValidationResultBuilder().success().build();
282 return new ValidationResultBuilder().failed().setMessage(
283 "Chain named {" + pv.getStringValue() + "} not found in config DS.").build();
287 public ServiceFunctionPath getSfcPath(SfcName chainName) {
288 ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
289 for (ServiceFunctionPath path : paths.getServiceFunctionPath()) {
290 if (path.getServiceChainName().equals(chainName)) {
297 private ParameterValue getChainNameParameter(List<ParameterValue> paramValueList) {
298 if (paramValueList == null)
300 for (ParameterValue pv : paramValueList) {
301 if (pv.getName().getValue().equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
308 private List<ActionBuilder> addActionBuilder(List<ActionBuilder> actions,
309 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action action, Integer order) {
310 ActionBuilder ab = new ActionBuilder();
311 ab.setAction(action);
318 public List<SupportedParameterValues> getSupportedParameterValues() {
319 // supported parameter does not contain parameter type - it means all strings are supported
320 return ImmutableList.<SupportedParameterValues>of(new SupportedParameterValuesBuilder()
321 .setParameterName(new ParameterName(ChainActionDefinition.SFC_CHAIN_NAME)).build());