73b1e908d08e9db5a66126736cdbb41fe220efce
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / sf / ChainAction.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
10
11 /*
12  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
13  *
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
17  */
18
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;
22
23 import java.util.List;
24 import java.util.Map;
25
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.OfWriter;
29 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
30 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.NetworkElements;
31 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.PolicyPair;
32 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcIidFactory;
33 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader;
34 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader.SfcNshHeaderBuilder;
35 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
36 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
37 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
38 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
39 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
40 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
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.policy.rev140421.HasDirection.Direction;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.IsRequired;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.Type;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.ParameterBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 import com.google.common.base.Optional;
69 import com.google.common.collect.ImmutableList;
70 import com.google.common.collect.Iterables;
71
72 /**
73  * Chain action for the OpenFlow Overlay renderer
74  * TODO: separate the generic definition from the concrete
75  * implementation for the OpenFlow Overlay renderer
76  */
77 public class ChainAction extends Action {
78
79     private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class);
80
81     public static final ActionDefinitionId ID = new ActionDefinitionId("3d886be7-059f-4c4f-bbef-0356bea40933");
82
83     public static final Integer CHAIN_CONDITION_GROUP = 0xfffffe;
84
85     protected static final String TYPE = "type";
86
87     // the chain action
88     public static final String SFC_CHAIN_ACTION = "chain";
89     // the parameter used for storing the chain name
90     public static final String SFC_CHAIN_NAME = "sfc-chain-name";
91
92     protected static final ActionDefinition DEF = new ActionDefinitionBuilder().setId(ID)
93         .setName(new ActionName(SFC_CHAIN_ACTION))
94         .setDescription(new Description("Send the traffic through a Service Function Chain"))
95         .setParameter(
96                 (ImmutableList.of(new ParameterBuilder().setName(new ParameterName(SFC_CHAIN_NAME))
97                     .setDescription(new Description("The named chain to match against"))
98                     .setIsRequired(IsRequired.Required)
99                     .setType(Type.String)
100                     .build())))
101         .build();
102
103     @Override
104     public ActionDefinitionId getId() {
105         return ID;
106     }
107
108     @Override
109     public ActionDefinition getActionDef() {
110         return DEF;
111     }
112
113     @Override
114     public List<ActionBuilder> updateAction(List<ActionBuilder> actions, Map<String, Object> params, Integer order,
115             NetworkElements netElements, PolicyPair policyPair, OfWriter ofWriter, OfContext ctx, Direction direction) {
116         /*
117          * Get the named chain
118          */
119         String chainName = null;
120         if (params != null) {
121             LOG.debug("updateAction: Searching for named chain");
122             for (String name : params.keySet()) {
123                 if (name instanceof String) {
124                     if (name.equals(SFC_CHAIN_NAME)) {
125                         chainName = (String) params.get(name);
126                         if (chainName == null) {
127                             LOG.error("updateAction: Chain name was null");
128                             return null;
129                         }
130                     }
131                 }
132             }
133         } else {
134             LOG.error("updateAction: Parameters null for chain action");
135             return null;
136         }
137
138         if (chainName == null) {
139             LOG.error("updateAction: Chain name was null");
140             return null;
141         }
142
143         /*
144          * If path is symmetrical then there are two RSPs.
145          * if srcEp is in consumer EPG use "rspName"
146          * else srcEp is in provider EPG, "rspName-Reverse".
147          */
148         ServiceFunctionPath sfcPath = getSfcPath(new SfcName(chainName));
149         if (sfcPath == null || sfcPath.getName() == null) {
150             LOG.error("updateAction: SFC Path was invalid. Either null or name was null.", sfcPath);
151             return null;
152         }
153         // Find existing RSP based on following naming convention, else create it.
154         RspName rspName = new RspName(sfcPath.getName() + "-gbp-rsp");
155         ReadOnlyTransaction rTx = ctx.getDataBroker().newReadOnlyTransaction();
156         RenderedServicePath renderedServicePath;
157         RenderedServicePath rsp = getRspByName(rspName, rTx);
158         if (rsp == null) {
159             renderedServicePath = createRsp(sfcPath, rspName);
160             if (renderedServicePath != null) {
161                 LOG.info("updateAction: Could not find RSP {} for Chain {}, created.", rspName, chainName);
162             } else {
163                 LOG.error("updateAction: Could not create RSP {} for Chain {}", rspName, chainName);
164                 return null;
165             }
166         } else {
167             renderedServicePath = rsp;
168         }
169
170         try {
171             if (sfcPath.isSymmetric() && direction.equals(Direction.Out)){
172                 rspName = new RspName(rspName.getValue() + "-Reverse");
173                 rsp = getRspByName(rspName, rTx);
174                 if (rsp == null) {
175                     LOG.info("updateAction: Could not find Reverse RSP {} for Chain {}", rspName, chainName);
176                     renderedServicePath = createSymmetricRsp(renderedServicePath);
177                     if (renderedServicePath == null) {
178                         LOG.error("updateAction: Could not create RSP {} for Chain {}", rspName, chainName);
179                         return null;
180                     }
181                 } else {
182                     renderedServicePath = rsp;
183                 }
184             }
185         } catch (Exception e) {
186             LOG.error("updateAction: Attemping to determine if srcEp {} was consumer.", netElements.getSrcEp().getKey(), e);
187             return null;
188         }
189
190         RenderedServicePathFirstHop rspFirstHop = SfcProviderRenderedPathAPI.readRenderedServicePathFirstHop(rspName);
191         if (!isValidRspFirstHop(rspFirstHop)) {
192             // Errors logged in method.
193             return null;
194         }
195
196         NodeId tunnelDestNodeId=netElements.getDstNodeId();
197
198         Long returnVnid = (long) netElements.getSrcEpOrds().getTunnelId();
199
200         IpAddress tunnelDest = ctx.getSwitchManager().getTunnelIP(tunnelDestNodeId, TunnelTypeVxlanGpe.class);
201         if (tunnelDest == null || tunnelDest.getIpv4Address() == null) {
202             LOG.error("updateAction: Invalid tunnelDest for NodeId: {}", tunnelDestNodeId);
203             return null;
204         }
205
206         RenderedServicePathHop firstRspHop = renderedServicePath.getRenderedServicePathHop().get(0);
207         RenderedServicePathHop lastRspHop = Iterables.getLast(renderedServicePath.getRenderedServicePathHop());
208         SfcNshHeader sfcNshHeader = new SfcNshHeaderBuilder().setNshTunIpDst(rspFirstHop.getIp().getIpv4Address())
209             .setNshTunUdpPort(rspFirstHop.getPort())
210             .setNshNsiToChain(firstRspHop.getServiceIndex())
211             .setNshNspToChain(renderedServicePath.getPathId())
212             .setNshNsiFromChain((short) (lastRspHop.getServiceIndex().intValue() - 1))
213             .setNshNspFromChain(renderedServicePath.getPathId())
214             .setNshMetaC1(SfcNshHeader.convertIpAddressToLong(tunnelDest.getIpv4Address()))
215             .setNshMetaC2(returnVnid)
216             .build();
217
218         // Cannot set all actions here. Some actions are destination specific, and we don't know
219         // a destination is to be
220         // chained until we reach this point. Need to write match/action in External Table for
221         // chained packets.
222         actions = addActionBuilder(actions, nxSetNsiAction(sfcNshHeader.getNshNsiToChain()), order);
223         actions = addActionBuilder(actions, nxSetNspAction(sfcNshHeader.getNshNspToChain()), order);
224         createChainTunnelFlows(sfcNshHeader, netElements, ofWriter, ctx);
225         return actions;
226     }
227
228     private RenderedServicePath createRsp(ServiceFunctionPath sfcPath, RspName rspName) {
229         CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder().setParentServiceFunctionPath(
230                 sfcPath.getName().getValue())
231             .setName(rspName.getValue())
232             .setSymmetric(sfcPath.isSymmetric())
233             .build();
234          return SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfcPath, rspInput);
235     }
236
237     private RenderedServicePath createSymmetricRsp(RenderedServicePath rsp) {
238         if (rsp == null) {
239             return null;
240         }
241         return SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
242     }
243
244     private boolean isValidRspFirstHop(RenderedServicePathFirstHop rspFirstHop) {
245         boolean valid = true;
246         if (rspFirstHop == null) {
247             LOG.error("isValidRspFirstHop: rspFirstHop is null.");
248             return false;
249         }
250         if (rspFirstHop.getIp() == null || rspFirstHop.getIp().getIpv4Address() == null
251                 || rspFirstHop.getIp().getIpv6Address() != null) {
252             LOG.error("isValidRspFirstHop: rspFirstHop has invalid IP address.");
253             valid = false;
254         }
255         if (rspFirstHop.getPort() == null) {
256             LOG.error("isValidRspFirstHop: rspFirstHop has no IP port .");
257             valid = false;
258         }
259         if (rspFirstHop.getPathId() == null) {
260             LOG.error("isValidRspFirstHop: rspFirstHop has no Path Id (NSP).");
261             valid = false;
262         }
263         if (rspFirstHop.getStartingIndex() == null) {
264             LOG.error("isValidRspFirstHop: rspFirstHop has no Starting Index (NSI)");
265             valid = false;
266         }
267         return valid;
268     }
269
270     private RenderedServicePath getRspByName(RspName rspName, ReadOnlyTransaction rTx) {
271         Optional<RenderedServicePath> optRsp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
272                 SfcIidFactory.rspIid(rspName), rTx);
273         if (optRsp.isPresent()) {
274             return optRsp.get();
275         }
276         return null;
277     }
278
279     @Override
280     public boolean isValid(ActionInstance actionInstance) {
281         return isValidGbpChain(actionInstance.getParameterValue());
282     }
283
284     private boolean isValidGbpChain(List<ParameterValue> paramValue) {
285         ParameterValue pv = getChainNameParameter(paramValue);
286         if (pv == null) {
287             return false;
288         }
289         SfcName sfcName = new SfcName(pv.getStringValue());
290         LOG.trace("isValidGbpChain: Invoking RPC for chain {}", pv.getStringValue());
291         ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(sfcName);
292         return (chain != null);
293     }
294
295     public ServiceFunctionPath getSfcPath(SfcName chainName) {
296         ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
297         for (ServiceFunctionPath path : paths.getServiceFunctionPath()) {
298             if (path.getServiceChainName().equals(chainName)) {
299                 return path;
300             }
301         }
302         return null;
303     }
304
305     private ParameterValue getChainNameParameter(List<ParameterValue> paramValueList) {
306         if (paramValueList == null)
307             return null;
308         for (ParameterValue pv : paramValueList) {
309             if (pv.getName().getValue().equals(SFC_CHAIN_NAME)) {
310                 return pv;
311             }
312         }
313         return null;
314     }
315
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);
320         ab.setOrder(order);
321         actions.add(ab);
322         return actions;
323     }
324
325 }