Extra route fixes
[vpnservice.git] / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / vpnservice / VpnManager.java
1 /*
2  * Copyright (c) 2015 - 2016 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.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.List;
13 import java.util.concurrent.*;
14
15 import com.google.common.util.concurrent.CheckedFuture;
16 import org.opendaylight.bgpmanager.api.IBgpManager;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
21 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
22 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnIdBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnRouteList;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
29 import org.opendaylight.yangtools.concepts.ListenerRegistration;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
33 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
34 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
35 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
36 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
37 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
38 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
39 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder;
40 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpDataBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.vrfentries.VrfEntry;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import com.google.common.base.Optional;
52 import com.google.common.util.concurrent.FutureCallback;
53 import com.google.common.util.concurrent.Futures;
54
55 public class VpnManager extends AbstractDataChangeListener<VpnInstance> implements AutoCloseable {
56     private static final Logger LOG = LoggerFactory.getLogger(VpnManager.class);
57     private ListenerRegistration<DataChangeListener> listenerRegistration, fibListenerRegistration, opListenerRegistration;
58     private ConcurrentMap<String, Runnable> vpnOpMap = new ConcurrentHashMap<String, Runnable>();
59     private ExecutorService executorService = Executors.newSingleThreadExecutor();
60     private final DataBroker broker;
61     private final IBgpManager bgpManager;
62     private IdManagerService idManager;
63     private VpnInterfaceManager vpnInterfaceManager;
64     private final FibEntriesListener fibListener;
65     private final VpnInstanceOpListener vpnInstOpListener;
66
67     private static final FutureCallback<Void> DEFAULT_CALLBACK =
68             new FutureCallback<Void>() {
69                 public void onSuccess(Void result) {
70                     LOG.debug("Success in Datastore operation");
71                 }
72
73                 public void onFailure(Throwable error) {
74                     LOG.error("Error in Datastore operation", error);
75                 };
76             };
77
78     /**
79      * Listens for data change related to VPN Instance
80      * Informs the BGP about VRF information
81      *
82      * @param db - dataBroker reference
83      */
84     public VpnManager(final DataBroker db, final IBgpManager bgpManager) {
85         super(VpnInstance.class);
86         broker = db;
87         this.bgpManager = bgpManager;
88         this.fibListener = new FibEntriesListener();
89         this.vpnInstOpListener = new VpnInstanceOpListener();
90         registerListener(db);
91     }
92
93     private void registerListener(final DataBroker db) {
94         try {
95             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
96                     getWildCardPath(), VpnManager.this, DataChangeScope.SUBTREE);
97             fibListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
98                     getFibEntryListenerPath(), fibListener, DataChangeScope.BASE);
99             opListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
100                     getVpnInstanceOpListenerPath(), vpnInstOpListener, DataChangeScope.SUBTREE);
101
102         } catch (final Exception e) {
103             LOG.error("VPN Service DataChange listener registration fail !", e);
104             throw new IllegalStateException("VPN Service registration Listener failed.", e);
105         }
106     }
107
108     public void setIdManager(IdManagerService idManager) {
109         this.idManager = idManager;
110     }
111
112     public void setVpnInterfaceManager(VpnInterfaceManager vpnInterfaceManager) {
113         this.vpnInterfaceManager = vpnInterfaceManager;
114     }
115
116     private void waitForOpDataRemoval(String id) {
117         //wait till DCN for update on VPN Instance Op Data signals that vpn interfaces linked to this vpn instance is zero
118         Runnable notifyTask = new VpnNotifyTask();
119         synchronized (id.intern()) {
120             vpnOpMap.put(id, notifyTask);
121             synchronized (notifyTask) {
122                 try {
123                     notifyTask.wait(VpnConstants.WAIT_TIME_IN_MILLISECONDS);
124                 } catch (InterruptedException e) {
125                 }
126             }
127         }
128
129     }
130
131     @Override
132     protected void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
133         LOG.trace("Remove VPN event - Key: {}, value: {}", identifier, del);
134         String vpnName = del.getVpnInstanceName();
135
136         //Clean up vpn Interface
137         InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
138         Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.OPERATIONAL, vpnInterfacesId);
139
140         if(optionalVpnInterfaces.isPresent()) {
141             List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
142             for(VpnInterface vpnInterface : vpnInterfaces) {
143                 if(vpnInterface.getVpnInstanceName().equals(vpnName)) {
144                     LOG.debug("VpnInterface {} will be removed from VPN {}", vpnInterface.getName(), vpnName);
145                     vpnInterfaceManager.remove(
146                             VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
147                 }
148             }
149         }
150         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
151             vpnIdentifier = VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnName);
152         delete(LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
153
154         VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
155         String rd = del.getIpv4Family().getRouteDistinguisher();
156
157         if (rd !=null) {
158
159             try {
160                 bgpManager.deleteVrf(rd);
161             } catch (Exception e) {
162                 LOG.error("Exception when removing VRF from BGP", e);
163             }
164             waitForOpDataRemoval(rd);
165             delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd));
166         } else {
167             waitForOpDataRemoval(vpnName);
168             delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
169         }
170     }
171
172     @Override
173     protected void update(InstanceIdentifier<VpnInstance> identifier,
174             VpnInstance original, VpnInstance update) {
175         LOG.trace("Update event - Key: {}, value: {}", identifier, update);
176     }
177
178     @Override
179     protected void add(InstanceIdentifier<VpnInstance> identifier,
180             VpnInstance value) {
181         LOG.trace("VPN Instance key: {}, value: {}", identifier, value);
182         VpnAfConfig config = value.getIpv4Family();
183         String rd = config.getRouteDistinguisher();
184
185         long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, value.getVpnInstanceName());
186         LOG.trace("VPN instance to ID generated.");
187         org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
188             vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(value.getVpnInstanceName(), vpnId,
189                                                                     (rd != null) ? rd : value.getVpnInstanceName());
190
191         syncWrite(LogicalDatastoreType.CONFIGURATION,
192                    VpnUtil.getVpnInstanceToVpnIdIdentifier(value.getVpnInstanceName()),
193                    vpnInstanceToVpnId, DEFAULT_CALLBACK);
194
195
196         if(rd == null) {
197             syncWrite(LogicalDatastoreType.OPERATIONAL,
198                     VpnUtil.getVpnInstanceOpDataIdentifier(value.getVpnInstanceName()),
199                     VpnUtil.getVpnInstanceOpDataBuilder(value.getVpnInstanceName(), vpnId), DEFAULT_CALLBACK);
200
201         } else {
202             syncWrite(LogicalDatastoreType.OPERATIONAL,
203                        VpnUtil.getVpnInstanceOpDataIdentifier(rd),
204                        VpnUtil.getVpnInstanceOpDataBuilder(rd, vpnId), DEFAULT_CALLBACK);
205
206             List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
207
208             List<String> ertList = new ArrayList<String>();
209             List<String> irtList = new ArrayList<String>();
210
211             for (VpnTarget vpnTarget : vpnTargetList) {
212                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
213                     ertList.add(vpnTarget.getVrfRTValue());
214                 }
215                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
216                     irtList.add(vpnTarget.getVrfRTValue());
217                 }
218                 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
219                     ertList.add(vpnTarget.getVrfRTValue());
220                     irtList.add(vpnTarget.getVrfRTValue());
221                 }
222             }
223
224             try {
225                 bgpManager.addVrf(rd, irtList, ertList);
226             } catch(Exception e) {
227                 LOG.error("Exception when adding VRF to BGP", e);
228             }
229         }
230         //Try to add up vpn Interfaces if already in Operational Datastore
231         LOG.trace("Trying to add the vpn interfaces  -1.");
232         InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
233         Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
234
235         if(optionalVpnInterfaces.isPresent()) {
236             List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
237             for(VpnInterface vpnInterface : vpnInterfaces) {
238                 if(vpnInterface.getVpnInstanceName().equals(value.getVpnInstanceName())) {
239                     LOG.debug("VpnInterface {} will be added from VPN {}", vpnInterface.getName(), value.getVpnInstanceName());
240                     vpnInterfaceManager.add(
241                                 VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
242
243                 }
244             }
245         }
246     }
247
248     private InstanceIdentifier<?> getWildCardPath() {
249         return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class);
250     }
251
252     private InstanceIdentifier<?> getFibEntryListenerPath() {
253         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class)
254                 .child(VrfEntry.class);
255     }
256
257     private InstanceIdentifier<?> getVpnInstanceOpListenerPath() {
258         return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
259
260     }
261
262     @Override
263     public void close() throws Exception {
264         if (listenerRegistration != null) {
265             try {
266                 listenerRegistration.close();
267             } catch (final Exception e) {
268                 LOG.error("Error when cleaning up Vpn DataChangeListener.", e);
269             }
270             listenerRegistration = null;
271         }
272         if (fibListenerRegistration != null) {
273             try {
274                 fibListenerRegistration.close();
275             } catch (final Exception e) {
276                 LOG.error("Error when cleaning up Fib entries DataChangeListener.", e);
277             }
278             fibListenerRegistration = null;
279         }
280         if (opListenerRegistration != null) {
281             try {
282                 opListenerRegistration.close();
283             } catch (final Exception e) {
284                 LOG.error("Error when cleaning up VPN Instance Operational entries DataChangeListener.", e);
285             }
286             opListenerRegistration = null;
287         }
288
289         LOG.trace("VPN Manager Closed");
290     }
291
292     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
293             InstanceIdentifier<T> path) {
294
295         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
296
297         Optional<T> result = Optional.absent();
298         try {
299             result = tx.read(datastoreType, path).get();
300         } catch (Exception e) {
301             throw new RuntimeException(e);
302         }
303
304         return result;
305     }
306
307     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
308             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
309         WriteTransaction tx = broker.newWriteOnlyTransaction();
310         tx.put(datastoreType, path, data, true);
311         Futures.addCallback(tx.submit(), callback);
312     }
313
314     private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
315                                                    InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
316         WriteTransaction tx = broker.newWriteOnlyTransaction();
317         tx.put(datastoreType, path, data, true);
318         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
319         try {
320             futures.get();
321         } catch (InterruptedException | ExecutionException e) {
322             LOG.error("Error writing VPN instance to ID info to datastore (path, data) : ({}, {})", path, data);
323             throw new RuntimeException(e.getMessage());
324         }
325     }
326
327     protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) {
328         InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
329         Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = read(LogicalDatastoreType.OPERATIONAL, id);
330         if(vpnInstanceOpData.isPresent()) {
331             return vpnInstanceOpData.get();
332         }
333         return null;
334     }
335
336     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
337         WriteTransaction tx = broker.newWriteOnlyTransaction();
338         tx.delete(datastoreType, path);
339         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
340     }
341
342     private class FibEntriesListener extends AbstractDataChangeListener<VrfEntry>  {
343
344         public FibEntriesListener() {
345             super(VrfEntry.class);
346         }
347
348         @Override
349         protected void remove(InstanceIdentifier<VrfEntry> identifier,
350                 VrfEntry del) {
351             LOG.trace("Remove Fib event - Key : {}, value : {} ", identifier, del);
352             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
353             String rd = key.getRouteDistinguisher();
354             Long label = del.getLabel();
355             VpnInstanceOpDataEntry vpnInstanceOpData = getVpnInstanceOpData(rd);
356             if(vpnInstanceOpData != null) {
357                 List<Long> routeIds = vpnInstanceOpData.getRouteEntryId();
358                 if(routeIds == null) {
359                     LOG.debug("Fib Route entry is empty.");
360                     return;
361                 }
362                 LOG.debug("Removing label from vpn info - {}", label);
363                 routeIds.remove(label);
364                 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
365                            new VpnInstanceOpDataEntryBuilder(vpnInstanceOpData).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
366             } else {
367                 LOG.warn("No VPN Instance found for RD: {}", rd);
368             }
369         }
370
371         @Override
372         protected void update(InstanceIdentifier<VrfEntry> identifier,
373                 VrfEntry original, VrfEntry update) {
374             // TODO Auto-generated method stub
375
376         }
377
378         @Override
379         protected void add(InstanceIdentifier<VrfEntry> identifier,
380                            VrfEntry add) {
381             LOG.trace("Add Vrf Entry event - Key : {}, value : {}", identifier, add);
382             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
383             String rd = key.getRouteDistinguisher();
384             Long label = add.getLabel();
385             VpnInstanceOpDataEntry vpn = getVpnInstanceOpData(rd);
386             if(vpn != null) {
387                 List<Long> routeIds = vpn.getRouteEntryId();
388                 if(routeIds == null) {
389                     routeIds = new ArrayList<>();
390                 }
391                 LOG.debug("Adding label to vpn info - {}", label);
392                 routeIds.add(label);
393                 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
394                            new VpnInstanceOpDataEntryBuilder(vpn).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
395             } else {
396                 LOG.warn("No VPN Instance found for RD: {}", rd);
397             }
398         }
399     }
400
401     class VpnInstanceOpListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener<VpnInstanceOpDataEntry> {
402
403         public VpnInstanceOpListener() {
404             super(VpnInstanceOpDataEntry.class);
405         }
406
407         @Override
408         protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry del) {
409
410         }
411
412         @Override
413         protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
414             final VpnInstanceOpDataEntryKey key = identifier.firstKeyOf(VpnInstanceOpDataEntry.class, VpnInstanceOpDataEntryKey.class);
415             String vpnName = key.getVrfId();
416
417             LOG.trace("VpnInstanceOpListener update: vpn name {} interface count in Old VpnOp Instance {} in New VpnOp Instance {}" ,
418                             vpnName, original.getVpnInterfaceCount(), update.getVpnInterfaceCount() );
419
420             //if((original.getVpnToDpnList().size() != update.getVpnToDpnList().size()) && (update.getVpnToDpnList().size() == 0)) {
421             if((original.getVpnInterfaceCount() != update.getVpnInterfaceCount()) && (update.getVpnInterfaceCount() == 0)) {
422                 notifyTaskIfRequired(vpnName);
423             }
424         }
425
426         private void notifyTaskIfRequired(String vpnName) {
427             Runnable notifyTask = vpnOpMap.remove(vpnName);
428             if (notifyTask == null) {
429                 return;
430             }
431             executorService.execute(notifyTask);
432         }
433
434         @Override
435         protected void add(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry add) {
436         }
437     }
438 }