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