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