add rest of sfc flows
[netvirt.git] / openstack / net-virt-sfc / impl / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / sfc / workaround / NetvirtSfcWorkaroundOF13Provider.java
1 /*
2  * Copyright © 2015 Red Hat, 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.ovsdb.openstack.netvirt.sfc.workaround;
10
11 import com.google.common.base.Preconditions;
12 import java.util.List;
13 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
14 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
15 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
16 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
17 import org.opendaylight.ovsdb.openstack.netvirt.sfc.INetvirtSfcOF13Provider;
18 import org.opendaylight.ovsdb.openstack.netvirt.sfc.ISfcClassifierService;
19 import org.opendaylight.ovsdb.openstack.netvirt.sfc.NshUtils;
20 import org.opendaylight.ovsdb.openstack.netvirt.sfc.SfcUtils;
21 import org.opendaylight.ovsdb.openstack.netvirt.sfc.workaround.services.SfcClassifierService;
22 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
23 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
24 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
25 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
26 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.Matches;
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.Ipv4Address;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.acl.rev150105.RedirectToSfc;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.Bridges;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
47 import org.osgi.framework.ServiceReference;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider {
52     private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcWorkaroundOF13Provider.class);
53     private volatile NodeCacheManager nodeCacheManager;
54     private volatile Southbound southbound;
55     private volatile ISfcClassifierService sfcClassifierService;
56     private static final short SFC_TABLE = 150;
57     private static final int GPE_PORT = 6633;
58     private static final String NETWORK_TYPE_VXLAN = "vxlan";
59
60     public void setSfcClassifierService(ISfcClassifierService sfcClassifierService) {
61         this.sfcClassifierService = sfcClassifierService;
62     }
63
64     private MdsalUtils mdsalUtils;
65     private SfcUtils sfcUtils;
66     private static final String VXGPE = "vxgpe";
67     private static final String TUNNEL_DST = "192.168.120.31";
68     private static final String TUNNEL_VNID = "10";
69
70     public NetvirtSfcWorkaroundOF13Provider(final DataBroker dataBroker, MdsalUtils mdsalUtils, SfcUtils sfcUtils) {
71         Preconditions.checkNotNull(dataBroker, "Input dataBroker cannot be NULL!");
72         Preconditions.checkNotNull(mdsalUtils, "Input mdsalUtils cannot be NULL!");
73         Preconditions.checkNotNull(sfcUtils, "Input sfcUtils cannot be NULL!");
74
75         this.mdsalUtils = mdsalUtils;
76         this.sfcUtils = sfcUtils;
77         //this.setDependencies(null);
78     }
79
80     @Override
81     public void addClassifierRules(Bridge bridge, Acl acl) {
82
83     }
84
85     @Override
86     public void addClassifierRules(Bridges bridges, Acl acl) {
87         Preconditions.checkNotNull(bridges, "Input bridges cannot be NULL!");
88         Preconditions.checkNotNull(acl, "Input acl cannot be NULL!");
89     }
90
91     @Override
92     public void removeClassifierRules(Sff sff, Acl acl) {
93
94     }
95
96     @Override
97     public void addClassifierRules(Acl acl) {
98         String aclName = acl.getAclName();
99         Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, sfcUtils.getClassifierIid());
100         if (classifiers == null) {
101             LOG.debug("addClassifierRules: No Classifiers found");
102             return;
103         }
104
105         LOG.debug("addClassifierRules: Classifiers: {}", classifiers);
106         for (Classifier classifier : classifiers.getClassifier()) {
107             if (classifier.getAcl().equals(aclName)) {
108                 for (Ace ace : acl.getAccessListEntries().getAce()) {
109                     processAclEntry(ace);
110                 }
111             }
112         }
113     }
114
115     @Override
116     public void removeClassifierRules(Acl acl) {
117
118     }
119
120     private void processAclEntry(Ace entry) {
121         Matches matches = entry.getMatches();
122         Preconditions.checkNotNull(matches, "Input bridges cannot be NULL!");
123
124         RenderedServicePath rsp = getRenderedServicePath(entry);
125         if (rsp == null) {
126             LOG.warn("Failed to get renderedServicePatch for entry: {}", entry);
127             return;
128         }
129
130         handleIngressClassifier(rsp, entry);
131         //handleEgressClassifier();
132         //handleSfLoopback();
133         //sfArp and ingressSfLoopback uses linux stack
134     }
135
136     private void handleIngressClassifier(RenderedServicePath rsp, Ace entry) {
137         LOG.info("handleIngressClassifier: RSP: {}", rsp);
138
139         Matches matches = entry.getMatches();
140         if (matches == null) {
141             LOG.warn("processAclEntry: matches not found");
142             return;
143         }
144
145         List<RenderedServicePathHop> pathHopList = rsp.getRenderedServicePathHop();
146         if (pathHopList.isEmpty()) {
147             LOG.warn("handleIngressClassifier: RSP {} has empty hops!!", rsp.getName());
148             return;
149         }
150         LOG.info("handleIngressClassifier: pathHopList: {}", pathHopList);
151
152         final List<Node> bridgeNodes = nodeCacheManager.getBridgeNodes();
153         if (bridgeNodes == null || bridgeNodes.isEmpty()) {
154             LOG.warn("handleIngressClassifier: There are no bridges to process");
155             return;
156         }
157         for (Node bridgeNode : bridgeNodes) {
158             OvsdbBridgeAugmentation ovsdbBridgeAugmentation = southbound.getBridge(bridgeNode, "br-int");
159             if (ovsdbBridgeAugmentation == null) {
160                 continue;
161             }
162             long vxGpeOfPort = getOFPort(bridgeNode, VXGPE);
163             if (vxGpeOfPort == 0L) {
164                 LOG.warn("programAclEntry: Could not identify tunnel port {} -> OF ({}) on {}",
165                         VXGPE, vxGpeOfPort, bridgeNode);
166                 continue;
167             }
168             long dataPathId = southbound.getDataPathId(bridgeNode);
169             if (dataPathId == 0L) {
170                 LOG.warn("programAclEntry: Could not identify datapathId on {}", bridgeNode);
171                 continue;
172             }
173
174             // Find the first Hop within an RSP.
175             // The classifier flow needs to send all matched traffic to this first hop SFF.
176             RenderedServicePathFirstHop firstRspHop = SfcProviderRenderedPathAPI
177                     .readRenderedServicePathFirstHop(rsp.getName());
178
179             LOG.info("handleIngressClassifier: firstRspHop: {}", firstRspHop);
180             LOG.debug("handleIngressClassifier: First Hop IPAddress = {}, Port = {}",
181                     firstRspHop.getIp().getIpv4Address().getValue(),
182                     firstRspHop.getPort().getValue().intValue());
183
184             NshUtils nshHeader = new NshUtils();
185             nshHeader.setNshMetaC1(NshUtils.convertIpAddressToLong(new Ipv4Address(TUNNEL_DST)));
186             nshHeader.setNshMetaC2(Long.parseLong(TUNNEL_VNID)); // get from register //get from
187             nshHeader.setNshNsp(rsp.getPathId());
188
189             RenderedServicePathHop firstHop = pathHopList.get(0);
190             RenderedServicePathHop lastHop = pathHopList.get(pathHopList.size()-1);
191             nshHeader.setNshNsi(firstHop.getServiceIndex());
192             // workaround: bypass sff and got directly to sf
193             //nshHeader.setNshTunIpDst(firstRspHop.getIp().getIpv4Address());
194             IpAddress sfIpAddress = sfcUtils.getSfIp(firstHop.getServiceFunctionName().getValue());
195             nshHeader.setNshTunIpDst(sfIpAddress.getIpv4Address());
196             nshHeader.setNshTunUdpPort(firstRspHop.getPort());
197             LOG.debug("handleIngressClassifier: NSH Header = {}", nshHeader);
198
199             sfcClassifierService.programIngressClassifier(dataPathId, entry.getRuleName(), matches,
200                     nshHeader, vxGpeOfPort, true);
201
202             sfcClassifierService.program_sfEgress(dataPathId, GPE_PORT, true);
203             //not needed if ip route and arp can be added to stack
204             //sfcClassifierService.program_sfIngress(dataPathId, true);
205             //sfcClassifierService.program_sfArp(dataPathId, true);
206
207             short lastServiceindex = (short)((lastHop.getServiceIndex()).intValue() - 1);
208             long tunnelOfPort = getTunnelOfPort(bridgeNode, VXGPE);
209             sfcClassifierService.programEgressClassifier1(dataPathId, vxGpeOfPort, rsp.getPathId(),
210                     lastServiceindex, (int)tunnelOfPort, 0, (short)0, true);
211             sfcClassifierService.programEgressClassifier2(dataPathId, vxGpeOfPort, rsp.getPathId(),
212                     lastServiceindex, (int) tunnelOfPort, 0, true);
213
214             sfcClassifierService.programSfcTable(dataPathId, vxGpeOfPort, SFC_TABLE, true);
215         }
216     }
217
218     // loop through sf's:
219     // - program arp responder
220     // - program sf to sff
221     // - program sff to sf
222     private void handleSfWorkaround(RenderedServicePath rsp) {
223
224     }
225
226     private RenderedServicePath getRenderedServicePath (Ace entry) {
227         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
228         LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
229         if (sfcRedirect == null) {
230             LOG.warn("processAClEntry: sfcRedirect is null");
231             return null;
232         }
233
234         String sfcName = sfcRedirect.getRedirectSfc();
235         ServiceFunctionPath sfp = sfcUtils.getSfp(sfcName);
236         if (sfp == null || sfp.getName() == null) {
237             LOG.warn("There is no configured SFP with sfcName = {}; so skip installing the ACL entry!!", sfcName);
238             return null;
239         }
240
241         LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcRedirect.getRedirectSfc(), sfp);
242         // If RSP doesn't exist, create an RSP.
243         String sfpName = sfp.getName().getValue();
244         RenderedServicePath rsp = sfcUtils.getRspforSfp(sfpName);
245         String rspName = sfp.getName().getValue() + "_rsp";
246         if (rsp == null) {
247             LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}", sfpName, rspName);
248             CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
249                     .setParentServiceFunctionPath(sfpName)
250                     .setName(rspName)
251                     .setSymmetric(sfp.isSymmetric())
252                     .build();
253             rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
254             if (rsp == null) {
255                 LOG.warn("failed to add RSP");
256                 return null;
257             }
258
259             // If SFP is symmetric, create RSP in the reverse direction.
260             if (sfp.isSymmetric()) {
261                 LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
262                 String rspNameRev = rspName + "-Reverse";
263                 RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
264                         sfcUtils.getRspId(rspNameRev));
265                 if (rspReverse == null) {
266                     rspReverse = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
267                     if (rspReverse == null) {
268                         LOG.warn("failed to add reverse RSP");
269                         return null;
270                     }
271                 }
272             }
273         }
274         return rsp;
275     }
276
277     // loop through all ports looking for vxlan types, skip vxgpe, keep the rest
278     // first pass we only have two tunnels: one for normal vxlan and the other for gpe
279     // so just return the first non-gpe vxlan port
280     private long getTunnelOfPort(Node bridgeNode, String vxGpePortName) {
281         long port = 0L;
282         List<OvsdbTerminationPointAugmentation> ovsdbTerminationPointAugmentations =
283                 southbound.getTerminationPointsOfBridge(bridgeNode);
284         if (!ovsdbTerminationPointAugmentations.isEmpty()) {
285             for (OvsdbTerminationPointAugmentation terminationPointAugmentation : ovsdbTerminationPointAugmentations) {
286                 if (terminationPointAugmentation.getInterfaceType() ==
287                         SouthboundConstants.OVSDB_INTERFACE_TYPE_MAP.get(NETWORK_TYPE_VXLAN)) {
288                     if (!terminationPointAugmentation.getName().equals(vxGpePortName)) {
289                         port = terminationPointAugmentation.getOfport();
290                         break;
291                     }
292                 }
293             }
294         }
295         return port;
296     }
297
298     private long getOFPort(Node bridgeNode, String portName) {
299         long ofPort = 0L;
300         OvsdbTerminationPointAugmentation port = southbound.extractTerminationPointAugmentation(bridgeNode, portName);
301         if (port != null) {
302             ofPort = southbound.getOFPort(port);
303         }
304         for (int i = 0; i < 5; i++) {
305             LOG.info("Looking for ofPort {}, try: {}", portName, i);
306             if (ofPort == 0L) {
307                 TerminationPoint tp = southbound.readTerminationPoint(bridgeNode, null, portName);
308                 if (tp != null) {
309                     port = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
310                     if (port != null) {
311                         ofPort = southbound.getOFPort(port);
312                         break;
313                     }
314                 }
315             }
316             try {
317                 Thread.sleep(1000);
318             } catch (InterruptedException e) {
319                 e.printStackTrace();
320             }
321         }
322         return ofPort;
323     }
324
325     @Override
326     public void setDependencies(ServiceReference serviceReference) {
327         nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
328         southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
329         sfcClassifierService = (ISfcClassifierService) ServiceHelper.getGlobalInstance(ISfcClassifierService.class, this);
330         LOG.info("sfcClassifierService= {}", sfcClassifierService);
331     }
332 }