2 * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt;
10 import java.util.ArrayList;
11 import java.util.List;
12 import java.util.concurrent.*;
14 import com.google.common.util.concurrent.CheckedFuture;
15 import org.opendaylight.bgpmanager.api.IBgpManager;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
18 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
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.opendaylight.fibmanager.api.IFibManager;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 import com.google.common.base.Optional;
53 import com.google.common.util.concurrent.FutureCallback;
54 import com.google.common.util.concurrent.Futures;
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;
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");
75 public void onFailure(Throwable error) {
76 LOG.error("Error in Datastore operation", error);
81 * Listens for data change related to VPN Instance
82 * Informs the BGP about VRF information
84 * @param db - dataBroker reference
86 public VpnManager(final DataBroker db, final IBgpManager bgpManager) {
87 super(VpnInstance.class);
89 this.bgpManager = bgpManager;
90 this.fibListener = new FibEntriesListener();
91 this.vpnInstOpListener = new VpnInstanceOpListener();
95 private void registerListener(final DataBroker db) {
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.OPERATIONAL,
102 getVpnInstanceOpListenerPath(), vpnInstOpListener, DataChangeScope.SUBTREE);
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);
110 public void setIdManager(IdManagerService idManager) {
111 this.idManager = idManager;
114 public void setVpnInterfaceManager(VpnInterfaceManager vpnInterfaceManager) {
115 this.vpnInterfaceManager = vpnInterfaceManager;
118 private void waitForOpRemoval(String id, long timeout) {
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()) {
123 vpnOpMap.put(id, notifyTask);
124 synchronized (notifyTask) {
126 notifyTask.wait(timeout);
127 } catch (InterruptedException e) {
138 protected void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
139 LOG.trace("Remove VPN event key: {}, value: {}", identifier, del);
140 String vpnName = del.getVpnInstanceName();
141 String rd = del.getIpv4Family().getRouteDistinguisher();
142 long vpnId = VpnUtil.getVpnId(broker, vpnName);
144 //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
145 Optional<VpnInstanceOpDataEntry> vpnOpValue = null;
146 if ((rd != null) && (!rd.isEmpty())) {
147 vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
148 VpnUtil.getVpnInstanceOpDataIdentifier(rd));
150 vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
151 VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
154 if ((vpnOpValue != null) && (vpnOpValue.isPresent())) {
155 VpnInstanceOpDataEntry vpnOpEntry = null;
156 long timeout = VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS;
159 vpnOpEntry = vpnOpValue.get();
160 intfCount = vpnOpEntry.getVpnInterfaceCount();
161 if (intfCount != null && intfCount > 0) {
162 // Minimum wait time of 10 seconds for one VPN Interface clearance (inclusive of full trace on)
163 timeout = intfCount * 10000;
164 // Maximum wait time of 90 seconds for all VPN Interfaces clearance (inclusive of full trace on)
165 if (timeout > VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS) {
166 timeout = VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS;
168 LOG.trace("VPNInstance removal count of interface at {} for for rd {}, vpnname {}",
169 intfCount, rd, vpnName);
171 LOG.trace("VPNInstance removal thread waiting for {} seconds for rd {}, vpnname {}",
172 (timeout/1000), rd, vpnName);
174 if ((rd != null) && (!rd.isEmpty())) {
175 waitForOpRemoval(rd, timeout);
177 waitForOpRemoval(vpnName, timeout);
180 LOG.trace("Returned out of waiting for Op Data removal for rd {}, vpnname {}", rd, vpnName);
182 // Clean up VpnInstanceToVpnId from Config DS
183 VpnUtil.removeVpnInstanceToVpnId(broker, vpnName);
184 LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", rd, vpnName);
187 bgpManager.deleteVrf(rd);
188 } catch (Exception e) {
189 LOG.error("Exception when removing VRF from BGP for RD {} in VPN {} exception " + e, rd, vpnName);
192 // Clean up VPNExtraRoutes Operational DS
193 VpnUtil.removeVpnExtraRouteForVpn(broker, rd);
195 // Clean up VPNInstanceOpDataEntry
196 VpnUtil.removeVpnOpInstance(broker, rd);
198 // Clean up FIB Entries Config DS
199 VpnUtil.removeVrfTableForVpn(broker, vpnName);
201 // Clean up VPNExtraRoutes Operational DS
202 VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName);
204 // Clean up VPNInstanceOpDataEntry
205 VpnUtil.removeVpnOpInstance(broker, vpnName);
208 // Clean up PrefixToInterface Operational DS
209 VpnUtil.removePrefixToInterfaceForVpnId(broker, vpnId);
211 // Clean up L3NextHop Operational DS
212 VpnUtil.removeL3nexthopForVpnId(broker, vpnId);
214 // Release the ID used for this VPN back to IdManager
216 VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
220 protected void update(InstanceIdentifier<VpnInstance> identifier,
221 VpnInstance original, VpnInstance update) {
222 LOG.trace("Update VPN event key: {}, value: {}", identifier, update);
226 protected void add(InstanceIdentifier<VpnInstance> identifier,
228 LOG.trace("Add VPN event key: {}, value: {}", identifier, value);
229 VpnAfConfig config = value.getIpv4Family();
230 String rd = config.getRouteDistinguisher();
232 long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, value.getVpnInstanceName());
233 LOG.trace("VPN instance to ID generated.");
234 org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
235 vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(value.getVpnInstanceName(), vpnId,
236 (rd != null) ? rd : value.getVpnInstanceName());
238 syncWrite(LogicalDatastoreType.CONFIGURATION,
239 VpnUtil.getVpnInstanceToVpnIdIdentifier(value.getVpnInstanceName()),
240 vpnInstanceToVpnId, DEFAULT_CALLBACK);
242 IFibManager fibManager = vpnInterfaceManager.getFibManager();
244 String cachedTransType = fibManager.getReqTransType();
245 LOG.trace("Value for confTransportType is " + cachedTransType);
246 if (cachedTransType.equals("Invalid")) {
248 fibManager.setConfTransType("L3VPN", "VXLAN");
249 LOG.trace("setting it to vxlan now");
250 } catch (Exception e) {
251 LOG.trace("Exception caught setting the cached value for transportType");
252 LOG.error(e.getMessage());
255 LOG.trace(":cached val is neither unset/invalid. NO-op.");
257 } catch (Exception e) {
258 System.out.println("Exception caught accessing the cached value for transportType");
259 LOG.error(e.getMessage());
263 VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder();
264 builder.setVrfId(value.getVpnInstanceName()).setVpnId(vpnId);
265 builder.setVpnInterfaceCount(0L);
266 syncWrite(LogicalDatastoreType.OPERATIONAL,
267 VpnUtil.getVpnInstanceOpDataIdentifier(value.getVpnInstanceName()),
268 builder.build(), DEFAULT_CALLBACK);
271 VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder();
272 builder.setVrfId(rd).setVpnId(vpnId);
273 builder.setVpnInterfaceCount(0L);
274 syncWrite(LogicalDatastoreType.OPERATIONAL,
275 VpnUtil.getVpnInstanceOpDataIdentifier(rd),
276 builder.build(), DEFAULT_CALLBACK);
278 List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
280 List<String> ertList = new ArrayList<String>();
281 List<String> irtList = new ArrayList<String>();
283 for (VpnTarget vpnTarget : vpnTargetList) {
284 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
285 ertList.add(vpnTarget.getVrfRTValue());
287 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
288 irtList.add(vpnTarget.getVrfRTValue());
290 if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
291 ertList.add(vpnTarget.getVrfRTValue());
292 irtList.add(vpnTarget.getVrfRTValue());
297 bgpManager.addVrf(rd, irtList, ertList);
298 } catch(Exception e) {
299 LOG.error("Exception when adding VRF to BGP", e);
302 //Try to add up vpn Interfaces if already in Operational Datastore
303 InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
304 Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
306 if(optionalVpnInterfaces.isPresent()) {
307 List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
308 for(VpnInterface vpnInterface : vpnInterfaces) {
309 if(vpnInterface.getVpnInstanceName().equals(value.getVpnInstanceName())) {
310 LOG.debug("VpnInterface {} will be added from VPN {}", vpnInterface.getName(), value.getVpnInstanceName());
311 vpnInterfaceManager.add(
312 VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
319 public boolean isVPNConfigured() {
321 InstanceIdentifier<VpnInstances> vpnsIdentifier =
322 InstanceIdentifier.builder(VpnInstances.class).build();
323 Optional<VpnInstances> optionalVpns = read( LogicalDatastoreType.CONFIGURATION,
325 if (!optionalVpns.isPresent() ||
326 optionalVpns.get().getVpnInstance() == null ||
327 optionalVpns.get().getVpnInstance().isEmpty()) {
328 LOG.trace("No VPNs configured.");
331 LOG.trace("VPNs are configured on the system.");
335 private InstanceIdentifier<?> getWildCardPath() {
336 return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class);
339 private InstanceIdentifier<?> getFibEntryListenerPath() {
340 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class)
341 .child(VrfEntry.class);
344 private InstanceIdentifier<?> getVpnInstanceOpListenerPath() {
345 return InstanceIdentifier.create(VpnInstanceOpData.class).child(VpnInstanceOpDataEntry.class);
350 public void close() throws Exception {
351 if (listenerRegistration != null) {
353 listenerRegistration.close();
354 } catch (final Exception e) {
355 LOG.error("Error when cleaning up Vpn DataChangeListener.", e);
357 listenerRegistration = null;
359 if (fibListenerRegistration != null) {
361 fibListenerRegistration.close();
362 } catch (final Exception e) {
363 LOG.error("Error when cleaning up Fib entries DataChangeListener.", e);
365 fibListenerRegistration = null;
367 if (opListenerRegistration != null) {
369 opListenerRegistration.close();
370 } catch (final Exception e) {
371 LOG.error("Error when cleaning up VPN Instance Operational entries DataChangeListener.", e);
373 opListenerRegistration = null;
376 LOG.trace("VPN Manager Closed");
379 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
380 InstanceIdentifier<T> path) {
382 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
384 Optional<T> result = Optional.absent();
386 result = tx.read(datastoreType, path).get();
387 } catch (Exception e) {
388 throw new RuntimeException(e);
394 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
395 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
396 WriteTransaction tx = broker.newWriteOnlyTransaction();
397 tx.put(datastoreType, path, data, true);
398 Futures.addCallback(tx.submit(), callback);
401 private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
402 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
403 WriteTransaction tx = broker.newWriteOnlyTransaction();
404 tx.put(datastoreType, path, data, true);
405 CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
408 } catch (InterruptedException | ExecutionException e) {
409 LOG.error("Error writing VPN instance to ID info to datastore (path, data) : ({}, {})", path, data);
410 throw new RuntimeException(e.getMessage());
414 protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) {
415 InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
416 Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = read(LogicalDatastoreType.OPERATIONAL, id);
417 if(vpnInstanceOpData.isPresent()) {
418 return vpnInstanceOpData.get();
423 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
424 WriteTransaction tx = broker.newWriteOnlyTransaction();
425 tx.delete(datastoreType, path);
426 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
429 private class FibEntriesListener extends AbstractDataChangeListener<VrfEntry> {
431 public FibEntriesListener() {
432 super(VrfEntry.class);
436 protected void remove(InstanceIdentifier<VrfEntry> identifier,
438 LOG.trace("Remove Fib event - Key : {}, value : {} ", identifier, del);
439 final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
440 String rd = key.getRouteDistinguisher();
441 Long label = del.getLabel();
442 VpnInstanceOpDataEntry vpnInstanceOpData = getVpnInstanceOpData(rd);
443 if(vpnInstanceOpData != null) {
444 List<Long> routeIds = vpnInstanceOpData.getRouteEntryId();
445 if(routeIds == null) {
446 LOG.debug("Fib Route entry is empty.");
449 LOG.debug("Removing label from vpn info - {}", label);
450 routeIds.remove(label);
451 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
452 new VpnInstanceOpDataEntryBuilder(vpnInstanceOpData).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
454 LOG.warn("No VPN Instance found for RD: {}", rd);
459 protected void update(InstanceIdentifier<VrfEntry> identifier,
460 VrfEntry original, VrfEntry update) {
461 // TODO Auto-generated method stub
466 protected void add(InstanceIdentifier<VrfEntry> identifier,
468 LOG.trace("Add Vrf Entry event - Key : {}, value : {}", identifier, add);
469 final VrfTablesKey key = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
470 String rd = key.getRouteDistinguisher();
471 Long label = add.getLabel();
472 VpnInstanceOpDataEntry vpn = getVpnInstanceOpData(rd);
474 List<Long> routeIds = vpn.getRouteEntryId();
475 if(routeIds == null) {
476 routeIds = new ArrayList<>();
478 LOG.debug("Adding label to vpn info - {}", label);
480 asyncWrite(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd),
481 new VpnInstanceOpDataEntryBuilder(vpn).setRouteEntryId(routeIds).build(), DEFAULT_CALLBACK);
483 LOG.warn("No VPN Instance found for RD: {}", rd);
488 class VpnInstanceOpListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener<VpnInstanceOpDataEntry> {
490 public VpnInstanceOpListener() {
491 super(VpnInstanceOpDataEntry.class);
495 protected void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry del) {
500 protected void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
501 final VpnInstanceOpDataEntryKey key = identifier.firstKeyOf(VpnInstanceOpDataEntry.class, VpnInstanceOpDataEntryKey.class);
502 String vpnName = key.getVrfId();
504 LOG.trace("VpnInstanceOpListener update: vpn name {} interface count in Old VpnOp Instance {} in New VpnOp Instance {}" ,
505 vpnName, original.getVpnInterfaceCount(), update.getVpnInterfaceCount() );
507 //if((original.getVpnToDpnList().size() != update.getVpnToDpnList().size()) && (update.getVpnToDpnList().size() == 0)) {
508 if((original.getVpnInterfaceCount() != update.getVpnInterfaceCount()) && (update.getVpnInterfaceCount() == 0)) {
509 notifyTaskIfRequired(vpnName);
513 private void notifyTaskIfRequired(String vpnName) {
514 Runnable notifyTask = vpnOpMap.remove(vpnName);
515 if (notifyTask == null) {
516 LOG.trace("VpnInstanceOpListener update: No Notify Task queued for vpnName {}", vpnName);
519 executorService.execute(notifyTask);
523 protected void add(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry add) {