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