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