Fix OF pipeline for local nexthop
[vpnservice.git] / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / vpnservice / VpnInterfaceManager.java
1 /*
2  * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.vpnservice;
9
10 import java.math.BigInteger;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.ArrayList;
16 import java.util.Map;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
20
21 import com.google.common.base.Optional;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.FutureCallback;
25
26 import org.opendaylight.bgpmanager.api.IBgpManager;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
29 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
30 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
31 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
32 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
33 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
34 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
35 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
36 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
37 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
44 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
45 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
46 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
58 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
60 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
61 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
62 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
63 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
64 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
65 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
66 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
72     private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
73     private ListenerRegistration<DataChangeListener> listenerRegistration;
74     private final DataBroker broker;
75     private final IBgpManager bgpManager;
76     private IMdsalApiManager mdsalManager;
77     private IInterfaceManager interfaceManager;
78     private IdManagerService idManager;
79     private Map<Long, Collection<Long>> vpnToDpnsDb;
80     private Map<Long, Collection<String>> dpnToInterfaceDb;
81
82     private static final FutureCallback<Void> DEFAULT_CALLBACK =
83             new FutureCallback<Void>() {
84                 public void onSuccess(Void result) {
85                     LOG.debug("Success in Datastore operation");
86                 }
87
88                 public void onFailure(Throwable error) {
89                     LOG.error("Error in Datastore operation", error);
90                 };
91             };
92
93     /**
94      * Responsible for listening to data change related to VPN Interface
95      * Bind VPN Service on the interface and informs the BGP service
96      * 
97      * @param db - dataBroker service reference
98      */
99     public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
100         super(VpnInterface.class);
101         broker = db;
102         this.bgpManager = bgpManager;
103         vpnToDpnsDb = new ConcurrentHashMap<>();
104         dpnToInterfaceDb = new ConcurrentHashMap<>();
105         registerListener(db);
106     }
107
108     public void setMdsalManager(IMdsalApiManager mdsalManager) {
109         this.mdsalManager = mdsalManager;
110     }
111
112     public void setInterfaceManager(IInterfaceManager interfaceManager) {
113         this.interfaceManager = interfaceManager;
114     }
115
116     public void setIdManager(IdManagerService idManager) {
117         this.idManager = idManager;
118     }
119
120     @Override
121     public void close() throws Exception {
122         if (listenerRegistration != null) {
123             try {
124                 listenerRegistration.close();
125             } catch (final Exception e) {
126                 LOG.error("Error when cleaning up DataChangeListener.", e);
127             }
128             listenerRegistration = null;
129         }
130         LOG.info("VPN Interface Manager Closed");
131     }
132
133     private void registerListener(final DataBroker db) {
134         try {
135             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
136                     getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
137         } catch (final Exception e) {
138             LOG.error("VPN Service DataChange listener registration fail!", e);
139             throw new IllegalStateException("VPN Service registration Listener failed.", e);
140         }
141     }
142
143     @Override
144     protected void add(final InstanceIdentifier<VpnInterface> identifier,
145             final VpnInterface vpnInterface) {
146         LOG.trace("key: {} , value: {}", identifier, vpnInterface );
147         addInterface(identifier, vpnInterface);
148     }
149
150     private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
151                               final VpnInterface vpnInterface) {
152         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
153         String interfaceName = key.getName();
154         InstanceIdentifierBuilder<Interface> idBuilder = 
155                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
156         InstanceIdentifier<Interface> id = idBuilder.build();
157         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
158         if (port.isPresent()) {
159             Interface interf = port.get();
160             bindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
161             updateNextHops(identifier, vpnInterface);
162         }
163     }
164
165     private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
166         //Read NextHops
167         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
168         Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
169         String intfName = intf.getName();
170
171         if (adjacencies.isPresent()) {
172             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
173             List<Adjacency> value = new ArrayList<>();
174
175             //Get the rd of the vpn instance
176             String rd = getRouteDistinguisher(intf.getVpnInstanceName());
177
178             long dpnId = interfaceManager.getDpnForInterface(intfName);
179             String nextHopIp = interfaceManager.getEndpointIpForDpn(dpnId);
180
181
182             LOG.trace("NextHops are {}", nextHops);
183             for (Adjacency nextHop : nextHops) {
184                 String key = nextHop.getIpAddress();
185                 long label = getUniqueId(key);
186                 value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
187             }
188
189             Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
190             VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
191             InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
192             asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
193             for (Adjacency nextHop : nextHops) {
194                 String key = nextHop.getIpAddress();
195                 long label = getUniqueId(key);
196                 updatePrefixToBGP(rd, nextHop, nextHopIp, label);
197             }
198         }
199     }
200
201     private Integer getUniqueId(String idKey) {
202         GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
203                                            .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
204                                            .setIdKey(idKey).build();
205
206         try {
207             Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
208             RpcResult<GetUniqueIdOutput> rpcResult = result.get();
209             if(rpcResult.isSuccessful()) {
210                 return rpcResult.getResult().getIdValue().intValue();
211             } else {
212                 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
213             }
214         } catch (NullPointerException | InterruptedException | ExecutionException e) {
215             LOG.warn("Exception when getting Unique Id",e);
216         }
217         return 0;
218     }
219
220     private long getVpnId(String vpnName) {
221         //TODO: This should be a Util function
222         InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
223                 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
224         Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.OPERATIONAL, id);
225         //TODO: Default vpnid should be a constant.
226         long vpnId = -1;
227         if(vpnInstance.isPresent()) {
228             vpnId = vpnInstance.get().getVpnId();
229         }
230         return vpnId;
231     }
232
233     private String getRouteDistinguisher(String vpnName) {
234         InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
235                                       .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
236         Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
237         String rd = "";
238         if(vpnInstance.isPresent()) {
239             VpnInstance instance = vpnInstance.get();
240             VpnAfConfig config = instance.getIpv4Family();
241             rd = config.getRouteDistinguisher();
242         }
243         return rd;
244     }
245
246     private synchronized void updateMappingDbs(long vpnId, long dpnId, String intfName) {
247         Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
248         if(dpnIds == null) {
249             dpnIds = new HashSet<>();
250         }
251         if(dpnIds.add(dpnId)) {
252             vpnToDpnsDb.put(vpnId, dpnIds);
253             //TODO: Send an Event that new DPN added...
254         }
255
256         Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
257         if(intfNames == null) {
258             intfNames = new ArrayList<>();
259         }
260         intfNames.add(intfName);
261         dpnToInterfaceDb.put(dpnId, intfNames);
262     }
263
264     private synchronized void remoteFromMappingDbs(long vpnId, long dpnId, String inftName) {
265         Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
266         if(intfNames == null) {
267             return;
268         }
269         intfNames.remove(inftName);
270         dpnToInterfaceDb.put(dpnId, intfNames);
271         if(intfNames.isEmpty()) {
272             Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
273             if(dpnIds == null) {
274                 return;
275             }
276             dpnIds.remove(dpnId);
277             vpnToDpnsDb.put(vpnId, dpnIds);
278         }
279     }
280
281     private void bindServiceOnInterface(Interface intf, long vpnId) {
282         LOG.trace("Bind service on interface {} for VPN: {}", intf, vpnId);
283
284         long dpId = interfaceManager.getDpnForInterface(intf.getName()); 
285         if(dpId == 0L) {
286             LOG.warn("DPN for interface {} not found. Bind service on this interface aborted.", intf.getName());
287             return;
288         } else {
289             updateMappingDbs(vpnId, dpId, intf.getName());
290         }
291
292         long portNo = interfaceManager.getPortForInterface(intf.getName());
293         String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
294
295         String flowName = intf.getName();
296         BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
297
298         int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
299         short gotoTableId = VpnConstants.FIB_TABLE;
300
301         List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
302         mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
303                 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
304
305         mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
306
307         List<MatchInfo> matches = new ArrayList<MatchInfo>();
308         matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
309                 dpId, portNo }));
310
311         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
312                           priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
313
314         mdsalManager.installFlow(flowEntity);
315     }
316
317     private String getVpnInterfaceFlowRef(long dpId, short tableId,
318             long vpnId, long portNo) {
319         return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
320     }
321
322     private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp, long label) {
323         try {
324             bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
325         } catch(Exception e) {
326             LOG.error("Add prefix failed", e);
327         }
328     }
329
330     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
331             InstanceIdentifier<T> path) {
332
333         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
334
335         Optional<T> result = Optional.absent();
336         try {
337             result = tx.read(datastoreType, path).get();
338         } catch (Exception e) {
339             throw new RuntimeException(e);
340         }
341
342         return result;
343     }
344
345     private InstanceIdentifier<VpnInterface> getWildCardPath() {
346         return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
347     }
348
349     @Override
350     protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
351         LOG.trace("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
352         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
353         String interfaceName = key.getName();
354         InstanceIdentifierBuilder<Interface> idBuilder = 
355                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
356         InstanceIdentifier<Interface> id = idBuilder.build();
357         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
358         if (port.isPresent()) {
359             Interface interf = port.get();
360             removeNextHops(identifier, vpnInterface);
361             unbindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
362         } else {
363             LOG.warn("No nexthops were available to handle remove event {}", interfaceName);
364         }
365     }
366
367     private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
368         //Read NextHops
369         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
370         Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
371         String intfName = intf.getName();
372         String rd = getRouteDistinguisher(intf.getVpnInstanceName());
373         if (adjacencies.isPresent()) {
374             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
375
376             if (!nextHops.isEmpty()) {
377                 LOG.trace("NextHops are " + nextHops);
378                 for (Adjacency nextHop : nextHops) {
379                     removePrefixFromBGP(rd, nextHop);
380                 }
381             }
382         }
383         InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
384         delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
385     }
386
387     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
388         WriteTransaction tx = broker.newWriteOnlyTransaction();
389         tx.delete(datastoreType, path);
390         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
391     }
392
393     private void unbindServiceOnInterface(Interface intf, long vpnId) {
394         LOG.trace("Unbind service on interface {} for VPN: {}", intf, vpnId);
395
396         long dpId = interfaceManager.getDpnForInterface(intf.getName());
397         if(dpId == 0L) {
398             LOG.warn("DPN for interface {} not found. Unbind service on this interface aborted.", intf.getName());
399             return;
400         } else {
401             remoteFromMappingDbs(vpnId, dpId, intf.getName());
402         }
403
404         long portNo = interfaceManager.getPortForInterface(intf.getName());
405         String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
406
407         String flowName = intf.getName();
408
409         int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
410
411         List<MatchInfo> matches = new ArrayList<MatchInfo>();
412         matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
413                 dpId, portNo }));
414
415         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
416                           priority, flowName, 0, 0, null, matches, null);
417
418         mdsalManager.removeFlow(flowEntity);
419     }
420
421     private void removePrefixFromBGP(String rd, Adjacency nextHop) {
422         //public void deletePrefix(String rd, String prefix) throws Exception;
423         try {
424             bgpManager.deletePrefix(rd, nextHop.getIpAddress());
425         } catch(Exception e) {
426             LOG.error("Delete prefix failed", e);
427         }
428     }
429
430     @Override
431     protected void update(InstanceIdentifier<VpnInterface> identifier, 
432                                    VpnInterface original, VpnInterface update) {
433         // TODO Auto-generated method stub
434
435     }
436
437     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
438                         InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
439         WriteTransaction tx = broker.newWriteOnlyTransaction();
440         tx.put(datastoreType, path, data, true);
441         Futures.addCallback(tx.submit(), callback);
442     }
443
444     synchronized Collection<Long> getDpnsForVpn(long vpnId) {
445         Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
446         if(dpnIds != null) {
447             return ImmutableList.copyOf(dpnIds);
448         } else {
449             return Collections.emptyList();
450         }
451     }
452 }