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