d480f6dfed58eff479e08d0deeb6feb336a5b689
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / sf / ChainAction.java
1 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
2
3 /*
4  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
5  *
6  * This program and the accompanying materials are made available under the
7  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
8  * and is available at http://www.eclipse.org/legal/epl-v10.html
9  */
10
11 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadNshc1RegAction;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadNshc2RegAction;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIPv4Action;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIdAction;
15 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxSetNsiAction;
16 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxSetNspAction;
17 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.outputAction;
18
19 import java.math.BigInteger;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.NetworkElements;
24 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
25 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
26 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
27 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.IsRequired;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.Type;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.ParameterBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionBuilder;
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.inventory.rev130819.NodeConnectorId;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 import com.google.common.collect.ImmutableList;
57 import com.google.common.net.InetAddresses;
58
59 /**
60  * Chain action for the OpenFlow Overlay renderer
61  * TODO: separate the generic definition from the concrete
62  * implementation for the OpenFlow Ovelray renderer
63  */
64 public class ChainAction extends Action {
65
66     private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class);
67
68     public static final ActionDefinitionId ID = new ActionDefinitionId("3d886be7-059f-4c4f-bbef-0356bea40933");
69
70     public static final Integer CHAIN_CONDITION_GROUP = 0xfffffe;
71
72     protected static final String TYPE = "type";
73
74     // the chain action
75     public static final String SFC_CHAIN_ACTION = "chain";
76     // the parameter used for storing the chain name
77     public static final String SFC_CHAIN_NAME = "sfc-chain-name";
78
79     protected static final ActionDefinition DEF = new ActionDefinitionBuilder().setId(ID)
80         .setName(new ActionName(SFC_CHAIN_ACTION))
81         .setDescription(new Description("Send the traffic through a Service Function Chain"))
82         .setParameter(
83                 (ImmutableList.of(new ParameterBuilder().setName(new ParameterName(SFC_CHAIN_NAME))
84                     .setDescription(new Description("The named chain to match against"))
85                     .setIsRequired(IsRequired.Required)
86                     .setType(Type.String)
87                     .build())))
88         .build();
89
90     @Override
91     public ActionDefinitionId getId() {
92         return ID;
93     }
94
95     @Override
96     public ActionDefinition getActionDef() {
97         return DEF;
98     }
99
100     @Override
101     public List<ActionBuilder> updateAction(List<ActionBuilder> actions,
102                                             Map<String, Object> params,
103                                             Integer order,
104                                             NetworkElements netElements) {
105         /*
106          * Get the named chain
107          */
108         ServiceFunctionPath sfcPath = null;
109         String chainName = null;
110         if (params != null) {
111             LOG.debug("Searching for named chain");
112             for (String name : params.keySet()) {
113                 if (name instanceof String) {
114                     if (name.equals(SFC_CHAIN_NAME)) {
115                         chainName = (String) params.get(name);
116                         if (chainName == null) {
117                             LOG.error("ChainAction: Chain name was null");
118                             return null;
119                         }
120                         sfcPath = getSfcPath(chainName);
121                     }
122                 }
123             }
124         } else {
125             LOG.error("ChainAction: Parameters null for chain action");
126             return null;
127         }
128
129         if (sfcPath == null) {
130             LOG.error("ChainAction: SFC Path null for chain {}", chainName);
131             return null;
132         }
133         String rspName = sfcPath.getName() + "-gbp-rsp";
134         RenderedServicePathFirstHop rspFirstHop = SfcProviderRenderedPathAPI.readRenderedServicePathFirstHop(rspName);
135         if (rspFirstHop == null) {
136             LOG.info("ChainAction: Could not find RSP {} for Chain {}", rspName, chainName);
137
138             CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder().setParentServiceFunctionPath(
139                     sfcPath.getName())
140                 .setName(rspName)
141                 .setSymmetric(Boolean.FALSE)
142                 .build();
143             RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(
144                     sfcPath, rspInput);
145             if (renderedServicePath == null) {
146                 LOG.error("Could not find or create RSP for chain {}", chainName);
147                 return null;
148             }
149             rspFirstHop=SfcProviderRenderedPathAPI.readRenderedServicePathFirstHop(renderedServicePath.getName());
150         }
151
152         IpAddress sfcTunIpDst = rspFirstHop.getIp();
153         sfcTunIpDst.getIpv4Address();
154         if (sfcTunIpDst == null || sfcTunIpDst.getIpv4Address() == null || sfcTunIpDst.getIpv6Address() != null) {
155             LOG.error("Invalid IP Tunnel destination for SFC RSP First Hop {}", rspName);
156             return null;
157         }
158         PortNumber sfcTunUdpPort = rspFirstHop.getPort();
159         if (sfcTunUdpPort == null) {
160             LOG.error("Invalid UDP Port Number for SFC RSP {}", rspName);
161             return null;
162         }
163         Long sfcNsp = rspFirstHop.getPathId();
164         if (sfcNsp == null) {
165             LOG.error("Invalid NSP for SFC RSP {}", rspName);
166             return null;
167         }
168         Short sfcNsi = rspFirstHop.getStartingIndex();
169         if (sfcNsi == null) {
170             LOG.error("Invalid NSI for SFC RSP {}", rspName);
171             return null;
172         }
173
174         NodeConnectorId tunOpenFlowPort = SwitchManager.getTunnelPort(netElements.getNodeId(), TunnelTypeVxlanGpe.class);
175
176         /*
177          * Setting NSH Network Context Headers for post-SFC encapsulation
178          * VXLAN header encap:
179          * - TunnelDestination IP: NSH C1
180          * - Tunnel ID (VNID) NSH C2
181          */
182         long postSfcTunnelDst = 999L;
183         IpAddress tunnelDest;
184
185         if (netElements.getDst().getAugmentation(OfOverlayContext.class).getNodeId().equals(netElements.getNodeId())) {
186             // Return destination is here
187             tunnelDest=SwitchManager.getTunnelIP(netElements.getNodeId(), TunnelTypeVxlanGpe.class);
188         } else {
189             tunnelDest=SwitchManager.getTunnelIP(netElements.getDst().getAugmentation(OfOverlayContext.class).getNodeId(), TunnelTypeVxlanGpe.class);
190         }
191         postSfcTunnelDst = (InetAddresses.coerceToInteger(InetAddresses.forString(tunnelDest.getIpv4Address().getValue()))) & 0xFFFFFFFFL;
192
193         // TunnelDestination after Chain
194         actions = addActionBuilder(actions, nxLoadNshc1RegAction(postSfcTunnelDst), order++);
195         // VNID after Chain
196         actions = addActionBuilder(actions, nxLoadNshc2RegAction((long) netElements.getSrcOrds().getTunnelId()), order++);
197
198         /*
199          * Set the tunnel destination IP
200          */
201         if (sfcTunIpDst.getIpv4Address() != null) {
202             String nextHop = sfcTunIpDst.getIpv4Address().getValue();
203             actions = addActionBuilder(actions, nxLoadTunIPv4Action(nextHop, false), order);
204         } else if (sfcTunIpDst.getIpv6Address() != null) {
205             LOG.error("IPv6 tunnel destination {} not supported", sfcTunIpDst.getIpv6Address().getValue());
206             return actions;
207         } else {
208             // this shouldn't happen
209             LOG.error("Tunnel IP is invalid");
210             return actions;
211         }
212
213         /*
214          * Put TunID - with NSH we don't really care about this.
215          */
216         actions = addActionBuilder(actions,
217                 nxLoadTunIdAction(BigInteger.valueOf(netElements.getSrcOrds().getTunnelId()), false), order);
218
219         /*
220          * Set the NSH header fields, based on RSP
221          */
222          actions = addActionBuilder(actions,nxSetNsiAction(sfcNsi),order);
223          actions = addActionBuilder(actions,nxSetNspAction(sfcNsp),order);
224          /*
225          * Set up the actions to send to the destination port
226          */
227          actions = addActionBuilder(actions,outputAction(tunOpenFlowPort), order);
228
229         return actions;
230     }
231
232     private List<ActionBuilder> addActionBuilder(List<ActionBuilder> actions,
233             org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action action, Integer order) {
234         ActionBuilder ab = new ActionBuilder();
235         ab.setAction(action);
236         ab.setOrder(order);
237         actions.add(ab);
238         return actions;
239     }
240
241     @Override
242     public boolean isValid(ActionInstance actionInstance) {
243         return validChain(actionInstance.getParameterValue());
244     }
245
246     private boolean validChain(List<ParameterValue> paramValue) {
247         ParameterValue pv = getChainNameParameter(paramValue);
248         if (pv == null) {
249             return false;
250         }
251         LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
252         ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(pv.getStringValue());
253         return chain != null;
254     }
255
256     public ServiceFunctionPath getSfcPath(String chainName) {
257
258         ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
259         for (ServiceFunctionPath path : paths.getServiceFunctionPath()) {
260             if (path.getServiceChainName().equals(chainName)) {
261                 return path;
262             }
263         }
264         return null;
265     }
266
267     private ParameterValue getChainNameParameter(List<ParameterValue> paramValueList) {
268         if (paramValueList == null)
269             return null;
270         for (ParameterValue pv : paramValueList) {
271             if (pv.getName().getValue().equals(SFC_CHAIN_NAME)) {
272                 return pv;
273             }
274         }
275         return null;
276     }
277
278 }