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