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