Adding IdManager/LockManager and related Changes
[vpnservice.git] / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / vpnservice / VpnManager.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.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Collection;
13 import java.util.List;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.Future;
16
17 import org.opendaylight.bgpmanager.api.IBgpManager;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
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.yangtools.concepts.ListenerRegistration;
23 import org.opendaylight.yangtools.yang.binding.DataObject;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
25 import org.opendaylight.yangtools.yang.common.RpcResult;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
28 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
29 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
31 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
32 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder;
33 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1Builder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.vrfentries.VrfEntry;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import com.google.common.base.Optional;
48 import com.google.common.util.concurrent.FutureCallback;
49 import com.google.common.util.concurrent.Futures;
50
51 public class VpnManager extends AbstractDataChangeListener<VpnInstance> implements AutoCloseable {
52     private static final Logger LOG = LoggerFactory.getLogger(VpnManager.class);
53     private ListenerRegistration<DataChangeListener> listenerRegistration, fibListenerRegistration;
54     private final DataBroker broker;
55     private final IBgpManager bgpManager;
56     private IdManagerService idManager;
57     private VpnInterfaceManager vpnInterfaceManager;
58     private final FibEntriesListener fibListener;
59
60     private static final FutureCallback<Void> DEFAULT_CALLBACK =
61             new FutureCallback<Void>() {
62                 public void onSuccess(Void result) {
63                     LOG.debug("Success in Datastore operation");
64                 }
65
66                 public void onFailure(Throwable error) {
67                     LOG.error("Error in Datastore operation", error);
68                 };
69             };
70
71     /**
72      * Listens for data change related to VPN Instance
73      * Informs the BGP about VRF information
74      * 
75      * @param db - dataBroker reference
76      */
77     public VpnManager(final DataBroker db, final IBgpManager bgpManager) {
78         super(VpnInstance.class);
79         broker = db;
80         this.bgpManager = bgpManager;
81         this.fibListener = new FibEntriesListener();
82         registerListener(db);
83     }
84
85     private void registerListener(final DataBroker db) {
86         try {
87             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
88                     getWildCardPath(), VpnManager.this, DataChangeScope.SUBTREE);
89             fibListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
90                     getFibEntryListenerPath(), fibListener, DataChangeScope.BASE);
91         } catch (final Exception e) {
92             LOG.error("VPN Service DataChange listener registration fail !", e);
93             throw new IllegalStateException("VPN Service registration Listener failed.", e);
94         }
95     }
96
97     public void setIdManager(IdManagerService idManager) {
98         this.idManager = idManager;
99     }
100
101     public void setVpnInterfaceManager(VpnInterfaceManager vpnInterfaceManager) {
102         this.vpnInterfaceManager = vpnInterfaceManager;
103     }
104
105     @Override
106     protected void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
107         LOG.trace("Remove VPN event - Key: {}, value: {}", identifier, del);
108         String vpnName = del.getVpnInstanceName();
109         InstanceIdentifier<VpnInstance> vpnIdentifier = VpnUtil.getVpnInstanceIdentifier(vpnName);
110         //Clean up vpn Interface
111         InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
112         Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.OPERATIONAL, vpnInterfacesId);
113
114         if(optionalVpnInterfaces.isPresent()) {
115             List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
116             for(VpnInterface vpnInterface : vpnInterfaces) {
117                 if(vpnInterface.getVpnInstanceName().equals(vpnName)) {
118                     LOG.debug("VpnInterface {} will be removed from VPN {}", vpnInterface.getName(), vpnName);
119                     vpnInterfaceManager.remove(
120                             VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
121                 }
122             }
123         }
124
125         delete(LogicalDatastoreType.OPERATIONAL, vpnIdentifier);
126
127         String rd = del.getIpv4Family().getRouteDistinguisher();
128         try {
129             bgpManager.deleteVrf(rd);
130         } catch(Exception e) {
131             LOG.error("Exception when removing VRF from BGP", e);
132         }
133     }
134
135     @Override
136     protected void update(InstanceIdentifier<VpnInstance> identifier,
137             VpnInstance original, VpnInstance update) {
138         LOG.trace("Update event - Key: {}, value: {}", identifier, update);
139     }
140
141     @Override
142     protected void add(InstanceIdentifier<VpnInstance> identifier,
143             VpnInstance value) {
144         LOG.trace("key: {}, value: {}", identifier, value);
145
146         long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, value.getVpnInstanceName());
147
148         VpnInstance opValue = new VpnInstanceBuilder(value).
149                  addAugmentation(VpnInstance1.class, new VpnInstance1Builder().setVpnId(vpnId).build()).build();
150
151         asyncWrite(LogicalDatastoreType.OPERATIONAL, identifier, opValue, DEFAULT_CALLBACK);
152
153         //public void addVrf(String rd, Collection<String> importRts, Collection<String> exportRts)
154         VpnAfConfig config = value.getIpv4Family();
155         String rd = config.getRouteDistinguisher();
156         List<String> importRts = Arrays.asList(config.getImportRoutePolicy().split(","));
157         List<String> exportRts = Arrays.asList(config.getExportRoutePolicy().split(","));
158         try {
159             bgpManager.addVrf(rd, importRts, exportRts);
160         } catch(Exception e) {
161             LOG.error("Exception when adding VRF to BGP", e);
162         }
163     }
164
165     private InstanceIdentifier<?> getWildCardPath() {
166         return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class);
167     }
168
169     private InstanceIdentifier<?> getFibEntryListenerPath() {
170         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class)
171                 .child(VrfEntry.class);
172     }
173
174     @Override
175     public void close() throws Exception {
176         if (listenerRegistration != null) {
177             try {
178                 listenerRegistration.close();
179             } catch (final Exception e) {
180                 LOG.error("Error when cleaning up Vpn DataChangeListener.", e);
181             }
182             listenerRegistration = null;
183         }
184         if (fibListenerRegistration != null) {
185             try {
186                 fibListenerRegistration.close();
187             } catch (final Exception e) {
188                 LOG.error("Error when cleaning up Fib entries DataChangeListener.", e);
189             }
190             fibListenerRegistration = null;
191         }
192         LOG.trace("VPN Manager Closed");
193     }
194
195     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
196             InstanceIdentifier<T> path) {
197
198         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
199
200         Optional<T> result = Optional.absent();
201         try {
202             result = tx.read(datastoreType, path).get();
203         } catch (Exception e) {
204             throw new RuntimeException(e);
205         }
206
207         return result;
208     }
209
210     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
211             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
212         WriteTransaction tx = broker.newWriteOnlyTransaction();
213         tx.put(datastoreType, path, data, true);
214         Futures.addCallback(tx.submit(), callback);
215     }
216
217     private VpnInstance getVpnForRD(String rd) {
218         InstanceIdentifier<VpnInstances> id = InstanceIdentifier.create(VpnInstances.class);
219         Optional<VpnInstances> vpnInstances = read(LogicalDatastoreType.OPERATIONAL, id);
220         if(vpnInstances.isPresent()) {
221             List<VpnInstance> vpns = vpnInstances.get().getVpnInstance();
222             for(VpnInstance vpn : vpns) {
223                 if(vpn.getIpv4Family().getRouteDistinguisher().equals(rd)) {
224                     return vpn;
225                 }
226             }
227         }
228         return null;
229     }
230
231 //    private Integer getUniqueId(IdManagerService idManager, String poolName,String idKey) {
232 //        AllocateIdInput getIdInput = new AllocateIdInputBuilder()
233 //                .setPoolName(poolName)
234 //                .setIdKey(idKey).build();
235 //
236 //        try {
237 //            Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
238 //            RpcResult<AllocateIdOutput> rpcResult = result.get();
239 //            if(rpcResult.isSuccessful()) {
240 //                return rpcResult.getResult().getIdValue().intValue();
241 //            } else {
242 //                LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
243 //            }
244 //        } catch (InterruptedException | ExecutionException e) {
245 //            LOG.warn("Exception when getting Unique Id",e);
246 //        }
247 //        return 0;
248 //    }
249
250     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
251         WriteTransaction tx = broker.newWriteOnlyTransaction();
252         tx.delete(datastoreType, path);
253         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
254     }
255
256     private class FibEntriesListener extends AbstractDataChangeListener<VrfEntry>  {
257
258         public FibEntriesListener() {
259             super(VrfEntry.class);
260         }
261
262         @Override
263         protected void remove(InstanceIdentifier<VrfEntry> identifier,
264                 VrfEntry del) {
265             LOG.trace("Remove Fib event - Key : {}, value : {} ", identifier, del);
266             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
267             String rd = key.getRouteDistinguisher();
268             Long label = del.getLabel();
269             VpnInstance vpn = getVpnForRD(rd);
270             if(vpn != null) {
271                 InstanceIdentifier<VpnInstance> id = VpnUtil.getVpnInstanceIdentifier(vpn.getVpnInstanceName());
272                 InstanceIdentifier<VpnInstance1> augId = id.augmentation(VpnInstance1.class);
273                 Optional<VpnInstance1> vpnAugmenation = read(LogicalDatastoreType.OPERATIONAL, augId);
274                 if(vpnAugmenation.isPresent()) {
275                     VpnInstance1 vpnAug = vpnAugmenation.get();
276                     List<Long> routeIds = vpnAug.getRouteEntryId();
277                     if(routeIds == null) {
278                         LOG.debug("Fib Route entry is empty.");
279                         return;
280                     }
281                     LOG.debug("Removing label from vpn info - {}", label);
282                     routeIds.remove(label);
283                     asyncWrite(LogicalDatastoreType.OPERATIONAL, augId,
284                             new VpnInstance1Builder(vpnAug).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
285                 } else {
286                     LOG.warn("VPN Augmentation not found for vpn instance {}", vpn.getVpnInstanceName());
287                 }
288             } else {
289                 LOG.warn("No VPN Instance found for RD: {}", rd);
290             }
291         }
292
293         @Override
294         protected void update(InstanceIdentifier<VrfEntry> identifier,
295                 VrfEntry original, VrfEntry update) {
296             // TODO Auto-generated method stub
297
298         }
299
300         @Override
301         protected void add(InstanceIdentifier<VrfEntry> identifier,
302                 VrfEntry add) {
303             LOG.trace("Add Vrf Entry event - Key : {}, value : {}", identifier, add);
304             final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
305             String rd = key.getRouteDistinguisher();
306             Long label = add.getLabel();
307             VpnInstance vpn = getVpnForRD(rd);
308             if(vpn != null) {
309                 InstanceIdentifier<VpnInstance> id = VpnUtil.getVpnInstanceIdentifier(vpn.getVpnInstanceName());
310                 InstanceIdentifier<VpnInstance1> augId = id.augmentation(VpnInstance1.class);
311                 Optional<VpnInstance1> vpnAugmenation = read(LogicalDatastoreType.OPERATIONAL, augId);
312                 if(vpnAugmenation.isPresent()) {
313                     VpnInstance1 vpnAug = vpnAugmenation.get();
314                     List<Long> routeIds = vpnAug.getRouteEntryId();
315                     if(routeIds == null) {
316                         routeIds = new ArrayList<>();
317                     }
318                     LOG.debug("Adding label to vpn info - {}", label);
319                     routeIds.add(label);
320                     asyncWrite(LogicalDatastoreType.OPERATIONAL, augId,
321                             new VpnInstance1Builder(vpnAug).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
322                 } else {
323                     LOG.warn("VPN Augmentation not found for vpn instance {}", vpn.getVpnInstanceName());
324                 }
325             } else {
326                 LOG.warn("No VPN Instance found for RD: {}", rd);
327             }
328         }
329     }
330 }