OVSDB QoS - move QoS and Queue lists to OVSDB Node
[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.Constants;
16 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
17 import org.opendaylight.ovsdb.openstack.netvirt.api.OvsdbTables;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
19 import org.opendaylight.ovsdb.openstack.netvirt.sfc.INetvirtSfcOF13Provider;
20 import org.opendaylight.ovsdb.openstack.netvirt.sfc.ISfcClassifierService;
21 import org.opendaylight.ovsdb.openstack.netvirt.sfc.NshUtils;
22 import org.opendaylight.ovsdb.openstack.netvirt.sfc.SfcUtils;
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.sfc.provider.api.SfcProviderServiceForwarderAPI;
27 import org.opendaylight.sfc.provider.api.SfcProviderServiceFunctionAPI;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace;
39 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;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.acl.rev150105.RedirectToSfc;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.Bridges;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
51 import org.osgi.framework.ServiceReference;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider {
56     private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcWorkaroundOF13Provider.class);
57     private volatile NodeCacheManager nodeCacheManager;
58     private volatile Southbound southbound;
59     private volatile ISfcClassifierService sfcClassifierService;
60     private static final short SFC_TABLE = 150;
61     private MdsalUtils mdsalUtils;
62     private SfcUtils sfcUtils;
63     private static final String VXGPE = "vxgpe";
64     public static final String TUNNEL_ENDPOINT_KEY = "local_ip";
65
66     public NetvirtSfcWorkaroundOF13Provider(final DataBroker dataBroker, MdsalUtils mdsalUtils, SfcUtils sfcUtils) {
67         Preconditions.checkNotNull(dataBroker, "Input dataBroker cannot be NULL!");
68         Preconditions.checkNotNull(mdsalUtils, "Input mdsalUtils cannot be NULL!");
69         Preconditions.checkNotNull(sfcUtils, "Input sfcUtils cannot be NULL!");
70
71         this.mdsalUtils = mdsalUtils;
72         this.sfcUtils = sfcUtils;
73         //this.setDependencies(null);
74     }
75
76     public void setSfcClassifierService(ISfcClassifierService sfcClassifierService) {
77         this.sfcClassifierService = sfcClassifierService;
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         handleRenderedServicePath(rsp, entry);
131     }
132
133     private void handleRenderedServicePath(RenderedServicePath rsp, Ace entry) {
134         LOG.info("handleRenderedServicePath: RSP: {}", rsp);
135
136         Matches matches = entry.getMatches();
137         if (matches == null) {
138             LOG.warn("processAclEntry: matches not found");
139             return;
140         }
141
142         List<RenderedServicePathHop> pathHopList = rsp.getRenderedServicePathHop();
143         if (pathHopList.isEmpty()) {
144             LOG.warn("handleRenderedServicePath: RSP {} has empty hops!!", rsp.getName());
145             return;
146         }
147         LOG.info("handleRenderedServicePath: pathHopList: {}", pathHopList);
148
149         RenderedServicePathFirstHop firstRspHop = SfcProviderRenderedPathAPI
150                 .readRenderedServicePathFirstHop(rsp.getName());
151         LOG.info("handleRenderedServicePath: firstRspHop: {}", firstRspHop);
152
153         RenderedServicePathHop firstHop = pathHopList.get(0);
154         RenderedServicePathHop lastHop = pathHopList.get(pathHopList.size()-1);
155
156         final List<Node> bridgeNodes = nodeCacheManager.getBridgeNodes();
157         if (bridgeNodes == null || bridgeNodes.isEmpty()) {
158             LOG.warn("handleRenderedServicePath: There are no bridges to process");
159             return;
160         }
161         for (RenderedServicePathHop hop : pathHopList) {
162             for (Node bridgeNode : bridgeNodes) {
163                 // ignore bridges other than br-int
164                 OvsdbBridgeAugmentation ovsdbBridgeAugmentation = southbound.getBridge(bridgeNode, "br-int");
165                 if (ovsdbBridgeAugmentation == null) {
166                     continue;
167                 }
168                 long vxGpeOfPort = getOFPort(bridgeNode, VXGPE);
169                 if (vxGpeOfPort == 0L) {
170                     LOG.warn("programAclEntry: Could not identify gpe vtep {} -> OF ({}) on {}",
171                             VXGPE, vxGpeOfPort, bridgeNode);
172                     continue;
173                 }
174                 long dataPathId = southbound.getDataPathId(bridgeNode);
175                 if (dataPathId == 0L) {
176                     LOG.warn("programAclEntry: Could not identify datapathId on {}", bridgeNode);
177                     continue;
178                 }
179
180                 ServiceFunction serviceFunction =
181                         SfcProviderServiceFunctionAPI.readServiceFunction(firstHop.getServiceFunctionName());
182                 if (serviceFunction == null) {
183                     LOG.warn("programAclEntry: Could not identify ServiceFunction {} on {}",
184                             firstHop.getServiceFunctionName().getValue(), bridgeNode);
185                     continue;
186                 }
187                 ServiceFunctionForwarder serviceFunctionForwarder =
188                         SfcProviderServiceForwarderAPI
189                                 .readServiceFunctionForwarder(hop.getServiceFunctionForwarder());
190                 if (serviceFunctionForwarder == null) {
191                     LOG.warn("programAclEntry: Could not identify ServiceFunctionForwarder {} on {}",
192                             firstHop.getServiceFunctionName().getValue(), bridgeNode);
193                     continue;
194                 }
195
196                 handleSf(bridgeNode, serviceFunction);
197                 handleSff(bridgeNode, serviceFunctionForwarder, serviceFunction, hop, firstHop, lastHop,
198                         entry.getRuleName(), matches, vxGpeOfPort, rsp);
199                 if (firstHop == lastHop) {
200                     handleSff(bridgeNode, serviceFunctionForwarder, serviceFunction, hop, null, lastHop,
201                             entry.getRuleName(), matches, vxGpeOfPort, rsp);
202                 }
203             }
204         }
205     }
206
207     private void handleSff(Node bridgeNode, ServiceFunctionForwarder serviceFunctionForwarder,
208                            ServiceFunction serviceFunction,
209                            RenderedServicePathHop hop,
210                            RenderedServicePathHop firstHop,
211                            RenderedServicePathHop lastHop,
212                            String ruleName, Matches matches,
213                            long vxGpeOfPort, RenderedServicePath rsp) {
214         long dataPathId = southbound.getDataPathId(bridgeNode);
215
216         if (hop == firstHop) {
217             LOG.info("handleSff: first hop processing {} - {}",
218                     bridgeNode.getNodeId(), serviceFunctionForwarder.getName());
219             NshUtils nshHeader = new NshUtils();
220             nshHeader.setNshNsp(rsp.getPathId());
221             nshHeader.setNshNsi(firstHop.getServiceIndex());
222             if (isSffOnBridge(bridgeNode, serviceFunctionForwarder)) {
223                 LOG.info("handleSff: sff and bridge are the same: {} - {}, skipping first sff",
224                         bridgeNode.getNodeId(), serviceFunctionForwarder.getName());
225                 Ip ip = sfcUtils.getSfIp(serviceFunction);
226                 nshHeader.setNshTunIpDst(ip.getIp().getIpv4Address());
227                 nshHeader.setNshTunUdpPort(ip.getPort());
228             } else {
229                 LOG.info("handleSff: sff and bridge are not the same: {} - {}, sending to first sff",
230                         bridgeNode.getNodeId(), serviceFunctionForwarder.getName());
231                 Ip ip = sfcUtils.getSffIp(serviceFunctionForwarder);
232                 nshHeader.setNshTunIpDst(ip.getIp().getIpv4Address());
233                 nshHeader.setNshTunUdpPort(ip.getPort());
234             }
235             sfcClassifierService.programIngressClassifier(dataPathId, ruleName, matches,
236                     nshHeader, vxGpeOfPort, true);
237         } else if (hop == lastHop) {
238             LOG.info("handleSff: last hop processing {} - {}",
239                     bridgeNode.getNodeId(), serviceFunctionForwarder.getName());
240             short lastServiceindex = (short)((lastHop.getServiceIndex()).intValue() - 1);
241             String sfDplName = sfcUtils.getSfDplName(serviceFunction);
242             long sfOfPort = getSfPort(bridgeNode, sfDplName);
243             sfcClassifierService.programEgressClassifier(dataPathId, vxGpeOfPort, rsp.getPathId(),
244                     lastServiceindex, sfOfPort, 0, true);
245             sfcClassifierService.programEgressClassifierBypass(dataPathId, vxGpeOfPort, rsp.getPathId(),
246                     lastServiceindex, sfOfPort, 0, true);
247         } else {
248             // add typical sff flows
249         }
250
251         sfcClassifierService.programSfcTable(dataPathId, vxGpeOfPort, SFC_TABLE, true);
252     }
253
254     void handleSf(Node bridgeNode, ServiceFunction serviceFunction) {
255         if (isSfOnBridge(bridgeNode, serviceFunction)) {
256             LOG.info("handleSf: sf and bridge are on the same node: {} - {}, adding workaround and arp",
257                     bridgeNode.getNodeId(), serviceFunction.getName());
258             long dataPathId = southbound.getDataPathId(bridgeNode);
259             Ip ip = sfcUtils.getSfIp(serviceFunction);
260             String sfIpAddr = String.valueOf(ip.getIp().getValue());
261             int sfIpPort = ip.getPort().getValue(); //GPE_PORT
262             String sfDplName = sfcUtils.getSfDplName(serviceFunction);
263             long sfOfPort = getSfPort(bridgeNode, sfDplName);
264             String sfMac = getMacFromExternalIds(bridgeNode, sfDplName);
265             if (sfMac == null) {
266                 LOG.warn("handleSff: could not find mac for {} on {}", sfDplName, bridgeNode);
267                 return;
268             }
269             //should be sffdplport, but they should all be the same 6633/4790
270             sfcClassifierService.program_sfEgress(dataPathId, sfIpPort, true);
271             sfcClassifierService.program_sfIngress(dataPathId, sfIpPort, sfOfPort, sfIpAddr, sfDplName, true);
272             sfcClassifierService.programStaticArpEntry(dataPathId, 0L, sfMac, sfIpAddr, true);
273         }
274     }
275
276     private boolean isSffOnBridge(Node bridgeNode, ServiceFunctionForwarder serviceFunctionForwarder) {
277         String local_ip = "";
278         Ip ip = sfcUtils.getSffIp(serviceFunctionForwarder);
279         Node ovsdbNode = southbound.readOvsdbNode(bridgeNode);
280         if (ovsdbNode != null) {
281             OvsdbNodeAugmentation ovsdbNodeAugmentation = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
282             if (ovsdbNodeAugmentation != null && ovsdbNodeAugmentation.getOpenvswitchOtherConfigs() != null) {
283                 southbound.getOtherConfig(ovsdbNode, OvsdbTables.OPENVSWITCH, TUNNEL_ENDPOINT_KEY);
284             }
285
286         }
287         return local_ip.equals(String.valueOf(ip.getIp().getValue()));
288     }
289
290     private boolean isSfOnBridge(Node bridgeNode, ServiceFunction serviceFunction) {
291         String sfDplName = sfcUtils.getSfDplName(serviceFunction);
292         long sfOfPort = getSfPort(bridgeNode, sfDplName);
293         return sfOfPort != 0L;
294     }
295
296     private RenderedServicePath getRenderedServicePath (Ace entry) {
297         RenderedServicePath rsp = null;
298         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
299         LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
300         if (sfcRedirect == null) {
301             LOG.warn("processAClEntry: sfcRedirect is null");
302             return null;
303         }
304
305         if (sfcRedirect.getRspName() != null) {
306             rsp = getRenderedServicePathFromRsp(sfcRedirect.getRspName());
307         } else if (sfcRedirect.getSfpName() != null) {
308             LOG.warn("getRenderedServicePath: sfp not handled yet");
309         } else {
310             rsp = getRenderedServicePathFromSfc(entry);
311         }
312         LOG.info("getRenderedServicePath: rsp: {}", rsp);
313         return rsp;
314     }
315
316     private RenderedServicePath getRenderedServicePathFromRsp(String rspName) {
317         return sfcUtils.getRsp(rspName);
318     }
319
320     private RenderedServicePath getRenderedServicePathFromSfc (Ace entry) {
321         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
322         LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
323         if (sfcRedirect == null) {
324             LOG.warn("processAClEntry: sfcRedirect is null");
325             return null;
326         }
327
328         String sfcName = sfcRedirect.getSfcName();
329         ServiceFunctionPath sfp = sfcUtils.getSfp(sfcName);
330         if (sfp == null || sfp.getName() == null) {
331             LOG.warn("There is no configured SFP with sfcName = {}; so skip installing the ACL entry!!", sfcName);
332             return null;
333         }
334
335         LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcName, sfp);
336         // If RSP doesn't exist, create an RSP.
337         String sfpName = sfp.getName().getValue();
338         RenderedServicePath rsp = sfcUtils.getRspforSfp(sfpName);
339         String rspName = sfp.getName().getValue() + "_rsp";
340         if (rsp == null) {
341             LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}", sfpName, rspName);
342             CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
343                     .setParentServiceFunctionPath(sfpName)
344                     .setName(rspName)
345                     .setSymmetric(sfp.isSymmetric())
346                     .build();
347             rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
348             if (rsp == null) {
349                 LOG.warn("failed to add RSP");
350                 return null;
351             }
352
353             // If SFP is symmetric, create RSP in the reverse direction.
354             if (sfp.isSymmetric()) {
355                 LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
356                 String rspNameRev = rspName + "-Reverse";
357                 RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
358                         sfcUtils.getRspId(rspNameRev));
359                 if (rspReverse == null) {
360                     rspReverse = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
361                     if (rspReverse == null) {
362                         LOG.warn("failed to add reverse RSP");
363                         return null;
364                     }
365                 }
366             }
367         }
368         return rsp;
369     }
370
371     private long getSfPort(Node bridgeNode, String sfPortName) {
372         return getOFPort(bridgeNode, sfPortName);
373     }
374
375     private long getOFPort(Node bridgeNode, String portName) {
376         long ofPort = 0L;
377         OvsdbTerminationPointAugmentation port =
378                 southbound.extractTerminationPointAugmentation(bridgeNode, portName);
379         if (port != null) {
380             ofPort = southbound.getOFPort(port);
381         }
382         if (ofPort == 0L) {
383             for (int i = 0; i < 5; i++) {
384                 LOG.info("Looking for ofPort {}, try: {}", portName, i);
385                 TerminationPoint tp = southbound.readTerminationPoint(bridgeNode, null, portName);
386                 if (tp != null) {
387                     port = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
388                     if (port != null) {
389                         ofPort = southbound.getOFPort(port);
390                         break;
391                     }
392                 }
393                 try {
394                     Thread.sleep(1000);
395                 } catch (InterruptedException e) {
396                     e.printStackTrace();
397                 }
398             }
399         }
400         return ofPort;
401     }
402
403     private String getMacFromExternalIds(Node bridgeNode, String portName) {
404         String mac = null;
405         OvsdbTerminationPointAugmentation port = southbound.getTerminationPointOfBridge(bridgeNode, portName);
406         LOG.info("getMac: portName: {}, bridgeNode: {},,, port: {}", portName, bridgeNode, port);
407         if (port != null && port.getInterfaceExternalIds() != null) {
408             mac = southbound.getInterfaceExternalIdsValue(port, Constants.EXTERNAL_ID_VM_MAC);
409         }
410         return mac;
411     }
412
413     @Override
414     public void setDependencies(ServiceReference serviceReference) {
415         nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
416         southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
417         sfcClassifierService =
418                 (ISfcClassifierService) ServiceHelper.getGlobalInstance(ISfcClassifierService.class, this);
419         LOG.info("sfcClassifierService= {}", sfcClassifierService);
420     }
421 }