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