workaround for sfc
[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.utils.mdsal.utils.MdsalUtils;
23 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
24 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
25 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
26 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace;
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.Matches;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.acl.rev150105.RedirectToSfc;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.Bridges;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
46 import org.osgi.framework.ServiceReference;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider {
51     private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcWorkaroundOF13Provider.class);
52     private volatile NodeCacheManager nodeCacheManager;
53     private volatile Southbound southbound;
54     private volatile ISfcClassifierService sfcClassifierService;
55
56     public void setSfcClassifierService(ISfcClassifierService sfcClassifierService) {
57         this.sfcClassifierService = sfcClassifierService;
58     }
59
60     private MdsalUtils mdsalUtils;
61     private SfcUtils sfcUtils;
62     private static final String VXGPE = "vxgpe";
63     private static final String TUNNEL_DST = "192.168.120.31";
64     private static final String TUNNEL_VNID = "10";
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     @Override
77     public void addClassifierRules(Bridge bridge, Acl acl) {
78
79     }
80
81     @Override
82     public void addClassifierRules(Bridges bridges, Acl acl) {
83         Preconditions.checkNotNull(bridges, "Input bridges cannot be NULL!");
84         Preconditions.checkNotNull(acl, "Input acl cannot be NULL!");
85     }
86
87     @Override
88     public void removeClassifierRules(Sff sff, Acl acl) {
89
90     }
91
92     @Override
93     public void addClassifierRules(Acl acl) {
94         String aclName = acl.getAclName();
95         Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, sfcUtils.getClassifierIid());
96         if (classifiers == null) {
97             LOG.debug("addClassifierRules: No Classifiers found");
98             return;
99         }
100
101         LOG.debug("addClassifierRules: Classifiers: {}", classifiers);
102         for (Classifier classifier : classifiers.getClassifier()) {
103             if (classifier.getAcl().equals(aclName)) {
104                 for (Ace ace : acl.getAccessListEntries().getAce()) {
105                     processAclEntry(ace);
106                 }
107             }
108         }
109     }
110
111     @Override
112     public void removeClassifierRules(Acl acl) {
113
114     }
115
116     private void processAclEntry(Ace entry) {
117         Matches matches = entry.getMatches();
118         Preconditions.checkNotNull(matches, "Input bridges cannot be NULL!");
119
120         RenderedServicePath rsp = getRenderedServicePath(entry);
121         if (rsp == null) {
122             LOG.warn("Failed to get renderedServicePatch for entry: {}", entry);
123             return;
124         }
125
126         handleIngressClassifier(rsp, entry);
127         //handleEgressClassifier();
128         //handleSfLoopback();
129     }
130
131     private void handleIngressClassifier(RenderedServicePath rsp, Ace entry) {
132         LOG.info("handleIngressClassifier: RSP: {}", rsp);
133
134         Matches matches = entry.getMatches();
135         if (matches == null) {
136             LOG.warn("processAclEntry: matches not found");
137             return;
138         }
139
140         List<RenderedServicePathHop> pathHopList = rsp.getRenderedServicePathHop();
141         if (pathHopList.isEmpty()) {
142             LOG.warn("handleIngressClassifier: RSP {} has empty hops!!", rsp.getName());
143             return;
144         }
145         LOG.info("handleIngressClassifier: pathHopList: {}", pathHopList);
146
147         final List<Node> bridgeNodes = nodeCacheManager.getBridgeNodes();
148         if (bridgeNodes == null || bridgeNodes.isEmpty()) {
149             LOG.warn("handleIngressClassifier: There are no bridges to process");
150             return;
151         }
152         for (Node bridgeNode : bridgeNodes) {
153             OvsdbBridgeAugmentation ovsdbBridgeAugmentation = southbound.getBridge(bridgeNode, "br-int");
154             if (ovsdbBridgeAugmentation == null) {
155                 continue;
156             }
157             long vxGpeOfPort = southbound.getOFPort(bridgeNode, VXGPE);
158             if (vxGpeOfPort == 0L) {
159                 LOG.warn("programAclEntry: Could not identify tunnel port {} -> OF ({}) on {}",
160                         VXGPE, vxGpeOfPort, bridgeNode);
161                 continue;
162             }
163
164             // Find the first Hop within an RSP.
165             // The classifier flow needs to send all matched traffic to this first hop SFF.
166             RenderedServicePathFirstHop firstRspHop = SfcProviderRenderedPathAPI
167                     .readRenderedServicePathFirstHop(rsp.getName());
168
169             LOG.info("handleIngressClassifier: firstRspHop: {}", firstRspHop);
170             LOG.debug("handleIngressClassifier: First Hop IPAddress = {}, Port = {}",
171                     firstRspHop.getIp().getIpv4Address().getValue(),
172                     firstRspHop.getPort().getValue().intValue());
173
174             NshUtils nshHeader = new NshUtils();
175             nshHeader.setNshMetaC1(NshUtils.convertIpAddressToLong(new Ipv4Address(TUNNEL_DST)));
176             nshHeader.setNshMetaC2(Long.parseLong(TUNNEL_VNID));
177             nshHeader.setNshNsp(rsp.getPathId());
178
179             RenderedServicePathHop firstHop = pathHopList.get(0);
180             nshHeader.setNshNsi(firstHop.getServiceIndex());
181             // workaround: bypass sff and got directly to sf
182             //nshHeader.setNshTunIpDst(firstRspHop.getIp().getIpv4Address());
183             IpAddress sfIpAddress = sfcUtils.getSfIp(firstHop.getServiceFunctionName().getValue());
184             nshHeader.setNshTunIpDst(sfIpAddress.getIpv4Address());
185             nshHeader.setNshTunUdpPort(firstRspHop.getPort());
186             LOG.debug("handleIngressClassifier: NSH Header = {}", nshHeader);
187
188             sfcClassifierService.programIngressClassifier(
189                     southbound.getDataPathId(bridgeNode), entry.getRuleName(),
190                     matches, nshHeader, vxGpeOfPort, true);
191         }
192     }
193
194     // loop through sf's:
195     // - program arp responder
196     // - program sf to sff
197     // - program sff to sf
198     private void handleSfWorkaround(RenderedServicePath rsp) {
199
200     }
201
202
203     private RenderedServicePath getRenderedServicePath (Ace entry) {
204         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
205         LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
206         if (sfcRedirect == null) {
207             LOG.warn("processAClEntry: sfcRedirect is null");
208             return null;
209         }
210
211         String sfcName = sfcRedirect.getRedirectSfc();
212         ServiceFunctionPath sfp = sfcUtils.getSfp(sfcName);
213         if (sfp == null || sfp.getName() == null) {
214             LOG.warn("There is no configured SFP with sfcName = {}; so skip installing the ACL entry!!", sfcName);
215             return null;
216         }
217
218         LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcRedirect.getRedirectSfc(), sfp);
219         // If RSP doesn't exist, create an RSP.
220         String sfpName = sfp.getName().getValue();
221         RenderedServicePath rsp = sfcUtils.getRspforSfp(sfpName);
222         String rspName = sfp.getName().getValue() + "_rsp";
223         if (rsp == null) {
224             LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}", sfpName, rspName);
225             CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
226                     .setParentServiceFunctionPath(sfpName)
227                     .setName(rspName)
228                     .setSymmetric(sfp.isSymmetric())
229                     .build();
230             rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
231             if (rsp == null) {
232                 LOG.warn("failed to add RSP");
233                 return null;
234             }
235
236             // If SFP is symmetric, create RSP in the reverse direction.
237             if (sfp.isSymmetric()) {
238                 LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
239                 String rspNameRev = rspName + "-Reverse";
240                 RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
241                         sfcUtils.getRspId(rspNameRev));
242                 if (rspReverse == null) {
243                     rspReverse = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
244                     if (rspReverse == null) {
245                         LOG.warn("failed to add reverse RSP");
246                         return null;
247                     }
248                 }
249             }
250         }
251         return rsp;
252     }
253
254     public Long getOFPort(Node bridgeNode, String portName) {
255         Long ofPort = 0L;
256         OvsdbTerminationPointAugmentation port = southbound.extractTerminationPointAugmentation(bridgeNode, portName);
257         if (port != null) {
258             ofPort = southbound.getOFPort(port);
259         }
260         for (int i = 0; i < 5; i++) {
261             LOG.info("Looking for ofPort {}, try: {}", portName, i);
262             if (ofPort == 0L) {
263                 TerminationPoint tp = southbound.readTerminationPoint(bridgeNode, null, portName);
264                 if (tp != null) {
265                     port = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
266                     if (port != null) {
267                         ofPort = southbound.getOFPort(port);
268                         break;
269                     }
270                 }
271             }
272             try {
273                 Thread.sleep(1000);
274             } catch (InterruptedException e) {
275                 e.printStackTrace();
276             }
277         }
278         return ofPort;
279     }
280
281     @Override
282     public void setDependencies(ServiceReference serviceReference) {
283         nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
284         southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
285         sfcClassifierService = (ISfcClassifierService) ServiceHelper.getGlobalInstance(ISfcClassifierService.class, this);
286         LOG.info("sfcClassifierService= {}", sfcClassifierService);
287     }
288 }